Skip to content

Customizing the spec scaffold

Most projects never touch this page. Read it once, when your team wants every spec to carry something the bundled scaffold does not: a risk register, a rollout runbook, user-story criteria, a compliance block.

The bundled canonical scaffold lives at ${PLUGIN_ROOT}/templates/spec.md. In Flow-Next 1.1.3+, /flow-next:capture, /flow-next:refine, and /flow-next:plan resolve the scaffold through a three-tier cascade, and since 4.15.0 flowctl spec create and flowctl spec skeleton apply the same cascade, so a repo SPEC.md shapes CLI-created specs too (the old six-heading CLI skeleton is gone). First match wins:

  1. <repo_root>/SPEC.md
  2. <repo_root>/spec.md
  3. bundled ${PLUGIN_ROOT}/templates/spec.md

Migrating from a pre-4.0.0 install: a customized .flow/templates/spec.md is no longer read - copy it to a repo-root SPEC.md (tier 1) to keep your scaffold, then delete the old file.

Use the repo-root SPEC.md or spec.md when a project needs a customized scaffold. That matches Flow-Next’s other repo-root guidance files such as STRATEGY.md, CLAUDE.md, AGENTS.md, and GLOSSARY.md.

The bundled plugin template is the fallback and source of truth when the repo has not opted into customization.

Case-insensitive filesystems such as macOS APFS and Windows NTFS collapse SPEC.md and spec.md to one inode, so Flow-Next treats that as a single tier-one hit. On case-sensitive filesystems where both files exist distinctly, SPEC.md wins and Flow-Next warns.

The bundled section list is a default, not a requirement. Tier one exists so a project can impose its own spec shape without forking the plugin. If your team wants user stories on every spec, a risk register, a rollout runbook, a compliance block or a data-retention statement, add it and every spec that project authors carries it from then on.

Terminal window
# from your repo root
cp "$CLAUDE_PLUGIN_ROOT/templates/spec.md" SPEC.md
$EDITOR SPEC.md
git add SPEC.md && git commit -m "docs: project spec scaffold"

Commit it. The scaffold is a team artifact, and an uncommitted SPEC.md gives you a spec shape your teammates and your CI agents do not have.

Free to change: adding sections, reordering them, and rewriting the guidance prose under any heading. That prose is instruction to the authoring agent, so making it say what your project actually needs is the highest-leverage edit and the one most often skipped.

A worked example - house style for acceptance criteria. Want BDD-flavored criteria on every spec? Rewrite the guidance under ## Acceptance Criteria in your copied SPEC.md:

## Acceptance Criteria
<!-- House style: write each criterion as a BDD scenario - Given <context>,
when <action>, then <observable outcome>. Keep the **R<n>:** marker;
use sibling ids (R4a, R4b) for scenario variants of one requirement. -->
- **R1:** Given a signed-in user with an expired session, when they submit the form, then the draft is preserved and they are returned to it after re-auth.

Every refine and capture pass then writes criteria in that shape. Only the heading name and the **R<n>:** bullet marker are load-bearing - the criterion text is yours (the parser reads wrapped multi-line bullets, and anything unreadable surfaces as acceptance_criteria_residue rather than being silently dropped). The same move carries any house convention.

More house styles, same mechanism - each is one instruction comment (or one rewritten guidance line) in your copied SPEC.md, and every spec the project authors from then on obeys it:

Team wantsInstruction to add under the relevant heading
User-story criteria (product orgs)Under ## Acceptance Criteria: “Phrase each criterion as: As a <role>, I can <capability>, so that <outcome> - observable outcome mandatory, keep the **R&lt;n&gt;:** marker.”
Observability mandate (SRE-minded)Under ## Acceptance Criteria: “Every criterion names the signal that proves it in production - a metric, log line, or alert. A criterion with no signal is not accepted.”
Compliance traceability (regulated)Under ## Acceptance Criteria: “Tag each criterion with the control it satisfies (e.g. [SOC2-CC6.1], [GDPR-Art17]).” Plus an added ## Data & Compliance section: classification of data touched, retention, audit-trail impact.
Non-functional budgetsAn added ## Budgets section: “State the numeric budgets this change must hold: p95 latency, bundle-size delta, WCAG level. ‘Fast’ is not a budget; ‘<200ms p95’ is.”
Rollout & revert (ops)An added ## Rollout section: “Feature flag or migration? Name the flag, the rollout stages, and the tested revert path. A migration with no revert statement is incomplete.”
Test-evidence ruleUnder ## Acceptance Criteria or ## Boundaries: “Every criterion names the command or test file that will prove it - the worker baselines and re-runs exactly those.”
Specs in your languageRewrite the guidance prose in German/French/Japanese/… - the authoring passes follow the instruction language, and the four parsed headings plus **R&lt;n&gt;:** markers stay English so the tooling keeps reading them.

Composable with additional role passes: a platform or quality role’s standing section is just another row of this table.

Handle with care: four headings are parsed by flowctl. Renaming or removing one does not raise an error, it silently drops a feature. Historical specs written with the older names (## Overview or ## Context, ## Boundaries / non-goals, any-case ## Decision context) are still read through read-only synonyms, and flowctl validate prints one legacy spec headings warning per such spec as the nudge to migrate; text inside HTML comments and fenced code never counts as a heading or a criterion.

HeadingWhat stops working
## Acceptance CriteriaR-ID extraction. Coverage tables come out empty, task satisfies: mapping breaks, unaddressed-R-ID verdict gating stops firing. Legacy ## Acceptance criteria and bare ## Acceptance are still tolerated.
## BoundariesThe “Not in this PR” section of a generated PR body loses its source.
## Goal & Contextflowctl brief falls back to the body’s first line for the spec’s goal, and the PR briefing loses its goal.
## Decision ContextThe PR briefing loses its list of recorded decisions.

Keep R-ID bullets in the form - **R1:** <criterion>, with optional single-letter siblings such as R4a. Prose numbering like “Requirement 1” is not recognized.

Your sections need no markers. /flow-next:refine writes each answer into the section it belongs in, custom or canonical, and leaves every section no answer belongs in byte-for-byte. The read-back before writing names every section the session changed, including yours. Older specs that still carry <!-- scope: ... --> markers load and refine unchanged.

/flow-next:capture and /flow-next:plan seed from the template directly and are unaffected.

We tested widening the bundled scaffold with user-story and test-seam sections. A first pass looked promising, a pre-registered replication did not hold up, and the wider scaffold ran roughly a third longer, which every worker and reviewer downstream pays to read. So the default stays lean and this override stays the place to express a project preference.

Since 5.5.1, /flow-next:capture writes the sections your template names and adds none it leaves out. Before that, capture put a ## Conversation Evidence block at the top of every spec and a ## Requirement coverage table at the end, whatever your SPEC.md said.

The seven canonical sections are named as headings. The auxiliary sections (Conversation Evidence, Strategy Alignment, Parked unknowns, Requirement coverage and the rest) are named in the frontmatter auxiliary_sections list, and each still appears only when its own trigger fires. To stop one from landing in your specs, delete its entry from that list in your SPEC.md.

EntryWhat it isWhat dropping it costs
Conversation EvidenceThe verbatim user quotes capture puts at the top of a spec, typically 15 to 20 lines. No tool reads it.A reviewer who later doubts that an untagged line is the user’s own words can no longer look the quote up in the spec. Capture still collects the quotes during the run and still checks every untagged line against them before it writes.
Requirement coverageThe placeholder table capture adds when the spec is headed for a plan.Nothing. /flow-next:plan writes its own coverage table, so the table arrives with the plan.

Position follows the template as well. An auxiliary section you place as a heading in your SPEC.md lands where you put it. One named only in the list takes the placement its list entry states, which for the bundled entries is evidence at the top of the body and coverage at the end.

One consequence to know about. A SPEC.md written from scratch, with no auxiliary_sections list, gets no auxiliary sections from capture. A SPEC.md copied from the bundled file keeps the list and behaves as before. If you wrote yours by hand and want the strategy or parked-unknowns sections, copy the list across from the bundled template.

/flow-next:setup Step 4a offers a repo-root spec template when neither <repo_root>/SPEC.md nor <repo_root>/spec.md exists:

Copy template / Skip / abort

On consent, setup writes <repo_root>/SPEC.md from the bundled template. It always writes uppercase SPEC.md, preserves file mode 0644, and never creates lowercase spec.md.

On re-setup, Flow-Next uses a byte-compare gate:

Keep mine / Overwrite with canonical / abort

The compare normalizes CRLF to LF and ignores trailing-newline differences, so real project customizations are not silently clobbered.

Authoring guidance for the spec itself (what to include, the ready flag, when to refine) is in Writing specs.