Specification Capture
Contract
Iron law: every issue gets the four elements (non-goals, failure modes, interface contracts, risk map) in the journal before PLAN. Invoked with issue context, journal path, and invocation reason by /flow:start Phase 1 (before the Spec Validation Gate), /flow:design Phase 1, and /flow:brainstorm Phase 1. Returns the ## Captured Specification payload from references/specification-journal-format.md after writing and re-reading .decisions/issue-{N}.md. Permitted skips: elements outside the invoker's scope row; the risk map when specFirst.riskMap is false (written as disabled — specFirst.riskMap=false); elements the journal already holds for an unchanged issue.
Inputs
Issue context, journal path (.decisions/issue-{N}.md), invocation reason (start | design | brainstorm). Any missing: halt with SPEC_CAPTURE_BLOCK: missing input <name>. Toggle: "${CLAUDE_PLUGIN_ROOT:-plugins/flow}/bin/cascade-resolve.sh" --default true '.specFirst.riskMap'.
Process
Step 1: Read the journal first
Run awk '/^## Specification$/{f=1;print;next} /^## /{f=0} f' "$JOURNAL" and record which of ### Non-goals, ### Failure modes, ### Interface contracts, ### Risk map exist. All four present and the issue not newer than the journal (staleness rule in the reference): return verbatim. Otherwise fill only the gaps.
Step 2: Extract from the issue body
Cues. Non-goals: ## Non-goals, ## Out of scope, "Does NOT". Failure modes: ## Failure modes, ## Error cases, "timeout", "fallback". Interface contracts: ## API, ## Schema, ## Contract, type definitions, signatures. Risk map: ## Risks, ## Tricky parts, "edge", "subtle", "off-by-one".
Verbatim matches are extracted-from-issue; never prompt for what the issue states.
Step 3: Prompt for missing elements
Draft each missing element, then surface one blocking six-field escalation per element (never bundled) via AskUserQuestion per references/escalation-format.md. Options: (1) accept draft, (2) edit, (3) reject (update the issue first). Recommend (1).
Risk map draft: 2-6 rows from the issue and the touched files. Each row: where the core logic is most likely to be subtly wrong; what the plausible wrong version does (reversed order, transposed streams, off-by-one, wrong rounding, wrong precedence, wrong empty case); one concrete input on which right and wrong differ.
Step 4: Write the journal
Write ## Specification per the reference shape (replace if present, append otherwise). All four failure-mode categories are required; a non-applicable one is none — {reason}, never blank. Re-read with the Step 1 awk; if the section is absent, halt with SPEC_CAPTURE_BLOCK: journal write verification failed.
Step 5: Return the captured specification
Return the ## Captured Specification payload; consumers cite subsections as Non-goals touched, Failure modes covered, Interface contract, Risk areas.
Per-invoker scope
commands/start.mdPhase 1: all four (risk map exempt whenspecFirst.riskMapisfalse); partial = BLOCK.commands/design.mdPhase 1: non-goals + interface contracts; risk map recommended.commands/brainstorm.mdPhase 1: non-goals only, captured before generating approaches.commands/debug.mdPhase 3 (viagoal-contract-capture): outcome + acceptance criterion (the reproducing test) + root-cause constraints; full specification skipped.
Elements are always written, so brainstorm non-goals pre-populate a later /flow:start.
Anti-patterns
- "The acceptance criteria are enough": criteria scope WHAT; the elements scope IS NOT, FAILS HOW, WHICH SCHEMAS, WRONG WHERE.
- "I'll capture during PLAN": PLAN's fence is built from the specification; the Stranger Test fails.
- "The failure-mode categories cover risk": no. Timeouts, partial failures, invalid input, missing context are infrastructure error paths agents already over-test. The risk map is about the core logic being subtly wrong.
Verification gates
The invoker MUST verify (any failure: halt, re-invoke):
- The journal contains
## Specification. ### Non-goals,### Failure modes,### Interface contractspresent and non-empty (none — {reason}allowed per failure-mode category).### Risk mapis a 2-6 row table, or exactlydisabled — specFirst.riskMap=false.- The return payload matches the journal.