fix(app): a tab moved to a new pane leaves its old pane's strip in the same turn, so focus follows it at once

Merged
LA
5d ago
+170-3

Summary

Ports b76052722530 from cp/dogfood-v2-2-12-opt (dogfood-only fixes audit, row 10).

When a tab moved to a new pane, the workspace's layout apply showed it in its new pane and sent the focus topology while the old pane's strip still listed it. That strip changed only when the old pane's own strip observation ran, a main-actor job later. The first topology had the tab in both panes, so focus did not follow it in that turn; keys typed in that gap reached the window instead of the moved terminal.

On feat, Home's dock no longer shows this: feat's OutboundTab (#28127) keeps a tab that the dock makes out of the dock's strip until it has moved, and the dogfood regression test HomeDockNewTerminalFocusTests passes on feat 3 of 3 (fleet step fd5c7591a5259c2e6904a199). Move Tab to New Split (and other moves of a tab the pane already shows) still has the gap: the new MoveTabToNewSplitFocusTests is red on feat.

Now the model changes in one turn, under one topology (Chrome's rule: input sees the model as of its turn):

  • The workspace's apply brings every pane strip whose record's tabs changed since its last apply up to the store before it sends its one topology (syncStaleStrips). O(tabs of the workspace's panes), only on a layout change.
  • A pane whose own strip observation runs first applies the workspace's layout first (applyLayoutIfChanged, a no-op when the layout did not change), so the other order also moves the tab in one turn.

No key hold and no wait.

App lines: StaticURL (a Foundation-only enum of fixed URLs) moves from CmuxNextApp to CmuxNextAppCore with no change in behavior, so CmuxNextApp does not grow (app_lines ratchet).

Testing

  • MoveTabToNewSplitFocusTests (new): one workspace, two terminal tabs in one pane, tab.moveToNewSplit of the second (origin user). The first topology that lists the moved tab in a new pane must no longer list it in the old pane; focus must follow it in that reduction; focus never resolves to an empty pane.
    • Red on feat: test-only commit 4d5a30dfa80, fleet step 24f91a9c4ae78fd84ea0fb0d (cmux9s-Mac-mini): 5 of 5 runs fail at the same two expectations (the old pane still lists the moved tab; focus stays on the old pane). One of the 5 runs also hit the suite's 3-minute limit after the expectations.
    • Green at e47f4f3ab16, fleet step ff1ad1628fc1be4064596120 (aws-m4pro-1): 5 of 5 pass, and HomeDockNewTerminalFocusTests, HomeDockOpensTests, PaidPlanUpgradeTests, CloudCreateWithoutAskingTests (the StaticURL users) pass.
  • HomeDockNewTerminalFocusTests (the dogfood test, ported as is) passes on feat before the fix (step fd5c7591a5259c2e6904a199, 3 of 3) and with it.
  • Compile checks (CmuxNext, app, cmux-tui): 97b2f3597e57fb786fd0972f passed at the head.

Observed / Not run

Observed: the red and green fleet steps above; the pre-push gate (no new violations, no arch-ratchet rise).
Not run: a live tagged build with typed keys right after Move Tab to New Split; a drag of a tab to a pane edge (it goes through the same layout and strip observation paths, read only).

Changelog

Fixed: After Move Tab to New Split, keys typed at once go to the moved terminal.

Proof

No capture: the change is in the same-turn focus order, not in what a frame shows.


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


Summary by cubic

Fixes the gap where a tab moved to a new pane stayed listed in its old pane's strip until that pane's strip observation ran later, so the focus topology had the tab in both panes and keys typed in that turn reached the window instead of the moved terminal.

Now the model changes in one turn, under one topology: the workspace's layout apply syncs every stale pane strip before sending its one topology (syncStaleStrips), and a pane whose own strip observation runs first applies the workspace's layout first (applyLayoutIfChanged, a no-op when unchanged).

  • StaticURL moves from CmuxNextApp to CmuxNextAppCore, so CmuxNextApp does not grow (app_lines ratchet).
  • Adds MoveTabToNewSplitFocusTests (red on the base before the fix) and ports the dogfood regression test HomeDockNewTerminalFocusTests.