Two lifecycle findings from the review: - A (blocker): dispose() could hang forever. It only sent SIGTERM and awaited exit, with no escalation — a child that traps SIGTERM (or our acp-agent if it doesn't quiesce on stdin EOF) would wedge dispose, stranding tool-subagent's finally cleanup and orphaning child-owned work (e.g. bash subprocesses). dispose now: ends stdin (graceful ACP close so the child can flush + exit), SIGTERM, then escalates to SIGKILL if it doesn't exit within a grace period (DEFAULT_DISPOSE_GRACE_MS, injectable via spec.disposeGraceMs), awaiting the certain exit. Mirrors the bash executor's bounded teardown. Regression test drives a SIGTERM-trapping mock subprocess and asserts dispose returns promptly — proven to hang (red) without the escalation. - B: an already-aborted request still spawned the configured binary. startAcpRun now returns an inert already-aborted run BEFORE spawning, so a pre-cancelled request launches nothing. Test points the command at `touch <sentinel>` and asserts the sentinel never appears. The dispose regression test exposed (via systematic-debugging) that the child must signal trap-armed readiness before the test cancels — a bare timeout raced the trap install and the default SIGTERM handler killed the child, making the guard a no-op. The mock now touches its ready file once the trap is in place and the test waits on that condition. The `cancelled` flag moved onto a holder object so TS control-flow doesn't narrow the catch-time read to always-false.
subagent/ — subagent capability family
The subagent seam: an agent delegating work to a child agent. Like the bash and llm families this is a capability seam (see capability seams) — but with one defining difference: multiple provider implementations coexist in one context, registered by name, rather than the single-implementation bash shape. The registry mirrors the LLM adapter registry.
| Package | Role | ctx key |
|---|---|---|
subagent/ |
Abstract subagent seam: named-provider registry + vocabulary | ctx.subagents |
subagent-spawn/ |
In-process backend: a fresh child agent (+ the shared run driver) | (registers on ctx.subagents) |
subagent-fork/ |
In-process backend: a child seeded with the parent's completed-turn prefix | (registers on ctx.subagents) |
subagent-acp/ |
Out-of-process backend: a child agent in a spawned subprocess, driven over ACP | (registers on ctx.subagents) |
tool-subagent/ |
Model-facing subagent delegation tool over ctx.subagents |
(registers on ctx.tools) |
The interface lives at subagent/subagent/. The in-process subagent-spawn / subagent-fork and the out-of-process subagent-acp backends ship here; the test-only dsh-subagent-mock (in support) is separate. All product packages except the mock.
The proposal and design rationale: docs/rfc/implemented/feature/2026-06-21-subagent-capability-seam.md.