[BUG] Agent Teams: inbox messages to team lead deferred until stop_reason=end_turn (buried during tool_use chains)

Open 💬 6 comments Opened Apr 19, 2026 by ericonuki

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.

  1. TeamCreate team_name="repro"
  2. Spawn an msg-sender child 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
``

  1. Immediately as lead: Bash (foreground) → sleep 25.
  2. 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.
  3. cat ~/.claude/teams/repro/inboxes/team-lead.json → three entries (msg-1, DONE, idle_notification), all "read": true.
  4. Emit a text-only assistant response (no tool calls). stop_reason=end_turn.
  5. 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

View original on GitHub ↗

This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗