Skip to content

cmux NEXT: a link opened to the right shows in its split in the click's frame, never in the source pane first - #32900

Merged
teamleaderleo merged 6 commits into
feat-cmux-nextfrom
lane/right-open-split-first-2
Nov 3, 2026
Merged

teamleaderleo merged 6 commits into
feat-cmux-nextfrom
lane/right-open-split-first-2

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Leo (2026-11-03): "it's so slow to open and close idk if there's some sort of threading issue but it's nigh instant for claude and codex so we nede to be at least as fast", and "the browser/tab opening animation still plays on the main pane before going". Earlier ask (#23032's title): "a link opened to the right appears in its split, never in the source pane first".

leo-named-fix. The daemon half, #32862, has landed (de2ecaf).

What

A web link LinkOpener puts in a new split right or below (a chat link, a terminal Cmd-click, a file page's link) no longer gets made as an unselected tab of the source pane, then waits for the store to list it (up to listLimit, 30 s) before a TabMoves.toNewSplit and a second wait: two daemon round trips with nothing on screen, and the opening played on the source pane first.

Now, when the pane's daemon serves frontend-browser-tab-split-v1 (plus split-client-keys-v1):

  • BrowserTabSplitCommand (CmuxNextDaemonService) puts a provisional split with a stand-in browser tab in the store in the click's turn, and the window lays it out in that frame, the way an agent tab's split (ConversationTabSplit) and Cmd-D already do.
  • One new-frontend-browser-tab {pane, split} makes the tab in the daemon's new pane, which replaces the provisional one by its client-minted id. Even split sizing is shown at once and committed on the reply, or rolled back on a refusal.
  • LinkOpener.open takes an optional SplitMake; .splitRight / .splitBelow use it and fall back to make-and-move when it answers nil (an older daemon, a remote pane without browser tabs, Home's pinned dock, an incognito pane, a pane without split room). .newColumn, .besideDock and .dockChat are unchanged here.

Review fixes (router-review gpt-6.1-sol BLOCK at 6027476): the open's turn ends only once the store lists the daemon's pane, not just at the reply, so the next queued link never aims at the provisional pane; incognito panes keep make-and-move, since their record holds about:blank until the reply registers the URL. The bench's quick arm skips commands with no key (link_right).

Not in this PR (#33021): folder and terminal opens to the right still use make-and-move, and turns.serially still serializes opens beside one source.

Carried from austinywang's #23032: PaneBrowserTabSplit (now BrowserTabSplitCommand, moved into CmuxNextDaemonService), the split request field and connection check, the stand-in ProvisionalTab.browser(_:), the split-room rule, and LinkOpenSplitDirectTests (adapted to today's opener). Those commits carry Co-authored-by: Austin Wang.

Also: LinkDragItem and LinkDropPlan moved from CmuxNextApp into CmuxNextAppCore and existingPane moved onto LinkOpenPlacement.Target, so the App target does not grow. debug.new_tab {action: bench_create, command: link_right} times a link opened to the right (click to the frame the new pane shows).

Tests

LinkOpenSplitDirectTests (CmuxNextAppTests): a link to the right shows a split intent with the stand-in at once, the source pane never lists the tab, and exactly one create goes out carrying split with the provisional ids; the open's turn waits for the daemon's pane, not just the reply; an incognito pane keeps the move; without the capability the tab is made in the source with no split; New Column still moves; a split maker that answers nil falls back.

Performance

Same fleet Mac for both arms (instant-new-surfaces-bench.py --commands link_right --arms warm --phases idle,load --runs 12). Before: 0d15fd4 (#32862 plus only the bench command). After: 71fd6d3 (this head).

metric before after delta harness or run link
idle link_right visible_ms p50 340.8 103.6 -237 (-70%) cmux-ci steps 81efa5771658 / 20e57e757a53
idle link_right visible_ms p95 378.1 106.9 -271 (-72%) same
load link_right visible_ms p50 336.1 188.8 -147 (-44%) same
load link_right visible_ms p95 522.5 206.2 -316 (-61%) same
idle frames to visible p50 / p95 4 / 4 1 / 1 -3 same
idle confirmed_ms p50 442.4 215.7 -227 (-51%) same
load confirmed_ms p50 448.8 294.2 -155 (-34%) same
key_ms (click's main-thread turn) p50 / p95 0.35 / 2.6 9.0 / 15.4 +8.7 / +12.8 same

The split now shows in the first frame after the click (frames to visible 1, from 4) and never in the source pane. The click's turn costs more: it now records the split intent and lays out the window in that turn, as Cmd-D does, instead of doing nothing until the daemon replies. The p95 of 15 ms is above the 10 ms action budget; #33021 tracks trimming it.

Observed / Not run

  • Ran on the fleet at 71fd6d3: CmuxNext compile passed; LinkOpenSplitDirectTests, CmdClickOpensRightTests, SplitBrowserFailureTests, BrowserEnabledToggleTests, LinkDragDropTests and RightColumnAfterSplitTests all passed (6 of 6 suites).
  • Regression tests for the review's two findings: theTurnWaitsForTheDaemonsPaneNotJustTheReply and anIncognitoPaneKeepsTheMove.
  • Not run: a tagged-build click-through by hand; the timing above is from the bench's scripted open.

Risk

risk:normal. Opens to the right on a current daemon change path; older daemons keep the old path. A refused create rolls the split back.

Changelog

Fixed: a link opened to the right shows in its new split at once, never as a tab of the pane it was clicked in first.

Agent session: agent session

🤖 Generated with Claude Code

@cursor

cursor Bot commented Nov 3, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

@teamleaderleo
teamleaderleo force-pushed the lane/right-open-split-first-2 branch from afdedd6 to 3febc50 Compare November 3, 2026 03:24
Base automatically changed from lane/right-open-split-first to feat-cmux-next November 3, 2026 04:22
@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Nov 3, 2026
@teamleaderleo
teamleaderleo force-pushed the lane/right-open-split-first-2 branch from ddcfc13 to 6027476 Compare November 3, 2026 04:25
@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review 71fd6d3: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr32900 -f ref=lane/right-open-split-first-2 -f platform=macos -f backend_mode=local

web and size checks: green

Default changes

This pull request changes 1 default at 777eebc828fc (what everyone gets without configuring anything):

  • tunable-source.CmuxNextApp/Control/DebugInstantCreateBench.swift:run: "9d1836a0453e460355760c8a5060c3255750c8efcb90ee928a011dc67b5da380" -> "ac3da2d24945055686692b663783c33bb206d47f60c982a4489fa6cec62340ba"

Signed off on this commit by review (codex gpt-6-astra high, router-review).

Merge receipt: merged as 00793f850ff1 with these defaults changed, signed off by review (codex gpt-6-astra high, router-review).

Regenerate the snapshot with python3 scripts/ci/defaults_guard.py snapshot.

Shipped in:

@blacksmith-sh

blacksmith-sh Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Found 6 test failures on Blacksmith runners:

Failures

Test View Logs
/tmp/tmpthn83wcu/summary-fail.json (live):/summary-fail.json (live): View Logs
/tmp/tmpy0a5xywa/summary-missing.json (live):/summary-missing.json (live): View Logs
[new_workspaces] debug.new_workspace timed 0 of 50 new workspace rows/
[new_workspaces] debug.new_workspace timed 0 of 50 new workspace rows
View Logs
[new_workspaces] the window draws no frames/[new_workspaces] the window draws no frames View Logs
[tabs] debug.tab_switch timed 0 of 40 switches/
[tabs] debug.tab_switch timed 0 of 40 switches
View Logs
[tabs] the window draws no frames/[tabs] the window draws no frames View Logs

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

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6.1-sol high, sweep, via the review router; full diff from merge base e674d0b to this head): BLOCK at 6027476

[P2] [blocking] Packages/macOS/CmuxNext/Sources/CmuxNextDaemonService/BrowserTabSplitCommand.swift:59 - noteSettled does not wait for the store to apply the split’s events. Returning releases queued link opens while the right pane can still have its provisional handle; the next link sends that handle to the daemon and fails with “unknown pane.” Previously, placement completed after applying the move. - Wait for the created pane’s adoption before returning and releasing the next queued open.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6.1-sol xhigh, router-review): BLOCK at 6027476

BLOCK at 6027476
Visual verdict: no images; Leo tries the build

  1. [P1] [blocking] Wait for the daemon pane before releasing queued opens. BrowserTabSplitCommand.swift:59 records settlement without waiting for the pane to arrive, then completion starts the next queued link. If the reply precedes the pane events, that link targets the provisional right pane. BrowserTabService.open sends its provisional handle directly, causing an “unknown pane” refusal. The previous move path waited for the real pane. Resolve the daemon target before allowing dependent creates.

  2. [P1] [blocking] Preserve incognito URLs when events precede the reply. PaneBrowserTabOpener.swift:203 enables direct splits for incognito panes. Their daemon record contains about:blank, while BrowserTabService.open registers the actual URL only after the create replies. A preceding pane-added can therefore mount and cache a blank page; registering the URL later never navigates that page. Previously, make-and-move kept it unselected until the URL was registered. Register the URL before mounting, or retain the fallback for incognito.

Follow-ups:

  1. [P2] [follow-up] Implement the benchmark’s warm-only restriction. instant-new-surfaces-bench.py:14 documents link_right, but --commands link_right retains the default quick arm. That arm accesses the absent KEYS["link_right"] entry and raises KeyError before writing results. Enforce warm-only execution or implement the other arms.

  2. [P2] [follow-up] Supply native split screenshots. PaneBrowserTabOpener.swift:214 changes the displayed split layout; no after images were supplied.

teamleaderleo and others added 5 commits November 3, 2026 05:51
…it in the click's frame

LinkOpener's split right and split below no longer make the tab in the
source pane and move it once the store lists it (two daemon round
trips, and the opening played on the source pane first). A web link's
split shows now with a stand-in tab (PaneBrowserTabSplit) and one
new-frontend-browser-tab {pane, split} makes the tab in the daemon's
new pane (frontend-browser-tab-split-v1). A folder's terminal opens as
Cmd-D's optimistic split. An older daemon keeps make-and-move.

PaneBrowserTabSplit and the tests come from austinywang's #23032.

Co-authored-by: Austin Wang <38676809+austinywang@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-ed9226b71907d8aaa0b6
…(review)

A local foreground link took openStandIn first: the tab showed in the
source pane and its split request never finished, so the source's open
turns queued forever. The split branch now runs first; the test checks
that the next open beside the pane still runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-ed9226b71907d8aaa0b6
…ock rules

As in austinywang's #23032 (08c04fc): a chat dock, a pane with no
room or a strip column too narrow keeps the move, which refuses or
opens a new column as every split does.

Co-authored-by: Austin Wang <38676809+austinywang@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-ed9226b71907d8aaa0b6
… pane is listed; incognito keeps the move (review)

router-review (gpt-6.1-sol) BLOCK at 6027476: the reply can come before the
pane's events, so the next queued link aimed at the provisional pane; and an
incognito pane could mount about:blank before the reply registered its URL.
BrowserTabSplitCommand now waits (30 s bound) for the store to list the made
tab, as LinkOpener's move does; incognito panes keep make-and-move. The bench's
quick arm skips commands with no key (link_right).

Regression tests: LinkOpenSplitDirectTests.theTurnWaitsForTheDaemonsPaneNotJustTheReply,
anIncognitoPaneKeepsTheMove.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-ed9226b71907d8aaa0b6
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex, router-review): SHIP at 71fd6d3

Reviewer: codex gpt-6.1-sol xhigh, fast tier.

Follow-ups: 3. Each lands as an issue in this repo that links this receipt; the lane that opened the PR fixes it forward.

SHIP at 71fd6d3
Visual verdict: no images; Leo tries the build

  1. [P2] [follow-up] BrowserTabSplitCommand.swift:71: End the listing wait on cancellation. If the reply precedes pane-added and the user then closes the stand-in, the cancelled overlay hides the surface this wait requires. Subsequent opens beside that source stall until the 30-second timeout. Observe cancellation during the wait and test this ordering.

  2. [P2] [follow-up] DebugInstantCreateBench.swift:140: Check confirmation of the newly created pane. link_right becomes visible while focus can still remain on the source terminal; this condition accepts that terminal’s existing real IDs and reports confirmation while the split remains provisional.

  3. [P2] [follow-up] PaneBrowserTabOpener.swift:214: Supply after images for the native split layout; none were available for visual review.

Follow-ups:

cmux-bot[bot]
cmux-bot Bot previously approved these changes Nov 3, 2026

@cmux-bot cmux-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cmux-bot approves 71fd6d3: an independent review passed this exact head (router-review receipt by teamleaderleo). A push needs a new review; a later BLOCK at this head dismisses this approval.

@cmux-bot
cmux-bot Bot temporarily deployed to ci-routing-agent November 3, 2026 07:32 Inactive
@cmux-bot
cmux-bot Bot temporarily deployed to ci-routing-agent November 3, 2026 07:33 Inactive
@cmux-bot
cmux-bot Bot temporarily deployed to ci-routing-agent November 3, 2026 07:35 Inactive
@cmux-bot
cmux-bot Bot dismissed their stale review November 3, 2026 07:40

A newer verdict blocks 777eebc (no independent SHIP at the head).

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6-astra high, router-review): SHIP at 777eebc
Visual verdict: no images; Leo tries the build

Range review of the PR's diff at the head left by cmux-bot's base merge. No blocking findings.

Follow-ups: [P2] link_right bench confirmation should require the new pane's real tab (already #33243 item 2).

@teamleaderleo
teamleaderleo merged commit 00793f8 into feat-cmux-next Nov 3, 2026
149 of 153 checks passed
@teamleaderleo
teamleaderleo deleted the lane/right-open-split-first-2 branch November 3, 2026 08:33
@cmux-bot cmux-bot Bot removed the stack-blocker label Nov 3, 2026
teamleaderleo added a commit that referenced this pull request Nov 3, 2026
…in their split

Leo 2026-11-03: "the new tab at the top opening first in the main pane THEN in
the right split is still an issue... that top tab animation". #32900 made a
link opened to the right use `new-frontend-browser-tab {split}`; Split Browser
Right/Down (its key and menus, link hints' F, VS Code inline, browser hits) and
the chat header's Browser still made the tab in the source pane and moved it.
Both now use the one split maker (LinkOpener.browserSplit), falling back to
make-and-move where the daemon or pane cannot.

Leo-ask: ask-08552f8ea0

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-910f9bcf7fd2069746eb
teamleaderleo added a commit that referenced this pull request Nov 3, 2026
…in their split, never in the main pane first (#33466)

* test(next): Split Browser Right shows its split with its tab in the action's turn (failing)

Leo-ask: ask-08552f8ea0

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-910f9bcf7fd2069746eb

* fix(next): Split Browser and the chat header's Browser open straight in their split

Leo 2026-11-03: "the new tab at the top opening first in the main pane THEN in
the right split is still an issue... that top tab animation". #32900 made a
link opened to the right use `new-frontend-browser-tab {split}`; Split Browser
Right/Down (its key and menus, link hints' F, VS Code inline, browser hits) and
the chat header's Browser still made the tab in the source pane and moved it.
Both now use the one split maker (LinkOpener.browserSplit), falling back to
make-and-move where the daemon or pane cannot.

Leo-ask: ask-08552f8ea0

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-910f9bcf7fd2069746eb

* fix(next): browserSplit's doc keeps the App target's line count flat after the base merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-07121e8fa07d68e815b1

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@lawrencecchen

Copy link
Copy Markdown
Contributor

Swift logic follow-up (tip watcher swift-logic row, STATUS 13027 "SWIFT LOGIC CULPRITS"; lead order).

This PR raised swift_logic_lines by +123 at its merge commit 00793f8: scripts/cmux-next/swift-logic-lines.py before and after the merge, with the same table (the tip's, c6be943). The largest declarations of the rise:

  • struct BrowserTabSplitCommand in Packages/macOS/CmuxNext/Sources/CmuxNextDaemonService/BrowserTabSplitCommand.swift: +76 lines counted as Swift logic (no row in plans/cmux-next/swift-logic-classes.tsv)
  • extension PaneBrowserTabOpener in Packages/macOS/CmuxNext/Sources/CmuxNextApp/PaneBrowserTabOpener.swift: +49 lines counted as Swift logic (no row in plans/cmux-next/swift-logic-classes.tsv)
  • enum LinkDropPlan in Packages/macOS/CmuxNext/Sources/CmuxNextAppCore/LinkDropPlan.swift: +26 lines counted as Swift logic (its row counts it as logic)
  • struct BrowserTabSplitRequest in Packages/macOS/CmuxNext/Sources/CmuxNextDaemonService/BrowserTabSplitCommand.swift: +15 lines counted as Swift logic (no row in plans/cmux-next/swift-logic-classes.tsv)

plans/cmux-next/layer-ownership.md says Rust owns every state, list and rule and Swift is the platform shell. Please move the new logic of this PR in a follow-up: to Rust (the crate that owns the area; the owner lists in STATUS 12147-12165 name the target per area), or at least out of the app into its package. If a declaration above is platform code (AppKit, Core Animation, OS calls, strings, tokens, DEBUG tooling), reply here and the table gets a PLATFORM row with your reason instead.

The swift-logic row is a report, not a gate: nothing blocks on this comment.

This branch was previously deployed

3 inactive (2 outdated) deployments
ci-routing-agent — 777eebc8 Deployed Nov 3, 2026 by cmux-bot[bot] via wait for same-key tree publisher #104662
artifacts — 71fd6d36 Deployed Nov 3, 2026 by teamleaderleo via publish tree-addressed cmux-tui #104222
sentry-dsyms — 71fd6d36 Deployed Nov 3, 2026 by teamleaderleo via upload cmux-tui dSYMs to Sentry #104222
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants