Skip to content
GitHub

Product brief fields

Fields, lifecycle gates, spec maps, and provenance links for Core product briefs.

Authoritative field list for a delivery brief and the linkage fields it stamps on derived specs. A brief lives at docs/product/briefs/<slug>.md. Use author-delivery-brief create to author a Draft from bounded source material; use author-delivery-brief continue to review an existing repository brief and offer confirmed slices. For the workflows, see Intake an external brief into a product brief and Continue a delivery brief and confirm delivery slices; for why the layer exists, see Why a brief layer.

FieldRequired?Meaning
SlugyesKebab-case identifier; matches the filename.
ReceivedrecommendedThe date the brief was handed over (YYYY-MM-DD).
OwnerrecommendedWho owns delivering this repo’s slice.
EpicoptionalId or link of an external coordinator (a tracker epic, an integration repo) when this repo’s work is one slice of a cross-repo effort. Omit when there is none. This is the only pointer to the wider effort — the repo owns its slice, not a coordination hub.
Parent intentoptionalThe intent this brief was cut from. Write it as the target intent’s registered kind and Slug:, such as outcome:service-reliability or intent:account-self-service. An absent value, or a value that begins with the word none, records no pointer. Distinct from Epic, which names an external coordinator.
Cut-closedrequired only when Status: Shipped; refused when Status: DraftRecords that the cut — the set of delivery slices this brief will ever have — is finalised and no further slices are coming. ISO 8601 date followed by non-empty evidence text (e.g. 2026-08-25 All nine slices are Shipped.); a value in any other shape is refused. Like every field in this table it must sit above the brief’s first ## heading: written below one, or inside an HTML comment, it reads as absent, so a refusal can name a file that visibly contains the line. A template row whose value is only a comment or is empty counts as absent rather than malformed, which is why the shipped template does not trip the gate.
SectionRequired?Meaning
Outcomeyes (load-bearing)The problem and the user-facing outcome, in the user’s terms. The one field a brief cannot do without — it’s what every slice is measured against.
Success metricsoptionalObservable signals that the outcome landed (not activities). E.g. “p95 checkout under 400ms”, “reset tickets down 60%”. Stays at brief altitude — see below.
Scope / Non-goalsyesThe boundary of this repo’s slice. Non-goals are as load-bearing as scope — they stop the decomposition from sprawling.
AppetiteoptionalA constraint, not an estimate: how much time/effort the outcome is worth (“a few weeks, not a quarter”). Bounds the decomposition.
User storiesoptional (Shape B)Stories with ids (US-1, US-2, …). Present → decomposition groups stories into specs and coverage is story-granular. Absent → Shape A, spec-granular coverage.
Assumptions / Risksoptional until ReadyNamed assumptions or risks that could change the outcome or delivery boundary. At least one is required to reach Ready.
Spec mapsection required for Ready; rows optionalThe coverage table. One row per materialized spec; the Status column is auto-derived by the coverage lint (never hand-edited). Shape B adds a Story column. A Ready brief may have zero rows.
Rabbit holesoptionalNamed design traps, constraints, or out-of-bounds explorations to avoid.
Sourceoptional until ReadyDurable source provenance. Tracker-origin work also records the reviewed source revision.
Statusset by the owning workflowOne of Draft, Ready, Executing, Shipped, Withdrawn, or Cancelled. create sets Draft; continue may set Ready; terminal transitions go through close-work. Setting Shipped also requires a Cut-closed record — see § Lifecycle states.

A brief sits between the roadmap and a spec, and Success metrics inherits that altitude. Write each one as an outcome-level observation that can fail, and say in the section itself that the falsifiable criteria which operationalise them are spec-owned.

Make each metric bind in both directions. A metric worth keeping is one an over-restrictive implementation fails as surely as a permissive one. “A placement path that escapes its root refuses; one that merely leaves the repository asks” fails both ways. “Unsafe paths are rejected” fails only one.

The tell that the section has dropped below brief altitude is a review finding that asks for a checker name, an exit code, a read boundary, or a fixture cohort. Those are spec-owned details. When a finding asks for one, the section has gone too far down, not too little.

Rebuild rather than patch. Answering a “one-sided contract” finding by adding another criterion makes the section grow, and each addition tends to contradict scope or non-goals somewhere else — so the next round finds more. If the section has taken two rounds of additions, replace it with a handful of outcome-level observations instead of amending it a third time.

Sweep every section the rule touches. Repairing only the section a finding names leaves the same defect in its siblings — a fixed count removed from Non-goals can survive in a later section and return as a fresh finding.

A brief is Ready — eligible for slice selection by author-delivery-brief continue — only when the human confirms the canonical gate below. These fields are required to reach Ready, not required for a Draft:

FieldRequirement
OutcomePresent and non-empty
In-scope and out-of-scope boundariesBoth explicit
Constraints or appetitePresent and non-empty
Named assumptions or risksAt least one
Durable source provenancePresent; tracker-origin work includes the reviewed source revision
Spec mapSection present; placeholder rows are not required

author-delivery-brief create may elicit these fields but always sets Status: Draft and does not certify readiness. author-delivery-brief continue owns this gate and is the only mode that sets Status: Ready, after human confirmation. A Ready brief may have zero materialized specs and remains non-executable until the user confirms a slice.

The tables below and the lint cannot disagree: both are derived from one module, scripts/brief_shape.py in the author-delivery-brief skill, which is the single source of the vocabulary, the coherence rules and the transition set.

StatusChild-scope ruleCut-closed:
DraftNo child is Implementing or Shipped.Refused — Draft means the cut is still open, and a material edit returns a brief here.
ReadyNo child is Implementing or Shipped.Permitted.
ExecutingAt least one child is Implementing or Shipped.Permitted — a cut may be declared while a slice is running.
ShippedNon-empty child set; every child is Shipped.Required.
WithdrawnNo child is Implementing or Shipped.Permitted, not required.
CancelledAt least one child is Implementing or Shipped.Permitted, not required.

All currently mapped children being Shipped does not close the brief. Keep it Executing when a later slice is still expected or has not been materialized. Closeout changes the brief and its matching workspace collection together; it does not rewrite child specs.

FromTo
DraftReady, Withdrawn
ReadyDraft, Executing, Withdrawn
ExecutingReady, Shipped, Cancelled
Shipped— terminal
Withdrawn— terminal
Cancelled— terminal

A pair of two different states absent from this table is an illegal move. A state paired with itself is not a move. Two edges carry a record obligation: Executing → Shipped must add the Cut-closed: record (required on Shipped, and the state is terminal), and Ready → Draft must clear it (a material edit reopens the cut).

A markdown table whose rows the coverage lint reconciles against the specs:

| Spec | Status | ← Shape A (no stories)
| --- | --- |
| `password-reset-request` | Shipped |
| Spec | Story | Status | ← Shape B (story list)
| --- | --- | --- |
| `billing-plan-management` | US-1 | Shipped |
  • The first column is the spec slug (docs/specs/<slug>/).
  • The last column is the auto-derived status — leave it to the lint.
  • A brief is delivered only when its own status is explicitly Shipped, its map is non-empty, and every mapped spec is Shipped. A Ready brief with an empty map is valid but not delivered; an Executing brief with an all-shipped current map is also not delivered.

author-delivery-brief continue stamps these on the specs it scaffolds (both are additive and optional — a directly-authored spec omits them and stays valid):

Field / markerWhereMeaning
Brief: brief:<slug>spec header (sibling to Constrained by: / Contract:)Product provenance — the brief this spec was derived from. Write it as brief:<slug>. Readers still accept a legacy repository path in older specs, so nothing written under the old convention breaks. A bare slug is accepted by traceability and the coverage join, but dispatch reconciliation refuses it, so a bare slug still blocks dispatch. Distinct from Constrained by:, which cites the ADRs/RFCs that govern the spec. The coverage map rolls up from these back-links.
Satisfies: US-nappended to an acceptance criterionStory trace (Shape B only). Marks the AC that satisfies story US-n, giving story-granular coverage. Omitted in Shape A.

Write a brief’s Parent intent: as the target intent’s registered kind and Slug:, such as outcome:service-reliability or intent:account-self-service. Write a spec’s Brief: as brief:<slug>. New and changed artifacts are checked: each typed pointer must name an existing target of the field’s type. The check is forward-only, so an older artifact you do not touch is not checked. Readers keep accepting legacy repository paths in those older artifacts. An absent value or a value beginning with the word none records no pointer.

scripts/lint-brief-coverage.py (bundled with author-delivery-brief) reads every spec’s Status: field, follows the Brief: back-links, and rolls each brief’s Spec map up from its children. Behavior:

  • Reports each brief as delivered only for explicit Shipped closeout with a non-empty all-shipped map; every other state reports not delivered.
  • Fails when the brief status is absent, unknown, or contradicts child execution evidence — the § Lifecycle states table states the rule per status.
  • A spec that back-links a brief but isn’t in that brief’s map is reported untracked (informational) — add the row; it’s not an error.
  • A Shipped brief carrying no Cut-closed record is a failure (exit 1). Add the field to the preamble — an ISO 8601 date then evidence text, for example 2026-08-25 All nine slices are Shipped.
  • A Draft brief carrying a Cut-closed record is a failure (exit 1); a material edit reopens the cut, so the declaration is no longer true. Remove it, or move the brief out of Draft.
  • A Cut-closed value that is not an ISO 8601 date followed by evidence text is a failure (exit 1).
  • A brief’s Spec-map Status cell that contradicts the spec’s real status (a hand-edited, stale cell) is a failure (exit 1) — the column is auto-derived and must not be hand-maintained.
  • It no-ops (exit 0, silent) when no brief exists.
  • The shipped _template.md is skipped — it’s the template, not a brief.