SubagentStart records a phantom dispatch when a subagent resumes after its own background work #773
Labels
No labels
P0
P1
P2
P3
bug
category/code-change
category/cross-repo
category/eval-run
category/investigation
category/no-test-target
category/owner-ruling
category/prose-artifact
category/recurring-chore
category/structure
category/umbrella
crc-refactor
create
delete
enhancement
filed-by/agent
filed-by/user
frozen
lint-rule
needs-info
needs-triage
next
plugin/cc-architect
plugin/os
plugin/os-adr
plugin/os-aidd-lint
plugin/os-backlog
plugin/os-context
plugin/os-doc-hygiene
plugin/os-sdlc
plugin/os-vault
project/cc-os
ready-for-agent
ready-for-human
recurring
review
rule-proposal
update
waiting
wayfinder:grilling
wayfinder:map
wayfinder:map
wayfinder:research
wayfinder:task
wayfinder:task
wontfix
worklist/deviations
worklist/lint-rule
worklist/new-implement-build
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
jared/cc-os#773
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
The SubagentStart hook records a new dispatch each time it fires.
A subagent that stops with its own background work still running resumes when that work completes, and Claude Code fires SubagentStart again for the resume.
The Runner then holds a second open dispatch for an agent that already stopped and settled.
os-sdlc-nextreturnsawait_completionfor 900 seconds until StallWatch marks the phantom dispatch stalled, and the real pipeline waits that long.Observed
On 2026-09-27, implementation 50 (#728) dispatched programmer 515, which stopped at 10:52:21 with verdict pass and background work still running.
Contract-auditor 518 started at 10:53:50.
At 10:55:18 a programmer dispatch 519 appeared with no brief, no handoff, and no gate steps; its
agents.agent_idis the same agent ID as dispatch 515.After 518 settled with verdict fail at 10:56:31, the Runner did not route it.
Rows from
.sdlc/pipeline.db:Reproduce
Observed: a second programmer dispatch with the same agent ID and
await_completionfor 900 seconds. Expected: no new dispatch for an agent ID that already settled a dispatch in this implementation.Approach
The SubagentStart handler already receives the agent ID.
Before it creates a dispatch, it looks up an existing
agentsrow with the sameagent_idin the same implementation that has a completed dispatch.When it finds one, it records nothing and exits 0.
Required work:
Rejected: shortening
StallWatch::STALL_AFTER_SECONDS— it treats the symptom, and a live slow agent would then stall falsely.Acceptance criteria
os-sdlc-nextroutes the last completed dispatch.Owner sketch
Origin
os-sdlc-next 17688436-99ef-4a59-9eec-74e4f87e9c1fduring /os-sdlc:implement 728, 2026-09-27await_completion← open dispatch 519 with no brief ← SubagentStart fired on subagent resume ← DESIGN (plugins/os-sdlc/lib/os_sdlc/command/start_subagent.rbcreates a dispatch on every SubagentStart; #766 closed 2026-09-27 ruled the prompt pipe authoritative for stop, but start has no resume guard)VERDICT: CREATE
REASON: Complete-process, database-evidenced defect (dispatch 519 phantom, 15-min stall) in a real pipeline run, with root traced to DESIGN in start_subagent.rb, distinct from #766 stop-side scope; no upstream mechanism already covers a resume guard.
Occurrence: Keep a dispatch from binding to a finished agent
Seen: os-sdlc Runner dispatch binding, SubagentStart/stop handling, os-sdlc-next
Run: implementation 250 (#1616), session 9d8c2e15-00c1-4dec-b177-e749fa05ec67
Adds: A double notification from the test-writer bound dispatch 2384 to finished agent a5044b6c89ae3d206. os-sdlc-next returned await_completion with no end, the stalled result never fired, and the run needed a hand-finish. In the same run, test-reviewer dispatch 2383 failed new tests for being green in a red round, although they were correct regression pins. The map has no path for that case.
Occurrence: SubagentStart hook records a resumed non-pipeline agent as a dispatch of the open implementation
Seen: plugins/os-sdlc/lib/os_sdlc/dispatch/admission.rb (SubagentStart admission)
Run: Session 87bc217f-61e2-4d4d-8470-9f3c8fd1ef43, implementation 267 (#1642), dispatch 2615
Adds: the resumed agent was an os-sdlc:blast-radius agent launched before implementation-open (not a map agent, never calls os-sdlc-subagent-stop). After dispatch 2614 (test-reviewer pass) the hook recorded the resume as dispatch 2615 agent_type blast-radius; os-sdlc-next returned await_completion until StallWatch (900 s). The guard must also refuse an agent type that is not the current node's agent.
Occurrence addendum (implementation 267): the stall directive re-dispatched test-reviewer although dispatch 2614 had already passed it; the re-dispatch (2616) returned verdict error and the run ended implementation_failed. So the phantom dispatch does not only cost 900 s: it can fail the run.