[BUG] Agent Teams: inbox messages to team lead deferred until stop_reason=end_turn (buried during tool_use chains)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
What's Wrong?
In Agent Teams, messages delivered to the team lead's inbox file are read and marked "read": true by the harness on schedule, but are not injected as <teammate-message> user turns until the assistant emits stop_reason=end_turn. While the assistant is in a sequence of stop_reason=tool_use messages — the normal state for an orchestrator running a long agentic loop — the queue grows silently and the orchestrator continues acting on stale state.
Layer-by-layer:
| Layer | Status |
|---|---|
| Sender → inbox JSON file | works |
| Inbox file → harness consumption (read:false → true) | works (proven by 0.5s filesystem probe during foreground sleep) |
| Harness → model input (user turn injection) | broken — gated on stop_reason=end_turn |
What Should Happen?
Teammate messages should surface to the orchestrator's model with latency bounded by the harness polling interval, regardless of the assistant's current stop_reason. A natural place to inject them is the next tool_result user turn (which is already a user turn, so appending queued <teammate-message> blocks would deliver them on the normal cadence without needing an end_turn).
Error Messages/Logs
0.5s probe of ~/.claude/teams/<team>/inboxes/team-lead.json
during a foreground "Bash sleep 25" on the lead:
13:05:05.754 read=0 unread=1 (msg-1 just arrived in file)
13:05:07.469 read=1 unread=0 (harness flipped flag — mid-foreground-sleep)
13:05:08..17 unchanged (sleep still running)
13:05:17 sleep returns
13:05:25 DONE arrived (read=1 unread=1)
13:05:26 DONE flipped (read=2 unread=0)
teammate-message blocks surfaced to the model during this entire window: 0
JSONL transcript ordering proving the end_turn trigger:
"stop_reason":"tool_use" ← foreground Bash sleep 25
"stop_reason":"tool_use" ← probe `ls`
"stop_reason":"tool_use" ← probe `echo`
"stop_reason":"end_turn" ← text-only assistant response
teammate-message teammate_id="s7" ← queued msg-1
teammate-message teammate_id="s7" ← queued DONE-s7
teammate-message teammate_id="s7" ← queued idle_notification
All three teammate-messages arrived in the single user turn immediately following end_turn, with no user input.
Steps to Reproduce
~30 seconds, 6 tool calls.
TeamCreate team_name="repro"- Spawn an
msg-senderchild in the background:
````
Agent
subagent_type: msg-sender
team_name: repro
name: s1
model: claude-opus-4-7
run_in_background: true
prompt: |
SELF_NAME=s1
RECIPIENT=team-lead
COUNT=1
DELAY_MS=5000
LOG_DIR=/tmp/repro
- Immediately as lead:
Bash(foreground) →sleep 25. - After sleep returns, do 1–2 fast tool calls (e.g.
ls,echo hi). Observe: no<teammate-message>block appears in any tool-result turn. cat ~/.claude/teams/repro/inboxes/team-lead.json→ three entries (msg-1, DONE, idle_notification), all"read": true.- Emit a text-only assistant response (no tool calls).
stop_reason=end_turn. - The next user turn arrives automatically with no user input and contains all three queued
<teammate-message>blocks.
(msg-sender is a custom test agent that fires N numbered messages then a DONE marker. The same mechanism reproduces with any teammate that sends while the lead is in a tool_use chain.)
Claude Model
Opus
Is this a regression?
Yes, this worked in a previous version
Last Working Version
Worked on Opus 4.6. Reproduces on Opus 4.7. CLI version was not held constant between observations (I keep the CLI auto-updated), so I can only attest to the model change — not rule out a CLI change that shipped in the same window.
Claude Code Version
2.1.114 (Claude Code)
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
Other (Git Bash / MINGW64 on Windows)
Additional Information
Model ID: claude-opus-4-7 (1M ctx)
Backend: in-process
Env: CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
Regression scope. The same orchestrator pattern worked on Opus 4.6. After switching to Opus 4.7, the symptom appeared. I keep the CLI auto-updated, so I cannot attest that the CLI version was constant between the working and broken observations — the only controlled variable I can confirm changed is the model. So this may be a 4.7 model-level change in turn cadence, a CLI-side change that shipped in the same window, or an interaction between the two.
Why this matters in production. A /work-style orchestrator produces stop_reason=tool_use for many minutes at a time (Read → Grep → Edit → Bash → repeat). Any teammate message (Triage, reviewer, child-agent status) sent during that loop lands in the inbox file on time but is never surfaced to the orchestrator's model. The orchestrator proceeds on stale assumptions — matching the originally observed symptom of "orchestrator missing basic messaging from a child agent; harness didn't surface the message."
Five experiments support this diagnosis:
| Experiment | Setup | Result |
|---|---|---|
| EXP1 | Foreground Bash sleep 25 + 3 tool-use probes | 3 messages in inbox read:true, 0 surfaced |
| EXP2a | Foreground Grep (wide pattern) | Same: read but not surfaced |
| EXP3 (control) | Background Bash sleep 25 | Messages surfaced live during sleep |
| EXP6 | 0.5s filesystem probe during foreground sleep | read:true flip observed; no surface |
| EXP7 | Foreground sleep + end_turn (text-only response) | Queue auto-flushed on end_turn, no user input needed |
Related issues (not duplicates):
- #25254 — same observable symptom, but was closed as VS-Code-only. This reproduction is on the terminal CLI on Windows, contradicting that closure.
- #34668 — same suspected mechanism (long tool-call polling stalls delivery); different direction (lead→teammate vs my teammate→lead). My evidence shows the bug is symmetric.
- #23415 — tmux/macOS-specific; this repro is in-process Windows.
Suggested fix direction. Inject queued <teammate-message> blocks into the next tool_result user turn, not only at end_turn boundaries. A tool_result is already a user turn; appending teammate-messages to it would deliver on the normal polling cadence.
Artifacts available on request:
- 0.5s inbox state probe log (
exp6/probe.log) - Per-experiment markers (
exp{1,2a,3,6,7}/markers.log) - Session JSONL excerpt with the EXP7 ordering
This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗