scripts/check/core/
August 21, 2026 ยท View on GitHub
| gate | fails when |
|---|---|
check-dtcg.ts | a file under contracts/design/ is not strictly-conformant DTCG 2025.10: a missing $type, a colour that is not a structured sRGB object, a dimension that is not a {value,unit} pair. |
check-user-scale.ts | a dimension under contracts/design/ says nothing about what happens to it when the user asks for larger text. CSS emits px whichever answer a token gives, so nothing here is about a rendered page: it is about the platforms that HAVE that setting, where the answer cannot be derived from a value and a unit and a target inventing one per token is how a screen ends up scaled by a ratio nobody chose. AXES is the closed set of three and an axis nothing takes fails, so the set cannot outlive the cases it names. A group declares for the leaves under it and a leaf overrides its group, which is Arena's rule and not DTCG's: 2025.10 admits $extensions on a group and defines no inheritance for it. Only a dimension is required to resolve one, because a multiplier is not a dimension and answers by construction. |
check-role-contract.ts | contracts/design/roles.json stops being a declaration: a role carrying a $value, which is an answer and belongs to a style plugin; one declaring no $type, so nothing can check an answer to it; one with no $description, which is a question nobody can answer on purpose; or a keyword declaring no closed set, since the set is the whole of what earns that type. It is the gate check-dtcg.ts cannot be, and why that one excludes this file by name: a DTCG token without a $value is not a DTCG token, so a conformance walk would measure the file against a claim it never made. |
check-compat-aliases.ts | COMPAT_ALIASES in the shipped audit and the custom properties contracts/design/colors.css defines stop agreeing. The compatibility layer maps Arena's own names onto the palette and ships with the packages, so a rule in a style plugin reading var(--mute) assigns a step of the ramp under another name rather than answering a role, and the raw-colour rule stays silent because an alias is a token. The audit reports one inside a declared plugin directory, and it cannot read that stylesheet to know the names: it ships beside the CLI and depends on nothing but its siblings, so the list is a constant there. An alias the layer gains and the rule does not name is a value a plugin can assign with nothing reporting it; one the rule names and the layer has dropped is a report about a property nothing declares. Comments are stripped before the parse, since a name written in prose above a block is not a definition. |
check-catalogue.ts | an entry under plugin-style-store/catalogue/ stops being usable by a project that takes it: a role of the kernel left unanswered, a colour role answered with anything but a {color.*} alias, a name that is not a role at all or a type disagreeing with contracts/design/roles.json, a stylesheet reaching a component through anything but a data-arena-part hook or spelling @layer itself, a config missing a polarity, a palette key or a font slot, an ENTRY.md missing or carrying no line beginning Take this entry when, or a reading floor closed in either polarity. It is the gate nothing else can be, because no entry is compiled: the catalogue is inert by decision, so check-style-plugin.ts and its coverage sibling read default and complete and nothing here, and the projects that would notice a broken entry are the ones that took it and are not in this tree. The floors are reached by resolving the entry against the token catalogue in memory, which is the same arithmetic a consumer's build does and the only way to do it where no sheet is emitted; they are measured only once the answers themselves are clean, since an alias resolving to nothing would otherwise report twice and the second report would name a floor rather than the defect. The $description clause of keyProblems is what stays out: it asks for reasoning ENTRY.md already owns and no plugin in this tree carries. |
check-style-plugin.ts | the root style plugin leaves a declared role unanswered, or a scope this build emits closes the reading floors below what a reader is owed: prose leading under 1.5, a heading's leading under 1, or a prose measure outside 45 to 90 characters. Totality is the sharper half: a custom property with no value is invalid at computed-value time, so the declaration reading it is dropped and the whole property disappears, and an unanswered role is a missing border rather than a plainer appearance. The floors are measured against the values the generated sheets actually resolve to rather than against the file that authored them, in the base scope and in every theme scope beside it, because a value restated under a theme can close a paragraph up in the one scope nobody measured. |
check-style-plugin-coverage.ts | a role complete answers the way default does, or a part no rule in plugin-style-store/complete/plugin.css paints. A role nothing can reach is a role that does not exist, and a part nothing can paint is a hook that was emitted and wired to nothing, so the question it asks is whether the surface the kernel advertises is the surface it exposes. complete is the witness: it is not coherent as a design and does not try to be. The parts are read from the manifests rather than from the generated modules, so a manifest that gained a slot and never rebuilt is caught, and the painted set comes from paintedParts in the shipped audit rather than from a selector parsed a second time. |
check-polarity.ts | the two files that state which way a theme points stop agreeing. scripts/lib/core/palette-read.ts:THEMES maps a polarity to the selector its palette answers to, and contracts/design/colors.css declares what that selector means to a browser, as color-scheme and as --picker-invert. Neither file reads the other, which is what makes the comparison worth anything. It is the gate no token gate can be: color-scheme is not a custom property, so parseDecls skips it and every check built on that reads straight past, and a render suite cannot see it either because happy-dom paints nothing. A scope carrying a direction THEMES names no theme for fails too, so a polarity cannot be added to the stylesheet alone, and a theme whose name is not a POLARITIES word fails before any block is read, because the polarity IS the keyword the property takes and a third word is one the browser drops as invalid. What it does NOT reach is a page: whether a palette's floor is painted at all is the project's decision, and Arena declares the direction rather than the paint. |
check-tokens-generated.ts | the committed contracts/design-generated/*.generated.css drifts from what the DTCG source would emit: a missing selector, a changed value, or a custom property that is committed but no longer generated. |
check-token-collisions.ts | two of the generated stylesheets declare the same custom property on the same selector, so source order silently decides which one a page gets. It is the gate check-tokens-generated.ts cannot be: each file is in sync with its own source and the collision only exists where they meet. A role named after a scale step is the usual cause, which is why the tracking roles are track-* and the size roles step-*. |
check-fonts-generated.ts | a family declared in contracts/design/typography.json has no @font-face in fonts.generated.css, so a token names a font nothing loads. |
check-ramp.ts | the 8-slot categorical chart ramp stops clearing its contrast and colour-vision-deficiency gates on either surface. |
check-boundary-contrast.ts | the answer this build ships sets a control's or a field's border to zero and the fill it sits on does not carry the 3:1 WCAG 1.4.11 asks of a component's boundary, measured in both themes. A surface's border is deliberately not asked about: 1.4.11 is about components and a card is not one. |
check-text-contrast.ts | a text token fails contrast against the surface it is declared for, or a retired token reappears in colors.css; REMOVED names each one with the token that replaces it. |
core because every one of these reads contracts/ and assets/ alone, and no framework
layer.
Each of these reads its sheets inside main(), and none of them at module top level. The
graph collects a node by importing the script that declares it, so a gate doing its work where
an import reaches it cannot be collected, and three of these exited the process outright.
check/arena/script-imports.test.ts:importTimeEffects(path) holds that over every script under
build/, generate/ and check/.
Every X.test.ts beside a gate covers that gate.