new-validator
One rule, held by the build
The new-validator skill is a checklist the coding agent follows to turn a rule written in prose into a script the build runs. Once the script is in the chain, breaking the rule stops the build. Below are the steps, then one real validator traced from the files it reads to the result a build sees. The full skill file is on Skills.
The steps
- Study the referencesRead the Registries section of
CLAUDE.md, then one script of each shape:validate-doc-refs.mjson its own, thegenerate-component-md.mjspair, andvalidate-rendered-spacing.mjsfor built HTML. - Decide the shapeA validator checks a rule and fails the build. A generator writes a derived file and gets a validator that holds the file to its source. A countable fact usually wants a registry as well.
- Decide where it runsWhat the script reads decides the slot. Source files go in the chain that runs before every build. Built HTML goes after the website build. A check against an earlier release needs the git tags. Anything that reads the network runs by hand, in no chain.
- Write the doc blockThe script opens with its filename, what it guards and why a failure matters, and why it runs where it runs. Any exception lives there or in a named constant beside it, with a reason a stranger could check.
- Follow the house conventionsCollect every error before exiting, and make each one name its fix. Normalise Windows line endings once, on the read. Build paths from the script’s own location and compare them with forward slashes.
- For a generator, compare bytesThe generator exports its assemble function and writes only when run directly. Its validator regenerates in memory and compares with the file on disk, so a hand edit or a stale file fails.
- Wire it inAdd it to
validate-registryin the rootpackage.json, generators ahead of validators. A website check also joinswebsite/package.json. A post-build check joinsverifyand CI in the same change. - Close the loop and prove it failsGive the rule its line in CLAUDE.md. Run the script, break the rule on purpose to see the message, restore it, then run the whole chain. A check that cannot be made to fail is not guarding anything.
A hex code, end to end
A hex code like #1A2B3C may live in one file, tokens-primitives.css. Every other tier reaches it through a token, so a theme can swap the value in one place. Two validators hold that line. validate-token-references.mjs fails a semantic token written as a hex instead of a var(). validate-raw-values.mjs reads every stylesheet the package ships and fails a hex in component CSS. Say one is typed into a component’s stylesheet. The next build fails with the file, the line and the value, and says what to do: use the token that owns it, or mark it ds-allow(color) with a reason when it is deliberately off the scale.
Where a hex may live
Each tier has one answer, and a script holds it. A var() fallback is a consumer’s override hook, so the raw values check reads past it.
| Where | Held by | A hex there |
|---|---|---|
Primitives file | The tier rule itself | Allowed |
Semantic token | validate-token-references.mjs | Fails |
Component CSS | validate-raw-values.mjs | Fails |
Component CSS under ds-allow(color) | validate-raw-values.mjs | Sanctioned |
A var() fallback | validate-raw-values.mjs | Sanctioned |