mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-12 14:16:28 +03:00
bfa60147968a03147fa35cc246f58d46de7dcdbc
23
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5856161014 |
Plate pipeline, asset producer rewrite, and two detector rules for CSS standing in for material
generate-image.mjs --plate produces one raster region of the measured spec from the comp crop, scores it against the crop, and refuses under --min. The asset producer's job becomes producing the spec's plates. Detector gains organic-clip-path (many-vertex polygon / curved path() clips) and buried-raster (raster under a near-opaque wash or at near-zero opacity), wired into both engines with fixtures. AI-assisted (Claude). Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
19e400e392 |
Retire the single-font rule
One family with weight and size contrast carrying the hierarchy is a legitimate type system, and in practice the rule mostly punished minimal pages: it was the loudest cross-rule noise on the fixture corpus's should-pass columns. Removed from the registry, both engine paths, the regex page analyzers, and the devtools category map; the negative assertions stay as resurrection guards, and the text-content analyzer index base shifts down one with the removal. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
33a1c5fcae |
Ban kickers outright: one eyebrow above a heading is one too many
The detector's repeated-section-kickers rule waited for three tracked labels before calling the pattern; generated pages earn the finding on the first one. Retire that id and replace it with kicker-above-heading, which flags any tracked-caps or small-caps label block sitting directly above an h1-h4 or heading-role element, at full warning severity. The candidate gate absorbs the false-positive shapes the repetition count used to paper over: editorial category-and-date meta lines, breadcrumbs with separators, legal and chapter numbering, application panel context labels, nav landmarks before page titles, and stat callouts with the label below the number. Hero-scale h1 eyebrows stay with hero-eyebrow-chip so one element gets one finding, and the static cascade now carries font-variant so small-caps kickers register. The craft floor entry moves from caution to ban in the same breath. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
6ff9f957ac |
Add radial-spotlight-glow detector rule
Flags the decorative low-opacity chromatic radial-gradient "spotlight" washed behind a hero or section and fading to transparent, an AI-slop reflex the saturated radial-halo gate lets slip (e.g. rgba(80,111,255, 0.26) -> transparent on a mobile hero). Gates: a non-repeating radial-gradient whose last stop is transparent, whose visible stops are all low-opacity (alpha < 0.45) with at most two of them, at least one chromatic (channel spread >= 24 exempts neutral vignettes), on a decorative-scale surface (width >= 240, height >= 160, exempting badges/avatars/small lights). The alpha band is disjoint from radial-halo (>= 0.7), so the two never double-report. Wired into both element loops (static-html + injected browser) with the pure checkRadialSpotlight shared by both adapters. TDD fixture with 5 flag / 9 pass shapes. Browser-path sweep over the eval corpus: 29 hits on 11 pages, 0 false positives. Count 59 -> 60. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
daec380cdb |
Add undersized-ui-text rule for functional text below an 11px floor
The existing `tiny-text` rule owns long body copy and deliberately exempts the UI furniture layer (nav, footer, links, buttons, labels, uppercase micro-labels). That left a real gap: a build shipped its entire furniture layer (nav links, category names, timecodes, meta rows) at 8px because the chosen pixel font only steps in 8px increments, and the design hook waved it through as merely "not on the DESIGN.md ramp" -- which the model resolved by adding 8px to the ramp. Being on the ramp launders the token, not the legibility problem. New `undersized-ui-text` quality rule closes that laundering path: - Flags interactive and short content-bearing text (links, buttons, nav items, labels, table cells, meta rows, timecodes) below an 11px floor. The floor holds inside a footer; only non-interactive legal smallprint gets the softer 10px floor. - Ignores the design system entirely, so a value ON the ramp is still flagged. - Uppercase letterspaced micro-labels stay in scope (still functional). - Exempts sup/sub, visually-hidden (sr-only) text, and code/terminal contexts. em/rem/%-sized text that computes at or above the floor never fires. - Complements tiny-text without double-flagging: long non-furniture body copy stays with tiny-text. Implemented as a single check in checkQuality (rules/checks.mjs), so both the static-html (jsdom) and browser adapters pick it up through the unified per-element path -- no dual wiring. Registered in registry/antipatterns.mjs. TDD: fixture tests/fixtures/antipatterns/undersized-ui-text.html (7 flag / 7 pass shapes), failing test first, then implement. Full fixtures suite 64/64. Deferred (blocked by an active release-gate eval reading build/_data/dist): regenerate the browser bundle (bun run build:browser -> cli/engine/detect-antipatterns-browser.js) and the extension detector (bun run build:extension -> extension/detector/detect.js + antipatterns.json) so the standalone browser/extension artifacts carry the new rule. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
270f4d20aa |
Make em-dash-overuse an advisory rule with browser parity
Em-dashes are used legitimately by humans, so em-dash-overuse fired far too often. Reclassify it as the first advisory-tier rule: detected, but never a failure. Engine - Add `advisory: true` to the rule metadata schema (em-dash-overuse is the first). findings.mjs stamps `advisory: true` on advisory findings so every consumer can partition without a registry lookup. Rule count stays 58. - Raise the firing threshold from a flat 5 dashes to two gates: an absolute floor of 8 and a density of about one dash per 500 characters of body text. A long article that uses a few em-dashes no longer trips; a short, dash-per-clause page still does. Entity decoding (mdash, numeric, hex) is unchanged. Thresholds live in shared/constants.mjs so every engine agrees. Browser parity - The browser bundle carried a registry entry but no logic, so the overlay and extension could never flag it. Add checkEmDashOveruse / checkEmDashOveruseDOM in rules/checks.mjs (reads rendered text, no entity decoding needed), wire it into the injected page-level pass, and carry the advisory flag through serializeFindings so the overlay/extension can render it with the mildest affordance. CLI - Advisory findings print under a separate dimmed "Advisory" section, are excluded from the failure count, and never change the exit code (an advisory-only scan exits 0). JSON keeps them with `"advisory": true`. `--no-advisory` suppresses them entirely. Hook - Advisory rules are skipped by default in both the per-edit and Stop deep-pass hooks, so the hook never nags about them. Opt in with `.impeccable/config.json` -> `detector.advisoryRules: "include"`. Tests - Fixture + threshold + browser-adapter coverage; advisory-skip default and opt-in for the hook; formatFindings partitioning. The em-dash-overuse stand for a deferred copy rule in the tier tests is swapped to marketing-buzzword. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
144cee5c36 |
Fix detector coverage for generated UI tells
Remove provider gating, share grid-background detection across source and rendered scan paths, and update the detector catalog and tests.\n\nAI-assisted: prepared by Codex at Paul's request. |
||
|
|
77c7d8e0fc | Refine product and visual work lifecycle | ||
|
|
ed7a6fbe4e |
detector: text-occlusion + first-viewport-column-overflow (57 -> 59)
Two browser-engine quality rules, both warning severity. text-occlusion / element-overlap fires on three shapes: an opaque decorated box painted over a text element (elementFromPoint confirms real coverage, box >= 30%), one text run buried under another when at least one side is a positioned layer (text >= 45%, so line-box leading bleed between stacked flow blocks does not count), and an inline element whose opaque fill leaks past its line onto a neighbour (the class-name collision bug). A large headline whose edge overhangs a bounded content card is caught as an element collision even when the text stays on top. Gradient scrims, decorative SVG emblems, fixed/sticky overlays, floats, and raw image backdrops (contrast territory, deduped against the pixel low-contrast rule) are exempt. first-viewport-column-overflow fires when a multi-column opening section runs one column past 140% of the viewport while a sibling fits inside one screen, the stretched-hero signature. Single-column pages and full-page heroes with no fitting sibling are exempt. Validated: fires on the diagnosed repros, clean across a 60-sample sweep. Fixtures + browser tests added. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
dc0b25d393 |
detector: hero pulsing-dot promotion, nav-CTA contrast gap closure, shape-assembled-illustration (56 -> 57)
Item 1 (hero liveness theater):
- pulsing-dot now merges declarations per selector across rule blocks
(cascade-approximate), descends into media queries, and strips
prefers-reduced-motion: reduce overrides before the predicate runs.
Catches the shipped split-block constructions (size in the base rule,
animation added later or inside a no-preference media block).
- Dots whose element sits inside a header/nav landmark are promoted to
error severity (string-level landmark ranges in both engines); the
browser engine additionally promotes dots resting in the first ~900px.
- blinking-cursor findings in the first ~900px or inside header/nav are
promoted from advisory to warning.
- Per-finding severity overrides now flow through static-html,
browser-injected serialization, and detect-url.
Item 2 (nav-CTA contrast constructions):
- The a24-opus 01/002 header CTA already fires (specificity cascade +
oklch + var() all resolved); systematic sweep found two remaining
escapes and closes both:
- own gradient background on a SAFE_TAGS element (checkColors styled-
control exception now treats an own gradient as an own surface,
contrast measured against the worst stop)
- ::before/::after full-cover surface (static cascade marks pseudo
surfaces; browser adapter reads the pseudo computed style) so text is
measured against the surface the browser actually paints
- nav-cta-constructions fixture locks all eight computable construction
families; background-image: url() remains unflaggable by design.
Item 3 (shape-assembled-illustration, slop/advisory):
- New rule for large inline SVGs composing a pictorial scene from >= 8
primitive shapes at >= 200x200 intrinsic size with >= 3 distinct fills.
Charts (axis labels), stroke-only technical drawings, icons/logos
(small explicit size), and pattern-tiled backgrounds are exempt.
1.8 percent fire rate over the 3069-sample eval corpus, all verified
pictorial scenes; zero fires across val-a22/val-a24.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
c98f5d42ed |
detector: script-error, content-hidden-at-rest, edge-flush-cards + chip contrast and inline-overflow widenings (53 -> 56)
Three new rules and three widenings, all from confirmed eval-corpus
escapes found by eye:
script-error (quality, error severity, URL engine): pageerror listener
attached before goto catches uncaught exceptions AND parse errors (a
syntax error fires during the initial parse, long before load). Deduped
by message, capped at 3. A JS typo was silently deleting whole pages.
content-hidden-at-rest (quality, error, URL engine): after the main
at-rest scan, an instant-scroll reveal sweep (bypasses scroll-behavior:
smooth, which silently defeated the first sweep design) gives every
IntersectionObserver reveal its chance to fire, returns to top, then
measures the share of text characters still at opacity 0 / visibility
hidden. display:none / [hidden] / aria-hidden subtrees stay out of the
denominator. Fires above 30% with a 200/150-char floor. Calibration on
30 corpus samples: broken repro holds 83% after the sweep, all clean
samples (including 0.75-0.93 at-rest reveal pages) drop to <= 7%.
edge-flush-cards (quality, warning, browser): cards with their own
opaque background or 2+ borders inside a horizontal scroller, flush
against one edge of the clip box at rest (< 8px, > -24px so deliberate
mid-card peeks stay exempt) while keeping a gutter on the other side.
Grouped per scroller. Repro: transit-mobile pager whose first snap
panel is 407px wide inside a 390px clip. New --viewport WxH CLI flag
makes mobile-width URL scans reachable (--viewport 390x844).
Chip/badge contrast widening: the SAFE_TAGS styled-button exception in
checkColors now covers any text-bearing element painting its own opaque
background at >= 9px font, not just a/button. The shipped miss: a span
SEV-2 chip whose white text lost a specificity fight and rendered
muted-on-red at 1.2:1. Static adapter also resolves var() own-bg via
the custom-property map so the gate engages on token backgrounds.
background:none cascade fix: the background shorthand now resets
background-color/-image when it names neither (and no var()). Exposed
by the chip widening: pre code { background: none } left an earlier
surface color standing and manufactured 1.1:1 phantom findings.
text-overflow inline-owner widening: inline elements have no client
geometry (clientWidth 0) so the scrollWidth path never saw them, and
their block parent owns no direct text. New branch measures the inline
rect against the nearest block container's padding box (16px floor,
transform-path exempt). Repro: nowrap span.v spilling 45px past its
grid cell.
The round-3 nav-CTA contrast escape (val-a22-opus obs 003 header CTA)
was verified already covered at HEAD by the earlier parseAnyColor
oklch fallback; both engines fire 3.6:1 on the repro, no change needed.
FP sweep across 36 val-a21/a22/a23 samples: new rules fire only on
their repros (script-error also catches a second genuinely broken
sample); static-engine delta is limited to the chip repro plus two
borderline-but-real chip findings on one sample.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
cc8906ecaa |
detector: add heading-rhythm and blinking-cursor rules (51 -> 53)
heading-rhythm (quality): a heading binds to the content it introduces, so its rendered space above must exceed its space below. Browser-only: measures real getBoundingClientRect gaps (margin collapsing, flex rows, and section padding make authored margins untrustworthy), merges eyebrow labels into the heading cluster, requires same-column edges, and exempts first-in-container headings, bounded bands, and small cards. Fires only when 2+ headings on a page invert the rhythm. blinking-cursor (slop, advisory): a decorative blinking caret (solid block, underscore bar, or block glyph) bound to an infinite blink animation in the landing region of a page. Real editable surfaces (contenteditable, role=textbox, inputs) are exempt; round pulsing dots stay with the pulsing-dot rule. Verified against eval corpus repros: heading-rhythm fires on the val-a18 observability sample Paul flagged (6 headings, 0px above vs 40px below) and blinking-cursor on the val-a19 hero terminal cursor; 10 other samples across both runs stay clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
7a99e1725d |
detector: four human-review rules — nav-CTA oklch contrast, numbered section labels, floating side-tab stripes, repeated card text
Four gaps found shipping in Opus 4.8 eval samples during human review: 1. low-contrast (extended): the browser adapters parsed text/own-bg colors with parseRgb only, so Chrome's oklch()-serialized computed colors silently skipped every contrast check — a flat dark-on-dark nav CTA (broader nav selector beating the button class) shipped at 1.5:1 undetected. checkElementColorsDOM and readOwnBackgroundColor now fall back to parseAnyColor. Near-threshold ratios print two decimals so a 4.497 finding no longer reads "4.5 needs 4.5". 2. NEW numbered-section-labels (slop, advisory): tiny (<=13px) styled numeric index labels riding beside section headings, repeated across 2+ sections with distinct indices. Sibling of repeated-section-kickers (which deliberately excludes bare numeric labels); handles both the direct prev-sibling shape and label-before-heading-wrapper shape. List/nav/table/card-item numbering is exempt. 3. side-tab (extended): the vertical pseudo-element stripe scan required the stripe to touch both corners (top/bottom 0 or height 100%), so a left accent bar inset a few px from each end evaded it; small end insets (<=20px each) now count. Added a browser-side pseudo-element check (getComputedStyle(el, '::before'/'::after')) since runtime- assigned custom-property colors are invisible to the text scanner. Selection-state exemptions stay as narrowed: only aria-selected=true / aria-current / active-class markers exempt, plus button/link affordances on the horizontal variant. 4. NEW repeated-container-text (quality): the same literal string (>=4 chars, contains letters) rendered 3+ times at 3+ structurally distinct positions inside one bordered/elevated container. Parallel/templated repetition (table cells, calendar grids, nav lists, identical sibling rows) never counts — structural signatures, not word lists. Verified: each rule fires on its repro sample via the file:// browser scan; clean eval samples add no new findings (the new low-contrast hits on other samples are genuine sub-AA oklch button pairs). Full test suite green; browser bundle regenerated; README/homepage rule counts bumped 49 -> 51 (docs-integrity test enforces them). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ea44f514f9 |
detector: grid-background variants, dash-prefix eyebrow, marquee rule, inset-shadow stripes
Four gaps from human review of gpt-5.6 eval artifacts: 1. codex-grid-background variants: the block scan now also matches the inverted end-of-tile hairline form (transparent calc(100% - Npx)) and reads the tile cell from the background shorthand's `/ Npx Npx` slot, not just background-size declarations. A single hairline layer qualifies when tiled by a px pair cell (page-scale line field); percent-tiled single hairlines (background-size: 25% 100% rules on data-viz tracks/graphs) stay legal. 2. hero-eyebrow-chip branch C (dash-prefix): sentence-case, regular- weight microlabels above the h1 announced by a short chromatic ::before/::after bar (8-80px x 1-6px, accent fill). Static cascade marks dash-pseudo targets during rule collection; the browser path reads getComputedStyle(el, '::before'/'::after'). 3. New `marquee` slop rule: <marquee> elements, and infinite animations bound to keyframes with >= 20 percentage points of X travel. Percent travel only — px-travel loops are bespoke product animations (waveform playheads, progress sweeps). Centered elements animating other properties (constant -50% X), non-infinite slide-ins, rotations, and pulses never qualify. 4. side-tab inset box-shadow variant: single-edge inset shadows (3-12px offset on one axis, no blur/spread, chromatic) drawn as stripes on cards/badges/menu items. Selection-state indicators ([aria-current], [aria-selected], [role=tab], active/current/selected hints, interaction states) stay exempt; the same stripe repeated unconditionally on every item flags. Narrow fixed-width glyphs (logo marks) are exempt. isTabContextElement narrowed to match: bare nav ancestry no longer blanket-exempts top/bottom border stripes — only explicit tab semantics or state markers do. Browser bundle regenerated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
1a4b5c2fa2 |
detector: hover-state contrast + color-mix/compositing, radial-halo rule, top/bottom stripe variant, file:// browser scans
Four changes driven by human design review of eval artifacts:
1. Static engine contrast fidelity (nav-CTA cascade miss):
- parseAnyColor evaluates color-mix() (premultiplied sRGB mix; exact
for the dominant `color-mix(in oklab, C n%, transparent)` chip form)
- extractStaticColor captures color-mix() balanced instead of plucking
"transparent" out of the expression
- resolveBackground composites translucent layers over the opaque base
in both engines instead of skipping (static) or returning them
as-if-opaque (browser)
- NEW hover pass in the static cascade: :hover rules are matched via
state-stripped selectors, merged per-property against the resting
cascade with real specificity, and checked for WCAG contrast on
styled controls (checkHoverContrast). Catches the recurring miss
where a broader selector (.nav-links a:hover) beats the CTA's own
hover color and drops the pair below AA.
2. New `radial-halo` slop rule: chromatic radial-gradient wash (visible
saturated center -> transparent) as a decorative background on a dark
page. Exempts achromatic vignettes, opaque-end sheens, px-stop dot
textures, url() photo layers, and translucent (<0.7 alpha) staged-
light washes. Separate id from dark-glow so dashboards track the
gradient-drawn variant independently.
3. side-tab horizontal variant: 3-12px chromatic border-top/bottom (and
top/bottom-anchored full-width pseudo stripes) on cards/badges flag as
side-tab. Exempt: tablist/nav/aria-selected underlines, link/button
affordances, table cells, hr, state-conditional pseudo stripes, and
>12px bands. Badge-shaped spans (own visible background) participate.
4. CLI: file:// URLs route to the Puppeteer browser engine (~2s on a
50KB page), and detect --json findings now carry the registry
`category` field so downstream QA loops can separate mechanical slop
tells from judgment calls.
Fixture policy update: flat 3px top-accent cards moved from should-pass
to flag columns; tablist-underline and 16px-band pass cases added.
Browser bundle regenerated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
cfdb7d4c81 |
detector: catch pseudo-element side stripes; add pulsing-dot rule
Two gaps surfaced by human eval review of real artifacts: 1. side-tab missed the pseudo-element variant. The accent stripe drawn as an absolutely-positioned ::before/::after (left/right: 0, top+bottom: 0 or height: 100%, narrow width, colored background) uses no border property at all, so neither the element-level border checks (pseudo elements never enter the static cascade or DOM walk) nor the border-left/right regexes could see it. New scanCssTextForPseudoStripe scans stylesheet text for that shape, mirroring the border rule's gates: >= 3px thick (<= 12px), chromatic fill (var()-resolved, neutral dividers skipped), full height against a side edge, with the blockquote/prose exemptions preserved. 2. New pulsing-dot rule (slop): small circular "live" indicator dots (<= 16px, border-radius >= 40% or pill values) bound to an infinite animation whose keyframes vary opacity, scale, or box-shadow — or pulse/blink/ping names when the keyframes aren't in the scanned text — plus the Tailwind animate-ping/pulse + rounded-full + tiny-size utility combo. Rotation-only keyframes (spinners) never flag, including when they hide behind a pulse-like name. Both scanners live in checkHtmlPatterns, so the static-html engine and the browser bundle share the same detection path. Browser/extension bundles regenerated; docs rule count bumped to 47. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
2c62f0f4f9 |
de-SaaS the skill: mode-aware rules, neutral runtime injections, diversified examples
Fix batch from the visitor-mode bias audit. The skill's four modes (Persuade / Operate / Read / Experience) now reach the places that were still hard-coded to a SaaS-marketing default: - palette.mjs: rewrote 45 seed blurbs in material/world terms. The 29 tech-tool-world moods (13 Linear-indigo variants, 6 Figma-era, 5 climate-tech, 3 fintech, 2 Glossier DTC, incl. seed-201's docs-page CTA red) lose all company names and product-category words; Aesop trimmed from 17 blurbs to 4 and Klim from 7 to 4, excess rewritten as unnamed material terms. Also carries the earlier bg-block rewrite (brand refs out of the composition doc). - init.md: register explainer now names the four modes and the family each belongs to (stored value stays brand/product for compatibility); Conversion & proof interview + PRODUCT.md section gated to Persuade surfaces only (Experience/Read get no CTA/belief-ladder/proof). - critique.md: Nielsen heuristics 7 and 10 may score n/a on Persuade and Experience surfaces, total renormalized to the applicable max, snapshot records which were n/a; working-memory examples diversified beyond dashboard/pricing anatomy. - Register headers in bolder/delight/quieter/colorize/layout/animate/ typeset renamed from Brand:/Product: to Persuade + Experience: / Operate + Read:; typeset and layout gain one Read-specific sentence (steady reading measure; navigable linearity). - animate.md: plan checklist and implementation order lead with feedback and transitions; the single entrance moment comes after, scoped to modes that invite it. - codex.md: mock inventory says "primary-action treatment (when the surface has one)" instead of assuming a CTA. - delight.md: loading/empty-state/console-egg examples diversified beyond SaaS; streaks/badges scoped to Operate surfaces with recurring tasks. - distill.md: step-removal and next-action lines neutralized away from signup/checkout/CTA vocabulary. - document.md: canonical button label GET STARTED -> SAVE CHANGES; signature components gain a non-marketing example. - antipatterns registry: single-font rule renamed to "Single font without hierarchy" with a description that permits one family when weight/size contrast carries hierarchy. Staged provider copies regenerated via build:skills:release for the touched files only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
0d72991bc8 |
detector: catch glow shadows in any color format, zero-offset halos on any background, and text-shadow glows
- parseAnyColor now covers oklab(), hsl()/hsla(), hwb(), and ~35 common named colors on top of rgb/rgba/hex/oklch, so checkGlow sees the color regardless of authoring format (Chrome preserves oklch() in computed styles, which the old rgba-only match silently passed). - checkGlow gains a second tell: a zero-offset chromatic box/text-shadow with blur > 4px is flagged on ANY background (the halo pattern); achromatic zero-offset shadows and focus rings stay legal. The existing chromatic-blur-on-dark-background rule is unchanged in semantics but now parses every color format. - text-shadow is checked wherever box-shadow was (browser DOM path with inherited-value dedupe, static engine via new textShadow cascade support, text engines). - The page-level text scan (regex engine + checkHtmlPatterns) is now a shared scanCssTextForGlow that resolves single-level var() refs against custom properties collected from the same text; unresolvable var() in a shadow color position is skipped, never guessed. Its dark-page heuristic accepts var()/oklch backgrounds but only when declared at root scope (body/html/:root or body inline style). - dark-glow keeps its id; registry name/description updated to cover both cases. Validated: three eval repro samples with oklch / var(--x) glows that previously produced zero findings now flag on the static CLI path; ten known-good largerun samples stay clean except one with genuine amber status-dot halos (0 0 12px oklch(.73 .17 65/.4)). Note: cli/engine/detect-antipatterns-browser.js and the extension detector are generated and still need 'node scripts/build-browser-detector.js' + 'node scripts/build-extension.js' once builds are unblocked. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
f40e2f8f0a |
Add mechanical pre-scan for typeset and layout (#345)
* Add mechanical pre-scan for typeset and layout commands. Introduce --scope filtering, layout/type rule scopes, DESIGN.md font-size validation, and pre-scan steps in the skill references so agents run detect before LLM judgment. Fixes #149 Co-authored-by: Cursor <cursoragent@cursor.com> * Add isolated sub-agent orchestration for typeset and layout pre-scans. Run the mechanical detector and visual assessment in parallel sub-agents so deterministic findings cannot anchor LLM judgment, matching the critique pattern Paul requested on PR #345. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix: reject bare --scope so detect never scans unscoped by mistake. When --scope had no value, the CLI dropped the flag and ran a full scan instead of failing, which could silently use the wrong rule set during typeset/layout pre-scans. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix: require both typeset and layout assessments in sub-agents. Close a loophole where agents ran only the mechanical pre-scan inline by interpreting "running both" as permitting one inline assessment. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Abdul Wahab <abdulwahab@Abduls-MacBook-Pro-2.local> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
f5c1bd65ae |
Add codex-grid-background detector rule (#328)
* Add codex-grid-background detector rule Detects the Codex two-axis grid-line background tell: a single background value carrying two or more hairline `linear-gradient(... 1px, transparent 1px)` layers (one per axis), usually paired with a repeating `background-size` cell. Gated behind --gpt like the sibling codex tells, off by default. Counts hairline stops within a single background declaration (not across the page) so unrelated single-axis ruled lines don't add up to a false flag, and matches the stop directly rather than parsing whole gradient layers, since colors like oklch(...) carry nested parens. Extends the gpt-tells fixture with one flag case and two pass cases (single-axis rule, two-color blend), regenerates the browser detector bundle, and bumps the rule count 44 -> 45. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Require tiling background-size for codex-grid-background Address review: two hairline gradients alone draw a fixed crosshair, not a grid. Scope detection to a single style block (CSS rule body or inline style attr) and require both >=2 hairline stops AND a tiling `background-size` px cell in the same block, matching the skill rule's "plus background-size" wording. Add a crosshair-without-tiling pass case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Scope codex-grid-background hairline count to background values Address review: count hairline stops only inside background/background-image declaration values, not the whole style block, so a hairline in an unrelated property (mask-image, border-image) can't stand in for the grid's second axis. Add a bg+mask-image hairline pass case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
51d01e3a5f |
[codex] Add design-aware detector rules (#252)
* Add design-aware detector rules * Fix design-aware detector noise * Unify CLI and hook detector ignores * Fix remaining design-system review findings * Add detector ignore CLI * Fix design detector review findings * Fix design color source false positives * Fix core test suite registration * Add design-aware detector docs * Fix font priority design-system parsing * Fix color ignore value matching |
||
|
|
9ffd3211d5 |
Neo Kinpaku design system + Live Mode v3 (#169)
* Add neo kinpaku design system page * skill: rip out baked-in category recipes and saturated-default motion tropes Programmatic bias mining (impeccable-evals) traced four major defects back to specific lines in this skill that contradicted SKILL.md's own first-order-reflex warning: - brand.md "Pairing and voice" prescribed four category→aesthetic recipes (editorial → serif+sans, tech/dev/fintech → tight tracking, consumer/food/travel → script/display serif, creative → rule-break). These directly drove OpenAI's 76% extreme-negative letter-spacing on tech briefs and Anthropic/Google's 28-34% italic-serif-display slop on editorial/food briefs. Replaced with one sentence: the shape depends on the brand, not on the brand's category. - brand.md "Brand permissions" had "Typographic risk. Enormous display type, unexpected italic cuts, mixed cases, hand-drawn headlines, a single oversize word as a hero." — a four-for-one slop driver behind 97% OpenAI comically-large H1, 42% bad-SVG illustration, and the editorial-italic slop. Deleted outright. - typeset.md and teach.md repeated the same category recipes; trimmed to the principle without the recipe. - SKILL.md Typography: added a hard hero-H1 ceiling (clamp() max ≤ 6rem ≈ 96px), with a <codex> block to make it explicit since OpenAI over-indexes here (97% ≥128px vs 24% for Anthropic). - animate.md, bolder.md, brand.md: removed "staggered reveals" and "scroll-triggered transitions" as the prescribed default ambitious motion. By 2026 that's the saturated AI tell, not a choreography. Reserved stagger for legitimate list-sibling rhythm. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * skill: anti-cream + codex-specific defect bans + universal slop bans Second pass after measuring more biases against the eval corpus. - SKILL.md Color: explicit "cream/sand/beige body bg is the saturated AI default of 2026" rule. Tone down the "tint every neutral" line so it doesn't read as "default to warm-tinted near-white" (which OpenAI hits at 74% and Anthropic at 31%-47%). - SKILL.md Absolute bans: add universal bans for two slop patterns detected at 55-95% across providers — tiny uppercase tracked eyebrow above every section (the 2023-era kicker that's now AI grammar) and numbered section markers (01/02/03). Also explicit "text that overflows its container is the universal defect on tablet/mobile." - SKILL.md Absolute bans → <codex> block: ban the GPT-specific defects Paul annotated repeatedly — `border:1px solid` + soft-wide-shadow (≥16px blur) "ghost cards", `border-radius:32px+` over-rounding, hand-drawn/sketchy SVG illustrations (loose-sketch / *-sketch classes, feTurbulence paper-grain filters), repeating-linear-gradient stripes, "X theater" AI-slop copy phrases. - SKILL.md Motion → <gemini> block: the image :hover transform tell (38% Google skill-on rate). Hover effects on images add no info; the image isn't an action target. Animate card chrome, not the image. - SKILL.md Typography: hard display letter-spacing floor ≥-0.04em (OpenAI defaults to -0.075em → cramped). Existing hero ceiling <codex> block extended with the letter-spacing rule. - codex.md Step A example: stop seeding "warm-grounded (deep oxblood + cream)" as the warm-palette template, which primes the cream default. - colorize.md Tinted backgrounds: stop printing the literal cream recipe `oklch(97% 0.01 60)`; replace with brand-anchored guidance. - document.md examples: warm-ash-cream → cool-paper so the example doesn't seed cream as the canonical neutral example. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * skill: universal anti-slop bans + contrast/font-count/all-caps-body rules Third pass after measuring the rest of the cross-provider matrix: - Color: explicit "Verify contrast" rule. Low-contrast text fires at 68% across all providers skill-on (90+% off). The most common failure is muted gray body on a tinted near-white; light-gray-for- elegance is named as the single biggest cause of unreadable AI pages. - Typography: max-3-font-families rule. Overused-fonts (>4 families) fires at 28% Anthropic / 36% Google / 0% OpenAI skill-on; >50% off. Also: universal "no all-caps body copy" (moved from brand-only ban to Shared design laws since product-register also overuses caps). - Copy: anti-aphoristic-cadence ban targets Anthropic's signature "X. No Y." / "X. Just Y." voice (63% skill-on copy-slop rate, 77% off — the worst rate in the matrix). Once-is-voice / three-or-more- is-tell framing per the runner's copy-slop detector. - Copy: anti-SaaS-buzzword-string ban with the literal phrase list the detector watches for (streamline/empower/supercharge, trusted- by-leading, best-in-class/enterprise-grade/cutting-edge, etc). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * skill: strengthen anti-cream rule across full warm-neutral band Smoke validation showed the cream fix worked for Google + OpenAI but Anthropic Sonnet italian-restaurant still shipped `--paper: oklch(90% .018 88)` — cream just outside the L≥95% band the rule cited. Broaden the rule: - Band: OKLCH L 0.84-0.97, C < 0.06, hue 40-100 (was 95-97% / 60-95). - Name the token-name tells explicitly (paper / cream / sand / bone / flour / linen / parchment / wheat / biscuit / ivory) — the model defaults to one of these regardless of what hex it lands on. - Call out the specific brief patterns ("warm, traditional, family- coastal-Italian" / "editorial-restraint") that the model translates into cream by reflex. Then provide three explicit non-cream options: saturated brand color, true off-white at C=0, or darker mid-tone. Warmth in the brand is carried by accent + typography + imagery, not by body bg. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * v3.2.0: skill bias-fix release Bumps version from 3.1.1 to mark the four-commit skill cleanup that rips out baked-in category recipes (brand.md), saturated-default motion tropes (staggered reveals everywhere), the cream/sand body-bg AI tell, codex-specific defects (1px+wide-shadow, over-rounding, hand-drawn SVGs, stripes, X-theater copy), the extreme-letter-spacing default, and universal slop bans (all-caps eyebrow on every section, numbered-section markers, all-caps body, font-family-count > 3, aphoristic copy cadence, SaaS buzzword strings). Plus a hard hero-H1 ceiling (clamp() ≤6rem) and a Gemini-specific image:hover transform block. Validated against ~190 post-fix samples — see impeccable-evals biases tab for per-provider deltas. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * drop "no pure black/white" rule entirely The rule was contested in the design world and causing more damage than good — pushing every page into the tinted-near-white default which is the cream/sand AI tell we already explicitly ban elsewhere. Vercel, SVKMS, Brutalist sites, et al. use pure black/white successfully; the skill shouldn't second-guess that. Skill markdown deletions: - SKILL.md Color: drop the "Never use #000 or #fff" bullet. - color-and-contrast.md: drop the "Never Use Pure Gray or Pure Black" subsection, the "Never pure black" table-row prescription, and the "Avoid: Using pure black for large areas" bullet. - colorize.md: drop the "NEVER use pure black or pure white for large areas" bullet. - polish.md: drop the "Tinted neutrals: No pure gray or pure black" half of the bullet (the gray-on-color bullet survives). Detector code (cli/engine): - registry/antipatterns.mjs: remove the `pure-black-white` entry. - rules/checks.mjs: remove the three `findings.push({ id: 'pure-black-white', ... })` emit points (inline #000 bg, Tailwind bg-black class, plain-HTML scan path). - engines/regex/detect-text.mjs: remove the two pure-black-white regex rules (CSS `background: #000…` + Tailwind `bg-black`). - detect-antipatterns-browser.js: regenerated via scripts/build-browser-detector.js. Tests: - detect-antipatterns-fixtures.test.mjs: invert the assertion that pure-black-white fires; expect it to NOT fire post-v3.2. Drop the Tailwind bg-black-opacity edge-case test (no longer relevant). - detect-antipatterns.test.js: drop the standalone "detects pure- black-white in styled-components" test and remove pure-black-white from the multi-detector assertions in PricingCard, globals.css, and GlobalStyle.tsx tests. 166 bun tests pass; 24 node fixture tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * skill: strip example patterns from copy rules, strengthen gemini block v3.2 rerun validation surfaced two issues: 1. Copy-slop detector fires more on Gemini under v3.2 (48% → 84%) than under no-skill baseline. Root cause: the anti-aphoristic-cadence rule printed the literal "X. No Y." / "X. Just Y." patterns as examples, and Gemini imitated them as the recommended voice. Same recipe-becomes- bias trap we hit with brand.md:116's "Enormous display type, unexpected italic cuts, mixed cases, hand-drawn headlines" enumeration. Fix: describe the cadence as a rhythm ("serious statement, then punchy short negation") without printing literal patterns. Buzzword list trimmed to a single inline phrase family rather than quoted strings. 2. Gemini image:hover transform Gemini-tell hadn't dropped (31% off → 32% v3.2). Strengthen the <gemini> block: explicit "Never animate <img> elements on hover", call out the Tailwind group-hover:scale / group-hover:rotate / group-hover:translate parent-hover patterns by name (Gemini was reaching for these via Tailwind even though the prior text talked about :hover on the image directly). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * skill: simplify context loading and inline register directive Replaces load-context.mjs's JSON output with a tight markdown block from the renamed context.mjs. The script now extracts PRODUCT.md's `## Register` field and appends a `NEXT STEP:` directive naming the matching reference (brand.md / product.md), which moved Gemini from skipping the register load entirely to honoring it. Drops the `.impeccable.md` auto-migration; makes IMPECCABLE_CONTEXT_DIR a lazy escape hatch consulted only when the default paths come up empty. Setup is now four bullets in one list. The DESIGN.md nudge is gone; in its place, a "familiarize with the existing design system" step that calls out CSS / tokens / running app as authoritative sources alongside DESIGN.md. The standalone `### Register` H3 stays for the cascade rules (task cue → surface → register field). New LLM-backed test suite at tests/skill-behavior/ runs five scenarios against claude-haiku-4-5, gpt-5.4-mini, and gemini-3.1-flash-lite via Vercel AI SDK. Captures real tool traces, asserts on context.mjs calls, brand.md loads, and teach.md fallback. Skips cleanly when API keys are unset. 13-14/15 pass; only stable failure is the v3.2.0-era gpt-mini S4 "don't re-run" regression. Adds @ai-sdk/google as devDep and the test:skill-behavior npm script. Touches em-dashes in skill/SKILL.md and four reference files so `bun run build:skills` passes its skill-prose validator. teach.md and document.md drop their "re-run the loader to refresh session cache" steps since the agent's own write is now the freshest source. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * skill: merge orphan reference files into command sub-skills + inline S-tier invariants Two related restructurings: 1. SKILL.md now carries the cross-domain invariants that catch defects in any project (contrast/placeholder/gray-on-color, similar-font pairing, text-wrap, tabular-nums, centered-stack default, Flex/Grid choice, auto-fit grids, semantic z-index, reduced motion, stagger vs section-fade, premium motion materials, focus-visible, placeholders-aren't-labels, dropdown overflow trap, button/link copy). Greenfield-only rules (theme picking, color strategy, tinted neutrals) live under "New projects only". 2. Reference files merged into their command counterparts: - spatial-design.md -> layout.md - motion-design.md -> animate.md - color-and-contrast.md -> colorize.md - responsive-design.md -> adapt.md - ux-writing.md -> clarify.md - typography.md -> typeset.md (bolder.md redirected) - cognitive-load.md + heuristics-scoring.md + personas.md -> critique.md craft.md and shape.md "load references" lists updated to new file homes. interaction-design.md stays standalone (no 1:1 command verb). Net: 36 -> 27 reference files. Same content, fewer files, no orphaned reference loaded only from craft.md. Also extends the routing rules: if the user's first word doesn't match a command but the intent clearly maps to one, load that command's reference and proceed as if invoked. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * skill: add sub-command + existing-project scenarios; move sub-command load to step 2 Adds three new LLM-backed scenarios to tests/skill-behavior: - S6: `/impeccable polish` → loads polish.md - S7: `/impeccable audit` → loads audit.md - S8: existing SvelteKit project (PRODUCT.md + DESIGN.md + src/app.css + src/lib/components/*.svelte + src/routes/+page.svelte) → agent reads at least one project code file to understand the existing design system S6/S7 surface a real model-floor: gpt-5.4-mini reads brand.md, reads the target index.html, and just does the polish/audit without ever loading the sub-command reference. Stronger SKILL.md wording didn't move it. Captured in the README baseline as a known weakness. Claude and Gemini honor the load reliably. To fix Gemini on S6/S7, sub-command reference loading is now Setup step 2 (right after context.mjs), not step 4 — placing it before the model gets focused on "doing the work". Step 3 (design-system familiarization) is tightened to require at least one project code read even when a sub-command reference loads in step 2, so Claude doesn't laser-focus on the sub-command flow and skip the broader exploration. Two new fixtures: MINIMAL_LANDING_HTML (a tiny static landing page for S6/S7) and SVELTE_PROJECT_FILES (a minimal SvelteKit scaffold with tokens, components, and a routes/+page.svelte for S8). Both designed to look real enough that agents treat them as production code. Suite is now 24 tests across three providers; baseline is 21-22/24, with the stable failures being gpt-5.4-mini scenarios 6 and 7. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * skill: add reveal-animation safety rule (must enhance, not gate visibility) Class-triggered visibility transitions pause on hidden tabs and headless renderers. The italian-restaurant smoke produced a build where 2 sections shipped opacity:0 because the CSS transition never advanced past currentTime=0 (timeline paused). Added one-liner under Motion to prevent the antipattern: reveals must enhance an already-visible default, never gate content visibility on a class-triggered transition. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * skill: restore prescriptive cream/sand/beige paragraph Bisection across 5 historical skill commits on Gemini 3.5 flash fast lane n=3 found that |
||
|
|
e1d3ea0b6f |
Detector architecture v2: static engine, benchmarks, lab, and visual contrast (#156)
* Add detector benchmark lab and visual contrast fallback * Expand visual contrast fixture coverage * Add browser visual contrast fallback * Show visual contrast overlays in detector lab * Fix detector lab short viewport layout * Fix detector lab visual overlays * Add visual contrast to browser scan overlays * Avoid browser scroll jumps during visual contrast scans * Resolve visual contrast lazily on scroll * Refresh detector lab visual counts lazily * Update pnpm lockfile for static parser deps * Address Bugbot detector API comments * Report extension visual contrast errors * Refactor detector into engine modules * Address Bugbot detector comments * Fix latest Bugbot detector notes * Fix visual contrast fixture labels * Refine detector lab fixtures * Fix stale detector overlay references * Fix detector lab fixture URLs * Fix typography lab fixture highlights * Fix typography lab page-level signal * Fix visual overlay lifecycle cleanup * Remove dead spotlight timer cleanup * Make browser async APIs reject consistently |