Writing Specs
A Flow-Next spec is a durable implementation contract. It should remove ambiguity without turning into pseudocode.
The spec should say what must be true when the work is done, why that matters, and what is out of bounds. The agent should still be free to discover the implementation path inside those boundaries.
Include
Section titled “Include”- Problem and target user
- Business outcome
- Acceptance criteria with stable R-IDs
- Constraints and non-goals
- Edge cases
- API or data contract expectations
- Decision context and trade-offs
- Genuinely-open unknowns, parked under
## Parked unknownsinstead of written up as if they were decided
- File-by-file implementation orders
- File paths and line numbers as spec content - they rot on the first refactor; state the contract (types, signatures, behaviors) instead.
**Files:**/**Touches:**stay a task’s job - Premature abstractions
- Pseudocode
- “Maybe later” features
- Renumbering R-IDs after review
- Restating a standing G-ID from
.flow/criteria.mdas an R-ID - completion review already judges every G-ID against the spec; reference it in prose and write an R only for what this spec adds (Standing Criteria)
Spec authoring loop
Section titled “Spec authoring loop”flowchart LR Draft["Draft intent"] --> Business["Business interview"] Business --> Technical["Technical interview"] Technical --> Ready["Spec ready?"] Ready -->|gaps| Technical Ready -->|ready| Plan["/flow-next:plan"] Plan --> Tasks["Spec + tasks"] Tasks --> PlanReview["/flow-next:plan-review (optional)"] PlanReview -->|gaps| Technical PlanReview -->|ship| Work["/flow-next:work"]
Solo developers can collapse the business and technical passes into one conversation. Teams should keep them distinct so product ownership and engineering ownership are visible in the artifact.
/flow-next:plan produces both the spec (with R-IDs) and the task breakdown sized for one work-loop iteration. /flow-next:plan-review is an optional gate between planning and implementation — see Plan Review for when it earns its cost.
Before planning — the ready flag
Section titled “Before planning — the ready flag”The “Spec ready?” diamond above is a real, recorded state since Flow-Next 1.12.0 — not just a judgment you hold in your head. A spec carries a ready boolean (default false) marking it complete enough to hand to an agent:
flowctl spec ready fn-1 # bless the spec (idempotent)flowctl spec unready fn-1 # back to draftflowctl specs # ready specs carry a [ready] badgeReadiness is human-owned, never agent-inferred — the spec is where your judgment concentrates, so blessing it is your call. It is orthogonal to status: a ready spec stays open through planning and work. For tracker-connected repos, readiness projects one-way from the tracker instead (tracker.readyState — see Tracker Sync), so the board is the single place the team blesses work.
The gate is opt-in and invisible until adopted. Once it is in use (any spec marked ready, or tracker.readyState configured), /flow-next:capture and /flow-next:interview offer to mark a finished spec ready, and /flow-next:plan soft-warns before planning a spec that is still draft — warn, never block. The flag is the entry gate for autonomous loop execution: /flow-next:pilot selects ready specs only, so a half-baked draft is never executed unattended — and on tracker-connected repos that makes the board the loop’s control plane (see Readiness as the control plane). It stands alone as backlog hygiene too — flowctl specs shows at a glance which specs are blessed vs still-draft.
Template discovery
Section titled “Template discovery”The bundled canonical scaffold lives at ${PLUGIN_ROOT}/templates/spec.md. In Flow-Next 1.1.3+, /flow-next:capture, /flow-next:interview, and /flow-next:plan resolve the scaffold through a three-tier cascade. First match wins:
<repo_root>/SPEC.md<repo_root>/spec.md- 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. A leftover .flow/templates/spec.md from an older install is no longer consulted - delete it.
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.
Customizing the scaffold
Section titled “Customizing the scaffold”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.
# from your repo rootcp "$CLAUDE_PLUGIN_ROOT/templates/spec.md" SPEC.md$EDITOR SPEC.mdgit 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 interview 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 wants | Instruction 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<n>:** 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 budgets | An 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 rule | Under ## 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 language | Rewrite the guidance prose in German/French/Japanese/… - the authoring passes follow the instruction language, and the four parsed headings plus **R<n>:** 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.
| Heading | What stops working |
|---|---|
## Acceptance Criteria | R-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. |
## Boundaries | The “Not in this PR” section of a generated PR body loses its source. |
## Goal & Context | The business interview pass loses a write target. |
## Decision Context | Flat-versus-substructured detection for ### Motivation / ### Implementation Tradeoffs. |
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.
Enumerate error cases inside the criterion. Each behavioral R-ID states its error / invalid-input / boundary handling as sub-clauses in the same bullet — or explicitly records “no error surface beyond X”. A one-line “none” declaration is a complete enumeration; silence is not, and a reviewer can’t tell considered-and-none from forgot. The work stage inherits enumerated cases as required tests, which is exactly where untested error paths otherwise entrench: a persisted regression suite reports “all green” for the cases nobody enumerated. Error cases stay prose sub-clauses — no sub-R-IDs, no extra coverage-table columns. Standing G-IDs keep their own error handling; don’t restate it per spec.
Mark your sections so the interview can fill them. flowctl scope write-policy enumerates the seven canonical sections only, so a section you add sits in neither its writable nor its preserved list. Ownership of a project-added section therefore comes from the section’s own scope-owner marker, and /flow-next:interview applies a three-way rule:
| Marker on your section | What an interview pass does |
|---|---|
names the pass’s own scope, e.g. <!-- scope: business --> under --scope=business | writes it, filling and refining it like a canonical section |
| names the other scope | preserves it byte-for-byte |
<!-- scope: both --> | writable under any pass |
| absent or unparseable | preserves it byte-for-byte and says so in the read-back |
The marker is the difference between a section that gets filled and one that stays frozen. Add one when you want the interview to do the work, leave it off when the section is yours to hand-write. Two consequences: a marked section is rewritable, so hand-authored content under a marker you own will be refined by the next pass of that scope, and scope-owner markers must be kept on project-added sections rather than stripped, because for those the marker is the only ownership signal a later pass has.
/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.
Setup template copy
Section titled “Setup template copy”/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 / abortOn 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 / abortThe compare normalizes CRLF to LF and ignores trailing-newline differences, so real project customizations are not silently clobbered.
Good acceptance criteria
Section titled “Good acceptance criteria”**R1:** Users can create a spec from conversation context.**R2:** Each generated acceptance criterion is source-tagged as [user], [paraphrase], or [inferred].**R3:** The agent shows a read-back before writing files.Briefing evidence from chart
Section titled “Briefing evidence from chart”When a spec is captured from a chart briefing package, keep three provenance lanes separate:
- Chart decision provenance - structural: D-ID, decision type, answer, evidence/assets, supersession, briefing membership (B-ID / cluster key). Cite these as links and references in Goal, Decision context, or Notes - e.g. “Storage approach settled in
fn-140.D2(probe); see briefing B1.” - Acceptance-criterion author provenance - trailing tags
[user] | [paraphrase] | [inferred] | [strategy:<track>]on criteria that capture or interview newly write. These answer who grounded this criterion, not how strong the chart evidence was. A criterion derived from a resolved unattended D-ID is not automatically[user]. - Verified-versus-inferred technical fact grammar - not used on chart facts or decisions. Do not invent trailing
[verified]/[inferred]labels for D-ID evidence.
D-ID evidence never receives acceptance-criterion source tags. Capture and interview tag only the criteria they author; they never retag an earlier pass.
Scope split for teams
Section titled “Scope split for teams”Business pass:
/flow-next:interview fn-1 --scope=businessTechnical pass:
/flow-next:interview fn-1 --scope=technical --strategy --docsBoth passes write to the same spec file. They are states of one evolving artifact, not separate documents.
Product-owner pass
Section titled “Product-owner pass”The business pass should answer:
- Who is affected?
- What behavior must exist?
- What customer or operational outcome defines success?
- What is explicitly not in scope?
- What can the agent infer, and what must remain human-owned?
Good product acceptance criteria are observable:
**R1:** A maintainer can run a single command to generate a PR body from an implemented spec.**R2:** The PR body lists every acceptance criterion and whether it has evidence.**R3:** Deferred review findings appear in the PR body instead of being hidden in agent context.Technical pass
Section titled “Technical pass”The technical pass should add:
- System boundaries and integration points.
- Data model or file format expectations.
- Security, permissions, and migration constraints.
- Test strategy and evidence expectations.
- Known risky files or modules.
Do not write a step-by-step implementation unless the sequence is itself a requirement. Let /flow-next:plan map the spec to the codebase after the spec is stable.
Additional role passes
Section titled “Additional role passes”Business and technical are the two built-in scopes, not the ceiling. Field teams add passes for the roles their organization actually gates on — platform/cloud enablement (deployment topology, IAM and secrets, cost and quota constraints, observability requirements) and quality (test-environment needs, regression surface, release-gate criteria) are the common two.
Two mechanics, composable:
- Standing sections via the scaffold. Copy the bundled template to a repo-root
SPEC.mdand add the sections those roles always fill (## Platform & operations,## Quality gates, …) — every new spec then arrives with the role’s checklist built in, and the guidance prose inside a section is free to rewrite. See Customizing the scaffold; the four parsed headings are the only ones that must keep their names. - A focused interview pass per role. Run
/flow-next:interview fn-Nand steer it in conversation — “interview from the platform-engineering angle: deployment, IAM, cost, observability” — or simply discuss the concerns in chat and let the interview’s write-back consent fold them in. R-IDs are append-only and source-tagged, so a platform pass addsR14-R16without disturbing the product criteria, and the coverage table later shows exactly which task satisfies the ops requirement.
The same rule binds every pass: each role states what must be true, not how to build it. An ops criterion like “R14: the service exposes a health endpoint the deploy gate can probe” ages well; “use Terraform module X” belongs in the technical constraints only if it genuinely is a constraint.
Before planning
Section titled “Before planning”Run the checklist:
- Every requirement has an R-ID.
- Non-goals are explicit.
- Ambiguous words like “fast”, “simple”, “secure”, or “nice” are translated into observable criteria.
- Product assumptions are marked as product assumptions, not engineering facts.
- Technical constraints are constraints, not preferences.
- The spec can be reviewed by someone who never saw the original chat.