Agent docs

Scripts

The scripts this repo runs by hand, what each is for, when to run it, and what it must never do.
Scripts

The things this repo runs by hand
What each one is for, when you run it, who else calls it, and what it must never do.

layer ci

The table#

Three of these run in CI on every push, ci-citations.mjs, build-do-nots.mjs (as a regenerate-and-diff) and check-notebook.mjs (advisory without a cross-repo token), so a docs commit that skips the first two fails there. dev-safe runs the guard for you. The rest you run when the row says. Every file in the directory has a row here; a script without one is a defect in this page, not a private tool.

ScriptWhat it doesRun it whenIt must never
build-do-nots.mjsRegenerates docs/DO-NOTS.md from every contract doc's Banned combinations section, the design canon, and the app-patterns recipesafter editing any of those three sourcesbe hand-edited around; the page has no opinions of its own
check-citations.mjsWalks every file.ext:N citation in the docs and reports BROKEN, OUT-OF-RANGE, and ROT-SUSPECTafter any canon or doc change; the docs-wide baseline lives in citations-baseline.jsonbe read as pass or fail on its own; ci-citations.mjs is the gate
ci-citations.mjsThe citation ratchet CI runs: docs-wide BROKEN and OUT-OF-RANGE may not rise above citations-baseline.json, and the canon, contract docs and breakdowns must carry zero BROKENin CI on every push, and locally before a docs commitbe silenced by raising the baseline; lower it when the count drops
check-notebook.mjsKeeps the canon curator honest: the notebook's last read: line may not fall more than 14 days behind the newest CANON-NOTES.md entry in a joined repo, read over gh apiin CI on every push (advisory until a cross-repo read token is a repo secret), and by hand before a curator readpass silently on a repo it cannot reach; an unreachable repo is reported
check-prop-reach.mjs with prop-reach-baseline.jsonThe prop-reach ledger: every prop a kit component declares should reach a rendered difference a test can see; counts unexercised props and exits 1 when the count rises above the baselineafter adding a prop or a test to the kit; --list prints every unexercised propbe read as proof a prop works; it checks presence in a rendering test, not the rendered effect
kit-inventory.mjsOne inventory of what the kit provides, derived from the showcase registry, the contract docs and the do-nots page: writes packages/ui/docs/KIT-INVENTORY.md and llms.txt, answers --find <term>, and --check exits 1 when the written inventory drifts from the registryafter any registry change; --find any time "does the kit already have X" comes upbe edited by hand around; the inventory has no opinions of its own, the reader prompt (packages/ui/prompts/claims.md) is the judging half
sync-method-docs.mjsCopies the design canon, the app-patterns recipes and the do-nots page into packages/ui/method/ so they ship in the package; --check exits 1 when a copy drifts from its source or when a doc in a published tree is missing from mkdocs.yml's navafter any canon or recipe edit, and before a docs commit (the nav half is the check CONTRIBUTING section 7 names)be run in CI; the nav check is a by-hand check by owner ruling (D7a, 2026-09-03)
dev-guard.mjs with dev-guard.mdRuns the playground dev server and recycles it when it is over a memory ceiling and idleonly when a human asks for it (npm run dev-safe)be started by an agent on its own
push-triad.shPushes the three Versable repos in one approved run, refusing any repo it cannot account forwhen the owner wants all three mains pushed at onceforce anything; a repo that fails a check is skipped
check-export-drift.mjsCompares the kit barrel's exports against the published tarball of the same version and fails on a difference (two artifacts under one version string, the 0.2.0 lesson)BY HAND before a bump, with a read token. It ran in publish-kit's drift job until that job was removed on 2026-08-24 (23fb48c); publish-kit.yml now has only check and publish. Do not re-wire it into CI without the owner askingpass on an auth failure; no registry read means no verdict (exit 2)
ship-kit.shPushes main and watches the publish-kit workflow until the bumped kit version is on GitHub Packages; refuses when the version is already published, the tree under packages/ is dirty, or nothing is ahead of originafter the bump commit, the owner's one release command (--check rehearses without pushing)publish anything itself; the workflow does
release-kit.shBumps and publishes @versable-git/ui to GitHub Packages from a laptopsuperseded; the publish-kit workflow publishes on push to mainbe used to publish; a hand publish races the workflow

Each one, from its own header#

build-do-nots.mjs#

node scripts/build-do-nots.mjs

Builds docs/DO-NOTS.md by re-reading every Banned combinations section, the design canon, and the app-patterns recipes, "so the page can only go stale by its sources going stale, never by a fourth copy nobody updated" (build-do-nots.mjs:3-6). Every line on that page cites the source it came from. Named in CLAUDE.md as the step after editing a Banned section, and in CONTRIBUTING.md section 7.

check-citations.mjs#

node scripts/check-citations.mjs [--verbose] [docs-dir ...]

Walks every file.ext:N[-M] citation in the doc corpus and reports what a compiler never will: BROKEN (file missing), OUT-OF-RANGE (line past EOF), ROT-SUSPECT (file committed after its citing doc; --verbose lists them). Exits 1 on the first two only. Ambiguous bare basenames are listed, never guessed (check-citations.mjs:3-8). Deliberately not wired into CI: it is a reading aid for the person changing a doc, and the canon rule is that every canon change re-runs it (CLAUDE.md, "Canon edits stay one pen"). CONTRIBUTING.md section 3 explains the scan set and why the number does not have to be zero.

check-notebook.mjs#

node scripts/check-notebook.mjs

The canon feedback loop has a curator who reads every joined repo's CANON-NOTES.md; docs/canon/NOTEBOOK.md carries a last read: line, and this script fails when that line is more than 14 days behind the newest note in any joined repo (check-notebook.mjs:2-5). It reads the repos over gh api, so on a token that cannot see a private sibling it reports the repo as unreachable rather than passing; CI runs it with the default token, which is why it is advisory there until a cross-repo read token is a repo secret (docs/plan/71-canon-feedback-loop-plan.md, Q1).

check-prop-reach.mjs and prop-reach-baseline.json#

node scripts/check-prop-reach.mjs [--list]

Every prop a component declares should reach a rendered difference a test can see, because a declared prop that silently does nothing blames the caller forever (check-prop-reach.mjs:3-7). This v0 counts a prop as exercised when some rendering test passes it; it does not prove the test asserts the effect, and it does not see props inherited through extends. The unexercised count is a ratchet against prop-reach-baseline.json: lower the baseline as tests land, and the script exits 1 when the count rises. In-repo only.

kit-inventory.mjs#

node scripts/kit-inventory.mjs [--find <term> [--json] | --check]

"Does the kit already have X" as a command instead of a multi-grep that picks the wrong surface (kit-inventory.mjs:3-7). Derives from three sources it never edits (the showcase registry, the contract docs, the generated do-nots page), writes packages/ui/docs/KIT-INVENTORY.md and llms.txt, and answers --find with ranked matches across every surface. --check exits 1 when the written inventory drifts from the registry. No deps, no network, no model call; the judging half is packages/ui/prompts/claims.md. The same script ships in the package as the kit-inventory bin, where it answers against the consumer's own resolved tree.

sync-method-docs.mjs#

node scripts/sync-method-docs.mjs [--check]

The package carries its method, not only its exports: verbatim copies of docs/design-language, docs/app-patterns and docs/DO-NOTS.md under packages/ui/method/, so a consumer's install says how to build with the kit. The repo copies are the single pen and this script is the copies' one writer. --check exits 1 on a drifted copy and, since 2026-09-03, on any doc in the four published trees (canon, app patterns, breakdowns, kit contracts) that mkdocs.yml's nav does not name, because the in-app browser sweeps directories and the published site reads a hand-kept list, and the list had fallen seventeen docs behind. By hand before a docs commit; not a CI gate by owner ruling.

dev-guard.mjs and dev-guard.md#

npm run dev-safe (in apps/playground)

The Next dev server's memory grows with every route visited and never lets go. The guard owns npm run dev and recycles it only when it is both over a memory ceiling and idle, so the recompile lands while you are away from the keyboard, never mid-edit (dev-guard.md:16-19). Tuning knobs are env vars, DEV_GUARD_MAX_MB, DEV_GUARD_MAX_PCT, DEV_GUARD_IDLE_CPU, DEV_GUARD_IDLE_N, DEV_GUARD_MIN_UP, DEV_GUARD_INTERVAL, all documented in dev-guard.md. Opt-in and local: agents do not start it on their own; they may offer it when someone hits dev-server memory pain (dev-guard.md:5).

push-triad.sh#

scripts/push-triad.sh [--dry-run]

Pushes versable-builder, walmart-mvp, and speedway in one run, "written so a human approves once instead of three times" (push-triad.sh:4). It refuses to push anything it cannot account for: every repo must be on main, clean of staged work, and tracking a remote that has not moved underneath it. A repo that fails a check is skipped, never forced, and the script still tries the others. --dry-run shows what would go and pushes nothing. The repo paths are fixed at the top of the file for this machine.

release-kit.sh (superseded)#

Not the release path. The kit, the toolkit and qsync publish from the publish-kit workflow on a version bump pushed to main (packages/ui/README.md, Releasing); a hand publish races it and the workflow then skips. This script is the pre-workflow laptop publish, kept only as the fallback if the workflow is down; --dry-run is safe to read, nothing else in it should run.

Adding a script#

A script here carries a header comment that says what it is for in a sentence a human can read, a Usage: line, and any env knobs it reads; then it gets a row in the table above and a section below it. If something else calls it (a package.json script, a workflow, a rule in CLAUDE.md), the section names the caller. Zero em dashes, the same as every file in this repo.

  • CONTRIBUTING.md section 3, running things, and section 7, docs.
  • CLAUDE.md, the prose rules and the release rule these scripts serve.
  • README.md, the repo explained for a human reader: who reads what, the map by what you came to do.
@versable-git/ui · reference, canon, and method, read in place