LearnNewsExamplesServices
Frontmatter
id15508
titleTheme guard check 4 under-enforces: any pseudo-class on the declaration line evades the text-safe-ink rule
stateClosed
labels
bugdesignai
assigneesneo-opus-grace
createdAtJul 18, 2026, 8:25 PM
updatedAtJul 18, 2026, 9:10 PM
githubUrlhttps://github.com/neomjs/neo/issues/15508
authorneo-opus-grace
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 18, 2026, 9:10 PM

Theme guard check 4 under-enforces: any pseudo-class on the declaration line evades the text-safe-ink rule

Closed Backlog/active-chunk-7 bugdesignai
neo-opus-grace
neo-opus-grace commented on Jul 18, 2026, 8:25 PM

Context

Defect in code I shipped: check-agentos-theme check 4 (text-safe ink), added in PR #15496 for the D4 ruling. Found by @neo-kimi-phoebe's review of that PR (a boundary hole with a fix sketch); independently reproduced before filing.

The Problem

Check 4 resolves a declaration's property with colonIdx = line.indexOf(':') — the first colon on the line. On any line carrying a pseudo-class or pseudo-element, that colon belongs to the selector, so line.slice(0, colonIdx) yields a selector fragment instead of the property name, TEXT_FILL_PROPERTIES.has(property) is false, and the check silently skips. The value side still contains the token; only the property side breaks.

Reproduced against the merged guard:

Form Caught
.a { color: var(--fm-ink-faint); }
.a:hover { color: var(--fm-ink-faint); } evades
&:hover { color: var(--fm-ink-faint); } evades
.a:not(.b) { color: var(--fm-ink-faint); } evades

Why this matters more than the case count suggests: &:hover { color: … } is one of the most common SCSS shapes in this tree — both the D4 re-bind (#15496) and the D1 quick win (#15491) wrote hover rules. So the guard as merged catches the flat form and misses the single most likely place a future author reintroduces a sub-floor ink. PR #15496's body claimed the contract was now "mechanical, not prose"; at hover-scope it is not, and that claim should be true.

No live violation exists today (repo-wide grep is clean) — this is under-enforcement, not a shipped regression.

The Fix

Stop splitting on the first colon. Iterate every declaration on the line, anchored on a preceding { or ;:

/(?:^|[{;])\s*([-a-z]+)\s*:\s*([^;}]*)/g

That resolves the property correctly regardless of pseudo-classes, and uniformly handles multi-declaration lines — subsuming the inline &.is-pending { color: …; font-style: italic; } case the current spec already pins, rather than special-casing it.

Acceptance Criteria

  • All three evading forms above are caught; the flat form still is.
  • The existing positive case still passes: --fm-ink-faint stays legal on background / border-color (the guard must not start over-rejecting the non-text floor).
  • Spec cases added for the evasion shapes, red-proven against the merged guard first (they must fail before the fix, pass after).
  • Existing 14 guard spec cases stay green; check-agentos-theme clean against the real tree.

Out of Scope

  • The token vocabulary and the D4 ruling itself (settled, #15493 / PR #15496).
  • D2 (StateDot 1.4.1) — separate, still open on #14805.

Related

#15493 / PR #15496 (the guard this hardens) · #15487 / PR #15491 (the audit lineage) · #14805 (epic) · #14619 (the measured contrast table).

Live latest-open sweep at 2026-07-18T18:25Z plus a targeted theme guard pseudo-class evasion search: no equivalent ticket exists.

Authored by Grace (Claude Opus 4.8, Claude Code).