epiphany/spikes/editor-toolkit
Levi Neuwirth ffe313cd73 Packet 2A: the candidate-neutral text fixtures, emitter, and differential
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
2026-07-29 18:40:16 -04:00
..
a11y-verifier T4 round 0: iced is eliminated, and the probe that cleared it proved nothing 2026-07-28 19:01:51 -04:00
probe-egui T4 round 0: iced is eliminated, and the probe that cleared it proved nothing 2026-07-28 19:01:51 -04:00
probe-iced T4 round 0: iced is eliminated, and the probe that cleared it proved nothing 2026-07-28 19:01:51 -04:00
probe-vello T4 round 0: iced is eliminated, and the probe that cleared it proved nothing 2026-07-28 19:01:51 -04:00
round0-evidence T4 round 0: iced is eliminated, and the probe that cleared it proved nothing 2026-07-28 19:01:51 -04:00
round1-candidates T4 round 1: both candidates render the compound paths, and the oracle now refuses to be edited into agreement 2026-07-29 10:59:49 -04:00
round1-evidence T4 round 1 record: both candidates PASS, and the two carry-forwards that outlive the verdict 2026-07-29 11:10:05 -04:00
round1-oracle T4 round 1: the precommitted fill oracle, and nothing has rendered against it 2026-07-28 21:25:07 -04:00
round2-diff Packet 2A: the candidate-neutral text fixtures, emitter, and differential 2026-07-29 18:40:16 -04:00
round2-reference Packet 2A: the candidate-neutral text fixtures, emitter, and differential 2026-07-29 18:40:16 -04:00
round2-svgref Packet 2A: the candidate-neutral text fixtures, emitter, and differential 2026-07-29 18:40:16 -04:00
round2-textkit Packet 2A: the candidate-neutral text fixtures, emitter, and differential 2026-07-29 18:40:16 -04:00
.gitignore T4 round 0: iced is eliminated, and the probe that cleared it proved nothing 2026-07-28 19:01:51 -04:00
Cargo.lock Packet 2A: the candidate-neutral text fixtures, emitter, and differential 2026-07-29 18:40:16 -04:00
Cargo.toml Packet 2A: the candidate-neutral text fixtures, emitter, and differential 2026-07-29 18:40:16 -04:00
DECISIONS.md T4 round 1 record: both candidates PASS, and the two carry-forwards that outlive the verdict 2026-07-29 11:10:05 -04:00
FIXTURE_RECIPE.md T4 pin 7: the fixture recipe is approved, and the spike tree opens with it 2026-07-28 17:50:12 -04:00
ROUND2_TEXT_RECIPE.md Round 2 recipe: the precommitted stand-in, oracles, and differential 2026-07-29 18:39:52 -04:00