Repository navigation
feat(orchestration): add Close Idle Orchestration Agents - #962
Merged
Merged
Conversation
Orchestration runs leave finished workers behind as idle Dispatch rows whenever the parent never calls close_run. The plan records what "idle" means (the completed lifecycle a parent agent already sees), why coordinators stay open while a worker below them is open, and why the bulk-close loop moves out of Close Old Agents so both surfaces share one copy of the destructive safety rules. Refs #960 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FmkiA3y12dq41bYLVZcuZj
…up surfaces Close Old Agents carried the #886 approved-close loop inline: preConfirmed per session, onlyIf revalidation at the kill boundary, linked children before parents, sequential mutation and per-session outcome buckets. A second bulk cleanup surface is coming, and a second copy of destructive safety code would drift with nothing comparing the two, so the loop moves to workspace/bulkClose.ts as closeGrantedSessions. Kills are ordered along both ownership edges (linkedParentId, then orchestrationParentId), deepest first. For Close Old Agents this only reorders kills among sessions it was already closing. The modal's pre-call revalidation is dropped: onlyIf re-runs the same predicate synchronously against the action's live refs, and a target either check drops lands in the same skipped bucket. That leaves narrowGrantToCurrent without a caller, so it is deleted with its tests. The existing CloseOldAgentsModal close tests pass unchanged. Refs #960 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FmkiA3y12dq41bYLVZcuZj
Orchestration runs leave finished workers behind as idle Dispatch rows whenever the parent never calls close_run. The new command closes every orchestration child in the window that has finished and is quiet, after one confirmation listing exactly those agents. "Idle" is the completed lifecycle a parent agent already sees through list_agents (exported as orchestrationChildLifecycle so there is one derivation), plus settled evidence. Requiring output rather than only the absence of work is what spares a just-spawned worker whose prompt has not been delivered. A coordinator closes only when every worker below it does, and is refused at its kill boundary while any worker is left, so a working worker is never orphaned. The flow is wired as workspace.closeIdleOrchestrationAgents because it needs the global toast and the action's live refs. It confirms even a single target: unlike ⌘W on a visible pane, it reaches rows the user may not see and records no undo entry. Fixes #960 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FmkiA3y12dq41bYLVZcuZj
… its PR Refs #960 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FmkiA3y12dq41bYLVZcuZj
…stration-agents # Conflicts: # src/renderer/src/features/command-palette/catalog.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Orchestration runs leave their workers behind. A parent agent spawns children with
orchestration_create_agent; when they finish, they stay in Dispatch as idle rows until the parent callsorchestration_close_run, which often never happens (the parent moved on, was closed, or the user wanted to read the output first). The only cleanup tools were closing rows one by one, or Close Old Agents, which is driven by an inactivity threshold and cannot tell a worker from an agent the user opened by hand.What this adds
A palette command, Close Idle Orchestration Agents:
orchestrationParentId, grandchildren included) placed in a project as a grid pane or Dispatch row.completed— the same renderer-derived stateorchestration_list_agentsreports to the parent — and its evidence is settled (not bootstrapping, spawning, or loading its transcript). Working, starting, never-answered, exited and failed children stay open.close_runand Close Old Agents.Design decisions
CloseOldAgentsModalintoworkspace/bulkClose.ts(closeGrantedSessions); Close Old Agents and the new command both call it. A second copy of destructive safety code would drift with nothing comparing them.linkedParentId ?? orchestrationParentId), deepest first. For Close Old Agents this only reorders kills among sessions it was already closing.onlyIfat the kill boundary re-runs the same predicate against the action's live refs and lands a dropped target in the sameskippedbucket.narrowGrantToCurrentlost its only caller and is deleted with its tests.orchestrationChildLifecycleis exported fromorchestrationMcp.tsandbuildAgentRecorduses it, so the user's cleanup and a parent'swait_agentscannot disagree about whether a worker is done. Requiring output (not merely "not live") is what protects a just-spawned worker whose prompt has not been delivered yet.advanced:advancedhides a command from the palette, and this is the everyday end of an orchestration run.whenis metadata-only (any orchestration child exists) because the palette evaluates it on every render and idleness scans transcripts.workspace.closeIdleOrchestrationAgentsinuseWorkspace, because the flow needs the global toast and the action's live refs, which a command context does not have (same as Close Tab and Switch Agents).Plan:
docs/superpowers/plans/2026-09-12-close-idle-orchestration-agents.md(first commit).Fixes #960
Tests and verification
New tests:
workspace/idleOrchestrationAgents.test.ts(unit): the eligibility contract. A finished worker is listed; the lead agent, an agent the user opened and a terminal child are not. Each working signal excludes, and so does each not-done or unsettled state (no output yet, no runtime, exited, failed, spawning, replaying history, loading transcript). A buried worker is ignored. A coordinator stays out while any worker below it is not a target, and joins once they all are. The per-kill predicate refuses a coordinator with any worker left.workspace/idleOrchestrationAgents.renderer.test.tsx(renderer, real close executor viamountPaneActions): the dialog lists exactly the finished workers, and workers close before their coordinator with no undo entries. A worker that starts working while the dialog is open is skipped and its coordinator kept. Declining closes nothing. With nothing idle there is a toast and no dialog.Local runs on Node 24:
npm run typecheck(control SDK, workflow-mcp build,tsc -b): pass.idleOrchestrationAgents,closeConfirmation,orchestrationClose, commandcatalogandtaxonomy, 113 passed. Renderer:idleOrchestrationAgents.renderer,CloseOldAgentsModalclose and rows, 23 passed. The Close Old Agents tests are unchanged and guard the shared-loop refactor.npm run test:contract: pass.rendererproject: 182 files, 928 tests passed. Fullunitproject: 2663 of 2664 passed; the one failure isimageAttachment.test.ts"every fixture is traceable to a census row and a real session", which needs a local Claude transcript that is no longer on this machine and fails identically onorigin/main(unrelated to this change; CI does not depend on it).npm run check:keybindings: pass.Not run locally: the app itself (the flow is exercised through the real close executor instead), and the
systemproject (this change is renderer-only; CI runs it). CIquality-gateis the merge gate.Known limitations
prompt_sentoverlay (OrchestrationBridge.lifecycleWithPromptDelivery) is not visible there, so a prompt a parent sends to a finished worker in the moment before the user confirms can still be in flight. The kill-boundary check catches it once the provider reports activity;close_runand Close Old Agents share this window.waitingand is left open.🤖 Generated with Claude Code
https://claude.ai/code/session_01FmkiA3y12dq41bYLVZcuZj