Skip to content

fix(app): a workspace asked for while the app connects opens in the launch window - #30080

Merged
lawrencecchen merged 5 commits into
feat-cmux-nextfrom
perfx/newtab-at-launch
Nov 1, 2026
Merged

lawrencecchen merged 5 commits into
feat-cmux-nextfrom
perfx/newtab-at-launch

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

A workspace asked for while the app is still connecting to cmux-tui now opens in the launch window, and the keys typed next reach its terminal. Before, newTab sent at that point (the type journey, about 1 run in 8 on v2.2.2 and v2.2.3: 3/29 and 3/17) made the workspace in the daemon, but the window stayed on Home and Enter sent the typed command to the Chief composer.

Takes back #29514: it does not undo #29514. PreviousLaunch.swift, which #29514 kept in CmuxNextApp, moves to CmuxNextAppCore with the same logic (only public and @MainActor on plan are added). This continues #29514's own aim of moving pure types out of the App, and pays for this fix's App lines.

Summary

Until the restore registers it, the launch window is outside the window registry. So New Workspace's target (WindowManager.targetWindow(preferring:)) was a new window id: the workspace was claimed for a second window, and the restore's reconcile gave Home to the launch window, the one the person sees.

  • targetWindow(preferring:) treats the launch window as open until the restore applies the saved registry (awaitsRestore). A claim for it stays pending until the restore's own reconcile places and selects it there. A reconcile before the restore (FirstTabHold's release, the workspace's own delta) would place it in a registry the restore then replaces.
  • A create without focus at launch is a quiet claim in the launch window, as in any open window. It no longer sends the launch window behind.
  • adoptLaunchWindow: claims, sidebar rows, slots, a frame and a behind-open asked for under the launch id follow the saved window id it takes (launchWindowAdopted).
  • FirstWorkspaceLaunch.land: the landing on Home is decided before the restore places what was asked for during the launch. It no longer takes the window from a workspace of the person's own that came meanwhile.
  • New on feat: when the saved-window read fails for good, the restore stops before it applies a registry. restoreGaveUp then places the held claims at once, so nothing waits for a restore that never comes.
  • PreviousLaunch (a pure value type) moves from CmuxNextApp to CmuxNextAppCore. This pays for the fix's App lines: CmuxNextApp is 11 lines smaller after the PR.

Ported from cp/fix-newtab-at-launch (9691f36, b9c0250, and the feat-relevant half of 7cd6360), PERF EXTRACTION item 3. Not ported: activeWindowID and b7e2a9b. They exist for the perf branch's launch picture, which has no window controller; on feat LaunchSnapshotWindow always builds the controller. The perf branch's savedRegistryApplied is feat's restored, which feat's restore sets in the same turn as it applies the saved registry.

Testing

NewTabAtLaunchTests (CmuxNextAppTests), run on the fleet with CMUX_TEST_REQUIRE_GUI=1, so a skipped test fails the suite:

  • Red: step b8b9f78a7c1bd56c39f1e790 at 16a4ff7 (perfx/newtab-at-launch-red: this head with the fix commit f57d733 reverted, never merge), aws-m4pro-1: FAILED, 4 of 4 tests on their own checks, 10 issues: the target is a second window (:60); a reconcile before the restore placed and dropped the claim (:81, :83); a quiet create opens a second window and sends it behind (:98-:100); end to end, another window shows the new workspace and the launch window shows none (:148-:157).
  • Green: step c235ecb9c183cd7e6878784e at ff46167, aws-m4pro-3: PASSED, "Test run with 5 tests in 1 suite passed", no window leak, no FLAKY notice.
  • Compile (cmux-ci compile-check --app --package CmuxNext --recipe-version 2) at ff46167: job 9d3fe078593f2335f75ce459 passed (CmuxNext, app, cmux-tui).

The suite has five cases (the red ref has four: the adopted-id case comes with the fix): three with no daemon (New Workspace's target while connecting, a claim surviving the reconciles before the restore, a quiet create), one for the adopted id, and one end to end on the same-tree cmux-tui (newTab sent once the connection exists and before the restore: one window, the launch window, showing the new workspace; the Home landing does not take it back).

Observed / Not run

  • Observed: the compile job above, and the red and green steps above, read from their logs.
  • Not run: the type journey and a capture-lease run on a real key window. The perf branch's evidence for the same fix was new-tab-at-launch 21/21 PASS on fix builds 7809358c and c55aa3fc. That ran the perf code, not this port.

Changelog

Fixed: A new workspace asked for while cmux is still starting opens in the window you see, and what you type goes to its terminal, not to the Chief

Proof

No capture: the change is in where a workspace is placed at launch, with no new UI; the tests above cover it.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Fixes a launch-window bug where a new workspace requested while the app is still connecting to cmux-tui opened in a separate window, leaving the visible window on Home and sending typed keys to the Chief composer.

  • targetWindow(preferring:) now treats the launch window as open until the restore applies the saved registry, and claims for it stay pending until the restore's reconcile places them.
  • A quiet create without focus at launch files into the launch window as in any open window, without sending it behind.
  • When the launch window adopts its saved identity, pending claims, sidebar rows, frames, and behind-window entries follow the new id.
  • The first-launch Home landing no longer takes the window from a workspace requested during launch.
  • A restore whose saved-window read fails permanently places held claims immediately instead of waiting for a restore that never comes.
  • PreviousLaunch moves to CmuxNextAppCore; adds NewTabAtLaunchTests covering the target, reconcile survival, adopted identity, quiet create, and an end-to-end daemon case.

Written for commit 1fb24dd. Summary will update on new commits.

View guided diff Turn on auto-fix

lawrencecchen and others added 4 commits November 1, 2026 02:26
…launch window, and the first-launch Home landing leaves it shown (red)

Dogfood gate v2.2.3, journey type (fails about 1 run in 8 on v2.2.2 and v2.2.3):
newTab sent right after the app's socket answered made the workspace in the
daemon, but the window stayed on Home and the typed keys went to the Chief
composer.

Ported from cp/fix-newtab-at-launch (0980a25 and the review tests of
a7e5c3f), adapted to feat-cmux-next: feat's restore reads the saved
windows from a live daemon, so the end-to-end case runs on the same-tree
cmux-tui; the target, reconcile and quiet-create cases run with no daemon,
before the restore.
…aunch window, and the first-launch Home landing leaves it shown

Root cause (dogfood gate v2.2.3, journey type; also on v2.2.2, about 1 run
in 8): until restore registers it, the launch window is outside the window
registry, so New Workspace's target (targetWindow(preferring:)) was a new
window id. The workspace was claimed for a second window, and restore's
reconcile gave Home to the launch window and the new terminal to the other
window. The keys went to the key window, Home's, and Enter sent them to the
Chief.

- targetWindow(preferring:) treats the launch window as open until the
  restore applied the saved registry (awaitsRestore), and a claim for it
  stays pending until the restore's reconcile places and selects it there:
  a reconcile before the restore (FirstTabHold's release, or the workspace's
  delta) would place it in a registry the restore replaces.
- A create without focus at launch is a quiet claim in the launch window,
  as in any open window; it no longer sends the launch window behind.
- adoptLaunchWindow: claims, sidebar rows and slots, a frame and a behind
  open asked for under the launch id follow the saved identity it takes.
- FirstWorkspaceLaunch.land: its landing (Home) is decided before the
  restore places what was asked for during the launch; it no longer takes
  the window from a workspace of the person's own that came meanwhile.
- A restore whose saved-window read failed for good (restoreGaveUp) places
  the held claims at once, so nothing waits for a restore that never comes.

Ported from cp/fix-newtab-at-launch (9691f36, b9c0250 and the
feat-relevant half of 7cd6360). Not ported: activeWindowID and
b7e2a9b, which serve perf's launch picture without a controller; on
feat the launch picture always has its controller (LaunchSnapshotWindow).
feat's restore sets `restored` in the same turn as it applies the saved
registry, so it stands for perf's savedRegistryApplied.
The launch-window claim fix adds App lines; this pays for them with a move of
a pure value type out of CmuxNextApp (plans/cmux-next/code-quality.md SW14).
PreviousLaunch (Restore Previous App Launch's plan) reads only ClosedItem
(CmuxNextDaemon) and WindowRegistry (CmuxNextLayout); CmuxNextAppCore already
had CmuxNextLayout in its closure through CmuxNextBridge and now declares it.
`plan` is @mainactor because WindowRegistry's API is (CmuxNextLayout's default
isolation); every caller is on the main actor. No behavior change.
@lawrencecchen lawrencecchen added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Nov 1, 2026
@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review 1fb24dd: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr30080 -f ref=perfx/newtab-at-launch -f platform=macos -f backend_mode=local

Not verified: macOS jobs did not run: compile and app tests were skipped on 1fb24dd99e; ci-status is green without them.

CI passes on 1fb24dd99e (run 41271170643 attempt 1).
macOS jobs did not run: compile and app tests were skipped: Fast static checks skipped (the macos job needs both to succeed). Re-run CI once they pass, before merging.

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

red: cmux-next checks (god files, concurrency, crash safety, l10n); base red: the same failure on the base 67aa3f8; does not block

Shipped in:

@cubic-dev-ai cubic-dev-ai 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.

3 issues found across 10 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Windows/WindowManager.swift">

<violation number="1" location="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Windows/WindowManager.swift:101">
P3: This comment says no restore places the launch window's claims, which is the opposite of what the flag does. Say that the launch window's claims are no longer held for a restore.</violation>

<violation number="2" location="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Windows/WindowManager.swift:255">
P2: A permanent saved-window read failure reconciles claims but never completes the window-restore lifecycle. `restored` and `registry.isLaunching` remain in their startup values, so post-restore observers and startup-dependent actions never begin; run the common completion/setup path or explicitly transition the manager into a usable degraded state before returning.</violation>
</file>

<file name="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Windows/FirstWorkspace.swift">

<violation number="1" location="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Windows/FirstWorkspace.swift:32">
P2: This startup guard only considers local-daemon workspaces, so a workspace created on a remote or Cloud machine can be missed. Include all machine stores when deciding whether the first-workspace landing is still needed, otherwise the launch landing can override the user's remote workspace.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

guard let document = await readSavedWindows(), !Task.isCancelled, !isTerminating else { return }
let saved = await readSavedWindows()
guard !Task.isCancelled, !isTerminating else { return }
guard let document = saved else { restoreGaveUp = true; return reconcileMembership() }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: A permanent saved-window read failure reconciles claims but never completes the window-restore lifecycle. restored and registry.isLaunching remain in their startup values, so post-restore observers and startup-dependent actions never begin; run the common completion/setup path or explicitly transition the manager into a usable degraded state before returning.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextApp/Windows/WindowManager.swift, line 255:

<comment>A permanent saved-window read failure reconciles claims but never completes the window-restore lifecycle. `restored` and `registry.isLaunching` remain in their startup values, so post-restore observers and startup-dependent actions never begin; run the common completion/setup path or explicitly transition the manager into a usable degraded state before returning.</comment>

<file context>
@@ -247,7 +250,9 @@ final class WindowManager {
-        guard let document = await readSavedWindows(), !Task.isCancelled, !isTerminating else { return }
+        let saved = await readSavedWindows()
+        guard !Task.isCancelled, !isTerminating else { return }
+        guard let document = saved else { restoreGaveUp = true; return reconcileMembership() }
         // Incognito workspaces a crashed run left on a daemon without state
         // resources: the app's ledger owns them, so they close, never shown.
</file context>

func land(_ workspaceID: String?, leftover: [String] = []) {
let services = manager.services
guard let workspaceID, services.machines.workspace(id: workspaceID) != nil,
services.daemon.store.workspaces.needsFirstWorkspace(leftover: leftover + [workspaceID], home: services.home.homeWorkspaceID)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: This startup guard only considers local-daemon workspaces, so a workspace created on a remote or Cloud machine can be missed. Include all machine stores when deciding whether the first-workspace landing is still needed, otherwise the launch landing can override the user's remote workspace.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextApp/Windows/FirstWorkspace.swift, line 32:

<comment>This startup guard only considers local-daemon workspaces, so a workspace created on a remote or Cloud machine can be missed. Include all machine stores when deciding whether the first-workspace landing is still needed, otherwise the launch landing can override the user's remote workspace.</comment>

<file context>
@@ -24,9 +24,13 @@ struct FirstWorkspaceLaunch {
+    func land(_ workspaceID: String?, leftover: [String] = []) {
+        let services = manager.services
+        guard let workspaceID, services.machines.workspace(id: workspaceID) != nil,
+              services.daemon.store.workspaces.needsFirstWorkspace(leftover: leftover + [workspaceID], home: services.home.homeWorkspaceID)
+        else { return }
         _ = manager.reveal(workspaceID: workspaceID)
</file context>

/// Windows being closed by the registry (not by the user).
var programmaticCloses: Set<String> = []
private(set) var restored = false
/// The saved windows' read failed for good: no restore places the launch window's claims (``awaitsRestore(_:)``).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: This comment says no restore places the launch window's claims, which is the opposite of what the flag does. Say that the launch window's claims are no longer held for a restore.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextApp/Windows/WindowManager.swift, line 101:

<comment>This comment says no restore places the launch window's claims, which is the opposite of what the flag does. Say that the launch window's claims are no longer held for a restore.</comment>

<file context>
@@ -97,6 +98,8 @@ final class WindowManager {
     /// Windows being closed by the registry (not by the user).
     var programmaticCloses: Set<String> = []
     private(set) var restored = false
+    /// The saved windows' read failed for good: no restore places the launch window's claims (``awaitsRestore(_:)``).
+    private(set) var restoreGaveUp = false
     /// Windows the person closed before `restore` finished (cx-ghagp): the restore puts back the
</file context>
Suggested change
/// The saved windows' read failed for good: no restore places the launch window's claims (``awaitsRestore(_:)``).
/// The saved windows' read failed for good: the launch window's claims are no longer held for a restore (``awaitsRestore(_:)``).

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Review (claude-reviewer-30080): SHIP at ff46167
reviewer-session: aaded9d7-7424-47f7-b4c7-8ef2234f507f
author-session: a38e12b5d87d2869b
R9: pass | the change adds no cap, queue limit or deadline. restoreGaveUp is a one-way flag. It is not a timeout.
perf claim: none

Verdict

SHIP. I found no correctness defect. A claim that awaitsRestore holds always gets placed. Every exit path I followed places it, or the app is quitting. The notes below do not block the merge.

How I checked that a held claim always gets placed

  • Restore returns early because of Task.isCancelled or isTerminating. loadObservation is cancelled only in prepareForTermination, so the app is quitting. A held claim does not matter then.
  • readSavedWindows returns nil. It returns nil only for a permanent failure. A cancelled read hits the guard before it. On nil, the code sets restoreGaveUp = true and runs reconcileMembership(). awaitsRestore is now false, so the claim goes to the launch id through placementWindow, which registers that id.
  • The launch window is adopted (adoptLaunchWindow). restored = true is set in the same turn as the adoption, the registry swap and the reconcile, with no await between them. launchWindowAdopted moves these from the old id to the new id: pendingClaims, pendingPlacements, pendingFrames, behindWindows and the PendingWorkspaceRows rows. quietClaims and backgroundClaims are keyed by workspace, so they need no move. The reconcile then places the claim in the adopted window and selects it.
  • Nothing is adopted. The claim names the launch id. register adds it to the registry, and line 304 or line 507 turns the launch window into an ordinary window. Base closed the launch window here as "other windows took every workspace". Now it keeps the claimed workspace.
  • The person closes the launch window during the restore. windowWillClose sets launchWindowID = nil, so awaitsRestore turns false at once. The next reconcile, or the one in the restore, places the claim under the old id. keepClosedDuringRestore keeps that window closed, which matches the existing closedLaunchWindowID path. The workspace ends up in a closed window that the person can reopen, so it is not lost.
  • Quiet and background claims. With awaitsRestore, these become quiet claims in the launch window. None goes to behindWindows. A held Cloud workspace from a background create is still dropped by unopened in reconcile (Cloud: a quiet workspace.newOnMachine create marks an unopened Cloud machine as opened #28609 is unchanged). In held, a key appears only through placements, so a held claim is never lost before the restore's own reconcile.
  • Ephemeral and incognito. Incognito windows use new window ids, never the launch id, so this change does not reach them.
  • Other callers of targetWindow. I checked all 15 callers. Each sends the id to createWorkspace(into:) and then claimNew, or to NewWorkspacePlacements.expect and WorkspaceStructureHandlers.build, which also go through claimNew. None moves existing workspaces to the id. A side effect: an external open (ExternalOpenController) while the app connects now also goes into the launch window, not a second window. That is the intended behavior.
  • The new land() guard. create() already returns nil when a workspace of the person's own exists, so the guard only matters when such a workspace arrived between create and land. That is the bug. Leftover workspaces and workspaceID itself are excluded, so a fresh tree with only Home still lands. The guard reads only the local store, and Cloud workspaces do not count.
  • Main thread. All new code is @MainActor (WindowManager). It adds no blocking call and no await in a handler.
  • The PreviousLaunch move.
    • CmuxNextAppCore uses daemonSwiftSettings, which has no default MainActor. The struct is therefore nonisolated and Sendable, which follows REVIEW.md section 11.
    • plan is @MainActor because the WindowRegistry API is MainActor-isolated.
    • The public import of CmuxNextDaemon and CmuxNextLayout is needed, because ClosedItem and WindowRegistry appear in public signatures. The public inits are added.
    • The Package.swift dependency on CmuxNextLayout is declared. The CmuxNextAppTests target already declares CmuxNextAppCore.

What I ran (read-only)

  • prepush-gate.py --base cba94d55a6a --head HEAD: no new violations. arch-ratchet: app_lines -11.
  • check-package-dependency-lists.py: clean. check-layers.py: ok.
  • I read the fleet logs:
    • Red, b8b9f78a7c1bd56c39f1e790 at 16a4ff7: 4 tests failed on their assertions. In the e2e test, the launch window was replaced, and the workspace was owned by another window id.
    • Green, c235ecb9c183cd7e6878784e at the head: 5/5 passed, and the e2e test ran (it was not skipped).
  • I did not compile or test on this machine.

Notes that do not block

  1. The land() guard counts any non-home local workspace as "the person's own", including a quiet newTab from the CLI or from automation. With Debug "First launch opens Home" off, such a quiet create at launch now stops the New Tab landing from being revealed. The window then shows whatever selection the reconcile repair picks. This is an edge case on a debug setting.
  2. Cloud claims while the app connects. land() reads only the local store, so the Home landing can still take the window from a foreground Cloud workspace asked for at launch. Also, a Cloud claim on the launch window now waits for the local restore, where base opened a second window at once.
  3. Placements now run before the restore. pendingPlacements and onListed for a launch-window claim now run before the restore, against the launch window's sidebar, while the workspace is still held. On base they ran in the second window. I saw no harm from this.
  4. Coverage gaps.
    • No test covers restoreGaveUp placing a held claim.
    • No test covers a held claim when the launch window closes during the restore.
    • aClaimOnTheLaunchWindowFollowsItsAdoptedIdentity has no red run, because its API comes with the fix.

@lawrencecchen lawrencecchen added the ready-to-land Reviewed at this head; next-autoland merges it into feat-cmux-next when its only reds are base reds label Nov 1, 2026
@cmux-bot
cmux-bot Bot temporarily deployed to ci-routing-agent November 1, 2026 03:57 Inactive
@cmux-bot
cmux-bot Bot temporarily deployed to ci-routing-agent November 1, 2026 03:57 Inactive
@cmux-bot
cmux-bot Bot temporarily deployed to ci-routing-agent November 1, 2026 04:05 Inactive
@lawrencecchen lawrencecchen added ready-to-land Reviewed at this head; next-autoland merges it into feat-cmux-next when its only reds are base reds and removed ready-to-land Reviewed at this head; next-autoland merges it into feat-cmux-next when its only reds are base reds labels Nov 1, 2026
@blacksmith-sh

blacksmith-sh Bot commented Nov 1, 2026

Copy link
Copy Markdown

Found 2 test failures on Blacksmith runners:

Failures

Test View Logs
test_every_listed_hook_is_one_glaeda_released_with_the_check (main.SigningHooksInSt
epWithGlaedaTests.test_every_listed_hook_is_one_glaeda_released_with_the_check)/
test_every_listed_hook_is_one_glaeda_released_with_the_check (main.SigningHooksInSt
epWithGlaedaTests.test_every_listed_hook_is_one_glaeda_released_with_the_check)
View Logs
test_no_runner_picker_returns_a_hosted_mac (main.NoHostedMacInNightlies.test_no_run
ner_picker_returns_a_hosted_mac)/
test_no_runner_picker_returns_a_hosted_mac (main.NoHostedMacInNightlies.test_no_run
ner_picker_returns_a_hosted_mac)
View Logs

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Review (claude-reviewer-30080-merge): SHIP at 1fb24dd
reviewer-session: 3a401d86-1ad4-4717-8135-0432d64bd755
author-session: a38e12b5d87d2869b
R9: pass | the change adds no cap, queue limit or deadline. restoreGaveUp is a one-way flag. It is not a timeout. The new base adds none in this area.
perf claim: none

Verdict

SHIP. The fix is still correct on the new base (feat-cmux-next 67aa3f8). I found no feat change that places workspaces, claims a window, or reads launchWindowID / restored in a way that can strand, duplicate or misplace a claim that awaitsRestore holds.

What I checked

  • The PR diff did not change. git diff 67aa3f884a3 1fb24dd99e8 has the same 10 files and the same hunks as the reviewed diff at ff46167.
  • The core files did not change on feat between the old merge-base cba94d5 and 67aa3f8. These are WindowManager.swift, WindowManager+Creation.swift, FirstWorkspace.swift, PendingWorkspaceRows.swift, PreviousLaunch.swift, WindowRecordSaver.swift, CmuxNextLayout (the registry reconcile), CmuxNextHomeEngine/FirstWorkspace.swift (needsFirstWorkspace) and HomeService.homeWorkspaceID.
    • In the whole feat diff, no added or removed line uses launchWindowID, restored, claimNew, pendingClaims, quietClaims, backgroundClaims, behindWindows, pendingPlacements, reconcileMembership, adoptLaunchWindow, FirstWorkspaceLaunch or readSavedWindows.
  • The callers of targetWindow( are unchanged: 15 callers plus the definition, the same set as at ff46167. Only the line numbers in WorkspaceVerbHandlers.swift moved (115/142 to 128/155) because cmux NEXT: CLI verbs for every sidebar move (workspace move-above/move-below, pin-unpin --pinned) #30008 added the moveAbove/moveBelow binds above them. Those binds reorder an existing row in a sidebar. They do not create a workspace, claim a window or call targetWindow.

The feat commits I read

PreviousLaunch move

  • There is no new user of PreviousLaunch on feat. At 1fb24dd the only users are WindowManager.swift (the property and its construction at restore), the doc reference in PreviousLaunchRestorer.swift and RestorePreviousLaunchTests.swift. All 3 have import CmuxNextAppCore.
  • ClosedItem and WindowRegistry (the public API types) did not change on feat.
  • feat's Package.swift changes (Screenshots, ChromiumWarmup and CrashReporting dependencies) do not touch the CmuxNextAppCore target line. The PR's added CmuxNextLayout dependency merged cleanly.

Compile result I read

Fleet job fab5b25fc0380b7a8117cfd1 (cmux-next-swift-check v2, linux pool, ref 1fb24dd) is done. Its receipts show ok: true for all 3 packages:

  • CmuxNext: 243.7 s. The log shows that the CmuxNextAppTests sources compiled.
  • app: 49.2 s.
  • cmux-tui: 11.3 s.

I saw it pass. This is a compile check only. It ran no test.

Coverage gaps

  • No red or green test run exists at this head. The red run (b8b9f78a7c1bd56c39f1e790, fix reverted, 4/4 failed on their assertions) and the green run (c235ecb9c183cd7e6878784e, 5/5 with CMUX_TEST_REQUIRE_GUI=1) are at ff46167. I judge them still valid because the patch is the same and no file under the fix changed on feat. I did not re-run them.
  • These gaps from the earlier review are still open:
    • No test covers restoreGaveUp placing a held claim.
    • No test covers a held claim when the launch window closes during the restore.
    • aClaimOnTheLaunchWindowFollowsItsAdoptedIdentity has no red run.
  • The non-blocking notes from the earlier review still apply without change:
    • The land() guard counts a quiet CLI newTab.
    • A Cloud claim at launch waits for the local restore.
    • Placements now run before the restore.
  • I did not compile or test on this machine.

@lawrencecchen
lawrencecchen merged commit 3e3636e into feat-cmux-next Nov 1, 2026
216 of 231 checks passed
@lawrencecchen
lawrencecchen deleted the perfx/newtab-at-launch branch November 1, 2026 04:15

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 1fb24dd9 Deployed Nov 1, 2026 by cmux-bot[bot] via wait for same-key tree publisher #93538
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-land Reviewed at this head; next-autoland merges it into feat-cmux-next when its only reds are base reds risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant