Add deterministic render-free static visual-parity gate (replace flaky pixelmatch)#344
Merged
Merged
Conversation
Replace flaky full-page pixelmatch as the primary visual-parity signal with a deterministic, render-free static style parity gate. The whole import pipeline (transform + CSS materialization) is deterministic, so the parity check is too: same inputs yield a byte-identical 0..1 score plus a per-element / per-property diff naming exactly which CSS property on which selector diverged. No screenshot rasterization, no dimension-sensitivity, no scroll/animation flakiness, no OOM. Extends the existing render-free probe/comparator class (Typography/ButtonMenu) that already runs inside the fast `composer parity` loop: - StaticCssCascade: deterministic, browser-free CSS cascade resolver (specificity-then-source-order, inline override, inheritance) over <style> + caller-supplied (linked) CSS. Approximates getComputedStyle for the subset of author-declared, statically-resolvable properties parity cares about. - StaticStyleParityProbe: extracts a stable effective-style fingerprint per styleable element over a frozen, sorted set of visually load-bearing properties, with a stable selector path, class-free structural path, class set, and text snippet for deterministic correspondence. - StaticStyleParityComparator: matches source<->candidate elements by a fixed deterministic tier order (selector path -> class+text -> class -> structural position), consuming the first still-unmatched candidate in document order; emits a stable score (property agreement x element coverage), per-element matches, and per-property findings through the shared VisualParityReportContract. - StaticStyleParityRunner: end-to-end render-free runner that transforms the source, rebuilds a candidate from the serialized blocks, and compares both under the same author CSS so the only variable is the transformed DOM. - tools/static-parity/run.php (+ `composer static-parity`): corpus gate harness with a report mode and an opt-in `--fail-under` threshold gate. Element correspondence is solved by stable identity (preserved class -> className and semantic tags), never by pixels. Determinism verified by byte-identical output across runs; reliability verified on 15-saas and 38-medical-clinic (which full-page pixelmatch could not capture without OOM) in seconds with no browser. Wires two parity fixtures (static_style_parity.extract / .compare) into the fast loop; full suite green (182 parity fixtures). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a DETERMINISTIC, render-free visual-parity signal as the primary parity gate for SSI imports — replacing flaky full-page pixelmatch (which OOM'd on tall pages and hung on source capture). The pipeline is deterministic PHP (transform + CSS materialization), so the parity check is too: same inputs -> byte-identical output, no browser, no rasterization, no dimensions, no OOM.
How it works
Run the source through the transformer, rebuild the candidate HTML from the serialized blocks (delimiters stripped), and compare source vs candidate under the same author CSS — so the only variable is the transformed DOM. Yields a per-property diff (which CSS property on which selector diverged) + a stable score.
Components (src/VisualParity/)
StaticCssCascade.php— browser-free cascade resolver (specificity + source order, inline override, inheritance; inline<style>+ caller linked CSS).StaticStyleParityProbe.php— effective-style fingerprint per styleable element over 16 frozen visual properties + stable selector/structural identity.StaticStyleParityComparator.php— deterministic 4-tier element matching (selector / class+text / class / structural), per-property findings, stable score (property_parity x coverage), viaVisualParityReportContract.StaticStyleParityRunner.php+composer static-parityharness (report mode + opt-in--fail-undergate).Proofs
composer static-parity --fixture 15-saas --jsonrun twice -> byte-identical (SHA-256 match).section.hero background-color -> no declared value,border-radius -> no declared value.composer test+composer parity: 182 fixtures green (+3 new).Honest limits / next slice
AI assistance