Repository navigation
acpmux: other agents' messages never go into the person's own turn; they run as a turn of their own after it - #30157
Conversation
…hey run as a turn of their own after it Leo: "why do the x messages from y sessions seem to always piggyback on my messages instead of going on their own" An inbox notice steered into the person's turn the moment its prompt was written (1 ms after it in the logs), and other agents' steers went in at its next tool step, batched as "N messages from M sessions arrived during this step". Now a turn the person's own prompt started (TurnInfo::persons) takes neither: notices and steers wait for its end and go in as a turn of their own, at once (messages the person's turn held back are due whatever their class). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-713fc0ea5e33d227b29f
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
red: cmux-next checks (god files, concurrency, crash safety, l10n); NEW: Shipped in:
|
…mt the tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-713fc0ea5e33d227b29f
There was a problem hiding this comment.
1 issue found and verified against the latest diff
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="cmux-tui/crates/acpmux/src/hub/steer_end.rs">
<violation number="1" location="cmux-tui/crates/acpmux/src/hub/steer_end.rs:258">
P2: A steer-only request that races with the person's turn start returns the generic no-turn error because this hold is not recorded for the later error mapping. Track the person-turn hold and return `persons_turn_steer_refused()` in that path.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| } else if !digest && super::inbox::persons_prompt_waits(session) { | ||
| prompt_held.store(true, std::sync::atomic::Ordering::SeqCst); | ||
| true | ||
| } else if !digest && super::inbox::persons_turn(session) { |
There was a problem hiding this comment.
P2: A steer-only request that races with the person's turn start returns the generic no-turn error because this hold is not recorded for the later error mapping. Track the person-turn hold and return persons_turn_steer_refused() in that path.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/acpmux/src/hub/steer_end.rs, line 258:
<comment>A steer-only request that races with the person's turn start returns the generic no-turn error because this hold is not recorded for the later error mapping. Track the person-turn hold and return `persons_turn_steer_refused()` in that path.</comment>
<file context>
@@ -255,6 +255,9 @@ impl Hub {
} else if !digest && super::inbox::persons_prompt_waits(session) {
prompt_held.store(true, std::sync::atomic::Ordering::SeqCst);
true
+ } else if !digest && super::inbox::persons_turn(session) {
+ // The person's own turn (`inbox.rs`): it goes in after it.
+ true
</file context>
There was a problem hiding this comment.
4 issues found across 8 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="cmux-tui/crates/acpmux/src/hub/steer_end.rs">
<violation number="1" location="cmux-tui/crates/acpmux/src/hub/steer_end.rs:258">
P2: A steer-only agent message refused by this new `persons_turn` check at write time gets the generic 'steer: no turn is running' error, not the `steer.unavailable` / `personsTurn` refusal that `turns.rs` returns. It also skips the `steer_withdrawn` log that the `promptWaits` path writes for a logged steer that never went. Track the person's-turn hold with its own flag, like `prompt_held`, and return `persons_turn_steer_refused()` and log `steer_withdrawn` when it is set.</violation>
</file>
<file name="cmux-tui/crates/acpmux/src/hub/turns.rs">
<violation number="1" location="cmux-tui/crates/acpmux/src/hub/turns.rs:495">
P1: Agent-pair delivery still cancels the captured running turn after accepting a queued message, and it skips only review boxes. Add the person-turn guard to that cancellation path so another agent cannot stop the person’s turn.</violation>
<violation number="2" location="cmux-tui/crates/acpmux/src/hub/turns.rs:495">
P1: This interrupt decision uses a stale turn snapshot, so an agent message can still cancel a person’s turn if that turn starts while queue admission waits. Recheck `persons_turn` at the cancellation boundary before honoring `interrupt`.</violation>
<violation number="3" location="cmux-tui/crates/acpmux/src/hub/turns.rs:597">
P2: An auto-resume continuation of a turn another agent started is classed as the person's own turn. `agent_sender` ignores `auto_resume` provenance, so `persons_prompt` returns true and notices and other agents' steers wait through it. That changes existing behavior for agent-started turns. Derive the turn's agent from the cut turn's sender (`CutTurn.from`) so `persons_prompt` sees it, or confirm that auto-resume and subagent retry turns are meant to count as the person's.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| // agent's message stop a review box's review, or go ahead of the person's prompt that | ||
| // waits (`inbox.rs`). | ||
| interrupt: opts.interrupt && !capped && !review_held && !prompt_held, | ||
| interrupt: opts.interrupt && !capped && !review_held && !prompt_held && !persons_held, |
There was a problem hiding this comment.
P1: This interrupt decision uses a stale turn snapshot, so an agent message can still cancel a person’s turn if that turn starts while queue admission waits. Recheck persons_turn at the cancellation boundary before honoring interrupt.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/acpmux/src/hub/turns.rs, line 495:
<comment>This interrupt decision uses a stale turn snapshot, so an agent message can still cancel a person’s turn if that turn starts while queue admission waits. Recheck `persons_turn` at the cancellation boundary before honoring `interrupt`.</comment>
<file context>
@@ -481,7 +492,7 @@ impl Hub {
// agent's message stop a review box's review, or go ahead of the person's prompt that
// waits (`inbox.rs`).
- interrupt: opts.interrupt && !capped && !review_held && !prompt_held,
+ interrupt: opts.interrupt && !capped && !review_held && !prompt_held && !persons_held,
interrupt_turn: opts.interrupt_turn.as_deref(),
provenance: opts.provenance.as_ref(),
</file context>
| // agent's message stop a review box's review, or go ahead of the person's prompt that | ||
| // waits (`inbox.rs`). | ||
| interrupt: opts.interrupt && !capped && !review_held && !prompt_held, | ||
| interrupt: opts.interrupt && !capped && !review_held && !prompt_held && !persons_held, |
There was a problem hiding this comment.
P1: Agent-pair delivery still cancels the captured running turn after accepting a queued message, and it skips only review boxes. Add the person-turn guard to that cancellation path so another agent cannot stop the person’s turn.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/acpmux/src/hub/turns.rs, line 495:
<comment>Agent-pair delivery still cancels the captured running turn after accepting a queued message, and it skips only review boxes. Add the person-turn guard to that cancellation path so another agent cannot stop the person’s turn.</comment>
<file context>
@@ -481,7 +492,7 @@ impl Hub {
// agent's message stop a review box's review, or go ahead of the person's prompt that
// waits (`inbox.rs`).
- interrupt: opts.interrupt && !capped && !review_held && !prompt_held,
+ interrupt: opts.interrupt && !capped && !review_held && !prompt_held && !persons_held,
interrupt_turn: opts.interrupt_turn.as_deref(),
provenance: opts.provenance.as_ref(),
</file context>
| } else if !digest && super::inbox::persons_prompt_waits(session) { | ||
| prompt_held.store(true, std::sync::atomic::Ordering::SeqCst); | ||
| true | ||
| } else if !digest && super::inbox::persons_turn(session) { |
There was a problem hiding this comment.
P2: A steer-only agent message refused by this new persons_turn check at write time gets the generic 'steer: no turn is running' error, not the steer.unavailable / personsTurn refusal that turns.rs returns. It also skips the steer_withdrawn log that the promptWaits path writes for a logged steer that never went. Track the person's-turn hold with its own flag, like prompt_held, and return persons_turn_steer_refused() and log steer_withdrawn when it is set.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/acpmux/src/hub/steer_end.rs, line 258:
<comment>A steer-only agent message refused by this new `persons_turn` check at write time gets the generic 'steer: no turn is running' error, not the `steer.unavailable` / `personsTurn` refusal that `turns.rs` returns. It also skips the `steer_withdrawn` log that the `promptWaits` path writes for a logged steer that never went. Track the person's-turn hold with its own flag, like `prompt_held`, and return `persons_turn_steer_refused()` and log `steer_withdrawn` when it is set.</comment>
<file context>
@@ -255,6 +255,9 @@ impl Hub {
} else if !digest && super::inbox::persons_prompt_waits(session) {
prompt_held.store(true, std::sync::atomic::Ordering::SeqCst);
true
+ } else if !digest && super::inbox::persons_turn(session) {
+ // The person's own turn (`inbox.rs`): it goes in after it.
+ true
</file context>
|
Found 6 test failures on Blacksmith runners: Failures
|
…rvives a restart; messages waiting as the person's turn starts are due at its end From the review at 41bd251: subagent_retry and auto_resume prompts carry no agent, so they counted as the person's turns; turn_started now logs persons and adoption restores it; and inbox_turn_started marks messages already waiting as held by the person. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-713fc0ea5e33d227b29f
…the inbox is on With agentInbox off, agents' sends steer into the person's turn as before (tests/send_steer.rs, Leo 2026-10-25), which the full suite at 41bd251 caught. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-713fc0ea5e33d227b29f
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-713fc0ea5e33d227b29f
|
Review (codex-review-box/gpt-6-astra high, lane-place): BLOCK at 51ef316 BLOCK at 51ef316 Visual verdict: no UI changes; no images needed.
BLOCK at 51ef316 |
- A person's turn a Stop kept from its agent ends with inbox_turn_ended, so what the inbox held for it goes (it waited for no timer). - A new harness keeps the open turn's persons mark (host_started no longer clears it). - Messages a person's turn held back never steer into a turn carried on from it: they go as a turn of their own once the chat is idle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-713fc0ea5e33d227b29f
There was a problem hiding this comment.
1 issue found across 3 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="cmux-tui/crates/acpmux/src/hub/inbox.rs">
<violation number="1" location="cmux-tui/crates/acpmux/src/hub/inbox.rs:1017">
P2: `held_by_person` is only cleared when a notice is delivered. If the held messages are withdrawn before that, the flag stays set with nothing held. A later message in a carried turn then waits for that turn to end instead of steering in. Clear the flag when no new message remains, or check it only when a held message is still new.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| held_by_person = turn.persons; | ||
| if !steers | ||
| || turn.persons | ||
| || inbox.held_by_person |
There was a problem hiding this comment.
P2: held_by_person is only cleared when a notice is delivered. If the held messages are withdrawn before that, the flag stays set with nothing held. A later message in a carried turn then waits for that turn to end instead of steering in. Clear the flag when no new message remains, or check it only when a held message is still new.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/acpmux/src/hub/inbox.rs, line 1017:
<comment>`held_by_person` is only cleared when a notice is delivered. If the held messages are withdrawn before that, the flag stays set with nothing held. A later message in a carried turn then waits for that turn to end instead of steering in. Clear the flag when no new message remains, or check it only when a held message is still new.</comment>
<file context>
@@ -1009,10 +1009,12 @@ impl Hub {
held_by_person = turn.persons;
if !steers
|| turn.persons
+ || inbox.held_by_person
|| capped(&meta, turn.started_at, now)
|| prompt_waits(session)
</file context>
|
Review (codex gpt-6-astra high, review box): SHIP at bee457c Round 3, a delta review of 51ef316..bee457c after the round-2 BLOCK. Reviewer's words:
|
… turn; the turn-cap tests cover the pre-inbox path CI at bee457c (acpmux tests): a sub-session's next_step result (carried_turns) and a family steer (full_access_bound) were held out of the person's turn that started them. A family message is the chat's own sub-session, not another session piggybacking: it goes in. The turn_cap tests steer agents' sends into a person's turn, so they run with agentInbox off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-713fc0ea5e33d227b29f
|
Review (codex gpt-6-astra high, review box): SHIP at ccd05c9 Delta review of bee457c..ccd05c9 (sub-session family messages still go into the person's turn; the turn_cap tests run with agentInbox off), after the SHIP at bee457c. Reviewer's words:
|
|
Red classification at ccd05c9: It is a base red. The same test fails the same way at the feat-cmux-next tip 2a2aa0c and passes at this PR's head. This PR changes only |
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
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_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.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.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.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.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)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).another_agents_steer_waits_for_the_persons_turn(fails on base: the steer goes in).a_queued_prompt_never_waits_behind_agent_messages(fix(acpmux): queue automatic messages after the original steering window #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
Need help on this PR? Tag
@codesmith-botwith 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.agentInboxoff, agents' sends steer in as before.turn_startednow logspersons, so a restart and a new harness keep the turn the person's own.steer.unavailable/personsTurn; itsinterruptstops nothing.Written for commit ccd05c9. Summary will update on new commits.