Four crates, none of which knows anything about a candidate. This is the
apparatus Round 2 will judge with, built and mutation-tested before either
candidate exists — the role Round 1's oracle tranche played.
round2-textkit — the W3 3E stand-in. Resolves two declared faces from an
explicit path list (pin 9), shapes and itemizes five fixtures with
rustybuzz/unicode-bidi/unicode-segmentation, asserts all five W3 section 5
invariants, and emits fixtures.json + an 80-probe hit-test table (none
dropped).
round2-svgref — pin 10's explicit-glyph SVG emitter. Never emits <text>, and
asserts it: a <text> element would let the viewer's shaper pick glyphs and
reintroduce exactly the re-shaping this round exists to forbid.
round2-diff — the bounded visual differential, D1-D4, zero dependencies.
round2-reference — composes the three into reference rasters and D4 regions,
and executes the text mutations M4/M5/M6 against the real frozen fixtures.
Defects found by testing rather than by reading, and what each cost:
* The stand-in had its own quantizer. `(v*1024).round()/1024` is
ties-away-from-zero; QuantizedCoord is ties-to-even (Appendix D). W3
section 5 says text quantization "is not a second convention", and naming
the divergence in a doc comment is not the same as being allowed to take
it. Now routed through QuantizedCoord, with four half-grid tests at +/-0.5
and +/-2.5 units chosen because the conventions DISAGREE there, plus one
recording a tie where they agree. is_on_grid claimed exactness while
accepting anything within 1e-6; it is now an exact round-trip.
* The validator accepted any self-consistent file. Named checks say nothing
about individual glyph ids or quantized offsets — tens of thousands of
numbers a candidate will be scored against. A whole-artifact SHA-256
closes it; four tests fail without it.
* stable_id: u128 did not survive JSON. Measured,
82875741697311382809239399464544864365 came back as 8.287574169731139e+37.
A provenance id that changes when a tool reads and rewrites a file is not
an identity. Now a decimal string (W3-F5).
* SpikeProvenance was lossy while claiming to mirror every 3E field: a Debug
rendering for `source`, a length for `dependencies`. It lost nothing
measurable only because these fixtures' dependency lists are empty, which
is an accident of the fixtures. Now canonical_bytes under W3's own field
names (W3-F6).
* A cross-crate string-slice: the reference composer extracted <path>
fragments by searching for another crate's markers. It worked, and a
formatting change there would have broken it silently. Now an API call, so
a rename is a build failure. Verified byte-identical across the refactor.
* Two harnesses printed their own bad news and exited 0.
M4/M5/M6 measured: M4 kills D1 at 221 px (D4 blind at 1.20%); M5 kills D4 at
13.28%/13.19% on the two composed-acute regions; M6's emitter refusal fires
on U+05D0 before any raster, and forced past it D4 reads 95.37/71.26/34.69%.
F-D is labelled SUPPLEMENTARY in code, in fixtures.json, in the summary, and
in every generator's output, with check 3 NOT RUN — enforced by the
validator, because a ruling recorded only in prose loses to whichever record
the reader opens.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RSX4zSLgKvtiXaPjnMqLGz