fix(cmux-tui): a terminal tab aimed at a pinned dock that takes no tabs opens in a new column right of it (FC-1 follow-up)
Summary
cmux-tui now opens a terminal tab aimed at a pinned dock that takes no tabs in a new column right of the dock. Before, it refused the tab with dock-column-pinned. The rule lives in one daemon commit point, so every terminal creator gets it: the app's own creators, the CLI, MCP and scripts.
FC-1 (fuzz catalog). Home's dock is pinned in its dock move, and it takes other tabs only after a later column.update {allow_tabs: true}. A person can also close it to tabs. While allow_tabs is off, cmux-tui refused every terminal tab in it. #30676 fixes this for TabLifecycle.newTerminal (New Terminal Tab from a script, the CLI or MCP). The independent review of #30676 found that many other creators still failed in the dock: config commands (ConfigCommandRunner), the account and provider login openers, Duplicate Tab (TabHandlers:80), the page openers, Tab Group's New Tab, agent handlers and Agent Teams (raw connection.newTab).
Fix (per cmux-shared-behavior: one shared point, not a guard per caller). Every terminal tab commits through Mux::commit_resource_topology_operation (cmux-tui/crates/cmux-tui-core/src/mux/resource_topology/topology_operation.rs): the JSON-lines new-tab, tab.create_terminal, and the frontend's creation. There, a tab.create_terminal whose pane is in a pinned column that takes no tabs becomes one pane.split to the right with a viewport width, which makes a new column beside the dock:
- Beside a lone dock, it uses
LONE_DOCK_NEIGHBOR_WIDTH, the same width as a link Home opens there (the daemon's browser rule for a lone dock). - Beside a dock with other columns, it uses
DEFAULT_VIEWPORT_PANE_WIDTH. - The split keeps the focus where it was unless the caller asked, as a tab made in another pane does. The tab's strip slot is dropped. Its caller-chosen terminal id, cwd, env and size go to the split, and a prelaunched host is adopted by its terminal id as before.
New helpers in pinned_columns.rs: pane_in_pinned_column_taking_no_tabs (now also used by ensure_pane_takes_tabs) and terminal_column_beside_closed_dock.
Behavior change: two wire tests asserted the old refusal for terminal tabs (pinned_column_takes_no_new_tab_until_allow_tabs_is_on, a_browser_tab_made_in_a_lone_pinned_dock_opens_in_a_new_column_beside_it). They now assert that no tab enters the dock. Browser and conversation tabs are unchanged: in a dock with a column beside it they are still refused, and from a lone dock the daemon already reroutes them.
Related: #30503 (landed), #30676, #30826.
Owner note: this touches the cmux-tui daemon area of PL-daemon (session cmux-b6; plans/cmux-next/OWNERS.md). Only the creation route changes; no request-speed path changes.
Changelog
- Fixed: A terminal opened in Home's dock by a config command, a login, Duplicate Tab, a tab group, an agent, the CLI or MCP before Home's dock takes tabs now opens beside it instead of failing.
Testing
Regression tests (red at 34883fd748b, tests only):
- Rust
server::pinned_columns_tests::a_terminal_tab_aimed_at_a_pinned_dock_that_takes_no_tabs_opens_in_a_new_column_right_of_it. It sendsnew-tabinto a lone dock (expects a new column right of the dock at the lone-dock width), thentab.create_terminalinto the dock with a column beside it (expects a third column, right of the dock). The dock keeps only its pinned tab. - App
HomeClosedDockTerminalCreatorsTests. It callsPaneController.newTerminalTab(the config-command and login-opener path) on Home's closed dock, and expects no failure and a real terminal in a column right of the dock.
Fleet:
- Rust red (Linux) a9ab569f3f9645ea63fc64ab at 34883fd748b: failed, 17 passed and 1 failed:
{"cmd":"new-tab","pane":3} failed: {"error":"dock-column-pinned: the pinned column takes no other tab (allow_tabs is off)" ...}. - Rust green (Linux) f392de60c38e74863e3bdb0b at dbf93b8e5f7: 18 of 18
pinned_columns_testspassed. - App display red f1b1e52d1caabe2a437aeaa7 and green 904e86672320b448bfc02432 (
HomeClosedDockTerminalCreatorsTests, plusHomeDockOpensTestsin the green): running when this was written; results are added here. - Compile: 39521237e012b6e7588bce20 passed at dbf93b8e5f7 (CmuxNext, app, cmux-tui). 5d2ecda26ebf80ae9bee68d4 passed at the head 4e49f3862f2 (CmuxNext, app, cmux-tui).
- Clippy (Linux):
- c67bcca8c54f843230214193 at dbf93b8e5f7 stopped at a base red.
- e8bbbe6c1b45590581ef66b2 ran on scratch ref cp-scratch/fc1-reroute-clippy-with-30791, which is this branch plus #30791 and never merges. It found one lint of this PR's own,
unused_qualificationsattopology_operation.rs:601, fixed at 4e49f3862f2. - 1a7101f2d30d705aa3e3d705 at 4239db61c67 (the head plus #30791): no lint in this PR's files. It failed only on the two base reds below.
Base clippy reds (not this PR's)
crates/cmux-tui-cloud-conversations/src/environments.rs:89"this boolean expression can be simplified": from #30357. Its revert #30791 (ready-to-land) removes it.crates/acpmux/src/prompt_provenance.rs:584"this boolean expression can be simplified": from #30526. The panic agent's #30853 fixes it.crates/cmux-tui/src/link/start_chat_cli.rs:282"redundant clone": from #30574. The panic agent's #30853 fixes it.- The lead's list also named
cmux-tui-core/src/mux/resource_topology/topology_operation.rs:601as a base red (from ebce6312d8f). On this branch that line was this PR's own new code, and 4e49f3862f2 fixed it.
Observed / Not run
- Observed: the fleet steps above and
prepush-gate.pywith no new violations. - Not run: the full cmux-tui test suite (only
pinned_columns_tests), a first-5s bench, a dogfood build. I read, but did not run, the frontendnew-tabcreation path throughcmd_frontend.rs; it commits through the same function.
No capture: the visible result is a terminal in a new column right of Home, as for a person's new terminal from Home.
perf claim: none