RiskMandatecustomers ↗ riskmandate.ai first batch, being set up v0.1.1
Home / How the site is built

How the site is built

A static site, served byte for byte from this repository by GitHub Pages. There is no build step at deploy time: seven small generators run before a commit and keep the parts that must agree in agreement, and a gate refuses the push when they do not.

Status, from docs/reality.md: shipped v0.1.0 One version in admin/build/version.txt, a release history, and four CI jobs: validate, tag, deploy, verify live · shipped v0.1.0 The RiskMandate chrome (head, header, breadcrumbs, footer, CSP) generated into every page from one definition · shipped v0.1.0 A markdown twin for every page, llms.txt, llms-full.txt and sitemap.xml, generated from the pages · shipped v0.1.0 The eight-check gate: version agreement, links and orphans, canonical host, the leak tripwire, no foreign runtime origin, the data holding together, reality regenerating identically, the house words · shipped v0.1.0 The review folder: the brief read as intent, each node anchored to a section of the brief, validated and rendered · proposed The derived half of the review folder: the code graph from parsers, the join with a coverage figure, every commit read upwards, the navigator as web components

The one-screen version

pip install pytest                        # the gate's only Python dependency; Node 22 and Python 3.11+ otherwise
python3 admin/build/gate.py --build       # regenerate everything, then run the whole gate
python3 admin/build/gate.py               # what CI runs: check mode only
python3 -m http.server 8000               # serve the tree locally; there is no build step
python3 admin/build/release.py --version 0.2.0 --kind feature --title "what" --notes "why"   # bump, build, gate, commit, push, verify live

The generators, in order

GeneratorReadsWrites
gen_customers.pydata/customers.json, data/milestones.jsona page per customer, the batch table, the steps, the counts on the home page
gen_reality.pydata/reality.jsondocs/reality.md, the status lines and tables on the pages
gen_docs.pydocs/**/*.md, team/**/*.md, review/*.mdan HTML page beside each, and an index page per folder
review/tools/the brief, review/intent/stories.jsonreview/intent/sections.json, the review page; validates the intent files
gen_chrome.pychrome.py, version.txt, CNAMEthe head, header, breadcrumbs and footer of every page
gen_versions.pydata/versions.jsonthe release table on the release history
gen_twins.pyevery page's <main>the markdown twin beside it
gen_llms.pyevery page and documentllms.txt, llms-full.txt, sitemap.xml

The eight checks of the gate

In admin/build/validate.js, with no dependency beyond Node. Every problem of every check is printed before the run fails, so one run shows everything.

  1. Version agreement. version.txt, every badge, the release table, the indexes and the home twin say the same version.
  2. Internal links, fragments and no orphans. Every link resolves against the real tree; every fragment exists; a page nothing links to fails, because an orphan is unpublished.
  3. Canonical host. Every page's canonical URL and og:url are on the CNAME host and agree.
  4. The leak tripwire. sgit key shapes, write tokens, cloud key prefixes, private key blocks, append tokens, bare account numbers, against every text file. The fix for a trip is a redaction, never a wider pattern.
  5. No runtime foreign origin. No script, style or import from another origin; every script parses; no inline script or style, because the content security policy forbids them.
  6. The data holds together. Every customer record has a page and every page a record; the milestones name the ladder; no contact details; the versions file is valid.
  7. Reality regenerates identically. docs/reality.md and the status tables are what data/reality.json says.
  8. The house words. riskmandate.ai's rules: never "ADP", never "the policy" alone, no score on a behaviour policy, "steps" not "rungs", British spelling, customers not "beta users". The verbatim brief and memo are exempt.

Plus an advisory that never fails the build: em-dashes in prose we write. Then pytest tests/build/ (the chrome on every page, the twins, the customer data, the converters) and node --test tests/unit/ (the gate's own helpers).

The four CI jobs

JobWhenDoesOn failure
validateevery push to dev or main, every pull request, manual dispatchpython3 admin/build/gate.pynothing is tagged or deployed
tag-releasepush to dev onlyasserts version.txt equals the newest site vX.Y.Z : … commit subject and that the bump is the next minor, a patch or a major; tags; backfills; pushes the tagsno deploy
deployany push or dispatch, never a pull requestcopies the tree minus .git, .github, tests/unit into the Pages artifact and deploys itthe previous version stays live
verify-liveafter deploypolls https://customers.riskmandate.ai/admin/build/version.txt and the home page badge, with a cache-buster, until both equal the version, for up to ten minutesthe run is red: green does not mean live

The whole of it is in how a release works. What only a human can do, starting with the DNS record for this host, is in what needs a human.

Where this came from

The pipeline is copied from secrets.sgit.ai, the newest gate in the sgit.ai estate, as every sgit repository says a new repository should do first; the look and the house words are riskmandate.ai's, because this is a sub-site of it. What was changed and why is in the corrections file.