Repository navigation
Conversation
|
@Nauxie is attempting to deploy a commit to the Manaflow Team on Vercel. A member of the Team first needs to authorize it. |
|
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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Team Run ID: 📒 Files selected for processing (6)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthroughChangesDock Codex tab-title presentation
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant DockSplitStore
participant CodexTabTitleComposer
participant BonsplitTab
DockSplitStore->>DockSplitStore: Resolve stable terminal title and Codex lifecycle
DockSplitStore->>CodexTabTitleComposer: Compose title and loading presentation
CodexTabTitleComposer-->>DockSplitStore: Return tab presentation
DockSplitStore->>BonsplitTab: Update title and loading state
Suggested reviewers: Merge Risk: 🔵 Low · up to The PR preserves Codex activity markers and title ownership across Dock moves and restores. A bounded risk remains in direct restore, where the initial activity presentation may not be immediately correct until lifecycle reconciliation completes; the restore path should receive explicit owner verification. 🚥 Pre-merge checks | ✅ 14 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (14 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
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 |
|
I have read the CLA Document v2.2 and I hereby sign the CLA |
|
@codex review |
|
@lawrencecchen this is a focused stack on #10450 covering the Dock-owned terminal path that the main fix missed, including local/remote transfer, direct restore, title ownership, lifecycle transitions, and persistence. The focused app-host suite, package suite, repo guards, merge simulation, and tagged build are green. Ready for review. |
@Nauxie cubic can't start this review because your workspace has reached its free monthly review limit. cubic has reviewed 282,776 of the 280,000 allowed lines of code this month. Reviews resume on 1 October 2026 (in 28 days). Paid plans include much higher monthly review limits. Upgrade now to resume reviews. To help optimise your usage, you can tune cubic to get the most out of your usage limits:
|
|
To use Codex here, create a Codex account and connect to github. |
|
✅ Action performedReview finished.
|
f03460f to
b46b154
Compare
|
@austinywang @lawrencecchen @azooz2003-bit rebased onto the latest #10450 ( |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit b46b154. Configure here.
a2b4385 to
ae1bbbc
Compare
|
@austinywang @lawrencecchen @azooz2003-bit rebased again onto current #10450 ( |
…2161), over-budget warnings (manaflow-ai#12159), un-normalized pbxproj, stale hook-test expectations (manaflow-ai#12177) (manaflow-ai#12168) * ci: unbreak main's macOS lane (manaflow-ai#12161, manaflow-ai#12159) The CmuxTerminal test target no longer compiled after manaflow-ai#10564 added a `UUID` parameter to FakeTerminalEngine.swift, which imported only GhosttyKit; add `import Foundation`. The "Validate Swift warning budget" step also failed on five warnings that landed after the budget refresh: wrap the boundsDidChangeNotification observer body (queue: .main) in MainActor.assumeIsolated in SessionIndexTableController, drop the unreachable `default` from the exhaustive `switch resourceID.kind` in CmuxTuiSnapshotParser, stop binding an unused `rowID` in SurfaceCatalogModel, and make the never-mutated `payload` in TerminalController a `let` (identical to manaflow-ai#12153). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MPAVb9SSqAnuUnPEFQguE9 * ci: normalize project.pbxproj so the workflow guard passes main's pbxproj carries the StackAccountAvatarViewTests entries (manaflow-ai#12145) out of normalized order, so scripts/check-pbxproj.sh fails "Validate pbxproj objectVersion pin and normalization" in workflow-guard-tests on every full CI run. Output of scripts/normalize-pbxproj.py, no content change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MPAVb9SSqAnuUnPEFQguE9 * fix: parenthesize confusable trailing closures in the pane memory guardrail The full CI run on this branch had one bucket left over the Swift warning budget: two "trailing closure in this context is confusable with the body of the statement" warnings in postAggregateMemoryPressureWarning's guard condition. Pass the closures as parenthesized arguments, matching the existing call later in the file. No behavior change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MPAVb9SSqAnuUnPEFQguE9 * test: assert journal pane targeting instead of the removed prompt/pre-tool clear (manaflow-ai#12177) manaflow-ai#11976 routes attention through the journal reconciler and removed the explicit `clear_notifications --tab --panel` from the Claude prompt-submit and pre-tool-use hook paths; the app clears attention from the emitted agent_journal_append event instead. Two ClaudeHookLifecycleCleanupTests still asserted the old command and failed on every full CI run. Assert the new contract: the agent.turn.started / agent.state.changed event names the resolved (moved) pane, sibling and fallback panes are untouched, and no workspace-wide clear is sent. Verified by replaying both hooks against a post-manaflow-ai#11976 CLI with a port of the mock socket server. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MPAVb9SSqAnuUnPEFQguE9 --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
… and auto-resume (manaflow-ai#12151) * test: cover Antigravity hook session snapshot restore * test: make the Antigravity hook snapshot restore test compile and keep the permission flag The test called RestorableAgentSessionIndex.load(homeDirectory:fileManager:processArgumentsProvider:), which does not exist: the overload that accepts a process-arguments provider also requires the registry and detected-snapshots arguments, so the file failed to compile on the hosted unit-test lane. Load the built-in Vault registry so the registry-owned antigravity kind resolves, and expect the rendered resume command to keep --dangerously-skip-permissions: the sanitizer preserves unknown flags, and dropping the permission mode on resume is exactly what manaflow-ai#5473 asks cmux not to do. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VwJZhVvc3cQynztiSzVe3w * test: Antigravity hooks must dispatch to the terminal's own cmux first Installing Antigravity hooks from any cmux build pins every hook command to that build's CLI and socket, so a session started in a different build (stable, nightly, another tagged dev build) reports to the wrong app and its pane restores as an empty shell. agy preserves the launch environment, so the hook can and must use the launching terminal's CMUX_BUNDLED_CLI_PATH and CMUX_SOCKET_PATH before falling back to the pinned install. manaflow-ai#5473 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VwJZhVvc3cQynztiSzVe3w * Route Antigravity hooks through the launching terminal's cmux before the pinned build Antigravity hooks use pinned dispatch because agy may not preserve the launch environment. In practice agy does preserve it, and pinning alone meant that `cmux hooks setup` from any cmux build (nightly, a tagged dev build) redirected every Antigravity SessionStart/Stop to that build's socket. Sessions started in another build were never registered, so quit/relaunch brought their panes back as plain shells with no agent and no resume binding (manaflow-ai#5473). Each generated hook command now dispatches through the terminal's own CMUX_BUNDLED_CLI_PATH and CMUX_SOCKET_PATH when that environment is present and the socket is live, and keeps the pinned CLI/socket as the fallback for sanitized hook environments. Documented the Antigravity integration and the dispatch order in docs/agent-hooks.md. No user-facing strings changed. Fixes manaflow-ai#5473 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VwJZhVvc3cQynztiSzVe3w * Replay a restored agent's startup input when the login shell drops it Session restore types the ` cmux restore <kind> <id>` selector through Ghostty's initial input, which is written to the PTY as soon as the shell spawns. On a slow login shell (conda/oh-my-zsh style init taking a few seconds) that typeahead is discarded before the line editor is ready, so the pane comes back at an empty prompt with nothing typed and cmux retires the binding. Five quit/relaunch cycles on this machine lost the selector three times, which is the "empty shell instead of resume" experience in manaflow-ai#5473 even when the hook data is intact. The lifecycle coordinator now retains the startup input while the launch is `.awaitingAutoResumeCommand`. When shell integration reports an idle prompt in that state, the Workspace or Dock owner replays the input once after a short grace period, unless the command already started, the user or a socket client typed into the pane, or the pane no longer has a live runtime. A single prompt-then-command sequence therefore still runs the selector exactly once. Tests cover the coordinator's one-shot replay contract and the workspace shell-state wiring; the new test file is wired into the cmuxTests target. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VwJZhVvc3cQynztiSzVe3w * Drop retained restore input on cancelled launches and log restore admission decisions A deferred restore that is cancelled (live owner elsewhere, ambiguous ownership, changed binding) must never have its typed selector replayed by the idle-prompt safety net, so every cancel path now clears the retained input. Debug builds also log which line cancelled or admitted a deferred restore, the shell-state transitions with the lifecycle state, and each resend decision, so a lost or refused resume can be diagnosed from the tagged debug log. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VwJZhVvc3cQynztiSzVe3w * test: Antigravity hooks must fall back to the pinned build when ambient dispatch fails A socket node can outlive the cmux app that owned it. The generated hook must then continue to the pinned CLI and socket instead of dropping the event. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VwJZhVvc3cQynztiSzVe3w * Fall back to the pinned build when ambient hook dispatch fails; carry retained restore input across pane moves Review follow-ups on manaflow-ai#12151: - The ambient branch now has to succeed (`guard && invocation`) before the pinned chain is skipped, so a stale socket node left by an exited app no longer swallows the hook event (CodeRabbit). - A Workspace/Dock pane transfer carries the retained restore selector while the launch is still awaiting it, via DetachedSurfaceTransfer and seedTransferredState (Bugbot). - The workspace resend test polls the lifecycle predicate with a deadline instead of a fixed sleep (CodeRabbit). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VwJZhVvc3cQynztiSzVe3w * Resolve deferred agent restores against the last completed index instead of cancelling them At relaunch the live-agent index is loaded off-main, so every agent restore is deferred until SharedLiveAgentIndex.indexRefreshingNow() settles. That refresh gives up after two passes whenever hook stores keep changing, which is the steady state on a Mac running several agents. The deferred path treated the resulting nil as "no index" and cancelled every restore: the pane came back as a plain shell, the lifecycle went manual, and the next quit saved wasAgentRunning=false, so the session never auto-resumed again. This is the "resumes once, then never" report in manaflow-ai#5473, and it affected every restorable agent kind, not just Antigravity. On this machine the instrumented build showed the cancel firing four seconds after restore, well inside the ten-second deadline. The deferred task now resolves against the most recent completed load when the settled refresh gives up (its process evidence is revalidated during resolution), and only when no index has ever loaded does it start plain shells, without retiring the bindings, so the next relaunch can try again. Tests: DeferredAgentResumeIndexFallbackTests covers the index fallback and the retire-vs-keep distinction; the file is wired into the cmuxTests target. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VwJZhVvc3cQynztiSzVe3w * test: an index that has not caught up with a fresh hook record must not mark its binding stale Red on purpose: a freshly registered Antigravity session has an agent-hook binding before the live-agent index has rescanned the hook store. The autosave reconciliation used to treat the missing index entry as an exited process and retire the binding, so the next relaunch restored a plain shell (manaflow-ai#5473). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VwJZhVvc3cQynztiSzVe3w * Keep agent-hook bindings until the live index has an entry for their session isStaleAgentHookBinding retired a binding whenever the live-agent index had no entry matching the session, which is exactly the window between a hook registering a session and the next index scan. On a busy Mac that window covered the autosave that persists the binding, so an Antigravity session was saved with autoResume=false and relaunch left an empty shell. A missing or non-matching entry is now unknown evidence; only an entry for the session with no live process marks it stale. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VwJZhVvc3cQynztiSzVe3w * test: a pane moved after its shell idled must still replay the selector, and every transfer path must carry it A Workspace/Dock move re-registers the retained restore selector, but the shell's idle-prompt report went to the previous owner and never repeats at the destination, so nothing arms the replay there. The Dock's own detach and the remote-cleanup transfer copy also drop the selector outright. Covers PR manaflow-ai#12151 review findings (CodeRabbit on withRemoteCleanupConfiguration, Cursor Bugbot on transfer re-arming and Dock detach) for manaflow-ai#5473. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VwJZhVvc3cQynztiSzVe3w * Re-arm the restore selector replay when a pane is adopted after its shell idled; carry the selector through Dock detach and transfer copies A launch still awaiting its typed restore selector only replays it when the new owner sees the shell's promptIdle transition, but that report went to the previous owner and same-state updates return early, so a pane moved after its shell settled stayed an empty prompt and never auto-resumed. Adoption now arms the grace-period replay itself when the transferred shell state is promptIdle, in both Workspace and Dock; the coordinator's one-shot contract still holds. The Dock's detachSurface never placed the retained selector on its transfer, and withRemoteCleanupConfiguration(_:) dropped it when re-stamping a transfer, so a Dock -> Workspace move (or a remote pane leaving a workspace) lost the replay outright. Both paths carry it now. Addresses the CodeRabbit and Cursor Bugbot findings on PR manaflow-ai#12151 for manaflow-ai#5473. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VwJZhVvc3cQynztiSzVe3w * test: a fresh Antigravity launch without --conversation must still validate as the owner of its hook-recorded session Antigravity's registration keys session identity on the --conversation argv option, which only appears on explicit resumes. A freshly started agy has none, so CachedAgentProcessIdentityValidator failed closed, the live-agent index marked the running process exited, and the next autosave retired the hook binding (autoResume=false). Relaunch then cancelled the deferred restore and the pane came back as an empty shell (manaflow-ai#5473). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VwJZhVvc3cQynztiSzVe3w * Accept a bare argv for argv-keyed agent registrations once pid identity, scope, and executable match A fresh agy launch carries no --conversation option (Antigravity mints the id in-process and reports it through its hooks), so the argv-keyed session check rejected the live process. The index then reported the session exited and the next autosave retired the agent-hook binding, which is why an Antigravity pane never auto-resumed after quit/relaunch even once its hooks reached the right cmux. The record behind the snapshot was written by the same process generation that already matched on pid start-time identity, cmux scope, and executable, so a missing option is treated as no evidence rather than a contradiction; an explicit --conversation or exported CMUX_AGENT_SESSION_ID still has to name the session. Fixes manaflow-ai#5473 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VwJZhVvc3cQynztiSzVe3w * Scope the bare-argv fallback to validations against the current hook record Review follow-up: only an index built from the current hook record can vouch for a process that cannot state its own session id. A cached snapshot may predate an in-process conversation switch, so it keeps failing closed for argv-keyed registrations, matching the Hermes rule. Production autosaves build the index from the hook stores, so fresh Antigravity sessions still stay live. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VwJZhVvc3cQynztiSzVe3w --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* test(ios): warn when directory version is missing * fix(ios): warn for Macs missing directory versions * fix(ios): warn for unusable directory versions
…ow-ai#12028) * fix(ios): restore notification search after workspace pop * fix(ios): restore notification search on navigation pop * test(ios): assert bottom search placement through workspace return * test(ios): verify native search controls through repeated navigation * fix(ios): keep the native search navigation host stable
* test: cover Codex native title tab sync * fix: sync Codex native titles to terminal tabs * fix: harden Codex native title sync * fix: expose Codex title handler to async bridge * fix: capture title sync bridge explicitly * fix: retry Codex title reads during SQLite contention * test: cover Codex visible tab header reconciliation * fix: reconcile stale Codex tab headers * fix: use SQLite busy timeout for native title lookup * fix: compile async title socket test * fix: refresh window title for native Codex sync * fix: reject stale Codex native title hooks * fix: gate Codex title sync by surface owner * test: keep CLI ledger coverage out of app target
* iOS: avoid full refresh on mobile terminal input * iOS: rebase render grid after replay decoration * iOS: preserve decorated render-grid emission baseline * iOS: keep primary terminal deltas on baseline grid * iOS: send render-grid typing over independent lane * Fix input lane configuration initialization * iOS: coalesce initial terminal viewport replay * iOS: cover terminal lane replay backpressure * iOS: stop replay churn on hybrid terminal input * test: stop repeated terminal artifact count scans * iOS: coalesce unchanged artifact count scans * test: cover terminal lane repair cases * fix: preserve fast terminal lane availability * test: cover artifact scan retry after failure * fix: retry failed artifact count scans * test: cover viewport preparation ownership * fix: scope deferred viewport replays by Mac instance * test: cover input-only runtime and replay theme reset * fix: keep input-only lanes available with replay resets * test: cover failed artifact scan round trips * fix: preserve lane provider roles across retries * test: keep output lanes separate from input lanes * fix: reopen lanes only for authoritative render grid * test: cover artifact count refresh lifetime * fix: bound advisory frame and artifact scan dedupe * test: complete count scan before dedupe refresh * test: keep queued artifact scans current * fix: refresh queued artifact scan generation * test: cover deferred cold replay acknowledgement * fix: fulfill deferred cold replay after viewport ack * fix: scope deferred replay to viewport generation * fix: preserve queued artifact scan generation * fix: order terminal theme test arguments * fix: carry deferred replay across viewport supersession * fix: align artifact scan dedupe with promoted requests * fix: clear deferred replay on viewport detach * ci: avoid pipefail false negative in suite validation
… a machine exists; Cloud Machines is beta-toggle only (manaflow-ai#12160) * Cloud Machines: the Beta Features toggle is the only gate; retire the PostHog flag `CloudMachinesFeature.isEnabled` was `remote PostHog flag OR local Beta Features toggle`, with the flag defaulting to on in DEBUG. Cloud Machines is a beta feature that people turn on in Settings › Beta Features, so the toggle (`cloud.beta.machines.enabled`, default off on every build, never forced on by a managed `DisableCloud` profile) is now the single way in. The `cloud-vm-ui-enabled-release` flag is removed from the registry and retired so its key can never be reused; the two Settings tests that reached the Simulator flag by array index now name it, since the index shifted. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012grxVW9c5MtyGHndH1gX1a * Cloud tunnel: gate every start on a local activation policy; build the NetworkExtension controller only when admitted Closes manaflow-ai#12143. Every entitled launch built `NetworkExtensionTunnelController`, whose init reads `NETunnelProviderManager` preferences, and the cmux-tui registry polled `GET /api/vm` for every signed-in user, regardless of whether the user ever opted into Cloud. `CloudActivationPolicy` is now the one decision at the composition root, made from local state only (the Beta Features toggle plus the managed policy, a cached "account has a machine" marker written by every machine list and create, this Mac's tunnel enrollment files, and the browser-role VPN config on disk): - The coordinator asks `refuseStart` on every start path (browser use, `cmux vpn up`, `vm.tunnel_config`, `vm.tunnel_up`) and refuses with `cloud-machines-off` or `no-cloud-machine` before enrollment or any NetworkExtension call. - The NetworkExtension controller sits behind `CloudTunnelDeferredController` and is built on the first admitted `install`; only a Mac whose browser-role config already exists gets the eager controller, so an inherited tunnel is still adopted or stopped at launch. - Turning Cloud Machines off at runtime brings the tunnel down; turning it on lets the next use start it without a relaunch. The registry's periodic fleet read follows the same policy. - `down`, `revoke`, sign-out, and quit stay available for cleanup; sign-out also clears the machine marker. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012grxVW9c5MtyGHndH1gX1a * Cloud tunnel: resolve an unknown machine count on demand; re-sync fleet polling on sign-out Two findings from review on manaflow-ai#12160: - Sign-out clears the machine marker and enrollment files, but the registry only re-synced its fleet poll on the Beta Features notification, so with Cloud Machines off the 45 s `GET /api/vm` loop kept running and would have listed the next signed-in account, re-marking a user who never opted in. `accessDidEnd` now re-syncs the poll. - Right after sign-in (or a fresh opt-in, or a machine created on the web) the marker cannot confirm a machine, and the tunnel refused with "create a machine first" until the next poll. The marker is now tri-state; launch-time decisions still treat anything but a known machine as "no", while an explicit start (`cmux vpn up`, a Cloud browser open) settles an unknown or zero count against the control plane once through `CloudTunnelAdmission`, which also refills the marker. Status reporting stays local and read-only, and a policy refusal inside a scheduled start ends `.off` without failure backoff. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012grxVW9c5MtyGHndH1gX1a * Cloud tunnel: report an in-start policy refusal to waiting callers When a scheduled start is refused by the policy after the outer check admitted it (the toggle flipped while a stop drained, or a fleet resolve answered after an earlier unanswered one), `requirePrivateNetworkUse` and `requestUp` wait in `ensureUp` on the state stream, where `.off` read as a generic cancellation. The coordinator now remembers that refusal until the next start is scheduled, so browser navigation and `cmux vpn up` surface the actionable Cloud Machines message instead. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012grxVW9c5MtyGHndH1gX1a * Cloud tunnel: keep "used Cloud" apart from "has a machine"; fail closed and reconcile on review findings Review findings on manaflow-ai#12160 (CodeRabbit, Bugbot): - The enrollment-file fallback made "has a machine" true forever, so a delete could not be noticed and the tunnel could start for an empty fleet. `CloudActivationPolicy` now keeps two facts apart: `hasUsedCloud` (marker or enrollment files) keeps an existing user's fleet polling alive; `hasCloudMachine` is only the cached marker, which `VMClient.destroy` resets to unknown so the next start asks the control plane. - `CloudTunnelActivationObserver` evaluates the policy before it starts listening, so a toggle change posted before its notification stream registers cannot leave a tunnel up. - `vm.tunnel_up` throws the refusal that ended a scheduled start instead of answering with a bare "off"; status falls back to that recorded refusal. `vm.tunnel_config` fails closed while the coordinator is not wired, like `vm.tunnel_up`. - `CloudTunnelDeferredController` drops its test-only observation fields; tests assert on the injected factory instead. - Suites: the registry polling tests are serialized (they post on the default notification center); the new suites carry no hard time limit. `RightSidebarCommandPaletteTests` now enables the Cloud Machines beta key it relied on the removed DEBUG flag for. - Docs state the prior-use exception for fleet polling. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012grxVW9c5MtyGHndH1gX1a * Cloud tunnel: read the recorded refusal for status without an actor call in an autoclosure The status payload's `??` fallback evaluated `recordedStartRefusal()` inside a synchronous autoclosure, which does not compile for an actor method. Await both answers explicitly. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012grxVW9c5MtyGHndH1gX1a * Cloud tunnel: status reports only the refusal local state knows now A refusal recorded by a scheduled start stayed in `vm.tunnel_status` after the user turned Cloud Machines back on or created a machine, so `cmux vpn status` said unavailable while the next start would be admitted. Status now reports only `knownStartRefusal()`; the recorded refusal is read only by `vm.tunnel_up` right after its own wait. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012grxVW9c5MtyGHndH1gX1a * Normalize project.pbxproj after merging main Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012grxVW9c5MtyGHndH1gX1a * Restore the vendor/bonsplit submodule pointer to main's The pbxproj normalization commit staged the whole tree and carried the pre-merge submodule checkout with it; the pointer is back at main's. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012grxVW9c5MtyGHndH1gX1a * Cloud tunnel: settle the fleet before every scheduled start; beginUp reports its refusal Review findings on manaflow-ai#12160 (CodeRabbit): - A marker that still said "has a machine" admitted a start after the last machine was deleted outside this app. An explicit start now settles the count against the control plane before it is scheduled, whatever the marker says; while the tunnel is up or a start is in flight, uses read local state only, so a burst of uses never lists the fleet once each. Offline, the local answer stands (a positive marker admits, a negative one refuses, an unknown one lets enrollment report the real cause). - `beginUp` returns the refusal it hits so `vm.tunnel_up` throws it instead of reading an off state as success. - The failure-backoff check runs before resolution, so a dial burst during backoff does not list the fleet per dial. - The right-sidebar palette default-state test clears the Cloud Machines key so it verifies the default-off contract on any host. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012grxVW9c5MtyGHndH1gX1a * Cloud tunnel: revalidate admission after enrollment and after install; discard a refused enrollment Review finding on manaflow-ai#12160 (CodeRabbit): the admission decision was taken before enrollment, a control-plane round trip, and never re-checked, so a toggle turned off meanwhile could still enroll the Mac and install the VPN configuration. The coordinator now re-reads local admission after `enroll()` (discarding what the enrollment wrote, so the next launch does not treat this Mac as configured) and again after `install()`, which can wait minutes for the user's extension approval. `vm.tunnel_config` does the same around its own enrollment. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012grxVW9c5MtyGHndH1gX1a * Cloud tunnel: an install-time refusal removes the saved configuration; guard the registry refresh after a delete Review findings on manaflow-ai#12160 (CodeRabbit): - A refusal that landed while the install waited for the user's approval threw after the VPN configuration was already saved, leaving a refused Mac with a configuration and an enrollment. The coordinator now removes the configuration and discards the enrollment before throwing. - In the cmux-tui registry's refresh, a machine deleted while `setPrivateAddress` was suspended bumped the generation but the loop still created a provider, which that delete's teardown could then close. The loop re-checks the generation after the await, and looks the teardown up by the id `machineWasDeleted` stored it under. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012grxVW9c5MtyGHndH1gX1a * Cloud tunnel: a cancelled start that saved its configuration after a refusal removes it too Review finding on manaflow-ai#12160 (Bugbot): the production opt-out path brings the tunnel down first (`CloudTunnelActivationObserver` -> `requestDown`), which cancels the start, so when a late `install` returned with the VPN configuration already saved, `checkCancellation()` threw before the refusal cleanup and the next launch treated the Mac as configured. The cancellation path now removes the configuration and discards the enrollment when the policy refuses; an explicit `cmux vpn down` with the opt-in still on keeps the configuration as before. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012grxVW9c5MtyGHndH1gX1a * Cloud tunnel: a superseded start's cleanup never races a newer start Review finding on manaflow-ai#12160 (Bugbot): the cancellation-path cleanup ran after `requestDown` had already handed the tunnel to whatever start came next, and `controller.remove()` suspends, so a newer admitted start could enroll and save a configuration that the late cleanup then deleted. The cleanup now steps aside when a newer start is already in flight (its install overwrites the configuration), and a newer start scheduled while a cleanup is still running waits for it before enrolling. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012grxVW9c5MtyGHndH1gX1a * Cloud tunnel: a refused configuration left behind by a superseded start is discharged by the start that inherits it Review finding on manaflow-ai#12160 (Bugbot): skipping the cancellation-path cleanup whenever a newer start was in flight assumed that start would install, but it can be cancelled or refused before it does, leaving the superseded start's configuration and enrollment on a refused Mac. The obligation is now explicit (`orphanedInstall`): a superseded start hands it to the newer start, whose own install overwrites the configuration, and any start that ends without installing while the policy refuses discards it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012grxVW9c5MtyGHndH1gX1a * Cloud tunnel: hand a superseded start's saved configuration over regardless of the policy at that moment Review finding on manaflow-ai#12160 (Bugbot): `settleRefusedInstall` only handed the obligation to a newer start when the policy already refused, so a superseded install that finished while Cloud Machines was back on left its configuration unowned; if the newer start was then refused before installing, the configuration stayed and the next launch treated the Mac as configured. The hand-off no longer depends on the policy; only the final decision (discard or keep) does. The existing regression test exercises exactly this interleaving. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012grxVW9c5MtyGHndH1gX1a * Cloud tunnel: settle a refused install once The post-install refusal path settled the saved configuration itself and then threw into the refusal handler, which settled it again: two `remove()` calls and two enrollment discards, caught by the launch-gate suite on the previous head. The refusal handler now owns that cleanup. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012grxVW9c5MtyGHndH1gX1a * Cloud tunnel: one settle step for everything a refused start wrote The post-enrollment refusal discarded its own enrollment directly and the refusal handler then settled again, so one interleaving of the hand-off test discarded twice. Every exit path now goes through one settle step that knows what the start wrote (an enrollment, a saved configuration) or inherited, hands both to a newer in-flight start, and on a refusal removes the configuration only when one was ever saved, so a Mac that never installed never calls NetworkExtension. The fake enroller counts only discards that removed something, as the real one does. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012grxVW9c5MtyGHndH1gX1a * Cloud tunnel: point the pending-discard doc at the current cleanup method Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012grxVW9c5MtyGHndH1gX1a --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…indow points in the right coordinate space (manaflow-ai#12152 follow-up) (manaflow-ai#12167) * debug: content-view traversal mode and geometry for the drag_hit_chain probe drag_hit_chain <x> <y> content reproduces the traversal Bonsplit's tab-drag veto and the sidebar-divider diagnostic use (contentView.hitTest with a point converted into the content view) and reports the content/theme geometry that decides whether it agrees with the theme-frame traversal. Pulls the Bonsplit veto hit-chain diagnostic (5e3f682). Refs manaflow-ai#12152 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DGgpc86Qe4AMMsUrtso2Tu * Bump bonsplit: let only a control the press is inside veto a tab drag Pulls manaflow-ai/bonsplit b550689. The dogfood build of manaflow-ai#12164 still could not drag a tab opened from the file explorer: the DEBUG diagnostic showed the press was vetoed by isNativeInteraction because the window's contentView.hitTest answered the focused file editor (an editable NSTextView below the strip) for a press on the tab strip. A control now vetoes only when the press is inside its own frame and that frame lies in the strip. Refs manaflow-ai#12152 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DGgpc86Qe4AMMsUrtso2Tu * test: window-point hit tests must see the view under the point in a flipped content view The main window's content view is a flipped SwiftUI host under an unflipped window frame. NSView.hitTest takes a point in the receiver's superview space, so a window point converted into the content view itself and handed to hitTest is mirrored vertically. Add NSView.cmuxHitTest(windowPoint:) and cover it, plus the folder-drag window-move suppression that still resolves its press through the mirrored traversal. Refs manaflow-ai#12152 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DGgpc86Qe4AMMsUrtso2Tu * fix: hit-test window points through the content view's superview space Every window-space hit-test in the app converted the point into the content view itself before calling hitTest, which mirrors the point vertically under the main window's flipped SwiftUI host: the sidebar workspace drag veto, the folder-drag window-move suppression, the file-drop overlay's forwarding target, the browser panel's pointer-blur target, the Canvas pointer-entry check, the first-responder guard fallback, and the sidebar-divider diagnostic all answered the view at the mirror image of the press. Route them through NSView.cmuxHitTest(windowPoint:). Pulls manaflow-ai/bonsplit d967a86, where the tab-drag veto makes the same correction and additionally requires the press to be inside the control it yields to. Refs manaflow-ai#12152 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DGgpc86Qe4AMMsUrtso2Tu * fix: hit-test the folder drag icon against its frame, not its bounds NSView.hitTest receives a point in the superview's coordinate space. DraggableFolderNSView compared it against its own bounds, so the accepted region sat at the superview's origin instead of at the icon whenever the icon's frame origin was nonzero. Same coordinate-space class as the tab strip veto in this branch. Refs manaflow-ai#12152 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DGgpc86Qe4AMMsUrtso2Tu * test: the explorer-opened tab arms inside a flipped host window with its editor focused Reproduces the report on issue 12152 at the cmux level: a flipped host content view, the just-opened file's editable text view focused below the strip, and a press on the new tab. The old Bonsplit veto hit-tested the mirrored point, answered the editor, and dropped the press; the strip must arm the drag. Refs manaflow-ai#12152 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DGgpc86Qe4AMMsUrtso2Tu * test: wait on layout predicates instead of fixed run-loop turns CodeRabbit on manaflow-ai#12167: a fixed number of run-loop passes can return before SwiftUI commits under a loaded host. Poll the state each lookup needs (the strip mounted, the tab's registered frame, the editor in the window and holding first responder) up to a deadline instead. Refs manaflow-ai#12152 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DGgpc86Qe4AMMsUrtso2Tu --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
ae1bbbc to
37deb7f
Compare
|
Rebased onto current #10450 ( |
… cache (manaflow-ai#12121)" (manaflow-ai#12189) This reverts commit 513a595.
…i#11512) * test: cover pane focus handoff from markdown find * fix: release markdown find focus when leaving pane * test: cover find dismissal without leaving Markdown pane * fix: keep document focus when dismissing markdown find * test: cover stale preview focus after find dismissal * fix: avoid stale preview focus after find dismissal * test: stabilize rejected preview focus regression * test: isolate preview focus rejection assertion * refactor: split markdown focus boundary * fix: quote markdown focus source path * fix: expose split markdown focus state * fix: remove redundant markdown focus access modifier * chore: normalize project file
…ookies (manaflow-ai#12191) * Reapply "web: make dashboard navigation instant with a private session cache (manaflow-ai#12121)" (manaflow-ai#12189) This reverts commit b9eb587. * web: recognize every Stack refresh cookie name in the dashboard session gate The edge gate and the private session key looked only for stack-refresh-<project>. Current Stack SDKs store the refresh token under hexclave-refresh-<project>--<suffix>, optionally with a __Host- prefix, and treat the old name as legacy, so signed-in users looped between sign-in and the dashboard. One shared resolver mirrors the SDK's cookie name patterns for both the middleware and the session key.
* test: cover OpenCode idle notification event shapes * fix: harden OpenCode notification event delivery * test: cover string OpenCode idle status * test: wait for OpenCode feed frames deterministically * ci: execute OpenCode notification regression
* test: isolate app rate limits from shared IPs * Fix cross-user Iroh rate-limit starvation * test: keep legacy relay refreshes off Stack * Keep legacy relay refreshes off Stack * test: honor relay credential retry deadlines * Honor server cooldown in IRX credential loop * Use internal retry protocol import * Test Retry-After ownership across active clients * Honor Retry-After across active clients * Cover retry cooldown isolation and admission regressions * Close retry isolation and overflow gaps before merge
37deb7f to
3e019d8
Compare
|
Too many files changed for review (1075 files, 100 file limit). |
azooz2003-bit
left a comment
There was a problem hiding this comment.
Reviewed the Dock Codex lifecycle, title ownership, transfer, and restore changes. The behavior is ready from my side.
|
Superseded by the conflict-resolved in-org replacement, which has merged: #12333 |

Summary
.user,.auto, and.remotetitle ownership.This is intentionally stacked on #10450 rather than duplicating its main-area fix. The original regression came from #9098 collapsing high-frequency spinner OSC titles at ingress; lifecycle state is the safe source for restoring activity UI.
Related to #10217.
Testing
CodexTabTitlePresentationTests: 16 tests passed, including local/remote transfer, restore, title provenance, running → idle → clear, and persistence coverage.CmuxTerminalCoreTests: 302 tests passed across 53 suites using the repository's Xcode/xctestpath.check-pbxproj.sh,lint-pbxproj-test-wiring.sh, package grouping, package lockfile policy, Dock shortcut routing, storedDispatchWorkItemlint, Swift parse, andgit diff --checkpassed.codex-dock-spinner-review.cmux-unittarget is currently blocked on the latest fix: show Codex lifecycle markers in tab titles #10450 base by unrelated pre-existing test-target compile errors inSurfaceCatalogTestsandCLILocalTmux*Tests; the focused suite passed after excluding only those files.Demo Video
Checklist
Note
High Risk
Touches release signing/notarization gates, app-host test tolerance, and new privileged
sudoplus agent restore admission paths—any regression affects shipping or session restore reliability.Overview
This PR is dominated by CI and release pipeline work, plus a large CLI surface expansion—not the Codex Dock tab app changes described in the PR metadata.
CLI and agent hooks: Adds
cmux vm dev(folder → sync → detect dev server → layout → open workspace),cmux sudoviaCmuxSudoBroker,cmux coderouter agentas an alias ofvm agent, and--commandon workspace/terminal creation. Agent restore now retries structuredbusyadmission (v2RetryableonCLIError). Claude hooks track process generations and callagent.hibernation.session_end; pinned hooks prefer the launching terminal’s bundled CLI/socket before the pinned install. Codex gets shell-safe hook script paths, detached native title sync, and related fire-and-forget timing tweaks; Pi’s extension drops inline resume-binding setup on session start.CI (
ci.ymland friends): Many new guard scripts (localization catalogs, cloud-vm skill coverage, app-host failure classification, compilation-cache pruning, reusable-workflow permissions, release/iOS screenshot decoupling, Sparkle/tunnel checks). App-host runs gain per-test timeouts, longer idle teardown budget, focused non-tolerant suites (agent restore/resume, global search shortcuts, Ghostty locale, CLI creation, etc.), one-shot rerun for flaky remote-tmux mirror crashes, andclassify-app-host-test-output.pyinstead of the old “0 unexpected” grep.OpenCodeHookRegressionTestsjoins agent-notification CI; node is ensured earlier for hook regressions.Release / nightly / cloud: Release no longer blocks on iOS screenshots (parallel job, least-privilege reusable workflow), bumps sign job timeout, fixes tunnel extension bundle id normalization, hardens Sparkle appcast presence/signature checks, and sets
application/xmlon R2 appcast uploads. Nightly addsbuild_only/cold_cache, splits compilation-cache restore/save with CAS pruning, and skips publish/sign paths for measurement runs. New repair-nightly-appcast-content-types workflow; cloud-vm migrate can optionally clean up Iroh challenges. Swift warning and test-determinism budgets are bumped for new/changed files.Docs:
CLAUDE.mdlists the full set of supported macOS app locales for localization audits.Reviewed by Cursor Bugbot for commit 3e019d8. Bugbot is set up for automated code reviews on this repo. Configure here.