docs/facts.json
The register of facts that are stated in more than one place, and where each one really lives. facts-check reads it and fails the build when a restatement stops agreeing with its source.
It exists because R4 says a fact has one home, and reality says sometimes it cannot. The Node version belongs in .nvmrc, but every workflow that installs Node has to name it again. A count belongs where the things are counted, but a sentence somewhere wants to say it out loud.
What it is for
So a duplicate is declared rather than accidental. An undeclared restatement is invisible: nothing knows it exists, so nothing notices when it goes wrong, and it goes wrong quietly - the source moves and the copy keeps asserting the old value with total confidence.
A declared one is checked on every pull request.
What goes in here
One entry per fact: where it lives, and every place that repeats it with the pattern that extracts it.
[
{
"id": "node-runtime-version",
"what": "the Node version the dependency-free guards are pinned to",
"home": { "match": { "file": "standard/.nvmrc", "pattern": "^(\\d+\\.\\d+\\.\\d+)" } },
"claims": [
{ "file": ".github/workflows/checks.yml", "pattern": "node-version: \"(\\d+\\.\\d+\\.\\d+)\"" }
]
}
]
A home is either a file to read, a count of a glob, or a match that extracts the truth from another file. Each claim's capture group must equal it.
The failure mode it is built for
A pattern that matches nothing fails, and that is deliberate rather than lenient. If a surface gets reworded past its pattern, the check has stopped covering it - and a check that silently stops covering something is worse than no check, because the green build now means less than it did and nobody was told.
This fires in practice. Editing a quickstart to remove a version from a command was enough to make its declared restatement match nothing, and the build said so in the same run.
What does not go in here
A fact with one home. If nothing repeats it, there is nothing to declare. This file is for the exceptions, and it should stay short - a long one means R4 is being routed around rather than followed.
Prose that merely mentions a topic. A claim is a specific value with a pattern that extracts it. "The standard ships several guards" is not a fact you can check; the number of guards is.
A restatement you could delete instead. Declaring it is the fallback. Linking to the home is the answer, and it is available more often than it looks. The standard's own version was declared on nine surfaces and is now declared on one: the landing reads the newest release tag at runtime, and the prose that named a number says which release line it is on instead. Eight entries left this file by deleting the copy, not by checking it harder.
Decisions behind it
- Declaration, not prohibition. Banning restatement outright was the simpler rule and it is unenforceable: surfaces exist that must print a value. Declaring them makes the duplicate visible and checked instead of forbidden and present anyway.
- A pattern that stops matching is a failure, not a skip. The lenient behaviour was considered and rejected in one sentence: it turns the whole file into decoration the first time somebody rewords a paragraph.
Reference
- Status. Optional - part of the
coreprofile. - How it arrives.
fill-from-repo- scaffold the shell from the standard, then author the body from THIS repo's reality - never blind-copy the example prose (ARCHITECTURE, PRODUCT, specs) - Shipped form. None - this one is written into your repo during alignment, from your repo's own reality.
The rule that requires it
Documents are living: they MUST be updated in place.
