Skip to content

feat(limitless-mcp): complete the lint catalog — 57/57 detectors#35

Merged
tknatwork merged 5 commits into
mainfrom
feat/limitless-wave11
Jul 14, 2026
Merged

feat(limitless-mcp): complete the lint catalog — 57/57 detectors#35
tknatwork merged 5 commits into
mainfrom
feat/limitless-wave11

Conversation

@tknatwork

Copy link
Copy Markdown
Owner

Completes the design-system lint catalog to 57/57 detectors — the 6 rules deferred in PR #34, implemented across two waves with the same test-first + adversarial-review discipline.

Wave 11a — offline rules (opt-in)

  • default-variant-is-base-tuple — reuses the defaultVariantTuple gathered in Wave 10b
  • multi-brand-alias-discipline — pure alias-graph, config {brandPrefix, roles?}
  • contrast-fallback-export-samplingreframed as offline: flags translucent (alpha<1) tokens via existing colour data (no exportAsync)

Wave 11b — plugin-gather rules

  • no-instance-restyle-override (warn) — bounded INSTANCE scan; top-level-only, style-fields-only
  • component-set-has-code-mapping (opt-in) — getDevResourcesAsync, adoption-gated, honestly worded as a dev-resource proxy
  • detached-component-frame-signal (opt-in info) — name-prefiltered exact name+child-type-seq match

Quality

  • 46 offline tests, golden fixture still 0
  • Adversarial review (6 lenses, refute-by-default): 3 confirmed / 3 refuted → fixed the getDevResourcesAsync≠Code-Connect gate defeat (now opt-in + honest) and the multi-brand first-mode-collapse bug (now mode-threaded)
  • Live-verified on the real 1,121-variable Nectar DS: rule_failures: [], precise low-noise findings (2 real instance restyles, 0 false positives on detached-frame/default-variant/code-mapping, 24 accurate translucent-token infos), the new instance/dev-resource/frame gather runs clean across 48 pages with no timeout

Every new gather is bounded (cap + *Truncated flag; truncation ⇒ false-negatives only). All opinionated/house-style rules are defaultOn:false, keeping the default lint precise (advise, don't dictate).

🤖 Generated with Claude Code

tknatwork and others added 5 commits July 15, 2026 01:52
…(51→53/57)

Both opt-in (defaultOn:false), so the default lint stays precise:
- default-variant-is-base-tuple (components): uses the enrichment's
  defaultVariantTuple already gathered in Wave 10b; flags a COMPONENT_SET
  whose default variant isn't each axis's first-authored (base) option.
  "base" is a house convention, hence opt-in.
- multi-brand-alias-discipline (theming, config {brandPrefix, roles?}):
  pure over the alias graph. A brandable semantic (accent/action/… role)
  should alias THROUGH a brand/* token so re-branding is a single-layer
  swap; flags one whose chain reaches a primitive without a brand hop. The
  brand layer itself is exempt. Only meaningful for multi-brand DSs, so
  opt-in + requires the brand layer declared via config.

+3 tests. Only the 4 data-dependent rules remain pending (need plugin
gather for instance overrides / dev resources / frame fingerprints /
exportAsync sampling). 41 tests pass; golden still 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…4/57)

The rule's real job (info) is to FLAG color tokens whose alias-resolved
contrast is untrustworthy — i.e. TRANSLUCENT paints that composite over
their backdrop. Alpha is already in the snapshot's colour values, so this
needs no exportAsync/pixel-sampling in the gather (which the design panel
rightly flagged as the heaviest/riskiest path). Resolves each semantic
fg/bg/border token's alias chain to its raw alpha and flags alpha < 1,
pointing the user at pixel sampling for those specific tokens. +1 test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extends the plugin lint_run with three bounded gathers (all degrade to
silent on an old plugin; a NEW plugin always emits the keys — even [] —
so undefined=not-gathered vs []=scanned-clean):

- no-instance-restyle-override (components, warn): scans INSTANCE nodes
  (INSTANCE_SCAN_BUDGET=20k, MAX_RESTYLED_INSTANCES=300) and emits ONLY
  TOP-LEVEL instances carrying STYLE overrides (fills/strokes/effects/
  *StyleId) — nested instances are skipped so a restyle isn't
  double-reported on its container. Membership == the finding.
- component-set-has-code-mapping (code-output, warn): getDevResourcesAsync
  per COMPONENT_SET, batched via Promise.all + capped (120). CONDITIONAL
  on adoption — only flags missing mappings once SOME set has one, so a
  team not using Code Connect isn't spammed; capped/errored sets are
  undefined and excluded from numerator+denominator.
- detached-component-frame-signal (components, info, OPT-IN): name-prefilters
  the FRAME population by collision with a standalone-component name, then
  flags an EXACT name + direct-child-type-sequence match. Both fingerprints
  must be complete (childCount===seq.length) so a capped >40-child seq never
  yields a partial-prefix false match. Opt-in given inherent heuristic noise.

Every new array is capped + *Truncated-flagged; truncation causes false
negatives only, never a fabricated finding. Server types + detectors in
lockstep. +3 tests (45 total); golden still 0. Detector coverage now
57/57 — the entire canonical catalog is implemented.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6-lens adversarial review (refute-by-default): 3 confirmed, 3 refuted
(instance scan, detached-frame, default-variant, backward-compat, and
contrast-fallback all held).

1. component-set-has-code-mapping (medium+low, same root): the plugin
   sourced hasCodeMapping from getDevResourcesAsync, which returns ANY Dev
   Mode link (Storybook/Jira/docs) — NOT Code Connect. So one stray link on
   one set tripped the adoption gate and spammed a false finding on every
   other set, and the suggested remedy (set_code_mapping) writes a disjoint
   server-side store so the warning never cleared. Fix: make the rule
   opt-in (defaultOn:false) and reword it honestly as a dev-resource-presence
   proxy (not Code Connect); drop the misdirected set_code_mapping remedy.
2. multi-brand-alias-discipline (medium): the multi-hop alias walk advanced
   via Object.values(target.valuesByMode)[0] — the target's FIRST mode
   regardless of the mode being followed — making results order-dependent
   and able to fabricate a finding on per-mode-divergent chains. Fix:
   thread the current mode through the walk (fall back to first mode only
   when absent), mirroring resolveColor/resolveAlpha. Added a multi-hop
   per-mode-divergent regression test that fails under the old collapse.

Server-only (no plugin change); neither rule changes a default lint
(both opt-in). 46 tests pass; golden still 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tknatwork
tknatwork merged commit 414c1cf into main Jul 14, 2026
2 checks passed
@tknatwork
tknatwork deleted the feat/limitless-wave11 branch July 14, 2026 21:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant