Build

The gates

The builder writes nothing until every gate passes. The first failure stops the build, prints the file and the line, and leaves docs/ untouched.

A gate that cannot fail is not a gate. Each one below is a check the builder runs against the rendered output, not a promise in a document. The build is deterministic, so the same inputs produce the same bytes and the same verdict.

The gate pipeline Eleven gates run in order and fail fast: secret scan, forbidden strings, links, page metadata, alt text, JSON-LD, sitemap, the social image size, tokens only, the revision-2 content gates, and the site gates. A red cross beside every gate marks a failure that stops the build before anything is written. The gate pipeline Every gate runs before anything is written. The first failure stops the build. 1 Secret scan ✕ 2 Forbidden strings ✕ 3 Internal links and fragments ✕ 4 Page metadata: one h1, title, description, canonical ✕ 5 Alt text on every image ✕ 6 JSON-LD parses ✕ 7 Every sitemap URL has a file ✕ 8 Social image is exactly 1200 by 630 ✕ 9 Colours come from the tokens ✕ 10 Content gates: word counts, diagrams, reference ✕ 11 Site gates: CNAME, legal texts, no external requests, no internal process, claims ✕ fail the build stops nothing is written all pass docs/ is written gate, in order ✕ failure stops the build the site-specific gates

Scroll the diagram sideways to see all of it.

The gates run in order and fail fast; nothing is written until all of them pass.

Text alternative: Eleven gates in order: secret scan, forbidden strings, internal links and fragments, page metadata, alt text, JSON-LD, sitemap files, the social image size, tokens only, the content gates for word counts, diagrams and the generated reference, and the site gates for CNAME, legal texts, external requests, internal process and the claims ledger. A failure at any gate stops the build before anything is written.

The ten core gates

The gates the playbook prescribes, in order
#GateFails when
1Secret scanA pattern matches a text file under site/ or docs/.
2Forbidden stringsA fixed list of unfinished-content markers appears anywhere.
3LinksAn internal href or src does not resolve, or a fragment has no id.
4Page metadataNot exactly one h1, a title over 60 characters, a description outside 120 to 160, or a duplicate.
5Alt textAn img has no non-empty alt.
6JSON-LDA structured-data block does not parse.
7SitemapA URL in sitemap.xml has no file in docs/.
8Social imageog.png is missing or not exactly 1200 by 630.
9TokensA colour in the CSS, a template or a diagram is not a design token.
10Content gatesA page is outside its word range, a diagram lacks a title or description, or the generated reference is incomplete.

The site gates

This site adds the checks its own job needs: the CNAME file must hold the bare hostname, the non-affiliation and licence texts must appear in every footer, no page may request an external resource, no internal process artifact may leak into the output, and every page must carry a claims list whose evidence resolves to a real file.

The reference page has a gate of its own: the builder reads the commands, agents, references, templates and scripts from the repository, renders one entry per file with a stable anchor, and fails if a listed file is missing, a family is empty, or the rendered entry count differs from the real file count.

The secret scan

The scan covers every text file under site/ and docs/: HTML, JSON, JSON-LD, the sitemap, the manifest, alt text and captions. It looks for token shapes, key prefixes, service-role assignments, connection strings, credential assignments, email addresses, private IP addresses, hosted project references and owner file paths.

A hit is reported by file and line with the pattern name, never with the matched value. Zero hits is the only pass. An allowlist entry would need the owner's written approval and a comment saying why; this project has none.

Determinism

The build must be byte-identical across machines, checkouts and days. That rules out four things: the wall clock, git history, random identifiers and unordered iteration. The sitemap's last-modified date is a fixed value in the site data, updated deliberately. Slugs are derived from names. Every list is sorted and every file is written with LF newlines.

The proof is the check mode: it builds into a temporary directory, compares every file byte for byte with the committed docs/, prints the names that differ, and exits non-zero on any difference. It never writes to docs/.

The check
python scripts/build_site.py --check
check: docs/ is byte-identical to a fresh build

If the check reports differences on every page, the playbook says to suspect configuration before content: line endings from a checkout setting, a generated file that is ignored by git, stale committed output, or a date read from history.

CI

A workflow named Site check runs the same command on every pull request and on pushes that touch the site paths. It installs the pinned requirements and runs python scripts/build_site.py --check. The command is identical locally and in CI; if CI ran something else, the gate would not be the gate.

The existing plugin workflow is untouched. The site workflow is a new file, and it has read-only permissions.

What the gates do not prove

A gate proves that the output is internally consistent, not that every sentence is true. Truth comes from the claims ledger: one row per factual statement, with evidence that resolves to a file. A green build with a wrong number is still a wrong site, which is why the numbers are computed from the repository or recorded with the command that produced them.