cmux-next: a link opened to the right appears in its split, never in the source pane first
A web link opened to the right (a terminal's Cmd-click with Open Links and Files: Split right, or a chat link with links.pagesFollowPlacement on, or a link dropped on a pane's right or bottom edge) first showed as another tab of the source pane, then slid into a new right split. Reported 2026-10-20 on nightly 828d367273f, where a pull request link clicked in an agent chat did exactly that.
Cause. LinkOpener made the browser tab hidden in the source pane (new-frontend-browser-tab), waited for the store to list it, then moved it with move-tab-to-split. The tab was in the source pane's strip for that whole round trip.
Fix. The tab is now created directly in its new split.
- Daemon:
new-frontend-browser-tabtakessplit: {direction, pane_id?, split_id?}(frontend-browser-tab-split-v1). It reuses thepane.splitpath ofconversation-tab-split-v1(now a sharedTabSplit/split_tab_pane_with_fields_as): one commit, onepane-addeddelta, and the source pane never lists the tab.splitneedspane,afterwithsplitis a bad request, and a keyed request fingerprints the split. Keys stored withoutsplitkeep their old fingerprint byte for byte. A right split beside Home's lone pinned dock is a 0.5-wide column, as the conversation split is.spec/commands.md,spec/sdk-schema.jsonand the regenerated bindings carry the field. - App:
PaneBrowserTabSplitshows the provisional split with a stand-in browser tab in the click's frame, along with its even sizing. It then sends one create with the provisional pane's client-minted ids, and the daemon's pane replaces the stand-in bypane_id. A refusal takes the split and its sizing away. Closing the new pane before the reply closes the tab when it arrives. - Kept on the old make-then-move path: older daemons, files,
.newColumn, left or top drops, popups the engine already made, and any panesplitRoomwould not split in place (a chat dock, no room, or a strip column too narrow, which opens a new column instead).
Not changed: what a chat link does by default. Since https://github.com/manaflow-ai/cmux/pull/22822 it opens as a tab in the chat's own pane. Related open PR: https://github.com/manaflow-ai/cmux/pull/22949 (the lone-chat dock hand-off animation of an agent-opened tab), a different path (AgentBesidePlacement).
Test
- Swift:
LinkOpenSplitDirectTestschecks that a Cmd-click to the right makes one create withsplitand no move, that the split shows at once, and that the source pane lists only its own tab; it also covers the fallbacks.FrontendBrowserTabSplitRequestTestscovers the wire encoding, and a scripted daemon checks that an older daemon is never sentsplit. - Rust:
server/frontend_browser_split_tests.rscovers the capability being advertised, onepane-addeddelta, the title kept, left and up, malformed requests refused, keyed replay and conflict, and the lone pinned dock. - Red commit d9f23d0b85c88 (tests only), fix 92ce549f6cf63, review follow-up 08c04fcf270ca.
Review
A correctness-first review subagent first returned FIX:
- The direct split skipped
splitRoom, so it split narrow strip columns. Fixed in 08c04fcf270ca. - It also split chat docks. Fixed in 08c04fcf270ca.
- The settle sequence is read from the current connection, not the replying one. Low; masked because
ProvisionalSplitkeeps the split until the daemon's layout places the pane. Left as is. - Test polling. Fixed: the test now awaits the create.
Changelog
- Fixed: A link opened to the right now appears in its split right away, instead of showing as a tab of the pane you clicked in first.
Observed / Not run
- Observed: the Python checks
check-sdk-schema.py,check-spec-inventory.py,bindings/codegen/generate.py --check, andprepush-gate.pywith no new violations. - Pending, added in comments: the fleet Swift suites on 08c04fcf270ca and the red commit, and the hosted Rust
--filter frontend_browserand--filter conversation_tab_split. - Not run: tagged-app dogfood of the click.
🤖 Generated with Claude Code
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Fixes a link opened to the right (a terminal's Cmd-click, a chat link with links.pagesFollowPlacement on, or a link dropped on a pane's edge) showing as another tab of the source pane first, then sliding into its split. The tab is now created directly in the new split, so the source pane never lists it.
new-frontend-browser-tabtakes asplitfield (frontend-browser-tab-split-v1), reusing the conversation tab split'spane.splitpath: one commit, onepane-addeddelta, and the source pane never lists the tab.- The app shows the provisional split with a stand-in tab in the click's frame and sends one create with client-minted ids; a refusal removes the split.
- The direct split respects the same rules as the old path: a pane that
splitRoomwill not split in place (a chat dock, no room, or a strip column too narrow) falls back to opening a new column. - Older daemons, files,
.newColumn, left or top drops, and engine-made popups keep the old make-then-move path. - Regenerated all SDK bindings and updated the command, schema, and api-compat specs; a Rust refactor shares the split code between conversation and browser tabs without changing the encoded request shape.