Files
hq/00-META/checks/README.md
T
jochen daa9f171ae
mesh/merge-gate pass: the change touches no module of the mesh's graph
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
Issue reports carry only opened: the tracker holds every status, so no copy can drift
The import of slice 3 (ADR 0275) was checked against every folder: no report changed since the
commit it read, and the tracker holds each report's status, owner, fix, design amendment, replay
and closing day. The keys are removed from all 361 reports, cycle.py refuses them in any report,
and the playbooks, skills and READMEs that still pointed at them name the tracker instead.
2026-10-10 17:44:18 +02:00

110 lines
8.9 KiB
Markdown

# Checks
```
python3 00-META/checks/records.py structure: links, citations, supersession, topics
python3 00-META/checks/index.py every record's topic is in the reading order, and no list of records is stored
python3 00-META/checks/index.py --print the records, by topic, in reading order (generated, never written)
python3 00-META/checks/words.py the glossary's retired words are not used, no daemon's line is written, and no word is defined twice
python3 00-META/checks/words.py --list tools the words the catalogue's copy must list
python3 00-META/checks/words.py --list lines the daemons' lines the catalogue's copy must list
```
Non-zero exit on any problem, so it can be a gate rather than a report.
**Why this exists.** Until now nothing in this repository was verified by anything but reading,
which is how a superseded decision stayed live in the constitution for days and in
`01-to-be/README.md` alongside it. Both were found by a person looking. `how-we-build` §5 says
*an unenforced rule is indistinguishable from a wrong one, and costs more, because people
believe it* — this repository was carrying several.
**Every check here failed on something real before it passed.** A check that has never failed is
indistinguishable from one that cannot.
| Check | Asserts | Found |
|---|---|---|
| `links` | every relative link resolves | — (run ad hoc during authoring; now permanent) |
| `rests-on` | `decisions:` and `extends:` name records that exist and are **accepted** | the class behind both incidents |
| `live-citation` | a governing document citing a **superseded** record names its replacement in the same paragraph | `01-to-be/README.md` citing ADR 0022 as live guidance |
| `supersession` | if A says it was superseded by B, B says it supersedes A | ADR 0012 never declared that it superseded 0011 |
| `numbering` | the number in the filename is the number in the heading | — |
| `topics` | every record names a topic of the reading order in `02-DECISIONS/README.md` | — |
| *(index.py)* | the reading order is written, every record's topic is one of it, and the README stores no list of records ([ADR 0248](../../02-DECISIONS/0248-the-decision-index-is-generated-when-it-is-read-and-never-stored.md)) | a stored list made every two open decisions conflict ([issue 297](../../04-ISSUES/297-two-open-decisions-always-conflict/00-report.md)); it failed on the README of `main` before the list was removed |
| `status-vs-code` | a to-be document naming specific code is not still `designed` | **ten documents**, several with a *What was built* section, describing lab-proven code |
## What is deliberately not checked
- **`02-DECISIONS/` and `01-RESEARCH/` may cite superseded records freely.** A decision record
discusses history; research records what was observed. Flagging those would produce noise on
correct documents, and a check that cries wolf gets suppressed — which costs more than not
having it.
- **`03-DESIGN/00-as-is/` may rest on a superseded record.** It describes what runs, and what
runs was built under whatever was decided at the time
([ADR 0006](../../02-DECISIONS/0006-the-substrate-and-the-control-plane.md):
*as-is describing a superseded decision is exactly what as-is is for*).
- **Whether a citation's prose is still true.** Only whether the record it points at is live.
A document can cite an accepted record and describe it wrongly, and nothing here notices.
So "governing" means `00-META/` and `03-DESIGN/01-to-be/` — the documents that tell somebody
what to do.
## Adding a check
State what incident it would have caught, and make it fail before you make it pass. A check
whose failure has never been observed is a guess about its own correctness.
## cycle.py
The development cycle, checked ([ADR 0080](../../02-DECISIONS/0080-the-development-cycle-is-checked.md)):
a to-be design names a decision, an in-progress/implemented design names its owning code, a graduated
research overview says what it became, and no two issue records share a number (issue 155 — the
number is how a record is cited, and `main` lags every open pull request, so two people reading it
allocate the same one). An issue report carries the day it was `opened:` and no status key — `status:`,
`located-in:`, `fixed-by:`, `amended-design:`, `replay:`, `replay-none:` or `resolved:` — because an issue's
status lives in the issue tracker ([ADR 0275](../../02-DECISIONS/0275-the-mesh-numbers-and-tracks-its-issues-in-a-module-of-its-own-and-hq-keeps-only-the-narrative-of-a-design-or-governance-issue.md));
it failed on all 361 reports that carried them before they were removed. The rules on an issue's status this
check held until then — an owner for a located issue, a fix for a resolved one, and a core issue's `replay`
or `replay-none` ([ADR 0237](../../02-DECISIONS/0237-a-change-is-judged-against-the-mesh-that-runs-before-it-merges-on-the-build-seat.md))
— are refused at the write by `issue-tracker.move`. And an accepted record that puts work
off — "until renamed", "left for later", "not decided here", "postponed", "future work", "deferred", "a later
change" — names the issue that tracks it, as tracking it ("tracked by issue N", or "issue N tracks"), in that
paragraph or list item, or in a dated progressive insight that speaks about it: one in the same section, or one
that quotes it. An issue cited for another reason tracks nothing, and one insight does not excuse every deferral
in a record. A rejected option is not work put off and is not held, judged by its `##` section, so an option under a
`###` subheading of "Considered Options" is skipped too
([issue 354](../../04-ISSUES/354-a-decided-rename-was-never-scheduled-and-the-operator-believed-it-live/00-report.md):
ADR 0244 deferred the code renames with no issue, and two days later the operator believed them done). Held to
it: a record dated from 2026-10-09, and an older one corrected under the rule. Measured from ADR 0244's date
it found deferrals without an issue in ADR 0241, 0244 and 0270; held from 2026-10-09 it failed on 0270 and 0273
(whose "not decided here" sits beside a citation of issue 340 that tracks none of it), and on 0244's four
deferral places once its insight brought it under the rule — on three of them while the insight, in §6, named
the issue without quoting them — before each named its issue (355, 357, 354). `python3 00-META/checks/cycle.py`
## words.py
One word per thing, checked ([ADR 0244](../../02-DECISIONS/0244-the-mesh-is-described-in-domains-and-one-word-names-one-thing.md)).
It reads the glossary's *Not:* lines (the retired words, each with its scope) and *Identifier until
renamed* lines, and fails on a retired word or a bare identifier in running prose — what is left once code,
quotations, struck-through text, link targets, comments and frontmatter are taken out — in `00-META/`,
`03-DESIGN/`, `AGENTS.md`, `README.md`, and research and issues dated from 2026-10-07. Decision records are
never checked. It also fails when a glossary head word heads two entries or is also retired.
[`words-allowed.md`](words-allowed.md) names a document that could not be reworded at once, with a date, or
a graduated research effort kept as written. With `MESH_CATALOG_DIR` set it compares the catalogue's copy
of the tools' retired words (`retired-words`) with the glossary.
It also fails on a **daemon's line** ([ADR 0272](../../02-DECISIONS/0272-the-operator-reaches-the-controller-with-mesh-cli-on-every-node-and-a-node-is-the-terminal-only-where-no-agent-runs-as-its-operator.md),
its insight of 2026-10-09): the operator types one command, `nox`, and a document names a `nox` line, never the
controller's or the node-engine's binary followed by one of its commands. The lines are the check's own list
(`DAEMON_LINES`); a command that is a line only with an argument after it is matched as a line is written. Looked
for in the same documents, from 2026-10-09 for research and issues, in the document's own words — code blocks and
spans included, quotations and block quotes taken out. With `MESH_CATALOG_DIR` set it compares the catalogue's copy
(`retired-lines`) with the list.
It failed on something real before it passed: 581 uses of retired words in 63 documents on its first run, besides the glossary itself and research 034 —
"the host" for the node-engine in 30 designs, "control plane" in 10, the glossary's own entry for the tool runner —
and two glossary contradictions (the "console", the deprecated broker's seat), fixed in the change that
added it. A retired word's plural (`s` or `es` on its last word) is matched as the word: until 2026-10-07
it was not, so "alerts", "rollouts" and "the hosts" (the node-engines) passed while their singulars
failed; the plural failed on 15 uses in 10 documents before it passed. A verb that reads like a plural
("a node hosts") is a finding too, and is reworded. Homonyms (*plan*, *gate*, *tier*, *ask*, *store*, *record*, *check*) are not checked by it: a
word list cannot tell one sense from another, so they are reviewed.