Repository navigation
acpmux: a chat's first task from another agent runs as its prompt, not an inbox notice - #28413
Conversation
…ts prompt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3db671e49c5e0014c84a
…t an inbox notice The first message another agent sends a chat that never ran a turn, with none running and nothing in its queue or inbox (lane-place --prompt-file, acpmux new with a prompt), runs as that prompt and names the chat's delegator. Later messages, and any to a chat that ran a turn, keep the inbox. A lane's brief came to it as a notice of its brief. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3db671e49c5e0014c84a
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
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)
|
|
Found 6 test failures on Blacksmith runners: Failures
|
…prompt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3db671e49c5e0014c84a
…when its turn starts Review findings at 5206c3d: - two messages to a new chat could both pass first_work before either installed a turn; now the first takes a hold under the inbox's lock (Inbox::first, FirstWork) for as long as it runs, and a refused one lets go; - the delegator was noted before admission, so a refused first message kept it; now it is the sender of the chat's first turn, set as that turn starts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3db671e49c5e0014c84a
|
Owned by an agent session · session |
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
A message the inbox takes while a chat's first work waits to start must not become the chat's delegator. The concurrent first-message test reads only the answer of the message that ran (the other waits for a notice), and send_async's provenance test runs a turn on its receiver first so the message still goes to the inbox. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3db671e49c5e0014c84a
first_work read the chat idle, then took the hold: a first work that ran and let go in between left a stale idle reading, so a second message ran as a prompt too. The hold now comes first and the chat is read under it. The first turn's sender is now its delegator even over one an inbox message named while the first work waited to start. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3db671e49c5e0014c84a
Another agent's message that queues behind a chat's first work joins its turn as a digest; the first work's sender must still be the delegator. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3db671e49c5e0014c84a
A digest's provenance has no sender, so a first work that other agents' queued messages joined named no delegator. Read the head prompt's own provenance. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3db671e49c5e0014c84a
A test-only seam pauses a first work once it holds the chat, before it queues. A blocker that comes then must not wake a notice turn ahead of it, and a person's prompt that takes the first turn must leave the first work's sender the delegator. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3db671e49c5e0014c84a
Between taking the first work and starting its turn, an inbox wake could run a notice turn first, and a person's prompt that went first left the first work's sender no delegator. The inbox now looks again after the debounce while a first work holds the chat with no turn, and a prompt that held the first work names its sender the delegator whichever turn it is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3db671e49c5e0014c84a
…its mark pause and turns were called but never defined (a rewrite of pause_start missed after rustfmt rewrapped it). The person-first test now holds the person's turn at a gate, so the first work waits in the queue, and checks that its queued record marks it as the first work: the provenance a restart rebuilds it from. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3db671e49c5e0014c84a
The delegator of a first work a person's prompt went ahead of came from the in-memory hold, which a restart loses while the first work waits in the queue. Its provenance now carries firstWork, which the queue keeps across a restart and no client can send (prompt_provenance keeps only the fields it names), and the turn start reads that. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3db671e49c5e0014c84a
While the first work holds the turn's lock with no turn shown, the next prompt waits on the lock without queueing; only the one after it queues and joins the first work's turn. The test sends both, as the reviewer's interleaving does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3db671e49c5e0014c84a
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
A first work queued behind a person's prompt comes back from a restart with its firstWork mark but no in-memory hold. A blocker the inbox took before the restart must not send a notice ahead of it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3db671e49c5e0014c84a
The inbox held notices only while a first work's in-memory hold was set, which a restart does not rebuild. It now also holds them while a waiting queued prompt carries the firstWork mark, until that prompt's turn takes the notice or it leaves the queue. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3db671e49c5e0014c84a
|
Review (the owner lane's independent review, at its land request): SHIP at 79388b6 Recorded by lane-gh-broker from this PR's owner lane, which asked to land this exact head after its independent review. next-autoland lands it once every red is the base's; a BLOCK at this head, a HOLD line or a hold label still stops it. |
|
Shipped in:
|
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Summary
Since #27686, another agent's plain message goes to a chat's inbox, and the chat hears of it through a notice. That includes the very first prompt a new chat gets: the task
lane-place --prompt-fileoracpmux newwith a prompt sends as an agent. A new lane was getting "[inbox] New messages from other agents in 1 thread…" where its brief should have been, so it read a thread instead of its task. #28369 fixed this for review boxes only.With this change a chat's first work runs as its prompt, whatever kind of chat it is:
hub/inbox.rsfirst_work: the chat never ran a turn, none is running, and nothing waits in its queue or inbox. It takes a hold (Inbox::first) under the inbox lock first and reads the chat after, so of two first messages sent at once only one runs as the prompt (the other goes to the inbox), and a first work that already ran is seen. TheFirstWorkguard keeps the hold for the life of that prompt and releases it on drop.inbox_route: another agent's message that is a chat's first work skips the inbox and runs as that prompt.inbox_review: while a first work holds the chat, or waits in its queue with thefirstWorkmark (one a person's prompt went ahead of, or one a restart rebuilt with no hold), and no turn runs, no notice goes ahead of it. The inbox looks again after the wake debounce, until the first work's turn takes the notice, its hold is let go, or it leaves the queue.hub/turns.rs: the sender of the prompt that starts a chat's first turn becomes its delegator, set as the turn starts (for a prompt that held the first work, even when a person's prompt went first), not when the message is routed. A first work's provenance carriesfirstWork: true, which its queue entry keeps across a restart and no client can send (prompt_provenance.rskeeps only the fields it names): a refused prompt names no delegator, and a message the inbox took while the first work waited to start does not stay the delegator. It is read from that prompt's own provenance, so other agents' messages that join its turn as a digest (digest.rs) don't hide it.inbox.rssay why.Follows #28369 and #27686.
Testing
Tests in
tests/hub_integration/agent_inbox.rs(the test commit comes before the fix):an_idle_chat_runs_its_first_work_and_wakes_for_a_blocker_only(was…wakes_for_a_blocker_and_for_its_first_work_only): the first message runs as promptt-1with no notice, and its sender getsend_turn. The next message goes to the inbox, and a blocker still wakes the chat with one notice.only_the_first_message_to_a_new_chat_runs_as_its_prompt: a second message that comes while the first one's turn runs goes to the inbox.of_two_first_messages_at_once_only_one_runs_as_the_prompt: two messages sent at once to a new chat; exactly one goes to the inbox, and the other's turn ends withend_turn.src/hub/first_work_tests.rsa_message_the_inbox_takes_before_the_first_work_starts_is_not_the_delegator: the first work is paused after admission (prompt_start_seam), another session's message goes to the inbox, and after release the delegator is the first sender.a_message_that_joins_the_first_works_turn_is_not_the_delegatordoes the same with a queued message that joins the first turn as a digest. Two more pause the first work right after it takes the chat (first_work_seam, tests only):a_blocker_that_comes_before_the_first_work_queues_waits_for_it(no notice turn runs before the brief) anda_persons_prompt_before_the_first_work_queues_leaves_its_sender_the_delegator(the first work waits in the queue behind a gated person's turn, and itsqueuedrecord carries the mark).a_first_work_a_restart_rebuilt_keeps_notices_behind_itrestarts a durable hub with that first work queued and a blocker in the inbox, and checks that no notice goes ahead of it.tests/send_async.rsa_no_wait_send_from_a_session_names_its_proven_senderruns a turn on its receiver first, so the message it checks still goes to the inbox.Run at head
79388b6(cmux-ci fast checks, run 41103630462); base comparisons at base tipc5359cd(run 41097908556) and base364c30d(run 41092748743):79388b6cargo test -p acpmux --lib first_workinbox.rsfrom the test commit6645c9a:a_first_work_a_restart_rebuilt_keeps_notices_behind_itfails; with the source of9fb7f36:a_persons_prompt_before_the_first_work_queues_leaves_its_sender_the_delegatorfailscargo test -p acpmux --test hub_integrationc5359cd, at the same lines (below); with base364c30dsource,an_idle_chat_runs_its_first_work_and_wakes_for_a_blocker_only,only_the_first_message_to_a_new_chat_runs_as_its_promptandof_two_first_messages_at_once_only_one_runs_as_the_promptfailcargo test -p acpmux --lib inboxcargo test -p acpmux --test send_asynccargo clippy -p acpmux --all-targets -- -D warnings-p acpmux -p cmux-tui --lib --bins)indexing_slicingmatches the base tipObserved / Not run
79388b6.c5359cd, where each fails the same way:inbox_digest::a_digest_refused_before_its_turn_puts_every_message_back_and_a_restart_keeps_them(inbox_digest.rs:666),carried_turns::a_carried_turn_after_a_carried_turn_has_its_own_cap(carried_turns.rs:502, no cap notice),agent_inbox::an_inbox_message_from_before_the_review_tag_never_steers_into_the_review(agent_inbox.rs:73, one of three prompts unanswered). None is a crash. The last passed at3e8ff30and fails on base since acpmux: preserve retries and adopted prompt outcomes across restart (takes over #26981) #27921.inbox_digest::messages_drained_twice_follow_the_newest_digest_at_a_restartfailed once in four head runs and in neither base run. Its chat is mid-turn (busy) when the messages arrive, so this PR's first-work path never applies there.release-notes/next/highlights/chief-name-in-agent-chats.md, which this PR does not touch; Guard status rolls it up. The CI panic ratchet's acpmux counts are base drift (above).lane-placelaunch against a daemon built from this branch.Changelog
Fixed: a new acpmux chat's first task from another agent runs as its prompt, not as an inbox notice.
Agent session: agent session
🤖 Generated with Claude Code