@@ -45,6 +45,9 @@ produces the same document, the same selector, and the same verdict.
4545 path-directed generation is that the * combinator/breadcrumb* walker —
4646 the part most likely to harbor a matching bug — is now exercised with
4747 real depth, not that every assertion is non-vacuous.
48+ ` runner.php ` persists per-bucket/per-target match assertion counts and
49+ vacuous/non-vacuous rates under ` matchStats ` in ` state.json ` , so this
50+ distribution is reported on every run instead of relying on stale notes.
4851 - ` unsupported ` — valid CSS the API intentionally rejects (pseudo-classes
4952 and -elements, ` + ` /` ~ ` /` || ` combinators, namespaces, non-type context
5053 selectors); must not parse.
@@ -88,15 +91,16 @@ produces the same document, the same selector, and the same verdict.
8891 Skipped for ASTs containing invalid UTF-8 (reachable only from
8992 chaos/mutated inputs), which the renderer cannot round-trip.
9093 - lexbor differential (third, independent oracle; requires the harness —
91- see below): on no-quirks documents whose selector parsed, a canonical
94+ see below): on full-document cases whose selector parsed, a canonical
9295 re-render of the verified AST is matched by liblexbor and compared,
93- as a multiset of fids, against the reference matcher. Gated on WP and
96+ as a multiset of fids, against the reference matcher. Quirks documents
97+ participate only when the startup probe confirms lexbor's class/#id
98+ folding behavior in both no-quirks and quirks mode. Gated on WP and
9499 lexbor building the same element tree (fid/tag/ancestry), so it tests
95- the selector layer, not tree construction. Verdicts: ` lexbor-divergence `
96- (lexbor ≠ reference) is a fuzzer-oracle problem; ` match-mismatch-html `
97- with no accompanying divergence means reference == lexbor ≠ WP — a
98- high-confidence WP finding. (Roughly half of ` compared ` cases are
99- themselves non-vacuous; the rest assert ` [] == [] ` on both engines.)
100+ the selector layer, not tree construction. Verdicts:
101+ ` lexbor-divergence ` (lexbor ≠ reference) is a fuzzer-oracle problem;
102+ ` match-mismatch-html ` with no accompanying divergence means reference
103+ == lexbor ≠ WP — a high-confidence WP finding.
100104 - Repeating a case yields a byte-identical result digest (determinism).
101105 Note the digest covers the WP-under-test surface (selector, html,
102106 parse-nullness, ASTs, failure invariants) but ** not** the lexbor
@@ -112,7 +116,8 @@ and reports per-batch tallies, persisted to `state.json` under `lexbor`:
112116
113117- ` compared ` — the differential ran and matched fid-multisets.
114118- ` tree-gated ` — WP and lexbor built different trees; differential skipped.
115- - ` skipped-quirks ` / ` skipped-utf8 ` — quirks document / non-UTF-8 AST.
119+ - ` skipped-quirks ` / ` skipped-utf8 ` — quirks document while lexbor class/#id
120+ case behavior is not trusted / non-UTF-8 AST.
116121- ` n/a ` — the differential does not apply (unparseable selector, fragment, no
117122 captured tree).
118123- ` unavailable ` / ` error ` — the harness was missing or died. The runner prints
@@ -127,14 +132,9 @@ Known lexbor issues compensated for when present:
127132 case-sensitive). Detected by a startup probe; when present, lexbor is
128133 compared against the reference matcher run with quirks-style class/ID
129134 folding, and quirks-mode documents are excluded from the differential
130- entirely. ** Consequence — a real coverage hole:** quirks-mode class/ID
131- matching has no independent third oracle. ` ReferenceMatcher ` is the sole
132- authority there, and it encodes the same "ASCII-only case fold in quirks"
133- reading WP does (both fold via ASCII-only lowercasing), so if that reading
134- is wrong they would be wrong identically and lexbor — the one engine that
135- could disagree — is excluded. This is inherent to lexbor #368 being open;
136- it is the weakest-covered behavior in the suite and is called out here
137- rather than papered over.
135+ entirely. The same startup probe also checks class and ` #id ` selectors in
136+ quirks mode; only when all four probes pass is quirks-mode class/ID matching
137+ included in the differential.
138138- lexbor rejects uppercase ` I ` /` S ` attribute-selector modifiers, and its
139139 non-ASCII ident-codepoint table omits U+00B7 and U+00C0–U+00F6 (it
140140 starts at U+00F8), rejecting e.g. ` .Über ` while accepting ` .über ` .
@@ -159,13 +159,11 @@ The match oracle's independence differs between class and attribute selectors:
159159 splits on ASCII whitespace and folds NUL → U+FFFD per token;
160160 ` ReferenceMatcher::class_matches() ` reimplements that independently (and is
161161 pinned against ` class_list() ` on NUL/FF boundary inputs by ` self-check.php ` ).
162- The random document generators do ** not** emit control bytes inside class
163- values, so the * randomized* fuzzing never exercises this boundary — it is
164- covered only by the deterministic self-check cases. Randomized document-side
165- injection is deliberately deferred: adding it to the hot path perturbs the
166- deterministic self-check seed space enough to surface the known Bug 3, which
167- would first require decoupling ` self-check.php ` from the unfixed core bugs.
168- A worthwhile, scoped future improvement.
162+ The safe and wild random document generators now inject NUL into class
163+ tokens occasionally and expose the decoded U+FFFD token to class-selector
164+ generation. Raw class attribute values are intentionally kept out of the
165+ generic ` attrValues ` pool so attribute-selector generation does not inherit
166+ class-list-only decoding semantics.
169167- ** Attribute values are matched through a single shared read.** Both WP's
170168 attribute matcher and ` ReferenceMatcher::attr_matches() ` read the same
171169 ` get_attribute() ` output, so a value-decoding bug there would be shared and
0 commit comments