Each recipe ends with npm run check. If it is not green, it is not done.
Add or edit a page
node scripts/site/new-page.mjs <name> --title "RiskMandate — …" --desc "…" \
--body /path/to/body.html --css /path/to/page.css --donor work.html --cta "Get in touch"
node scripts/site/add-licence-chrome.mjs # GitHub link + licence line, if the donor predates them
# add the page to site/pages.json (group or unlisted; at most 7 top-level entries)
node scripts/site/generate.mjs # injects the menu, writes the twin, sitemap, llms.txt
npm run check
- A page is one self-contained document: its CSS, its markup, its inlined data, the four shared
modules. Copy classes from a donor (
work.htmlhas cards, lists, tables, notices, a prompt block). - Colours are tokens on
:root. A literal hex outside:rootis a bug. - Components never use
innerHTML; text goes in throughtextContent. - The voice: plain, sourced, dated. Say what is not built next to what is. British spelling.
- Edit the page, never its
.mdtwin. Rerungenerate.mjsafter every edit. - A renamed page leaves a redirect stub at the old address (see
site/abp-vaults.html).
Add a behaviour-policy vault
Inputs are four files; everything else is derived.
mkdir -p site/vaults/<slug>/data && cp -r site/vaults/gmail-readonly/data/vocabulary site/vaults/<slug>/data/
# write site/vaults/<slug>/vault.json who, which shape, status "template", renderer version
# write site/vaults/<slug>/data/grant.json abp/profile/v1 — one row per capability, evidence, barrier, via, material, undo
# write site/vaults/<slug>/data/mandate.json abp/mandate/v1 — want / do_not_want / unstated over the 23; status starting-point
# write site/vaults/<slug>/data/scenarios.json six scenarios: three normal, three advanced; each wants and refuses primitives
node scripts/site/build-abp-vault.mjs <slug> # derives the documents, delta, validity, zip, loader, history
node scripts/site/build-abp-vault.mjs <slug> --check
# add the vault to site/vaults/index.json (slug, app, title, shape, family, blurb, logo, brand, group; vid + key once pushed)
node scripts/site/build-abp-pages.mjs # the library page and abp-vault-<slug>.html
node scripts/site/generate.mjs && npm run check
- A grant is measured or documented, never typed. Measured needs a system we are entitled
to run and the evidence file in
history/. Documented quotes the vendor's page, with URL and date read, in each row'snote. Anything unsettled goes inresearch_needed; disagreements between the vendor's own pages go incontradictions, unresolved; permissions the grammar cannot name go innot_in_grammar. materialon every row:own,organisation,third_party,mixed.- The credential is the grant, not a barrier. A key that permits a thing is not what stops it.
- Note the door: the same capability through two paths is one row with two
viaentries. - No score field exists and none is added.
- The vault id and the read key come from the push; until the lead pushes it, leave
vidandkeyout of the catalogue entry so the tile lists as asked for, not built, and say so in your work file.site/vaults/index.json's note explains the fields. - The PDF in
dist/isrender-abp-vault-pdf.mjs(Playwright); the zip is deterministic andchecked by CI.
Correct a mandate or answer an open question
Edit data/mandate.json (move rows between want, do_not_want, unstated) or data/grant.json (move a row's evidence, put the quote in note, delete the entry from research_needed). Rebuild, --check, and the history records whether the counts moved.
Cut a release
git fetch origin dev && git merge origin/dev # first; see the rules of engagement
node scripts/site/release.mjs 1.17.0 "What changed, in a line"
# write site/versions/1.17.0.md — what changed, why, what was not changed and why not
node scripts/site/generate.mjs && npm run check
git commit -am "v1.17.0 — what changed, in a line"
Notes are prose, in the voice of site/versions/1.15.0.md: bold lead phrases, links to the pages, a paragraph on what was deliberately not done. The number is claimed at merge time; if dev took it, run release.mjs again with the next one. CI tags the commit; nothing else to do.
Which number moves. The third, by default: 1.19.0 → 1.19.1. A fix, a page edit, a new vault, a new brief, an every-page chrome change, a restructured page: all patches. The second number is for a release that changes what the site is or what it sells: a new section in the menu, a new product on the pricing page, a page family rebuilt around a new idea. Two agents merging in one day should produce 1.19.1 and 1.19.2, not 1.20.0 and 1.21.0. The first number is the lead's to move.
Cut a Lab edition
A Lab page's content hash moved → node scripts/site/render-lab-pdfs.mjs <slug> (Playwright; serves the site on a local port). It appends to lab-editions.json with the digest; add a note to the entry saying what separates this edition from the last. Then generate.mjs lists it on the page. Never edit or remove an edition.
Write a brief
docs/briefs/<kind>__<slug>.md. Header: title as a claim, date, author @website-agent, trigger (what the lead said, quoted), reads against (the sources with versions). Numbered sections. Tables where a table is honest. End with what this does not settle or decisions I need. Then add one line to .claude/onboarding/01-map.md and, if it changes the state, to 03-state-and-next.md. If the brief arrived as a file from outside, it also goes into the register: copy it byte for byte to site/assets/briefs/, add an entry to site/briefs-register.json with its sha256, size, arrival time and status, and render it on site/briefs.html. The test checks the digest.
Give a brief, a task or a work file its page on the console
Nothing to do but rerun the build. node scripts/site/build-admin.mjs renders every docs/**/*.md under site/admin/briefs/<slug>/, every .claude/briefs/Txx-*.md under site/admin/work/Txx/, every .claude/work/<branch>.md under site/admin/work/branches/<branch>/, and the onboarding files, CLAUDE.md and the prompts under site/admin/agents/. The counts on the console — what needs the lead, what is in flight, memos not fully worked — are read off 03-state-and-next.md (the queue table and Decisions the lead owns), the work files and briefs-register.json, so keeping those true is what keeps the console true. test_admin.mjs fails if a document has no page; build-admin.mjs --check fails in CI if the console is stale. Never hand-edit anything under site/admin/ except console.css.
Register a document that arrived from outside
See the paragraph above. Statuses: received, partly, processed, superseded, and they only move forward. produced names what exists because of it; not_done names what does not.
Touch a shared module
Edit scripts/site/modules/<name>.js, run node scripts/site/sync-modules.mjs, then generate.mjs. That is an every-page edit: claim it, commit it alone, merge it fast.
Change the chrome (header, footer, menu)
There is no template. The chrome is copied from a donor when a page is scaffolded, and add-licence-chrome.mjs shows the pattern for retrofitting one element to every page: an idempotent script with --check, run in CI. Do the same for anything new rather than sed-and-hope; then update new-page.mjs's FOOTER/header constants so scaffolded pages carry it too.
Merge a branch into dev
.claude/commands/merge-to-dev.md is the checklist. Short form: merge dev in, regenerate, check, release last, check again, --no-ff into dev, delete your work file, push, watch CI.
Run it locally
bash scripts/run-locally__riskmandate_ai.sh → http://localhost:10070/. Use localhost: /scenarios/ and the vault pages decrypt with Web Crypto, which needs a secure context.