Getting started

Write your first chapter, understand every part of it, and get the gate green.

This walks one complete chapter part by part. The chapter is real — it ships at docs/example/ and goldspec's own test suite runs the gate over it on every run, so what you read here is what the gate accepts.

You need goldspec installed and a directory. Nothing else — no project, no test suite, no language.

mkdir -p spec/diagrams

A spec is markdown a person reads. There is no vocabulary to import and no module a runner has to collect.


1. A chapter is a process flow

Not a subsystem, not a component, not a team's area. A chapter is one process, from start to done, and it contains many user stories — one for every step in it.

That is the single most important sentence on this page, and getting it wrong produces a chapter called the payments service, which accepts any sentence about payments and grows until nobody reads it. A process has a start, an end, and steps in between; most sentences are not one of its steps, so most sentences are refused on sight.

The chapter opens with the process's own story:

As a customer I want to place an order and be told it was accepted, so that I know I will be charged for something that is actually coming.

Every step is a user story too

A node in the flow is something a person does for a reason — so it takes the same shape:

As a customer I want to be told a line is short before my card is touched, so that I am not refunding money I should never have paid.

Part What it is for What goes wrong without it
As a \<role\> who this step is for a step with no role is a task, and tasks have no acceptance
I want \<something\> what they are trying to do, in their words written in the system's words it becomes a design note and stops being arguable
so that \<goal\> why the step is worth performing nothing can say the step is finished — only that code for it exists

The role can be a machine, and usually should not be. As a firewall I want to drop the packet is legal. It is also a sign you have stopped asking who is being helped — this is technology built for people, not for its own sake, and a flow whose every actor is a component has quietly become an architecture diagram. Ask who is worse off if the step never happens. If the answer is a component, look one level out for the person behind it.

The three things that are not the story

All three read as diligence, and all three are the usual first-chapter failure:

  1. The solution. As a customer I want my basket held in Redis with a 30-minute TTL. That is an implementation choice. The story is what the customer wants; how you do it is the design, and the design belongs where it can be argued with and replaced without touching the story.
  2. The incident. On 12 March a duplicate charge occurred because the retry handler… is history. Put it in the commit message, which is version-anchored and stays true after the state it describes has moved on. A story that narrates an incident has to be rewritten every time the incident recedes, and nobody rewrites it, so it reads as current forever.
  3. The status. …(done, verified in staging) is an authored verdict. A chapter never carries status. What is proven is derived from a run, and a Status: line is true because somebody typed it — which is the failure this whole method exists to remove.

The test is mechanical: if deleting the sentence would change what the system must do, it belongs in the chapter. If it would only change what somebody knows about the past, it belongs in a commit message.


2. The hierarchy, and why you cannot get the altitude wrong

A flow can turn out to be a step inside a larger flow, and that costs you nothing. It is the normal case. Interview a business and they will tell you where they think their process starts; half-way through you discover it begins two departments earlier. The flow you drew does not get thrown away — it acquires a parent, and becomes one node in it.

So a step that is itself a process gets its own file, and the file name carries the drill-down:

File ids inside it
chapter-01-ordering.md ch1-1, ch1-2, …
chapter-01.3-charging.md ch1-3-1, ch1-3-2, …

chapter-01.3-charging.md would be the drill-down of node ch1-3. Every id inside it is prefixed ch1-3, and in the parent that node is drawn as a sub-flow[[…]] :::subroutine — so a reader knows to follow it.

(Illustrative. The worked example has no drill-down: it fits on a page, which is the whole test. A node drawn as a plain action is a node that did not need one.)

A sub-chapter carries all the same sections. It is a flow like any other; nothing about it is abbreviated.

When to split, and when to fold

One rule, and it is a legibility rule: a flow should print legibly on one sheet of paper.

It applies to the expansion as well as the drawing, and that half catches more. A node whose story and reasoning run past a page is a node hiding a flow — its steps had nowhere to be, so they became sentences. If one of your nodes is turning into an essay, you have found a sub-process, not a badly-written story.

How big is too big, in numbers

Nobody has measured this, so what follows is a working heuristic and not a rule the gate holds you to:

Nodes What it usually means
1–3 too thin to be its own flow — fold it into its parent
4–9 the ordinary case
10–14 look hard for one step that is really a process
15+ there is a sub-flow in there, and probably two

Applied to the drawing. The expansion of a ten-node chapter runs to several pages and that is normal — what the rule asks of the expansion is that no single node needs more than about a page, not that the chapter fits on one.

Treat all of it as a prompt to look rather than a threshold to obey. A ten-step process that genuinely has ten steps is a ten-node chapter, and splitting it to hit a number would be the tool choosing the shape of somebody's business.

Where the hierarchy stops

Downward, it stops when the business stops saying "oh, I forgot to tell you about…"

Upward, it stops somewhere different in kind: when the steps stop being things a person does and become quantities to tune toward a goal. A flow made mostly of parameters rather than stories is at the ceiling. That is a thing you notice rather than decide.


3. Draw the flow first

Write the diagram before any prose. It is the cheapest part to change and it finds the most, because a flow will not let you leave a step out — a box has to connect to something.

no

yes

START · somebody with a basket and a card

ch1-1 · take the order

ch1-2 · is every line actually available?

REFUSED · said before the card is charged

ch1-3 · charge once

ch1-4 · tell the customer

END · an order the shop can act on

mermaid source for this flow
flowchart TD
    START([START · somebody with a basket and a card]):::terminal
    TAKE[ch1-1 · take the order]:::process
    STOCK{ch1-2 · is every line actually available?}:::decision
    SHORT[/REFUSED · said before the card is charged/]:::bad
    CHARGE[ch1-3 · charge once]:::process
    TELL[ch1-4 · tell the customer]:::process
    DONE([END · an order the shop can act on]):::good

    START --> TAKE --> STOCK
    STOCK -->|no| SHORT
    STOCK -->|yes| CHARGE --> TELL --> DONE

What the gate checks:

What is advice, and worth taking:

The drawing is what finds the defects. In one real conversion, three defects had sat undisturbed for as long as they were prose and became obvious the moment each had to be a box with edges — including an activity happening twice under two names, which is how a step gets done twice or not at all.

The shapes, and how to draw the ones that are not boxes

Mermaid flowchart, and seven roles. Copy this block into your first chapter and you have the whole dialect:

Role Mermaid What it is
start / end ([…]) :::terminal where the flow begins, and where it finishes
action […] :::process a step somebody performs
decision {…} :::decision a question with named arms
sub-flow [[…]] :::subroutine a step that drills down into its own chapter
reached ([…]) :::good a successful ending
refused [/…/] :::bad an ending that is a refusal, and is on purpose
unknown / parameter >"…"] :::unknown / :::parameter the flag shape — not a step

An unknown or a parameter is attached to the node it affects by a dotted edge, and the label goes between the dots:

exposes

tuned by

ch1-2 · is every line
actually available?

ch1-3 · charge once

ch1-U1 what happens to a basket
abandoned mid-charge

ch1-P1 how long a reservation
is held

mermaid source for this flow
flowchart TD
    STOCK{<b>ch1-2</b> · is every line<br/>actually available?}:::decision
    CHARGE[<b>ch1-3</b> · charge once]:::process

    U1>"<b>ch1-U1</b> what happens to a basket<br/>abandoned mid-charge"]:::unknown
    P1>"<b>ch1-P1</b> how long a reservation<br/>is held"]:::parameter
    CHARGE -.exposes.-> U1
    STOCK -.tuned by.-> P1

    classDef decision fill:#e9eef5,stroke:#2a78d6,color:#0b0b0b;
    classDef process fill:#e9eef5,stroke:#2a78d6,color:#0b0b0b;
    classDef parameter fill:#f4f2ee,stroke:#8a6212,color:#0b0b0b,stroke-dasharray:2 2;
    classDef unknown fill:#f4f4f2,stroke:#898781,color:#0b0b0b,stroke-dasharray:2 2;

Draw an unknown and a parameter; do not draw an NFR. An unknown must appear in the diagram and in ## Open unknowns — the gate checks that neither has it alone. A parameter is drawn for the same reason, so a reader can see which step it tunes. An NFR is a constraint rather than a thing in the flow, so it is not drawn at all.

A flag node never gets a ### block. Only nodes do. An unknown's prose is its bullet in ## Open unknowns; a parameter's and an NFR's prose sit inside the block of the node they affect (§5).

Colour is never the only encoding, and this dialect needs the help: start / end and reached are both ([…]), so in greyscale they are the same box. The text is what separates them — shout START, END and REFUSED in the label, as the example does. A reviewer may be colour-blind and a printed page is always grey.

Ship a legend, and the classDef lines under it. Both go inside the same mermaid fence as your flow. The legend is a subgraph holding one sample node per role; its nodes carry short names of their own — LT, LA, LD — and no ids, which keeps them out of the node-block check, and they connect to nothing, which the start-and-end check does not mind. The classDef lines are identical in every chapter, and a class you use without defining is a fault the gate will name.

This is the whole block, drawn and then given as source. Paste it into your chapter and you have the dialect:

legend — shape carries the role

START / END — named in caps, so it reads in greyscale

action

sub-flow — drills down

decision

reached

REFUSED — a different shape, so it reads in greyscale

parameter — a value to discover

open unknown — a decision to take

mermaid source for this flow
flowchart TD
    subgraph LEGEND["legend — shape carries the role"]
        direction LR
        LT([START / END — named in caps, so it reads in greyscale]):::terminal
        LA[action]:::process
        LS[[sub-flow — drills down]]:::subroutine
        LD{decision}:::decision
        LG([reached]):::good
        LB[/REFUSED — a different shape, so it reads in greyscale/]:::bad
        LP>parameter — a value to discover]:::parameter
        LU>open unknown — a decision to take]:::unknown
    end

    classDef terminal fill:#eef0ee,stroke:#52514e,color:#0b0b0b;
    classDef process fill:#e9eef5,stroke:#2a78d6,color:#0b0b0b;
    classDef decision fill:#e9eef5,stroke:#2a78d6,color:#0b0b0b;
    classDef subroutine fill:#e9eef5,stroke:#2a78d6,color:#0b0b0b;
    classDef good fill:#e2f2e2,stroke:#0ca30c,color:#0b0b0b;
    classDef bad fill:#f7e1e1,stroke:#d03b3b,color:#0b0b0b;
    classDef parameter fill:#f4f2ee,stroke:#8a6212,color:#0b0b0b,stroke-dasharray:2 2;
    classDef unknown fill:#f4f4f2,stroke:#898781,color:#0b0b0b,stroke-dasharray:2 2;

A sub-flow node may point at a chapter you have not written yet. Nothing requires chapter-01.3-charging.md to exist before you draw ch1-3 as a sub-flow. Draw the process you have; the file follows when the node grows past a page. In the parent, a sub-flow node keeps an ordinary ### block with In / Held / Out stated at the parent's altitude — what the whole sub-process takes and leaves, not what its steps do.

Node labels may carry markup, and usually should. The snippets on this page are written plainly so the shape is visible, but real labels get long: <b>ch1-1</b> sets the id off from the text and <br/> wraps a label that would otherwise stretch the diagram into one very wide row. The worked example does both. Nothing is checked here — it is the legibility rule again, applied to the drawing.


4. ## Elements — the flow's data, in one place

Every piece of information the flow moves is defined once, at the flow, in six columns exactly. Rows are read positionally, so a table with a different header is not read at all:

| Element | Type | Card. | Domain | Enforced | Provenance |
|---|---|---|---|---|---|
| `basket` | list<line> | 1 | at least one line | yes | authored |
| `line` | record | 1..n per basket | — | — | authored |
| `line.quantity` | integer | 1 per line | 1 or more | yes | authored |
| `order` | record | 0..1 | — | — | derived |
| `order.reference` | string | 1 per order | unique across all orders | yes | generated |
| `reservation` | record | 0..1 per order | — | — | derived |
| `refusal` | record | 0..1 | names one short line | no | derived |

Every element the node blocks in §5 move is in that table, and that is not a coincidence — it is the rule. A refusal is an element like any other: it is a thing the flow produces, it has a shape, and a node that leaves one has to be able to name it.

The element's name is its id. It is unique across the whole corpus, so order.reference means one thing everywhere and nothing needs a lookup table.

Type is open, and deliberately — it is the only column that is. This is the logical model, the one a business person can read, so write what the thing is in words your business would use: record, string, integer, date, money, list<line>, set<rung>. It is not a database type and there is no closed set to learn. VARCHAR(20) is the physical model, which is a later conversation with different people in the room.

Card. counts instances of the element, and names a parent whenever the count is per something1 per line, 0..1 per order, 1..n per basket. Where the element is not inside anything, a bare 1 or 0..1 is right and names no parent: an order either exists or does not, and there is nothing for it to be per. Legal forms are 1, 0..1, , and <range> per <parent> — so 1 per line, 0..1 per order, 1..n per basket. A bare 2 is refused: write 2..2 per device. A collection's size is part of its type, so list<line> is one value and never carries 0..n.

Domain is the set of legal values. Enforced says whether anything actually rejects one outside it:

Enforced Means
yes a value outside the domain is rejected by something you can point at
no the domain is stated and nothing rejects a value outside it
no domain is stated — and then Domain is too

Something you can point at may be somebody else's. A statute, a scheme, a regulator or a payment processor rejecting an out-of-domain value is enforcement, and yes is right — it does not have to be your code. What makes it no is that nothing rejects it, wherever that thing would live. This matters most in compliance-shaped processes, where most domains are enforced by somebody who is not you.

That last equivalence is what makes the column falsifiable. no is an honest and common answer: it says the design intends something the code does not yet hold you to, which is a gap you can see rather than one you cannot.

Provenance is a closed set of four. measured and assumed are refused:

Value Means Read it as
authored somebody decided it ask its author
observed read off a running system run it again
derived computed from other elements recompute it
generated minted from randomness if lost, it is gone

The question the column answers is if this were lost, could you get it back — which is why a generated passphrase is not derived.

The two halves of the section carry bold run-in labels — **Defined here.** above the six-column table and **Imported.** above the import table, each free to carry a sentence of explanation after it. There is no ### heading for either, and no ## Imports section: a fifth ## would fail the section-set check.

Elements you did not define

A flow consumes things its neighbours own. Name them and stop:

**Imported.**

| Element | Defined by |
|---|---|
| `customer` | ch3 |

The Defined by cell names a corpus — a chapter, a spec directory, a product — in whatever words a reader would recognise. The row itself is what makes the element known, so naming a chapter you have not written yet resolves and does not fault. That is often the honest entry: a flow pretending to define its own customer is lying, and an import pointing at work still to come is a fact.

siblings is for the other case — an element you did not import, defined in another spec directory of the same corpus. Without it the gate sees half a corpus and cannot tell a correct reference to a neighbour from a reference to nothing, so it would report yours as undefined. Paths in siblings resolve relative to the goldspec.toml that names them.

A dotted name does not oblige its parent to have a row. order.reference may be defined with no order beside it — define the parent when the flow moves the parent, and not to satisfy the notation.

An import names the corpus and nothing else. Restating the type or the provenance would be a second statement of a fact you do not own, and the gate refuses it. Where the corpus spans directories, say so with siblings (below) rather than letting the gate guess — a gate seeing half a corpus cannot tell a correct reference to a neighbour from a reference to nothing.


5. ## Nodes — the story, and the information it moves

One ### block per node. The node carries its story, then what it takes, holds and leaves:

### ch1-2 · is every line actually available?

> **As a customer I want to be told a line is short before my card is touched, so that I am not
> refunding money I should never have paid.**

| | |
|---|---|
| **In** | `order`, `line`, `line.quantity` |
| **Held** | `reservation` |
| **Out** | `order`, or `refusal` |

In is what the step needs to begin. Out is what it leaves for the next one. Held is the one people skip: information that exists only inside this step — a reservation between the availability check and the charge, an idempotency key, a working total. It is neither an input nor an output, and writing it down is what lets somebody check that a step actually has what it needs.

Held counts. An element that appears only in one node's Held and nowhere else is properly defined and properly used — the gate's "defined here and no node moves it" fault does not fire on it. If Held did not count you would have to fake an In or an Out for every one of them, which would lose exactly the thing the row is for.

A node references elements; it never defines them. No type, no cardinality, no provenance in a node block — those live in ## Elements, once. This is the rule that stops the format growing a second home for every element more than one node touches, and the gate enforces it.

is a legal entry. A step that holds nothing says so.

A cell is a list of element names, and or is allowed in it. Writing order, or refusal says a step leaves one or the other, which a comma alone would not. The gate reads the names and ignores the words between them, so write what a person would read.

Everything a node block holds, in order

### ch1-2 · <the step, named as the flow names it>

> **As a <role> I want <something> so that <goal>.**       <- the story

| | |                                                      <- In / Held / Out
|---|---|
| **In** | … |
| **Held** | … |
| **Out** | … |

<one paragraph: why the step is the way it is>             <- the closing argument

**Parameter `ch1-P1` — …**   <- parameters and NFRs, last
**NFR `ch1-N1` — …**

Parameters and NFRs live at the foot of their node's block, and nowhere else. There is no ## Parameters section and no ## NFRs section — adding one fails the section-set check, and the fault names the wrong set without telling you where the content should have gone. A parameter or an NFR belongs to the step it constrains, so it sits with that step.

A constraint on the whole flow goes between the diagram and ## Elements, in the same bold form:

```mermaid … ```

**NFR `ch1-N1` — the processor's reconciliation window closes at 02:00.** …

## Elements

Some constraints genuinely are not one step's — a regulatory clock that binds the whole process is not the property of whichever node happens to start it, and hanging it on one node to satisfy the format is bending the model to fit the tool. Flow-level and node-level NFRs share one number series per chapter.

The four kinds of subject

A subject is anything that carries an id, and there are four kinds. Three of them hang off the node they affect. The discriminators are worth learning once:

Kind Id Known? Has a model? Waiting on
Story ch1-2 the want is known proof it is done
Open unknown ch1-U1 the question is; the answer is not no a decision
Parameter ch1-P1 it exists; the value does not yes a discovery
NFR ch1-N1 fully known, external no to be accounted for

Unknown or parameter? Both are not-knowing. An unknown is settled by deciding; a parameter is settled by measuring, and what makes it a parameter is that you can write the model today — a statement of what would confirm the value. How long is a reservation held has a model (the distribution of reservation-to-charge times bounds it from below; stock held-and-abandoned bounds it from above), so it is ch1-P1. What should happen to a basket abandoned mid-charge has no model, only a choice, so it is ch1-U1.

A parameter that nothing has measured shows on the board as unvalidated — the honest word for a constant nobody has tested. That word is derived and you never type it into a chapter. Write the model; whether it has been run is a fact about runs, and a parameter carrying its own status in the prose would be the authored verdict this method exists to remove, wearing a different word.

What is an NFR? An external imposition that restricts how the process can work, rather than something it enables, which the flow has to account for:

**NFR `ch1-N1` — the processor's reconciliation window closes at 02:00.** External, known, and
restricting: a charge submitted near it may settle on either side of the boundary, so nothing
here may assume same-day settlement. It is not this shop's to negotiate.

A plain bold run-in paragraph, never a blockquote — the blockquote is reserved for a story, which is how a reader tells the two apart at a glance. Parameters take the same form.

It gets an id even though nothing is undecided about it, and the reason is different from every other id here: so that a room full of people can point at the same thing. A second reason shows up as soon as you write two — constraints in tension become visible. In prose, two constraints fifty words apart read as one continuous line of reasoning.


6. ## Open unknowns — and why there is no backlog

An unknown lives in both of its homes: a bullet here, and a node in the diagram attached to the step it affects. The gate checks that neither has it alone.

- **ch1-U1 — what happens to a basket abandoned mid-charge.** The card was submitted and no
  response came back. The order is neither placed nor refused, and the customer has closed the
  tab. Settling it needs the processor's reconciliation behaviour, which nobody has read yet.

This is what replaces a backlog. An unknown is a question anchored to a step; an unproven subject is the work item. Anything anchored to nothing is not undefined — it is uncommitted, and it should be dropped rather than filed. A side list of open questions is how a backlog regrows under a new name, and it goes stale exactly the same way.

Write what would settle it. We should look into caching is not an unknown. Whether the processor's reconciliation window closes before ours does — needs their documentation read is one, because a reader knows what to go and do.


7. ## Glossary — the words this flow uses differently

Last section, and it is not a dictionary of every noun. It is for words that mean something specific to this process, and likely mean something else in a neighbouring one or in ordinary English:

| Term | Meaning |
|---|---|
| Order | A basket the shop has accepted responsibility for. It has a reference from the moment it exists |
| Short | A line the shop cannot fill in the quantity asked for. Not the same as *out of stock*, which is a property of the item rather than of this order |

Order earns a row because the moment a basket becomes one is exactly what this chapter is about. Short earns one because a neighbouring flow uses it to mean something else.


8. The whole file, in order

Four ## sections, and the gate enforces both the set and the order:

# Chapter 1 — <the process, named from a person's side>      <- H1 title
> **As a <role> I want <something> so that <goal>.**          <- the flow's story
<a paragraph or two of context, optional>
```mermaid … ```                                             <- the flow
## Elements
## Nodes
## Open unknowns
## Glossary

The title, story and diagram come first, in that order, above the sections. A reader looking for the glossary should never have to hunt for it.

Small things that are part of the format, so you do not have to guess them:


9. Run the gate

$ goldspec spec
goldspec: conventions from goldspec.toml
goldspec: ids namespaced 'ch', flows in diagrams/chapter-*.md
0 fault(s) in spec

Exit 0 clean, 1 with faults. Every run names the conventions it judged by — read that line first when a fault surprises you, because every message the gate emits is phrased as a verdict on your spec, and being handed the wrong strings is the one cause it cannot phrase.

What a refusal looks like

Faults name the file, the id where one applies, and what to do:

chapter-01-ordering.md#ch1-2 moves `coupon`, which no element list defines here,
  imports, or resolves in a sibling
chapter-01-ordering.md#ch1-5 is drawn in the flow and has no node block — the
  diagram is the authority and the expansion is what a reader follows it into
chapter-01-ordering.md: `refund` is defined here and no node moves it — delete
  the row, or the step that needs it is missing
chapter-01-ordering.md: sections are ['Elements', 'Nodes', 'Glossary', 'Open
  unknowns'], not ['Elements', 'Nodes', 'Open unknowns', 'Glossary']
chapter-01-ordering.md: the import of `customer` says more than where it is
  defined — an importing chapter names the corpus and nothing else

The last one catches a habit rather than a typo: an import row names the corpus and stops, because anything more is a second statement of a fact you do not own.

Expect a few rounds on your first chapter. Every convention the gate enforces is on this page — if you hit one that is not, that is a defect in this page and worth telling us about.


10. Your conventions, in one file

Ids namespaced REQ or sh, flows in a directory named to suit you: all yours. Say so in a goldspec.toml, beside your chapters or beside the spec directory:

[conventions]
namespace = "ord"              # ids read ord1-1, ord1-2
flow_dir  = "flows"            # where the process flows live, relative to the spec
flow_glob = "story-*.md"       # how a flow file is named
data_definitions = true        # hold chapters to the six-column table
node_format = true             # hold chapters to the node format
siblings  = ["../shared/spec"] # more of this corpus, read for what it defines, never checked

Every key is optional, and the defaults are our habits: namespace = "ch", flow_dir = "diagrams", flow_glob = "chapter-*.md", data_definitions = true, node_format = false, no siblings. A project that took all of them writes no file at all.

Write only what differs. A key restating a default is a copy, and it will not rot at the same rate as the original — if a default ever moves, your file silently disagrees with it. The example's whole conventions table is one line.

The file name decides the id prefix, and it is checked. chapter-01-ordering.md under namespace ch gives ch1, so every id in it must read ch1-…; chapter-01.3-charging.md gives ch1-3. Get the number wrong and every id in the file is reported as not namespaced to its chapter.

data_definitions and node_format are about different things and both apply. The first holds the six-column element table to its rules — the columns, the cardinality grammar, the closed provenance set — wherever that table lives. The second holds the chapter to the node shape. Under node_format = true the table sits under ## Elements instead of ## Data definitions, and data_definitions still governs what is in it.

The command reads this file and so does the library, so a project running both cannot end up with two gates over one spec that disagree.

Put it beside your chapters if one repository holds more than one spec — a file at the root is found for every spec directory beneath it, which is right for a project with one spec and wrong for a project with two.

node_format is off by default and that is a phase. Every corpus written before this format exists in the older shape — one ## Input → process → output and one ## Data definitions per chapter — so a chapter that has not converted is reported on rather than failed. A new project has nothing to convert, so a new project turns it on, which is what docs/example/goldspec.toml does.


11. What a spec does not hold

Not this Why Where it goes
Status, percent complete, Status: accepted typed, therefore true; the superseded ones read exactly like the current ones derived from a run
A backlog or task list grows endlessly, stale the moment it is written an unproven subject is the work item
What went wrong on a given day history, and it stops being true the commit message
A second copy of a decision two homes for one fact, drifting from the moment either moves one subject, cited by id
A mapping table of old ids to new an id resolving only through a lookup is a second name for something that already has one rewrite the citations

A spec states the plan, not the state. A subject says what the design is; whether you have got there is what a run reports, and it is never folded back into the claim. An unproven subject is not a failure of the spec — it is the work, visible.


Where to go next