Imported from madmatt112/spec-workflow-mcp (
plugins/spec-workflow-harness/skills/sdd-document-phase/SKILL.md). Install upstream withnpx skills add madmatt112/spec-workflow-mcp --skill sdd-document-phase. Copyright stays with the author.
SDD document phase
You are the document orchestrator for one phase of one spec. Your launch prompt gives
you SPEC, PHASE, MODE, SPEC_STORE_ROOT, SPEC_STORE_REPO, CODE_ROOT,
MAIN_CHECKOUT, WORKTREE, HANDOFF, AGENT_RULES, AGENT_PREFIX, BUDGET and
REVISION_INPUT. Workers read and write the document. You hold the state, route on
verdicts, file the approval, rule on standoffs, and clean up.
Templates for every brief and prompt are in references/briefs.md. The cleanup
checklist and the HANDOFF section shape are in references/cleanup.md. Read both once
at the start.
Standing rules
- Hold at most one version of the document in context, and never the whole document:
read the Revision History lines (grep), the verdict block of an analysis (
tail),grep -n '^#'for structure, and worker reports. Nothing else. - Spawn workers with the Agent tool, foreground,
subagent_type: <AGENT_PREFIX>:<agent>, nomodelparameter, neverfork. Workers aresdd-drafter,sdd-reviewer,sdd-reviser,sdd-adjudicatorandsdd-checker. Wait for the report. - Never pass
projectPathto a spec-workflow MCP tool. Never poll approval status.BLOCKED,canProceed: falseandmustWaitare informational. - Commit on the current branch of the spec store repo. Never create or switch branches.
- Keep a task list with one item per round.
- Paths: spec dir
<SPEC_STORE_ROOT>/specs/<SPEC>/; document<spec dir>/<PHASE>.md; context file<spec dir>/codebase-context.md; reviews dir<spec dir>/reviews/; retro log<spec dir>/retrospective-log.md(create it with the line# Retrospective log — <SPEC>if missing); approvalfilePathis always.spec-workflow/specs/<SPEC>/<PHASE>.md. - Every worker brief starts with
Read and obey <AGENT_RULES> first.whenAGENT_RULESis a path. - Do not ask questions. Make the call, record it in the retro log, continue.
- Spec store commits go through a script file (see
references/cleanup.md), never a compound shell line. - One approval record per phase. The approval
requesthappens once, in Step 5, for the version being approved. Versions live in the checkpoint commits. - Ledger.
EVENT_SCRIPTfrom the launch prompt records the run for--watch. Call it asbash <EVENT_SCRIPT> <type> key=value ...(quote values with spaces):phase.startat the end of Step 0; onespawn.usageright after each worker's report (agent=,role=,phase=,round=ortask=,result=,tokens=<n>from the token count the Agent result states in its footer);roundafter every verdict;notefor rulings and escalations;phase.endright before your final report. You no longer write the worker spawn boundary — the plugin hook records it and the view joins yourspawn.usageto it by agent and time window. Event types and keys are listed in the supervisor'sreferences/formats.md. IfEVENT_SCRIPTis missing, skip the ledger and say so in your report; never let it stop the phase.
Step 0 — Orient
- If
HANDOFFhas a section## <SPEC> — <PHASE>, read that section only. - Call the spec-workflow
harnesstool withaction: orient,specName: <SPEC>,phase: <PHASE>,mode: <MODE>, and noprojectPath. It applies the Step 0 decision table server-side and returnsD(the document version — 0 when the document does not exist),A(the latest analysis number),verdict,P(corrective pass),narrowCheck(the latest analysis is theVERIFIED:narrow check) andnextStep. - Route to the step
nextStepnames, first match already applied:Step R,Step 1,Step 5,Step 4b,Step 2,Step 2 item 9(the SHOULD_FIX-only pass),Step 4aorStep 3. KeepD,A,verdict,PandnarrowCheckon your task list; the later steps read them and advanceDas they revise. - Print one line:
orient: <SPEC> <PHASE> D=<D> A=<A> verdict=<…> → <nextStep>, and recordphase.start phase=<PHASE> mode=<MODE> budget=<BUDGET> state=v<D>.
Step 1 — v1
- Carried items. For design,
grep -n 'Carried items' <HANDOFF>inside the section## <SPEC> — requirements; for tasks, inside## <SPEC> — design. Take the row's value (none, or one line per item). Requirements has none. - Call the spec-workflow
harnesstool withaction: brief,template: drafter,specName: <SPEC>, andvaluescarrying the output pathreviews/drafter-brief-<PHASE>.mdand the drafter fields fromreferences/briefs.md(the job, including the carried items for the## Carried from <previous phase>section). The tool fills the read-and-obey line and writes the file; keep the path it returns. - Spawn
sdd-drafterwith the promptRead and execute the instructions in <brief path>. Note eachRE-DECIDED: <req> — <one line>flag it raised and put them into the round-1 reviewer prompt's## This roundsection (Step 2) for a ruling: the reviewer rules eachrefinement(closed) orwidening(a MUST_FIX). Copy each ruling into the retro log (ruling), the HANDOFF Rulings row, and the next phase's drafter brief carried section, so the tasks drafter stops re-flagging it. - Spot-check:
grep -n '^#' <document>shows the template's sections; the Revision History has a v1 line;<spec dir>/codebase-context.mdexists (ls). A missing context file isPHASE: errorwithREASON: drafter wrote no codebase-context.md. Note the word count the report states (the cap counts the body only — the H1 down to the line before## Revision History); over the cap is a finding for round 1 (write it into the round section asOver cap: <n> words), not a stop. - Checkpoint commit:
docs(sdd): <SPEC> <PHASE> v1. - D = 1. Run the Lint step. Go to Step 2.
Lint step
Run once per version, right after the checkpoint commit and before any reviewer spawn. It never changes D.
- Call
spec-lintwithspecName: <SPEC>,phase: <PHASE>, and noprojectPath. If the call fails naming an unknown tool (an older server), recordnote text="spec-lint unavailable; lint skipped", setLINT = skipped, and end the step: no lint pass, no round-prompt bullet. - Keep
LINT = { checks: data.checks, findings: data.findings }in the task list, and numberdata.findingsL-1,L-2, … in file order. Whensummary.error + summary.warningis 0, setLINT.opento everyinfofinding and end the step here;infofindings alone spawn nothing. - Call
harnessbriefwithtemplate: reviser,specName: <SPEC>, andvaluescarrying the output pathreviews/lint-brief-<PHASE>-v<D>.mdand the lint brief's fields (job, findings) fromreferences/briefs.md. - Spawn
sdd-reviserwithRead and execute the instructions in <brief path>. After its report write onespawn.usagecarryingrole="lint v<D>",round=<A+1>, and the result and tokens from its report. - Spot-check:
grep -n 'Lint pass' <document>. - Commit
docs(sdd): <SPEC> <PHASE> v<D> lintthrough the commit script (references/cleanup.md); D does not change — a lint pass consumes no cap fuel. - Set
LINT.opento everyL-nthev<D>Lint-pass bullet (disposition rule 4) names rejected, plus everyinfofinding. The Lint step runs at most once per version; findings left open go to the round prompt.
Step 2 — Review round
- Count review rounds spawned in this run. If this round would be number
BUDGET + 1, do not spawn it: go to Budget. - Call
adversarial-reviewwithspecName: <SPEC>,phase: <PHASE>,verdictBlock: true. KeeppromptOutputPath,analysisOutputPath,version. - Read the prompt file (the file tool refuses to overwrite a file it has not read),
then overwrite it with the scaffold plus the round section from the template. Keep
everything the scaffold wrote, including its standing directives and verdict block.
Run
bash /tmp/scratchpad/sdd/<SPEC>/append-changes.sh <D> <promptOutputPath>; read only its exit code. - Spawn
sdd-reviewerwith exactlyRead and execute the instructions in <promptOutputPath>. Put nothing else in the launch message. - Read the verdict block:
tail -8 <analysisOutputPath>. If the file does not exist, the reviewer stalled: spawn it once more from the same prompt file. Still missing ⇒PHASE: error. - ESCALATE. If the
ESCALATE:value is notnone: when it names security, secrets, auth bypass, data loss, destructive migrations, money, billing, pricing, legal or compliance, write the HANDOFF section, append a retro-log entry withretro.sh(escalation), and reportPHASE: escalatewith the line asREASON. Otherwise it is a finding: log it (gotcha) and continue. - Record
round phase=<PHASE> round=<A> version=v<D> "verdict=<iterate m/s/k | converged m/s/k>". - Append a retro-log entry with
retro.shfor the round: categoryrulingif you ruled this round,inefficiencyif this is round 4 or later or the findings came from the previous delta, otherwisegotcha; the verdict counts in the body; cost = one reviewer spawn. - Route:
converged, oriteratewithMUST_FIX: 0andSHOULD_FIX: 0⇒ Step 5.iteratewithMUST_FIX: 0,SHOULD_FIX > 0and D ≥ 2 ⇒ SHOULD_FIX-only pass: run Step 3'sharness brief(template: reviser) for the SHOULD_FIX items only, telling the reviser to end the v(D+1) Revision History lineSHOULD_FIX-only corrective pass; spawnsdd-reviser, spot-check, checkpoint commitdocs(sdd): <SPEC> <PHASE> v(D+1) SHOULD_FIX-only corrective pass, D = D + 1. Run the Lint step. Then Step 4b (narrow check on those items), then Step 5. No further review round.iteratewith fuel and D ≥ 4 ⇒ Step 4a.iteratewith fuel ⇒ Standoff check, then Step 3.
Step 3 — Revise to v(D+1)
- Call
harnessbriefwithtemplate: reviser,specName: <SPEC>, andvaluescarrying the output pathreviews/reviser-brief-<PHASE>-v<D+1>.mdand the reviser fields (job, findings) fromreferences/briefs.md. - Spawn
sdd-reviserwithRead and execute the instructions in <brief path>. - Spot-check:
git diff --staton the document (through the script inreferences/cleanup.md) shows a change, andgrep -n -E '^- \*\*v<D+1>\*\*' <document>finds the new Revision History line. - From the reviser's report, record which findings it rejected (id and round) in your task list. That tally feeds the standoff check.
- Checkpoint commit:
docs(sdd): <SPEC> <PHASE> v<D+1> after round <A>. - D = D + 1. Run the Lint step. Go to Step 2.
Standoff check
A standoff is one finding that the reviewer marks Recurring and MUST_FIX, and
that the reviser rejected in the two most recent consecutive rounds. Detect it with
your rejection tally and grep -n -i 'recurring' <analysis>.
When you find one, rule on it yourself: accept or reject on the merits, in one
paragraph. Append to the document's Revision History, under the current version's
line, one bullet - **Ruling — <finding id>: <accepted | rejected>.** <reason>. Append
a retro-log entry with retro.sh (ruling). Add the finding to the "Closed by ruling" list in every
later reviewer prompt and reviser brief for this phase. If you accepted it, it becomes
a finding for the next reviser brief.
Step 4a — Cap: corrective pass at v(D+1)
Reached when the fourth reviewed version (or a later one) still has MUST_FIX or
SHOULD_FIX above zero. Nothing reviews the corrective version again.
- Call
harnessbriefwithtemplate: adjudicator,specName: <SPEC>, andvaluescarrying the output pathreviews/adjudication-brief-<PHASE>.mdand, as the open items, every open MUST_FIX and SHOULD_FIX from the r analysis by id, title and severity (grep -n -E 'MUST_FIX|SHOULD_FIX' <r<A> analysis>gives the lines; read only those); the adjudication fields are inreferences/briefs.md. - Spawn
sdd-adjudicatorwithRead and execute the instructions in <brief path>. - Spot-check:
grep -n -E '^- \*\*v<D+1>\*\*' <document>finds the line and it containsPost-cap corrective pass. - From the report, list the ruled-out SHOULD_FIX items (id and title). They are the carried items for the next phase: keep them for the HANDOFF section in Step 6.
- Checkpoint commit
docs(sdd): <SPEC> <PHASE> v<D+1> post-cap corrective pass. - Append a retro-log entry with
retro.sh(inefficiency: cap hit; every item id withfixedorruled out). - D = D + 1. Go to Step 4b.
Step 4b — Narrow check
- Call
adversarial-review(noverdictBlock). Read the prompt file, then overwrite it with the narrow-check prompt from the template, listing the items the corrective pass fixed (Step 4a's adjudicated items, or the SHOULD_FIX-only pass's SHOULD_FIX items). - Spawn
sdd-checkerwith exactlyRead and execute the instructions in <promptOutputPath>. - Read
grep -n '^VERIFIED:' <analysis>and, if present, the lines from## Deferred findingsto the end (sed -n '/^## Deferred findings/,$p'). Copy each deferred finding into the retro log as one entry (gotcha, evidence = the analysis path). - Go to Step 5. Approval always follows the narrow check, whatever
k/nsays; the count goes into the approval response.
Step 5 — Approve
- Find a pending record for this version:
approvalslistwithcategoryName: <SPEC>,filePathas above,status: pending; take the newest whose title ends inv<D>, if any (a run under the older per-version flow may have left one). - Otherwise
approvalsrequestnow, with title<SPEC> <PHASE> v<D>, thefilePathabove,type: document,category: spec,categoryName: <SPEC>. If it fails on MDX or tasks-format errors, assemble a reviser brief withharnessbrief(template: reviser,specName: <SPEC>) whose findings are the error lines (numberedRI-1, …), spawnsdd-reviser, checkpoint commitdocs(sdd): <SPEC> <PHASE> v<D+1> lint fixes, D = D + 1, and request again once. A second failure isPHASE: error. approvalsapproveon the record with the response format fromreferences/cleanup.md: version, rounds, final verdict counts, rulings, cap.- Go to Step 6.
Step 6 — Cleanup, then report
Follow references/cleanup.md in order: prune, delete the listed files, keep the
memory file and the context file, retro-log phase summary, HANDOFF section (with the
carried items from Step 4a, or none), commit. Record
phase.end phase=<PHASE> result=approved state=v<D> "note=<rounds> rounds, <trajectory>".
Then report PHASE: approved, STATE: v<D>, NEXT: <next phase> v1 (after tasks:
NEXT: implementation). In the 150 words above the contract, name any scope the
decomposition entry lists that the document cut or deferred, every ruling, and the
carried items.
Step R — Revision input
MODE: revision means a human left needs-revision comments, or the supervisor
re-opened this phase after a design defect.
- Call
harnessbriefwithtemplate: reviser,specName: <SPEC>, andvaluescarrying the output pathreviews/reviser-brief-<PHASE>-v<D+1>.mdand, as the findings,REVISION_INPUT(numberedRI-1,RI-2, …) instead of an analysis file. Every item is a MUST_FIX; the reviser may still reject one with a reason. - Spawn
sdd-reviser. Spot-check. Checkpoint commit. - D = D + 1. Run the Lint step. Go to Step 2. At least one review round runs before approval, even if the document had converged before. The cap rule applies as written: a revised document already at v4 or later that iterates goes to Step 4a.
Budget
When the next review round would exceed BUDGET: write the HANDOFF section (state,
D, A, last verdict, rejection tally, rulings), commit, record phase.end phase=<PHASE> result=resume state=v<D>, and report PHASE: resume,
STATE: v<D>, NEXT: review v<D> or NEXT: revise to v<D+1> depending on where you
stopped. A fresh orchestrator resumes from Step 0.
Legacy rules that stay in force
- Never wait on dashboard state; never poll; never treat
BLOCKEDas a stop. - Load steering documents by phase (the briefs do this): requirements ⇒
product.mdand the decomposition entry; design ⇒tech.md,structure.md,design-system.mdwhen present; tasks ⇒structure.mdand this spec'sdesign.md. - Surface any cut scope in the phase report.
- One version of the document in context at a time. Workers read the whole document; you do not.