# Spec Schema

Source: https://flow-next.dev/reference/spec-schema/

Canonical Flow-Next spec sections.

Flow-Next specs follow the template in `plugins/flow-next/templates/spec.md`.

## Default sections

The scaffold is a default, not a requirement ([customize it](https://flow-next.dev/guides/spec-scaffold/)). Use the headings exactly as written; `flowctl brief` and the other readers key on them.

* Goal & Context
* Architecture & Data Models
* API Contracts
* Edge Cases & Constraints
* Acceptance Criteria
* Boundaries
* Decision Context

## Recommended shape

```md
---
id: fn-12-add-pr-briefing
title: Add PR briefing generation
---


## Goal & Context


## Architecture & Data Models


## API Contracts


## Edge Cases & Constraints


## Acceptance Criteria


**R1:** ...
**R2:** ...


## Boundaries


## Decision Context
```

The exact template in the Flow-Next repository is the source of truth. This page explains how to think about the sections when writing or reviewing.

Auxiliary sections are written only when they apply - `## Strategy Alignment` and `## Strategy Conflicts` when `STRATEGY.md` has content, `## Glossary Conflicts` on a doc-aware vocabulary mismatch, `## Conversation Evidence` from capture, `## Resolved via Codebase` / `## Resolved via Project Docs` / `## Resolved via Experiment` from refine, `## Resolved via Research` from `refine --scope=research` or plan’s research scouts (one sub-block per scout, a source on every bullet), and `## Parked unknowns` for genuinely-unknown items. Park an item only when it fails the fog-or-ticket test: decidable now means decide it, schedulable means make it a task, and only true fog gets parked. Parked items graduate into the real sections as refine or plan resolves them, so the section empties as the spec matures.

## Spec and task ids

A spec is referenced by **one of two id schemes**. `fn-NN` is the most common form in these docs, but it is not the only form - a spec linked to a tracker is referenced by its **tracker key** instead.

|                           | Flow-first (the default)                                                                                           | Tracker-first                                                                                           |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
| canonical spec id         | `fn-NN-slug`                                                                                                       | `wor-17-slug`                                                                                           |
| canonical task ids        | `fn-NN-slug.M`                                                                                                     | `wor-17-slug.M`                                                                                         |
| branch                    | `fn-NN-slug`                                                                                                       | `wor-17-slug`                                                                                           |
| bare aliases that resolve | `fn-NN`, `fn-NN.M`                                                                                                 | `wor-17`, `wor-17.M`, and `WOR-17` (the tracker display key)                                            |
| how it is created         | `/flow-next:capture`, `flowctl spec create`                                                                        | `flowctl spec create --tracker-first --tracker-identifier WOR-17 --tracker-id <id> --tracker-url <url>` |
| linking to a tracker      | `flowctl sync set-tracker-id` adds `WOR-17` as a resolvable alias; the `fn-NN` id, branch, and tasks are unchanged | tracker-keyed and linked at creation when the issue id is passed                                        |

All four trackers can be grabbed tracker-first. **Linear `WOR-17` and Jira `PROJ-123`** are already alpha-prefixed `KEY-N` display keys and mint directly. **GitHub `#N` and GitLab `<project>#<iid>`** are not `KEY-N`, so they mint through a synthetic key derived from the tracker type: GitHub issue 123 becomes `gh-123-slug`, and a GitLab project-scoped `iid` of 456 becomes `gl-456-slug`. That is unambiguous because a repo has exactly one configured tracker, and it is guarded so a minted id can never collide with a historical one. A non-standard Jira Data Center / Server key that can’t slugify into a canonical id (an underscore key like `MY_PROJECT-7`, or a long alphanumeric one) still links flow-first / display-only.

The **full** `…-slug` form is the filesystem, branch, and task identity. A shared bare ordinal (two `fn-122-…` files with different slugs) is untidy, not a broken identity.

Which scheme new specs use is a setting: `flowctl config set tracker.specIds tracker` makes tracker-keyed ids the default, and setup asks once when a tracker is configured. Teams want it - see [why](https://flow-next.dev/guides/for-teams/).

Both schemes resolve everywhere a spec or task id is accepted. `flowctl show wor-17`, `/flow-next:work wor-17`, `/flow-next:plan wor-17`, and the task form `wor-17.1` all resolve, and **resolution is case-insensitive** - `WOR-17` works the same as `wor-17`.

The two columns are set at creation and never converted into each other. Only a spec created with `--tracker-first` is canonically `wor-17-slug`; linking an existing `fn-NN` spec to a tracker keeps it `fn-NN` and merely **adds** the tracker key as an alias.

Three rules keep the two schemes from colliding:

* **Ids never change.** There is no rename-on-push. When a `fn-NN` spec is linked to a tracker, the tracker key is added as a resolvable alias, and the existing id, branch, and dependency edges are never mutated. `flowctl spec set-title` on a tracker-linked spec updates the title only; it does not re-slug the id. Switching `tracker.specIds` affects *new* specs only, so mixed stores are permanent and expected.
* **The `fn-` prefix is reserved** for the native sequential scheme. Tracker-key resolution is tried only after the `fn-` path misses, and native `fn-N` allocation counts `fn-*` ids only - a `wor-9999` never bumps the next `fn` number.
* **One tracker team per repo.** The bridge assumes a single team key so a bare `wor-17` resolves unambiguously. Two teams both keyed `WOR` in the same repo is out of scope.

Native `fn-N` allocation takes the max across the working tree, every registered git worktree, and every ref, which shrinks parallel-create races inside one repo. Separate unfetched clones can still collide; tracker-keyed ids avoid that race by using the tracker as the allocator.

The tracker key only exists once a spec is linked to a tracker - see [Tracker Sync](https://flow-next.dev/integrations/tracker-sync/). If you never connect a tracker, every spec stays `fn-NN` and nothing here changes. Bridge mechanics: [Tracker Sync](https://flow-next.dev/integrations/tracker-sync/#hybrid-id-model). Team recommendation: [Collaboration](https://flow-next.dev/guides/for-teams/#spec-ids-when-several-people-create-work).

## Writing acceptance criteria

Acceptance criteria use stable R-IDs:

```md
**R1:** The command creates `.flow/specs/<id>.md`.
**R2:** The command writes task files under `.flow/tasks/`.
**R3:** Validation fails if a task references a missing dependency.
```

## Source tags

`/flow-next:capture` marks criteria with source tags:

* untagged: the user’s verbatim words (counted under `[user]` in capture’s `Source:` tally)
* `[paraphrase]`: restated user requirement
* `[inferred]`: agent-inferred fill
* `[strategy:<track>]`: derived from `STRATEGY.md`

Capture’s summary after saving shows the inferred count.

## Section ownership

| Section                      | Primary owner             | Review question                           |
| ---------------------------- | ------------------------- | ----------------------------------------- |
| Goal and context             | PO, PM, user, maintainer  | Does this describe the real outcome?      |
| Acceptance criteria          | PO + tech lead            | Can we verify every requirement?          |
| Boundaries                   | PO + tech lead            | What must not be built?                   |
| Architecture and data models | Tech lead                 | Does this match the repo and constraints? |
| API contracts                | Tech lead                 | Are integrations and compatibility clear? |
| Edge cases and constraints   | PO + engineering          | Are failure modes explicit enough?        |
| Decision context             | Whoever made the tradeoff | Will this still make sense in six months? |

## R-ID rules

* Use `R1`, `R2`, `R3` in order.
* Freeze meanings after review.
* Leave gaps when removing accepted criteria.
* Append new criteria at the end.
* Map tasks back to R-IDs with `satisfies: [R1, R3]`.

R-IDs are not formatting. They are how Flow-Next connects requirements to tasks, review receipts, and PR explanations.

## Project-wide criteria (G-IDs)

A criterion that belongs to the project rather than to one spec (“every route change regenerates the API contract”) uses the same grammar with a `G` prefix, in `.flow/criteria.md`:

```markdown
- **G1:** Every route change regenerates the API contract.
- **G2:** No new dependency without a health check (scope: package.json).
```

Spec completion review judges every G-ID against the whole implementation and records `met` / `violated` / `n/a` per criterion in the review receipt. The same never-renumber rule applies. Full model: [Standing Criteria](https://flow-next.dev/reference/standing-criteria/).

G-IDs are never restated as R-IDs in a spec’s `## Acceptance Criteria`. A copy would drift as `criteria.md` evolves and be judged twice. Reference the G-ID in prose; an R-ID covers only what the spec adds beyond the standing rule.

Flow-Next 1.1.4 treats `## Acceptance Criteria` as canonical. The parser still accepts legacy `## Acceptance` and `## Acceptance criteria` headings so older specs keep working.

## Inferred criteria

Treat `[inferred]` criteria as provisional. They are useful because they show where the agent filled gaps, but they need human review before implementation. A high inferred count is a signal to read those lines before building, not a signal to trust the draft harder. Run `/flow-next:refine` on them when one hides a decision only you can make that would change what gets built.
