acpmux: other agents' messages never go into the person's own turn; they run as a turn of their own after it
Merged
TE
5d ago+283+283+012345678901234567890123456789-32-32-01234567890123456789
Leo: "why do the x messages from y sessions seem to always piggyback on my messages instead of going on their own"
What was wrong
- Inbox notices. When the person's prompt started a turn,
inbox_prompt_writtenraninbox_review, which saw a running turn that steers and steered the pending notice into it right away. In one coordinator log that happened 57 times, each notice written 1 ms after the person's prompt. So "[inbox] New messages from other agents…" landed in the person's turn, right under their words. - Other agents' steers. Messages that aren't inbox items (peer and mesh-relay forwards, team messages,
cmux agent message) steered into the person's turn at its next tool step. Claude Code's adapter batched them as "N messages from M sessions arrived during this step", the line Leo is quoting.
What changes
TurnInfo::personsrecords whether the person's own prompt started the turn (inbox::persons_prompt: no agent sender, and not a notice, an inbox digest or the turn cap's notice). It is set inrun_prompt_joined. Carried turns, restored host turns and test turns default tofalse, which is the old behaviour.- Inbox (
hub/inbox.rs):inbox_reviewandnotice_heldnever steer a notice into the person's turn. Messages held back that way setInbox::held_by_person, so they go in as one turn of their own as soon as the chat is idle, whatever their class. Before, an FYI or an action from an unrelated lane would have waited the 10-minute flush. The flag clears when a notice goes. - Other agents' steers (
hub/turns.rs, checked again as the steer is written insteer_end.rs): another agent's steer queues behind the person's turn instead of going in, and itsinterruptstops nothing. A steer-only one is refused withsteer.unavailable/personsTurn. The person's own steers, Send now, and an inbox digest the agent asked to drain still go in. - The person's prompt runs exactly as written. The batch runs as its own turn after it, so the transcript draws it as its own inbox row (
InboxNoticeCard), never inside the person's turn. No web change was needed. Older logs stay as they are.
Tests (tests/hub_integration/agent_inbox.rs)
- New:
a_notice_never_goes_into_the_persons_turn_and_runs_on_its_own_after_it(fails on base: the notice steers in at the turn's start). - New:
another_agents_steer_waits_for_the_persons_turn(fails on base: the steer goes in). a_queued_prompt_never_waits_behind_agent_messages(#27566's case) used to assert the old behaviour, that the notice goes into the queued person's turn. It now asserts that the notice runs as its own turn after that turn and that the person's words are unchanged.three_hundred_agent_messages_go_in_as_one_notice_at_the_next_tool_stepandanother_agents_steer_goes_in_when_no_prompt_waitsnow run under a turn started by another agent's first work, since steering into a running turn is still how a non-person turn takes notices.
Risk: this changes delivery (a state machine). A coordinator no longer reads lane messages in the middle of a long turn Leo started; it reads them right after. That is the ask.
Agent session: agent session
🤖 Generated with Claude Code
![View with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png)
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Other agents' messages no longer piggyback on the person's turn. Inbox notices used to steer into the person's turn the moment its prompt was written, and other agents' steers went in at its next tool step. A turn the person's own prompt started now takes neither; both wait for the turn's end and run as a turn of their own, immediately.
TurnInfo::personsrecords whether the person's own prompt started the turn. A retry, a continuation, and other acpmux-owned prompts are not the person's; carried, restored, and test turns default tofalse.- This applies only while the agent inbox is on; with
agentInboxoff, agents' sends steer in as before. turn_startednow logspersons, so a restart and a new harness keep the turn the person's own.- Notices held back by the person's turn are due as soon as the chat is idle, whatever their class, instead of waiting the 10-minute flush; messages already waiting as that turn starts are held back the same way. Held-back messages never steer into a turn carried on from it, and a Stop that cuts the turn still releases them.
- Another agent's steer-only prompt is refused with
steer.unavailable/personsTurn; itsinterruptstops nothing. - The person's own steers, Send now, agent-requested inbox digests, and a sub-session family message from the turn's own chat still go in.
- Delivery changes: a coordinator reads lane messages right after the person's turn ends, not in its middle.