The source scanner: strip the comments, follow the var()
qa/source-audit.mjsThe failure it prevents
A well-commented codebase makes a naive source scanner useless: every careful note explaining why the team avoids transition: all gets reported as a transition: all. And the moment the design system moves its numbers into custom properties, border-radius: var(--radius) parses as NaN, the check compares NaN to 12, fails for the wrong reason, and someone deletes it. Both bugs arrive on day one of every source-scanning audit ever written.
The prompt
Build `qa/source-audit.mjs`: the fast, deterministic half of the design gate. It reads files, never a browser, runs in seconds, and imports `design/contract.mjs` for every number it checks.
**Bug one, fixed before anything else: strip comments first.**
```js
function stripComments(src, path) {
// preserve newlines so line numbers still point at the right line
const noBlock = src.replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, " "));
return path.endsWith(".css")
? noBlock // `//` is valid inside a CSS url()
: noBlock.replace(/(^|[^:])\/\/[^\n]*/g, (m, p) => p + " ".repeat(m.length - p.length));
}
```
A prose note *about* a rule is not a violation of it. Replacing comment characters with spaces rather than deleting them keeps every `file.tsx:42` location honest.
**Bug two, fixed second: follow one level of `var()`.** To read a declaration out of a stylesheet and compare it to the contract: match the rule block by selector, match the property with a `(?<![-\w])` guard so `padding:` does not also match inside `--card-padding:`, then substitute any `var(--name)` from the custom properties declared in that same block. Without this, a design system that correctly tokenized its own geometry produces NaN, and **a NaN failure is worse than a miss** — a miss is silent, a NaN teaches the team the check is broken and they stop reading its output entirely.
**Then the checks. Each one imports its numbers from the contract; no literal appears twice in this repo.**
1. **Contract binding.** For every contract entry with a `binding`, read the declaration out of the app and fail when they disagree, printing both values and both file:line locations. This is the check that makes the contract real rather than aspirational.
2. **Motion contract.** Match real declarations only — `/\btransition(?:-property)?\s*:\s*([^;{}]+);/g` — then: fail on `all` ("it animates every property, including ones added later"); split on commas and fail when a part's first token is in a BOX_METRICS set (`height width top left right bottom margin* padding inset block-size inline-size min/max-width/height`), because animating a layout property moves the page under the reader. In component files, flag the framework's catch-all utility class (`transition-all` and its responsive prefixes) from `className` string literals, and flag animation-library props — `animate={{…}}`, `initial`, `exit`, `whileHover`, `whileInView` — whose keys are box metrics. Print the offending value truncated to 80 chars on the following line.
3. **Token re-declaration.** For each contract token that has a canonical owner module (an easing tuple, a shadow, a duration), grep the exact literal across the tree and report every occurrence outside the owner with file:line. A re-typed tuple drifts the moment one copy is tuned, and nothing will tell you.
4. **Stale-number drift.** Read `CONTRACT.retired` — the values that used to be right — and grep prose, comments and markdown for them. A comment still teaching last year's column width will be read by the next person, and by the next agent, as the spec. **Be honest in the output that this check has a shelf life**: it encodes a migration, and entries should be deleted from `retired` once the tree is clean, not left to accumulate.
5. **Placeholder copy.** After comment-stripping, fail on `Lorem ipsum` / `LOREM` in anything that renders. Comment-stripping is what makes this safe: a parked section kept in a comment block is a decision with a note attached, not shipped placeholder text.
**The generation step you must not skip.** Checks 2–4 need regexes that describe *this* codebase's dialects, and those cannot be authored in advance. Before writing them: scan the tree for the N competing ways this project does one thing — how a card/panel wrapper is expressed, how a section header is expressed, how vertical spacing is applied — and print a frequency-ranked inventory (`"rounded-2xl border" ×31, <Card> ×12, function Panel() ×4`). Show me that inventory, name which one is canonical, and only then write the patterns into the audit with the canonical one marked as allowed. **A dialect list invented without reading the codebase produces an audit that fails on correct code**, and that audit will be muted before it catches anything.
**Thresholds you invent ("more than 8 inline headers", "more than 400 words of copy in a component") must be printed with the measured value next to them and marked as tuned-by-feel in a comment.** They are calibrated against this codebase and nowhere else.
**Hand back:** the dialect inventory, the audit file, and a full run. State which findings you believe are true positives and which are the audit meeting an intentional exception you did not know about — then ask me before suppressing any of them.What it produces
qa/source-audit.mjs with comment-stripping, one-level var() resolution, contract-binding assertions, a motion contract (CSS transitions, utility classes, animation-library props), duplicate-token detection, stale-number drift and placeholder copy — preceded by a printed frequency-ranked dialect inventory of the codebase.
How you prove it works
Four edits, in this order. (1) Paste transition: all 0.3s ease; into a stylesheet — must FAIL with file:line and the value. (2) Paste /* we deliberately avoid transition: all here */ as a comment — must stay silent; if it fires, comment-stripping is not wired. (3) Change a bound custom property (radius 12px → 14px) — must FAIL printing both 14 and 12 with both locations, and must print numbers, never NaN. (4) Re-type the canonical easing tuple into a second file — must report both the owner and the copy.