Appearance
Contributor tooling
Everything here exists to answer one question before a commit lands: does this change hold?
The reference pages state what each unit does. The reasons they exist, and the order they run in, live here.
One command decides
A change is admissible when the gate passes. Run it before you commit:
bash
npm run checkThe order is not arbitrary. Cheap checks run before expensive ones, so a typo fails in seconds rather than after a browser suite. Type checking comes first, then the linter, then the prose and citation checks, then the data checks, then the suites, then the build.
Continuous integration runs the same command. There is no wider form: the browser suite and the dev-loop suite are the last steps of this one. Both start real servers, which is what makes the run slow enough to finish before you commit rather than while you type.
What each check holds
A check earns its place by having caught something. None of these are style preferences.
| Check | Refuses |
|---|---|
typecheck | A type error, across the whole tree rather than the file you touched. |
lint | The rule set the repository has settled on. |
comments:lint | A comment that narrates what the code plainly says, or that has drifted from it. |
glossary:lint | A word the project has settled against, naming the term to use instead. |
cites:check | A backticked repository path in prose that resolves to no tracked file. |
catalog:check | A unit, building or research entry whose costs or prerequisites are inconsistent. |
art:registry:check | An art asset in the tree that the registry does not account for. |
vendor:check | A vendored artifact that has drifted from its source. |
test, test:ui, test:desktop | A behaviour that changed without its spec changing. |
build | A tree that type-checks and tests but does not produce a site. |
The suites, and which one to reach for
Reach for the narrowest suite that reproduces what you are chasing. Running the whole gate to chase one assertion costs minutes per iteration.
| Command | Covers | Starts |
|---|---|---|
npm test | The node suites: engine, server, catalog, conformance, properties. | Nothing. |
npm run test:ui | The React and jsdom surface. | One node child per allotted concurrency slot. |
npm run test:browser | Real Chromium against a running client and server. | Playwright, vite and a local worker. |
npm run test:devloop | The dev loop's own behaviour. | The dev loop. |
npm run test:desktop | The desktop shell. | The shell's own harness. |
The test:ui script bounds its own fan-out. Without a bound, the node test runner forks one child per visible CPU. A machine with many cores and a modest memory limit then starves those children rather than running them. The bound is set in the script and travels with the repository, so it protects any machine the suite lands on.
The evidence rule
A claim about this repository is proved by a spec the gate re-runs. Not by a manual check, not by a screenshot, and not by an agent's report.
Two consequences follow. A bug is fixed by writing the failing test first, watching it fail for the expected reason, then making it pass. A passing run leaves nothing behind on disk — a suite that writes a file and does not remove it has a defect, whatever its assertions say.
The docs kit
The documentation site is split between a program and a person, and neither crosses into the other.
tools/docs-emit.mjs owns page structure. It scaffolds every page, holds the regions between emit markers byte-identical, and audits what is missing. It never writes a sentence.
Every sentence outside those markers belongs to whoever is writing. Editing inside a marker is drift, and the audit reports it as such rather than accepting it.
Four commands cover the loop. npm run docs:emit refreshes structure, npm run docs:check reports what is unwritten or has drifted, npm run docs:prose holds the prose rules against a recorded baseline, and npm run docs:headers names units with no source header.
The dev loop
The dev loop serves the client and the worker together, so a change is visible without a build step. It holds a lock and records its state, which is what stops two loops on one machine from fighting over the same ports.
One loop per machine is the working assumption. Starting a second one while the first holds the lock is the most common way to lose an hour to a port that is already bound.
When a check misbehaves
A check that fails on a tree you believe is correct is either right about something you have not seen, or it is failing for an infrastructural reason. Telling those apart is the first move, and Debugging is where that procedure lives.
The signature worth knowing here: a suite that reports a long duration and emits no subtests was not slow. Its test child was killed, and the number printed is how long it survived.
Units in this area
Every unit in contributor tooling, with the page that documents it.
| Unit | What it does |
|---|---|
build-site.mjs | Builds the game client into dist/ and stamps it with the commit and branch it was built from. |
dev.mjs | Starts and stops the local dev loop — vite serving the game and wrangler serving its Worker API, running together behind a per-worktree lock. |
play.mjs | Serves the built dist/ directory as a static local preview, with no /api. |
art-manifest.mjs | Renders app/src/game/art-manifest.mjs, the generated list of every set the art tree holds, and checks it for drift. |
art-registry.mjs | Renders app/src/game/art-registry.json, the one deterministic reading of which art the aliens skin's surfaces need and whether it exists, and checks that reading for drift and stale debt. |
catalog-check.mjs | Renders src/catalog/catalog.json from the data modules under src/catalog and checks it for drift. |
ci-log-publish.mjs | Publishes a CI job's captured log to a ci-logs/<run> branch the token API can read back. |
freeze.mjs | Publishes the current worktree branch to dev, after backing it up and proving it green. |
gate-report.mjs | Posts the digest and tail of a red CI gate run where an agent can read them. |
geo-check.mjs | Generates a fixed set of named planets and checks each one's geography against the engine's own rules for biomes, landmarks, flows, the abyss, and replay determinism. |
import-graph-check.mjs | unwritten |
kill-suite-strays.mjs | Frees the browser suite's two ports by killing whatever still holds them. |
land.mjs | unwritten |
layer-check.mjs | Refuses an import that runs against the layer direction. |
moon-check.mjs | Bakes the fixed set of NAMED_MOONS twice and checks each moon's terrain, landmarks, and palette against the rules a moon must hold. |
perf-series.mjs | Judges the newest run in a committed performance series against its baseline, and appends new runs to that series. |
proto-view.mjs | Drives an already-running Chrome instance over CDP — console capture, JS eval, reload, click, drag, and paint/DOM probes — and exits. |
proto-window.mjs | Launches Chrome with remote debugging on, at a fixed window position and size, for proto-view.mjs to drive. |
sync-agency-assets.mjs | Copies finished art from the external design-agency results directory into app/public/art, classifying each set by its own shape, and writes a manifest of what it copied. |
territory-check.mjs | Holds tools/territory.json total and disjoint over the tracked tree, so every tracked path has exactly one owner and every glob owns something. |
test.mjs | Collects every *.test.mjs file under test/ and runs it through Node's own test runner, in an exclusive phase and a parallel phase. |
vendor-bots.mjs | Keeps vendor/bots honest against the lab checkout it was copied from. |
visual-regression.mjs | Captures reference screenshots of a fixed set of UI scenes with Playwright, or checks the live render against those references. |