Repository navigation
Daemon: stash.restore is all or nothing (#20706) - #21943
Conversation
Regression for a restore whose second member cannot reopen: the first page stays open and stays in the stash, so a retry opens it twice. Covers target here and new_workspace. Fails on the current code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When a member cannot reopen, or the restore's commit fails, the tabs this call opened close again as a session end (never recorded in Recently Closed) under a key of their own, together with the new workspace's starter tab for target new_workspace. Every member stays in the stash and the error is returned, so a retry opens each page once. A rollback close that fails is logged. The starter close now takes a fresh key per attempt instead of one derived from the request's, so a retry after a failed attempt no longer conflicts with the first attempt's receipt (a replay of a committed restore returns before it). The spec says so in the stash.restore constraints and resource-api-v2; SDK descriptors are regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the review of the rollback: - Surfaces are recorded the moment a reopen creates them (closed_history::recording_created / note_created), not when the reopen finishes, so a tab whose name or pin step then failed, and the starter tab of a workspace made for a page, close with the rest. - The rollback closes only surfaces still open, and tries once more without any another client closed meanwhile, since a batch close refuses a surface that is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
|
Receipt for
|
|
Bead |
This comment has been minimized.
This comment has been minimized.
…source API catalog hashes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- regenerated cmux-tui/crates/cmux-app-host/generated/cmux-app.d.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39210649559
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-stash-restore-atomic # Conflicts: # cmux-tui/bindings/cpp/.cmux-resource-api.json # cmux-tui/bindings/go/.cmux-resource-api.json # cmux-tui/bindings/java/.cmux-resource-api.json # cmux-tui/bindings/python/.cmux-resource-api.json # cmux-tui/bindings/rust/.cmux-resource-api.json # cmux-tui/bindings/typescript/.cmux-resource-api.json # cmux-tui/bindings/zig/.cmux-resource-api.json
- cargo fmt in cmux-tui Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39216204764
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. |
This comment has been minimized.
This comment has been minimized.
…-stash-restore-atomic # Conflicts: # cmux-tui/crates/cmux-tui/src/cli/app.rs # cmux-tui/crates/cmux-tui/src/cli/app/tests.rs
- cargo fmt in cmux-tui Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39217495602
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. |
…-stash-restore-atomic
[Medium impact] The PR appears safe to merge; no actionable defect was established. Summary
Acknowledged choices from Verification here was by code inspection; no tests were run. Diagram%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Restore request] --> B{Committed result exists?}
B -->|Yes| C[Return saved result]
B -->|No| D[Track created pages and starters]
D --> E[Reopen selected members]
E -->|Success| F[Commit stash removal and result]
F -->|Success| G[Return result]
E -->|Failure| H[Close created pages still open]
F -->|Failure| H
H --> I[Return original error]
Reviews (1) · Last reviewed commit: "Merge remote-tracking branch 'origin/fea..." · Reviewed by Greptile |
|
Merging at 1f67cf6. A correctness review found nothing blocking, and the stash tests are green. The three reds don't come from this PR:
|
|
Shipped in:
|
#22022) * cmux-next: Swift daemon client for Stash (stash-v1), slice 3 of #20706 StashRecord and StateResourceClient+Stash call stash.create/list/restore/ delete/import (landed daemon side: #21670, #21943). DaemonCapabilities gains stash-v1 in `optional`; daemon-capabilities.json follows. No UI: the service, page, actions, sidebar item and New Tab link are slices 4-7. BranchStateResourcesTests covers OneTab import, list and delete against the same-tree daemon. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at b048ed3 - regenerated Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39212326369 * chore(next): PR autofix at c59b09e - Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39367796953 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
* cmux-next: Swift daemon client for Stash (stash-v1), slice 3 of #20706 StashRecord and StateResourceClient+Stash call stash.create/list/restore/ delete/import (landed daemon side: #21670, #21943). DaemonCapabilities gains stash-v1 in `optional`; daemon-capabilities.json follows. No UI: the service, page, actions, sidebar item and New Tab link are slices 4-7. BranchStateResourcesTests covers OneTab import, list and delete against the same-tree daemon. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at b048ed3 - regenerated Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39212326369 * cmux-next: Stash service and Stashed page, slice 4 of #20706 CmuxNextStash (StashService, StashHost) serves the Stashed page from this Mac's daemon (stash-v1). CmuxNextHistory gets the page: StashPageModel, StashPageView, StashPageHostView, StashedGroup, StashStrings and the OneTab export parser. The daemon's stash changes on session.events now reach the app (SessionStateMirror.Touched.stash, SessionStateStore .stashChanges), so the page lists again when the CLI, MCP or another window changes a stash. The App answers StashHost and registers the Stashed top page; nothing opens it yet (actions, sidebar item and New Tab link land in later slices). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: regenerate ci-target-graph.json on the merged head Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: Stash review fixes - An op's reload waits for the newest stash.list read, so a stash change event that starts a read mid-op no longer leaves the op returning with stale groups. - OneTab titles are capped at 1024 UTF-8 bytes (the daemon's limit), not 1024 characters, so a long non-ASCII title no longer refuses the import. - StashService keeps its change watcher and cancels it in deinit. - Rename the event-stream test to say what it covers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: regenerate ci-target-graph.json on the merged head Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d2c-74b7-74e2-a471-ad8e009b887f&host=cosmic-gray --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
stash.restorereopens members one at a time and only then commits their removal from the stash. When member N failed, members 1..N-1 stayed open and also stayed in the stash, so a retry opened them a second time. A restore is now all or nothing. If any member can't reopen, or the commit fails, every surface this call created is closed again and every member stays in the stash. The restore returns the original error, and a retry opens each page once.closed_history::recording_created/note_created). That catches a tab whose name or pin step then fails, and the starter tab of any workspace made for the pages (targetnew_workspace, or ahererestore with no workspace at all).new_workspacestarter close now takes a fresh key per attempt. It used to derive the key from the request's key, so a retry after a failed attempt hit an idempotency conflict with the first attempt's receipt. That was the documented "known limitation". A replay of a committed restore returns before this point, so it's unaffected.stash.restoreconstraints andresource-api-v2.mdnow state the all-or-nothing contract. The SDK descriptors are regenerated.Decision
Choice: roll back. The default rule is to roll back unless callers already depend on partial results, and none do.
Evidence (origin/feat-cmux-next after #21670):
stash.restoreare the resource registry (resource.rs,wire_name.rs,page_access.rs,resource_router/owner.rs), the SDK descriptors (operation class only) and the app scope tables.StashRestoreResult = {stash_id, workspace_id, workspace_ids, screen_ids, tab_ids, remaining}, which has no per-member status.cli/mcp/v2_tools.rs).Counterargument. A review subagent argued against rollback. Its points and what I did:
split_closed_pane_againcloses with its last tab, but the neighbor's split ratio may not come back exactly. That's cosmetic, and I accepted it.new_workspacerestore fail forever on that key. Fixed: the key is fresh per attempt.None of these makes a partial result better on correctness. Points 1 to 4 were real bugs in the first rollback version and are fixed here; 5 and 6 are tradeoffs, and I accept them.
Testing
cdff2a44addsstash_restore_that_fails_partway_opens_nothing_and_keeps_every_member. It stashes three pages and breaks the middle member's record so it can't reopen. Then:hereandnew_workspace, the restore has to fail with tab and workspace counts unchanged and all 3 members still in the stash;2312fe94(rollback, starter key, spec) plus233d0f2d(from the review: record surfaces at creation, skip ones already gone).cdff2a44, hostedverify-cmux-tui-hosted.sh --filter stash_restore_that(run 39201789671) failed the test on Linux and macOS. Counts were(2, 1), expected(1, 1): the first page stayed open.233d0f2d, hosted--filter stash_(run 39202184409) passedtest (linux)andmacOS lint and tests, 12/12 stash tests on each. The earlier run at2312fe94passed on Linux. Its macOS job never ran a test, because the runner hook refused while a fleet build waited for the host.--filter stash_run is in progress; I'll record the result here.cmux-tui-core, so it compiles only in the hosted runs.feat-cmux-next(eae2935) regenerated the SDK descriptors. It also regeneratedcmux-app-host/generated/cmux-app.d.ts: base's copy lackstab.update/panefields (pinned,allow_tabs,marked_unread) that base's catalog already has, sogen-cmux-global.ts --checkfails on base.check-resource-api-boundary.py,check-spec-inventory.py,check-sdk-schema.py,sync-resource-api.pyandgen-cmux-global.ts --checkpass, andrustfmt --checkis clean.Freeze WINDOW areas: CORE (touches cmux-tui-core and the resource spec). No Cargo file changes, so not LOCK. The Air Blue coordinator merges this PR.
Changelog
Fixed: restoring a stash that fails partway no longer leaves pages open that a retry opens again; the restore is all or nothing.
🤖 Generated with Claude Code