fix(app): a workspace asked for while the app connects opens in the launch window
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.
restoreGaveUpthen 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 (9691f3665ed, b9c0250fb6a, and the feat-relevant half of 7cd6360c6cc), PERF EXTRACTION item 3. Not ported: activeWindowID and b7e2a9b4f5e. 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 16a4ff7b312 (
perfx/newtab-at-launch-red: this head with the fix commit f57d7336a37 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 ff461673280, 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 ff461673280: 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
typejourney 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](https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png)
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
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.
PreviousLaunchmoves toCmuxNextAppCore; addsNewTabAtLaunchTestscovering the target, reconcile survival, adopted identity, quiet create, and an end-to-end daemon case.