cmux NEXT: a chat's subagents open beside it; Tile Subagents stacks them (Lawrence's zellij ask)
Summary
Lawrence's zellij-style ask: watching a chat's subagents work side by side, without them taking over the window. Built on #20553 (open beside), so it lands after it.
- A subagent row opens beside the chat. Clicking a row in the chat's Subagents card opens that subagent as a tab in the pane right of the chat, by the same rule as a link (#20553). The first click makes the pane, and a lone chat docks left so the card stays in view. Later clicks add tabs to that pane. Clicking a row whose tab is already open selects that tab. Each tab is named for the subagent's task, and keyboard focus stays in the chat. Before this, each click split the chat's pane again.
- Tile Subagents (palette
agentChat.tileSubagents, plus a "Tile" button on the Subagents card) opens every running subagent of the chat in its own pane, stacked right of the chat. The stack's panes share its height equally, and again when one closes. Only that stack is equalized, not the whole screen. An ended subagent gets no pane. - default call: auto-tile OFF (Lawrence's zellij ask). The card's switch, renamed "Tile subagents automatically", now defaults to off. When it's on, new running subagents tile on their own as before. The coordinator agreed: it matches the decided "open beside, one right pane" model.
- The switch is also a Settings row. Settings > General > Agent Chat has "Tile Subagents Automatically" (
agentPane.tileSubagentsAutomaticallyin cmux.json, off by default). The card's switch and the row are the same setting. The host pushes it to chat pages with the composer's flags, and the switch writes it throughpane.tileSubagentsAutomatically, the same host write aspane.showDictation. The switch's old localStorage copy is dropped, so a saved "on" from before reads as off.
SubagentBeside.swift holds the placement. AgentSubagentPanes keeps which tab shows which subagent, and the tiled stack's order.
Limits:
- A pane's title is the task only. acpmux doesn't carry a subagent's model yet.
- A finished subagent's pane stays open, showing its ended state.
Testing
scripts/cmux-next/tile-subagents-e2e.py drives a real chat on a fleet capture slot. It uses acpmux's fake harness, plus the subagents seed fixture: three running subagents and one done.
| build | result |
|---|---|
| red: #20553's head plus only the fixture and the script (9610be36, Mac DEV) | FAIL 0/6: each row click splits again, no Tile action |
| dae377f3, before the dock fix | 5/6: the first click opened a full-width column and scrolled the chat off-screen |
| f627dbf3 (dock fix), Mac DEV | PASS 6/6: tab beside the chat; second tab same pane; re-click selects; Tile makes 3 panes named for their tasks, none for the ended one; heights 297/297/296; after a close 446/445 |
- Release compile on e26f4bbb (the feature commits): BUILD SUCCEEDED. Since then: base merges, the dock fix (
SubagentBeside.swift) and test-only changes. - Pre-push on each Swift push: crash ratchet, concurrency, god files (nothing grew), system-blue, conventions lint. Also run on the web side: webviews
bun run check, ui-rules andgen-strings --check.
The fixture's subagents aren't real acpmux subagents, so in the recording their tabs show the chat's transcript, not a subagent's own view.
Settings row: TileSubagentsSettingTests (Swift) and webviews/test/tile-subagents-setting.test.tsx, red first (no row; the switch kept its own copy), then green.
Changelog
Added: Clicking a subagent in a chat's Subagents card opens it as a tab beside the chat, and Tile Subagents stacks every running subagent in its own pane, with a Settings row to tile them automatically (off by default)
🤖 Generated with Claude Code