carry of #28044: cmux NEXT: anything opened on the right goes to the lower pane when the right already holds something

Merged
TE
6d ago
+1143-324

Carries #28044 with its commits and authorship kept, brought up to current feat-cmux-next with one merge. #28044 is claimed by a live lane (ux-tabs-panes-r2), so this is a separate PR instead of a push to its branch. A comment on #28044 links here.

Leo, 2026-10-30 and today:

"subagent stuff... or anything we expect to open on "the right"... if there's already a thing on the right, there should be logic for just putting it on the lower pane of the right. that kinda split."

"do we have this fix yet wrt the thing opening up terminal and browser" ... "like having it go split instead of just adding a third colujmn"

"bro if i told you guys to do it and it's aligned with claude/codex then do it"

Default change. This deliberately changes where the second and later items opening on the right go. The defaults guard lists three tunable-source defaults: PaneHandlers.neighbor, PanePlacementRouting.autoLayoutTarget and PanePlacementRouting.route. Leo's words above authorize the change, and the independent review receipt at the head clears the guard.

Spec: leo-workspace design/pane-placement.md ("Something already on the right"). Leo's main-pane rule wins over the narrow-window fallback there: teamleaderleo/leo-workspace#1831.

The rule

One function, RightColumnPlacement.decide (CmuxNextAgentPane), decides every open on the right of a pane. Its input is the right column the window actually shows (LinkOpener.rightColumn(beside:), from the pane geometry):

What's on the right of the sourceWhere the new item goes
nothinga new split right of the source
one pane (diff, terminal, browser, session, anything)a new split below it; that pane keeps what it shows
two or more panesa tab in the lowest pane, never a third split
  • No split leaves the chat under 480 pt or the right column under 420 pt; that open becomes a tab instead.
  • Closing the lower pane's last tab removes the pane, so the split collapses back, and the next open splits below again.

Paths routed through it

  • Transcript subagent / Task cards (AgentSubagentPanes): #27789's placement function, now shared and reading the window's geometry instead of the tracker's own list.

  • Side chats, /btw, Session Info sessions, transcript session cards (AgentSidePanes; chat.sideChat and chat.offer.open).

  • Links opened to the right, and chats opened from Needs you (LinkOpener / LinkOpenPlacement): new .splitBelow target for a source that takes tabs. openWeb's reuse of a page already open on the right, openFolder and DurableTabLink's fallback handle it.

  • Main-pane layout (ux-owner, 2026-10-30, on Leo's "anything we expect to open on 'the right'"): the strip column right of the docked chat is its right side, so the same lower split applies (LinkOpenPlacement.inColumn, openOnRight). With nothing on its right yet, main-pane layout still makes the column by its own rule.

Unchanged: Home's pinned dock (a different surface, not named) keeps the link rule: the pane on its right, else a column beside the dock.

Overlap

This overlaps #26582 (RightPanePlacement, the general document/session policy, owned by another lane), which doesn't touch these paths yet. This PR doesn't take its type or touch its branch. Every path here calls one pure function, so it can fold into that policy later.

What this carry adds on top of #28044's head (e371587e)

  • Base merge. Four files conflicted:
    • AgentTabs+Header: kept the turn and bound the subagent's own tab title (#28497).
    • AgentTabs+Wiring: dropped the unused paneWidth, so the any-machine lookup ratchet goes from 14 to 13.
    • LinkOpener: kept base's OutboundTab and input resolver alongside the turn's done.
    • The defaults snapshot was regenerated.
  • P1 from the review at 1cfd9d14. A link from a chat on an inactive screen ignored the right column. right now falls back to the measured column's top pane, so an occupied column never reads as empty.
  • P2 from the review at bd2b918d. The duplicate-link search already covers both right panes at #28044's head (LinkOpenPlacement.reusePanes).
  • Turns are an instance. RightColumn's static queue became RightColumnTurns in CmuxNextAgentPane, owned by LinkOpener. The arch ratchet had refused two new static vars in the App.
  • Review of this carry at d495c86e. A right-column pane is looked up only in the chat's own workspace, because a bare pane:<handle> key can repeat across daemons.
  • Review at 1eb458cf. Three fixes:
    • A file that a tab already shows is selected in place and ends its turn at once. Before, it held the next open for 30 s. RightColumnEntryPointTests.anOpenShownWhereItAlreadyWasEndsItsTurn covers it.
    • A folder's terminal no longer flashes in the source pane.
    • A refused agent open that had waited its turn now reports finished(false).

Interactions

  • #28633 (draft, stacked on #28044) moves header Changes/Preview/Files and docked New Terminal/New Browser onto this rule. It needs a rebase onto this PR: RightColumn.measure is now LinkOpener.rightColumn(beside:), and RightColumn.serially is linkOpener.turns.
  • #28544 adds an up-front "reuse the existing right pane" in LinkOpener.target(fromChat:). Rebased on this PR, it should go through LinkOpenPlacement.inColumn, so a chat's link still splits below one right pane instead of always taking a tab in it.
  • #26603 (RightPanePlacement's agent beside opens) overlaps AgentTabs+Header and LinkOpener. It is older, is stacked on #26582, and conflicts with this PR.

Follow-ups: manaflow-ai/cmux#28908 (a link from a narrow main-pane chat still opens in place), plus any filed from the review receipt.

Verification

  • Fleet compile-check --app and the pane placement suites at the final head: results in a comment.

Observed / Not run

  • Observed: three different-model reviews of this carry (gpt-6-astra, high). Every blocking finding is fixed above.
  • Not run: no local builds; compile and tests run on the fleet only.

Screenshots

Before/after: a chat with a diff on the right, then a browser opened from it. The before shot shows a third column; the after shot shows the right side split. Captures are pending the fleet capture run.

Changelog

  • Changed: In cmux NEXT, opening something on the right of a chat that already has a pane there now splits below that pane, then adds tabs there, instead of adding another column.

Risk: normal. A behaviour change in where the second and later items open on the right. The only timer is the turn backstop (30 s, cancelled when the turn ends).

šŸ¤– Generated with Claude Code

Agent session: agent session