Skip to content

fix: preserve Codex activity in Dock tabs - #11743

Closed
Nauxie wants to merge 151 commits into
manaflow-ai:issue-10217-codex-tab-spinnerfrom
Nauxie:abhinav/fix-codex-dock-tab-spinner
Closed

Nauxie wants to merge 151 commits into
manaflow-ai:issue-10217-codex-tab-spinnerfrom
Nauxie:abhinav/fix-codex-dock-tab-spinner

Conversation

@Nauxie

@Nauxie Nauxie commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Extend fix: show Codex lifecycle markers in tab titles #10450's lifecycle-driven Codex tab presentation to Dock-owned terminals.
  • Preserve running/idle markers across Workspace ↔ Dock moves and direct session restore, including ordinary remote terminals.
  • Keep transient markers out of snapshots/transfers while preserving .user, .auto, and .remote title ownership.
  • Preserve newer admitted PTY titles when a restored panel is detached again before its restore boundary is released.

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/xctest path.
  • check-pbxproj.sh, lint-pbxproj-test-wiring.sh, package grouping, package lockfile policy, Dock shortcut routing, stored DispatchWorkItem lint, Swift parse, and git diff --check passed.
  • Tagged Debug build passed with codex-dock-spinner-review.
  • The full unfiltered cmux-unit target is currently blocked on the latest fix: show Codex lifecycle markers in tab titles #10450 base by unrelated pre-existing test-target compile errors in SurfaceCatalogTests and CLILocalTmux*Tests; the focused suite passed after excluding only those files.

Demo Video

  • Not attached; the title-state transitions are covered by deterministic app-host tests and an isolated tagged app build.

Checklist

  • I tested the change locally
  • I added or updated tests for behavior changes
  • I updated docs/changelog if needed (not needed for this stacked Dock follow-up)
  • I followed the repository's red-test/green-fix commit policy
  • No new user-facing strings were introduced; localization catalogs do not change

Note

High Risk
Touches release signing/notarization gates, app-host test tolerance, and new privileged sudo plus 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 sudo via CmuxSudoBroker, cmux coderouter agent as an alias of vm agent, and --command on workspace/terminal creation. Agent restore now retries structured busy admission (v2Retryable on CLIError). Claude hooks track process generations and call agent.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.yml and 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, and classify-app-host-test-output.py instead of the old “0 unexpected” grep. OpenCodeHookRegressionTests joins 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/xml on R2 appcast uploads. Nightly adds build_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.md lists 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.

@vercel

vercel Bot commented Sep 3, 2026

Copy link
Copy Markdown

@Nauxie is attempting to deploy a commit to the Manaflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Sep 3, 2026 •

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

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

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

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: aec3e313-70c3-4083-94d3-2dd549c27360

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

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Team

Run ID: f35f1837-ff7e-4ff8-9a59-a504756f285d

📥 Commits

Reviewing files that changed from the base of the PR and between 6698b26 and f03460f.

📒 Files selected for processing (6)
  • Sources/DockSplitStore+RestoredAgentLifecycle.swift
  • Sources/DockSplitStore+SessionRestore.swift
  • Sources/DockSplitStore+ShortcutCommands.swift
  • Sources/DockSplitStore+SurfaceTransfer.swift
  • Sources/DockSplitStore.swift
  • cmuxTests/CodexTabTitlePresentationTests.swift

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

Changes

Dock Codex tab-title presentation

Layer / File(s) Summary
Stable title resolution and presentation
Sources/DockSplitStore.swift
Dock terminal tabs now resolve stable titles, preserve user-owned titles, map Codex lifecycle states, and reconcile title markers and loading state through one path.
Lifecycle, rename, transfer, and restore wiring
Sources/DockSplitStore+RestoredAgentLifecycle.swift, Sources/DockSplitStore+SessionRestore.swift, Sources/DockSplitStore+ShortcutCommands.swift, Sources/DockSplitStore+SurfaceTransfer.swift
Runtime updates, renames, detached-surface attachments, and session restoration now preserve custom-title provenance and trigger Codex presentation reconciliation.
Dock lifecycle and persistence regression coverage
cmuxTests/CodexTabTitlePresentationTests.swift
Tests cover lifecycle changes, Dock transfers, local and remote title persistence, user renames, and session restoration.

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
Loading

Suggested reviewers: austinywang, lawrencecchen

Merge Risk: 🔵 Low · up to f0346

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)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 22 functions across 6 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (14 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed PASS. The new production value types in CmuxTerminalCore are pure Sendable enum/struct declarations and have no implicit @MainActor isolation. CodexTabTitleComposer stores only immutable `Stri…
Cmux Swift Blocking Runtime ✅ Passed PASS: The committed diff from merge-base 9e2dd509 to f03460f1 adds title/lifecycle state handling and synchronous tab updates, but no blocking or timing primitive covered by the rule. Added produc…
Cmux Browser Automation Off-Main ✅ Passed PASS: The pull-request diff does not modify browser socket automation. The policy targets Sources/TerminalController.swift and `Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Wire/Contro…
Cmux Expensive Synchronous Load ✅ Passed PASS. The cumulative production diff adds no synchronous history loader, file read, JSON/JSONL parse, directory scan, or per-record syscall. The new Dock title and lifecycle paths read in-memory panel…
Cmux Cache Substitution Correctness ✅ Passed PASS: The snapshot and transfer path did not replace a fresh authoritative read. Before the change, resolvedDockTitleMetadata already read Bonsplit.Tab.title; the new stableDockTerminalTabTitle …
Cmux No Hacky Sleeps ✅ Passed PASS. The PR diff changes Swift source/tests, a README, and Xcode project wiring only. It introduces no TypeScript, JavaScript, shell, or build/runtime script changes. The added-line search found no c…
Cmux Algorithmic Complexity ✅ Passed The production diff adds no nested collection scans, per-target rescans, sorting, filtering, or in-memory joins. The new Dock title path uses UUID-keyed dictionaries and the existing panel-to-surface …
Cmux Swift Concurrency ✅ Passed PASS. The aggregate PR diff (HEAD~8..HEAD) adds no DispatchQueue, DispatchGroup, Task, Combine, publisher, sink, completion-handler, or callback concurrency patterns. The changed production code is sy…
Cmux Swift @Concurrent ✅ Passed PASS: The PR introduces no async function, nonisolated async function, or @concurrent annotation. The only new isolation-related declaration is synchronous `private nonisolated func codexTabTitleCompo…
Cmux Swift Package Boundaries ✅ Passed PASS. The PR diff against the stacked base changes only Dock integration files and app-host tests. The added stableDockTerminalTabTitle and reconcileCodexTabTitlePresentation methods depend on `Do…
Title check ✅ Passed The title clearly summarizes the primary change: preserving Codex activity presentation in Dock tabs.
Description check ✅ Passed The description covers the change, motivation, testing, known test limitation, demo status, and checklist. It is sufficiently complete despite not including a demo video or the full template review-tr…
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

❤️ Share

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

@Nauxie

Nauxie commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

I have read the CLA Document v2.2 and I hereby sign the CLA

@Nauxie

Nauxie commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

@codex review
@coderabbitai review
@cubic-dev-ai review

@Nauxie

Nauxie commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

@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.

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 3, 2026

Copy link
Copy Markdown

@codex review
@coderabbitai review
@cubic-dev-ai 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:

Learn more →

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@coderabbitai

coderabbitai Bot commented Sep 3, 2026 •

Copy link
Copy Markdown

@Nauxie I will review pull request #11743.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Nauxie
Nauxie force-pushed the abhinav/fix-codex-dock-tab-spinner branch from f03460f to b46b154 Compare September 7, 2026 19:18
@Nauxie

Nauxie commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

@austinywang @lawrencecchen @azooz2003-bit rebased onto the latest #10450 (76ceed62) and reran review/verification. The pass caught and fixed three rebase-edge cases: remote title provenance, admitted PTY title preservation, and lifecycle markers on ordinary remote terminals. 16 focused tests, 302 package tests, repo guards, and the tagged build pass. Ready for review when you have a chance.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ 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.

Comment thread Sources/DockSplitStore.swift
@Nauxie
Nauxie force-pushed the abhinav/fix-codex-dock-tab-spinner branch 2 times, most recently from a2b4385 to ae1bbbc Compare September 8, 2026 08:23
@Nauxie

Nauxie commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

@austinywang @lawrencecchen @azooz2003-bit rebased again onto current #10450 (1377e1004d) and addressed the Cursor finding. The regression test now covers an auto title surviving an active restore boundary; the fix also keeps Dock remote-title protection aligned with the base branch. 19/19 focused tests, 302/302 CmuxTerminalCore tests, repo guards, and the tagged Debug build pass. Ready for another look.

austinywang and others added 9 commits September 8, 2026 11:26
…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>
@Nauxie
Nauxie force-pushed the abhinav/fix-codex-dock-tab-spinner branch from ae1bbbc to 37deb7f Compare September 9, 2026 00:33
@Nauxie

Nauxie commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current #10450 (bba0c0a6). The only semantic conflict was a newly-added transfer test asserting the pre-fix Dock presentation; it now expects the transient Codex marker/loading state in Dock while continuing to verify that detached/returned metadata stays stable and undecorated. 21/21 focused tests, project guards, and the tagged Debug build pass. No maintainer re-ping here—this was a straightforward stacked-branch reconciliation.

lawrencecchen and others added 5 commits September 8, 2026 17:48
…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
@Nauxie
Nauxie force-pushed the abhinav/fix-codex-dock-tab-spinner branch from 37deb7f to 3e019d8 Compare September 11, 2026 18:47
@greptile-apps

greptile-apps Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Too many files changed for review (1075 files, 100 file limit).

@azooz2003-bit azooz2003-bit left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the Dock Codex lifecycle, title ownership, transfer, and restore changes. The behavior is ready from my side.

@azooz2003-bit

Copy link
Copy Markdown
Collaborator

Superseded by the conflict-resolved in-org replacement, which has merged: #12333

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants