fix(app): a script's new terminal never takes a typing person's window; keys for a person's new terminal over Home wait for it (dogfood port)
Summary
Ports 23248fdeba4a and f6646860fad3 from cp/dogfood-v2-2-12-opt (with their review fixes 2be9a0cc33a1, a4c01b0b4036 and 80723559628f), onto feat's own creation hold. Audit row 5 of handoff/dogfood-only-fixes.md.
Two faults on feat today:
- The person asks for a new workspace with a terminal (a terminal-first Cmd-N, the menu, the palette) while Home shows. Nothing holds the keys typed for that terminal, so they go into Home's Chief message box, and Return sends the shell command to the Chief as a chat message (dogfood v2.2.3, fleet job 2c9383257301002fab1539d7, P0 journey
type). Terminal input must never reach an agent. - A script's, the CLI's, MCP's or the socket's
newTabwithfocustakes the window from a person who types in a text field there. CLAUDE.md: input from the person always goes before automation.
After this PR:
- Only the person's own
newTab(the run's origin isuser, read from the run scope, so a script's nested run is not the person's) opens a key hold for its terminal (CreationInputCoordinator.beginWorkspace). Its keys wait until the window shows that workspace and its terminal has the keyboard. A failed create, or a workspace the window never shows, drops them (CreationInputBuffernew-workspace lanes,dropKeys(ofWorkspacesOtherThan:)). They never run into what has the focus. - The person's new terminal also claims the window's keyboard (
CreationKeyboardClaims, in CmuxNextFocusUI) until their next intent there: a click, Escape, a focus move of theirs (mouse, keyboard, palette:FocusEvent.isPersonsOwn, in CmuxNextFocus), another of their top-level actions; or until the window shows the workspace. Never by a timeout, never by automation. While it holds, no primary input takes the keyboard by itself (NSWindow.pendingCreationOwnsKeyboard, read byPrimaryInputTarget.landKeyboardandTopHomePageView.show). After the hold, every key that runs no binding is kept from the window. - A script's
newTabwithfocusis listed, not shown, while the person's keyboard is in a text input of the active key window (NSWindow.hasTextInputKeyboard). It still shows when no text input has the keyboard, or when the app is not active.
What feat already had: the creation hold for splits and new tabs in a pane (#28331, #29301, #29409, #30827, 89adc366610c) and the New Tab page hold (NewTabInputCoordinator, person's Cmd-N onto the New Tab page). Neither covers a new workspace whose first tab is a terminal, and neither checks who asked. The dogfood code is not cherry-picked: feat's buffer has per-terminal lanes (P0 1193), so the new-workspace keys use their own lane in that model.
Testing
Regression test: HomePendingTerminalFocusTests (ported from dogfood 4aefbf64ce1 and 228e8b006cc), command cmux-ci run --class light --script scripts/ci/package-test-lane.sh --arg=suite --arg=Packages/macOS/CmuxNext --arg=HomePendingTerminalFocusTests.
- Red on the test-only commit 9432f35b692 (feat base 9ce095b907c6), step 18182dca582318d3b44879f7 on cmux14:
keysTypedForAPersonsNewTerminalOverHomeNeverReachTheChiefMessageBoxfails 5 issues (no hold; the box takes the keyboard; "echo ok" passes to the window and lands in Home's message box);aScriptsNewTerminalNeverTakesTheKeysOfAPersonTypingInAFieldfails (the window shows the script's workspace). The two controls pass. - Red2 at 785959d9253 (feat + the head's tests, no fix; branch fix/dogfood-port-scripted-newtab-keys-head-tests-red2, never merge): step 21378ae66713bf96aa0d7968 failed as expected (no key hold opens, Home's box takes the keyboard,
echo okgoes into Home's box, the script's terminal takes the typing person's window). - Green 5x at head a499c9c191c: step b3d03866f5f4643f8dff680f (aws-m4pro-9) passed all 17 suites: HomePendingTerminalFocusTests 5 of 5, PersonTerminalKeyboardClaimTests 5 of 5, CreationInputHoldTests 5 of 5, CloseThenTypeKeysTests, SecondCreationKeysTests. Compile-check of the head: 2bf885432a573e880df2d4b5 passed.
- Earlier greens that failed and were repaired: be6344130d3 (fe6f824d, 0 of 5: the held keys had no terminal lane), 24be0e0aa03 (499f0eac, test leaked Home's popover), 96d3ef989bb (71f66fc3, 4 of 5: the flush ran before the terminal had the keyboard).
- After the merge of feat (plain merge b18c73fbb77; conflicts with #31707's surface hold in CreationInputCoordinator.capture and with an arch-ratchet exception, both sides kept): compile 528c977f825a1fa1e0649ce2 passed; green step 62db23b80bd7e9d29866bad2 (aws-m4pro-3) passed all 21 suites: HomePendingTerminalFocusTests, PersonTerminalKeyboardClaimTests and CreationInputHoldTests 5 of 5 each, plus CloseThenTypeKeysTests, SecondCreationKeysTests and the #31707 suites FocusedPaneShowsFirstTests, FocusedPanePresentedFirstTests, ColdFocusedTerminalTypeAheadTests, NoActivateKeyboardGuardTests. Merge review SHIP at b18c73fbb77 (two non-blocking follow-ups listed there).
- Feat moved twice more while the checks ran (PR DIRTY each time). Merge 2 = 093df561768 (conflict: DaemonStore+WorkspaceID.swift, both functions kept): compile passed, green 702592ee45f3815502bcd618 passed 18 suites, merge review SHIP. Merge 3 = d1791fd64db (head; conflict: arch-ratchet.json, both exceptions kept; the PR diff is identical against the new merge-base): compile passed, green 5b2d456d838a036df58d93d1 (aws-m4pro-3) passed all 18 suites (row 5 suites 5 of 5 each, CloseThenTypeKeysTests, SecondCreationKeysTests, ColdFocusedTerminalTypeAheadTests), merge review SHIP at d1791fd64db (comment 6573133091). CmuxNextApp is exactly +105 against the new merge-base.
- CmuxNextApp +105 lines under a reviewed, dated exception in plans/cmux-next/arch-ratchet.json (expiry 2026-11-16, owner dogfood-ports).
- Also ported:
PersonTerminalKeyboardClaimTests(the claim's ends: Escape, a person's focus move, a person's action; automation, a nested run and an earlier create's reply leave it alone) andCreationInputHoldTests.keysTypedForANewWorkspacesTerminalGoOnlyToThatWorkspace(the buffer rule). They use the new API, so they do not compile on the base.
Observed / Not run
- Observed: compile-check 3a28abf9843323849ba7aaa7 of the red commit passed; the red step above.
- Not run: a live journey (P0 journey
type) and a tagged build. Not tested by a failing test: dogfood 80723559628f items 2 and 3 (a window with no controller at the reply; --activate inside the show's scope), as on the dogfood line. Known differences (reviews): a script's new window with --focus and no --activate can open behind; AppKit-only shortcuts with no cmux binding (Cmd-H) are kept while a claim holds.
Changelog
Fixed: Keys typed right after you open a new terminal from Home go to that terminal, never into Home's message box, and a script or CLI opening a terminal no longer takes the window while you type in a text field.
Proof
No capture: keyboard routing only, no visible UI change.