specgate/docs v0.20.0

How Specgate thinks#

Six ideas explain everything the CLI does. Read this once and every command, error and flag has a reason you can predict.


1. A requirement is a sentence that can be wrong#

A requirement lives in spec.md (or in a capability spec under docs/specs/) as a numbered section:

markdown
## REQ-007 — Customers download invoices as PDF

<!-- specgate:trace status=Implemented -->

A customer with a paid invoice MUST be able to download it as a PDF from the
invoice page, within two seconds, with the invoice number in the file name.

The prose is for people. The specgate:trace comment is for the tool — it holds the status, and any link the tool cannot infer. specgate new writes the section with a draft sentence for you to rewrite; specgate done adds the comment when it records a status.

A requirement is useful when two people could test it and agree on the answer. "Invoices are easy to get" is a wish; the paragraph above is a requirement.

2. A scenario makes it executable#

Every requirement has at least one Gherkin scenario, tagged with its ids:

gherkin
@REQ-007 @SCN-007
Scenario: A paid invoice downloads as a PDF
  Given a customer with a paid invoice INV-2041
  When they download it from the invoice page
  Then they receive "INV-2041.pdf" within 2 seconds

The scenario is the acceptance criterion. Gherkin, rather than prose, because a runner can execute it — and because the gate can check things about it that it cannot check about prose: that it has steps, that its keywords are ones Cucumber recognises, that an Outline has Examples.

There is nothing to declare. A test or a source file that mentions REQ-007 is linked to it:

js
// REQ-007 SCN-007 — a paid invoice downloads as a PDF
test("a paid invoice downloads as a PDF", async () => { /* … */ });

That is the whole linking model. Rename the test and the link moves with it; delete it and the requirement loses its test — which the gate then reports.

4. The matrix is generated, never edited#

docs/specs/traceability.md is one row per scenario: requirement, scenario, feature file, test, code, status. Every column is already said somewhere else, so it is computed from those sources:

  1. REQ-001requirement
  2. SCN-001scenario
  3. load_pack.featureGherkin
  4. UC-001use case
  5. CMD-001command
  6. AGG-001aggregate
  7. EVT-001event
  8. src/…code
  9. …steps.tstest
  10. Verifiedstatus
One row of the matrix, read as a sentence. Break any link and specgate check fails — which is what stops a specification from quietly becoming false.
ColumnComes from
Requirement, titlethe ## REQ-NNN sections in spec.md and capability specs
Scenario, feature file@REQ-NNN @SCN-NNN tags in features/**/*.feature
Test, codefiles that mention REQ-NNN
Statusthe specgate:trace comment, written by specgate done

specgate check and specgate done regenerate it before they judge. specgate validate — what CI runs — fails with matrix_stale when the committed copy no longer matches its sources. It stays committed because people and tools read it; it just has no author.

5. A status is a promise, and the gate collects on it#

The gate is strict, but it only asks a requirement for what its status promises:

StatusWhat it owes
DraftNothing — except that its scenarios cannot lie: no scenario without steps, no keyword Cucumber would read as prose, no Outline without Examples.
Past Draft (Approved, In Dev, In Review, …)A test that mentions it (and, on a hand-kept matrix, a scenario id).
Delivered (Implemented, Verified, Released)All of the above, plus every file it points at exists and its scenario has no <placeholder> steps.
DeprecatedIts files may be gone; nothing is owed.

This is why the gate is green on the day you adopt it and means more with every requirement you deliver. It is also why it never lies about its own reach — a green run says how many rows were exempt:

text
- Strict TDD gate: passed (3 row(s) exempt — still Draft, so not checked)

specgate done REQ-007 is how a requirement reaches a delivered status, and it runs the gate for that requirement first: if the gate would fail, nothing is written. (--no-check skips it, for the rare case the gate is wrong — say so in the pull request.)

6. One gate, one name#

There is exactly one gate: validate --strict. Everything runs it.

WhereWhat runs
Your machine, before a PRspecgate check — the gate, then your tests if a test command is configured
Closing a requirementspecgate done — the gate for that requirement, then the status
CIthe job specgate ci init writes — specgate check, with the project's JDK or Node set up
An agentthe /specgate:check command, the Stop hook, the harness

A gate that was slightly different in each place used to approve work in one and fail it in the next. Now a green in one place is a green everywhere.


Around the core#

Three things build on those six ideas. None of them is needed on day one.

Changes. A requirement that already shipped changes through a reviewable delta under docs/specs/changes/<id>/ — only what moves, never a copy. Archiving merges it into the spec tree and writes the feature files. → Reviewing changes

Domain packs. A versioned, schema-validated set of requirements and scenarios for a domain (multi-tenancy, payments, …), installed like a dependency, pinned by digest in .specops.lock, and upgraded through a reviewable diff. → Domain packs

The harness. Hands one pending requirement at a time to any coding agent in its own git worktree, runs the gate on the result, and closes the requirement only when it passes. It never merges. → The harness


The vocabulary#

TermMeaning
Requirement (REQ-NNN)One obligation of the system, in spec.md.
Scenario (SCN-NNN)A Gherkin example that demonstrates a requirement.
Capability specA docs/specs/<capability>/spec.md holding the requirements of one area.
The matrixdocs/specs/traceability.md, generated from the above.
The gatevalidate --strict; check adds your tests.
DeliveredA status of Implemented, Verified or Released.
ChangeA reviewable delta to requirements that already shipped.
PackA versioned domain model installed with specgate specops add.
HarnessThe unattended plan → agent → gate → done loop.

Next#