Redesign os-sdlc pipeline advancement around SubagentStart/SubagentStop hooks #425
Labels
No labels
P0
P1
P2
P3
bug
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
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#425
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?
Context
The implement pipeline advances via a PostToolUse hook on the Agent tool,
which requires synchronous dispatch (ADR-0121). In harnesses that force
async Agent dispatch, PostToolUse fires at launch with status:
async_launched, so the guard rejects every dispatch and the pipeline stalls
— hit live in a test session 2026-08-18. Experiment verified that
SubagentStop fires at the agent's real completion time for background-
launched agents, with agent_type, agent_id, agent_transcript_path, and
last_assistant_message in the payload (vault: reference/claude-code-
subagent-hooks-fire-for-background-agents.md). This redesign moves
completion signaling to SubagentStop and supersedes ADR-0121.
Design (agreed 2026-08-18):
• Hooks are webhook-thin and data-only — they call a single runner endpoint
with payload data, never perform work themselves. Matched to ^os-sdlc:.*$ on
both SubagentStart and SubagentStop.
• SubagentStart: records the dispatch (agent metadata, relations) and
fetches the pending handoff for that agent_type as the agent's intake
context.
• SubagentStop: posts payload metadata + created handoff to the runner
endpoint; runner records, runs post-agent steps (test suite, lint on diffed
files), and sets state.
• PostToolUse on Agent becomes a state-aware ack/next-instruction query:
async launch → "dispatched, await notification"; state shows a completed
step → routing instructions.
• Orchestrator reads only runner instructions between dispatches; an escape-
hatch investigation skill (knows expected states, queries the db for
evidence) covers breakdowns.
• Data model (simplified 2026-08-18): sessions → implementations
(created_at/completed_at/abandoned_at; state DERIVED from timestamps — both
null = active, completed_at set = completed, abandoned_at set = abandoned;
current implementation = session's latest row; runner stamps abandoned_at on
the prior unfinished round when opening a new one, and stamps completed_at
when sending final instructions) → dispatches (implementation_id, agent_id,
received_handoff_id, created_handoff_id, created_at, completed_at stamped by
SubagentStop). Agents table separate from dispatches (1:1 in practice under
the fresh-dispatch rule; a revisit is a new dispatch row with the same
agent_id). Handoffs are a noun-only table; NO handoff_transfers join table —
direction lives on the two dispatch FKs. Full design: .
sdlc/tickets/425/design.md
Tasks
[ ] Superseding ADR for ADR-0121 recording the SubagentStop-based
advancement design
[ ] Runner endpoints: dispatch-record + handoff-fetch (SubagentStart),
completion intake (SubagentStop), round start (runner start), state-aware
next-instruction query (PostToolUse)
[ ] Schema: implementations/dispatches/agents/handoffs/handoff_transfers per
the model above
[ ] SubagentStart + SubagentStop hooks with ^os-sdlc:.*$ matchers,
registered in the plugin manifest
[ ] Rework PostToolUse hook into the ack/query role (drop the async_launched
rejection)
[ ] Escape-hatch investigation skill
[ ] Confirm SubagentStop fires in the async-forcing harness on the first
real run (assumed working; adjust only if it becomes a pain point)
Acceptance criteria
[ ] A ticket runs end-to-end through the implement pipeline in a harness
where every Agent call launches async
[ ] No pipeline state advances at agent launch time
[ ] Orchestrator receives next-step instructions without reading any
pipeline file other than runner output
Origin
• Trigger: /os-sdlc:implement test run in a Fable-5 harness session
(fc87bc6b), stuck at code-probe with repeated async_launched rejections
• Improvised this session: none (manual synthetic-event piping identified as
interim unstick, not applied here)
• Chain: pipeline stall ← PostToolUse-on-Agent fires at launch under async
dispatch ← sync-only advancement design — DESIGN (ADR-0121)
• Root candidate: this ticket
Spec (published 2026-08-18; supplements the capture sections above — design detail lives in .sdlc/tickets/425/design.md)
Problem Statement
When I run /os-sdlc:implement in a harness that launches every Agent call asynchronously, the pipeline stalls permanently: the PostToolUse hook fires at agent launch with status async_launched, the ADR-0121 guard rejects it, and no advancement path remains. Even in sync harnesses, advancement depends on a launch-time hook standing in for a completion signal, and the recovery machinery (resume/recover/reemit/rollback) exists mostly to escape stuck stored state.
Solution
Advance the pipeline on the agent's real completion signal (SubagentStop, verified to fire at true completion for background agents) instead of the Agent tool call return. Hooks become webhook-thin data couriers into runner CLI endpoints; the runner owns all state, post-agent steps (tests, lint), and routing; state is derived from timestamps so ambiguous in-flight state is structurally impossible; the orchestrator reads only runner instructions, with an escape-hatch skill for investigating divergence.
User Stories
Implementation Decisions
Testing Decisions
Out of Scope
Further Notes
Work started 2026-08-18: design phase. Design doc drafted at .sdlc/tickets/425/design.md (SubagentStop-based advancement, webhook-thin hooks, timestamp-derived implementation state). Implementation follows after design review.
Alignment investigation done 2026-08-18 (two ast-grep sweeps over plugins/os-sdlc); full findings in .sdlc/tickets/425/design.md § 'Alignment with the current implementation'. Highlights: storage is already SQLite/Sequel (runner/db.rb) so the schema is a migration, not a new stack; runner endpoints land as new Thor commands on runner/cli.rb; handoffs table replaces BriefAssembler's brief files; lint moves from the decoupled lint_changed.rb hook into a runner-owned SubagentStop step; dispatch_action needs a query/command split for an idempotent instruction endpoint. Obsoleted: the ADR-0121 async guard + advancement path in post_tool_use.rb, the run_in_background:false mandate in skills/implement/SKILL.md, tickets.state column-based in-flight logic, and most of the recovery cluster (resumer/rollback/reemit/recovery_status, ~10K over 5 files) plus ~10 test files. Net est. -200 to -600 LOC.
Decomposed into implementation tickets: #426–#432 (linear chain #426→#427→#428→#429→#430→#431; #432 parallel off #427).
Resolution
Done: SubagentStop-driven runner rebuild complete via child tickets #426–#433, all closed with three-part resolutions: ADR-0128 supersedes ADR-0121 (#426); schema migration + round-open/next idempotent instruction query (#427); SubagentStart endpoint + webhook-thin hook with handoff intake (#428); SubagentStop completion intake with gates, step results, advancement (#429); PostToolUse async ack + pull-instruction implement skill (#430); recovery cluster retired, −867 LOC (#431); pipeline-state-investigate skill (#432); assembly sweep, live-schema fix, and end-to-end dogfood (#433). Async-forcing harnesses no longer stall: launch receipts ack, real completion advances the pipeline via lifecycle timestamps.
Evidence: commits
f8ba73e,237d16c,63d0fbf,ea8cdfc,8050c83,28384cc,61aa3a3,1bb6700,b9a0b99on main; final suite 276 runs/594 assertions 0 failures; dogfood run verified all four pipeline-point states per the investigation skill's contractFollow-ups: carried on #433's close: live-session hook observation (monitor, no ticket) and the legacy ticket-pipeline retirement question (deliberate deferral pending direction); nothing else outstanding from this map