cmux NEXT: subagents fill one split below the first, then open as tabs

Merged
TE
6d ago
+260-19

Leo, 2026-10-28 (full words in leo-workspace design/pane-placement.md):

"likewise i feel like when i click your subagents/associated sessions... would it make sense to also open a new tab of it on the right? ..."

"We also like splits. Sometimes you can do the splits. I don't know what given sizes you can do the splits. Like the split on the right and all that stuff. But I don't know if you want to do more splits versus just adding more to the top tab or space pane, the top pane. I mean the right top pane, and then you split to the bottom once so that you get the three items. But you want four items. I don't know what the fourth item should go."

What was wrong

Transcript subagent and Task cards (chat.subagentPane → AgentSubagentPanes) opened every new subagent in another split below the newest subagent pane, with no cap and no width floor, so five subagents made five stacked slivers. Session Info's sessions and side chats (AgentSidePanes) already open later ones as tabs.

What changes

AgentSubagentPanes.placement(chat:hooks:), one small function, follows design/pane-placement.md:

  • A subagent that already has a tab is shown, as before.
  • 1st subagent: a split right of the chat (as before).
  • 2nd: one split below the first (the bottom-right area).
  • 3rd and later (the chat's 4th item and on): a tab in the newest subagent's pane, never another split.
  • No split may leave the chat under 480 pt or the right column under 420 pt (the right split counts as even). Such an open becomes a tab in that pane instead; for a first subagent that is the chat's own pane.
  • An unfocused tab open (a subagent that opened on its own) leaves focus where it is. A click still focuses it.

App side: place now takes a Placement (.right, .down or .tab), plus pane and width hooks (AgentTabs.paneWidth, the pane controller's view width). Main-pane layout is unchanged: there, a split already becomes a tab in the right column.

Overlap

This overlaps #26582 (RightPanePlacement), which doesn't touch AgentSubagentPanes yet. This PR doesn't take #26582's type or touch its branch. The decision lives in one function, so #26582's owner can fold it into that policy later.

Tests

AgentSubagentPanesPlacementTests:

  • aFourthSubagentIsATabNotAnotherSplit: four subagents give [.right, .down, .tab, .tab] and three panes. On base the 4th (and the 3rd) make a new split.
  • An open subagent is shown, not opened again.
  • A narrow chat (900 pt) opens the first subagent as a tab in the chat's pane.
  • A right column under 420 pt takes the second subagent as a tab.
  • A closed lower pane is split again once.

The existing AgentSubagentPanesFocusTests still compile against the new place signature.

Screenshots

No capture: the before/after of 3+ subagents opening needs the debug fixture's agents count from #28213, which isn't on this PR's base. Today's fixture starts two subagents, and two don't tell this PR from base. The same subagent placement is recorded on #28044, which carries it: four subagents, the new base with the fixture vs #28044's head. The capture that ran at this head (ef884b554e8f) was refused: its subagents had already finished, so none opened a pane.

Risk: behaviour change, limited to where the 2nd+ transcript subagent opens. No new timers, observers or animation loops.

Agent session: agent session

šŸ¤– Generated with Claude Code