Architecture (for contributors)
dscheck-core resolver + matchers + checker (host-agnostic, browser-safe)├─ css-source.ts @theme/:root loader, var() alias chains, TW default theme merge├─ config.ts config discovery, ignore/allow globs, mtime-cached index├─ match.ts ΔEOK color distance (alpha-aware), scale steps, edit distance└─ check.ts checkDeclaration(prop, value, ctx) → Violation[] ← rules live here
eslint-plugin-dscheck thin adapter: JSX walk → checkDeclaration, host reporting/fixingstylelint-dscheck thin adapter: decl walk → checkDeclaration, host reporting/fixingdscheck (CLI) drives the hosts; adds baseline/report/SARIF/agent + worker pooldscheck-sarif SARIF builder + standalone stylelint formatterAdding a rule: implement it in core/check.ts (add the RuleId, return Violations
with matches), register the id in both adapters’ RULES arrays and preset configs, add
fixture tests in core plus one integration test per adapter, and add a docs page. The
adapters need no logic — they only map offsets to host AST positions.
The two invariants: every violation carries the nearest token with a real distance
(that’s what makes agent self-correction work), and every skip is deliberate and
documented (calc() internals, dynamic classnames, --tw-*). The corpus
(fixtures/corpus.json) pins finding counts on real repos — if your change moves a
number, justify it in the PR.