[BUG] Agent idle transition doesn't drain queued inbox — idle notification races SendMessage, agents park on undelivered instructions
Summary
A background agent's idle transition does not drain its queued inbox: messages sent to the agent shortly before it finishes its turn sit undelivered while the agent emits an idle_notification and parks. The dispatcher sees an idle signal whose implicit state predates the queued messages, and the messages are only processed after a further manual "ping" message forces a resume.
This is the dispatcher→subagent mirror of #79570 (closed COMPLETED, which covered the subagent→dispatcher wake direction). The durable-mailbox half works — nothing is lost — but the wake half at the idle boundary is still racy in this direction.
Observed (2026-07-31, v2.1.220, Linux)
- Background agent (spawned via
Agentwithname, general-purpose) completed a unit of work and sent its closing report via SendMessage. - Dispatcher replied with two follow-up instruction messages (
SendMessage→ success, "Message sent to <agent>'s inbox"). - Agent emitted
{"type":"idle_notification","idleReason":"available"}— after both sends succeeded. - Agent stayed idle; neither queued message was processed.
- A third, minimal "ping" message resumed the agent, which then processed all three messages normally.
The same race recurs routinely (the operator's words: "this happens all the time") whenever a send lands near the recipient's turn end — which is the most likely moment for a dispatcher reply, since it's right after the agent's report arrives.
Expected
Any of:
- the idle transition drains the inbox before emitting
idle_notification(level-triggered check, not edge-triggered wake), or - a queued message delivered to an idle agent triggers a resume (idle re-poll), or
- delivery receipts, so the sender can distinguish "queued while running" from "queued while idle" and know a nudge is needed.
Workaround in use
Protocol-level: treat any idle notification as potentially stale, and send a minimal ping to disambiguate (never re-send the full instruction). Works, but costs an extra round-trip on exactly the hottest dispatch path, and misreading a stale idle as current state remains an easy operator error.
---
🤖 Filed by Claude Code (Opus 5) on behalf of the account owner, from a live dispatch session where the race occurred.