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>
This commit is contained in:
Paul Bakaus
2026-07-13 13:09:54 -07:00
co-authored by Claude Fable 5
parent 50fc88ec85
commit 7a99e1725d
12 changed files with 1208 additions and 14 deletions
+17
View File
@@ -1477,6 +1477,7 @@ if (IS_BROWSER) {
const findings = [
...checkElementBordersDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
...checkElementPseudoStripeDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
...checkElementColorsDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
...checkElementMotionDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
...checkElementGlowDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
@@ -1526,6 +1527,22 @@ if (IS_BROWSER) {
addBrowserFindings(groupMap, document.body, sectionKickerFindings);
}
const numberedLabelFindings = checkNumberedSectionLabelsDOM()
.map(f => ({ type: f.id, detail: f.snippet }))
.filter(f => _ruleOk(f.type));
if (numberedLabelFindings.length > 0) {
pageLevelFindings.push(...numberedLabelFindings);
addBrowserFindings(groupMap, document.body, numberedLabelFindings);
}
const repeatedTextFindings = checkRepeatedContainerTextDOM()
.map(f => ({ type: f.id, detail: f.snippet }))
.filter(f => _ruleOk(f.type));
if (repeatedTextFindings.length > 0) {
pageLevelFindings.push(...repeatedTextFindings);
addBrowserFindings(groupMap, document.body, repeatedTextFindings);
}
const layoutFindings = checkLayout().filter(f => _ruleOk(f.type));
for (const f of layoutFindings) {
const el = f.el || document.body;