Treating documentation as a build artefact
This manual is compiled, not written into a CMS. One registry holds what may be claimed and with what status; the plan owns every canonical URL; the prose is data rather than code; and a gate refuses the build on a broken link, a thin page, a duplicate search intent or a capability described as working when the registry says it is not.
One registry decides what may be claimed#
docs-source/facts.py is the only place a capability's status is recorded, and it records the evidence alongside it — the module or script that proves the claim. A page whose subject appears there does not get to choose a friendlier word. A page whose subject does not appear there has to say the nearest honest thing and say that it is doing so.
The same file carries the numbers a page is allowed to quote, the canonical name for each thing, the names that have been retired, the protocol version strings the generated machine descriptions carry, and a written list of claims that may never be made. Putting all of that in one importable module rather than in a style guide is what makes the rules checkable rather than merely stated.
Many outputs, one source#
| Output | Who reads it | What it must never contradict |
|---|---|---|
Rendered HTML under webapp/docs/ | A person, and a crawler | The registry's status for the capability it describes |
| Markdown alternates | A retrieval system that prefers plain text | The HTML — same words, different envelope |
Documentation manifests under webapp/docs-data/ | A machine enumerating the corpus | The set of pages that actually exist |
llms.txt | An agent looking for a map | The public surface only — no private path may appear in it |
| The OpenAPI, Arazzo and AsyncAPI descriptions | A developer or a code generator | The protocol versions recorded in the registry |
| Sitemaps and the change feed | Search and AI crawlers | The published page set, in both directions |
Generating all of them from one source is not tidiness. It removes the failure where a capability is corrected on the page and left standing in the manifest, which is the version a machine reads and a person never sees.
The gate, and what each check is for#
| Check | Catches |
|---|---|
| Plan-to-content join | A planned page nobody wrote, and prose for a page nobody planned — there is no default body, so an unwritten page is an error rather than an empty page that quietly ships |
| Shape validation | A missing part, a page under the 420-word floor, an answer paragraph outside 22–95 words, fewer than three sections or two questions |
| Duplicate detection | The same canonical URL, the same page id, or two pages claiming one primary query |
| Link graph | A link to a page that does not exist, and a page nothing links to |
| Language | Machine-writing phrases, adjective tics past their budget, and one term taking more than 2.2% of a page it is not about |
| Honesty | A retired product name, a claim on the forbidden list, and a capability the registry calls foundation or not-yet described as working |
| Leakage | Anything shaped like a key, a token, a real address or a real telephone number |
| Kind rules | A technology page that never answers whether Connect uses the technology; a workflow page with no stage chain |
It fails closed, and its exit status is the number of distinct failure classes rather than the number of problems, so a hundred instances of one mistake read as one mistake. The corpus-wide checks are the ones a single page cannot perform on itself: nothing about one page reveals that another page already claims its query.
Counting is measured, never reported#
Progress is measured by importing each content module and counting the dictionary it exports, because that is what the build itself does. A writer's account of its own work is not evidence: the first batch to report twenty pages finished had written six, and an edit that replaced instead of appending shows up in the count and nowhere else.
- The count is of pages that import. A module with a syntax error counts zero, loudly, rather than counting what was intended.
- A single-module run imports only that module, so one writer's half-saved file cannot fail another writer's check.
- Template detection is deliberately corpus-wide and generous: it refuses an opening formula shared by more than twenty pages, a section heading shared by more than thirty, and a corpus where over a third of pages contain nothing but paragraphs.
What a build cannot check#
Every check above is a check of form. A page can pass all of them and still be wrong, because no gate can read a sentence and know whether it is true. What the machinery buys is that a *false* claim has to be written deliberately rather than arrived at by accident: the status has to be overridden, the sources line has to name a file, and the claim has to survive a list of things that may never be said.
- Freshness is dated, not verified. Every page carries the date its facts were read; nothing re-reads them.
- A source line is a convention. The gate can see that sources exist; it cannot see whether the writer opened them.
- Accessibility, contrast and rendering are not covered here at all — see A focus ring that computed to nothing.
Questions#
Why is the prose data rather than code?
Because a page written as a dictionary cannot change how pages render. A cluster of pages is then reviewable as content, a diff shows the page a change belongs to, and no writer can accidentally alter the template for everybody else while editing a paragraph.
Why does the gate fail closed rather than warn?
Because a warning in a build that ships anyway is a list nobody reads. The specific fear is an overclaim reaching the public site: a page describing something as working when the registry says the customer-facing capability is narrower is the failure this whole arrangement exists to prevent.
Are code samples held to the same language rules?
No, and deliberately. Code blocks are excluded from the word count and from the phrase scan, so a configuration sample that happens to contain one of the refused words is not treated as bad writing. Everything a person reads as prose is scanned.