epiphany/spec/EVIDENCE_P13S29_EXECUTION.md

797 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Evidence — P13-S29 execution
**Not part of the candidate's normative content.** The destination gates 6 and 7
require: every mutation transcript and boundary-gate output, recorded verbatim
rather than summarised.
Contract ratified at `cea21cd`. Executed 2026-08-12.
---
## §1. Pin 3's count movement, measured
Pin 3 requires the requirement to be added **first**, the counts measured, then
the temporary allowlist row removed. Observed on the first run after the
requirement landed and before any constant was touched:
```
thread 'requirement_labels_follow_the_grammar' panicked at
crates/epiphany-testkit/tests/requirement_labels.rs:299:5:
assertion `left == right` failed
left: 287
right: 286
```
`CORE_REQUIREMENT_COUNT` 215 → **216**; `SUITE_REQUIREMENT_COUNT` 286 → **287**;
`SUITE_LABEL_COUNT` 286 → **287**.
**The citation gate is green for the correct reason**, which gate 12 requires
distinguishing from green-because-still-allowlisted:
- the temporary `req:time:tempo-segment-shape` row is **absent** from
`DISCUSSED_NOT_CITED`, whose surviving rows are exactly
`req:layoutir:vertical-bands` and `req:graph:aleatoric-reference-locality`;
- `\label{req:time:tempo-segment-shape}` is **present** in `spec/core_spec.tex`,
so the label is *defined*, not excused.
---
## §2. Pin 11's migration, and what failed before it
After the emission split and before pin 11's migration, the workspace stood at
**1582 passed / 4 failed**, and the four were exactly the migrated positives pin
11 names:
```
invariants::accidental_compatibility_tests::cmn_chromatic_accidental_in_edo_31_fires
invariants::review_fix_tests_4::f3_aleatoric_bounds_key_absent_and_reversed_window_fire
invariants::review_fix_tests_4::f3_aleatoric_dag_referencing_absent_event_fires
invariants::review_fix_tests_4::f4_tempo_segment_structural_defects_fire
```
**The two accidental negatives did not fail** — they stayed green and vacuous,
exactly as pin 11b predicts, which is why that pin replaces their *predicates*
rather than only their selector. A migration repairing only the loud failures
would have left them passing for a reason unrelated to what they test.
All nine observations were migrated: five in `review_fix_tests_4`, four in
`accidental_compatibility_tests`. The ghost-region anchor assertion in
`f4_tempo_segment_structural_defects_fire` and the `EventCoordinateModel`
assertion in `f3_aleatoric_bounds_key_absent_and_reversed_window_fire` were
**left as invariant assertions**, per pin 11a.
---
## §3. Five execution faults, recorded rather than left in the transcript
None is a contract defect; none reached a commit. All are recorded because they
were self-inflicted by *how* the edits were made, because the first is a repeat
of one this rung's family has already paid for, and because the fifth is the
only one no tool would have reported.
### 3.1 A span replacement deleted `struct DeferredCheck`
Rewriting the `Display` impl by splicing from
`impl core::fmt::Display for WellFormednessViolation` to the **next** `impl`
swallowed the `pub struct DeferredCheck` declaration, which sat between the two.
Observed as:
```
error[E0432]: unresolved imports `invariants::DeferredCheck`, `invariants::InvariantViolation`
error[E0425]: cannot find type `DeferredCheck` in this scope
```
**Restored** with its fields and doc comment intact, including the note that
§0.5 excludes it from this rung deliberately.
**This is the second occurrence of the same defect in this rung's family.** The
first removed mutation **M18** from the P13-S26 contract's §3 during revision M,
also by replacing a span whose endpoints straddled an unrelated neighbour. A
span replacement is silent when it takes something extra: nothing fails, the
text simply no longer exists.
### 3.2 Line-number targeting edited the wrong module
Six assertions were rewritten by line number after a grep. Two of them —
reported at `4243` and `4261` — lay in **`review_fix_tests_3`**, not
`review_fix_tests_4`, which begins at `4266`. Both were reverted to their
original `CrossCuttingRefsResolve` form, and the genuine C9 site was located
symbolically at `f3_aleatoric_bounds_key_absent_and_reversed_window_fire`.
**The rule the contracts already state for locators applies to edits too.**
P13-S16 §7 and P13-S26 §0.6 require symbolic anchors because line numbers drift;
these edits drifted *within a single session*, between one grep and the
replacement that consumed it. **All remaining edits in this execution use
symbolic anchors.**
### 3.3 Two fixture faults, corrected by reading rather than guessing
Neither is a contract defect; both were invented shapes that the tree disproves.
**A C1 fixture assumed `valid_score` carries spanners.** It pushed a ghost staff
onto `cross_cutting.spanners[0]` and panicked: *"index out of bounds: the len is
0 but the index is 0"*. The generator produces no spanners. The fixture now uses
a **dangling staff instrument** — the same `check_cross_cutting_refs` surface,
and stable across generator changes in a way an index into an optional
collection is not.
**An `AleatoricTimeModel` literal named a field that does not exist**
(`discipline`), failing to compile. Rather than guess the shape, C8 and C9 were
rebuilt on the pattern
`f3_aleatoric_dag_referencing_absent_event_fires` already proves: locate the
aleatoric region by `matches!` on its time model, take its instance-0 voice-0
events, and install an `EventOrderingDAG` or a `bounds` key naming a ghost.
*Both faults share the shape of §3.1 and §3.2: a construct written from
expectation instead of from the tree. The difference is that the compiler and a
panic caught these two immediately, where a span replacement and a stale line
number are silent.*
### 3.4 Two source helpers, deliberately
`production_source()` now exists twice: privately inside `g3a_tests`, and again
inside `s29_violation_kind_tests`. **This is scoping, not duplication drift.**
Pin 1b requires its derives guard to live in `g3a_tests` **because** that
module's helper is private to it; pin 10's tests are a different module and
cannot reach it. Widening the original's visibility was declined — it would add
surface to satisfy a placement the contract already settled.
### 3.5 Shared kind bindings made a pinned mutation inapplicable
`check_tempo_maps` was first written with three shared bindings —
```rust
let anchor = ViolationKind::Invariant(GraphInvariant::CrossCuttingRefsResolve);
let shape = ViolationKind::Requirement("req:time:tempo-segment-shape");
let order = ViolationKind::Requirement("req:time:tempo-segment-order");
```
— passed to the `flag` closure at all six emission sites. **§3's M1 requires
"seven separate mutations, one per requirement condition", and §3's column
header requires each mutation be *applicable*.** Under shared bindings it is
not: re-tagging `shape` moves C4 **and** C5 together, and `order` moves C6 and
C7 together. The first two runs made that concrete — a single edit to `shape`
failed four tests, the union of C4's and C5's radii.
**The six kinds are now written inline at their emission sites and the three
bindings are gone.** Behaviour is identical — `cargo test --workspace` returned
the same `44 suites / 1604 passed / 0 failed / 0 ignored` before and after — and
each condition is now independently re-taggable, which is what M1 asks for.
*This is an execution fault of the same family as §3.1–§3.4: code written from
what reads well rather than from what the contract has to be able to do to it.
It is the only one of the five that no compiler, panic or assertion would ever
have caught — the mutation simply would have measured something coarser than the
cell it was compared against, and matched a union that looked like a radius.*
### 3.6 Restoration and baseline
After both faults were repaired and pin 11's migration completed:
```
suites=43 passed=1586 failed=0 ignored=0
```
The rung's structural baseline, unchanged from the ratified input, because
nothing in pins 1–11 adds or removes a test. The new tests of pins 1b and 10
move it; that movement is recorded in §4 when they land.
---
## §4. Structural baseline after the new tests
```
suites=44 passed=1604 failed=0 ignored=0
cargo +1.95.0 clippy --workspace --all-targets -- -D warnings: clean
cargo +1.95.0 fmt -p epiphany-core -p epiphany-testkit --check: clean
core_spec.pdf rebuilt, 0 undefined references
```
**1586 → 1604, and 43 → 44 suites.** Eighteen net-new tests: pin 10's sixteen,
pin 1b's derives guard, and pin 1b's integration test — the last being the new
suite, since `tests/public_surface.rs` is a new integration target.
**Every mutation radius below is measured against this surface**, not against
the ratified input's.
### 4.1 Pin 1b's guard found a real defect on its first run
`violation_types_declare_their_pinned_derives` failed immediately:
```
assertion `left == right` failed: ViolationKind's rustdoc is pinned
left: "A violation of a graph invariant: which invariant, and a short witness naming
the smallest offending objects (Chapter 5; QUICKSTART: …). What a
[`WellFormednessViolation`] failed. `Invariant` names a numbered …"
right: "What a [`WellFormednessViolation`] failed. `Invariant` names a numbered …"
```
**The old `InvariantViolation` rustdoc had survived**, stranded above
`ViolationKind` when the enum was inserted before the struct. Nothing else in the
suite could see it: it is prose, and every other assertion is behavioural. It was
removed.
*This is the case pin 1b was written for — round 15 called the derives and
rustdocs "prose that no test or gate reads", and the first thing the new guard
read was a stale one.*
---
## §5. Expected-versus-observed matrix
**The phase is halted at M1.** Five of the seven M1 conditions matched their §3
cells exactly; **M1·C6 and M1·C7 did not**, and the difference is a defect in
§3, not in the tree. §7 states the finding; the remaining mutations (M2 onward)
are **not run** and this section stays open until the amendment lands.
Every row below was run as `cargo test --workspace --no-fail-fast` against the
§4 surface (`44 / 1604 / 0 / 0`), with a `cargo build --tests --workspace`
compile check first — **a mutation that does not compile observed nothing** —
and restored by hand write-back.
| M | §3 cell | Observed | |
|---|---|---|---|
| M1·C4 | 2 | 2 | ✅ |
| M1·C5 | 3 | 3 | ✅ |
| M1·C6 | 1 | **2** | ❌ |
| M1·C7 | 1 | **2** | ❌ |
| M1·C8 | 4 | 4 | ✅ |
| M1·C9 | 2 | 2 | ✅ |
| M1·C10 | 3 | 3 | ✅ |
### 5.1 M1·C4 — `tempo_constant_mismatch_reports_shape`
Cell: that test **plus** `f4_tempo_segment_structural_defects_fire`. Observed
`1602 passed / 2 failed`, exactly those two.
### 5.2 M1·C5 — `tempo_nonconstant_missing_end_reports_shape`
Cell: that test, `f4_tempo_segment_structural_defects_fire`, and
`requirement_selector_discriminates_its_payload` — pin 10 builds the requirement
discriminator from C5 and C8. Observed `1601 passed / 3 failed`, exactly those
three.
### 5.3 M1·C6 — MISMATCH
**Cell: `tempo_out_of_order_reports_order` alone. Observed two.**
```
passed=1602 failed=2
invariants::s29_violation_kind_tests::tempo_out_of_order_reports_order
invariants::s29_violation_kind_tests::tempo_overlap_reports_order
---- invariants::s29_violation_kind_tests::tempo_out_of_order_reports_order stdout ----
thread '...tempo_out_of_order_reports_order' panicked at
crates/epiphany-core/src/invariants.rs:6677:9:
aggregate must carry (Requirement("req:time:tempo-segment-order"), "tempo segments are
out of start order"); got [(Invariant(CrossCuttingRefsResolve), "tempo segments are out
of start order"), (Requirement("req:time:tempo-segment-order"), "tempo segments overlap
in musical time")]
---- invariants::s29_violation_kind_tests::tempo_overlap_reports_order stdout ----
thread '...tempo_overlap_reports_order' panicked at
crates/epiphany-core/src/invariants.rs:6681:9:
the rider must not answer to invariant 10; got [WellFormednessViolation { kind:
Invariant(CrossCuttingRefsResolve), witness: "tempo segments are out of start order" }]
```
**The second failure is pin 10's own fourth assertion, on the sibling test.**
### 5.4 M1·C7 — MISMATCH, symmetrically
**Cell: `tempo_overlap_reports_order` alone. Observed the same two, with the
roles exchanged.**
```
passed=1602 failed=2
invariants::s29_violation_kind_tests::tempo_out_of_order_reports_order
invariants::s29_violation_kind_tests::tempo_overlap_reports_order
---- invariants::s29_violation_kind_tests::tempo_out_of_order_reports_order stdout ----
thread '...tempo_out_of_order_reports_order' panicked at
crates/epiphany-core/src/invariants.rs:6681:9:
the rider must not answer to invariant 10; got [WellFormednessViolation { kind:
Invariant(CrossCuttingRefsResolve), witness: "tempo segments overlap in musical time" }]
---- invariants::s29_violation_kind_tests::tempo_overlap_reports_order stdout ----
thread '...tempo_overlap_reports_order' panicked at
crates/epiphany-core/src/invariants.rs:6677:9:
aggregate must carry (Requirement("req:time:tempo-segment-order"), "tempo segments
overlap in musical time"); got [(Requirement("req:time:tempo-segment-order"), "tempo
segments are out of start order"), (Invariant(CrossCuttingRefsResolve), "tempo segments
overlap in musical time")]
```
### 5.5 M1·C8 — `aleatoric_ordering_outside_region_reports_locality`
Cell: that test, `f3_aleatoric_dag_referencing_absent_event_fires`,
`mixed_fixture_splits_by_arm` (C8 is in the pinned mixed fixture), and
`requirement_selector_discriminates_its_payload`. Observed `1600 / 4`, exactly
those four.
```
thread '...f3_aleatoric_dag_referencing_absent_event_fires' panicked at invariants.rs:4325:9:
assertion failed: fires_req(&s, "req:time:aleatoric-reference-locality")
thread '...mixed_fixture_splits_by_arm' panicked at invariants.rs:7040:9:
aggregate must carry the rider as its requirement; got [ ... kind: Invariant(
CrossCuttingRefsResolve), witness: "tempo segment start anchor target ... dangling" },
... kind: Invariant(CrossCuttingRefsResolve), witness: "aleatoric region ... ordering
references event ..., absent from the region" }]
```
### 5.6 M1·C9 — `aleatoric_bounds_outside_region_reports_locality`
Cell: that test plus `f3_aleatoric_bounds_key_absent_and_reversed_window_fire`.
Observed `1602 / 2`, exactly those two.
### 5.7 M1·C10 — `accidental_incompatible_reports_tuning_requirement`
Cell: that test, the migrated `cmn_chromatic_accidental_in_edo_31_fires`, and
`display_renders_each_arm_exactly`, whose requirement side uses a real
accidental violation. Observed `1601 / 3`, exactly those three.
### 5.8 What the five matching rows establish about the two that did not
**The sibling-fixture hazard is specific to C6/C7 and measurably absent
elsewhere**, which is why the correction must not be generalized into a blanket
rule about shared labels:
- **C4/C5 share the label `req:time:tempo-segment-shape`** and did **not**
cross-talk: C4's fixture is `Constant` with a mismatched `end_tempo`, C5's is
non-constant with none, and neither shape can emit the other's condition.
- **C8/C9 share `req:time:aleatoric-reference-locality`** and did **not**
cross-talk: each aggregate in the transcripts above carries exactly one
violation, so the fixtures are genuinely disjoint.
- **C6/C7 share `req:time:tempo-segment-order` and cannot be made disjoint** —
pin 10 says so and rules out the fixture that would separate them.
**Restoration after every row**, verified by full run:
```
suites=44 passed=1604 failed=0 ignored=0
```
---
## §6. Gate 6 — the identifier and field migration, verbatim
**6a. No `InvariantViolation` identifier in Rust code.** Gate 6 scopes this to
`crates/**/*.rs`; the contract, the ledger and this annex quote the old name
historically and are out of scope.
```
$ find crates -name '*.rs' -type f -exec grep -Hn 'InvariantViolation' {} +
$ echo $?
1
```
*Run with `find … -exec`, not a piped `grep | head`: a universal negative from a
truncated pipe is the failure mode `CLAUDE.md` names, and this gate is a
universal negative.*
**6b. No `.invariant` field access on a `WellFormednessViolation`.** Three hits
survive, and gate 6 requires each to be attributed rather than counted:
```
crates/epiphany-core/src/invariants.rs:320: self.invariant.number(),
crates/epiphany-core/src/invariants.rs:321: self.invariant,
crates/epiphany-core/src/invariants.rs:3326: assert_eq!(deferred[0].invariant, GraphInvariant::RegionExtents);
```
**All three are `DeferredCheck`, not the violation type.** Lines 320–321 are
inside `impl core::fmt::Display for DeferredCheck` (opened at `:315`); line 3326
indexes the result of `deferred_checks(&s)`.
**6c. `DeferredCheck.invariant` retained**, as §0.5 requires:
```
crates/epiphany-core/src/invariants.rs:305:pub struct DeferredCheck {
crates/epiphany-core/src/invariants.rs-306- /// The invariant whose decision was deferred.
crates/epiphany-core/src/invariants.rs-310- pub invariant: GraphInvariant,
```
---
## §7. Finding against §3: M1·C6 and M1·C7's radii
**Reported, not patched.** The pins are frozen; this needs an amendment with its
own review round, and the mutation phase stays halted until one lands.
### 7.1 What §3 says
> **C6 and C7 fail their pin-10 test alone**: the legacy `f4` assertion is one
> fixture tripping *both*, so re-tagging one leaves the other still reporting the
> same label and the legacy assertion still passes
### 7.2 Why it is wrong
**The quoted sentence is true about `f4`, and `f4` is indeed not in either
radius** — measurement confirms it. The error is that the derivation *stopped at
the legacy observer*. It never asked the same question of pin 10's own C6 and C7
tests, which share the fixture **shape**.
Pin 10 pins **four** assertions per C4–C10 row. The fourth is:
> the aggregate's **invariant arm is empty**
and pin 10 states the fixture relationship outright:
> C6's natural fixture — `seg(2,3)` then `seg(1,2)` — is *also* overlapping, so
> it emits C6 **and** C7 under the same label.
Both tests are built by `two_segments(seed, (2, 3), (1, 2))`, differing only in
the seed. So re-tagging **either** condition puts an `Invariant` violation into
**both** fixtures, and the sibling's fourth assertion fails. The radius is two,
not one, in both directions.
### 7.3 The tree is correct; the cell is not
**No implementation change can reconcile them, and none should be attempted.**
The only fixture that isolates C6 needs `seg1.end < seg1.start` — an
end-before-start segment — and pin 10 **explicitly rejects it**:
> *The alternative to pairs — an isolated C6 fixture — would need an
> end-before-start segment, which is unnatural and tests a shape the checker
> should never see.*
Pin 10 chose shared fixtures deliberately and §3 then costed them as though it
had not. **Changing the fixtures to fit §3 would violate pin 10**; changing the
fourth assertion would discard the property §3's own M2a derivation leans on.
### 7.4 The defect class
**The one-hop correction** — the ledger's recurring shape, and named as such in
this rung's own review history. A fact is established in one place (pin 10: this
fixture emits both conditions) and not carried into the place that depends on it
(§3: what fails when one of them moves). §3 even *cites* the shared-fixture
property, in the same cell, about a different observer.
It is also the fifth instance of the pattern §3's preamble already documents:
> Every one of the four differed from what static reading predicted
Four cells were measured because they reached tests the contract did not write.
**C6 and C7 reach tests the contract *did* write — which is exactly why they
were derived instead of measured, and exactly why the derivation was trusted.**
Authorship of a test is not knowledge of its fixture's reach.
### 7.5 The measured replacement
| M | Radius |
|---|---|
| M1·C6 | `tempo_out_of_order_reports_order` **and** `tempo_overlap_reports_order` |
| M1·C7 | the same two |
Measured, not derived; transcripts in §5.3 and §5.4.
**This strengthens M1 rather than weakening it.** Under the pinned cell a
re-tagging that broke only the sibling would have read as a radius mismatch;
under the corrected cell each of C6 and C7 is observed by two independent
assertions — its own pair assertion and the sibling's invariant-arm assertion.
---
## §8. Amendment 1: resolution and resumption
Amendment 1 is ratified at `29ef3af` and landed by this commit. It
corrects two radius cells in the contract's §3 and changes no pin, test, fixture
or behaviour.
**§5 and §7 above are the dated execution record and are not edited.** They
state what was expected, what was observed, and why execution stopped. The
mismatch they record is the reason this amendment exists; reconciling them would
remove it.
### 8.1 The corrected radii
| M | Dispatched cell (§5) | Corrected cell, measured |
|---|---|---|
| M1·C6 | `tempo_out_of_order_reports_order` | that test **and** `tempo_overlap_reports_order` |
| M1·C7 | `tempo_overlap_reports_order` | that test **and** `tempo_out_of_order_reports_order` |
Both were observed before the halt. The transcripts in §5.3 and §5.4 stand as
the observation and **were not re-run**: the amendment corrects the expectation
they were compared against, not the observation.
### 8.2 Resumption
The mutation sequence resumes at **M2**. M1 is complete — C4, C5, C8, C9 and C10
matched their dispatched cells, and C6 and C7 match the corrected cells above.
---
## §9. The mutation phase, resumed at M2
**§5 is closed at M1 by the digest gate (contract §6.5c-bis); this section
continues the matrix.** Every row was run as
`cargo test --workspace --no-fail-fast`, preceded by
`cargo build --tests --workspace` checked for `error[` — **a mutation that does
not compile observed nothing** — and restored by hand write-back.
### 9.1 Five more execution faults, none a contract defect
**They are recorded here rather than appended to §3** because §3 is the
pre-amendment record committed at `29ef3af` as amendment 1's oracle. Its count of
five is true as of that commit; these four are later events.
#### 9.1.1 Pin 10's eleventh row was never built
Pin 10's matrix has **eleven** rows. The eleventh is marked `—` rather than a
C-number:
| — | reversed aleatoric bounds | **invariant 4, unchanged** | `reversed_aleatoric_bounds_stay_invariant_four` |
It was read as a note and no test was written. **M11's cell names that test**, so
the omission surfaced as a radius mismatch — expected 4, observed 2 — and not
before.
*A row whose identifier is a dash reads as commentary. The other ten rows carry
C-numbers; the eleventh carries the same three columns and a dash, and only the
columns matter.*
#### 9.1.2 `display_renders_each_arm_exactly`'s invariant side was the wrong fixture
Its comment read:
```rust
// Invariant side: a reversed aleatoric bound (invariant 4), acquired
// from the aggregate -- pinned, because acquisition decides M3's radius.
```
The code beneath it built a **dangling staff instrument** and searched for
`CrossCuttingRefsResolve` — invariant 10, a C1 fixture. **The comment recorded
the pin; the code did something else.**
Pin 10 forecloses exactly this:
> *The choice changes radii, so it cannot be the executor's: a C1 fixture would
> put this test in M17·C1's cell, a tempo-anchor fixture in M17·C2/C3's, and
> reversed bounds put it in **M11's**, which is where it now belongs and where
> M11's cell names it.*
Both halves of that prediction were then observed: with the fixture repaired,
`display_renders_each_arm_exactly` **entered M11's radius** (§9.3) and is
**absent from M17·C1's measured 20** (§9.4). Under the unrepaired fixture it
would have failed M17·C1 too — a second mismatch the first one pre-empted.
*This is the most dangerous of the five fault classes in this rung: a comment
that states the pin correctly next to code that does not. Nothing reads the
comment.*
#### 9.1.3 Pin 3a's guard did not exist
`tempo_segment_shape_requirement_states_its_clauses_and_stays_s8_neutral` was
absent from the tree. **Thirteen mutations target it** — M12pre, M12, M13,
M13post, M13neutral, M14a–e, M15a, M15b — so thirteen cells had no possible
observer. Found when the `.tex` batch went to look for it.
It is now implemented as pin 3a requires: the `.tex` slice from the
`\begin{requirement}` preceding the label to the first `\end{requirement}` at or
after it, whitespace-collapsed, **equal** to pin 3's pinned source.
**M14e is the vindication of equality over a stem inventory.** It appends
`A \texttt{Constant} segment's \texttt{end\_tempo} \MUST{} be absent.` — no
`canonic`, no `normaliz`, no `prefer`, and it settles P13-S8 inside a requirement
minted not to. Equality fails it; every phrase-and-stem form passes it.
#### 9.1.4 An interrupted run left a mutation applied
The mutation harness restores in a `finally`. A **killed** process runs no
`finally`, so an interrupted batch leaves the tree mutated. One did: M2a's
wildcard survived at `invariants.rs:425`.
It was found by re-reading the file, and `invariant_selector_discriminates_its_payload`
confirmed it:
```
invariant 10's selector must return only its own; got [
WellFormednessViolation { kind: Invariant(CrossCuttingRefsResolve), witness: "staff ...
WellFormednessViolation { kind: Invariant(EventCoordinateModel), witness: "aleatoric ...
```
**Restored by hand-editing**, never git, and the full suite returned to
`44 / 1606 / 0 / 0`.
*The lesson is not "be careful with interrupts": it is that a restore guaranteed
only by process exit is not a guarantee. **Verify the baseline after any
interrupted mutation run, before trusting the next measurement** — a stray
mutation makes every subsequent radius wrong in a way that looks like a
mismatch in the wrong place.*
### 9.2 The surface moved twice, and what that obliges
| Surface | Cause |
|---|---|
| 1604 | §4's surface — pin 10's 16 tests, pin 1b's two |
| **1605** | §9.1.1's missing test added |
| **1606** | §9.1.3's missing guard added |
**Radii are measured against the final surface.** The 17 mutations first measured
at 1604 were **re-run in full** at 1605 and all matched. The 38 measured at 1605
were **not** re-run in full at 1606; the justification is bounded and stated:
- **Pin 3a's guard has exactly two inputs** — `core_spec.tex` via `include_str!`
and a string literal. Verified mechanically: no `production_source`, no
`check_*` call, one `include_str!` target.
- **None of those 38 modifies `core_spec.tex`.** They edit `invariants.rs`,
`generators.rs` or `public_surface.rs`.
- **Two representatives were re-run at 1606 anyway**, one per interaction class:
**M2a** — the widest selector radius, 21 — and **M20b**, a prose edit inside
`invariants.rs`. Both matched unchanged.
*This is an argument from a test's complete input set, not from reasoning about
fixture reach — the thing this rung has repeatedly got wrong. It is recorded as
an argument, not presented as a measurement.*
### 9.3 Expected versus observed — all 54 mutations
| M | Cell | Observed | |
|---|---|---|---|
| M1·C4 | 2 | 2 | ✅ |
| M1·C5 | 3 | 3 | ✅ |
| M1·C6 | 2 *(amendment 1)* | 2 | ✅ |
| M1·C7 | 2 *(amendment 1)* | 2 | ✅ |
| M1·C8 | 4 | 4 | ✅ |
| M1·C9 | 2 | 2 | ✅ |
| M1·C10 | 3 | 3 | ✅ |
| M2 | 8 | 8 | ✅ |
| M2a | 21 *(measured pre-ratification)* | 21 | ✅ |
| M3 | 13 | 13 | ✅ |
| M3a | 1 | 1 | ✅ |
| M4 | 1 | 1 | ✅ |
| M5 | 1 | 1 | ✅ |
| M6 | 1 | 1 | ✅ |
| M7 | 7 | 7 | ✅ |
| M7a | 1 | 1 | ✅ |
| M7b | 1 | 1 | ✅ |
| M7c | 1 | 1 | ✅ |
| M7d | 1 | 1 | ✅ |
| M8 | 5 | 5 | ✅ |
| M9 | 14 | 14 | ✅ |
| M10 | 3 | 3 | ✅ |
| M11 | 4 | 4 | ✅ |
| M12pre | 1 | 1 | ✅ |
| M12 | 1 | 1 | ✅ |
| M13 | 1 | 1 | ✅ |
| M13post | 1 | 1 | ✅ |
| M13neutral | 1 | 1 | ✅ |
| M14a–M14e | 1 each | 1 each | ✅ |
| M15a | 1 | 1 | ✅ |
| M15b | 1 | 1 | ✅ |
| M16a | 1 | 1 | ✅ |
| M16b | 1 | 1 | ✅ |
| M17·C1 | 20 *(18 measured + 2 new)* | 20 | ✅ |
| M17·C2 | 3 *(measured)* | 3 | ✅ |
| M17·C3 | 1 *(measured)* | 1 | ✅ |
| M18 | 1 | 1 | ✅ |
| M20 | 1 | 1 | ✅ |
| M20a–M20k | 1 each | 1 each | ✅ |
**No compile-only result. No passing-outcome mutation** — §3 requires every row
to fail, and every row did.
### 9.4 Three cells worth their own note
**M2a, 21, unchanged from its pre-ratification measurement.** §3 warned its proxy
over-approximated for any test observing a rider through the selector, and that
none of the 20 legacy tests was a rider test. Confirmed: the legacy 20 are exactly
the `g3b_measure20_tests` set measured, plus the new discriminator.
**M17·C1, 20, and `display_renders_each_arm_exactly` is not among them.** That
absence is the receipt for §9.1.2: under the unrepaired C1 fixture it would have
been.
**M7b, 1.** §3 records that revision L wrongly named
`every_invariant_has_a_negative_generator` here, since that test iterates `all()`
and cannot detect an omission from `all()`. Observed: `graph_invariant_all_is_unchanged`
alone.
### 9.5 Restoration
After every row, and after the interrupted-run repair:
```
suites=44 passed=1606 failed=0 ignored=0
cargo +1.95.0 clippy --workspace --all-targets -- -D warnings: clean
```
**1586 → 1606, twenty net-new tests**, and 43 → 44 suites:
| Count | Where |
|---|---|
| 17 | pin 10's eleven-row matrix and its whole-surface tests |
| 1 | pin 3a's `.tex` prose guard |
| 1 | pin 1b's derives guard, in `g3a_tests` |
| 1 | pin 1b's integration test — the new suite |
---
## §10. A sixth fault, found by gate 16 at the last moment
**Pin 9's `/// 10.` rider note was never migrated.** It is one row of pin 9's
table, and it was the only row left undone — the other eight were verified
individually rather than assumed, which is how this one surfaced.
The note still read:
```rust
/// Beyond that surface, further checks are reported under this same tag
/// and are NOT part of the normative invariant 10: tempo-map segment
/// shape, ordering and non-overlap (Chapter 3,
/// `req:time:tempo-segment-order`); ...
/// multiplexing is filed as P13-S29 — the public `check_invariant`
/// filter and this violation's `Display` attribute those failures to
/// invariant 10. Repairing it is a behaviour change, out of scope here.
```
**Three statements, all false as of this rung**, and the gate names all three:
the riders are no longer *"reported under this same tag"*; P13-S29 is no longer
their *pending owner* — it is this commit; and the note listed **three** labels
where pin 9 requires **four**, `req:time:tempo-segment-shape` being the one this
rung minted.
*The missing fourth label is the same defect shape as §9.1.1: a set that grew by
one, and a list that did not.* It is now rewritten to name all four and to say
that `check_invariants` still returns them, so a caller asking *"is this graph
well-formed"* keeps its coverage.
**Why no test caught it.** Pin 9's prose outcomes have **no machine observer** —
gate 16 says so outright: *"Every one of these can be omitted with all other
gates green."* Eight rows had landed; the ninth had not; nothing in 1606 tests
could tell the difference.
**Every other pin 9 row was re-verified by its own stale phrase**, not by
assumption:
| Stale phrase | Found in |
|---|---|
| `surfaced here under invariant 10` | clean |
| `go under invariant 10` | clean |
| `surfaced under an existing` | clean |
| `the compatibility invariant` | clean |
| `tempo-map segment invariants` | clean |
| `all 19 enumerated graph invariants` | clean |
| `reported under this same tag` | **`invariants.rs`** → repaired |
| `filed as P13-S29` | **`invariants.rs`** → repaired |
*`core_spec.tex:3120` still occurs in `DECISIONS.md` and `accidental.rs`.
Neither is a pin 9 row: pin 9 pins that locator's replacement in the **accidental
header comment** in `invariants.rs`, which is clean, and it explicitly does
**not** rewrite `DECISIONS.md`, which gains a supersession note instead.*
---
## §11. Gate results, 1–16
| # | Gate | Result |
|---|---|---|
| 1 | `cargo test --workspace` | **44 suites / 1606 passed / 0 failed / 0 ignored** |
| 2 | clippy `-D warnings` | clean, 0 warnings and 0 errors |
| 3 | `fmt -p epiphany-core -p epiphany-testkit --check` | clean (never `--all`) |
| 4 | staged paths ⊆ §2 rows, all rows staged | 13 paths, 13 rows |
| 5 | `git diff --cached --check` | clean |
| 6 | identifier and field migration | §6 above, verbatim |
| 7 | every §3 mutation observed | §9.3 — 54 rows, all matched |
| 8 | `all()` re-derived at 21; `.tex` count claim unchanged | 21 entries, 21 unique, 21 `number()` arms 1..21, order identical; `core_spec.tex:6746` still reads *"exactly \textbf{21} invariants"* and is absent from the diff |
| 9 | `latexmk -xelatex core_spec` | undefined references cleared on pass 1; `core_spec.pdf` rebuilt |
| 10 | ledger append, removed-plus-added reconstruction | 1 removed / 1 added; `added` ends with `\|`; `strip(added) == strip(removed) + " " + APPEND` **true** |
| 11 | temporary allowlist row absent, two survivors present | `req:time:tempo-segment-shape` absent; `req:layoutir:vertical-bands` and `req:graph:aleatoric-reference-locality` present |
| 12 | `requirement_labels` passes with the row absent | 6 passed, 0 failed |
| 13 | pin 11's inventory | **25 observations: 9 migrated (5 + 4), 16 unchanged**, none deleted, none softened; M16a and M16b prove both negatives non-vacuous |
| 14 | pin 12's lifecycle | status block exactly `STATUS: LANDED by this commit.`, no hash; frozen-pins statement shows **0 hunks** in a zero-context staged diff; revisions A–R marked a dated historical record |
| 15 | placement and Revision History row | shape follows order's `\end{requirement}` with only a `\begin{requirement}` between; the added run equals pin 13's block, whitespace-collapsed |
| 16 | pin 9's prose outcomes | §10 — eight rows verified clean by their own stale phrase, one repaired |
### 11.1 Gate 10's one-character finding
The staged ledger append first read `RESOLVED 2026-08-12`; pin 13's `APPEND` is
pinned verbatim as `2026-08-11`. The reconstruction failed on that character
alone, and **the artifact was corrected to the pin, not the pin to the artifact.**
*Flagged for the owner rather than silently reconciled: the contract's own
ratification line reads 2026-08-12, so the pinned append carries the date the
row was drafted rather than the date the rung resolved. Changing it is an
administrative amendment to pin 13, not an execution decision — gate 10 exists
to make exactly this deviation visible.*