Deviations
Not every project can satisfy every standard. A project migrating
off a legacy CSS framework keeps the old palette vendored in
global.css until the @dunn.dev/reference
import lands; the
design-system check
fails on exactly that assertion until it does. A legacy project
mid-migration can't always close every gap before the merge
window. The
deviation register is how a project legitimately documents a
gap, and how the gap stops being a deviation when the
reasoning behind it changes.
Where it lives
One file at the project root: .deviations.yaml.
A YAML array of entries. just check reads it
(inside _fail(), so both the local and CI paths get
it for free) and reports a matching FAIL as
WAIVED instead: a distinct, separately counted
outcome, never a silent PASS. Only for entries
whose reason is non-empty; anything else falls
straight through to FAIL unchanged.
A project with no deviations has no .deviations.yaml
at all. Absence is the conformant default.
The format
# .deviations.yaml — at project root
- standard: design-system
assertion: grep:global.css
reason: |
Migrating off a legacy CSS framework. global.css still carries
the old palette in parallel until the new build lands; the
@dunn.dev/reference import ships as part of that change.
owner: andrew@dunn.dev
- standard: design-system
assertion: grep:package.json
reason: |
Same migration. package.json declares @dunn.dev/reference once
the build swap lands.
owner: andrew@dunn.dev
- standard: design-system
assertion: grep:package.json
reason: |
Transitional. Pinned to a local file: spec while validating a
breaking-change candidate against @dunn.dev/reference. Flip
back to the registry version once the change ships.
owner: andrew@dunn.dev
Field semantics
standard: the contract directory name (repository,pipeline,design-system).assertion: the assertion id as it appears after the contract name inFAILoutput (e.g.grep:global.css,file-contains:README.md). Per-assertion entries are canonical: write one entry per failing assertion, named explicitly. The wildcard*is deprecated: it reads as "we didn't enumerate" rather than "we considered each assertion and concluded it doesn't apply".just checkstill honors it: it matches every assertion under that standard, so existing wildcard entries keep working. Write new entries per-assertion going forward; older repos may carry wildcards, and flip them on next touch.reason: one paragraph naming what's true about this project that makes the standard not apply OR what's in flight that will close the gap. Future-you reads this; write to that audience.owner: email of the person accountable for the entry. Audit point if the deviation outlives the reason.
What the deviation register is NOT
It is not a place to silence a check that you don't feel like satisfying. It is not retroactive cover for project-local hacks that should have been routed to the right repo. It is not a permanent license; an entry stands only while its reason stays true. A reason that stops being true is deleted at the next audit, or when the project re-conforms, never renewed in place.
The vacuity-detection principle applies here: an entry without a reason that names either a permanent project shape or an in-flight unblock is a vacuous register entry. Treat it as you would a vacuous check: broken even if it passes.
When in doubt, route first
Before adding a deviation, ask where the fix belongs. Most "I need a deviation here" instincts are actually "this fix belongs in the catalog / design package / arcana instead of this project." The deviation is the last resort, not the first move.