new-validator
Skill9 starsChanged 6 days agoMITView on GitHub
Author a new validator or generator script and chain it into the validate-registry build chain. Use when asked to add a validator, add a generator, build-enforce an invariant, or wire a new check into the build.
In this repository33 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- accessibility-auditSkill
- animation-jump-checkSkill
- api-consistencySkill
- chat-qualitySkill
- checkpointSkill
- component-consistency-auditSkill
- component-doc-pageSkill
- content-auditSkill
- dependency-advisorySkill
- design-qaSkill
- dogfoodSkill
- drift-auditSkill
- first-deploySkill
- landSkill
- link-rotSkill
- new-componentSkill
- new-pageSkill
- new-presetSkill
- new-templateSkill
- new-tokenSkill
- parkSkill
- pre-deploySkill
- release-postSkill
- releaseSkill
- responsive-sweepSkill
- security-auditSkill
- seo-auditSkill
- shipSkill
- super-shipSkill
- token-auditSkill
- token-renameSkill
- visual-reviewSkill
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 2 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- could be automatic: a setting could enforce it
- Very specific: line 33
Normalize CRLF: Every text file the script reads for parsing, matching, or byte-comparing gets .replace(/\r\n/g, '\n') immediately after …
- Very specific: line 49
Add it to the validate-registry entry in the root package.json — generators run first, validators after, so keep a generator ahead of …
- Very specific: line 55
Verify: Run the new script directly first (node scripts/<name>.mjs), break the invariant on purpose to see it fail with a useful message, …
- Distinctive: line 59
Never hand-edit a generated file to make a validator pass — regenerate it, and commit the regenerated content with the change (CI's drift …
- Distinctive: line 60
No counts, no hand-copied script lists, no machine-local paths in the doc block or the docs — point at the authoritative home
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
new-validator
Author a new script under scripts/ — a validator that fails the build on a broken invariant, or a generator that owns a derived surface — and wire it in so the invariant can never drift silently again.
When invoked
Use this skill when asked to add a validator or generator, to build-enforce a rule that is currently only prose, or to give a new registry or generated surface its guard — phrases like "add a validator for X", "make the build catch this", "generate this file from the registry".
Instructions
CLAUDE.md's Registries section is the philosophy: every displayed fact has one authoritative home, and a validator is how a home stays authoritative. Read the section first, then skim one reference of each shape before writing a line — scripts/validate-doc-refs.mjs for a standalone validator, the scripts/generate-component-md.mjs + scripts/validate-component-md.mjs pair for a generated surface, and scripts/validate-rendered-spacing.mjs for a built-HTML check.
- Decide the shape. A validator checks an invariant and fails the build; a generator owns a derived file and gets a companion validator that holds disk to source. If the fact being guarded is countable or displayed, it probably wants a registry + generator + validator, not a lone check — CLAUDE.md's Registries intro owns that call. A generated JSON surface in a shape this repo defines also wants a hand-written JSON Schema beside it and a row in
BINDINGSinscripts/validate-json-schemas.mjs, which proves every document against its schema on each build (the component schemas andsrc/tokens/values.jsonare the pattern); a surface in a standard format (the DTCG token files, the shadcn registry) is held to that format's own shape by its generator's validator instead. - Decide where it runs. Four slots exist, and the source of the data decides:
- The
validate-registrychain (the entry in the rootpackage.jsonis the authoritative list): for anything that reads source files, registries, or generated data. It runs before every build viaprebuild/prestorybook/prebuild-storybook, so nothing here may depend on build output. - The post-website-build slot in
verify(rootpackage.json) and CI's website job (.github/workflows/ci.yml): for checks that need the finished build. The tail of theverifyentry is the authoritative list of residents; each one's doc block argues why source-level checking would over- or under-report, and a new arrival owes the same argument. The slot has two sub-shapes: validators that read the prerendered HTML from disk, and served-site checks that start a real server (and, for the smokes, a real browser) — the latter sharescripts/served-site.mjsfor the server lifecycle and the registry-derived route sample, so a new served-site check starts there rather than rolling its own. - The git-history slot in
verifyand CI's library job, never invalidate-registry: for a check whose baseline is an earlier revision, which needs the repository's tags.scripts/validate-api-compat.mjsis the one resident: Vercel's tag-less shallow clone runs the registry chain before every deploy, so a missing baseline there must not block the site, and the script warns rather than fails outside CI (wherefetch-depth: 0makes a missing tag a real failure, so the gate cannot pass vacuously). - Run-on-demand, in no chain: a script whose source is the network or a running server never joins the build — it is invoked by hand (or by a loop skill), commits its output, and records the exclusion with a written reason in its own doc block.
scripts/sync-worldmap-land.mjsandscripts/check-external-links.mjsare the reference set. A generator in this class still gets a header in its output pointing back at the script, but no regenerate-and-byte-compare companion — CI cannot re-fetch the network, so step 7's determinism rules apply to chain generators only.
- The
- Write the doc block. Every script opens with
#!/usr/bin/env nodeand a/** ... */header that states, in this order: the filename; what it guards and why the failure matters (numbered checks if there are several); and why it runs where it runs. The doc block is the script's authoritative record — sanctioned exceptions live in it (or in a named constant beside it) with a written reason a stranger could audit, the wayCONDENSED_ROUTESinvalidate-corpus-coverage.mjsdoes it. Never enumerate facts another file owns; point at the owner. - Follow the output conventions. Collect failures into an
errorsarray rather than exiting on the first; on failure print a✗ <what> validation failed:header, each error on its own line, andprocess.exit(1); on success print one✓line with a count. Every error message names the fix — for a stale generated file, the exact regeneration command. Normalize CRLF. Every text file the script reads for parsing, matching, or byte-comparing gets
.replace(/\r\n/g, '\n')immediately afterreadFileSync, with the comment:// Normalize CRLF so Windows checkouts validate identically to CI. const read = (path) => readFileSync(path, 'utf8').replace(/\r\n/g, '\n');This is the convention most often missed: a script that passes on macOS and CI but fails every Windows checkout (
core.autocrlf=true) has been fixed repeatedly in this repo —git log --grep=CRLFfinds the history. Normalize on the read, once, not scattered through the checks.Keep paths portable. Derive the repo root from the script's own location —
join(dirname(fileURLToPath(import.meta.url)), '..')— never fromprocess.cwd()assumptions or a machine-local path. Build paths withjoin(), and before comparing a path to another string (a glob, a registry slug, a relative display path) normalize separators the wayscripts/component-docgen.mjsdoes:path.replaceAll('\\', '/'). On Windows,join()hands back backslashes; a comparison that assumes/fails only there.The same portability discipline covers child processes: a
node_modules/.binshim cannot bespawnSynced on Windows, andspawn('npm', …)fails there without a shell. A script that shells out resolves the tool's real bin entry and runs it underprocess.execPathinstead —scripts/validate-package-publish.mjsandscripts/served-site.mjsare the two references (the latter also owns the Windowstaskkillstop).- For a generator, use the regenerate-and-byte-compare pattern. The generator exports its assemble function and
outputPath(oroutputDir), and guards its write behindconst isMain = process.argv[1] === fileURLToPath(import.meta.url)so the validator can import it without side effects. It must be deterministic — no network, no timestamps, nothing not already derivable from the sources — or the byte-compare flaps. The companion validator imports the assemble function, regenerates in memory, and compares against the CRLF-normalized disk read; for a multi-file surface it also checks the reverse direction, so an orphan on disk fails too.validate-site-corpus.mjs(single file, plus content screens) andvalidate-component-md.mjs(folder, both directions) are the two references. - Wire it in.
- Add it to the
validate-registryentry in the rootpackage.json— generators run first, validators after, so keep a generator ahead of everything that imports it. - If it is website-relevant, add it to the website's own chains in
website/package.json(that file is authoritative for the subset — a library-only check stays out), split by kind: a generator goes in bothpredevandprebuild, a validator inprebuildonly.predevexists to start dev from fresh data, and a validator there would failnext devon exactly the stale state the generators are about to fix. - A post-build check goes at the end of
verifyin the rootpackage.jsonand into CI's website job in the same change — CLAUDE.md's CI & Local Verify section owns the keep-in-sync rule.
- Add it to the
- Close the loop in the docs. A script guarding a new countable collection gets a one-line row in CLAUDE.md's Registries table (registry, accessor, validator — follow the existing rows), with the mechanism detail in the script's own doc block rather than in CLAUDE.md; a script extending an existing surface amends that surface's row; a standalone invariant gets a sentence in the CLAUDE.md section that owns its subject.
scripts/validate-doc-refs.mjswill hold every path and npm script the new prose names to reality. - Verify. Run the new script directly first (
node scripts/<name>.mjs), break the invariant on purpose to see it fail with a useful message, restore, thennpm run validate-registryend to end. A post-build check needsnpm run verifyinstead, since nothing shorter builds the HTML it reads.
Guardrails
- Never hand-edit a generated file to make a validator pass — regenerate it, and commit the regenerated content with the change (CI's drift guard diffs after the generators run)
- No counts, no hand-copied script lists, no machine-local paths in the doc block or the docs — point at the authoritative home
- A check that reads build output never goes in the
validate-registrychain; it runs before any HTML exists - A validator with no failure mode is decoration — if you cannot make it fail by breaking the invariant, it is not guarding it
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

