Problem
Closing the first/root agent pane in a project can close/remove the whole project tab and make all agents listed under that project disappear from Dispatch. Undo then restores only the original/root pane, not the full project agent listing.
Observed symptom:
- Close the first agent in the first project.
- The whole project disappears, including panes/agents under it.
- Run Undo Close.
- Only the first/root pane comes back.
- The project's agent listing is effectively lost.
This is critical because a normal close action can destroy the visible project group and make active/recoverable work disappear from the UI.
Code Context
There is an asymmetry between explicit tab close and closing the last/root pane.
closeTab captures detached Dispatch agents before killing/removing the tab:
src/renderer/src/workspace/hook/actions/tab.ts
- It collects
detachedRecords from state.detachedSessions by projectTabId.
- It stores them as
detachedEntries on the tab undo entry.
undoClose later respawns those detached entries and re-associates them with the restored tab.
But closeFocused / closeSession root-pane close paths create a type: 'tab' undo entry without capturing detachedEntries:
src/renderer/src/workspace/hook/actions/pane.ts
- When
findParentSplitInfo(...) returns null, the code treats the pane as the root and records a tab undo entry.
- It only records
sessionMetas for collectLeaves(tab.root).
- Detached Dispatch agents associated with that project tab are not captured.
Dispatch grouping is tab-based:
src/renderer/src/workspace/dispatch/dispatchSelectors.ts
- Detached rows are grouped under tabs by
projectTabId.
So if root-pane close removes the tab but does not capture/recover its detached Dispatch agents, the project group can vanish and undo cannot restore the full listing.
Desired Behavior
Closing the root pane must not silently destroy the rest of the project's agent listing.
We need to decide the correct product behavior, then implement it consistently:
-
If closing the root pane is intended to close the whole project, it must behave exactly like closeTab:
- capture grid leaves
- capture detached Dispatch agents
- kill/remove all associated sessions
- undo restores the full project, including detached agents
-
If closing the root pane is intended to close only that pane/session, then it must not remove the project tab/group while detached agents still exist.
Tasks
- Reproduce with a project that has one root grid agent plus detached Dispatch agents.
- Compare
closeTab vs root-pane closeFocused / closeSession behavior.
- Make root-pane tab-close capture
detachedEntries the same way closeTab does, or prevent tab removal while associated detached agents exist.
- Ensure linked agents are handled deliberately.
- Add thick WHY comments documenting the chosen invariant.
- Add focused tests around:
- root-pane close with detached Dispatch agents
- undo after root-pane close
- explicit tab close remains unchanged
- dispatch project group survives/restores correctly
Acceptance Criteria
- Closing the root pane cannot make associated project agents permanently disappear from Dispatch.
- Undo restores the same project-level state that was closed.
- Root-pane close and tab close have consistent, documented semantics.
2026-09-11 product decision and confirmed scope
The user explicitly requests a choice between closing the first/root Dispatch agent and closing the entire tab. On current main, the earlier detached undo omission has been addressed, but root close still silently implies whole-tab scope. This is also the destructive expansion behind #886: Close Old Agents approves one root row and can terminate every excluded detached worker.
The implementation must offer Cancel, Close Agent, and Close Tab when closing a root with other project sessions. Close Agent preserves unrelated workers and the project by promoting an existing detached session into the mandatory grid leaf without restarting it. Undo restores the original root placement when the surviving layout has not subsequently changed. Close Tab explicitly names its full target set and captures all project sessions for undo. Bulk cleanup always uses bounded session-only scope and skips parents with surviving linked children.
Implementation branch: fix/dispatch-agent-close-safety. Related bug and selection/race coverage: #886.
Implementation status
Implemented in #887. The PR contains the root scope choice, survivor/undo handling, bounded bulk cleanup, activity and ownership revalidation, and regression coverage. Focused tests and type/package checks pass locally; the full local gate has the existing unrelated fixture-provenance failure tracked in #684. GitHub CI passed on the final PR head, including all test layers, coverage, package verification, and the minimum-Node gate. The PR is not merged, so this issue remains open.
Problem
Closing the first/root agent pane in a project can close/remove the whole project tab and make all agents listed under that project disappear from Dispatch. Undo then restores only the original/root pane, not the full project agent listing.
Observed symptom:
This is critical because a normal close action can destroy the visible project group and make active/recoverable work disappear from the UI.
Code Context
There is an asymmetry between explicit tab close and closing the last/root pane.
closeTabcaptures detached Dispatch agents before killing/removing the tab:src/renderer/src/workspace/hook/actions/tab.tsdetachedRecordsfromstate.detachedSessionsbyprojectTabId.detachedEntrieson the tab undo entry.undoCloselater respawns those detached entries and re-associates them with the restored tab.But
closeFocused/closeSessionroot-pane close paths create atype: 'tab'undo entry without capturingdetachedEntries:src/renderer/src/workspace/hook/actions/pane.tsfindParentSplitInfo(...)returnsnull, the code treats the pane as the root and records a tab undo entry.sessionMetasforcollectLeaves(tab.root).Dispatch grouping is tab-based:
src/renderer/src/workspace/dispatch/dispatchSelectors.tsprojectTabId.So if root-pane close removes the tab but does not capture/recover its detached Dispatch agents, the project group can vanish and undo cannot restore the full listing.
Desired Behavior
Closing the root pane must not silently destroy the rest of the project's agent listing.
We need to decide the correct product behavior, then implement it consistently:
If closing the root pane is intended to close the whole project, it must behave exactly like
closeTab:If closing the root pane is intended to close only that pane/session, then it must not remove the project tab/group while detached agents still exist.
Tasks
closeTabvs root-panecloseFocused/closeSessionbehavior.detachedEntriesthe same waycloseTabdoes, or prevent tab removal while associated detached agents exist.Acceptance Criteria
2026-09-11 product decision and confirmed scope
The user explicitly requests a choice between closing the first/root Dispatch agent and closing the entire tab. On current main, the earlier detached undo omission has been addressed, but root close still silently implies whole-tab scope. This is also the destructive expansion behind #886: Close Old Agents approves one root row and can terminate every excluded detached worker.
The implementation must offer Cancel, Close Agent, and Close Tab when closing a root with other project sessions. Close Agent preserves unrelated workers and the project by promoting an existing detached session into the mandatory grid leaf without restarting it. Undo restores the original root placement when the surviving layout has not subsequently changed. Close Tab explicitly names its full target set and captures all project sessions for undo. Bulk cleanup always uses bounded session-only scope and skips parents with surviving linked children.
Implementation branch:
fix/dispatch-agent-close-safety. Related bug and selection/race coverage: #886.Implementation status
Implemented in #887. The PR contains the root scope choice, survivor/undo handling, bounded bulk cleanup, activity and ownership revalidation, and regression coverage. Focused tests and type/package checks pass locally; the full local gate has the existing unrelated fixture-provenance failure tracked in #684. GitHub CI passed on the final PR head, including all test layers, coverage, package verification, and the minimum-Node gate. The PR is not merged, so this issue remains open.