Skip to main content

Architecture

This page explains the purpose of every part of the system. If you're trying to understand why a particular file exists or what would break if you removed it, start here.

The trust layer

Three things are load-bearing across the entire stack. Don't break them:

  1. The Zod schema is the only source of truth. schemas/source.ts defines every front-matter contract. pnpm codegen derives the CMS config (static/admin/config.yml) and the JSON Schema (schemas/frontmatter.schema.json) from it. The runtime validator (scripts/validate-frontmatter.ts) uses the same source. CI fails if codegen drifts.
  2. Slugs are immutable. The build runs with onBrokenLinks: 'throw', onBrokenAnchors: 'throw', and onBrokenMarkdownLinks: 'throw'. A renamed slug = broken citation = build failure. Renames require an explicit redirect or tombstone.
  3. The parser is one-shot (Strategy A). scripts/parse-source.ts ran once over _source/ to produce docs/. After that, _source/ is gitignored and the parser is archived. Re-running it would overwrite editorial work. Documented in CONTRIBUTING.md as the #1 footgun.

Build-time pipeline

_source/  ──(one-shot parser, archived)──>  docs/  ──(Docusaurus build)──>  build/

├── plugins/generate-aom-json → build/aom.json
├── @signalwire/...-llms-txt → build/llms.txt + llms-full.txt + .md mirrors
├── plugins/inject-defined-term → schema.org JSON-LD per page (postBuild HTML rewrite)
└── search-local + axe-core CI → search index + WCAG gate

Each layer:

  • schemas/source.ts — Zod source of truth. Five content types (Concept, Hat, MethodologyStep, MindsetPrinciple, Foundation) plus a discriminated union (FrontmatterSchema). The slug regex is strict kebab-case (FoundationSchema permits / as a special-case for the docs-root homepage).
  • scripts/codegen.ts — derives the CMS YAML + JSON Schema from the Zod source. Re-runs on every pnpm build indirectly (via pnpm schema:check in CI).
  • scripts/parse-source.ts + parse-source.lib.ts — the one-shot parser. Now archived; refuses to run unless AOM_FORCE_REPARSE=1 (Strategy A guard). Kept in tree as an emergency manual probe.
  • scripts/validate-frontmatter.ts — Zod validator that walks docs/**/*.md on every PR. Build fails if a page's front-matter doesn't validate.
  • docusaurus.config.ts — site config. Plugin slate: preset-classic, theme-mermaid, plugin-ideal-image, search-local, llms-txt, generate-aom-json, inject-defined-term. Top-level headTags for site-wide JSON-LD; top-level scripts for Plausible.
  • sidebars.ts — hand-rolled hierarchy. The 6-hat order is canonical so we don't autogenerate the top level; per-hat concept lists are autogenerated.
  • src/css/custom.css — theme tokens (hat colors, dark/light variants). HatBadge styling. WCAG fixes for the search-local plugin's chips and dark-mode primary.
  • src/data/hats.ts — UI-side duplicate of the parser's HAT_TABLE. Decoupled so theme tweaks don't drag content hashes.
  • src/components/ — three React components: HatBadge (was: now CSS-only via remark-emitted markup), OpenInAiButtons (AI-06/07), CiteThisPage (EDU-01/02).
  • src/theme/DocItem/Footer/index.tsx — wrap-not-eject swizzle that injects the OpenInAi + Cite buttons into every doc footer.
  • src/pages/admin.tsx — Sveltia CMS mount point at /admin.
  • plugins/generate-aom-json/ — custom Docusaurus plugin. loadContent walks docs/** front-matter, postBuild writes build/aom.json (60 concepts + 6 hats + 6 methodology + 8 mindset + 13 foundations).
  • plugins/inject-defined-term/ — custom plugin. postBuild parses each built HTML with JSDOM and appends a DefinedTerm (per concept) or DefinedTermSet (per hat) JSON-LD <script> tag.
  • @signalwire/docusaurus-plugin-llms-txt — third-party plugin. Emits /llms.txt (hierarchical index), /llms-full.txt (full content), and per-page .md siblings.

Edge layer

After the build artifact lands at Cloudflare Pages:

  • functions/_middleware.ts — Cloudflare Pages Functions middleware. Intercepts every GET/HEAD; if Accept: text/markdown → rewrites the URL to the .md sibling. Static asset paths (.json, .txt, .xml, anything with an extension) bypass the middleware.
  • static/robots.txt — explicit Allow for GPTBot, ClaudeBot, Google-Extended, PerplexityBot, etc. Sitemap directive.

Editorial layer

  • static/admin/config.yml — Sveltia CMS config. Generated by pnpm codegen from the Zod source. Five collections (Concept, Hat, MethodologyStep, MindsetPrinciple, Foundation) — slugs marked immutable per CMS-04.
  • src/pages/admin.tsx — Sveltia mount. Loads the bundle from CDN at runtime. Decap fallback documented inline.
  • sveltia-cms-auth Worker (Cloudflare, pending OAuth setup) — handles the GitHub OAuth dance. Saves create PRs against main; never direct push (CMS-05).

CI/CD

Two GitHub Actions workflows:

  • validate.yml — runs on every PR + push. 6 steps: install, typecheck, test (vitest), validate (front-matter), schema:check (codegen drift), build (Docusaurus production build with onBrokenLinks: 'throw').
  • axe.yml — runs on every PR + push. Builds the site, serves it on 127.0.0.1:3010, runs pnpm a11y (Playwright + @axe-core/playwright over 4 sample pages × 2 colour modes + a 360px responsive smoke). Fails on any WCAG 2.1 AA violation.

Phase 6 will add deploy.yml (push to main → Cloudflare Pages) and release.yml (tag → version cut + GitHub Release).

Sibling layer

  • the-kizz/aom-mcp — separate repo. The MCP server. Read-only HTTPS-fetch; serves 8 tools (list_hats, get_hat, search_concepts, get_concept, list_concepts_by_hat, get_methodology_step, get_mindset_principle, get_spec_version) + 2 resources (aom://graph, aom://hats). Not published to npm.

What's NOT here

  • No backend database. The site is fully static; the CMS auth Worker is the only stateful edge service (and it stores nothing — just OAuth dance).
  • No analytics dashboard inside the wiki. Plausible is external.
  • No real-time features. The methodology is a reference, not a chat product.
  • No SaaS lock-in. Content is CC BY 4.0 Markdown in a public Git repo. The whole stack could move to any host that serves static files + supports a simple OAuth proxy.
Continue in: