ADR-085
`audit-tokens.js` debt triage: exclusions, `audit-ignore` escape hatch, 7 promoted tokens
Context
The backlog carried a P2 ticket: ~5800 violations reported by node scripts/audit-tokens.js --ci (noted 2026-07-08, already down to ~2508 by this session — other work in between had organically reduced it), with the ticket explicitly flagging that "many are plausible" (e.g. direct component.json → primitive.* references) and asking for a triage with the Design System Lead.
A full categorized breakdown of the ~2508 violations showed four structural buckets accounting for ~96% of the total, none of which represented real drift in authored, enforceable source:
| Category | Count | Why not real debt |
|---|---|---|
packages/tokens/{css,js,tailwind,tokens}/ | 1758 (70%) | Style Dictionary build output, gitignored, regenerated by npm run tokens — hex values there are the resolved primitives doing their job, not drift |
decisions/*.md | 392 (16%) | ADRs cite historical hex values while explaining a past decision (e.g. "teal.9 → #12A594") — an ADR is immutable once active (decisions/README.md), not enforceable current code |
packages/components/agtc-*.js | ~19 | Verbatim gitignored copy of components/*.js — pure duplicate of violations already counted there |
scripts/figma/*.js | 45 (2%) | Figma Plugin API scripts read hex directly from tokens/*.json to apply Figma paints — the Plugin API has no CSS var() concept, literal hex is expected |
site/audit-lib.js's CONTRAST_PAIRS fixture (30 violations) turned out to be a fifth, narrower case: literal hex used as WCAG contrast-check test data, already self-documented in a comment as intentional — but audit-tokens.js had no escape-hatch mechanism at all (unlike scripts/audit-language.js's lang-audit-ignore), so a reviewed exception had no way to stop being reported.
A sixth, unrelated finding surfaced while investigating the file most responsible for the remaining real debt (site/build.js, 182 of the post-exclusion 750 violations): 7 CSS custom properties — --agtc-semantic-fontWeight-bold, --agtc-semantic-fontWeight-display, --agtc-semantic-radius-pill, --agtc-semantic-color-border-strong, --agtc-semantic-color-accent, --agtc-semantic-color-secondary, --agtc-semantic-color-tertiary — were declared locally in site/build.js's own :root extension block, under the --agtc-semantic-* prefix (implying they're part of the real token system), but were never actually added to tokens/semantic.json. The code even carried its own comment acknowledging this: "Brand semantic tokens (nouveaux — absents de tokens/semantic.json)". --agtc-semantic-fontWeight-bold/-display alone were used 60+ times throughout the file — an established de facto convention, not a one-off typo. This is audit-tokens.js's separate "Phantom tokens" check (#2: used in code but not defined), distinct from the "hardcoded values" check (#3) that accounts for the bulk of the ~2508.
Decision
1. scripts/audit-tokens.js now excludes, by path:
packages/tokens/{css,js,tailwind,tokens}/andpackages/components/— generated,
gitignored build output (mirrors the exclusion scripts/audit-language.js already had).
decisions/andscripts/figma/— historical ADR prose and Figma Plugin API scripts,
neither of which is enforceable "current source" in the sense this audit checks.
2. scripts/audit-tokens.js gains an audit-ignore line-comment escape hatch, mirroring lang-audit-ignore: a line containing the string audit-ignore is skipped by the hardcoded- value check. Applied immediately to site/audit-lib.js's CONTRAST_PAIRS (15 lines), the one already-self-documented case that needed it.
3. The 7 phantom tokens are promoted to real entries in tokens/semantic.json (and one new primitive, primitive.fontWeight.extrabold: 800 — no primitive step existed at that weight):
semantic.fontWeight.bold/.display— generic reusable weight utilities, distinct from
the existing per-context typography.*.weight tokens.
semantic.radius.pill— raw9999px(no primitive step exists at that value; same
precedent as component.badge.md.radius). This also fixes a real bug: the local shim had it at 999px, one digit short of the conventional "large enough to always fully round" value — harmless in practice (every real instance is far smaller than 999px, so the visual output is identical), but wrong on its face.
semantic.color.border.strong— aliasesprimitive.color.gray.6, matching the local
shim's value exactly.
semantic.color.brand.tertiary— new; aliasesprimitive.color.slate.9, completing the
accent/secondary/tertiary trio the code already treated as a set. accent and secondary already existed as real tokens (semantic.color.brand.accent/.secondary) with values identical to the fake local ones — site/build.js's usages were renamed to point at the real names instead of getting new aliases.
- Dark-mode:
brand-secondary's dark override already existed and matched.brand-accent
and brand-tertiary had no dark override at all under their real names — the fake shim's dark-tuned values (#ff8aa1, #6b7280, deliberately different from the light values for contrast) were carried over to the real token names rather than dropped, to avoid a silent dark-mode contrast regression.
border-strong's existing dark override already used the correct real name — no change
needed there.
4. site/build.js's local :root shim block removed (7 fake-semantic lines + 5 lines of duplicated primitive accent/secondary steps that were also just gitignored-primitives.css duplicates with identical values) — the real tokens now flow through site/build.js's own tokensCSS() function, which independently flattens tokens/semantic.json (it does not consume Style Dictionary's compiled output directly).
Rejected alternatives
| Alternative | Reason for rejection |
|---|---|
Triage all ~2508 violations line-by-line with audit-ignore comments, no path exclusions | ~2150 of them sit in gitignored generated output or immutable historical ADRs — annotating files that are either not committed or not meant to be edited again would be pointless busywork |
| Leave the 7 phantom tokens as an undocumented site-local convention | The naming already claims to be part of the real token system (--agtc-semantic-* prefix) and is used 60+ times — leaving it un-promoted keeps the phantom-token check permanently red for no reason, and keeps a real (if minor) radius-pill bug alive |
Rename brand-accent/brand-tertiary dark-mode usages to fall back to the light value rather than adding dark overrides | Would have silently regressed dark-mode contrast — the fake shim's separate dark values were deliberate, not accidental |
Consequences
node scripts/audit-tokens.js --ci: 2508 → 247 violations (103 critical + 144 warnings,
a 90% reduction). The remainder is genuine per-line triage in site/build.js, .stories.js, and a few test files — no more systemic bucket.
tokens/semantic.jsongainsfontWeight.bold/.display,radius.pill,
color.border.strong, color.brand.tertiary; tokens/primitives.json gains fontWeight.extrabold.
- Verified visually (Playwright, light + dark) after the change: every renamed/promoted CSS
custom property resolves to the exact same value as before in both themes — zero visual regression, confirmed by direct computed-style comparison, not just a screenshot glance.
- GitHub Projects "Dette audit-tokens.js" ticket stays at its board's in-progress status, not its done status (the board's Status field is French: Backlog/En cours/Terminé/etc.) — the remaining ~247 fine-grained items are being triaged in a follow-up pass within the same session.