Repository navigation
Run Settings and palette agent updates on their captured owner - #21447
Conversation
|
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 |
This comment has been minimized.
This comment has been minimized.
|
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)
Pending: CI is running on Last result: Unclear: Written by red: cmux-next checks (god files, concurrency, crash safety, l10n); NEW:
Agents: |
|
Agents: |
5134a81 to
448c207
Compare
caed266 to
0ed6bb3
Compare
0ed6bb3 to
0b87277
Compare
|
Visual preview of 6f00b00: in the maintainers' private review feed. |
|
Bead |
Replays #21398's change onto current feat-cmux-next as one commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replays #21401's change onto current feat-cmux-next as one commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replays #21402's change onto current feat-cmux-next as one commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replays #21414's change onto current feat-cmux-next as one commit. Keeps the crash ratchet flat: poisoned locks are recovered instead of unwrapped (only a held job lock is busy), and the job status parses its decimal strings with radix: 10. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replays #21429's change onto current feat-cmux-next as one commit, written without growing CmuxNextApp's source lines (size cap). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replays #21436's change onto current feat-cmux-next as one commit. ControlRequest takes callerApp in its init so CmuxNextApp's source lines don't grow (size cap). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replays #21447's change onto current feat-cmux-next as one commit, together with its follow-up fix. Settings and the palette run agent updates on their captured owner route instead of typing a health argv into a focused pane. The update owner prepares only npm and native installations, and refuses a family when any of its rows is another channel or already current, so both surfaces submit only those families and Settings shows Update only for rows whose channel the owner runs. A start the owner refuses (agent_cli_job.*, answered before it journals a creation) leaves no "unknown" job that blocks that CLI. One family's failure does not stop the rest of a batch; the batch reports every failure. A lost reply still keeps its unknown job, since the terminal may exist. Update, Update all and turning Update Automatically on still need a gesture or a confirmed user, as #21439 requires. The update action, its owner route and the health-row reader live in a new CmuxNextAgentUpdates module, leaving thin app glue in CmuxNextApp. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replays #21439's change onto current feat-cmux-next as one commit. Turning Update Automatically on now needs a gesture or native confirmation too, since acpmux runs the update after its next check (review finding); SettingsHarnesses is compacted so CmuxNextApp's source lines don't grow (size cap). The health read defaults to the named AcpmuxEnvironment.harnessHealth rather than an async closure literal (crash ratchet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
0b87277 to
239e427
Compare
# Conflicts: # Packages/macOS/CmuxNext/Sources/CmuxNextSettingsWindow/Localizable.xcstrings
Resolved two additive conflicts by keeping both sides: the chain's harness_health inventory_refresh/update_plan modules and its apps.md lane notes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- regenerated plans/cmux-next/action-surfaces.json - regenerated schemas/defaults/cmux-next.json - vp fmt webviews/src/agent-session/acpmux/replyHref.ts - vp fmt webviews/src/pages/markdown/links.ts - vp fmt webviews/test/markdown-cmux-app-links.test.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39208575467
Package.swift: CmuxNextAppTests keeps both CmuxNextAgentUpdates and the base's CmuxNextTerminalGeometry. settings/generated/strings.json is build output since #21970, so it stays deleted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
…LI updates (red) Regressions for the #21447 review: - The owner journals a failed creation under its key and answers every replay with the same failure (operation.failed, mutation.indeterminate). Such an answer, on the first start or on a replay of a lost reply, must settle the job instead of leaving it unknown and blocking the CLI for the rest of the session. - A job that already ended must not block the next update of its CLI just because nothing refreshed Settings > Agents in between; a job that still runs keeps refusing it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts: - Packages/macOS/CmuxNext/Package.swift: the base made the CmuxNextAppTests dependencies one sorted entry per line; add CmuxNextAgentUpdates in its sorted place. - CmuxNextSettingsWindow/Localizable.xcstrings: keep the base's catalog and add this PR's five settingsWindow.harnesses.job* keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| if Self.refusedBeforeCreation(error) { | ||
| if let index = Self.job(of: family, in: intent) { intent.jobs.remove(at: index) } | ||
| } else { | ||
| intent.unsent[family] = childKey |
There was a problem hiding this comment.
Failed starts block later updates
When terminal creation fails with operation.failed, start() keeps an unknown job and saves its key for replay. The owner can store that failure and return it on every replay, but refresh() only clears agent_cli_job.* errors. That CLI then stays blocked until the app restarts, even though no terminal was created.
Treat a confirmed, stored creation failure separately from a lost reply.
| let status = try await route.observe(receipt) | ||
| guard route.isCurrent(), status.terminalID == receipt.path.terminalID, | ||
| let index = intent.jobs.firstIndex(where: { $0["terminal"]?.stringValue == status.terminalID.rawValue }), | ||
| var job = intent.jobs[index].objectValue else { continue } | ||
| // An end observation settles the job even when it has no process result. | ||
| job["state"] = .string(status.state.rawValue) |
There was a problem hiding this comment.
Finished updates become blocked again
When two refreshes overlap, an older pending reply can arrive after another refresh saved exited. refresh() writes each reply without checking the saved state or revision, so the older reply marks the finished update as running and blocks further updates until another successful refresh.
Keep exited final, or reject older observations.
…ed jobs before refusing - Any coded owner answer to a start settles its key. agent_cli_job.* comes before a creation is journaled; any other code (a failed creation, operation.failed, or an interrupted one, mutation.indeterminate) is the key's journaled outcome and the owner answers every replay the same way. Such an answer, on the first start or on a replay, now drops the job instead of leaving it unknown and blocking the CLI for the rest of the session. Only a failure with no owner answer (a lost reply) is replayed; busy on a replay still keeps the job. - A start that finds an earlier job of the same CLI still unended refreshes once before refusing, so a job that already exited no longer blocks the next palette or Settings update until someone opens Settings > Agents. A job that still runs keeps refusing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the lost job (red) Regression for the delta review of 0ad8305: a replay can get a coded answer the owner sends before it reads its journal (daemon_shutdown_pending during a handoff, operation.failed from a registry error), while the lost first start may have created the update terminal. Dropping the job then hides a running update and unblocks a second one of the same CLI. A first start's fresh key has nothing running, so any coded answer there still settles; its cases are now operation.failed and daemon_shutdown_pending, with a fixture message that matches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A replay of a lost start now drops its job only on an agent_cli_job.* refusal, as before 0ad8305. Another coded answer can come before the owner reads its journal (daemon_shutdown_pending during a handoff, or a registry error mapped to operation.failed) while the lost first start created the terminal, so the job keeps blocking its CLI and the next refresh replays the same key. A first start's fresh key still settles on any coded answer, which is what keeps a failed creation from blocking the CLI for the session. A lost reply whose creation then failed is still replayed until the owner can say so; #21469's journal resolve settles that case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…4d92 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Picks up #21447's base merges and its review fix commits (7502ddf, 0ad8305). Conflicts: - CmuxNextAgentUpdates/AgentCLIUpdateActions.swift: keep this branch's ledger-based start, refresh and replay. Its start already recovers and observes retained jobs before it checks for a blocking one, which is what #21447's refresh-before-refuse added, so that helper is dropped here. The settled-answer rule is applied to this structure in a follow-up commit. - CmuxNextActions/ActionCatalog.xcstrings: keep the base's catalog and add this branch's argument.agent.harness.intent key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Picks up feat-cmux-next d3a9df0 through #21447, and #21447's narrowed settle rule with its tests (0902535, 445644e). Conflict: CmuxNextAgentUpdates/AgentCLIUpdateActions.swift; keep this branch's ledger structure, where replays and resumes already settle only on an agent_cli_job.* refusal. The first-start half of the rule goes into `send` in the next commit; until then #21447's first-start tests are red here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From #21447's review: a first start that the owner answers with a coded failure other than an agent_cli_job.* refusal (operation.failed for a failed creation, daemon_shutdown_pending during a handoff) left its ledger row unknown and unsent. `send` now settles the intent on any coded answer, since a first start's fresh key cannot have anything running. Replays and resumes still settle only on a refusal: a code the owner sends before reading its journal proves nothing there, and recovery's resolve settles a journaled failure. Makes #21447's aFirstStartTheOwnerAnswersWithAFailureLeavesNoBlockingJob pass on this branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ing its CLI (red) From the second delta review: the owner answers a fresh key with mutation.indeterminate when a failed creation left its reserved terminal possibly live (AmbiguousLive in creation_settlement.rs). Dropping that job would let a second update of the same CLI start beside it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`answered` no longer settles mutation.indeterminate: the owner says a failed creation's effect may have run, so the job stays unknown and keeps blocking its CLI, like a lost reply, until it is replayed or its owner session retires it. The comments no longer claim a coded first-start answer always means nothing runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…4d92 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. |
Picks up feat-cmux-next 550ef1d through #21447, and #21447's indeterminate first-start test and fix (47f662a and the commit after). Conflict: CmuxNextAgentUpdates/AgentCLIUpdateActions.swift; keep this branch's version. The indeterminate exception goes into this branch's `answered` in the next commit; until then the new test is red here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…w until the journal settles it (red) From #21469's review: a coded failure other than an agent_cli_job.* refusal can follow a durable creation (a launch activation failing after the commit maps to operation.failed), so deleting the ledger row on such an answer can hide a live update terminal and unblock a second one. The row must stay until recovery's resolve reads the owner's journal; a failed creation then settles. #21447's any-code variant of this test is replaced here; the fake owner can now answer resolve. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes from the review, at
|
…s every tier on any branch (#22325) * ci(cmux-next): test that a same-repo PR runs its routed Mac tiers and a branch dispatch runs every tier (red) Refs https://github.com/manaflow-ai/cmux/issues/22303 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci(cmux-next): a same-repo PR runs its routed Mac tiers, and dispatch runs every tier on any ref (green) A same-repository pull request into feat-cmux-next no longer needs the full-ci label for the Mac tiers the router picks (web bundles, Swift canary, swift test, daemon tests, generated files, Release compile, app scheme compile). A fork PR keeps the full-ci gate and still never places an owned runner. workflow_dispatch runs every tier on any ref, not only feat-cmux-next and next-batch/*, so a branch gets full CI without a side next-batch push. A newer plain dispatch on the same working branch cancels the older one. Fixes https://github.com/manaflow-ai/cmux/issues/22303 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci(cmux-next): side-lane placement test expects a branch dispatch to place its Mac jobs Refs https://github.com/manaflow-ai/cmux/issues/22303 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci(cmux-next): test that a branch dispatch's deferred tree still runs its daemon tests and web bundles follow the route (red) Refs https://github.com/manaflow-ai/cmux/issues/22303 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci(cmux-next): a branch dispatch's deferred tree still runs its daemon tests; web bundles follow the route (green) The tree wait marker records a working-branch dispatch as origin dispatch, so the same-tree run cmux_next_tree_notify.py starts for it runs daemon-test (a push's skips it). web-bundles waits for path routing and runs on a pull request only when swift, daemon or generated is routed. Refs https://github.com/manaflow-ai/cmux/issues/22303 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci(cmux-next): test that a tag dispatch's deferred tree jobs start on the tag (red) Refs https://github.com/manaflow-ai/cmux/issues/22303 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci(cmux-next): a tag dispatch's deferred tree jobs start on the tag (green) The tree wait marker of a dispatch records whether its ref is a branch or a tag. cmux_next_tree_notify.py takes a tag marker as current (a tag has no branch head to look up) and dispatches same-tree mode on the tag. Refs https://github.com/manaflow-ai/cmux/issues/22303 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci(next): fast tier builds the Swift packages and the cmux-tui tests side by side (#22330) The fast tier built CmuxNext (~160 s) and then the cmux-tui test binaries (~115 s) one after the other, about 290 of its ~320 s on a warm mini. The two builds share no outputs, so they now run concurrently, each group in order, with their output prefixed by build. A failed build still fails the tier before any entry runs. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * ci(next): a fast-tier timeout kills the entry's whole process group (#22357) * test(next): a timed-out fast-tier entry must not leave its children running cmux19-glaeda-1, run 39360407187 job 118427686199: three entries timed out twice each, and the runner's post-job cleanup then killed six orphan xctest processes. The timeout killed only swift test, not the xctest it spawned. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci(next): a fast-tier timeout kills the entry's whole process group swift test runs the tests in an xctest child. On a timeout, subprocess.run killed only swift test, so the xctest kept running (and loading the host) through the rest of the tier: cmux19-glaeda-1 run 39360407187 ended with six orphan xctest processes. Each entry now runs in its own session, and a timeout (or the entry's end) kills the group. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): a fast-tier entry's child in its own process group dies too From review: SwiftPM starts xctest in a process group of its own, so killing the entry's group misses it, and draining the output then waits for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci(next): a fast-tier timeout kills every process below the entry From review: SwiftPM starts xctest in a process group of its own, so a group kill missed it and the output drain then waited on it. The timeout now kills the entry's group, every descendant and each descendant's group (never the tier's own), drains with a 5 s cap, and tolerates a group already gone or zombie-only (EPERM on macOS). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux NEXT: full screen no longer lags (AppKit's titlebar window is not a page) In full screen AppKit makes its titlebar window (NSToolbarFullScreenWindow, a plain NSWindow) a child of the window. WindowOverlayHost.isPageWindow counted every non-panel child as a Chromium page, so a full screen window stayed in page mode: overlay planes moved into the overlay panel, divider mouse-catcher panels covered every split, pages re-synced after every layout, and the child order was checked on every event. Divider drags, sidebar hovers, group toggles and tab switches all lagged. AppKit's own child windows are now AppKit chrome: never pages, allowed by ChildWindowPolicy, and never re-added by reassertOrder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux NEXT: tests, a full screen window's AppKit titlebar child keeps it out of page mode Red on the base (FullScreenTitlebarPageModeTests: the titlebar child put the window in .overlayWindow page mode), green with the fix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux NEXT: a background chat or New Tab page can't take the keyboard (#22448) * test(next): a hidden agent page cannot take the keyboard by itself A pane keeps the tabs it does not show parked in place, hidden. A parked chat or New Tab page in the focused pane that calls element.focus() makes WebKit ask for first responder, and AgentPaneView.refusesSelfFocus lets it through because the page sits in the focused pane. Fails until the fix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): a hidden agent page cannot take the keyboard by itself AgentPaneView.refusesSelfFocus refused only a page in another pane. A chat or New Tab page parked hidden in the focused pane passed: its element.focus() (the composer mounting as a New Tab ask turned into the chat, a reconnect) made it first responder, the focus model filed that as the same pane's content and re-applied nothing, and typing went into the hidden page. A page that does not show is now refused too, outside input events and the focus model's own moves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 49641b36c868 - schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39400135919 --------- 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 New Tab: the field takes the keyboard back when you return to the page (#22456) * test(next): the New Tab field takes the keyboard back with its page After a press on the New Tab page's background nothing on it has focus. Switching to another tab or pane and back gave the page the keyboard with nothing in it: no caret, and Backspace, the arrows or Cmd-V went nowhere. Fails in Chromium and WebKit until the fix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): the New Tab field takes the keyboard back with its page The New Tab page focused its field only when it appeared and on Cmd-L. After a press on the page's background, a tab or pane switch back gave the page the keyboard with nothing in it focused. The field now takes an idle keyboard when the window takes focus, as the chat composer does (composerFocus.ts), keeping its caret; a focused control keeps it. Not with focusField off (an omnibar owns the keyboard). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): the New Tab field's reclaim keeps the page's scroll Review: focus() scrolls the field into view, so a scrolled New Tab page jumped back up when the field took the keyboard back. Fails until the follow-up fix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): the New Tab field's reclaim keeps the page's scroll Review follow-up: focus the field with preventScroll, as the pane's other focus returns do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux NEXT composer: a click anywhere on the card puts the cursor in the prompt (#22475) * test(next): a press on the composer card around the text focuses the prompt Only the editable text took a press: the field's padding, the gap around the button row and the card's edge left nothing focused. Codex and Claude Code treat the whole input card as the text area. Fails in Chromium and WebKit until the fix; the button case passes on both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): a press on the composer card around the text focuses the prompt The composer card takes a press outside its text, controls and attachments (the field's padding, the gap around the button row, the card's edge) and focuses the prompt where its caret was, or the shell field in shell mode. Codex and Claude Code treat the whole input card as the text area. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): a press inside a menu the composer card opened stays the menu's Review: the card's press handler cancelled presses in the + menu's padding (and React hands up presses from what the card portals). Fails on 64dc9976a3cc in Chromium and WebKit; passes on feat-cmux-next. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): the composer card leaves presses in its menus, dialogs and scrollbar alone Review follow-up: focusFromCard skips presses from outside the card's own DOM (portals), in .ui-popup menus and dialogs, on a long prompt's scrollbar, and Control-presses (the macOS context menu). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux NEXT New Tab: the composer stays where it first painted (#22365) * test(newtab): the New Tab composer stays put across loading, typing, attachments and Now Playing Fails on base: the composer template centers the card on everything under it, and a pasted image's chips push the field's text down. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux NEXT New Tab: the composer stays where it first painted The composer template no longer centers the page's content, which moved the card with every row under it, each pasted image and each line of a long draft; the card sits at a fixed place about the middle, where centering put the resting page. Pasted or dropped attachments go under the field instead of above it, so the typed text keeps its place in every template, and they take the chat composer's tile size. Gallery: composer-holds-its-place and composer-template-holds-its-place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux NEXT New Tab: the attachment's remove badge is the chat composer's Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux NEXT New Tab: review fixes for the composer-stays-put test The rect log compares whole rects, so a field at 100,180 after 100,18 counts as a move; the long-draft step starts once Now Playing is drawn. The New Tab box drops an attachment variable its strip padding overrides. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): show one notice at a time above the composer (#22370) * fix(next): show one notice at a time above the composer Notices above the composer used to stack: an agent CLI update card could sit on top of a chat error. They now share one slot (NoticeSlot): of every notice that wants the line, only the highest tier draws (error > status > hint > update), and the rest wait and show once it clears. The agent CLI update offer is no longer a banner. The model chip wears a small dot; the full picker offers Update and Skip This Version, and the compact model menu ends with an Update row. Only an update the person started shows its progress line, at the lowest tier, so it never covers an error. A signed-out agent CLI is an error. Refs manaflow-ai/cmuxterm-hq#2569 (#12). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): the update rows show their text without a tooltip tooltip-rules: an element whose text shows carries no title. The model picker's and the compact menu's update rows draw their text, so they drop the title. The compact menu's rail tab keeps its tooltip: it draws only the harness's mark or initial. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux NEXT: Switch to tab on the New Tab page selects and focuses the tab (#22300) * cmux NEXT: test that New Tab's Switch to tab selects and focuses the tab Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux NEXT: Switch to tab on the New Tab page selects and focuses that tab The jump went through the palette's tab source, which selected the tab through its pane controller. For a tab in another workspace the window swaps that workspace in on a later turn, so the pane was not there: the jump landed on the workspace's own focus. It now runs tab.focus, which shows the workspace and screen, selects the tab and focuses its pane. Another workspace with one open tab listed as that workspace (named by the tab, labelled Switch to workspace). It lists as the tab now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 12d677d94d24 - schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39358961960 * cmux NEXT: New Tab jump test reads the pane on the main actor Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 426a6b23aa29 - schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39398755138 --------- 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 agent chat: the composer takes the keyboard when its pane gets focus (#22527) * test(next): agent chat takes the host's focusContent into its composer On the shared page host the agent chat is a PageWebView, which sends focusContent on cmux.page.command each time its web view becomes first responder (pane-focus-DESIGN.md P18). The page never subscribes to that stream, so the signal is dropped. Fails until the page takes it: the composer prompt gets the keyboard, and a focused field or a text selection in the chat keeps it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): agent chat takes the host's focusContent into its composer On the shared page host the agent page now subscribes to cmux.page.command and claims an idle keyboard for the composer on focusContent, the same claim the window's focus event and the composer's ready event make (pane-focus-DESIGN.md P18). The claim now also leaves a text selection alone: a transcript selection kept while the pane was away stays to be copied, and a printed key still goes into the composer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): agent chat's selection guard covers only text outside the composer Review: a selection inside the composer's own prompt, with focus on the body, no longer blocks the claim. The test's stand-in host drops a subscription on unsub, so a delivered focusContent proves a live listener. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(next): pane-focus P18, the agent chat takes focusContent on the page host Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux NEXT New Tab: keys typed after opening a page from another pane's + reach it (#22528) * cmux NEXT New Tab: regression for held keys of a page in a pane focus has not reached A New Tab page opened cold in a pane the focus model does not name (the + of another pane, no prewarmed spare) holds the keys typed after it opens. At the page's readiness the hold's makeFirstResponder is refused by the window (AgentPaneView.refusesSelfFocus), so the keys wait for the hold's limit and then go to the pane that still has focus. This test fails until the fix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux NEXT New Tab: held keys reach a page opened in a pane focus has not reached The key hold gives the New Tab field the keyboard with makeFirstResponder at the page's readiness. The window refuses an agent page's web view outside an input event and the focus model's own moves while the model names another pane (AgentPaneView.refusesSelfFocus), so a page opened cold in a pane the model had not reached (another pane's +; no spare, so no focusPane) never drained: its keys waited for the hold's 2 s limit, then went to the old pane. The hold now moves the focus model to the page's pane (programmatic: no intent generation bump) before it focuses the field, once, and only when the opening may change the view (not a script run without focus) and focus is still on the pane it had at the opening (a click elsewhere since then wins). didShow and cancel become one-liners so CmuxNextApp's source lines don't grow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at f877a9af6943 - Packages/macOS/CmuxNext/ci-target-graph.json - schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39422082823 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com> * agent pane: permission asks wait in line instead of hiding each other (#22463) * agent pane: failing test for a second permission ask hiding the first Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agent pane: permission asks wait in line instead of hiding each other acpmux keeps every pending ask, but the pane held one: a second ask that joined no permission group replaced the shown card, and answering it left the first with no card while its agent still waited. The direct client now keeps the asks behind the shown one in arrival order (live and from the attach's pending list), the card says how many more wait, and answering one shows the next. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agent pane: failing test for a reopened chat whose pending asks come in hash order Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agent pane: a reopened chat shows its waiting asks oldest first acpmux lists a session's pending asks from a hash map, so the attach's order is arbitrary. The client now orders them by the record that asked (else by the time-ordered permission id), so the card keeps the ask the replay showed and the rest wait behind it in the order they came. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * agent pane: a permission card shows a command only for a command ask (#22465) * agent pane: failing test for a permission card that shows its title as a command Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agent pane: a permission card shows a command only for a command ask The single permission card fell back to the ask's title, minus a leading Run, for its command block, so an edit, a fetch or an MCP call drew its title twice: as the label and again as code. It now reads a command from the request, or from the title only for an execute ask (or an ask without a kind whose title says Run, as codex-acp titles commands). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agent pane: failing test for a permission ask whose command is an argv array Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agent pane: a permission ask reads an argv command the way the transcript does Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * agent pane: a peer's running subagent counts its time on the session's clock (#22485) * agent pane: failing test for a peer subagent's time read on this computer's clock Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agent pane: a peer's running subagent counts its time on the session's clock A subagent's start and end are the session's event times, but the group line, its rows, the agents pill and the summary list counted a running one against this computer's clock, so on a peer whose clock ran ahead it read 0s the whole time, and one behind read too long. useNow now returns the session's time (SubagentClockContext, the snapshot's clockOffsetMs), as the turn's own clock already does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: the agents pill nests a subagent under the one that spawned it (#22521) * cmux-next: failing test for nested agents in the agents pill list Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: the agents pill lists a nested agent right under the one that spawned it The list was flat by start time, so an agent another one spawned read as a peer of every top-level agent. It now sits under its parent, moved in a step per level, when both are in the same section. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: cover nestAgents' edge cases; the pill's indent follows the reading direction Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: running subagent groups start open; each chat keeps its open folds (#22525) * cmux-next: failing test for subagent groups that start open while their agents run Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: the subagent groups test starts its second batch after a reply A read joins the open batch; the session's own text ends it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: a group of running subagents starts open; a chat's open folds are its own Every subagent group started closed, so a live batch of agents showed one summary line until the person clicked it. A group with an agent still running in the working turn now opens in the render that first draws it, once: a group the person closes stays closed. A finished chat's groups, and an agent a cancel left "running", still start closed. The open folds were one set for every chat the pane showed, and row ids such as subagents-<seq> repeat across chats, so a group opened in one chat showed open in the next at the same place. A chat switch now clears them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: subagent groups test covers a switch back and an earlier turn's stale agent Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: each chat keeps its open folds; only the latest turn's running groups open Switching chats cleared the open folds, so a running group the person had closed opened again on the way back, and a group left open closed. Each chat now keeps its own set for a switch back. An earlier turn's group with an agent still running, or one a cancel left running, no longer opens when the next turn starts. The gallery's running entry checks the group starts open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: the Subagent of pill names the chat as the chat list does (#22526) * cmux-next: failing test for the Subagent of pill of an untitled chat Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: an untitled chat's Subagent of pill names the chat With no title and no name yet, the pill read "Subagent of" with nothing after it. It now names the chat as the pane's header and label do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: the Subagent of test expects the chat list's name, generated names included Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: the Subagent of pill names the chat as the chat list does acpmux names an untitled session after its agent (claude-2), and the pill showed that while the chat list showed the first prompt or New chat. The pill now reads sessionTitle, as the list does. A parent subagent with an empty name falls back to the chat too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: answered and cancelled questions leave a record in the transcript (#22531) * cmux-next: failing test for answered and cancelled question records in the transcript Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: an answered or cancelled question leaves a record in the transcript Once a question was answered its card went and nothing in the chat said what was chosen. The decision acpmux logs (permission_decision) now draws a compact record where the card was: "Auth method: OAuth 2.0", or "Not answered: ..." for one cancelled or declined. It renders through the card's own answered and cancelled states, stays out of the turn's Worked for fold, and comes back on a reload because both records are in the log. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: question record tests for work after the answer and comma-joined Claude answers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: work after an answer draws below its record; Claude answers split only for multi-select The record left the open activity row and reply segment open, so the next tool call joined the AskUserQuestion's row above the record. The record now closes them, as a background task's line does. A single-select Claude answer is read whole, Other text included, and a multi-select is read longest label first, so a label with a comma stays one choice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: failing test for a resync that replays a question's answer Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: a resync that replays a question's answer leaves the reply streaming whole After a lag the pane rebuilds the transcript, then replays the live records after its cursor. The replayed answer found its record drawn already but closed the stream again, so the reply streaming on split in two. A record drawn already is now left as it is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux NEXT Settings: the Chiefs card names each Chief's backend as Home does (#22420) * test(settings): Chiefs card names a Chief's backend as Home does Red on the base: the row prints the record's raw harness id ("codex", "default") where Home's chip and the Chief editor say Codex and Claude. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(settings): Chiefs card names a Chief's backend as Home does The row printed the record's raw harness id and fell back to Default whenever no harness was set, even with a model chosen. It now uses chiefBackendLabel (absent or "default" is the default engine) and shows the model and effort whenever any engine field is set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(settings): a Chiefs row with only "default" named reads Default Review follow-up: {harness: "default"} and no engine at all mean the same, so both read "Default (Chief's usual)"; a model with no harness still names Claude. Adds the no-harness, model-only case the PR leads with. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next Settings: tabbing through the background picker previews only the tile you stop on (#22441) * test(settings): tabbing across background tiles previews only the one you rest on Red on the base: keyboard focus previews each tile at once (no dwell), so Tab across the grid repaints every window per tile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(settings): keyboard focus on a background tile previews after the dwell Focus previewed at once, so Tab across the grid repainted every window and loaded full-size art per tile; it now waits the 150 ms the pointer waits, and moving on cancels it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(settings): the rested tile is the one previewed; docs name the focus dwell Review follow-up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux NEXT Chief Settings: clearing the name shows the Chief's name again (#22445) * test(home): clearing the Chief's name in Chief Settings shows it again Red on the base: leaving the field blank renames nothing but leaves the field empty, so the card shows no name for a Chief that kept its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(home): a blank Chief name shows the kept name again Leaving the name field blank renamed nothing but left it empty. It now shows the name the Chief keeps, and a saved name shows trimmed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at b1d9352299f1 - schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39399176716 --------- 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 Settings: This Workspace's Opacity over Art shows only over a picture (#22454) * test(settings): This Workspace's Opacity over Art shows only over a picture Red on the base: the slider shows for a workspace with No wallpaper, or one following an app-wide None, where the window paints no tint over art and the slider changes nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(settings): Opacity over Art shows only while the workspace shows a picture The window tints a workspace only over its picture, so with No wallpaper (its own, or the All Workspaces None it follows) the slider changed nothing. It now shows only while the workspace has a picture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(settings): a following workspace keeps Opacity over Art Review follow-up: a workspace that follows an app-wide None can still show a window's own picture (appearance.set scope=window), which the page does not see, and its tint applies there. Only the workspace's own No wallpaper hides the slider. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux NEXT Chief Settings: why a change did not take stays said, and VoiceOver hears it (#22494) * test(home): Chief Settings harness picks reset model and effort; a refused pick reverts Red on the base: a harness pick writes only the harness, so the old harness's model stays chosen, and a pick the brain refuses stays selected in its popup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(home): a Chief Settings harness pick resets model and effort; a refused pick reverts The popover sent only the harness, and the brain keeps a field it is not sent, so a Claude model stayed chosen after a switch to Codex. The Chief editor's backend picker clears both; the popover now sends the harness with model and effort cleared in one chief.engine.set (HomeChiefEngineSource.set takes several fields). A pick the brain refuses shows its last answer again instead of leaving the refused value selected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(home): picking the harness Default runs keeps the model and effort Review follow-up: compare a harness pick with the harness the turns run on, so naming the harness Default already runs is not a change. Drops the unused single-field set convenience, and the refused-pick test no longer races the card's own first read on the failure note. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 2dde177073f0 - Packages/macOS/CmuxNext/ci-target-graph.json - schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39394994833 * test(home): a refused Chief Settings pick stays said through a refresh Red on the base: the engine watch refreshes the card on every typing or transcript change, and each refresh hides the note, so a refused pick's reason flashes and is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(home): Chief Settings keeps the note about a refused change, and VoiceOver says it A refresh (the engine watch runs one on every typing or transcript change) hid the note, so a refused pick's reason flashed and was gone, and a picture that could not be used was never seen: its open panel had closed the card. The note from the person's own change now stays until their next change or another Chief, a picture failure also beeps, and every note is announced to VoiceOver. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(home): Chief Settings' engine rows move to HomeChiefSettingsView+Engine.swift check-no-godfiles: the view's file is over its 400-line budget on the base (402) and this PR grew it; the engine section (read, pick, say, show, the pickers) moves to its own extension file unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(home): Chief Settings announces a note once, and closing the card clears it Review follow-ups: a brain that still does not answer no longer re-announces its note on every refresh; a closed card's announcement comes from the main window; closing the card clears a change's note (a picture's, set after its open panel closed the card, waits for the next open); a late pick for the Chief's other host is not shown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at c5890b6f3f27 - Packages/macOS/CmuxNext/ci-target-graph.json - schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39419475923 --------- 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 Chief Settings: effort levels by name, plural counts, one Diagnostics control (#22503) * test(home): Chief Settings names effort levels, counts in plural, one Diagnostics control Red on the base: efforts show as the brain's ids ("xhigh"), the last turn reads "1 tool calls", and VoiceOver and Tab find Diagnostics twice (the triangle and its title). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(home): Chief Settings names effort levels, counts in plural, one Diagnostics control Effort pickers showed the brain's ids ("xhigh"); they show names, with an unknown id as it is. The last turn read "1 tool calls" with an English $ and %; the counts are plural strings and the numbers follow the reader's locale. The Diagnostics title was a second VoiceOver and Tab stop beside its triangle; the triangle is the one control. All new strings are translated in the Home catalog's 21 locales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(home): review follow-ups for the Chief Settings words German and French write Minimal the same as English (identity record); ja Extra High is no longer the same as Max; es one tool call is singular; ru and uk failures read N with an error; the cache share rounds half up as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next palette: the Background row names your own image or a macOS wallpaper (#22512) * test(settings): the palette's Background row names an image or wallpaper it does not list Red on the base: your own image or a macOS wallpaper marks no option current, so the row shows the raw value in quotes, and a retired painting id marks nothing current either. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(settings): the palette's Background row names what the window shows Your own image or a macOS wallpaper is one more option, current, by its name (BackdropSelection.title), instead of the raw value in quotes; a retired painting id marks the work that replaced it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at a99856264938 - Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39416454156 --------- 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 Chief Dock: keyboard and VoiceOver fold it cleanly, the mascot's hover stops flickering (#22422) * test(chief-dock): folded cards take no Tab stop, controls name what they do, Escape folds Red at feat-cmux-next: 5 of 7 fail (folded card actions are Tab stops, rail and close labels repeat Agent Chat/New chat/Move to tab, Escape does nothing, the badge stops at the four drawn cards). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(chief-dock): keyboard and VoiceOver fold the dock cleanly, the mascot's hover stops flickering Folded, the deck is inert, so its invisible Move to tab and close buttons are no longer Tab stops that pop their actions over the folded cards. Escape folds an open dock and puts focus back on the mascot. The rail's toggle is one Detach button with aria-pressed, the chevron says Expand or Collapse with aria-expanded, and a card's close says Collapse, instead of three controls sharing Agent Chat, New chat and Move to tab. The badge counts every chat that waits, not only the four drawn. The hover lift is on the mascot image, so the button's hit box no longer moves out from under a pointer at its bottom edge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(chief-dock): a card's close hands focus to the mascot; a modified Escape is left alone Review follow-ups: the close button folded the deck with focus still on it, inside the now inert cards, so WebKit dropped focus to the page. Escape skips Cmd/Ctrl/Option/Shift and IME composition as useEscapeToDismiss does. Drops the mascot button's dead transform transition, names translate and scale in will-change, and uses Abdocken (de) and Desacoplar (pt-BR) for Detach. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(chief-dock): Escape goes through ui/KeyboardScope, not a raw key handler ui-rules: page code outside src/ui has no raw key handlers. The dock's controls sit in a KeyboardScope (display: contents, so the layout is unchanged) whose capture handler folds the dock on Escape. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux NEXT Chief pet: VoiceOver can press the character and each notice (#22432) * test(chief-pet): VoiceOver presses the character and the notices Expected red at feat-cmux-next: the character is an image with no press action, and every capsule of a folded deck reads as a button although only the front one takes a click and none of them presses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(chief-pet): VoiceOver can press the character and each notice The panel never takes the keys, so VoiceOver's press is the only way in that is not the pointer, and neither the character (an image) nor a notice capsule (a button with no press action) took one. The character is now a button whose press is a click on it (the reply box), and a capsule's press opens its notice. A folded deck reads as its front notice only, the one a click can open; unfolded, every notice reads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 54b0aeca15c6 - schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39396331700 --------- 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 New Tab: a page you switch back to replays no recent-chat entrance (#22464) * cmux NEXT New Tab: failing test, switching away and back replays recent chats' entrance pageSwitch (webviews/test/support) hides and shows the page, counts fetches and entrance animations. A chat list change that lands as the page shows again fades and slides its cards on the first frame back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux NEXT New Tab: a page you switch back to starts no entrance for changes made while away shared/pageReveal.ts owns "may an entrance run now": not while the page is hidden, and not until its first frame back has passed. useListMotion asks it, so a chat list change that lands as the page shows again takes its places at once. The page entry installs the settle, which finishes every finite animation when the page hides and when it shows. plans/cmux-next/shared-projections.md writes down the pattern: one projection per data source above the tab, snapshot rendering, no refetch on remount or switch, and entrances on data-change diffs only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux NEXT New Tab: review follow-ups for the switch-back helper The animate stub carries finished, id and currentTime, so Web Animations code that awaits finished can use the helper. An attribute change counts as an entrance only when the changed attribute is what starts it. dispose removes the visibility getters. The motion-off test no longer asserts on the global document. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * Run Settings and palette agent updates on their captured owner (#21447) * acpmux: require version evidence for already-current updates Replays #21398's change onto current feat-cmux-next as one commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agents: refresh inventory versions without probing or updating Replays #21401's change onto current feat-cmux-next as one commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agents: connect explicit version refresh to the selected owner Replays #21402's change onto current feat-cmux-next as one commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add owner update terminal jobs with durable replay Replays #21414's change onto current feat-cmux-next as one commit. Keeps the crash ratchet flat: poisoned locks are recovered instead of unwrapped (only a held job lock is busy), and the job status parses its decimal strings with radix: 10. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Preserve app mutation keys through shared action runs Replays #21429's change onto current feat-cmux-next as one commit, written without growing CmuxNextApp's source lines (size cap). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Preserve app callers through the shared action registry Replays #21436's change onto current feat-cmux-next as one commit. ControlRequest takes callerApp in its init so CmuxNextApp's source lines don't grow (size cap). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Share agent CLI update UI across Settings and actions Replays #21447's change onto current feat-cmux-next as one commit, together with its follow-up fix. Settings and the palette run agent updates on their captured owner route instead of typing a health argv into a focused pane. The update owner prepares only npm and native installations, and refuses a family when any of its rows is another channel or already current, so both surfaces submit only those families and Settings shows Update only for rows whose channel the owner runs. A start the owner refuses (agent_cli_job.*, answered before it journals a creation) leaves no "unknown" job that blocks that CLI. One family's failure does not stop the rest of a batch; the batch reports every failure. A lost reply still keeps its unknown job, since the terminal may exist. Update, Update all and turning Update Automatically on still need a gesture or a confirmed user, as #21439 requires. The update action, its owner route and the health-row reader live in a new CmuxNextAgentUpdates module, leaving thin app glue in CmuxNextApp. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Require trusted page context for Settings agent updates Replays #21439's change onto current feat-cmux-next as one commit. Turning Update Automatically on now needs a gesture or native confirmation too, since acpmux runs the update after its next check (review finding); SettingsHarnesses is compacted so CmuxNextApp's source lines don't grow (size cap). The health read defaults to the named AcpmuxEnvironment.harnessHealth rather than an async closure literal (crash ratchet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 0aae74c95fc8 - regenerated plans/cmux-next/action-surfaces.json - regenerated schemas/defaults/cmux-next.json - vp fmt webviews/src/agent-session/acpmux/replyHref.ts - vp fmt webviews/src/pages/markdown/links.ts - vp fmt webviews/test/markdown-cmux-app-links.test.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39208575467 * Bind the Settings update scope with withValueInTask TaskLocal.withValue frees its marker with the task allocator even when no task is current (cx-bpcj); the crash-class gate bans it. Use the shared withValueInTask helper, as ActionRunScope does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 45adaef20fc3 - regenerated plans/cmux-next/action-surfaces.json - regenerated web/data/cmux-shortcuts.generated.json - regenerated cmux-tui/crates/cmux-app-host/generated/cmux-app.d.ts - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39219479749 * test(next): agent CLI updates survive reconnects, unknown exits, owner mismatches and palette failures Regression tests for four defects in the shared agent CLI update action: a reconnect to the same owner hides and blocks the job, a new daemon session leaves a job that blocks the CLI forever, an exit with an unknown outcome blocks the CLI forever, a receipt for another owner leaves a blocking job, and a palette run's failure is never shown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): agent CLI update jobs follow their owner across reconnects and settle on any end - Showing, refreshing and blocking now share one owner rule: a job whose route went stale is re-captured when the app still routes the same machine and session (its receipts stay observable after a reconnect), and otherwise neither shows nor blocks the CLI. - An exit with an unknown process result stays state "exited" with outcome "unknown", so it settles instead of blocking forever. - A receipt for another owner drops its job before the error. - A palette run (no caller capturing work) shows its failure through the registry's refusal instead of dropping it. - Nothing-to-update and still-updating refusals get their own localized text instead of the generic add/remove agents message. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): agent update survives a reconnect mid-batch and a lost start reply A reconnect while one family's start waits must not start the next family on the re-captured owner and leave it with a job that blocks that CLI. A lost start reply must be replayed by its key on refresh, so the job gets its receipt or the owner's refusal clears it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): agent update start keeps its captured owner and replays lost replies start() re-read intent.route after every await, while showing or refreshing the jobs can swap it for a re-capture after a reconnect. A batch then went on to start the next family on the new connection, whose route had no open carrier, so that family kept an unknown job with no receipt that blocked its CLI for good. start() now captures the route once and stops when it retires, and a route opens its carrier on start as well as on inventory and observe. A lost start reply left an unknown job that blocked the CLI until restart. The intent keeps the child key, and refresh sends it again on the current owner: the owner replays the key, so the job gets its receipt, or its refusal clears the job. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): a busy answer to a replayed agent CLI update start keeps its job The owner answers agent_cli_job.busy before it looks the key up, so on a replay busy usually means the key's original request is still running. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): a busy answer to a replayed agent CLI update start keeps its job Review of #21447: refresh treated agent_cli_job.busy on a replay as the key creating nothing, dropping the job while its original request could still create the terminal, which then went untracked and unblocked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 99a29e6f4d3a - plans/cmux-next/action-surfaces.json - plans/cmux-next/actions.md Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39360359843 * chore(next): PR autofix at 3b5a7cad2bea - Packages/macOS/CmuxNext/ci-target-graph.json - schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39400996092 * test(next): settled owner failures and ended jobs never block agent CLI updates (red) Regressions for the #21447 review: - The owner journals a failed creation under its key and answers every replay with the same failure (operation.failed, mutation.indeterminate). Such an answer, on the first start or on a replay of a lost reply, must settle the job instead of leaving it unknown and blocking the CLI for the rest of the session. - A job that already ended must not block the next update of its CLI just because nothing refreshed Settings > Agents in between; a job that still runs keeps refusing it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): settle agent CLI updates the owner answered, and check ended jobs before refusing - Any coded owner answer to a start settles its key. agent_cli_job.* comes before a creation is journaled; any other code (a failed creation, operation.failed, or an interrupted one, mutation.indeterminate) is the key's journaled outcome and the owner answers every replay the same way. Such an answer, on the first start or on a replay, now drops the job instead of leaving it unknown and blocking the CLI for the rest of the session. Only a failure with no owner answer (a lost reply) is replayed; busy on a replay still keeps the job. - A start that finds an earlier job of the same CLI still unended refreshes once before refusing, so a job that already exited no longer blocks the next palette or Settings update until someone opens Settings > Agents. A job that still runs keeps refusing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): a coded answer to a replayed agent CLI update start keeps the lost job (red) Regression for the delta review of 0ad8305a5bbf: a replay can get a coded answer the owner sends before it reads its journal (daemon_shutdown_pending during a handoff, operation.failed from a registry error), while the lost first start may have created the update terminal. Dropping the job then hides a running update and unblocks a second one of the same CLI. A first start's fresh key has nothing running, so any coded answer there still settles; its cases are now operation.failed and daemon_shutdown_pending, with a fixture message that matches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): only a refusal settles a replayed agent CLI update start A replay of a lost start now drops its job only on an agent_cli_job.* refusal, as before 0ad8305a5bbf. Another coded answer can come before the owner reads its journal (daemon_shutdown_pending during a handoff, or a registry error mapped to operation.failed) while the lost first start created the terminal, so the job keeps blocking its CLI and the next refresh replays the same key. A first start's fresh key still settles on any coded answer, which is what keeps a failed creation from blocking the CLI for the session. A lost reply whose creation then failed is still replayed until the owner can say so; #21469's journal resolve settles that case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): an indeterminate first agent CLI update start keeps blocking its CLI (red) From the second delta review: the owner answers a fresh key with mutation.indeterminate when a failed creation left its reserved terminal possibly live (AmbiguousLive in creation_settlement.rs). Dropping that job would let a second update of the same CLI start beside it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): an indeterminate first agent CLI update start keeps its job `answered` no longer settles mutation.indeterminate: the owner says a failed creation's effect may have run, so the job stays unknown and keeps blocking its CLI, like a lost reply, until it is replayed or its owner session retires it. The comments no longer claim a coded first-start answer always means nothing runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- 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: 512 classic workspaces leave no terminal without a PTY, failing first The 512-workspace simulation now allows no "PTY capacity exhausted" failure (about 40-53 today: macOS hands out at most kern.tty.ptmx_max, 511, PTYs), and a new simulation checks that a workspace that came over in the background has its terminal tabs parked (dead, relaunch.on_show) and that showing it starts them in the same tabs. Red: the test, the park-tabs request and capability (tab-park-v1) and TabRelaunch.onShow; no daemon serves park-tabs and the migration parks nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: classic workspaces in the background come over parked; each terminal starts on first show macOS hands out at most kern.tty.ptmx_max (511) PTYs, shared with every process, so a classic session at its limits (512 workspaces) left 40-53 workspaces with no terminal. Now: - cmux-tui: park-tabs (tab-park-v1) ends the shells of terminal tabs and keeps the tabs as keep-layout tabs, recorded first; the kept_tabs row gets on_show (additive column), and relaunch reads {cwd, on_show: true}. - The migration parks each workspace it makes that is not its window's selected one, once its terminals are in the store. A workspace whose classic terminals carry scrollback or an agent keeps running: its shell prints the scrollback at its first prompt. - A window that shows a workspace restarts its parked tabs in place (restart-tab, DaemonService.wakeParkedTabs); the connect-time kept-tab relaunch skips them, and they draw as the terminals they become. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: wakeParkedTabs imports CmuxNextDaemon publicly (its parameter is WorkspaceModel) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: only the launch's classic run parks; the merge test waits for the new workspace's group Import Workspaces from Classic cmux is the person asking now: its terminals open live, as before (ClassicImportTests type into them). The re-upgrade test read the tree before the daemon's event with the merged workspace's group landed; it now waits for it, up to 5 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: tab-park-v1 is listed as an optional daemon capability Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cmux-next): put new chrome fonts on the type scale; Chromium swipe arrows (#22483) * fix(cmux-next): put new chrome fonts on the type scale; Chromium swipe arrows The Screenshot section hint and the Home sidebar's Requests button use TypeScale steps and the emphasis weight instead of 13/11 pt medium; the file grid's duration badge uses the scale's semibold instead of medium. Sleepy Mode's scene (a 96 pt moon, a 64 pt thin clock, animated Zs) is illustration, not chrome, so its four raw values are baselined. The swipe-navigation pill draws Chrome's toolbar Back/Forward icons from the pack instead of SF Symbol arrows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 9255418df048 - schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39408665998 * fix(cmux-next): review fixes: skip Sleepy Mode's scene, size the swipe arrows - The type-scale ratchet skips SleepySceneView.swift as illustration (like the DEBUG text probes) instead of raising its baseline rows, which the ratchet never allows. - Chrome's arrow path spans about 60% of its box, so the swipe pill draws it at 22/24 pt to keep the old 14/16 pt SF arrows' size. - schemas/defaults/cmux-next.json goes back to the base: the autofix line belongs to the remote-hosts lane's reservation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com> * docs: merge on a passing review, not a person's approval (#22560) * docs: merge on a passing review, not a person's approval Agents were holding reviewed perf and UI PRs for a day waiting on the "into main, explicit approval after dogfood or a direct merge directive" rule. On either branch a PR now merges when an independent review subagent passes it and it causes no red of its own; base reds, flakes and pending checks don't hold it. Risky user-visible behavior ships behind a setting or Debug toggle. A red base is now priority zero to fix directly. The risk:high tier, merge freezes, never --admin, gh-merge-green on main and ready-to-land on feat-cmux-next stay as they were. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: keep gh-merge-green's gate on main and the take-back hold Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Leo <cheerleaderleo@outlook.com> Co-authored-by: Abdulaziz Albahar <67667005+azooz2003-bit@users.noreply.github.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com> Co-authored-by: lawrencecchen <54008264+lawrencecchen@users.noreply.github.com>
* acpmux: require version evidence for already-current updates Replays #21398's change onto current feat-cmux-next as one commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agents: refresh inventory versions without probing or updating Replays #21401's change onto current feat-cmux-next as one commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agents: connect explicit version refresh to the selected owner Replays #21402's change onto current feat-cmux-next as one commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add owner update terminal jobs with durable replay Replays #21414's change onto current feat-cmux-next as one commit. Keeps the crash ratchet flat: poisoned locks are recovered instead of unwrapped (only a held job lock is busy), and the job status parses its decimal strings with radix: 10. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Preserve app mutation keys through shared action runs Replays #21429's change onto current feat-cmux-next as one commit, written without growing CmuxNextApp's source lines (size cap). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Preserve app callers through the shared action registry Replays #21436's change onto current feat-cmux-next as one commit. ControlRequest takes callerApp in its init so CmuxNextApp's source lines don't grow (size cap). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Share agent CLI update UI across Settings and actions Replays #21447's change onto current feat-cmux-next as one commit, together with its follow-up fix. Settings and the palette run agent updates on their captured owner route instead of typing a health argv into a focused pane. The update owner prepares only npm and native installations, and refuses a family when any of its rows is another channel or already current, so both surfaces submit only those families and Settings shows Update only for rows whose channel the owner runs. A start the owner refuses (agent_cli_job.*, answered before it journals a creation) leaves no "unknown" job that blocks that CLI. One family's failure does not stop the rest of a batch; the batch reports every failure. A lost reply still keeps its unknown job, since the terminal may exist. Update, Update all and turning Update Automatically on still need a gesture or a confirmed user, as #21439 requires. The update action, its owner route and the health-row reader live in a new CmuxNextAgentUpdates module, leaving thin app glue in CmuxNextApp. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Recover agent updates across app restarts Replays #21469's change onto current feat-cmux-next as one commit, on top of the owner-channel fix, with these changes so a retained intent always has a way out: - A start the owner refuses before journaling (agent_cli_job.*) settles its intent, in a batch and on Resume, instead of leaving a Resume that is refused again. - Any terminal exit settles, including one whose outcome is unknown, and a failed creation state settles on recovery. - A person may dismiss an unstarted (not_found, prepared) or indeterminate intent from Settings; dismissing stops nothing. Pending stays fenced. Resume and Dismiss need a gesture, like Update. - The Rust ledger gains agent-cli-intents-settle, which deletes rows. Recording drops rows older than seven days and keeps the newest 256 per actor instead of failing at a cap. Recovery, the ledger port and the owner route live in the CmuxNextAgentUpdates module; the app keeps only its daemon glue. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Require trusted page context for Settings agent updates Replays #21439's change onto current feat-cmux-next as one commit. Turning Update Automatically on now needs a gesture or native confirmation too, since acpmux runs the update after its next check (review finding); SettingsHarnesses is compacted so CmuxNextApp's source lines don't grow (size cap). The health read defaults to the named AcpmuxEnvironment.harnessHealth rather than an async closure literal (crash ratchet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Let every retained agent update end Review follow-ups on #21469, so a retained intent always has a way out. - A closed update terminal settles its job. terminal.wait_exit answers terminal.closed, then selector.not_found for the tombstoned terminal; both mean the process ended. A missing session or connection still keeps it. - Ledger reads apply the seven-day age bound, not only recording, which a blocked family never reaches. - A row from a session no route can reach (a reinstalled server) no longer blocks the current session's update; it waits out the age bound. Rows of the same session, or of a session recovery reattached, still block. - A failed record clears the batch's jobs, since nothing started. - A creation still pending ten minutes after a recovery first saw it may be dismissed. - agent_cli_job.busy no longer settles: the owner may be running that key. - A test covers adding recorded_at to a ledger created without it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 0aae74c - regenerated plans/cmux-next/action-surfaces.json - regenerated schemas/defaults/cmux-next.json - vp fmt webviews/src/agent-session/acpmux/replyHref.ts - vp fmt webviews/src/pages/markdown/links.ts - vp fmt webviews/test/markdown-cmux-app-links.test.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39208575467 * Keep other sessions' agent update rows blocking A row from a session recovery cannot reach blocks the CLI on that machine again. "No current route" cannot tell a dead session from a second live server on the same machine that may still be running the updater, so unblocking it could start two global installs at once. The ledger's seven-day read bound retires true orphans. Also notes at the closed-terminal settle that Rust answers terminal.closed before it queues the runtime signal, so the updater may still be dying in its close grace. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 953137a - vp fmt webviews/src/pages/markdown/README.md - cargo fmt in cmux-tui Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39217229319 * Bind the Settings update scope with withValueInTask TaskLocal.withValue frees its marker with the task allocator even when no task is current (cx-bpcj); the crash-class gate bans it. Use the shared withValueInTask helper, as ActionRunScope does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 45adaef - regenerated plans/cmux-next/action-surfaces.json - regenerated web/data/cmux-shortcuts.generated.json - regenerated cmux-tui/crates/cmux-app-host/generated/cmux-app.d.ts - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39219479749 * chore(next): PR autofix at 1ee01b5 - regenerated plans/cmux-next/action-surfaces.json - regenerated web/data/cmux-shortcuts.generated.json - regenerated cmux-tui/crates/cmux-app-host/generated/cmux-app.d.ts - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39219477932 * Translate the agent update intent argument into every locale check-l10n flagged argument.agent.harness.intent as missing 12 of the 21 locales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): agent CLI updates survive reconnects, unknown exits, owner mismatches and palette failures Regression tests for four defects in the shared agent CLI update action: a reconnect to the same owner hides and blocks the job, a new daemon session leaves a job that blocks the CLI forever, an exit with an unknown outcome blocks the CLI forever, a receipt for another owner leaves a blocking job, and a palette run's failure is never shown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): agent CLI update jobs follow their owner across reconnects and settle on any end - Showing, refreshing and blocking now share one owner rule: a job whose route went stale is re-captured when the app still routes the same machine and session (its receipts stay observable after a reconnect), and otherwise neither shows nor blocks the CLI. - An exit with an unknown process result stays state "exited" with outcome "unknown", so it settles instead of blocking forever. - A receipt for another owner drops its job before the error. - A palette run (no caller capturing work) shows its failure through the registry's refusal instead of dropping it. - Nothing-to-update and still-updating refusals get their own localized text instead of the generic add/remove agents message. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): agent update survives a reconnect mid-batch and a lost start reply A reconnect while one family's start waits must not start the next family on the re-captured owner and leave it with a job that blocks that CLI. A lost start reply must be replayed by its key on refresh, so the job gets its receipt or the owner's refusal clears it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): agent update start keeps its captured owner and replays lost replies start() re-read intent.route after every await, while showing or refreshing the jobs can swap it for a re-capture after a reconnect. A batch then went on to start the next family on the new connection, whose route had no open carrier, so that family kept an unknown job with no receipt that blocked its CLI for good. start() now captures the route once and stops when it retires, and a route opens its carrier on start as well as on inventory and observe. A lost start reply left an unknown job that blocked the CLI until restart. The intent keeps the child key, and refresh sends it again on the current owner: the owner replays the key, so the job gets its receipt, or its refusal clears the job. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): a busy answer to a replayed agent CLI update start keeps its job The owner answers agent_cli_job.busy before it looks the key up, so on a replay busy usually means the key's original request is still running. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): a busy answer to a replayed agent CLI update start keeps its job Review of #21447: refresh treated agent_cli_job.busy on a replay as the key creating nothing, dropping the job while its original request could still create the terminal, which then went untracked and unblocked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): retained agent update rows no owner can reach are shown and dismissable (red) Regressions for the #21469 review: - A ledger row whose machine/session no route can capture blocks that CLI on that machine for up to seven days, but state never shows it and dismiss cannot reach it. The tests expect it shown as state "unknown" with can_dismiss, a gesture-only dismiss that deletes the row, and the block lifting after it (recovery, actions and the AppServices journey). - A failed receipt attach after a successful start aborts the whole batch; the test expects it reported while the other families still start. - Rust: one ledger row that fails validation fails the whole list; the test expects the bad rows skipped and the good one returned. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): show and let a person dismiss agent update rows no owner can reach A retained ledger row whose machine/session no route captures blocked that CLI on that machine for up to seven days while state never showed it and dismiss could not reach it. - state now lists, after the jobs of reachable intents, every unsettled retained row none of them shows, as state "unknown" with can_dismiss. What start blocks on is therefore always shown. - dismiss(key) on such a row settles it locally (a ledger delete, plus any hidden in-memory job), still behind a gesture; it stops nothing. - An unreadable ledger clears the retained rows instead of showing stale ones. - A failed receipt attach after a successful start is reported with the batch's other failures instead of aborting the remaining families. - Rust: read_rows skips and logs a row that fails validation instead of failing the whole list. - rustfmt the merged agent_cli_job.rs lock line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): busy first starts, dismiss during a replay, and resolve-then-replay (red) Regressions for the #21469 review at 217ba19: - A first start the owner answers busy created nothing (busy comes before the key lookup), but its row was kept: it blocked a retry click and a later refresh started the update after the person saw a failure. - A dismiss while a replay is in flight deleted the row the replayed receipt then could not attach to, leaving an untracked updater. - A lost reply whose key the owner has nothing for (not_found, prepared) was replayed by refresh with no fresh gesture instead of offering Resume. - A batch that stops early should name the families it never sent. The replayed-row test now uses an owner that cannot resolve the key, where the replay is the only path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 99a29e6 - plans/cmux-next/action-surfaces.json - plans/cmux-next/actions.md Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39360359843 * fix(next): settle busy first starts, hold replays against dismiss, and resume after not_found - A first start the owner answers busy settles. The owner answers busy from its inventory reservation or execution lock before it looks up the key, and send() only ever sends a fresh key, so nothing was created for it. Busy still keeps the row on a replay or resume, where the key may really be in flight. - A replay holds its intent busy, so a dismiss mid-replay is refused instead of deleting the row the replayed receipt attaches to. Settling a key with its intent also drops it from the unsent replays. - When resolve answers not_found or prepared for a lost reply, refresh no longer replays it: replaying is an execution request with no fresh gesture. The row offers Resume. Keys resolve cannot answer are still replayed. - A batch that stops early names every family it never sent in its failure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 3b5a7ca - Packages/macOS/CmuxNext/ci-target-graph.json - schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39400996092 * test(next): settled owner failures and ended jobs never block agent CLI updates (red) Regressions for the #21447 review: - The owner journals a failed creation under its key and answers every replay with the same failure (operation.failed, mutation.indeterminate). Such an answer, on the first start or on a replay of a lost reply, must settle the job instead of leaving it unknown and blocking the CLI for the rest of the session. - A job that already ended must not block the next update of its CLI just because nothing refreshed Settings > Agents in between; a job that still runs keeps refusing it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): settle agent CLI updates the owner answered, and check ended jobs before refusing - Any coded owner answer to a start settles its key. agent_cli_job.* comes before a creation is journaled; any other code (a failed creation, operation.failed, or an interrupted one, mutation.indeterminate) is the key's journaled outcome and the owner answers every replay the same way. Such an answer, on the first start or on a replay, now drops the job instead of leaving it unknown and blocking the CLI for the rest of the session. Only a failure with no owner answer (a lost reply) is replayed; busy on a replay still keeps the job. - A start that finds an earlier job of the same CLI still unended refreshes once before refusing, so a job that already exited no longer blocks the next palette or Settings update until someone opens Settings > Agents. A job that still runs keeps refusing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): a coded answer to a replayed agent CLI update start keeps the lost job (red) Regression for the delta review of 0ad8305: a replay can get a coded answer the owner sends before it reads its journal (daemon_shutdown_pending during a handoff, operation.failed from a registry error), while the lost first start may have created the update terminal. Dropping the job then hides a running update and unblocks a second one of the same CLI. A first start's fresh key has nothing running, so any coded answer there still settles; its cases are now operation.failed and daemon_shutdown_pending, with a fixture message that matches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): only a refusal settles a replayed agent CLI update start A replay of a lost start now drops its job only on an agent_cli_job.* refusal, as before 0ad8305. Another coded answer can come before the owner reads its journal (daemon_shutdown_pending during a handoff, or a registry error mapped to operation.failed) while the lost first start created the terminal, so the job keeps blocking its CLI and the next refresh replays the same key. A first start's fresh key still settles on any coded answer, which is what keeps a failed creation from blocking the CLI for the session. A lost reply whose creation then failed is still replayed until the owner can say so; #21469's journal resolve settles that case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): a first agent CLI update start settles on any owner answer From #21447's review: a first start that the owner answers with a coded failure other than an agent_cli_job.* refusal (operation.failed for a failed creation, daemon_shutdown_pending during a handoff) left its ledger row unknown and unsent. `send` now settles the intent on any coded answer, since a first start's fresh key cannot have anything running. Replays and resumes still settle only on a refusal: a code the owner sends before reading its journal proves nothing there, and recovery's resolve settles a journaled failure. Makes #21447's aFirstStartTheOwnerAnswersWithAFailureLeavesNoBlockingJob pass on this branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): an indeterminate first agent CLI update start keeps blocking its CLI (red) From the second delta review: the owner answers a fresh key with mutation.indeterminate when a failed creation left its reserved terminal possibly live (AmbiguousLive in creation_settlement.rs). Dropping that job would let a second update of the same CLI start beside it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): an indeterminate first agent CLI update start keeps its job `answered` no longer settles mutation.indeterminate: the owner says a failed creation's effect may have run, so the job stays unknown and keeps blocking its CLI, like a lost reply, until it is replayed or its owner session retires it. The comments no longer claim a coded first-start answer always means nothing runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): an indeterminate first agent CLI update start keeps its ledger row `answered` no longer settles mutation.indeterminate: the owner says a failed creation's effect may have run, so the row stays unknown and blocking, and recovery's resolve lets a person dismiss it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): a failed first agent CLI update start keeps its ledger row until the journal settles it (red) From #21469's review: a coded failure other than an agent_cli_job.* refusal can follow a durable creation (a launch activation failing after the commit maps to operation.failed), so deleting the ledger row on such an answer can hide a live update terminal and unblock a second one. The row must stay until recovery's resolve reads the owner's journal; a failed creation then settles. #21447's any-code variant of this test is replaced here; the fake owner can now answer resolve. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): a first agent CLI update start settles its ledger row only on a refusal Reverts this branch's `answered` rule from 1db61b6 and 2506184. A first start settles only on an agent_cli_job.* refusal or busy, as the branch had it; any other coded answer keeps the ledger row, because it can follow a durable creation. Recovery's resolve then reads the owner's journal: a failed creation settles, an indeterminate one becomes dismissable, and a created one reattaches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
#21480 (this PR's stacked parent) was brought current with feat-cmux-next, which now carries the landed #21447 and #21469. Merging its head keeps the agent-update chain exactly as #21480 resolved it; this PR's own change stays the server install replacement on top. Conflicts: Package.swift, Localizable.xcstrings and the generated plans/cmux-next/action-surfaces.json and actions.md were base drift on both sides; took #21480's newer side. plans/cmux-next/coordination/server.md kept both sides' Active streams lines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bd3-b49c-7d53-bea7-64f63a046a36&host=cosmic-gray
No conflicts. With #21447 and #21469 landed and #21480 merged in, this PR's diff against base is #21480's live-outcome observation plus the server install replacement (ServerReachPlan.diff compares installID, a closed ServerMachineSession is retired, and their tests). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bd3-b49c-7d53-bea7-64f63a046a36&host=cosmic-gray
* acpmux: require version evidence for already-current updates Replays #21398's change onto current feat-cmux-next as one commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agents: refresh inventory versions without probing or updating Replays #21401's change onto current feat-cmux-next as one commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agents: connect explicit version refresh to the selected owner Replays #21402's change onto current feat-cmux-next as one commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add owner update terminal jobs with durable replay Replays #21414's change onto current feat-cmux-next as one commit. Keeps the crash ratchet flat: poisoned locks are recovered instead of unwrapped (only a held job lock is busy), and the job status parses its decimal strings with radix: 10. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Preserve app mutation keys through shared action runs Replays #21429's change onto current feat-cmux-next as one commit, written without growing CmuxNextApp's source lines (size cap). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Preserve app callers through the shared action registry Replays #21436's change onto current feat-cmux-next as one commit. ControlRequest takes callerApp in its init so CmuxNextApp's source lines don't grow (size cap). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Share agent CLI update UI across Settings and actions Replays #21447's change onto current feat-cmux-next as one commit, together with its follow-up fix. Settings and the palette run agent updates on their captured owner route instead of typing a health argv into a focused pane. The update owner prepares only npm and native installations, and refuses a family when any of its rows is another channel or already current, so both surfaces submit only those families and Settings shows Update only for rows whose channel the owner runs. A start the owner refuses (agent_cli_job.*, answered before it journals a creation) leaves no "unknown" job that blocks that CLI. One family's failure does not stop the rest of a batch; the batch reports every failure. A lost reply still keeps its unknown job, since the terminal may exist. Update, Update all and turning Update Automatically on still need a gesture or a confirmed user, as #21439 requires. The update action, its owner route and the health-row reader live in a new CmuxNextAgentUpdates module, leaving thin app glue in CmuxNextApp. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Recover agent updates across app restarts Replays #21469's change onto current feat-cmux-next as one commit, on top of the owner-channel fix, with these changes so a retained intent always has a way out: - A start the owner refuses before journaling (agent_cli_job.*) settles its intent, in a batch and on Resume, instead of leaving a Resume that is refused again. - Any terminal exit settles, including one whose outcome is unknown, and a failed creation state settles on recovery. - A person may dismiss an unstarted (not_found, prepared) or indeterminate intent from Settings; dismissing stops nothing. Pending stays fenced. Resume and Dismiss need a gesture, like Update. - The Rust ledger gains agent-cli-intents-settle, which deletes rows. Recording drops rows older than seven days and keeps the newest 256 per actor instead of failing at a cap. Recovery, the ledger port and the owner route live in the CmuxNextAgentUpdates module; the app keeps only its daemon glue. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Require trusted page context for Settings agent updates Replays #21439's change onto current feat-cmux-next as one commit. Turning Update Automatically on now needs a gesture or native confirmation too, since acpmux runs the update after its next check (review finding); SettingsHarnesses is compacted so CmuxNextApp's source lines don't grow (size cap). The health read defaults to the named AcpmuxEnvironment.harnessHealth rather than an async closure literal (crash ratchet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Show agent update outcomes automatically Replays #21480's change onto current feat-cmux-next as one commit, on top of the rebuilt #21469. Settings, palette and recovered jobs follow their retained terminals. terminal-registry-changed events from the captured control connection only trigger a bounded read of the original receipt; they never dispatch work or imply completion. Completed batches stop observing. AgentCLIUpdateActions+Observation.swift lives in the CmuxNextAgentUpdates module beside the rest of the action, and the owner route captures the event stamp there. As in #21469, any exit, including one with an unknown outcome, settles its intent in the ledger. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Retire obsolete sessions when a paired server is reinstalled Replays #21487's change onto current feat-cmux-next as one commit, on top of the rebuilt #21480. A host reinstalled behind the same link socket is a replacement owner: the reach diff replaces its session when the paired installation changes, not only when the route does. A closed session is retired, so callbacks that still hold it cannot connect or wake it again. ServerMachineSession.close() is written on one line, like disconnect(), so CmuxNextApp gains no source lines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Let every retained agent update end Review follow-ups on #21469, so a retained intent always has a way out. - A closed update terminal settles its job. terminal.wait_exit answers terminal.closed, then selector.not_found for the tombstoned terminal; both mean the process ended. A missing session or connection still keeps it. - Ledger reads apply the seven-day age bound, not only recording, which a blocked family never reaches. - A row from a session no route can reach (a reinstalled server) no longer blocks the current session's update; it waits out the age bound. Rows of the same session, or of a session recovery reattached, still block. - A failed record clears the batch's jobs, since nothing started. - A creation still pending ten minutes after a recovery first saw it may be dismissed. - agent_cli_job.busy no longer settles: the owner may be running that key. - A test covers adding recorded_at to a ledger created without it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 0aae74c - regenerated plans/cmux-next/action-surfaces.json - regenerated schemas/defaults/cmux-next.json - vp fmt webviews/src/agent-session/acpmux/replyHref.ts - vp fmt webviews/src/pages/markdown/links.ts - vp fmt webviews/test/markdown-cmux-app-links.test.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39208575467 * Keep other sessions' agent update rows blocking A row from a session recovery cannot reach blocks the CLI on that machine again. "No current route" cannot tell a dead session from a second live server on the same machine that may still be running the updater, so unblocking it could start two global installs at once. The ledger's seven-day read bound retires true orphans. Also notes at the closed-terminal settle that Rust answers terminal.closed before it queues the runtime signal, so the updater may still be dying in its close grace. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 953137a - vp fmt webviews/src/pages/markdown/README.md - cargo fmt in cmux-tui Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39217229319 * Bind the Settings update scope with withValueInTask TaskLocal.withValue frees its marker with the task allocator even when no task is current (cx-bpcj); the crash-class gate bans it. Use the shared withValueInTask helper, as ActionRunScope does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 45adaef - regenerated plans/cmux-next/action-surfaces.json - regenerated web/data/cmux-shortcuts.generated.json - regenerated cmux-tui/crates/cmux-app-host/generated/cmux-app.d.ts - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39219479749 * chore(next): PR autofix at a51fde3 - regenerated plans/cmux-next/action-surfaces.json - regenerated web/data/cmux-shortcuts.generated.json - regenerated cmux-tui/crates/cmux-app-host/generated/cmux-app.d.ts - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39219476854 * chore(next): PR autofix at 226720e - regenerated plans/cmux-next/action-surfaces.json - regenerated web/data/cmux-shortcuts.generated.json - regenerated cmux-tui/crates/cmux-app-host/generated/cmux-app.d.ts - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39219477423 * chore(next): PR autofix at 1ee01b5 - regenerated plans/cmux-next/action-surfaces.json - regenerated web/data/cmux-shortcuts.generated.json - regenerated cmux-tui/crates/cmux-app-host/generated/cmux-app.d.ts - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39219477932 * Translate the agent update intent argument into every locale check-l10n flagged argument.agent.harness.intent as missing 12 of the 21 locales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-tui: drop the std::sync qualification the base's clippy fix removed (unused_qualifications) * chore(next): PR autofix at 52aaa2c - Packages/macOS/CmuxNext/ci-target-graph.json - plans/cmux-next/action-surfaces.json - plans/cmux-next/actions.md Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39360940433 * chore(next): PR autofix at cac5e24 - Packages/macOS/CmuxNext/ci-target-graph.json - plans/cmux-next/action-surfaces.json - plans/cmux-next/actions.md Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39361101859 * chore(next): PR autofix at a746f81 - Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39364024850 * chore(next): PR autofix at d86b10d - Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39366869850 * chore(next): PR autofix at 6714366 - Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39366896424 * Leave ci-target-graph.json to the base autofix Only the base autofix writes it; generated drift refused the hand edit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bbf-a30f-7992-abba-3e3ca6f5adc1&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>
* acpmux: require version evidence for already-current updates Replays #21398's change onto current feat-cmux-next as one commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agents: refresh inventory versions without probing or updating Replays #21401's change onto current feat-cmux-next as one commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agents: connect explicit version refresh to the selected owner Replays #21402's change onto current feat-cmux-next as one commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add owner update terminal jobs with durable replay Replays #21414's change onto current feat-cmux-next as one commit. Keeps the crash ratchet flat: poisoned locks are recovered instead of unwrapped (only a held job lock is busy), and the job status parses its decimal strings with radix: 10. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Preserve app mutation keys through shared action runs Replays #21429's change onto current feat-cmux-next as one commit, written without growing CmuxNextApp's source lines (size cap). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Preserve app callers through the shared action registry Replays #21436's change onto current feat-cmux-next as one commit. ControlRequest takes callerApp in its init so CmuxNextApp's source lines don't grow (size cap). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Share agent CLI update UI across Settings and actions Replays #21447's change onto current feat-cmux-next as one commit, together with its follow-up fix. Settings and the palette run agent updates on their captured owner route instead of typing a health argv into a focused pane. The update owner prepares only npm and native installations, and refuses a family when any of its rows is another channel or already current, so both surfaces submit only those families and Settings shows Update only for rows whose channel the owner runs. A start the owner refuses (agent_cli_job.*, answered before it journals a creation) leaves no "unknown" job that blocks that CLI. One family's failure does not stop the rest of a batch; the batch reports every failure. A lost reply still keeps its unknown job, since the terminal may exist. Update, Update all and turning Update Automatically on still need a gesture or a confirmed user, as #21439 requires. The update action, its owner route and the health-row reader live in a new CmuxNextAgentUpdates module, leaving thin app glue in CmuxNextApp. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Recover agent updates across app restarts Replays #21469's change onto current feat-cmux-next as one commit, on top of the owner-channel fix, with these changes so a retained intent always has a way out: - A start the owner refuses before journaling (agent_cli_job.*) settles its intent, in a batch and on Resume, instead of leaving a Resume that is refused again. - Any terminal exit settles, including one whose outcome is unknown, and a failed creation state settles on recovery. - A person may dismiss an unstarted (not_found, prepared) or indeterminate intent from Settings; dismissing stops nothing. Pending stays fenced. Resume and Dismiss need a gesture, like Update. - The Rust ledger gains agent-cli-intents-settle, which deletes rows. Recording drops rows older than seven days and keeps the newest 256 per actor instead of failing at a cap. Recovery, the ledger port and the owner route live in the CmuxNextAgentUpdates module; the app keeps only its daemon glue. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Require trusted page context for Settings agent updates Replays #21439's change onto current feat-cmux-next as one commit. Turning Update Automatically on now needs a gesture or native confirmation too, since acpmux runs the update after its next check (review finding); SettingsHarnesses is compacted so CmuxNextApp's source lines don't grow (size cap). The health read defaults to the named AcpmuxEnvironment.harnessHealth rather than an async closure literal (crash ratchet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Show agent update outcomes automatically Replays #21480's change onto current feat-cmux-next as one commit, on top of the rebuilt #21469. Settings, palette and recovered jobs follow their retained terminals. terminal-registry-changed events from the captured control connection only trigger a bounded read of the original receipt; they never dispatch work or imply completion. Completed batches stop observing. AgentCLIUpdateActions+Observation.swift lives in the CmuxNextAgentUpdates module beside the rest of the action, and the owner route captures the event stamp there. As in #21469, any exit, including one with an unknown outcome, settles its intent in the ledger. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Let every retained agent update end Review follow-ups on #21469, so a retained intent always has a way out. - A closed update terminal settles its job. terminal.wait_exit answers terminal.closed, then selector.not_found for the tombstoned terminal; both mean the process ended. A missing session or connection still keeps it. - Ledger reads apply the seven-day age bound, not only recording, which a blocked family never reaches. - A row from a session no route can reach (a reinstalled server) no longer blocks the current session's update; it waits out the age bound. Rows of the same session, or of a session recovery reattached, still block. - A failed record clears the batch's jobs, since nothing started. - A creation still pending ten minutes after a recovery first saw it may be dismissed. - agent_cli_job.busy no longer settles: the owner may be running that key. - A test covers adding recorded_at to a ledger created without it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 0aae74c - regenerated plans/cmux-next/action-surfaces.json - regenerated schemas/defaults/cmux-next.json - vp fmt webviews/src/agent-session/acpmux/replyHref.ts - vp fmt webviews/src/pages/markdown/links.ts - vp fmt webviews/test/markdown-cmux-app-links.test.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39208575467 * Keep other sessions' agent update rows blocking A row from a session recovery cannot reach blocks the CLI on that machine again. "No current route" cannot tell a dead session from a second live server on the same machine that may still be running the updater, so unblocking it could start two global installs at once. The ledger's seven-day read bound retires true orphans. Also notes at the closed-terminal settle that Rust answers terminal.closed before it queues the runtime signal, so the updater may still be dying in its close grace. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 953137a - vp fmt webviews/src/pages/markdown/README.md - cargo fmt in cmux-tui Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39217229319 * Bind the Settings update scope with withValueInTask TaskLocal.withValue frees its marker with the task allocator even when no task is current (cx-bpcj); the crash-class gate bans it. Use the shared withValueInTask helper, as ActionRunScope does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 45adaef - regenerated plans/cmux-next/action-surfaces.json - regenerated web/data/cmux-shortcuts.generated.json - regenerated cmux-tui/crates/cmux-app-host/generated/cmux-app.d.ts - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39219479749 * chore(next): PR autofix at a51fde3 - regenerated plans/cmux-next/action-surfaces.json - regenerated web/data/cmux-shortcuts.generated.json - regenerated cmux-tui/crates/cmux-app-host/generated/cmux-app.d.ts - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39219476854 * chore(next): PR autofix at 1ee01b5 - regenerated plans/cmux-next/action-surfaces.json - regenerated web/data/cmux-shortcuts.generated.json - regenerated cmux-tui/crates/cmux-app-host/generated/cmux-app.d.ts - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39219477932 * Translate the agent update intent argument into every locale check-l10n flagged argument.agent.harness.intent as missing 12 of the 21 locales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-tui: drop the std::sync qualification the base's clippy fix removed (unused_qualifications) * chore(next): PR autofix at 52aaa2c - Packages/macOS/CmuxNext/ci-target-graph.json - plans/cmux-next/action-surfaces.json - plans/cmux-next/actions.md Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39360940433 * chore(next): PR autofix at d86b10d - Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39366869850 * Leave ci-target-graph.json to the base autofix Only the base autofix writes it; generated drift refused the hand edit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bbf-a30f-7992-abba-3e3ca6f5adc1&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>
* acpmux: require version evidence for already-current updates Replays #21398's change onto current feat-cmux-next as one commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agents: refresh inventory versions without probing or updating Replays #21401's change onto current feat-cmux-next as one commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agents: connect explicit version refresh to the selected owner Replays #21402's change onto current feat-cmux-next as one commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add owner update terminal jobs with durable replay Replays #21414's change onto current feat-cmux-next as one commit. Keeps the crash ratchet flat: poisoned locks are recovered instead of unwrapped (only a held job lock is busy), and the job status parses its decimal strings with radix: 10. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Preserve app mutation keys through shared action runs Replays #21429's change onto current feat-cmux-next as one commit, written without growing CmuxNextApp's source lines (size cap). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Preserve app callers through the shared action registry Replays #21436's change onto current feat-cmux-next as one commit. ControlRequest takes callerApp in its init so CmuxNextApp's source lines don't grow (size cap). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Share agent CLI update UI across Settings and actions Replays #21447's change onto current feat-cmux-next as one commit, together with its follow-up fix. Settings and the palette run agent updates on their captured owner route instead of typing a health argv into a focused pane. The update owner prepares only npm and native installations, and refuses a family when any of its rows is another channel or already current, so both surfaces submit only those families and Settings shows Update only for rows whose channel the owner runs. A start the owner refuses (agent_cli_job.*, answered before it journals a creation) leaves no "unknown" job that blocks that CLI. One family's failure does not stop the rest of a batch; the batch reports every failure. A lost reply still keeps its unknown job, since the terminal may exist. Update, Update all and turning Update Automatically on still need a gesture or a confirmed user, as #21439 requires. The update action, its owner route and the health-row reader live in a new CmuxNextAgentUpdates module, leaving thin app glue in CmuxNextApp. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Recover agent updates across app restarts Replays #21469's change onto current feat-cmux-next as one commit, on top of the owner-channel fix, with these changes so a retained intent always has a way out: - A start the owner refuses before journaling (agent_cli_job.*) settles its intent, in a batch and on Resume, instead of leaving a Resume that is refused again. - Any terminal exit settles, including one whose outcome is unknown, and a failed creation state settles on recovery. - A person may dismiss an unstarted (not_found, prepared) or indeterminate intent from Settings; dismissing stops nothing. Pending stays fenced. Resume and Dismiss need a gesture, like Update. - The Rust ledger gains agent-cli-intents-settle, which deletes rows. Recording drops rows older than seven days and keeps the newest 256 per actor instead of failing at a cap. Recovery, the ledger port and the owner route live in the CmuxNextAgentUpdates module; the app keeps only its daemon glue. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Require trusted page context for Settings agent updates Replays #21439's change onto current feat-cmux-next as one commit. Turning Update Automatically on now needs a gesture or native confirmation too, since acpmux runs the update after its next check (review finding); SettingsHarnesses is compacted so CmuxNextApp's source lines don't grow (size cap). The health read defaults to the named AcpmuxEnvironment.harnessHealth rather than an async closure literal (crash ratchet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep Bun agent updates on the selected installation Replays #21568's change onto current feat-cmux-next as one commit, on top of the rebuilt #21469. acpmux binds a Bun installation's observed package and bin roots and its global config selection in fixed argv, so automatic, requested and owner-job updates all update the installation that was observed. Caller environment and argv stay refused. The private prepared argv bound becomes nine arguments; public inventory and RPC schemas are unchanged. Bun is now a channel the update owner runs, so Settings and the palette offer and submit Bun families as they do npm and native ones (ownerCanRun and the Settings ownerUpdate flag). Homebrew still needs explicit installation binding and stays unoffered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Let every retained agent update end Review follow-ups on #21469, so a retained intent always has a way out. - A closed update terminal settles its job. terminal.wait_exit answers terminal.closed, then selector.not_found for the tombstoned terminal; both mean the process ended. A missing session or connection still keeps it. - Ledger reads apply the seven-day age bound, not only recording, which a blocked family never reaches. - A row from a session no route can reach (a reinstalled server) no longer blocks the current session's update; it waits out the age bound. Rows of the same session, or of a session recovery reattached, still block. - A failed record clears the batch's jobs, since nothing started. - A creation still pending ten minutes after a recovery first saw it may be dismissed. - agent_cli_job.busy no longer settles: the owner may be running that key. - A test covers adding recorded_at to a ledger created without it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 0aae74c - regenerated plans/cmux-next/action-surfaces.json - regenerated schemas/defaults/cmux-next.json - vp fmt webviews/src/agent-session/acpmux/replyHref.ts - vp fmt webviews/src/pages/markdown/links.ts - vp fmt webviews/test/markdown-cmux-app-links.test.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39208575467 * Keep other sessions' agent update rows blocking A row from a session recovery cannot reach blocks the CLI on that machine again. "No current route" cannot tell a dead session from a second live server on the same machine that may still be running the updater, so unblocking it could start two global installs at once. The ledger's seven-day read bound retires true orphans. Also notes at the closed-terminal settle that Rust answers terminal.closed before it queues the runtime signal, so the updater may still be dying in its close grace. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 953137a - vp fmt webviews/src/pages/markdown/README.md - cargo fmt in cmux-tui Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39217229319 * Bind the Settings update scope with withValueInTask TaskLocal.withValue frees its marker with the task allocator even when no task is current (cx-bpcj); the crash-class gate bans it. Use the shared withValueInTask helper, as ActionRunScope does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 45adaef - regenerated plans/cmux-next/action-surfaces.json - regenerated web/data/cmux-shortcuts.generated.json - regenerated cmux-tui/crates/cmux-app-host/generated/cmux-app.d.ts - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39219479749 * chore(next): PR autofix at 85b92c6 - regenerated plans/cmux-next/action-surfaces.json - regenerated web/data/cmux-shortcuts.generated.json - regenerated cmux-tui/crates/cmux-app-host/generated/cmux-app.d.ts - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39219478271 * chore(next): PR autofix at 1ee01b5 - regenerated plans/cmux-next/action-surfaces.json - regenerated web/data/cmux-shortcuts.generated.json - regenerated cmux-tui/crates/cmux-app-host/generated/cmux-app.d.ts - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39219477932 * Translate the agent update intent argument into every locale check-l10n flagged argument.agent.harness.intent as missing 12 of the 21 locales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): agent CLI updates survive reconnects, unknown exits, owner mismatches and palette failures Regression tests for four defects in the shared agent CLI update action: a reconnect to the same owner hides and blocks the job, a new daemon session leaves a job that blocks the CLI forever, an exit with an unknown outcome blocks the CLI forever, a receipt for another owner leaves a blocking job, and a palette run's failure is never shown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): agent CLI update jobs follow their owner across reconnects and settle on any end - Showing, refreshing and blocking now share one owner rule: a job whose route went stale is re-captured when the app still routes the same machine and session (its receipts stay observable after a reconnect), and otherwise neither shows nor blocks the CLI. - An exit with an unknown process result stays state "exited" with outcome "unknown", so it settles instead of blocking forever. - A receipt for another owner drops its job before the error. - A palette run (no caller capturing work) shows its failure through the registry's refusal instead of dropping it. - Nothing-to-update and still-updating refusals get their own localized text instead of the generic add/remove agents message. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): agent update survives a reconnect mid-batch and a lost start reply A reconnect while one family's start waits must not start the next family on the re-captured owner and leave it with a job that blocks that CLI. A lost start reply must be replayed by its key on refresh, so the job gets its receipt or the owner's refusal clears it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): agent update start keeps its captured owner and replays lost replies start() re-read intent.route after every await, while showing or refreshing the jobs can swap it for a re-capture after a reconnect. A batch then went on to start the next family on the new connection, whose route had no open carrier, so that family kept an unknown job with no receipt that blocked its CLI for good. start() now captures the route once and stops when it retires, and a route opens its carrier on start as well as on inventory and observe. A lost start reply left an unknown job that blocked the CLI until restart. The intent keeps the child key, and refresh sends it again on the current owner: the owner replays the key, so the job gets its receipt, or its refusal clears the job. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): a busy answer to a replayed agent CLI update start keeps its job The owner answers agent_cli_job.busy before it looks the key up, so on a replay busy usually means the key's original request is still running. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): a busy answer to a replayed agent CLI update start keeps its job Review of #21447: refresh treated agent_cli_job.busy on a replay as the key creating nothing, dropping the job while its original request could still create the terminal, which then went untracked and unblocked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): retained agent update rows no owner can reach are shown and dismissable (red) Regressions for the #21469 review: - A ledger row whose machine/session no route can capture blocks that CLI on that machine for up to seven days, but state never shows it and dismiss cannot reach it. The tests expect it shown as state "unknown" with can_dismiss, a gesture-only dismiss that deletes the row, and the block lifting after it (recovery, actions and the AppServices journey). - A failed receipt attach after a successful start aborts the whole batch; the test expects it reported while the other families still start. - Rust: one ledger row that fails validation fails the whole list; the test expects the bad rows skipped and the good one returned. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): show and let a person dismiss agent update rows no owner can reach A retained ledger row whose machine/session no route captures blocked that CLI on that machine for up to seven days while state never showed it and dismiss could not reach it. - state now lists, after the jobs of reachable intents, every unsettled retained row none of them shows, as state "unknown" with can_dismiss. What start blocks on is therefore always shown. - dismiss(key) on such a row settles it locally (a ledger delete, plus any hidden in-memory job), still behind a gesture; it stops nothing. - An unreadable ledger clears the retained rows instead of showing stale ones. - A failed receipt attach after a successful start is reported with the batch's other failures instead of aborting the remaining families. - Rust: read_rows skips and logs a row that fails validation instead of failing the whole list. - rustfmt the merged agent_cli_job.rs lock line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): busy first starts, dismiss during a replay, and resolve-then-replay (red) Regressions for the #21469 review at 217ba19: - A first start the owner answers busy created nothing (busy comes before the key lookup), but its row was kept: it blocked a retry click and a later refresh started the update after the person saw a failure. - A dismiss while a replay is in flight deleted the row the replayed receipt then could not attach to, leaving an untracked updater. - A lost reply whose key the owner has nothing for (not_found, prepared) was replayed by refresh with no fresh gesture instead of offering Resume. - A batch that stops early should name the families it never sent. The replayed-row test now uses an owner that cannot resolve the key, where the replay is the only path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 99a29e6 - plans/cmux-next/action-surfaces.json - plans/cmux-next/actions.md Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39360359843 * chore(next): PR autofix at 8c68c4f - Packages/macOS/CmuxNext/ci-target-graph.json - plans/cmux-next/action-surfaces.json - plans/cmux-next/actions.md Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39360344030 * fix(next): settle busy first starts, hold replays against dismiss, and resume after not_found - A first start the owner answers busy settles. The owner answers busy from its inventory reservation or execution lock before it looks up the key, and send() only ever sends a fresh key, so nothing was created for it. Busy still keeps the row on a replay or resume, where the key may really be in flight. - A replay holds its intent busy, so a dismiss mid-replay is refused instead of deleting the row the replayed receipt attaches to. Settling a key with its intent also drops it from the unsent replays. - When resolve answers not_found or prepared for a lost reply, refresh no longer replays it: replaying is an execution request with no fresh gesture. The row offers Resume. Keys resolve cannot answer are still replayed. - A batch that stops early names every family it never sent in its failure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 19f6a8c - Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39366941274 * chore(next): PR autofix at 3b5a7ca - Packages/macOS/CmuxNext/ci-target-graph.json - schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39400996092 * test(next): settled owner failures and ended jobs never block agent CLI updates (red) Regressions for the #21447 review: - The owner journals a failed creation under its key and answers every replay with the same failure (operation.failed, mutation.indeterminate). Such an answer, on the first start or on a replay of a lost reply, must settle the job instead of leaving it unknown and blocking the CLI for the rest of the session. - A job that already ended must not block the next update of its CLI just because nothing refreshed Settings > Agents in between; a job that still runs keeps refusing it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): settle agent CLI updates the owner answered, and check ended jobs before refusing - Any coded owner answer to a start settles its key. agent_cli_job.* comes before a creation is journaled; any other code (a failed creation, operation.failed, or an interrupted one, mutation.indeterminate) is the key's journaled outcome and the owner answers every replay the same way. Such an answer, on the first start or on a replay, now drops the job instead of leaving it unknown and blocking the CLI for the rest of the session. Only a failure with no owner answer (a lost reply) is replayed; busy on a replay still keeps the job. - A start that finds an earlier job of the same CLI still unended refreshes once before refusing, so a job that already exited no longer blocks the next palette or Settings update until someone opens Settings > Agents. A job that still runs keeps refusing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): a coded answer to a replayed agent CLI update start keeps the lost job (red) Regression for the delta review of 0ad8305: a replay can get a coded answer the owner sends before it reads its journal (daemon_shutdown_pending during a handoff, operation.failed from a registry error), while the lost first start may have created the update terminal. Dropping the job then hides a running update and unblocks a second one of the same CLI. A first start's fresh key has nothing running, so any coded answer there still settles; its cases are now operation.failed and daemon_shutdown_pending, with a fixture message that matches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): only a refusal settles a replayed agent CLI update start A replay of a lost start now drops its job only on an agent_cli_job.* refusal, as before 0ad8305. Another coded answer can come before the owner reads its journal (daemon_shutdown_pending during a handoff, or a registry error mapped to operation.failed) while the lost first start created the terminal, so the job keeps blocking its CLI and the next refresh replays the same key. A first start's fresh key still settles on any coded answer, which is what keeps a failed creation from blocking the CLI for the session. A lost reply whose creation then failed is still replayed until the owner can say so; #21469's journal resolve settles that case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): a first agent CLI update start settles on any owner answer From #21447's review: a first start that the owner answers with a coded failure other than an agent_cli_job.* refusal (operation.failed for a failed creation, daemon_shutdown_pending during a handoff) left its ledger row unknown and unsent. `send` now settles the intent on any coded answer, since a first start's fresh key cannot have anything running. Replays and resumes still settle only on a refusal: a code the owner sends before reading its journal proves nothing there, and recovery's resolve settles a journaled failure. Makes #21447's aFirstStartTheOwnerAnswersWithAFailureLeavesNoBlockingJob pass on this branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): an indeterminate first agent CLI update start keeps blocking its CLI (red) From the second delta review: the owner answers a fresh key with mutation.indeterminate when a failed creation left its reserved terminal possibly live (AmbiguousLive in creation_settlement.rs). Dropping that job would let a second update of the same CLI start beside it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): an indeterminate first agent CLI update start keeps its job `answered` no longer settles mutation.indeterminate: the owner says a failed creation's effect may have run, so the job stays unknown and keeps blocking its CLI, like a lost reply, until it is replayed or its owner session retires it. The comments no longer claim a coded first-start answer always means nothing runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): an indeterminate first agent CLI update start keeps its ledger row `answered` no longer settles mutation.indeterminate: the owner says a failed creation's effect may have run, so the row stays unknown and blocking, and recovery's resolve lets a person dismiss it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(next): a failed first agent CLI update start keeps its ledger row until the journal settles it (red) From #21469's review: a coded failure other than an agent_cli_job.* refusal can follow a durable creation (a launch activation failing after the commit maps to operation.failed), so deleting the ledger row on such an answer can hide a live update terminal and unblock a second one. The row must stay until recovery's resolve reads the owner's journal; a failed creation then settles. #21447's any-code variant of this test is replaced here; the fake owner can now answer resolve. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(next): a first agent CLI update start settles its ledger row only on a refusal Reverts this branch's `answered` rule from 1db61b6 and 2506184. A first start settles only on an agent_cli_job.* refusal or busy, as the branch had it; any other coded answer keeps the ledger row, because it can follow a durable creation. Recovery's resolve then reads the owner's journal: a failed creation settles, an indeterminate one becomes dismissable, and a created one reattaches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
Settings and palette agent updates now create terminal jobs on their captured machine and session through one shared handler. Rust validates the command; the handler preserves the intent key and refuses scripts and App callers.
Settings shows receipts and exit outcomes, and blocks another update while the outcome is unsettled. Intent retention is currently in memory.
Validation: three fleet suites, nine web tests and TypeScript checks pass; regressions fail before the fix. Status messages cover 21 locales. Existing localization and package-group checks still fail.
Stacked on #21439.
Changelog
Changed: Settings and palette agent updates run on the selected machine with visible outcomes.