Repository navigation
feat: Report Band tool calls as turn activity - #841
AlexanderZ-Band merged 47 commits into
Conversation
270fe17 to
191870e
Compare
AlexanderZ-Band
left a comment
There was a problem hiding this comment.
Blocking — would be REQUEST_CHANGES on a PR by someone else. Cycle 1 found three verified issues: housekeeping acquisition, cancellation cleanup during initial reporting, and a missing terminal-clear assertion. Public runtime/MCP/REST reproductions and the mutation check confirmed them.
191870e to
dd716aa
Compare
42c90b0 to
0d3c24f
Compare
0d3c24f to
1a6a291
Compare
1a6a291 to
a674b67
Compare
bandzalkin
left a comment
There was a problem hiding this comment.
Scope
Reviewed #841 at a674b67f6deeea036f7f4a55bf9a34b5d7e935f2 against #840 at 06d8124481475460782840b8854285cf7c0a2d92, plus the complete #839 → #840 → #841 stack from a96fb54d0c9e496b22fb80353eea48656294b0cb. Scope includes provider mappings, approval waits, registered Band tools, and the changed validation toolkit.
Constraints
Activity must describe actual work independently of chat narration, remain bound to its originating turn, and carry semantic names rather than private arguments/results. Cleanup must preserve borrowed resources and active sibling work. Provider mappings must match released wire contracts.
Decisions
Keep the single scope-owned, serialized reporter. Correct observation identity and provider/toolkit boundary handling rather than add another reporting owner. R1 and R2 expose observation-lifetime gaps within that design; the overall direction remains appropriate.
Findings and proof
Five new P2 findings, R1–R5, inline. Each has an independently failing offline behavioral probe: real compiled LangGraph parallel execution; MCP dispatch across Claude wrapper rebinding; released Codex compaction events through the adapter; actual Agno callable dispatch; and real Phoenix subscription ownership against a local fake server. Details and smallest corrections are in the inline discussions.
The three earlier findings remain fixed and are not reopened: housekeeping acquisition, initial-report cancellation, and terminal-clear assertion. Their regression coverage passed. Narration gating and compatibility deadline/fallback attacks did not establish additional defects.
Executed on the combined stack using locked, isolated Python 3.12 environments:
| Suite | Passed | Skipped |
|---|---|---|
Core activity, tools, adapters, delivery and conformance (dev) |
1,466 | 69 |
Coding adapters, ACP and turn admission (dev) |
1,372 | 10 |
CrewAI activity (dev-crewai) |
2 | 0 |
Parlant activity (dev-parlant) |
3 | 0 |
Strands activity and CI-selection checks (dev-strands-nooa) |
5 | 0 |
These passing suites do not cover the five failing scenarios. Live platform/model acceptance and the full repository gate were not run. No product files were changed.
Architectural / merge verdict
HELD — the stack is not merge-ready until R1–R5 are corrected and verified. Also resolve #839's current conflict with main before integration.
…t-activity-detail-to-the-platform-INT-1744 Keep main's BackendTurns ownership of OpenCode cancellation and register the watcher as a turn worker so activity reporting outlives the handler. Reporter unit tests move to tests/runtime/workingreporter; main's workingstate package keeps the execution-level tests. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AgnNR4uiqD787Bg3yKdZpL
…detail-to-the-platform-INT-1744' into feat/python-sdk-report-activity-detail-to-the-platform-delivery-INT-1744 Port the startup-backlog claim-failure test edit to the split tests/runtime/execution package. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AgnNR4uiqD787Bg3yKdZpL
Band tool methods show as tool_call with their name, or memory while saving a memory, and return to reasoning when they end, even on failure or cancellation. tool_activity resolves the turn observer once, so a tool ends on the turn it started on. send_event reports only where a model invoked it (execute_tool_call, MCP, Pydantic AI, CrewAI, Parlant). CrewAI and Parlant custom tools report too. A tools object without a declared observer stays unreported instead of conjuring one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DFmR3J53UqbsYDMmTUd3Rb Signed-off-by: Alexander Zaikman <alexander.zaikman@band.ai>
d697f43 to
3f59acf
Compare
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DFmR3J53UqbsYDMmTUd3Rb Signed-off-by: Alexander Zaikman <alexander.zaikman@band.ai>
AlexanderZ-Band
left a comment
There was a problem hiding this comment.
Blocking — would be REQUEST_CHANGES on a PR by someone else.
Cycle 1 of /my-code-review: nested Band tools overwrite the outer tool's activity, Strands' model send_event is unreported, and observer failures break tool calls. Test gaps cover the Slack tee, CrewAI/Parlant send_event, double reporting, real Pydantic AI runs, family coverage and a realistic multi-tool turn. Four dropped claims are posted as resolved no-fix records.
…t-activity-detail-to-the-platform-delivery-INT-1744 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DFmR3J53UqbsYDMmTUd3Rb Signed-off-by: Alexander Zaikman <alexander.zaikman@band.ai>
Only the outermost tool_activity reports, so a Band tool that calls other Band tools (add/remove participant) shows as itself throughout. Every model entry point (MCP, Pydantic AI, execute_tool_call, CrewAI, Parlant, Strands) now wraps its call in tool_activity; the decorator's send_event exclusion is the one predicate, and memory saves derive from the registry. Observer updates are best-effort. The tool-activity producers move to band.runtime.toolactivity. Tests drive the real boundaries: an in-memory MCP session, a pydantic_ai Agent with FunctionModel, a scripted Strands turn, the Slack tee, and the CrewAI/Parlant send_event tools, plus a multi-tool turn sequence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DFmR3J53UqbsYDMmTUd3Rb Signed-off-by: Alexander Zaikman <alexander.zaikman@band.ai>
Select upstream band-ai#841 (activity producers, carrying band-ai#839/band-ai#840 via forward merges), band-ai#874 after 841 (tool-name emit gate with the I-08 name clamp), the host stack ahead of band-ai#763, band-ai#763 after 874 (native OMP with the tool/permission activity hooks and omp_acp retirement), and band-ai#854/band-ai#795/band-ai#848 after 763. Replayed from the branches' live heads.
AlexanderZ-Band
left a comment
There was a problem hiding this comment.
Cycle 2 of /my-code-review found no in-scope issues. There are two out-of-scope gaps:
- custom tools on the MCP engine and in Strands don't report activity;
- the Slack tee's own
slack_send_messagedoesn't report.
The rest are nits and test suggestions. The five dropped claims are posted as resolved no-fix records.
The Slack tee's own slack_send_message now reports through tool_activity. The tools-class TypeVar is defined once beside agent_tool_methods, and the start/end producers share one resolve-and-show helper. Tests: failed and awaited memory saves, exact results and named failed reports, a proxy that would conjure an observer, an intent-named Strands turn helper, and a wire-level flow through a real ExecutionContext. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DFmR3J53UqbsYDMmTUd3Rb Signed-off-by: Alexander Zaikman <alexander.zaikman@band.ai>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DFmR3J53UqbsYDMmTUd3Rb Signed-off-by: Alexander Zaikman <alexander.zaikman@band.ai>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DFmR3J53UqbsYDMmTUd3Rb Signed-off-by: Alexander Zaikman <alexander.zaikman@band.ai>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DFmR3J53UqbsYDMmTUd3Rb Signed-off-by: Alexander Zaikman <alexander.zaikman@band.ai>
Select upstream band-ai#841 (activity producers, carrying band-ai#839/band-ai#840 via forward merges), band-ai#874 after 841 (tool-name emit gate with the I-08 name clamp), the host stack ahead of band-ai#763, band-ai#763 after 874 (native OMP with the tool/permission activity hooks and omp_acp retirement), and band-ai#854/band-ai#795/band-ai#848 after 763. Replayed from the branches' live heads.
Select upstream band-ai#841 (activity producers, carrying band-ai#839/band-ai#840 via forward merges), band-ai#874 after 841 (tool-name emit gate with the I-08 name clamp), the host stack ahead of band-ai#763, band-ai#763 after 874 (native OMP with the tool/permission activity hooks and omp_acp retirement), and band-ai#854/band-ai#795/band-ai#848 after 763. Replayed from the branches' live heads.
Select upstream band-ai#841 (activity producers, carrying band-ai#839/band-ai#840 via forward merges), band-ai#874 after 841 (tool-name emit gate with the I-08 name clamp), the host stack ahead of band-ai#763, band-ai#763 after 874 (native OMP with the tool/permission activity hooks and omp_acp retirement), and band-ai#854/band-ai#795/band-ai#848 after 763. Replayed from the branches' live heads.
Select upstream band-ai#841 (activity producers, carrying band-ai#839/band-ai#840 via forward merges), band-ai#874 after 841 (tool-name emit gate with the I-08 name clamp), the host stack ahead of band-ai#763, band-ai#763 after 874 (native OMP with the tool/permission activity hooks and omp_acp retirement), and band-ai#854/band-ai#795/band-ai#848 after 763. Replayed from the branches' live heads.
Select upstream band-ai#841 (activity producers, carrying band-ai#839/band-ai#840 via forward merges), band-ai#874 after 841 (tool-name emit gate with the I-08 name clamp), the host stack ahead of band-ai#763, band-ai#763 after 874 (native OMP with the tool/permission activity hooks and omp_acp retirement), and band-ai#854/band-ai#795/band-ai#848 after 763. Replayed from the branches' live heads.
Why
#840 lets each turn carry room activity detail, but nothing reports it yet. Band's own tools (chat, contacts, memory, files and tasks) run through every adapter. Reporting them once, in the shared tool paths, gives every framework a visible
tool_callwith the tool's name before any adapter-specific mapping lands.What
runtime/toolactivity.pyis the single owner of what a tool shows:tool_detailmaps a tool name to its activity. Memory saves (store, supersede, archive), taken from the registry, showmemory. Every other tool, including memory reads, showstool_callwith its name.tool_activityreports a tool's start and end on the turn that ran it.@reports_tool_activitywraps every agent tool method exceptsend_event, which adapters also call to post their own events. A model'ssend_eventreports at the dispatcher.activity_observer_offinds a tools object's observer withgetattr_static. A duck-typed or mock tools object that never declared one runs unreported, instead of reporting to an auto-created attribute.DedupingAgentToolsexposes its inner tools' observer.AgentToolsexecutiondispatch_toolslack_send_messageband.testing.ActivityRecorder(withobserving(tools)) records what a turn observed. Producer tests assert through it.band_send_messagethrough a realExecutionContext.ci.ymland in their coverage guards.Scope
🤖 Generated with Claude Code
https://claude.ai/code/session_01DFmR3J53UqbsYDMmTUd3Rb