Skip to content

acpmux: a chat's first task from another agent runs as its prompt, not an inbox notice - #28413

Merged
teamleaderleo merged 16 commits into
feat-cmux-nextfrom
lane/first-prompt-not-inbox
Oct 30, 2026
Merged

teamleaderleo merged 16 commits into
feat-cmux-nextfrom
lane/first-prompt-not-inbox

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator

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-file or acpmux new with 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.rs first_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. The FirstWork guard 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 the firstWork mark (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 carries firstWork: true, which its queue entry keeps across a restart and no client can send (prompt_provenance.rs keeps 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.
  • Messages after the first, and any message to a chat that has run a turn, keep acpmux: every chat's inbox; another agent's plain send is never a turn of its own (A1) #27686's inbox behaviour, as before.
  • The module docs at the top of inbox.rs say 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 prompt t-1 with no notice, and its sender gets end_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 with end_turn.
  • src/hub/first_work_tests.rs a_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_delegator does 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) and a_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 its queued record carries the mark). a_first_work_a_restart_rebuilt_keeps_notices_behind_it restarts 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.rs a_no_wait_send_from_a_session_names_its_proven_sender runs a turn on its receiver first, so the message it checks still goes to the inbox.
  • The review-box test from acpmux: review boxes get no inbox notice and no other agent's steer #28369 now runs a turn on its ordinary chat before checking that the message goes to the inbox.

Run at head 79388b6 (cmux-ci fast checks, run 41103630462); base comparisons at base tip c5359cd (run 41097908556) and base 364c30d (run 41092748743):

Command Head 79388b6 Red check
cargo test -p acpmux --lib first_work 5 passed with inbox.rs from the test commit 6645c9a: a_first_work_a_restart_rebuilt_keeps_notices_behind_it fails; with the source of 9fb7f36: a_persons_prompt_before_the_first_work_queues_leaves_its_sender_the_delegator fails
cargo test -p acpmux --test hub_integration 617 passed, 3 failed the same 3 fail on base tip c5359cd, at the same lines (below); with base 364c30d source, an_idle_chat_runs_its_first_work_and_wakes_for_a_blocker_only, only_the_first_message_to_a_new_chat_runs_as_its_prompt and of_two_first_messages_at_once_only_one_runs_as_the_prompt fail
cargo test -p acpmux --lib inbox 23 passed
cargo test -p acpmux --test send_async 16 passed
cargo clippy -p acpmux --all-targets -- -D warnings clean
panic ratchet, as CI runs it (-p acpmux -p cmux-tui --lib --bins) exit 0; acpmux indexing_slicing matches the base tip

Observed / Not run

  • Observed: the table above; rustfmt clean. Reviewed by a different model (gpt-6-astra): SHIP at 79388b6.
  • Base reds, each compared by test name against base tip 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 at 3e8ff30 and 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_restart failed 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.
  • CI reds outside acpmux: release-notary fails on 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).
  • Not run: a real lane-place launch 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

teamleaderleo and others added 2 commits October 30, 2026 15:59
…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
@cursor

cursor Bot commented Oct 30, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot 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.

@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review 79388b6: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr28413 -f ref=lane/first-prompt-not-inbox -f platform=macos -f backend_mode=local

@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 30, 2026
@blacksmith-sh

blacksmith-sh Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Found 6 test failures on Blacksmith runners:

Failures

Test View Logs
main.DeveloperIdAppTests.test_download_prints_the_active_profile_only/
test_download_prints_the_active_profile_only
View Logs
stubs: /System/Library/Frameworks/Security.framework/Versions/A/Security has no stub/
Security has no stub
View Logs
stubs: CoreFoundation.tbd: install-name /System/Library/Frameworks/
CoreFoundation.framework/Versions/A/CoreFoundation, missing 1: ['_CFRetain']/
CoreFoundation, missing 1: ['_CFRetain']
View Logs
stubs: CoreFoundation.tbd: install-name /System/Library/Frameworks/
CoreFoundation.framework/Versions/A/CoreFoundation, missing 1: ['_log2']/
CoreFoundation, missing 1: ['_log2']
View Logs
test_every_listed_hook_is_one_glaeda_released_with_the_check (main.SigningHooksInSt
epWithGlaedaTests.test_every_listed_hook_is_one_glaeda_released_with_the_check)/
test_every_listed_hook_is_one_glaeda_released_with_the_check (main.SigningHooksInSt
epWithGlaedaTests.test_every_listed_hook_is_one_glaeda_released_with_the_check)
View Logs
test_the_repository_highlights_validate (main.DigestTests.test_the_repository_highl
ights_validate)/
test_the_repository_highlights_validate (main.DigestTests.test_the_repository_highl
ights_validate)
View Logs

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

teamleaderleo and others added 2 commits October 30, 2026 16:04
…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
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Owned by an agent session · session

@cursor

cursor Bot commented Oct 30, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot 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.

teamleaderleo and others added 8 commits October 30, 2026 16:14
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
@cursor

cursor Bot commented Oct 30, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot 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.

teamleaderleo and others added 2 commits October 30, 2026 17:47
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
@teamleaderleo

teamleaderleo commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator Author

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.

@teamleaderleo
teamleaderleo merged commit d559a73 into feat-cmux-next Oct 30, 2026
202 of 211 checks passed
@teamleaderleo
teamleaderleo deleted the lane/first-prompt-not-inbox branch October 30, 2026 18:41
@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

This branch was previously deployed

3 inactive deployments
artifacts — 79388b6c Deployed Oct 30, 2026 by teamleaderleo via add the Windows daemon to the tree #84392
sentry-dsyms — 79388b6c Deployed Oct 30, 2026 by teamleaderleo via upload cmux-tui dSYMs to Sentry #84392
ci-routing-agent — 79388b6c Deployed Oct 30, 2026 by teamleaderleo via cmux-tui tree preflight #84392
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant