The SDD pipeline
This page is the team member's guide to the spectacles spec-driven development (SDD) pipeline: how a plain GitHub issue becomes a merged implementation, and what a human does at each step.
The pipeline runs as a set of agentic GitHub Actions workflows. You operate it entirely through GitHub primitives you already use: opening an issue, applying a label, writing a comment, and reviewing and merging a pull request. There is no new tool to install and no separate task board.
The agents
| Agent | Turns | Into |
|---|---|---|
sdd-spec |
a tracking issue | full path: a structured spec, delivered as a PR. Single-PR (agile/fast) path: a stub or light spec PR plus a single execution-plan comment on the tracking issue (ADR 0012, ADR 0024) |
sdd-triage |
a merged spec | an architecture record, then a task graph of sub-issues |
sdd-dispatch |
/dispatch on a tracking issue, or a task sub-issue closing |
fan-out of ready tasks to sdd-execute variants, bounded by max-parallel (noop on fast-path issues) |
sdd-execute |
a ready task sub-issue, or a fast-path tracking issue on /approve |
an implementation PR with proof artifacts |
sdd-validate |
a phase-boundary artifact | advisory findings posted as a comment |
sdd-review |
an implementation PR | code-review comments on correctness, security, and spec compliance |
sdd-derive |
a pull request that shipped with no spec | a spec authored retrospectively from the code, delivered as a spec/<slug> documentation PR with a gap analysis (ADR 0027) |
Lifecycle labels on the tracking issue: sdd:spec, sdd:fastpath,
sdd:fastpath-review, sdd:triage, sdd:ready, sdd:in-progress,
sdd:review, sdd:done. The sdd:fastpath and sdd:fastpath-review
labels mark the fast-path states (ADR 0012); a fast-path tracking issue
never carries sdd:triage, sdd:ready, or sdd:review.
sdd-triage runs three phases under one workflow: architecture design, a
plan-comment proposal on the tracking issue, and — on /approve — the
creation of the Unit and task sub-issue tree (ADR 0010). A demoable unit that
groups two or more tasks becomes a Unit sub-issue; a unit that holds a single
task collapses to a task parented directly to the feature (Feature → task), so
no Unit sub-issue is created for it (ADR 0028). Structure is only created after
/approve: until then the plan is a proposal, not a tree.
sdd-dispatch is the cascade orchestrator. On /dispatch it computes the
ready set from the dependency graph and fans out to sdd-execute variants
in a bounded matrix; it then re-fires on every task close until the tree
is drained. Execution is fully event-driven (ADR 0011): there is no daily
cron.
sdd-spec has two modes: full-path (the default) and the single-PR
(agile/fast) path. On
intake it classifies the work against the single-PR criteria (ADR 0024,
widening ADR 0012): estimated net diff at or under SDD_AGILE_MAX
(default 800), no new external dependency, no schema/data-format
migration, no cross-cutting boundary change, no ADR-worthy decision.
When all pass, it
posts a proposal asking the human to comment /agile (or /fastpath)
to confirm or
/spec to keep the full flow. On confirmation it produces a
spec PR — a compressed stub for trivial work, a light spec (multiple
units, full R-IDs, optional Design notes) for anything larger — plus a
single execution-plan comment on the
tracking issue; one /approve then dispatches the implementation:
typed after the spec PR merges it dispatches directly, typed while the
spec PR is still open it records the sdd:approved marker — and, when
the consumer sets SDD_AUTO_MERGE, also arms squash auto-merge —
so the merge dispatches (merge and approve commute; with
SDD_AUTO_MERGE unset the human merges the spec PR by hand and the
merge still dispatches).
Two extra lifecycle labels —
sdd:fastpath and sdd:fastpath-review — mark the single-PR states.
/dispatch is a noop on a single-PR tracking issue (the fan-out is
unused).
End-to-end flow
The steps below trace one feature from idea to close. The lifecycle label on the tracking issue, listed in the right-hand column, tells you where the feature is at any moment.
The diagram traces that path end to end. Amber-bordered nodes are the steps a
human takes; blue-bordered nodes are automated agent runs; the red-bordered
node is a needs-human hand-off, which any agent can raise and only a human
clears. Dotted edges run
backward: a /revise comment sends a pull request back to its agent for
changes, and clearing needs-human resumes a stalled hand-off.
flowchart TD
classDef human stroke:#e0a82e,stroke-width:4px;
classDef agent stroke:#4493f8,stroke-width:4px;
classDef handoff stroke:#f85149,stroke-width:4px;
open([Human: open a feature or bug issue<br/>the template applies sdd:spec]):::human
subgraph s_spec [Tracking issue state: sdd:spec]
a_classify[sdd-spec classifies the issue<br/>full path: draft a spec<br/>single-PR path: post an /agile proposal]:::agent
a_spec[sdd-spec drafts the spec<br/>opens a spec sub-issue and a spec PR]:::agent
h_spec([Human: review and merge the spec PR<br/>the spec sub-issue closes on merge]):::human
end
subgraph s_fast [Single-PR path: sdd:fastpath then sdd:fastpath-review]
h_fast([Human: comment /agile or /fastpath]):::human
a_fast[sdd-spec: stub or light spec PR + execution plan comment]:::agent
h_fast_merge([Human: review and merge the spec PR<br/>or /approve first: sdd:approved arms auto-merge<br/>and the merge dispatches - either order works]):::human
h_approve_fast([Human: comment /approve]):::human
end
subgraph s_triage [Tracking issue state: sdd:triage]
h_triage([Human: comment /triage]):::human
a_arch[sdd-triage phase A: maps the code, builds the<br/>assumption ledger, opens an architecture sub-issue and PR]:::agent
h_arch([Human: review and merge the architecture PR<br/>the architecture sub-issue closes on merge]):::human
a_spikewave[sdd-triage phase A step 4a: one kind:spike<br/>sub-issue per needs-spike assumption]:::agent
a_actuator[sdd-spike-actuator: posts /execute<br/>on each kind:spike sub-issue]:::agent
a_spikedoc[sdd-execute: writes docs/spikes/<date>-<slug>.md<br/>opens a spike doc PR]:::agent
a_spikeval[sdd-validate spike boundary:<br/>proved, disproved, or partial]:::agent
a_reentry[sdd-spike-reentry: when the wave drains to<br/>zero open spikes, re-enters phase B]:::agent
a_units[sdd-triage phase B: posts the proposed plan<br/>as one comment on the tracking issue]:::agent
h_approve([Human: comment /approve]):::human
end
subgraph s_ready [Tracking issue state: sdd:ready]
a_tasks[sdd-triage phase C: creates Unit sub-issues for multi-task units<br/>and task sub-issues, collapsing a single-task unit to a feature-parented task;<br/>labels unblocked tasks sdd:ready]:::agent
a_cycle[sdd-cycle-detect: deterministic DAG backstop<br/>parks needs-human on a cycle the LLM missed]:::agent
end
subgraph s_dispatch [Tracking issue state: sdd:ready, awaiting /dispatch]
h_dispatch([Human: comment /dispatch on the tracking issue<br/>or automatic with SDD_AUTO_DISPATCH=1]):::human
a_dispatch[sdd-dispatch computes the ready set and fans out to<br/>sdd-execute variants, bounded by max-parallel<br/>re-fires on every task close until the tree drains]:::agent
end
subgraph s_progress [Tracking issue state: sdd:in-progress, sdd:dispatched]
a_exec[sdd-execute implements a ready task, opens an<br/>implementation PR with proof artifacts<br/>runs on workflow_dispatch from sdd-dispatch, or on /execute]:::agent
end
subgraph s_review [Tracking issue state: sdd:review]
a_check[sdd-validate posts advisory findings<br/>sdd-review posts code-review comments]:::agent
h_merge([Human: review and merge the implementation PR<br/>the task sub-issue closes on merge]):::human
end
subgraph s_done [Tracking issue state: sdd:done]
a_done[Every task closes its Unit sub-issue; once all<br/>Units close, sdd-execute sets sdd:done + needs-human]:::agent
h_close([Human: final review, then close the tracking issue]):::human
end
open --> a_classify
a_classify -->|full path| a_spec --> h_spec --> h_triage --> a_arch --> h_arch
a_classify -->|single-PR proposal| h_fast
h_fast --> a_fast --> h_fast_merge --> h_approve_fast --> a_exec
h_fast_merge -->|approved before merge: merge dispatches| a_exec
h_arch -->|needs-spike residue: pre-plan spike wave| a_spikewave
a_spikewave --> a_actuator --> a_spikedoc --> a_spikeval
a_spikeval -->|proved: sdd:spike-resolved on the spike sub-issue| a_reentry
a_spikeval -.->|disproved or partial: park the tracking issue| hand
a_reentry --> a_units
h_arch -->|ledger all settled: no wave| a_units
a_units --> h_approve --> a_tasks --> a_cycle --> h_dispatch
a_cycle -.->|cycle the LLM missed: park needs-human| hand
h_dispatch --> a_dispatch --> a_exec --> a_check --> h_merge
h_merge -->|tasks remain, full path| a_dispatch
h_merge -->|last task done, or fast path| a_done --> h_close
hand[/"Any agent, on low confidence or a blocker:<br/>posts questions, applies needs-human, stops"/]:::handoff
ans([Human: answer the questions, remove needs-human<br/>the agent re-runs and resumes]):::human
a_classify -.-> hand
a_spec -.-> hand
a_fast -.-> hand
a_arch -.-> hand
a_tasks -.-> hand
a_exec -.-> hand
hand --> ans
h_spec -.->|changes needed? comment /revise| a_spec
h_arch -.->|changes needed? comment /revise| a_arch
h_merge -.->|changes needed? /revise or a review comment| a_exec
| Step | Who acts | What happens | Lifecycle label |
|---|---|---|---|
| 1. Open the issue | you | Open an issue from the feature or bug template. The template applies sdd:spec, which triggers sdd-spec. |
sdd:spec |
| 2. Review the spec PR | you | sdd-spec drafts a spec and opens it as a PR. Read it, comment, and merge when it is right. Merging advances the pipeline. |
sdd:spec |
| 3. Start triage | you | Comment /triage on the tracking issue. sdd-triage phase A maps the code and opens an architecture PR. |
sdd:triage |
| 4. Review the architecture PR | you | Read the architecture record, comment, and merge it. Merging triggers phase B. | sdd:triage |
| 5. Approve the plan | you | sdd-triage posts the proposed plan as a comment on the tracking issue. Comment /approve to materialize it, or /revise <note> to amend. |
sdd:triage |
| 6. Tree is created | sdd-triage |
Phase C creates Unit sub-issues and sub-task issues together, each with its scope, proof artifacts, and a model:* tier label. |
sdd:ready |
| 6a. Dispatch the plan | you | Comment /dispatch on the tracking issue — or set SDD_AUTO_DISPATCH=1 and phase C completion arms the cascade automatically (ADR 0025; /dispatch stays the manual command and, via sdd:dispatched, the pause/resume control). sdd-dispatch arms the cascade: it computes the ready set from the dependency graph, fans out sdd-execute runs in a bounded matrix (SDD_DISPATCH_MAX_PARALLEL, default 5), and re-fires on every task close until the tree is drained. |
sdd:in-progress |
| 7. Tasks are implemented | sdd-execute |
Each dispatched task: sdd-execute picks it up via workflow_dispatch from the cascade and opens an implementation PR. A human may also comment /execute on a task to run it immediately, outside the cascade. |
sdd:in-progress |
| 8. Validation runs | sdd-validate |
At each phase boundary, sdd-validate posts advisory findings as a comment. A clean implementation pass moves the issue to sdd:review. |
sdd:review |
| 9. Code review runs | sdd-review |
sdd-review posts review comments on the implementation PR. You read them and decide. |
sdd:review |
| 10. Merge and close | you | Merge the implementation PRs. When every task sub-issue is closed, the issue moves to sdd:done and needs-human is applied for your final review and close. |
sdd:done |
Structure is only created after /approve. Until then the plan lives as a
single comment on the tracking issue, so a /revise <note> is cheap:
sdd-triage re-posts the plan with the note applied and there is no tree
to garbage-collect. ADR 0010 records the gate semantics.
Single-PR (agile) path steps
The single-PR flow (ADR 0012, generalized by ADR 0024) compresses
spec, architecture, and plan
into one agent run for work that fits in one implementation PR. The
steps below run in
place of the full table above when the tracking issue's lifecycle
forks off to sdd:fastpath after /agile or /fastpath.
| Step | Who acts | What happens | Lifecycle label |
|---|---|---|---|
| 1. Open the issue | you | Open from the feature or bug template (same as the full path). |
sdd:spec |
| 2. Classify | sdd-spec |
The agent reads the issue, checks the single-PR criteria (estimated diff ≤ SDD_AGILE_MAX, no new external dependency, no schema/data-format migration, no cross-cutting boundary change, no ADR-worthy decision), and posts one proposal comment asking for /agile (or /fastpath) or /spec (full flow). Silence means the full flow runs. |
sdd:spec |
| 3. Confirm the single-PR path | you | Comment /agile (or /fastpath) on the tracking issue. The wrapper moves the lifecycle to sdd:fastpath and re-invokes sdd-spec. |
sdd:fastpath |
| 4. Author the spec | sdd-spec |
One run produces a spec PR — a stub (problem statement, R-IDs, proof artifacts, one Unit) for trivial work, a light spec (multiple units, full R-IDs, 1–3 proof artifacts per unit, optional Design notes) otherwise — and an execution plan comment on the tracking issue naming one task that spans the feature. | sdd:fastpath-review |
| 5. Merge the spec PR | you | Review and merge. The spec sub-issue closes via the existing Closes keyword. Or comment /approve first: the approval is recorded as the sdd:approved marker, squash auto-merge is armed (with SDD_AUTO_MERGE), and the merge dispatches — merge and approve commute (ADR 0024). |
sdd:fastpath |
| 6. Approve and dispatch | you | Comment /approve on the tracking issue (skip if you approved in step 5 — the merge already dispatched). The sdd-spec wrapper finds the plan comment, parses the model:* tier, and dispatches one sdd-execute-{tier} against the plan. No Unit or task sub-issues are created. |
sdd:in-progress |
| 7. Implementation runs | sdd-execute |
The variant opens one implementation PR with proof artifacts. sdd-validate and sdd-review run as on the full path; the absence of an architecture record and a sub-task tree is not a finding. |
sdd:in-progress |
| 8. Merge and close | you | Merge the implementation PR. sdd-execute moves the tracking issue to sdd:done and applies needs-human for your final close. |
sdd:done |
/dispatch on a single-PR tracking issue is a noop with a one-comment
explanation pointing to /approve. A /revise <note> between the
execution plan comment and the dispatch edits the plan in place (a new
plan comment is posted; the prior one is hidden as OUTDATED). A
/revise after an early /approve clears the sdd:approved marker —
the plan changed, so re-approval is required — and a spec PR closed
without merging clears it too.
If during execution sdd-execute finds the work is materially bigger
than the classification assumed, it posts one comment naming the
mismatch and
applies needs-human. Your recourse is the standard needs-human
flow: answer in a comment and either tighten the scope (the executor
resumes) or comment /spec to bounce the issue into the full
pipeline (sdd:fastpath becomes sdd:spec; the existing spec is the
starting point of a fuller spec).
Retrospective specs
The forward pipeline assumes a spec exists before code. Code explored directly
on a feature branch — opened with no tracking issue — ships without one.
sdd-derive (ADR 0027) is the reverse path: it reads a pull request and authors
a spec from the implemented code.
There are two ways in. When a pull request opens or updates without SDD lineage
(no sdd/ head branch, no Closes link) and its diff is over the
SDD_SPEC_MIN_UNIT floor (default 400), a deterministic check posts one offer
comment and a needs-spec marker. Comment /derive-spec on that pull request
to take the offer; ignore it to defer. Separately, the sdd-unspecced-scan
workflow runs weekly and upserts one roll-up issue listing every unspecced
merged pull request; a maintainer comments /derive-spec #12 #34 there to
derive a set at once.
Either way, sdd-derive opens a separate spec/<slug> documentation pull
request adding the spec under docs/specs/, and comments the link on the source
pull request. The derived spec carries a Gap Analysis section recording what
the code did not do — implementation gaps, missing failure paths, weak
acceptance criteria, and skipped demoable units. Those gaps stay in the spec for
a human to triage; sdd-derive opens no follow-up issues. A derived spec's
tracking-issue is blank, so sdd-doc-status leaves it at planned until a
human links one.
Planning hardening
Two backstops keep the plan honest before and after /approve. Both run inside
the sdd:triage phase and need no extra human action in the normal case.
The pre-plan spike wave. While sdd-triage phase A designs the
architecture, it builds an assumption ledger in the architecture record: one
row per load-bearing assumption the chosen approach rests on. An assumption that
is load-bearing and not settleable from the repository working tree nor from
prior precedent is the residue — it is marked needs-spike. Phase A step 4a
then materializes one kind:spike sub-issue per needs-spike row, each a direct
child of the tracking issue. The sdd-spike-actuator wrapper posts /execute on
each spike, sdd-execute writes a docs/spikes/<date>-<slug>.md finding, and
sdd-validate resolves the outcome: a proved spike gains
sdd:spike-resolved; a disproved or partial spike parks the tracking issue
at needs-human so a human decides how the plan adapts. Phase B holds the plan
comment while any spike is open; the sdd-spike-reentry wrapper re-enters
phase B once the wave drains to zero open spikes, folding each resolved spike's
finding into the plan as settled ground. The spike wave is the one
materialization phase A performs — ADR 0010's all-or-nothing guarantee is scoped
to the main Unit/task tree, and the spike wave is the carved-out exception. See
Spikes for the full primitive.
The cycle-detect backstop. Phase B's plan composition runs a latent-edge
pass: a task whose proof artifacts consume an artifact a sibling task produces
is dependent on that producer even when no blocked by line was written, so the
implied edge is added to the plan and materialized verbatim by phase C. The
agent also checks the implied dependency graph for cycles before it posts. As a
deterministic backstop for a cycle the LLM misses, the sdd-triage wrapper runs
an sdd-cycle-detect composite-action job after phase-C materialization: it
walks the Feature → Unit → task sub-issues — including a task parented directly
to the feature when its Unit collapsed to one task (ADR 0028) — and if it finds
a real cycle (or a
blocked by reference it cannot resolve in the tree) it parks the tracking issue
at needs-human with a comment naming the cycle. The agent's in-prompt check is
the primary guarantee; this job is the authoritative backstop.
What a human does
Across the whole pipeline a human takes only four kinds of action:
- Open an issue from the
featureorbugtemplate to start a feature. When you already have a Claude plan document, open from the Specification (from Claude plan) template (spec.md) and paste the plan into the body instead. That template applies aplan:providedmarker, which puts the pipeline into translation mode:sdd-spectranslates the plan into a structured spec rather than authoring one from a slim description, andsdd-triagetranslates the plan's architecture section into the architecture record. The marker clears once the architecture PR opens (or, on the fast path, the stub spec PR). - Comment a command to steer:
/spec,/fastpath(or its alias/agile),/triage,/approve,/dispatch,/revise, or/execute. See the command table inshared/sdd-interaction.md. - Review and merge PRs. Merging a PR is the approval signal that advances the pipeline. No agent merges a PR; merge authority stays with humans and consumer CI.
- Answer
needs-human. When an agent cannot safely proceed, it applies theneeds-humanlabel and posts one comment with the blocker. Answer in a comment and clear the label; the agent re-reads the thread and resumes. See ADR 0001 (decisions/0001-needs-human.mdin the repository root).
Giving feedback on a pull request
Every pull request the pipeline opens can be sent back for changes instead of merged. Feedback never opens a second pull request; the owning agent updates the existing one.
- A spec PR or an architecture PR. Comment
/revise <note>on the pull request. The owning agent —sdd-specorsdd-triage— re-runs with the note as an added instruction and updates the same pull request. Repeat until it is right, then merge. - An implementation PR. Leave an inline review comment on the diff, or
comment
/revise <note>on the pull request.sdd-executepushes follow-up commits to the same branch addressing it. A comment that needs a human decision is escalated throughneeds-humaninstead.
Where state lives
- The spec and the architecture record are committed files, reviewed and merged as PRs and rendered into this docs site.
- Tasks are GitHub sub-issues linked to the tracking issue.
- Lifecycle is a single
sdd:*label on the tracking issue. - Every human decision point is an issue or PR comment.
Validation is advisory by design. sdd-validate posts findings and escalates
blockers through needs-human, but it is never a required status check and
never blocks a merge. Human review plus consumer CI is the only merge gate.
Verification
- Open a test issue from the
featuretemplate and confirm it carries both thekind:featureandsdd:speclabels. - Confirm
templates/.github/labels.ymldefines all eightsdd:*lifecycle labels, thesdd:dispatched,sdd:approved,plan:provided, andsdd:spike-resolvedmarkers, thekind:spikelabel, and all threemodel:*tier labels. - Confirm
shared/sdd-interaction.mdstates the lifecycle state machine, the command table, and theneeds-humancontract, and referencesdecisions/0001-needs-human.md.