Repository navigation
Description
Problem
In Dispatch Mode — most visibly in Tiled Dispatch — a terminal created from Dispatch always appears at the top of its project group in the session index, above every agent, no matter when it was created. Agents created from the same surface land in creation order at the bottom of the group, where you expect a new thing to appear.
The result is that the one row class you did not just create is the one that grabs the top slot, and the index stops reading as a chronological list of what you have open.
Expected behavior
A terminal created from Dispatch takes the same position an agent created from Dispatch would take: appended in creation order, in the natural flow of the list. Terminal and agent creation from Dispatch should be one flow, not two.
Actual behavior
Terminal rows sort above all agent rows in their project group, permanently.
Reproduction
- Enter Dispatch Mode (classic or Tiled) on a project with one grid agent.
- Create two or three Dispatch agents (⌥D / the project header "+"). They append below, in creation order.
- Create a terminal (⌥T, or the New Agent overlay → Terminal).
- The terminal row renders at/near the top of the project group, above every agent — including agents created before it.
Root cause
splitFocused (src/renderer/src/workspace/hook/actions/pane.ts) has two separate Dispatch branches:
dispatchMode && kind !== 'terminal'(line ~374) — creates a detached dispatch session (detachedSessions[id],detachedAt: Date.now()).dispatchMode && kind === 'terminal'(line ~450) — inserts a grid leaf viasplitLeafinto the owning tab's tile tree.
buildDispatchGroups (src/renderer/src/workspace/dispatch/dispatchSelectors.ts:72-75) builds each project group as [...gridSessionIds, ...detachedSessionIds] — grid leaves unconditionally before detached rows. Since every Dispatch-created agent is detached and every Dispatch-created terminal is a grid leaf, terminals are structurally guaranteed to sort above agents.
The position is not even stable within the grid: resolveDispatchTerminalSplitTarget picks targetLeafId ?? focusedLeafId ?? leafIds[0] as the split anchor. The focused Dispatch row is normally a detached agent with no grid leaf, so the anchor degrades to leafIds[0] — the terminal is spliced beside the first grid leaf, which is what makes it read as "pinned to the top".
The WHY terminals still enter the grid from Dispatch comment at pane.ts:~488 justifies this with tmux name, resize lifecycle, undo/close history, and persistence. Each of those is session-scoped, not grid-scoped, and detached terminals are already a supported state elsewhere:
detachFocusedToDispatchhas never excluded terminals.renderWorkspaceLeafrenderskind === 'terminal'in a Dispatch lane already.TerminalLeafwakes its own PTY on mount viaensureSessionLive('terminal-leaf.mount'), andensureSessionLivepassesrecoverTmuxNamefor terminals.undoClosealready gatesrecoverTmuxNameon kind for detached entries, with a comment anticipating exactly this change.findTerminalSessionInTab's own comment states the project terminal "is allowed to be detached into Dispatch now".
Impact
User-visible ordering defect on the primary multi-agent scanning surface. It also makes the A1/A2/A3 dispatch coordinates shift under the user whenever a terminal is created, because globalIndex is assigned in final render order.
Adjacent dead code found while tracing
findTerminalSessionInTab(dispatchSelectors.ts:224) — only caller is the function below.findTerminalInLatestTab(hook/actions/dispatch.ts:389) — defined, never called.
Both are leftovers from the removed Dispatch project-terminal column.
Acceptance criteria
- A terminal created from Dispatch is a detached dispatch session filed under the same resolved project as an agent would be.
- It renders in creation order in the session index, not above the agents.
- The Dispatch: new project terminal uses stale active tab instead of focused lane #366 invariant holds: project tab and cwd both come from the focused lane / focused Dispatch row, never from a stale
activeTabId. - Tiled Dispatch places the new terminal in the focused lane, exactly as a new agent does.
- The two Dispatch creation branches in
splitFocusedare merged into one. - Regression coverage asserts the ordering, not just the target resolution.
Activity
Juliusolsson05 commentedon Aug 28, 2026
Correction to the root-cause section above, found during review of #672.
The original analysis claimed the terminal was pinned to the top because
resolveDispatchTerminalSplitTarget's anchor degraded toleafIds[0]. That is wrong. The anchor istargetLeafId ?? focusedLeafId ?? leafIds[0], andfocusedLeafIdistab.focusedSessionId— whichfocusDispatchSessiondeliberately never writes (its own comment says so). So it stays a real grid leaf and theleafIds[0]fallback is effectively unreachable.splitLeafalso inserts after its anchor, so a Dispatch terminal could not occupy the first leaf position even if it were reached.Verified by executing the pre-fix path: with grid
[g1,g2,g3]and Dispatch focus on a detached agent, the terminal lands at the end of the grid slice, not the start.The real cause is the
[...gridSessionIds, ...detachedSessionIds]concatenation inbuildDispatchGroups, on its own and entirely. The terminal's position within the grid slice never mattered, because the whole grid slice precedes the detached rows every Dispatch agent lives in. The reported symptom is the same either way; the mechanism named in the issue was not.Two further corrections:
kindOnlyis true whenever Dispatch is active, which filters the kind picker down to agent providers, so Terminal is never offered there. The live routes are ⌥T / ⇧⌥T and the two palette rows.fix(dispatch): restore undo-close for detached sessions and prove the…
fix(dispatch): carry linked-child metadata through undo and correct t…
fix(dispatch): resolve the detached-undo review findings