fix(focus): after a relaunch the saved pane of the shown workspace gets the keyboard, and Home does not take it (QA v2.2.2 bug 1)
Summary
After Cmd-Q with a browser tab focused in the pane beside Home and a relaunch, the keys a person types go into Home's Chief message box (text for an agent), not to the browser page that had the keyboard (QA v2.2.2 bug 1).
Ports 77c95946de09 and 47d6a3f31d97 from cp/dogfood-v2-2-12-opt (with their companions 92f8d64d0be, bde3363a962e, 4033999f63b4 and c93f03a00149). On the dogfood line the restore rule ended in the app's FocusCoordinator only because that build could not take a reducer change; on feat the rule belongs to cmux-focus-core (plans/cmux-next/layer-ownership.md), so this PR puts it there.
Two causes, two fixes:
- cmux-focus-core (
reducer.rs,RestoredPane). The launch window shows the home workspace before it adopts the saved window record.WindowState.adoptthen sends the record's focused pane asrestoredPanefor the workspace the window already shows, and the rule dropped it (it only remembered a restore for a workspace the window does not show). Now, for the shown workspace, the saved pane becomes the focus (target content, the responder re-applied) while no user intent happened in the window (generation 0). A view that took the keyboard by itself (a programmatic report: Home's box, AppKit's own pick) does not keep it; a person's click or key does. The recorded Swift focus corpus has 4 restores for the shown workspace, all after an intent (generation 4 or 9), so no recorded step changes. - Home (Swift). Home's unasked keyboard grab (
TopHomePageView.showof a conversation nobody opened, and the message box's later install inMessagesLabHomeView.finishOpenKeyboard) read "the window itself is first responder" as "nothing has the keyboard". A Chromium page that has the keys leaves the cmux window itself first responder. Now the unasked path also asks the focus model: for a view in a pane the model lists, the model must keep the keyboard in that pane's content (FocusState.keepsKeyboard(inContentOf:),allowsUnaskedKeyboard(inContentOf:)). A conversation the person opens (a click, Return, a switch key, an action) takes the keyboard as before.
To keep CmuxNextApp from growing (arch ratchet app_lines), KeyRouter.mayHavePrimaryInput(_:) moves to FocusState.Resolved.mayHavePrimaryInput in CmuxNextFocus (same rule, two callers).
Repin: BLOCKED (do not land yet)
The app links cmux-focus-core only through the prebuilt CCmuxAppFFI pin. The R3b rule says the repin goes in this same PR, but STATUS 5013 (rust-migration) found that a PR cannot repin itself today: app-ffi-release.yml publishes only from feat-cmux-next, and the ffi-release environment admits only that ref. Decision 5013 waits for cmux-ad. Until a pre-merge release exists, this PR has no ready-to-land. Check the app FFI pin is red on this PR because of that (the reducer changed with no repin). Two Swift tests stay red on this head until the repin: theSavedPaneOfTheShownWorkspaceGetsTheKeyboard and aViewThatTookTheKeyboardByItselfDoesNotKeepTheRestore. The repin PR #31684 (REOPEN-F4) also changes the pin line; this PR does not touch it.
Testing
Swift (RestoredFocusTests, through the real relaunch path WindowState.adopt; fleet Mac lane, compile-checks passed first: 1b269db218a7d8e055324007 at the red ref, 8defc5b7b0aeaf74e15df608 at 56aa6b27bb9):
- Red at 27b2a65194e (test only, base FFI), step ae90896f420025a69f24e491 on aws-m4pro-1: 2 failed on outcome (
resolvedstayspage(home-pane), nomoveResponder):theSavedPaneOfTheShownWorkspaceGetsTheKeyboard,aViewThatTookTheKeyboardByItselfDoesNotKeepTheRestore. 2 guard tests passed (thePersonsChoiceBeforeTheRecordStays,aRecordBeforeTheWorkspaceShowsStillLandsOnItsPane), as they do on the base rule too. - Head 56aa6b27bb9, step 22aa69c245a7f9208f571c89 on aws-m4pro-7:
homeTakesTheKeyboardUnaskedOnlyWhileTheModelKeepsItInHomesPaneand the 2 guard tests pass; the same 2 restore tests fail the same way, as expected with the old pin.HomeTypeToFocusTests(9) andHomeOpenFocusesComposerTests(2) pass: the Home keyboard paths a person uses still work.
Rust (cmux-layout-reducer-ffi/tests/focus_rules.rs: a_record_for_the_shown_workspace_focuses_its_saved_pane, a_record_before_the_workspace_shows_still_lands_on_its_pane). The crate is its own Cargo workspace, so the fleet cmux-tui-rust-check.sh test steps (572f40404ba2, bbfa6a835f69) and the test (linux) dispatch jobs do not reach it; their green says nothing. The lint (linux) job runs it, but on every dispatch so far (runs 41394713812, 41394716337, 41397167791, 41397170316) a base clippy red stopped the job before that step (#31462's suspicious_open_options, fixed by #31697; then three clippy errors in cmux-tui-core on feat 947bb5e00d7). The red and green go through a Testbox run of cargo test --test focus_rules in that crate; results are added here.
Observed / Not run
- Observed: the Swift red and head results above; the CmuxNext compile at both refs.
- Read only: the focus corpus has 4
restoredPanesteps for the shown workspace, all at generation 4 or 9, so no recorded step changes (a script overtests/focus/corpus.json, repeated by the reviewer). - Not covered by a failing test: the Home gate itself (
TopHomePageView.focusPrimaryInput(unasked:),MessagesLabHomeViewopenKeyboardAllowed). The new test checks only the focus-model predicate it asks; a revert of the gate keeps every test green. A gate test needs a realWindowControllerwith a Home tab beside a browser page that has the keys; not written. - Known limit (same as the dogfood fix): a click into the pane that already has the focus, or keys typed before the record arrives, do not bump the generation, so a late record can still move focus once. The window is the short time before the first restore.
- Not run: the QA journey (Cmd-Q with a browser tab beside Home, relaunch, type) on a tagged build. It needs the repinned FFI.
Rust red/green (cmux-focus-core, fleet, Linux)
cmux-tui-rust-check.sh test restored_pane runs cmux-tui/crates/cmux-focus-core/tests/restored_pane.rs (3 of the 5 restored-pane cases of focus_rules.rs, in the cmux-tui workspace; commit 77e15d90ce5; the addressBar case stays only in focus_rules.rs, which the fleet step does not run):
- Red at 9c816b59b12 (feat tip + that test only, branch fix/dogfood-port-restored-pane-focuscore-red, never merge): step e3e9ae38fc72f7510047dc53 FAILED as expected:
restored_pane_for_the_shown_workspace_focuses_its_saved_panepanics at restored_pane.rs:41 (resolved stays Home's page); the guardrestored_pane_never_overrides_the_persons_choicepasses. - Green at 77e15d90ce5: step f16e672983292f101e8385a0 PASSED, 2 of 2.
Changelog
Fixed: After Cmd-Q with a browser tab focused beside Home and a relaunch, typed keys go to the browser page again, not into Home's Chief message box.
Proof
No capture: a focus-routing fix with no visual change; the proof is the red/green tests above.