Skip to content

acpmux: a chat move whose client goes away ends on its own (cx-xtg0.9) - #19411

Merged
teamleaderleo merged 14 commits into
feat-cmux-nextfrom
op-next-steering/transfer-recovery
Oct 11, 2026
Merged

teamleaderleo merged 14 commits into
feat-cmux-nextfrom
op-next-steering/transfer-recovery

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 11, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

A chat being moved (acpmux.transfer.v1) is frozen on its source until the client pumping the move commits or aborts. If that client died (the app quit, the CLI was killed, the machine slept), the chat stayed frozen forever. Now the source watches each move it began and ends a move left idle for the lease (5 min):

  • Not every bundle file was read to its end: no target can have finished (finish checks every file's size), so the move aborts (transfer_aborted {reason: "stalled"}) and the chat runs here again, its queue included.
  • Every file was read: the source asks the target named at begin (new optional to: {socket}, which the acpmux transfer CLI sends) with the new _acpmux/transfer_forget {transferId, sessionId}. The target drops any unfinished receive of the move, marks it so a later receive is refused (transfer.forgotten), and answers {landed}. Landed: the source commits. Not landed: it aborts. No answer (no to, target down, 30 s timeout): the chat stays frozen and the source asks again after another lease.
  • A restarted daemon watches its moves again (state lives in the move's directory: alive, read, to).
  • A page read and the watch's not-fully-read abort share a lock, so a client never gets a bundle's last page from a move that was just given back.

Commit is now idempotent on an already-closed chat. Backward compatible: to is optional, and old clients get the partial-read abort.

Testing

Behavior tests in cmux-tui/crates/acpmux/tests/hub_integration/transfer.rs. Fleet, scripts/ci/cmux-tui-rust-check.sh --arg test|all --arg transfer:::

  • Red 8689ea0: FAILED. 2 passed; 3 failed (the three new tests time out; the chat stays frozen).
  • Green f5429bf (all: fmt, clippy, tests): ok. 5 passed.
  • Review fixes. Red fafd941: FAILED. 4 passed; 1 failed (a forgotten move could be received again). Green 6982494 (all): ok. 5 passed.

Crash ratchet: panic_macro 33 -> 35 in cmux-tui, which also fails on the merge base, so it comes from base and not this branch. God-file check: no touched file is listed.

Reviewed by a subagent twice (correctness first). Known edge cases left as-is (all need the client to vanish mid-finish for longer than the lease): a watch commit and a client commit at the same moment can both record; a watch commit whose kill fails is not retried; a chat deleted on the target within the lease reads as not landed.

Changelog

none

closes cx-xtg0.9

🤖 Generated with Claude Code


Summary by cubic

A chat being moved (acpmux.transfer.v1) is frozen on its source until the client pumping the move commits or aborts; if that client dies or goes away, the chat stays frozen forever. The source now watches each move it began and ends a move left idle for the lease (5 min):

  • If the bundle was not read to its end, no target can have finished, so the move aborts (transfer_aborted {reason: "stalled"}) and the chat runs here again, its queue included.
  • If it was, the source asks the target named at begin (new optional to: {socket}, sent by the acpmux transfer CLI) with _acpmux/transfer_forget; the target drops any unfinished receive, refuses later ones, and answers whether the chat landed, which decides commit or abort.
  • If the target can't answer (or no target was named), the chat stays frozen and the source asks again after another lease.
  • State lives in the move's directory, so a restarted daemon resumes watching; repeated commits on an already-closed chat answer committed, and a forgotten move stays refused.
  • Old clients are unaffected: to is optional and they get the partial-read abort.

Written for commit 7a2ca8a. Summary will update on new commits.

View guided diff Turn on auto-fix

teamleaderleo and others added 4 commits October 11, 2026 06:29
Three behavior tests: the client is gone before the bundle is read (the chat
must come back here), after the target finished (the source must commit), and
after the copy but before finish (the target must drop the receive and the
chat come back). Only the lease setter they use exists yet; today the chat
stays frozen in all three.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The source watches each move it began (hub/transfer_watch.rs). Once the move
has been idle for the lease (5 min; begin and each read are activity):
- the bundle was not read to its end: no target can have finished, so the
  move aborts (transfer_aborted reason stalled) and the chat runs here again;
- it was: the source asks the target named at begin (to: {socket}) with the
  new local-only _acpmux/transfer_forget, which drops any receive of the move
  under the lock transfer_finish holds and says whether the chat landed. It
  landed: commit. It did not: abort. No answer (or no target named): the chat
  stays frozen and the source asks again after another lease.
The state lives in the move's directory, so a restarted daemon watches its
moves again. A repeated commit answers committed. acpmux transfer names its
target at begin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… abort never interleave

transfer_forget leaves transfers/forgot-<id>, so a client that kept the bundle cannot
receive the move again after the source gave the chat back. A page read and its note
share a lock with the watch's not-fully-read abort. The target's answer has a 30 s
timeout, and a clock step back counts as activity (cx-xtg0.9 review).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 11, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@coderabbitai

coderabbitai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 36552955-e4ac-4262-9191-df0cb152a482

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cursor

cursor Bot commented Oct 11, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

teamleaderleo and others added 3 commits October 11, 2026 07:22
A worker's temp dir can make the path longer than a socket allows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 11, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@teamleaderleo
teamleaderleo merged commit 06b3577 into feat-cmux-next Oct 11, 2026
72 checks passed
@teamleaderleo
teamleaderleo deleted the op-next-steering/transfer-recovery branch October 11, 2026 16:24
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Batch queue: dropped from this batch, turns heavy tier red.

  • cmux-next checks (god files, concurrency, crash safety, l10n)

Next: fix, push.

teamleaderleo added a commit that referenced this pull request Oct 11, 2026
acpmux now has a target-side transfer_forget (#19411). The daemon's
receive-abort should forward it for the move named in the receive's own
manifest, and acpmux's forget must stay unreachable as a daemon verb.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 11, 2026
Forwards acpmux's target-side _acpmux/transfer_forget (#19411) with the
transfer and session ids of the receive's own manifest, so acpmux drops
the receive's files at once and refuses that move afterwards; acpmux's
forget is not a daemon verb. A receive whose manifest lacks plain ids is a
bad request. server.rs keeps to its god-file baseline after the base
merge: the module is public and its route chains onto the attach route.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 11, 2026
…emon's own guard (cx-rppd.1) (#19460)

* cmux-tui: behavior tests for guarded agent chat moves (agent-transfer-v1)

Tests first for cmuxterm-hq#1874 slice 1 (cx-rppd.1): a trusted local
client moves a chat through the daemon, which forwards only the guarded
form of acpmux's transfer calls. The module is a stub that serves nothing,
so the tests fail on their assertions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-tui: agent-transfer-v1 forwards chat moves to acpmux with the daemon's own guard

The Move to Cloud pump (cmuxterm-hq#1874 slice 1, cx-rppd.1) moves a chat
between two daemons' acpmux through these verbs. A receive always carries a
guard the daemon builds: peer control rooted at $HOME/work on a Cloud host,
web control rooted at the named folder (strictly under $HOME, not in a dot
folder) elsewhere, and the folder must be under the roots. Finish uses the
receive's own folder. Source verbs take the chat only from the surface's
tab record, and begin serves the bundle manifest without absolute source
paths. Trusted local clients only; no relay allowlist entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-tui: agent-transfer page check in clippy's preferred form

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-tui: tests for agent-transfer host kind, slots and bundle scrubbing

Review of #19460: a receive must be refused on a team VM and on a Cloud VM
of unknown kind (the hq-84 rule agent-session-start follows), at receive
and again at finish; receive slots must not leak; begin must scrub
session.json launch settings; read must serve only paths the begin
manifest lists. These fail until the next commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-tui: agent-transfer follows the team VM rule and stops leaking slots

Review of #19460:
- receive and finish read agent-session-start's machine kind gate at each
  call and refuse on a team VM or a Cloud VM of unknown kind (hq-84), with
  start's codes: a peer-control chat keeps its policy and mode.
- the receive cap is checked before acpmux makes a receive; a finish that
  fails for good drops its receive; agent-transfer-receive-abort forgets
  one (acpmux has no target-side abort); abort forgets the move even when
  acpmux refuses.
- begin scrubs session.json launch settings (env, argv, harness roots,
  preset, tags) next to manifest.json sources, names compared
  case-insensitively, and read serves only paths the manifest lists.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-tui: test that receive-abort has acpmux forget the receive's move

acpmux now has a target-side transfer_forget (#19411). The daemon's
receive-abort should forward it for the move named in the receive's own
manifest, and acpmux's forget must stay unreachable as a daemon verb.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-tui: receive-abort has acpmux forget the receive's move

Forwards acpmux's target-side _acpmux/transfer_forget (#19411) with the
transfer and session ids of the receive's own manifest, so acpmux drops
the receive's files at once and refuses that move afterwards; acpmux's
forget is not a daemon verb. A receive whose manifest lacks plain ids is a
bad request. server.rs keeps to its god-file baseline after the base
merge: the module is public and its route chains onto the attach route.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 11, 2026
…br4q) (#19489)

* test(settings): empty states and full text for clipped help and paths (cx-9br4q)

Red on base: Accounts with no accounts, Harnesses while checking and a theme
search with no match draw nothing; clipped help and folder paths carry no title.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(settings): polish pass on every page (cx-9br4q)

- Buttons show a hover; MDM and token chips are small-radius rects, not capsules.
- Accounts with none, Harnesses while checking and a theme search with no match
  say so instead of drawing nothing.
- Card intros wrap; clipped help, subtitles and folder paths keep their full text
  as a tooltip; a refused chat root's reason is its own line.
- Action and theme rows reserve the Reset slot, so their controls line up with
  the setting rows; account details line up under the account name.
- Your Agents uses the page's buttons, pads its rows, drops the empty outline
  and draws the doctor's steps with the page's icons.
- Profile rows highlight under the pointer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(settings): review fixes for the polish pass (cx-9br4q)

- Action rows keep a long path and the Reset slot on one line.
- Terminal paths carry their full text as a tooltip.
- Only a collapsed profile row highlights, so an open form keeps its fields visible.
- Empty lines are output elements, announced as status.
- de and km use the page's existing word for theme.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-tui: agent-transfer-v1 forwards chat moves to acpmux with the daemon's own guard (cx-rppd.1) (#19460)

* cmux-tui: behavior tests for guarded agent chat moves (agent-transfer-v1)

Tests first for cmuxterm-hq#1874 slice 1 (cx-rppd.1): a trusted local
client moves a chat through the daemon, which forwards only the guarded
form of acpmux's transfer calls. The module is a stub that serves nothing,
so the tests fail on their assertions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-tui: agent-transfer-v1 forwards chat moves to acpmux with the daemon's own guard

The Move to Cloud pump (cmuxterm-hq#1874 slice 1, cx-rppd.1) moves a chat
between two daemons' acpmux through these verbs. A receive always carries a
guard the daemon builds: peer control rooted at $HOME/work on a Cloud host,
web control rooted at the named folder (strictly under $HOME, not in a dot
folder) elsewhere, and the folder must be under the roots. Finish uses the
receive's own folder. Source verbs take the chat only from the surface's
tab record, and begin serves the bundle manifest without absolute source
paths. Trusted local clients only; no relay allowlist entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-tui: agent-transfer page check in clippy's preferred form

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-tui: tests for agent-transfer host kind, slots and bundle scrubbing

Review of #19460: a receive must be refused on a team VM and on a Cloud VM
of unknown kind (the hq-84 rule agent-session-start follows), at receive
and again at finish; receive slots must not leak; begin must scrub
session.json launch settings; read must serve only paths the begin
manifest lists. These fail until the next commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-tui: agent-transfer follows the team VM rule and stops leaking slots

Review of #19460:
- receive and finish read agent-session-start's machine kind gate at each
  call and refuse on a team VM or a Cloud VM of unknown kind (hq-84), with
  start's codes: a peer-control chat keeps its policy and mode.
- the receive cap is checked before acpmux makes a receive; a finish that
  fails for good drops its receive; agent-transfer-receive-abort forgets
  one (acpmux has no target-side abort); abort forgets the move even when
  acpmux refuses.
- begin scrubs session.json launch settings (env, argv, harness roots,
  preset, tags) next to manifest.json sources, names compared
  case-insensitively, and read serves only paths the manifest lists.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-tui: test that receive-abort has acpmux forget the receive's move

acpmux now has a target-side transfer_forget (#19411). The daemon's
receive-abort should forward it for the move named in the receive's own
manifest, and acpmux's forget must stay unreachable as a daemon verb.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-tui: receive-abort has acpmux forget the receive's move

Forwards acpmux's target-side _acpmux/transfer_forget (#19411) with the
transfer and session ids of the receive's own manifest, so acpmux drops
the receive's files at once and refuses that move afterwards; acpmux's
forget is not a daemon verb. A receive whose manifest lacks plain ids is a
bad request. server.rs keeps to its god-file baseline after the base
merge: the module is public and its route chains onto the attach route.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix: close the agent page's artifact frame CSS rule (chat pages render unstyled) (#19494)

* test: every webviews stylesheet closes the rules it opens (red)

feat-cmux-next's conversation.css leaves .acpmux-artifact-frame open since
#19473; every rule after it is swallowed and chat pages render unstyled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: close the agent page's artifact frame rule (green)

The unclosed .acpmux-artifact-frame block from #19473 swallowed the rest of
conversation.css: the composer, code blocks and user rows lost their styles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 9ddddd6

- vp fmt webviews/test/stylesheets.test.ts

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38161397375

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant