CL-6942: split spawn_agent / wait_agents out of task() - #600
Merged
TheGreatAxios merged 4 commits intoAug 24, 2026
Merged
Conversation
4 tasks
TheGreatAxios
force-pushed
the
cl-6942-split-spawn-from-wait-non-blocking-spawn_agent
branch
from
August 24, 2026 05:36
74104e4 to
9aef384
Compare
TheGreatAxios
enabled auto-merge (squash)
August 24, 2026 05:38
TheGreatAxios
force-pushed
the
cl-6942-split-spawn-from-wait-non-blocking-spawn_agent
branch
from
August 24, 2026 05:48
9aef384 to
5701827
Compare
spawn_agent starts a worker and returns immediately with {agent_id,
status}; wait_agents blocks on any of a target set (default: all live
agents) reaching a terminal state, or a clamped timeout, without
touching the workers on timeout. task() is unchanged.
fleetRecords is a never-capped map of terminal spawn_agent results, written before the session store's complete()/fail() so wait_agents never loses a report to the store's TUI-sized finished-session cap. spawn_agent also now refuses a second concurrent implement-intent spawn against the same cwd (no worktree isolation yet), releasing the lane once the running one finishes; explore/plan/review-intent spawns are unaffected and may still run concurrently.
read_agent_trace's descendant-scoping check resolves a worker's parent chain from its trace directory name, so spawn_agent must pass the session-store id the same way task-tool.ts does.
TheGreatAxios
force-pushed
the
cl-6942-split-spawn-from-wait-non-blocking-spawn_agent
branch
from
August 24, 2026 05:51
5701827 to
7b229eb
Compare
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.
Summary
spawn_agent(starts a worker, returns immediately with{ agent_id, status: "running" }, never awaits completion) andwait_agents(blocks until any given/live target reaches a terminal state, or a clamped timeout elapses without touching the workers) as two new fleet verbs.wait_agentsblocking is driven by the existingSubAgentSessionStoremailbox (subscribe) raced against a real timer — no polling.task()is unchanged; it remains the fused spawn+block primitive for the single-worker case.spawn_agent/wait_agentsinFLEET_VERBS(already present from CL-6941) and gated at the same tier-mount point inrun.tsastask/search_agents, additively.Report retention fix: the session store's finished-session cap (
maxCompleted, default 20) is a TUI display cap that evicts the oldest finished session — report included — once more than that many have finished.task()never hit this because it awaits its own result before returning.spawn_agent/wait_agentscan fan out well past that cap in one turn, so a report could be silently lost beforewait_agentsever collected it. Fixed withfleetRecords, a small never-capped map (agent id -> terminal status/report/error) written before the session store'scomplete()/fail()(so the store's synchronoussubscribenotification always sees the up-to-date record); an entry is removed only oncewait_agentsactually delivers it to a caller.Worktree isolation: spawn_agent's whole point is running several workers at once, so two implement-intent workers sharing one cwd with no isolation could corrupt each other's edits. I chose the "refuse" option over wiring real worktree isolation into spawn_agent: duplicating
task()'s worktree machinery (isolation, cleanup, stash baseline, preserve-on-dirty) inside spawn_agent's independent dispatch path was a larger, riskier surface than this PR's scope justified.spawn_agentnow refuses a second concurrent implement-intent (directorbuild) spawn against the same cwd with an actionable error naming the conflicting agent id, and releases the lane once that worker finishes. Explore/plan/review-intent spawns don't write and are unaffected — they may still run fully concurrently. A caller that wants concurrent implementation work today should usetask(useWorktree: true), which already has real isolation.Documented follow-ups (not in scope here):
spawn_agentonly supports closed-director dispatch (agent=a director id, orintent=) — no customAgentProfilelookup, no worktree isolation, no nested orchestration, no re-dispatch ledger. These remaintask()-only for now.## [Unreleased].Test plan
bun run checkgreen (lint, typecheck, build, full test suite: 5417 pass)src/subagent/agent-fleet.test.ts:spawn_agentreturns immediately with a runningagent_idwithout waiting for the workerwait_agentson one target returns once it completes while sibling workers stay runningwait_agentswith a short timeout on a still-running agent returnstimed_out: truewithout cancelling it, and a secondwait_agentscall still workswait_agentswith notargetswaits on all currently running spawned agentswait_agentscollects themtask()/ subagent tests unaffected (bun test src/subagent/: pass)origin/main(no conflicts inrun.ts)