Wiki Publishing
See also: Tooling and Scripts, CI CD and Deploys, Data Model
The wiki/ folder in the main repo is the wiki — there is no GitHub wiki
(this repo's wiki was never readable without paying for seats; the old
publish-wiki.yml mirror was removed 2026-07-19). Pushes to main that touch
wiki/** fan out to the sibling fun-language/Stretched-wiki repo via
.github/workflows/notify-wiki.yml, which fires a repository_dispatch
(event_type=wiki-updated); that repo builds and serves the public site, see
CI CD and Deploys and Secrets Management. Agents and contributors read
the markdown directly; humans who want a browsable site serve it locally:
Serving the wiki
bun run wiki:serve # http://localhost:4201 — pages render from wiki/*.md on EVERY request
bun run wiki:site # static export → dist/wiki (index.html = Home)
tools/wiki/render-site.ts (+ serve-wiki.ts) renders GitHub-flavored
markdown with the wiki conventions intact: [[Page Title]] /
[[Alias|Page Title]] cross-links, relative [text](../Page.md) links,
_Sidebar.md as the nav, and ```mermaid fences rendered client-side with
a vendored mermaid bundle (no CDN — the site works offline). Because
wiki:serve renders per request, an edit or a wiki:build regeneration shows
up on reload; there is no watcher or rebuild step.
Deployment: every PR gets a wiki preview alongside the app and Storybook —
deploy-preview.yml builds dist/wiki and deploys it to a Firebase Hosting
preview channel on the stretched-dev-wiki site (1-day expiry; the action
comments the URL on the PR). Preview channels are unlisted-URL public — the
wiki documents internals, and Harlo accepted that exposure for PR review
(2026-08-02). On merge to main, notify-wiki.yml notifies the sibling
fun-language/Stretched-wiki repo (repository_dispatch); that repo is the
source of truth for the public, always-on wiki site. This repo does not
host the public wiki itself — the dispatch is fire-and-forget at the workflow
layer, so a dispatch failure does not corrupt wiki/; the next push re-triggers.
Before firing, the workflow runs a real production build
(bunx nx build stretched --configuration=production, same as ci.yml) — without
it wiki:build SKIPS generated/Production-Bundle.md — then re-runs
bun run wiki:build and requires an empty git status --porcelain over wiki/.
Porcelain catches modified, deleted-then-recreated (untracked) AND the hybrid
Data-Model.md (regenerated OUTSIDE wiki/generated/), all of which a plain
git diff over wiki/generated/ would miss — so a direct or hookless push can
never publish a snapshot this repo's own wiki gate rejects.
The PAT that authorizes the dispatch (secrets.WIKI_DISPATCH_TOKEN) is a
GitHub Actions secret — see Secrets Management for setup and rotation.
The generated section (wiki/generated/)
Machine-written pages — never hand-edit them. Every page opens with a
visible banner naming its generator, its data source, and the refresh command;
all diagrams are mermaid fences, so the pages render on GitHub and in the
local site alike. Generated Docs (wiki/generated/README.md — itself
generated from the page registry in tools/build-wiki.ts) is the canonical
index of the section and the guide for adding a new generated page — this
page deliberately doesn't duplicate that table.
Data Model is the older hybrid pattern: a hand-written page whose ERD
block is injected between ERD:START/ERD:END markers from the
stretched-types README.
Commands and gates
bun run wiki:build # regenerate every page whose inputs are available + link-check
bun run wiki:check # gate (pre-push + CI docs job): broken link ⇒ exit 1; drift ⇒ notice
bun run wiki:bundle:check # Production-Bundle.md ONLY — needs a fresh prod build
bun run wiki:test # unit tests for the generators (tools/build-wiki.spec.ts)
Generated pages are auto-accepted (2026-08-04). Machine pages carry no
hand-authored content, so whatever the generators produce IS the wanted
content: the pre-push hook runs wiki:build and auto-commits any refresh
(chore(wiki): regenerate machine pages, same contract as the prettier
step), and wiki:check reports drift as a notice (with a committed-vs-fresh
diff of the differing lines) instead of failing — only broken links fail
the gate. Drift can still reach CI from a hookless push or a PR behind main;
it heals on the next hooked push.
Auto-accept makes determinism MORE load-bearing, not less: a nondeterministic generator wouldn't fail a gate anymore — it would make the hook commit a phantom "refresh" on every push, forever. Generators must obey Generator Determinism — the incident log there is why. Two timing details:
wiki:bundle:checkruns right after the production build (afterbundle:check) in both the pre-push hook and CI'stestjob — never in the build-lessdocsjob, andwiki:checknever diffs the bundle page. So the page is always checked against the build that just ran.- The coverage page merges, it doesn't demand. A project with a
coverage-summary.jsonon disk gets fresh numbers; a project without one keeps its committed row. If a stale summary from another branch skews the page, rerun that suite with--coverage(or deletecoverage/<project>) andwiki:build.
Editing the wiki (hand-written pages)
- Edit or add a
wiki/*.mdpage (in a PR). - New page? Name it
Your-Title.md(hyphens become spaces in the title), add it towiki/_Sidebar.mdand Home, and link it from related pages. - Cross-link with
[[Page Title]](e.g.[[Data Model]]). - Run
bun run wiki:checklocally before pushing (wiki:buildto also refresh generated pages).
When to update a page
Source files that a page documents carry a pointer of the form:
> 📖 **Wiki:** see [[Page Title]] for … Update that page when you change …
If you touch code behind a 📖 Wiki pointer, update the referenced page in the
same change. Generated pages are the exception — they regenerate themselves;
you only ever run bun run wiki:build (never hand-edit the output).