# epiphany-testkit — Decisions Agent F's crate. The v0 decisions live in `spec/QUICKSTART.md` and the per-crate DECISIONS files; this file records the **Phase 2** calls F makes as its mandate broadens (`spec/PHASE2_QUICKSTART.md`, `spec/PHASE2_F_WEEK0_WORKLIST.md`). ## F0 — Where the new harnesses, benches, and the integration runner live *Worklist item F0: "a 1-day call that everything else lands inside of." Decide before writing F1/F3/F4/F5.* **Decision: keep the examples + library-module pattern; do not adopt `xtask`; put `benches/` in this crate; per-agent harnesses are library modules asserted by `tests/` integration tests; the end-to-end integration harness is a `tests/` integration test over a library `integration` module.** Concretely: - **No `xtask`, no `tests/reference-suite/`.** The QUICKSTART topology named both (HANDOFF §7-F), but their function is already met by per-crate `examples/*` fuzzers and `epiphany-testkit/examples/conformance_suite.rs`, which is the orchestration layer and the soak entry point. Adding `xtask` would introduce a second, parallel orchestration surface for no present gain; the workspace has one cross-cutting crate (this one) and `cargo` already drives every gate. Revisit only if cross-crate orchestration outgrows a single `examples/` runner (e.g. multi-binary pipelines that need a build graph). - **Per-agent harnesses are library modules in `src/`**, alongside the v0 harnesses (`roundtrip`, `convergence`, `equivocation`, `bundle_harness`, `negative`, `layout_stub`). Each new agent gets one module exposing `run_all()`-style entry points plus the granular asserts: - **H** → [`crate::prepass_harness`] (spelling/decomposition determinism, eligibility-taxonomy coverage, `RespellPitch` precedence, non-vacuity), driving the corpus in [`crate::corpus`]. **Live now** (H landed). - **I** → `engrave_harness` (hard-constraint validation against *declared* IR constraints + class-specific collision rules, provenance survival, SVG XML-validity, golden machine-acceptance snapshot). *Skeleton when I starts.* - **K** → `migration_harness` (deterministic + equivalence-preserving v0→v1 migration, payload-schema completeness). *Skeleton when K starts.* - **J** → `wire_harness` (cross-impl decoder, canonicalization tests) + the wire-format fuzzer as an `examples/` soak target. *Skeleton when J starts.* - **Each harness is asserted by a `tests/` integration test** (the same shape as `tests/acceptance.rs`) so it is a discrete `cargo test` target and a discrete CI job, and is **also** exercised at scale by `examples/conformance_suite.rs` for the nightly soak. The H harness lands as `tests/prepass.rs` + a `[prepass-harness]` stage in the conformance suite. - **`benches/` lives in this crate** (not per-crate). The Chapter-10 budgets are workspace-level, and the marquee bench (the reducer's — at the time `O(n²)` — `canonical_reduction_order` at 10K+ envelopes, worklist F1) drives `epiphany-ops` *through* the testkit's envelope generators — exactly what this crate already does. A bench that lived in `epiphany-ops` could not reuse the generators without a dev-dependency cycle. Uses `criterion`; thresholds are written in the bench, with known-pending scale points marked `xfail` per F1. - **The end-to-end integration harness (F5) is a `tests/` integration test** over a library `integration` module with documented stub swap-points, so real H/I/K/J stages replace stubs in place as they land, and the byte-identity assertion is wired from day one (trivially true on stubs, meaningful once stages are real). **Why a library module + integration test, not a bare integration test:** the harness logic (asserts, fingerprinting, the corpus) must be callable from both the unit-budget `tests/` target *and* the at-scale `examples/conformance_suite`. Bare `tests/` code is not importable across targets; library modules are. This mirrors how v0's `convergence`/`roundtrip` modules are shared between `tests/acceptance.rs` and the conformance example. **Unblocks:** F1 (benches), F3 (corpus + taxonomy harness — done), F4 (per-agent harness skeletons — H done), F5 (integration skeleton). ## F1 — The Chapter 10 budget benches (`benches/reduction.rs`, `benches/bundle.rs`) *Worklist item F1: a criterion bench asserting Chapter-10 budgets at documented scale points, known-pending points marked xfail with the numeric budget written in the bench. Home per F0: this crate's `benches/`.* **Criterion version: `0.5` (locks to 0.5.1), default features off plus `cargo_bench_support`.** The 0.5 line's MSRV (1.70) fits the workspace's pinned `rust-version = "1.77"`; criterion 0.6+ requires 1.80. Disabling default features drops the plotting stack (`plotters`) and `rayon` — dead weight for budget gates — while `cargo_bench_support` keeps plain `cargo bench` as the entry point. Two transitive pins in `Cargo.lock` keep the tree MSRV-clean, because the resolver (v2) is not MSRV-aware: `clap 4.5.53` (MSRV 1.74; 4.6 requires 1.85) and `half 2.4.1` (MSRV 1.70; 2.5+ requires 1.81). Re-check those two if `cargo update` touches the criterion tree. **The gate mechanism.** Criterion measures but never asserts, so each bench is `harness = false` and its `main()` runs criterion's measurements first, then the budget gate in [`crate::budget`] — a library module (the F0 pattern: shared logic lives in `src/`, and it deliberately uses no criterion types, so the library itself takes no new dependency). Every budget row carries an `Expectation` written in the bench source next to its numeric threshold: - `Pass` — must hold today; a miss exits nonzero (the CI tripwire). - `Xfail(reason)` — documented known-pending miss; the reason names the defect and owner. A miss prints an expected-failure line; a **pass** prints an XPASS promotion notice, so a stale marking is loud in both directions. Skipped rows (the 50K point in quick mode) print an explicit `skip` line, never silence. Under `cargo test --benches` (criterion test mode, detected by the absence of the `--bench` flag `cargo bench` passes) the measurements run once and the gate is skipped — the gate belongs to `cargo bench`. **Sampling calibration (a documented deviation from Chapter 10).** The conformance methodology — p99 over ≥1000 iterations on the reference hardware profile — is the reference suite's job, not this gate's: 1000 iterations of the (pre-fix ~29 s) 50K cold reduction was not a CI shape. The gate takes the **median of a small per-row iteration count** (reduction: 9/3/1 iterations at 1K/10K/50K; bundle: 40 commit / 25 read; cut to 5/2/skip and 12/8 under `EPIPHANY_BENCH_QUICK=1`), cold (no in-gate warm-up — the marquee budget is an explicitly *cold* rate). Criterion uses flat sampling with `sample_size(10)` for the reduction group; the 50K point is gate-only (calibrated when a single iteration took ~29 s and criterion's ≥10-sample loop would have blown the wall-clock budget; ~0.58 s post-fix). Full `cargo bench -p epiphany-testkit` stays under ~2 minutes of measurement; quick mode under ~15 seconds. **Measured on the dev profile (2026-07, post-K-fix), the table's ground truth:** | row | budget | measured | verdict | |-----|--------|----------|---------| | `reduction/1000` | > 10,000 env/s cold | ~674,000 env/s (~1.5 ms) | Pass | | `reduction/10000` | > 10,000 env/s cold | ~257,000 env/s (~39 ms) | Pass | | `reduction/50000` | > 10,000 env/s cold | ~87,000 env/s (~0.58 s) | Pass (promoted from Xfail) | | `bundle/typical_edit_commit` | ≤ 50 ms | ~14.7 ms (real fsync) | Pass | | `bundle/open_bootstrap_read` | ≤ 200 ms | ~63 µs (moderate corpus) | Pass | As F1 first measured it (2026-07, pre-fix), the O(n²) `canonical_reduction_order` indegree construction was unambiguous in the curve (~155K / ~12.5K / ~1.7K env/s at 1K/10K/50K — 10x envelopes cost ~120x time), sinking the 50K row (~29 s per cold reduce, the documented xfail) and leaving 10K only ~25% over budget. Agent K's subquadratic rewrite (threshold/frontier readiness, byte-identical order — see `epiphany-ops/DECISIONS.md`) triggered the gate's XPASS promotion notice, and the 50K row was flipped to Pass in the same change (**F surfaces, K fixes**, round-tripped). Any future red row is a fresh regression: fix the reducer, do not re-mark rows xfail without a written decision here. **Two honesty notes on the bundle rows.** (1) They run on the build target's filesystem, *not* `std::env::temp_dir()`: `/tmp` is commonly tmpfs, where fsync is a near-no-op — measured, the commit row is ~54 µs on tmpfs vs ~14.7 ms on a real NVMe filesystem, a 270x difference that would have made the 50 ms budget vacuous. (2) The read budget is spec'd against "a 100-page orchestral score"; no such corpus generator exists yet, so the row is a moderate-corpus (1K envelopes + canonical-base snapshot) stand-in with margin to spare, and says so in its docs. **Scope, and the budget rows deliberately not built yet.** F1 covers the three budgets that are measurable against shipped subsystems: the reduction rate and the two file-format budgets. The remaining Chapter 10 rows are blocked on unimplemented or in-flight subsystems and become future benches in this same `benches/` + gate shape: the interactive keystroke→frame budgets (blocked on Agent I's engrave/render pipeline maturing past the visible slice), cold solving of a 100-page score within 2 s p99 and the incremental-propagation bound (blocked on the real Chapter 9 solver and a 100-page corpus generator — the latter also unblocks the honest read-row corpus), and the memory ceilings (blocked on the same corpus). CI runs the gates in the `conformance` job with `EPIPHANY_BENCH_QUICK=1` (50K skipped) and in the nightly `soak` job in full. ## F3 — The representative score corpus + eligibility-taxonomy harness (Agent H) *Worklist item F3 — "the most underbuilt dependency; unblocks H entirely."* The corpus lives in [`crate::corpus`] as ≥20 deterministic, `check_invariants`-clean fixtures tagged by tier (common / edge / torture) and by the event-kind eligibility taxonomy of `PHASE2_QUICKSTART §H`. Rather than re-deriving the taxonomy, the harness runs Agent H's own `epiphany_core::derive_annotations` and reads its [`epiphany_core::TaxonomyReport`], then (a) independently recounts events by kind and **cross-checks** H's counts (so a miscount is caught, not trusted), and (b) aggregates per-bucket counts across the corpus and asserts every taxonomy bucket is non-empty or explicitly deferred. The corpus also re-uses the existing positive generators (`valid_score`, `valid_score_rich`, `ten_measure_single_staff`) as fixtures so F's taxonomy harness runs over the same graphs Agents H and I develop and render against. **Deferred buckets** (documented, not required non-empty by the clean corpus): none — every bucket including `decomposition_skipped_nonmusical` (zero-duration grace) and `decomposition_ungriddable` (off-grid / sub-sixty-fourth torture cases) is exercised by a dedicated fixture, so the honest-classification paths are all proven reachable. If a future invariant change makes a bucket unreachable by clean input, move it to `corpus::DEFERRED_BUCKETS` with a written reason rather than dropping the assertion. ## F4 (H) — The H merge gate [`crate::prepass_harness`] is H's merge gate (worklist F4). It asserts H's stated `PHASE2_QUICKSTART` acceptance criterion over the corpus: every *eligible* `IdentifiedPitch` carries a **non-trivial** spelling (verified by pitch-class correctness, which the old constant-`C4` stub fails); every *eligible* determinate metric duration carries a `Decomposition` whose components reconstruct the duration (invariant 15); ineligible cases are classified and counted; derivation is deterministic across runs (asserted by structural equality **and** a canonical textual fingerprint, since `DerivedAnnotations` deliberately has no codec); `RespellPitch`-style authored overrides take precedence; and the derivation stays deterministic when run on materialized scores in the criterion-5 pipeline. **Non-vacuity guard** (the F discipline — the gate must go red if H were stubbed): across the corpus the harness requires multiple distinct spelled nominals and at least one accidental (a constant-`C4` stub yields one nominal, zero accidentals), at least one multi-component (tied) decomposition and multiple distinct note values (an empty/no-op decomposition map yields neither), and per-pitch pitch-class correctness (the stub mis-spells the first non-`C` pitch). ## Pass 12 batch tracker Per F's mandate, the Pass 12 batch is tracked in `spec/PASS12_BATCH.md`. It opens once ≥3 ambiguities accumulate (same rule as v0 → Pass 11). Agent H's landing contributed five candidates (P12-H1…P12-H5, recorded in `crates/epiphany-core/DECISIONS.md`), which crosses the threshold, so the batch is open. F does not resolve these; F collects them. ## F5 — The Reference Suite harness (`src/reference_suite.rs`, 2026-07) The Reference Suite companion (v0.1.0) charters the v0.1 entry set — six scores named by reference-implementation **builder and seed** — and its non-normative Harness Binding chapter says the executable binding "is delivered with the reference implementation." This module is that binding, in the F0 shape: a library module holding the machinery (`entries`/`evaluate_minimal`/`table`), asserted by `tests/reference_suite.rs` with **one test per entry** so a failure names its entry, plus pins for the entry-set shape, the declared A4 default geometry (the companion's solve-configuration requirement — asserted against `Engraver::default().geometry()`), and RS-2's builder identity (`corpus gen_valid_score_rich` ≡ `generators::valid_score_rich(0xF302)`, byte-for-byte). **Solver-parametric on purpose.** The library module takes `&dyn ConstraintSolver`; the integration test supplies the real `Engraver`. This keeps `epiphany-engrave` a dev-only dependency (the library dependency graph is unchanged, mirroring the multi-system click test) while the harness itself stays reusable against any solver claiming Minimal. **The four-condition pass rule, with the F1 Pass/Xfail discipline on condition 4.** `evaluate_minimal` asserts the companion's per-entry rule exactly: hard-constraint satisfaction (renderable, non-partial, `satisfied_hard_constraints`, nothing unsatisfied); internal determinism (byte-identical `ResolvedLayoutIR` canonical bytes *and* bitwise-identical metric vectors across repeated solves); a well-formed, accurate report (Minimal tier claim, every axis a finite `[0,1]` value, never the unmeasured placeholder); and every axis at or below the Quality Metric Catalog's Minimal-column threshold. The first real measurement (2026-07) found exactly one miss: **RS-1's `casting_off_quality` = 1.0** — greedy first-fit leaves a two-measure stub last system (glyph spans ~78.6/18.8 staff spaces, width CV 0.61 ≥ the 0.5 anchor), the exact failure the axis exists to catch, on a layout that is byte-locked by the render goldens. Waiving it silently would fake conformance; failing the workspace would misreport a ratified-spec tension as a code bug. So condition 4 carries the budget harness's (F1) discipline: the miss is a **documented `minimal_xfail` row asserted to still miss** — if the layout or the catalog changes and RS-1 comes within threshold, the harness fails demanding promotion (remove the row), exactly like an F1 `XPASS`. The row's resolution is spec-side and tracked in `epiphany-engrave/DECISIONS.md`'s Pass-12 candidates (casting-off balance pass with golden regeneration, or a QMC anchor/threshold minor revision — the catalog's own threshold-tuning open question anticipated this). **Eligibility tiers vs. solver tiers.** The corpus `Tier` (Common/Edge/Torture) is Agent H's eligibility taxonomy; the suite's tiers (Minimal/Standard) are Chapter 9 conformance tiers. The companion carries the same caution; the harness resolves corpus entries by `name` string only and never reads the corpus tier. ## The Text Projection grammar checker (`tests/text_projection_grammar.rs`) Text Projection 0.3.0 claimed a "machine-checked" grammar. It was checked by a throwaway script, so the claim was **true of one run and of nothing durable**. This test file is the durable form; 0.4.0 retracts the earlier claim in its own revision history. It is the same evidence gap the P2–P4 decode work kept finding: a green gate proves nothing about coverage until the harness states its own reach. Six tests, and every one was **mutation-verified** — the mutation was applied to the `.tex`, the anchor asserted present before substitution (a `str.replace` that matches nothing looks exactly like a passing test), and the named test observed to fail: | Mutation | Killed by | |---|---| | delete the `transpose-interval` production | `the_kind_productions_are_the_operation_vocabulary` | | reference an undefined nonterminal | `every_nonterminal_is_defined_and_reachable` | | let `unescaped` admit U+005C (the 0.3.0 bug) | `the_escape_grammar_agrees_with_the_escape_requirement` | | drop the `derived-ordering` citations | `the_derived_ordering_requirement_is_cited_where_it_applies` | | spell the escape introducer `"\\"` | `the_escape_grammar_agrees_with_the_escape_requirement` | | drop the `\t` escape alternative | `the_escape_grammar_agrees_with_the_escape_requirement` | | restore `Ligatures={TeX}` on the mono font | `the_mono_font_does_not_substitute_glyphs_in_the_grammar` | **The vocabularies are derived, never transcribed.** The 31 operation-kind productions come from `OperationKindTag::PAYLOAD_FREE` plus `Registered`, mapped to Catalog section names through an **exhaustive `match`** — so a kind added to the enum and not to the grammar fails to compile, then fails the test. The chunk productions come from `ChunkKind::from_discriminant`. This is the `operation_kind_tag_vocabulary!` lesson applied to prose: a hand-maintained list parallel to an enum is a latent false lock. **Every locator finds its production by name, not by column.** Writing the checker exposed four bugs *in the checker*, three of them of this kind: a column-anchored needle (`"projection ::="`) that a reflow broke — a checker that cannot find the grammar silently checks nothing; a `defined()` that missed three productions whose left-hand side sat on the line above their `::=`, which would have **hidden** undefined nonterminals; and a per-line `<...>` stripper that leaked `0022` and `005` out of multi-line prose spans, where they read as nonterminals. Nonterminal tokens must now begin `[a-z]`, per `symbol` itself. **Two rendering rules are load-bearing, not cosmetic.** A document that specifies a text syntax must not misprint that syntax. - The escape introducer and the string delimiter are written as **codepoints** (`U+005C`, `U+0022`). A quoted terminal `"\\"` reads as *two* backslashes, making every escape three characters where the requirement says two. This is the audited 0.3.0 defect in a second disguise; it was written, caught by review, and is now asserted against. - The mono font must not enable **TeX ligatures**. `tlig` rewrites `"` as a right curly quote and `--` as an en dash, so `string ::= '"' schar* '"'` rendered as `’”’ schar* ’”’`. `core_spec.tex` already omitted `Ligatures={TeX}` from its `\setmonofont`, which makes this the house convention rather than a new one; the three companions that still enable it have no listing content the feature would touch. ### 0.5.0: what starting the implementation found The grammar could not derive an ordinary pitched note. `value` had **no alternative for a sequence**, though `req:textproj:value-projection` clause 5 required one. Adding it exposed why it had been missing: a sequence whose first element is a fieldless variant is *shape-identical* to a struct, and `()` is both the empty sequence and the absent option. Both collisions are reachable from the first pitched note in any score — `PitchedEvent` carries `articulations` and `ornaments` (sequences over the zero-field `ArticulationMark` / `OrnamentMark`) beside an optional `DynamicMark`. **The collision is irreducible without new syntax, and new syntax buys nothing.** `req:textproj:strict-parse` already obliges a parser to reject "a duplicate in a set-typed field" — which it cannot do without knowing the field is set-typed. A parser consults the schema either way. Spending `(seq …)` or `[…]` to remove a *shape* ambiguity, while leaving the schema dependency that syntax was meant to remove, lengthens every line and simplifies no tool. **User ratified schema-directed** (`req:textproj:schema-directed`), so `value` collapses to `"(" value* ")"` or a leaf — all that shape can honestly say — and the requirement assigns meaning by expected type. Three consequences, now stated: a struct with no fields is the bare symbol, as a fieldless variant is; a **byte string is not a sequence** (where the binary form writes a length-prefixed run of bytes — an opaque extension payload, a `SoundConfiguration` — the projection writes a byte string, never a list of integers); and the grammar's repetitions carry a notation rule, adjacent elements separated by exactly one space, without which `"(transpose (" bytes* ") "` spelled two targets as one undelimited run of hex. The checker gained `value_admits_a_bare_list_and_claims_no_shape_it_cannot_distinguish`, mutation-verified two ways: restoring the 0.4.0 `value` production, and stripping the `schema-directed` citations. A grammar that claims to distinguish a struct from a sequence would be lying, so the test asserts the symbol-headed alternative is **absent**. ## T2 W3 (2026-07-23) — Promoting the T1a visual goldens to gate `[9]`, behind `golden-gate` `spec/CONTRACT_EDITOR_T2_SELECTION.md` §W3. T1a (`crates/epiphany-editor-gui/src/goldens.rs`, `spec/CONTRACT_EDITOR_T1A_GOLDENS.md`) landed pixel-level golden tests in `epiphany-editor-gui` but left this crate's `conformance_suite` frozen at 8/8 — the harness existed but nothing counted it. This tranche adds gate 9, re-deriving the three T1a golden states headlessly inside `examples/conformance_suite.rs` and comparing against the same committed baselines, entirely behind a new `golden-gate` feature. **Why a feature, not an unconditional addition.** The MSRV `check` job (`.github/workflows/ci.yml`, the `check` job) runs `cargo test --workspace --exclude epiphany-editor-gui --all-targets` at the pinned floor (1.85) — `--all-targets` builds every example in this crate, `conformance_suite` included, with default features. `resvg` (and the transitive raster stack it pulls: `usvg`, `tiny-skia`, font/image decoding crates) is only ever exercised by `epiphany-editor-gui`, which is deliberately excluded from that job because one of its own dependencies (`image`, via `eframe`) declares a newer MSRV than the workspace floor. Making `resvg` reachable from `epiphany-testkit` unconditionally would silently re-impose that same floor on a crate the MSRV job is not supposed to skip. The fix is Cargo's standard shape for "a dependency only some callers need": `resvg` (and `epiphany-render-svg`, not previously a dependency of this crate at all) go into `[dependencies]` with `optional = true` — **dev-dependencies cannot be optional**, which is why example-only code still needs a `[dependencies]` entry — gated by a `golden-gate` feature that nothing enables by default. `cargo tree -p epiphany-testkit -e normal` shows zero `resvg` nodes without the feature and exactly one with it (`--features golden-gate`). `epiphany-editor-core` and `epiphany-engrave` needed no such treatment: T1a's `multisystem_click.rs` test and the Reference Suite harness (F5, above) already made both **unconditional dev-dependencies** of this crate, carrying no `resvg`-class MSRV risk of their own, so gate 9's `EditorSession` / `Engraver::default()` calls simply reuse them as-is. **Keeping the no-feature output byte-identical was the binding constraint,** stated in the contract as a hard requirement (the MSRV job's `--all-targets` build must see zero difference). The suite's gate-count denominator moved from a literal `8` in every `eprintln!` to a `TOTAL_GATES` const — `8` without `golden-gate`, `9` with it — so every existing print line renders byte-for-byte what it always has when the feature is off, and gate 9's own line plus the renumbered final `[9/9] ok:` line appear only when it is on. Verified directly: the pre-tranche example source, rebuilt and run at `SCALE=0`, produces output diffed byte-for-byte against the post-tranche no-feature build of the same invocation — `diff` reports no difference. **Gate 9's substance mirrors T1a's comparator, not its full machinery.** It re-renders `ten_measure_single_staff(0)` as opened, the same fixture after the scripted pencil insert (the click point re-derived by literally copying `goldens.rs::scripted_insert_target`'s algorithm — last system by lowest `bounding_box.origin.y`, rightmost Pitch-sourced notehead in that system's vertical band, +0.5 staff spaces right / +2.0 up — since this crate cannot call a `#[cfg(test)]`-private function in a different crate's binary), and `ten_measure_with_slurs(0)` as opened, rasterizing each exactly as `epiphany-editor-gui/src/main.rs::rasterize_pixmap` does (the algorithm copied for the same cross-crate-privacy reason: usvg parse, dimensions ceiling to whole pixels, opaque white background, `resvg::render`). Unlike `goldens.rs`'s own comparator, gate 9 is **compare-only**: no `EPIPHANY_BLESS_GOLDENS` path, no `actual.png`/`expected.png`/`diff.png` artifact writing. A mismatch's panic message points at `cargo test -p epiphany-editor-gui goldens` — the crate that owns the baselines, the bless mechanism, and the reviewable artifacts — as the place to diagnose, rather than duplicating that machinery here. G3 (undo-equals-G1) is deliberately not re-derived as a fourth comparison: it reuses G1's own baseline file in T1a, so covering G1 already covers what G3 would additionally lock at the pixel level for this gate's purposes. **The cross-crate baseline path is a documented coupling, not an accident.** The three committed PNGs live in `crates/epiphany-editor-gui/goldens/`, one level up and over from this crate's own directory. Gate 9 resolves that path from *this* crate's `CARGO_MANIFEST_DIR` (the only directory `env!` can see at compile time) as `{CARGO_MANIFEST_DIR}/../epiphany-editor-gui/goldens/`. A rename or relocation of either crate directory, or of `epiphany-editor-gui/goldens/`, breaks this path — silently succeeding at `cargo check` (it is a runtime path, not a compile-time one) but failing gate 9 loudly at run time with a clear "no golden baseline at ..." panic (mutation m6 below), never a silent skip. This crate's own `Cargo.toml` comment on the `epiphany-render-svg`/`resvg` dependencies and the `golden_gate` module doc in `examples/conformance_suite.rs` carry the same note; `epiphany-editor-gui` is outside this packet's blast radius (`spec/CONTRACT_EDITOR_T2_SELECTION.md` §W3), so its own `DECISIONS.md` — which already documents the baseline directory from the T1a side — is left untouched here. **Mutation evidence (independently reproduced against gate 9's real run, not just described):** - **m5 — tamper one decoded pixel before comparison.** Rendering G1, XOR-ing byte 0 of its pixmap with `0xFF`, then comparing that tampered pixmap against the committed baseline made gate 9 die with (actual panic, captured verbatim): `gate 9: ten_measure_open decoded RGBA differs from the committed baseline at .../epiphany-editor-gui/goldens/ten_measure_open.png (1 of 1119968 bytes differ); diagnose with \`cargo test -p epiphany-editor-gui goldens\``. (The pixel comparison is a manual `if actual.data() != expected.data() { panic!(...) }` reporting a differing-byte count, not a bare `assert_eq!` — the latter was tried first and dumps both full byte slices, ~1.1 MB each, into the panic message on a real score raster; the differing-byte count is the useful signal, and the visual artifacts to inspect already live in `epiphany-editor-gui`'s own comparator.) Restored by reversing the exact same byte-flip line (not `git checkout`); the suite returns to 9/9. - **m6 — point the baselines path at a nonexistent directory.** Appending `-DOES-NOT-EXIST` to `baseline_path`'s directory component made the `fs::read` call fail, and the gate panicked immediately with (captured verbatim): `gate 9: no golden baseline at .../epiphany-editor-gui/goldens-DOES-NOT-EXIST/ten_measure_open.png (No such file or directory (os error 2)) — \`epiphany-editor-gui/goldens/\` must contain the committed T1a baselines for this gate to compare against; diagnose with \`cargo test -p epiphany-editor-gui goldens\`` — a loud, named failure, never a silent skip or pass. Restored by reversing the substitution; the suite returns to 9/9. **Baselines pin the raster stack (inherited from T1a, restated here because this crate now also depends on it).** Any `Cargo.lock` movement of `resvg`/`usvg`/`tiny-skia`/`png` is a golden-review event — for both crates now, not just `epiphany-editor-gui` — never a silent re-bless. Gate 9 has no bless mechanism of its own by design (above), so a deliberate raster-stack bump that changes pixels must be reviewed and re-blessed through `epiphany-editor-gui`'s own `EPIPHANY_BLESS_GOLDENS=1` path; gate 9 will then pass again against the newly-blessed baselines with no changes of its own needed.