`ai-color-palette` catches the palette nobody picked: the violet and the cyan a model reaches for when there is no design system. The browser sweep was reading only the hue, so a site whose own documented tokens land in those bands tripped it on every element wearing one. On impeccable-site's "Paper and instruments" pull request, where the DESIGN.md palette is a verdigris `oklch(70% 0.12 188)` on an `oklch(24% 0 0)` instrument face, that was 218 findings across 54 pages, all of them "Cyan neon text on dark background" against a token the author had written down. `check_element_ai_palette_dom` now takes the scan's design system and skips any gradient stop or text color the DESIGN.md declares, matched with the same `browser_colors_close` tolerance the `design-system-color` rule uses, so a color that rule calls declared is declared here too. A scan with no DESIGN.md, or one whose DESIGN.md has no palette, is unchanged: there is nothing to consult and every color stays in scope. Measured on that site (54 pages, headless Chrome, the in-page bundle): with the design system, ai-color-palette drops 218 -> 8; without one it stays at 218. The 8 survivors are the real thing, the purple gradients the /slop and /docs before-and-after exhibits ship on purpose. No design-system-* count moves. Assisted-by: Claude Code Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LQBUunp8QttxZqihybNmtL
Live browser assets
Two generated files, both tracked, both rewritten by cargo xtask bundle.
Do not hand-edit either one.
detect-antipatterns-browser.js is the in-page detector bundle: the rule
core (crates/core) compiled to WebAssembly by crates/wasm, concatenated
with the page JS in browser-bundle/ and the module embedded as base64.
crates/live/src/browser_assets.rs embeds it with include_str! and the
live server hands it to the browser as /detect.js, so the binary has to
carry it.
antipatterns.json is the rule registry (crates/foundation/src/registry.rs)
as [{ id, name, category, description }], the same slice
cargo xtask bundle vendors into the extension's extension/detector/. It
is tracked because extension/detector/ is not: a consumer working from a
source checkout or a repo tarball (impeccable.style counts and renders the
rules from it) has no other way to read the registry without a Rust
toolchain.
cargo xtask bundle # rewrites both (and extension/detector/)
cargo xtask bundle --check # fails when either is stale
The other browser scripts the live server serves (live-browser*.js,
modern-screenshot.umd.js) are not copied here: they are embedded straight
from skill/scripts/, the one copy the build also ships to every provider.