Repository navigation
acpmux: chat multiplayer A: person author, provenance, answeredBy, queue.not_author - #25268
Conversation
|
Owned by hb-acpmux on big-red · session |
|
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); no finished base run on 943bb82 to compare; do not merge until green; see lane-rules |
|
Found 9 test failures on Blacksmith runners: Failures
|
|
Bead |
|
|
…swers Red first: the author is not read yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chat multiplayer, the daemon half (hq#4389): _meta.acpmux.author from the unix
socket or a forwarding peer is stamped as a person_message provenance on
user_message, queued and steer records, kept by the same author's edit and
across a restart; named in answeredBy {id, name} on permission_decision, and
told to a losing answer in not_found data {answeredBy, outcome}; and checked by
queue_edit, queue_remove and steer withdraw (queue.not_author).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…old to a later one Review round 1: supersede.rs matches senders by agent name, so every person counted as the owner; and a group answer removed the asks before the settled store knew of them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
96f8002 to
07c04ae
Compare
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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. |
|
|
Review round 2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…efuse an author beside a provenance Review: the author check snapshot left a gap before the change, and a prompt naming both an author and an agent provenance ran as the agent's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…a peer Review: the forward dropped the provenance and kept the author. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hange acpmux now records a person author on queued prompts (#25268) and refuses another author's queue edit with queue.not_author. The queue entry keeps the author from its queued or queue_updated record and through acpmux's list; its row shows an inline author chip and offers no Steer, Delete, edit, move or menu, and its keys only walk. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-847de1e22cadc71cd076
|
Shipped in:
|
…o change (#25357) * cmux NEXT chat: shared chat faces (author, presence, typing, answered by), render only Leo, 2026-10-26: "we can still do the regular chat multiplayer". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16f9b-2fcb-7c72-b06e-af8fce00adcb&host=cosmic-gray * shared chat faces: Chief adapter, gallery Chief variant, a11y and coverage fixes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16f9b-2fcb-7c72-b06e-af8fce00adcb&host=cosmic-gray * shared chat faces: format, covers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16f9b-2fcb-7c72-b06e-af8fce00adcb&host=cosmic-gray * shared chat faces: adapter reads hb-acpmux's final shapes (answeredBy {id,name}, own id instead of self) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16f9b-2fcb-7c72-b06e-af8fce00adcb&host=cosmic-gray * shared chat faces: typing line on the composer's edge; one letter on a small face Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16f9b-2fcb-7c72-b06e-af8fce00adcb&host=cosmic-gray * shared chat faces: names lose hidden characters and are bounded in both adapters; ids stay whole Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16f9b-2fcb-7c72-b06e-af8fce00adcb&host=cosmic-gray * shared chat faces: format Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16f9b-2fcb-7c72-b06e-af8fce00adcb&host=cosmic-gray * shared chat faces: the Chief adapter moves to the Home mount PR (size) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16f9b-2fcb-7c72-b06e-af8fce00adcb&host=cosmic-gray * multiplayer: a small face's one letter is a whole grapheme initials(name).slice(0, 1) cut the joined string by UTF-16 units, so a name starting with an astral character drew a lone surrogate. initials takes the count and slices graphemes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-847de1e22cadc71cd076 * multiplayer: a teammate's queued prompt names them and is theirs to change acpmux now records a person author on queued prompts (#25268) and refuses another author's queue edit with queue.not_author. The queue entry keeps the author from its queued or queue_updated record and through acpmux's list; its row shows an inline author chip and offers no Steer, Delete, edit, move or menu, and its keys only walk. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-847de1e22cadc71cd076 * fmt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-847de1e22cadc71cd076 * multiplayer: gallery variants for a teammate's queued prompt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-847de1e22cadc71cd076 * queue author: no move passes a teammate's prompt; reducer and menu paths tested A move shifts every row it passes, so Alt+arrows and the menu's moves now stop at a teammate's prompt (the menu shows them disabled). queueAuthor.test.ts drives the pane through queued, queue_updated, queue_order, acpmux's list and the re-read once a sender's chat is listed, and checks the author each time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-847de1e22cadc71cd076 * multiplayer: a name loses every invisible format character, Unicode tags too Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-847de1e22cadc71cd076 * queue author: kept by prompt id, so an attach's page that starts after the queued record keeps it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-847de1e22cadc71cd076 * multiplayer: spacing and sizes on the design scale Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-847de1e22cadc71cd076 * tests: formatted Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-847de1e22cadc71cd076 * Keep a teammate's queue author through a kept-tail redraw A switch back to a chat (or a new tab) draws it from its kept tail before the attach lands. When the authored queued record had fallen out of that tail, the author-less session list won and the row became editable. The kept queue's authors now seed the author cache. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-847de1e22cadc71cd076 * Queue author: an open edit closes on a teammate's row; a cold open takes the listed author An edit opened while the row named no author closes, unsaved, once the authored record arrives; its text goes back to the composer, and save refuses a teammate's prompt. A cold open (the authored record older than the attach page) takes the author acpmux's queue list names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-847de1e22cadc71cd076 * Queue author: a listed null author is the owner's; a native menu picked after a row turned a teammate's does nothing Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-847de1e22cadc71cd076 * Queue: an edit pulled back after its images load rechecks the row is still ours Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-847de1e22cadc71cd076 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Chat multiplayer, the daemon half, PR A of 2 (author, provenance, answeredBy, queue checks). PR B (hb-mp-presence) adds presence and typing.
The ask, word for word (subsession-card, relaying Leo): "A new ask for acpmux: multiplayer for regular agent chats. Leo, word for word (2026-10-26): "we can still do the regular chat multiplayer" and "we can just send some codexes to do whatever". The design doc is https://github.com/manaflow-ai/cmuxterm-hq/pull/4389 (plans/cmux-next/chat-multiplayer/DESIGN.md), under Codex review. My helper chat-multiplayer is building the webview, render-only behind an adapter. The daemon half is yours: 1. Provenance kind
person_message {from, fromName, login, node}. It comes in as_meta.acpmux.authorand is accepted only from unix-socket callers. Record it on user_message, queued and steer records, and keep it through peer forwarding. 2.answeredBy {person, name}on permission_decision, taken fromauthoron permission_respond. A losing answer's not_found error carriesdata {answeredBy, outcome}(first answer wins). 3. An author check on queue_edit, queue_remove and steer_withdraw: refuse withqueue.not_author. 4. Ephemeral presence, never stored in the log:_acpmux/typing {sessionId, on}(at most 2 per second, expires after 8 s); a_acpmux/presence {sessionId, participants:[{id,name,viewing,typing,self?}]}notification, andpresencein the attach reply; viewing from_acpmux/view, per author. Person ids aretailnet:<login>until accounts are mapped. Codex xhigh review, then land, and reply with the PR number(s). Stop when they're merged."This builds the contract hb-acpmux sent to the webview lane (#25253 renders it), items 1 to 4.
What changed
src/author.rs(new):Author {id, name, login, node}andauthor(params), which reads_meta.acpmux.author.idistailnet:<login>oruser_<id>, one line of 1 to 256 bytes;nameis 1 to 64 characters;loginandnodeare optional, one line of up to 256 bytes each. Unknown fields are dropped, and anything else getsauthor.value_refused.author: the local unix socket, and a peer link (servedAspeer: the owner's own machine forwarding its unix socket or app), the same wayfromAgentsurvives forwarding. Everything else is refused withauthor.origin_refused, on every method: Web, the app pane (LocalApp) and a peer forwarding for its own Web client (via: web).server/remote_guard.rscheck_innerenforces this.mark_forwardedkeepsauthor(it still dropsprovenance), and the hub that runs the chat stamps the provenance itself.hub/person.rs). The one change there: apermission_respondfor an ask already answered and still in the settled store passes the person check, but only to reach thenot_foundit then gets, which changes nothing. An allow that carries an author but no person proof is stillpermission.person_required, and a test covers that. It never unlocks a permission. Its only effect is to narrow who can change a queued prompt.session/prompt: an author is recorded as_meta.provenance {kind: "person_message", from: id, fromName: name, login?, node?}onuser_message,queuedand steer records. A person's prompt is not an agent message: it has noagentsender, it doesn't skip the agent-pair person rule, andprompt_withdrawrefuses it (withdraw.not_a_message)._acpmux/queue_edit(every op exceptresume, includingwithdrawof an unread steer) and_acpmux/queue_removerefuse withqueue.not_authorunless the caller's author id equals the prompt's. No author on both sides counts as equal (the owner). An agent message counts as the owner's. An edit by the same author keeps theperson_messageprovenance; it is written onqueue_updated, so a restart keeps it too. An owner's edit of an agent message still drops it, as before.permission_decision.answeredBygains{id, name}fromauthoronpermission_respond, next to a paired device's{device, feedItem}. A losing answer'snot_found(-32002) now carriesdata {answeredBy, outcome}.answeredByis null when the owner won or the ask was cancelled.docs/acpmux-schema.jsondocuments all of the above.Stop conditions (new state, no timer)
hub/permission_settled.rskeeps each chat's settled answers by permission id so a later answer can be told who won. No task or timer is involved. An entry is dropped once it is 10 minutes old (checked on every write and read), when it falls out of the newest 64 for its chat, or when the chat goes away.SteerNow.waitingnow maps each unread steer to its author (a HashSet before); entries are removed exactly as before, when the agent answers the steer.Tests (red at 8a37393, green at the head)
prompt_provenance::an_author_is_taken_from_the_unix_socket_and_a_forwarding_peer_only: accepted from unix and Peer; refused from Web, LocalApp and Peervia: web.peer_integration::a_persons_prompt_through_a_peer_is_recorded_as_theirs_on_the_owning_daemon: a prompt forwarded through a peer is still recorded with its author.prompt_provenance::a_persons_queued_prompt_and_steer_record_who_sent_them: covers user_message, queued and steer records.queue_restore::a_persons_queued_prompt_stays_theirs_across_a_crash: provenance and the author check survive a SIGKILL restart, through the author's own edit.prompt_provenance::a_losing_answer_is_told_who_answered_first:data.answeredByandoutcome; null for the owner; no allow without the person's proof.prompt_provenance::only_the_sender_changes_a_waiting_promptandsteer_now::only_the_sender_takes_back_a_persons_steer:not_authorfor another author and for the owner on a person's prompt; allowed for the same author, and for the owner on the owner's own prompt or an agent's message.prompt_provenance::a_persons_prompt_neither_supersedes_nor_is_superseded,author::tests::a_restored_persons_prompt_takes_no_supersede_key); a group answer and a Stop are recorded in the settled store under the same lock (permission_groups::an_answer_after_a_group_answer_is_told_the_group_won,prompt_provenance::an_answer_after_a_stop_is_told_the_ask_was_cancelled).author::testsandpermission_settled::tests.Size
About 920 changed lines: roughly 470 are source and the rest are tests and the schema doc. claude-sr2 cleared this size as long as the hand-written part stays under ~800.
Shared with PR B
src/author.rs(crate::author) is shared with hb-mp-presence's #25285, which carries an identical copy. Whichever PR lands second drops its copy.No capture: daemon only; the webview lane draws it.
Agent session: big-red
🤖 Generated with Claude Code