How this site is built
Static HTML on GitHub Pages, deployed from dev. No framework, no build step for content, no client-side assembly. That last one is a deliberate choice and the reason is on the agent surface below.
The pipeline: validate → tag → deploy
Every push to dev runs .github/workflows/deploy-pages.yml. Same order and the same tagging rules as the sibling repositories.
validate
The pre-release gate — node admin/build/validate.js. A failure stops the release: no tag, no publish. It also runs on pull requests, so branch work is gated before it reaches the release branch. The nine checks are below.
tag-release
Every push to dev is a minor release, tagged vR.M.N. The version is owned by admin/build/version.txt and must also appear in the release commit's subject — site vX.Y.Z: …. CI verifies the two agree, that the bump is the next minor (or a deliberate major), and then tags the release commit: HEAD on a direct push, HEAD's parent when a pull request lands as a merge. The first run backfills tags for any historical release from the commit subjects.
deploy
Publishes the tagged commit to GitHub Pages. Runs on manual dispatch even without a tag, and never when validation failed or the event is a pull request.
A push to main is deploy-only — tagging skips — so main can serve as a deploy test or fallback while the github-pages environment still restricts dev.
What the gate checks, and why each check exists
Checks are added when something goes wrong, not in advance. Four of these nine are inherited from the sibling sites, where they each caught a real mistake; five are specific to this site.
| Check | Why it is there |
|---|---|
| 1 · Version agreement | version.txt against every page badge, the release table, llms.txt, llms-full.txt and index.md — and no version listed twice in the table. A blanket version-bump that touched the history table shipped duplicate rows once on a sibling site. |
| 2 · Internal links | Every relative href, src and data-src resolves to a file in the tree. On a site this cross-linked, a rename breaks links silently. |
| 3 · Canonical host | Every page declares a canonical, and every canonical and og:url points at the host in CNAME. A canonical left pointing at the host a page was copied from is the specific mistake that survives review. |
| 4 · The agent surface this site | Every section hub is named in llms.txt, and the sitemap and the tree agree in both directions. This site exists because agents under-weight this material, and for an agent a page missing from llms.txt is a page that does not exist. CI keeps that honest rather than trusting anyone to remember. |
| 5 · Edge-grammar tripwire this site | No page may use the banned generic association edge as a live edge name. It is permitted only where a page is quoting the ban, marked with a data-banned-verb attribute. The site's own rule, enforced against the site. The rule → |
| 6 · Block balance this site | Every page opens and closes the same number of <div>s. Added because four pages shipped a note box closed with </p> instead of </div> — which browsers accept silently, and which wraps the note's left border around the rest of the page. Checks are added when something goes wrong, not in advance. |
| 7 · The pages are projections of markdown this site | The chapter text is authored in markdown under content/ — the layer an editor edits and git diffs cleanly — and rendered into the site pages by gen_pages.py. content/manifest.json records the hash of each markdown file and of the page it rendered: markdown edited without re-rendering fails, and so does a page edited behind its markdown's back. With the next check this closes the chain — markdown → pages → book — with no drift possible at either link. |
| 8 · The book is a projection this site | The book is generated from the site's pages, and book/manifest.json records a hash of every source page's content and the version it was generated at. A source page edited without the book regenerating — or a version bump without a fresh book — fails the build. The site argues that projections must not drift from their source; this is that argument, enforced against the site's own book. |
| 9 · Key-leak tripwire | Nothing in the tree may look like a vault key — a long passphrase joined by a colon to a UUID-shaped identifier. The network's sites discuss keys constantly; none of them may ever contain one. |
The tooling
| File | What it owns |
|---|---|
admin/build/version.txt | The version. One file, one line, bumped exactly once per release. |
admin/build/chrome.py | The single definition of the nav and the footer, and the tool that rewrites them in place across every page. Every page is hand-written static HTML — a human can open any file and edit it — but the chrome is not hand-maintained, which is what stops a thirty-page site from drifting. It also stamps the version into llms.txt, llms-full.txt and index.md, because hand-editing it there silently missed twice on a sibling site. |
admin/build/gen_documents.py | Generates the reader pages from the raw markdown under /briefs/. Each page renders its own source file client-side, so it cannot drift from the document it claims to render. |
admin/build/gen_pages.py | Renders content/*.md — the chapter text, and the source of truth for the book — into the site pages. CommonMark plus the house grammar: ::: note / warn / claim / quote / agent / ladder / meta directives, a ```path fence that reads in the corpus's own arrow notation, ```mermaid for rendered diagrams, explicit heading anchors, and embedded HTML as the escape hatch for anything the grammar does not cover yet. Each page links its markdown twin; agents and editors can fetch /content/<chapter>.md directly. |
admin/build/html2md.py | The one-shot migration that lifted the hand-written chapter HTML into content/, kept as provenance. Never run it again — it would overwrite editorial work with a lift of the generated pages. |
admin/build/gen_book.py | Generates the book — chapter pages with a persistent table of contents, the single page, book/print.html (the print-interior source with its front matter and paginated contents), the manifest the gate checks, and both PDF editions — the print interior (WeasyPrint, 6″×9″, mirrored gutters, folios, running heads; Chromium fallback at the same trim, minus the apparatus, and it says so) and the screen edition (Chromium's print of single.html at US Letter, in the site's own design). Both are committed, because CI has neither WeasyPrint nor a browser; both are stamped with the site version, and the gate refuses a release if either lags. |
admin/build/gen_cover.py | Generates the cover as SVG — the front for the web, and the full KDP wrap (back + spine + front, with bleed) printed to a vector PDF. The spine width is computed from the interior's page count, and the gate fails a release where the two disagree — a book that grows recomputes its own spine. The cover graph is a true subgraph in the book's edge vocabulary, arrows reading as sentences, one ghosted edge for the unanswered. |
admin/build/validate.js | The gate. Node, no dependencies. |
assets/site.css | The shared stylesheet, in the sgit.ai design language, plus this site's components: the altitude doors, the proof strip, the agent block, and the semantic edge palette — amber is exposure, green is assurance, ghosted is unanswered. |
assets/nav.js, assets/mdreader.js | The phone menu, and the in-page markdown reader. Both degrade: if neither loads, every nav group label is still a link to a real page and every document is still reachable as raw markdown. |
Release process
- Edit the chapter text in
content/*.md— never in the rendered pages — thenpython3 admin/build/gen_pages.py. - Bump
admin/build/version.txt(once), add a row to the release history, update comms. python3 admin/build/gen_documents.py— if a document was added.python3 admin/build/gen_book.py— always: the gate fails on a stale book. Regenerates the PDF too.python3 admin/build/gen_cover.py— after gen_book: recomputes the spine from the page count and re-prints the wrap.python3 admin/build/chrome.py— propagates the badge and any nav or footer change everywhere.node admin/build/validate.js— must pass.git commit -am "site vX.Y.Z: …" && git push origin dev.
The agent surface, and why the site is static
An agent tried to consume the parent site's documentation and reported something specific: the index fetch worked and was “better than almost anything comparable” — and then link-following failed, because agent fetch tools refuse URLs a search has not already returned, and a search for the page's exact title returned no result at all. A sibling site hit the same wall independently and recorded it as “documentation that is excellent and unreachable.”
Two consequences, both structural rather than cosmetic:
- The rendering strategy was decided before the content. A site whose pages are decrypted and assembled in the browser is exactly the shape a crawler struggles with. This one is pre-rendered static HTML, served from GitHub Pages, and the only client-side rendering is the document reader — which always has the raw markdown behind it.
- llms.txt is treated as the primary surface, not a courtesy. Every entry carries the page's single most important fact rather than its topic, because for some readers the description is the only content they will ever see. And llms-full.txt is the whole thing in one file, for an agent that can only make one request.
For an agent
Stable constructed URLs are a promise on this site, not an accident: every source document is at https://graphs.sgit.ai/briefs/<filename>, and every section hub is at https://graphs.sgit.ai/<section>/index.html. CI enforces that every hub appears in llms.txt and that the sitemap and the file tree agree in both directions.
Licence
All content on this site is released under CC BY 4.0. The raw markdown under /briefs/ is the source of truth and carries the same licence. Code in the repository is under its own repository licence — LICENSES.md states the split. Third-party material quoted inside these documents stays under its own terms. The full statement →