Runtime: the root docs CLI stays plain ESM .mjs on Node 22 or newer, without a build step or root prepare script. Owner-approved exception: packages/store uses TypeScript compiled to ESM JS plus .d.ts, built only within that package. The docs root never imports unbuilt store sources. Consumers install the published @kontourai/corpus at an exact version, or an unreleased commit's GitHub tarball, so whatever is committed under bin/ and src/ is what ships. Everything a consumer needs must be listed in the files allowlist in package.json. test/package-files.test.mjs fails when the packed file list leaves the independent allow-list in scripts/root-package-files.mjs, and npm run verify:pack installs the packed tarball and runs its bins.
Dependencies: keep the runtime set minimal: the MCP server SDK, zod for its tool schemas, marked, github-slugger, sanitize-html, htmlparser2 (pinned; also sanitize-html's parser), picomatch, npm-package-arg for dependency specs, yaml for workspace/workflow evidence, and shell-quote for CLI tokens. Never add native modules. Commit package-lock.json with any dependency change.
Contract: the corpus.json schema, CLI flags, exit codes (0 ok, 1 gate failure, 2 usage or configuration error), docs-bundle.json, products.json, and the MCP tool names are consumed by about 20 repositories. Changing any of them is a versioned, documented change. Update README.md and docs/ in the same commit.
Tests:npm test runs node --test. A gate's rejection path is tested by running the real CLI as a child process against a fixture in test/fixtures/, asserting both the exit code and the message. Fixtures have the real manifest shape. Tests that need git copy a fixture into a temporary repository; they never depend on this checkout's history.
Browser tests:npm run test:browser drives the generated site's selection toolbar (src/site-client.js, shipped as docs-kit.js) in headless Chromium through playwright-core, a dev dependency. It is separate from npm test and needs a Chromium for playwright-core (npx playwright-core install chromium); a missing browser fails the run. Run it when you change the client script, the page template, or the provenance attributes.
Fault injection: when you change a gate, commit, weaken the gate, confirm that its test fails, restore it, and confirm the tests are green again.
Dogfood:node bin/corpus.mjs check must pass on this repository. CI runs it.
Output discipline:mcp writes only protocol messages to stdout; diagnostics go to stderr.