diff --git a/crates/epiphany-testkit/tests/requirement_labels.rs b/crates/epiphany-testkit/tests/requirement_labels.rs index fc8d8b4..bb4b971 100644 --- a/crates/epiphany-testkit/tests/requirement_labels.rs +++ b/crates/epiphany-testkit/tests/requirement_labels.rs @@ -378,12 +378,46 @@ fn is_citation_byte(byte: u8) -> bool { /// /// A row here is a claim that the string is discussed, never cited. Keep it short, /// and give the reason. -const DISCUSSED_NOT_CITED: &[(&str, &str)] = &[( - "req:layoutir:vertical-bands", - "never existed; the Pass-12 log cited it for a behavioural fix no requirement \ - governs. Named in spec/PLAN_P13S1_LABELS.md as the finding that motivated \ - this checker.", -)]; +const DISCUSSED_NOT_CITED: &[(&str, &str)] = &[ + ( + "req:layoutir:vertical-bands", + "never existed; the Pass-12 log cited it for a behavioural fix no requirement \ + governs. Named in spec/PLAN_P13S1_LABELS.md as the finding that motivated \ + this checker.", + ), + // --- P13-S26 REVIEW SCAFFOLDING (pre-execution) ------------------------- + // + // The citation scan reads the filesystem, not git, so the contract at + // spec/CONTRACT_P13S26_INVARIANT10_SURFACE.md is scanned like any other + // repository text — tracked or not, staged or not. It names both labels + // below, neither of which exists yet, which broke the mandated green + // baseline for three review rounds before it was measured. Euphemism is not + // the remedy — see this const's own doc comment. Authorized as prerequisite + // review scaffolding, NOT as dispatch of P13-S26; no other pin work is + // licensed by it. + // + // Deliberately says nothing about its own staging or tracking state: both + // change at ratification, while pin 10 of that contract deletes this banner + // only at execution, so any such claim would spend that interval false. + // + // If S26 is abandoned, or either label changes before the rung lands, + // remove the corresponding row by hand. + ( + "req:graph:aleatoric-reference-locality", + "not defined in the restored tree; P13-S26's M6 mutation defines it only \ + while that mutation is applied. Named by \ + spec/CONTRACT_P13S26_INVARIANT10_SURFACE.md and recorded in \ + spec/EVIDENCE_P13S26_EXECUTION.md as M6's verbatim diagnostic. PERMANENT.", + ), + ( + "req:time:aleatoric-reference-locality", + "proposed by spec/CONTRACT_P13S26_INVARIANT10_SURFACE.md; the requirement \ + does not exist until that contract's pin 4 lands. TEMPORARY — pin 4 \ + REMOVES this row when it creates the requirement, because this row's own \ + claim (discussed, never cited) becomes false at that moment. A stale row \ + is inert, so nothing else will catch it; that contract's gate 12 does.", + ), +]; fn requirement_strings(text: &str) -> BTreeSet { let bytes = text.as_bytes(); diff --git a/spec/CONTRACT_P13S26_INVARIANT10_SURFACE.md b/spec/CONTRACT_P13S26_INVARIANT10_SURFACE.md new file mode 100644 index 0000000..e30a685 --- /dev/null +++ b/spec/CONTRACT_P13S26_INVARIANT10_SURFACE.md @@ -0,0 +1,1638 @@ +# Contract — P13-S26: invariant 10's reference surface, derived + +**Status:** **RATIFIED 2026-08-11, on the authority of the repository owner**, +after the independent whole-artifact passes recorded above §0 — the last +returning zero findings. **NOT YET DISPATCHED**; ratification and dispatch are +separate acts and no execution has begun. Which passes closed and what each +found are those records; this line does not restate them, and states no count of +them, per the rule this contract adopted after its own tallies went stale twice. + +**THE PINS ARE FROZEN. They may be executed, not edited.** A defect found during +execution is **reported, not patched in place** — if it needs a pin change, that +is its own amendment with its own review round. + +Owning candidate: **P13-S26** (`spec/PASS13_CANDIDATES.md`, Batch 3). +Rung class: **documentation-and-guard. No tag change, no behaviour change.** + +--- + +### REVIEW ROUND 18 — independent. One blocking finding. ACCEPTED. + +1. **[P1] Gate 10 observed only part of pin 7.** It required both row ids and + expanded S30's four consequences and M20 evidence, but did not require + S29's rendered-example citation, S29's behaviour-change classification, + S30's test-scope/no-production-consumer scoping result, or either row's + unresolved/out-of-scope disposition. A stub S29 row, or a row incorrectly + marked resolved, satisfied the gate. **Fixed** — gate 10 now checks the + complete filing contract for each row, including evidence, scope and status. + +**Review-process correction:** this finding came from reading pin 7 and gate 10 +side by side, not from another search for likely omissions. Future contract +reviews should begin with that complete cross-reference pass: for every pin, +identify its changed artifact, touch row, machine test or explicit read-check, +and signing mutation/evidence where one is claimed. A cell is either populated +or marked **N/A with a reason**; an unexplained empty cell is a finding in the +same round. This is a review method, not another execution gate for S26. + +--- + +### RATIFICATION CHECK 2 — independent, whole-artifact. **NO-GO: one completeness correction.** ACCEPTED. + +1. **[P1] S30's "exhaustive" inventory covered only the opening delimiter.** + Both parsers match the **closing** form exactly too — + `requirement_labels.rs:167` (`let end = r"\end{requirement}"`) and + `text_projection_grammar.rs:649` + (`split_once("\\end{requirement}").expect(…)`) — and the row listed neither. + **Fixed**, and it is not symmetry for its own sake: the failure differs by + which delimiter is spaced. + + **Exact opener with spaced closer** was traced, not assumed. The scan finds + the opener and hunts an exact close: with **no later exact close** both + parsers **panic**, with two distinct messages; with **a later exact close** + the scan **consumes through it**, swallowing the following block's opener so + that block is never recorded. Usually loud — the consuming block inherits ≠1 + label — **but a second silent case exists**: an additive, label-free block + with exact opener and spaced closer, placed immediately before an existing + single-label block **in the same chapter**, inherits that one label, replaces + the swallowed block one-for-one in the tally, leaves the whole-text label + scan untouched, and keeps the same chapter. Every count holds. + + So the requirement-block consequence has **two** silent cases, not one, and + the silent-cases paragraph now says so. + +2. **M20 is qualified as opener-only evidence.** Its probe spaces **both** + delimiters, so no parser reaches the opener and none ever hunts a close — it + cannot exhibit consequence (ii). Pin 7 and gate 10 both say this explicitly; + a rung wanting (ii) exhibited owes its own probe. *An unqualified citation + would have made the row claim evidence it does not have — the same class as + round 13's premature "M20 is the evidence".* + +3. **Gate 10's inventory requirement now spans both delimiters** of every + requirement-block scanner, so the omission cannot recur by transcription. + +--- + +### RATIFICATION CHECK — independent, whole-artifact. **NO-GO: three corrections.** ALL ACCEPTED. + +1. **[P1] Pin 1a contradicted pin 6.** It said an out-of-vocabulary term "fails + on the same assertion" as the ordering check — **the exact error round 3 + caught in pin 6 and fixed there only.** A bad term sorts perfectly well, so + order cannot see it; that is why pin 6 step 4 splits into (a) order and (b) + vocabulary. **Fixed** — pin 1a now states the split and points at the two + signing mutations. +2. **[P1] P13-S30's consumer sweep was incomplete**, and worse than unswept: + `binary_format_history.rs` **appeared in the round 12 grep** and was never + carried into the row. Also omitted were the exact `\label{…}` guards in + `text_projection_grammar.rs`. **Fixed by re-deriving the inventory from an + exhaustive grep rather than from recall**, and the result is larger than + either list: `text_projection_grammar.rs` at `:73`, `:341`, `:363`, `:508`, + `:538`, `:591`, `:596`, `:647`; `binary_format_history.rs:107`–`:112`, whose + `.expect` **panics** on a spaced heading; `parse.rs:726`. **Gate 10 now + requires the row to preserve the inventory in full**, so the omission class + is observed rather than trusted. +3. **[P2] §0.6's locator claim was too absolute.** Pin 10 retains a verbatim + `requirement_labels.rs:486:5` diagnostic. **Fixed** — the claim is narrowed + to *operative* locators: nothing is found, sliced or asserted by coordinate, + while coordinates inside frozen diagnostics are quoted evidence that + reproducing exactly is the whole point of. + +*Finding 2 is the sharper lesson: a sweep whose result is not written down is +not a sweep. I ran that grep, read the hit, and let it die in the transcript.* + +--- + +### REVIEW ROUND 17 — independent. One blocking finding. ACCEPTED. + +1. **[P1] Round 15's block still carried the superseded conclusion**, one + paragraph below its own correction: *"the consequence reads 'silent when + additive and label-free'"*, against the live pin's *"additive and free of + `req:` labels"*. **Fixed** — struck and corrected. Round 16's closing claim + that the record was *"struck in place with both corrections"* is struck too: + **two of three instances were struck, not all of them**, so that sentence was + false as written. + +**This is the one-hop failure at its narrowest** — not a correction that reached +one document and stopped, but one that reached two sentences of a paragraph and +stopped at the third. Rounds 13, 14, 16 and 17 have each found a variant of it. +The standing remedy in P13-S16 §7 is to **grep the superseded phrase rather than +re-read for it**; that is what produced this fix, and it is what round 16 should +have done before claiming the record was fully struck. + +--- + +### REVIEW ROUND 16 — independent. One blocking finding. ACCEPTED. + +1. **[P1] M20's isolation conditions were conflated with the parser defect's + silence conditions**, and one supporting fact was wrong. Verified in the + source: + - `CORE_REQUIREMENT_COUNT` and `SUITE_REQUIREMENT_COUNT` count **blocks** + (`document.requirements.len()`); `SUITE_LABEL_COUNT` counts **labels from a + whole-document scan**. So re-spacing an existing labelled block drops the + two **block** counts — **not** `SUITE_LABEL_COUNT`, as round 15 claimed; + the label remains visible precisely because collection is whole-text. That + is the same fact round 15 used correctly one sentence later and misapplied + here. + - **Parser silence** therefore requires: *additive*, and *no globally + recognized `req:` label*. A `tmp:`-namespaced label is invisible to + `labels()` and does not break silence. + - **Label-freedom is M20's own isolation condition**, not a silence + condition: any label inside item 10's slice would additionally trip test + 1's independent no-`\label` assertion, widening M20's radius to two + assertions and destroying the separation M18/M20 exist to maintain. + + **Fixed** in pin 7's consequence, the silent-cases paragraph and M20's + rationale, which now labels each of its three properties by which purpose it + serves. ~~Round 15's record is struck in place with both corrections.~~ + **— corrected by round 17: two of three instances were struck. The third, + in the same paragraph, still stated the superseded conclusion.** + +--- + +### REVIEW ROUND 15 — independent. Two blocking findings. ALL ACCEPTED. + +1. **[P1] The requirement-block branch is not unconditionally silent, and M20's + clean radius rested on an unstated condition.** Traced: ~~re-spacing an + *existing labelled* block drops `CORE_REQUIREMENT_COUNT` **and** + `SUITE_LABEL_COUNT`~~ **— corrected by round 16: it drops + `CORE_REQUIREMENT_COUNT` and `SUITE_REQUIREMENT_COUNT`, both *block* counts; + the label stays visible, so the label counts hold** — and a hidden block that + carries a `req:` label still + moves `SUITE_LABEL_COUNT`, because `all_defined_labels` calls `labels()` on + the **whole document text**, not per block. ~~Only an **additive, label-free** + probe is silent.~~ **— corrected by round 16: parser silence needs only + *additive* and *no `req:` label*; label-freedom is M20's isolation + condition.** **Fixed** — M20's content is pinned exactly, with each of + its three properties justified by the failure it avoids; the consequence + reads ~~*"silent when additive and label-free"*~~ **— corrected by round 17, + the third and last instance of this round's superseded conclusion: the live + pin reads *"silent when additive and free of `req:` labels"*** ; and gate 10 now cites the + **requirement-block silent case**, one of two, rather than "the" silent one. +2. **[P1] The chapter partition was incomplete.** Four outcomes, by what the + predecessor is: **(a)** no prior recognized chapter → `load_spec` panics; + **(b)** predecessor absent from `CHAPTER_AREAS` → the area test's + `unwrap_or_else` panics with "missing chapter-area data"; **(c)** predecessor + in a different area → loud mismatch; **(d)** predecessor in the same area → + silent. **Fixed**, all four recorded. Also corrected: `binary_format.tex` has + **twelve** chapter headings, of which **seven** appear in `CHAPTER_AREAS` — I + had written "all seven chapters", conflating the table's coverage with the + file's contents. The `Operation Wire Forms` example survives, and is now + anchored: `Graph Value Layouts` (`:814`) immediately precedes it (`:1196`). + +*Both findings are one class: a consequence recorded without the condition that +produces it. Pin 7 now says why that matters — an unconditioned "silent" invites +a later reader to reproduce it the loud way and conclude the candidate is wrong.* + +--- + +### REVIEW ROUND 14 — independent. Two blocking findings. ALL ACCEPTED. + +1. **[P1] The temporal sweep stopped one hop early.** Round 13 made S30's + evidence claims prospective and left *"two defects are filed"*, §0.4's + filing sentence, both `Filed with…` clauses and §5's S29 bullet asserting a + state execution has not yet produced. **Fixed** — every live occurrence is + prospective, and **the ledger row remains the definition of filed**, not this + contract's description of it. *This is the correction-propagates-one-hop + defect, in the round whose own subject was premature state.* +2. **[P1] The chapter branch is not always loud.** `load_spec` binds a + requirement to the previous *recognized* chapter while the area test compares + only the coarse area — so a lost `\chapter {…}` between chapters that share + an area is **silent**. Verified: ~~all **seven** `binary_format.tex` chapters + map to `binfmt`~~ **— corrected by round 15: the file has twelve chapter + headings, seven of which appear in `CHAPTER_AREAS`** — so losing + `\chapter {Operation Wire Forms}` rebinds its + requirements to `Graph Value Layouts` and the suite stays green. **Fixed** — + the consequence is now "silent or loud, by neighbourhood", with the loud + cases named (differing areas; the first chapter, which trips the + "requirement before first chapter" panic). + + **This changes the candidate's weight**, so pin 7 now says it: **two** of the + four consequences can pass silently, not one. A defect that fails loudly is + at worst misdiagnosed; one that passes is not seen at all. + +--- + +### REVIEW ROUND 13 — independent. Two blocking findings. ALL ACCEPTED. + +1. **[P1] The fourth consequence was overstated twice.** `parse.rs:726` sits + inside `#[cfg(test)] mod tests` (opened at `:639`) — **it is not production + code**, and my round 12 sweep claimed otherwise by treating a path under + `src/` as production without checking reachability. Separately, the read + proves **duplicated exact-string scanners**, not *present divergence*. + **Fixed** — the row now says duplication replicates the blind spot and makes + divergence a **latent risk**, all sites are marked test-scope, and S30's + filing states explicitly that **no production consumer was found during + scoping**, so a later one extends the row rather than contradicting it. The + round 12 claim is retained **struck through** with its correction, on the + precedent of P13-S16 §7's withdrawn finding. +2. **[P1] M20 was written as existing evidence.** It has not run, the annex does + not exist, and S30's row is created *during execution* — so "M20 is the + evidence", "the defect is demonstrated" and "the analysis lives in the + ledger" all asserted a future state as current. **Fixed** — M20 *will + furnish* the evidence, the **landed** row *must* cite its transcript, and §5 + says the analysis *will live* there. What the code read establishes now, and + what execution will exhibit, are separated. + +*Both findings are the same class the last several rounds have turned on: a +claim about a state that does not hold yet, or a scope inferred rather than +traced.* + +--- + +### REVIEW ROUND 12 — independent. One blocking finding. ACCEPTED, with the owner's ruling. + +1. **[P1] §5 scoped the checker defect too narrowly, and a §5 note is not + ownership.** Round 11 framed it around spaced `\label`, but **M20 proves the + worse branch**: a legal `\begin {requirement}` is silently missed by the + block scanner with nothing else reacting — this contract's own mutation plan + depends on that property. Exact parsing also reaches `\chapter`, and a second + requirement scanner exists in `text_projection_grammar.rs`. + + **Ruling: file it as P13-S30**, through a pin, a touch-row consumer and a + gate assertion. **Done** — pin 7 now files two rows, gate 10 asserts both, + touch row 6 names both, and §5 defers to the ledger instead of carrying the + analysis into a document that becomes historical at landing. + + **Verified all four consequences rather than transcribing them.** + + > ~~And one reaches further than the finding states: the duplicated-scanner + > branch is not confined to tests. `crates/epiphany-textproj/src/parse.rs:726` + > splits on an exact `\chapter{A Worked Example}` in **`src/`** — the + > exact-parsing assumption is in production code, not only in the checkers.~~ + > + > **WITHDRAWN by round 13, finding 1.** `#[cfg(test)] mod tests` opens at + > `parse.rs:639`, so line 726 is **test scope**. The claim was reached by + > treating a path under `src/` as production without checking reachability — + > the repository's own standing rule is to enumerate the reachable paths, not + > to infer them from where a file sits. Retained struck rather than deleted: + > the correction is the point. + +--- + +### REVIEW ROUND 11 — independent. Three findings, one blocking. ALL ACCEPTED. + +1. **[P1] The new structural guard was incomplete in syntax and in mutation + coverage.** It matched literal `\label{` and `\begin{requirement}`, which TeX + spellings like `\label {x}` bypass — and **the repository's own parser has + the same hole**: `command_arguments` builds an exact `\{command}{` needle + (`requirement_labels.rs:117`). A guard shaped like the checker it + complements inherits that checker's blind spot. Separately, M18 signed only + the label assertion, leaving the independent requirement-block assertion + unexercised. **Fixed** — recognition is whitespace-tolerant in test 1 *and* + in gate 11's file-wide check; M18 becomes the **spaced** label form; and new + **M20** adds a spaced `\begin {requirement}`, which the existing scanner + misses entirely, making test 1 the clean discriminator with no other suite + reacting. +2. **[P2] The round 10 audit tally was wrong** — "seven" against six + enumerated. **Fixed by removing the numeral**, not by repairing it, per the + rule this contract adopted in round 6 after the review-round count went + stale twice. +3. **[P2] M18 did not exercise count-remeasurement blindness.** `labels()` + keeps only `req:`-prefixed labels, so `tmp:m18` never reaches a count + constant and M18 is identical before and after remeasurement. The safe + namespace stays; the **claim** goes. **Fixed** — M18 is described as a + surrogate that signs test 1's assertion, with the count-blindness it guards + against explicitly marked as *argued, not exhibited*. + +**Raised, not filed** — and narrowed after tracing it. Finding 1 exposes a gap +in `requirement_labels.rs` itself, but *"invisible to the entire checker"* was +my first, overstated reading. Traced: a spaced label outside `req:` is indeed +invisible to every test — which is what makes M18 a sound surrogate — while a +spaced label **inside** `req:` desynchronizes the defining and citing sides and +**fails loudly with a misleading diagnosis** rather than passing. Still a defect +worth filing, but a different one. §5 carries it. + +--- + +### REVIEW ROUND 10 — independent. One blocking finding, three parts. ALL ACCEPTED. + +1. **[P1] Gate 11's claimed complement still missed two pin 3 outcomes**, and + its preamble overclaimed. + - *Opening sentence.* Pin 3 requires it to survive; pin 6 mentioned it only + as "the sentence the list follows", never pinning it as a locator. + **Fixed by the stronger of the two offered remedies:** test 1's outer + anchor is now **the complete opening sentence matched as an exact + literal**, so the requirement and its observation are the same string. A + prefix anchor would have left the remainder unguarded. M19 signs it. + - *No new label, no requirement block.* Pin 3 forbids both and nothing + enforced it — an accidental well-formed label is **absorbed the moment pin + 4's count constants are remeasured**, which pin 4 instructs execution to + do. **Fixed with a machine assertion rather than a read check**, since test + 1 already holds the outer slice: it must contain no `\label{` and no + `\begin{requirement}`. M18 signs it, run in the remeasured state — the + condition under which the label suite goes blind. + - *Preamble.* Narrowed from "the tests" to **tests 1 and 2**, since test 3 + deliberately observes requirement prose and is not part of the + machine-observed complement. + +**Sweep — the blindness is not confined to item 10.** The count-remeasure +argument holds for a label added *anywhere* in `core_spec.tex`, which test 1's +slice cannot see. Gate 11 gains a file-wide read-check: the staged diff adds +**exactly one** `\label{`, pin 4's. + +**And one defect of my own, caught by running the suite rather than by +reading.** M18 was first written with a `req:graph:`-shaped label literal (the +string is deliberately not reproduced here — see below), which **immediately +broke the baseline to 1582/1**, undefined-citation, +`spec/CONTRACT_P13S26_INVARIANT10_SURFACE.md`. That is round 4's finding +reproduced while writing round 10's fix, in a document whose pin 10 exists to +prevent it. + +**Then the repair's own record reproduced it a second time**, because naming the +offending literal in this block put it straight back into the scan — the checker +cannot distinguish quoting from citing. Hence the shape, not the string: a +`req:`-form label. Nothing is lost, since the string was arbitrary and only its +form mattered. *(This is not the euphemism the allowlist's doc comment warns +against — that concerns a dangling label a document genuinely needs to name.)* + +Repaired at the root — M18 now pins +`\label{tmp:m18}` and states *why* the non-`req:` form is mandatory — rather +than by adding a third allowlist row, which would have grown permanent +scaffolding to accommodate a throwaway mutation. **Every `req:*` literal in this +contract was then audited**, and each is accounted for: pin 10's two allowlisted +labels, the genuinely-defined requirements it cites +(`req:time:tempo-segment-order`, `req:time:aleatoric-anchoring-discipline`, +`req:tuning:accidental-modification-compatibility`), and +`req:layoutir:vertical-bands`, which the existing allowlist already covers. + +--- + +### REVIEW ROUND 9 — independent. Two findings, one blocking. ALL ACCEPTED. + +1. **[P1] Gate 11's inventory of unobserved prose was incomplete.** Three pin + 3/5 outcomes passed every test and gate if simply omitted: item 10's + re-anchoring exception and its `Chapter~\ref{ch:semops}` reference (deletable + with the nested inventory intact); pin 5's rider separation and its P13-S29 + attribution (test 2 reads only pin-shaped token rows); and **the G3a aside's + removal** — §0.1 rules out a mutation restoring it, but *declining to mutate + something is not observing it*, and the aside is not pin-shaped, so test 2 + would pass with it still in place. **Fixed** — all three added to gate 11, + which now opens by stating that the tests observe the token inventories and + nothing else, so every other prose outcome belongs there by default. +2. **[P2] Gate 12 did not observe pin 10's traceability clause.** The permanent + row's reason must name this contract and the evidence annex; gate 12 checked + existence, wording and marker absence, all of which a shortened reason + satisfies while stranding the tuple. **Fixed** — both provenance paths are + named in the read-check. + +**Sweep for the same class — one further instance.** Pin 6 requires its tests to +carry the comment explaining why the slice is mandatory; nothing observed it. +Added to gate 11. + +--- + +### REVIEW ROUND 8 — independent. Two findings, one blocking. ALL ACCEPTED. + +1. **[P1] The frozen-line observer was misattributed and underspecified.** Pin + 11 credited gate 6, which reads only `invariants.rs` and never opens this + file — leaving the invariant unobserved while appearing guarded. Worse, gate + 13 and touch row 7 forbade any "hunk" on the line, which **a correct + execution would fail**: pin 11's STATUS edit sits a few lines above, so at + default context the unchanged frozen line appears as ordinary diff context. + **Fixed** — the observer is gate 13, and the check is a comparison: + `git diff --cached -U0` must contain no added and no removed line matching + the statement. Zero context is what makes it satisfiable, and the pin says so. +2. **[P2] Pin 10's deletion rationale confused provenance with current + applicability.** *"Authorized as prerequisite scaffolding, NOT as dispatch"* + is a claim about how the rows were **introduced**; landing cannot + retroactively re-authorize the past. *"No other pin work is licensed"* stays + true, and the abandonment instruction becomes **inapplicable** rather than + false. The claim that "the rows become dispatched work" was also wrong on its + own terms — pin 4 deletes the temporary row, so only one survives. **Fixed** — + the rationale now separates **falsified** (two claims) from **obsolete but + true** (the rest), and deletion is justified on both counts rather than by + overstating the first. + +--- + +### REVIEW ROUND 7 — independent. Two findings, both blocking. ALL ACCEPTED. + +1. **[P1] Pin 10 described the deleted scaffold, not the live one.** It quoted + `(pre-ratification, unstaged)` and reasoned from claims round 6 had already + removed, with the stale terminology surviving in execution item 1 and touch + row 5. **Fixed** — every live consumer now names the `(pre-execution)` banner, + and the deletion rationale is re-derived from the claims the *current* banner + actually makes. *Round 6 fixed the scaffold and left the pin describing it — + the one-hop failure again, two rounds running.* +2. **[P1] Pin 11 assigned a ratification transition to landing.** The + frozen-pins statement is written in the **ratification** commit — verified in + both precedents, which carry it word for word — so by execution the editable + line is already gone. Pin 11's second "edit" could produce no hunk, touch row + 7 overstated, and gate 13 would have observed a pre-existing state as though + it were a landing action. **Fixed** — pin 11 now separates **two landing + edits** from **one ratified-input invariant**, and gate 13 checks the + invariant as an invariant: a diff hunk on that line is a finding, not a + discharge. + +**Sweep for both classes — no further instance.** Every remaining +`pre-ratification` mention is inside a review-round record, which pin 11 marks +as historical at landing; no live pin, gate or touch row carries the old +terminology. Every pin was re-read for actions that belong to ratification +rather than landing, and pin 11 was the only one. + +--- + +### REVIEW ROUND 6 — independent. Five findings, four blocking. ALL ACCEPTED. + +1. **[P1] The scaffold became false at ratification, not landing.** Its banner + claimed "pre-ratification, unstaged" and called the contract "untracked" — + claims falsified when both are committed *for* ratification, while pin 10 + deletes the banner only at *execution*. **Fixed with lifecycle-neutral + wording** rather than a second transition pin: the banner now says nothing + about its own staging or tracking state, and says why. +2. **[P1] Pin 11 required an impossible self-reference.** A commit cannot contain + its own hash. **Fixed** — the STATUS line is pinned as the symbolic + `LANDED by this commit.` with **no hash**, on P13-S16's precedent (`aee4ff9` + carried pre-landing wording; a later amendment recorded the id). +3. **[P1] Pin 10 pinned the wording it went on to forbid.** Its illustrative + repair block still showed "never exists" while its landed-form paragraph and + gate 12 required "not defined in the restored tree". **Fixed** — the block + carries the final wording, and item 2 now says the row is written once, + correctly, with nothing to edit later. *This is the one-hop correction + failure — the live scaffold and the prose were fixed, the illustration was + not — which is the defect P13-S16 §7 exists to name.* +4. **[P1] Round 5 was missing from the record, and the tally could not survive + this round.** **Fixed** — rounds 5 and 6 are recorded, and every count of + rounds is replaced by *all review-round blocks*, which cannot go stale. The + same rule the contract already applies to `GraphInvariant::all` and to pin 1's + inventory, applied to itself. +5. **[P2] Touch row 7 described one of pin 11's three edits.** **Fixed** — all + three named in the row. + +--- + +### REVIEW ROUND 5 — independent. Three findings, one blocking. ALL ACCEPTED. + +1. **[P1] The scaffolding had no transition to a truthful landed form.** Pin 4 + removed the temporary tuple but nothing removed the surrounding banner, so + the staged file would still have called itself pre-ratification scaffolding + while shipping the permanent row. **Fixed** — pin 10 requires deleting the + entire banner, gate 12 asserts the marker string is absent, and the row's + "never exists" reason was corrected in the live scaffold as well as pinned. +2. **[P2] Touch row 5 named only the count changes**, not pin 10's three + obligations. **Fixed** in the row. +3. **[P2] Pin 4a still claimed three signing mutations** after M16 was added. + **Fixed** — four (M11–M13, M16), with normative force listed among what test + 3 protects. + +**Ruling adopted:** no ledger row for the active scaffold. It would spread the +pre-ratification exception into another tracked file without creating a stronger +observer; the code-local instruction plus the visible dirty diff suffices while +S26 is active, and if S26 is abandoned, scaffold removal and the ledger +disposition happen together. + +**Sweep beyond the round — three uncited instances:** touch row 7 authorized an +edit no pin mandated (→ pin 11); touch row 4 omitted pin 4a's test 3; and pin 6 +said "both tests" in three places, written when there were two. + +--- + +### REVIEW ROUND 4 — independent. Six findings, four blocking. ALL ACCEPTED. + +1. **[P1] The evidence annex would have poisoned the final citation gate.** + M6's verbatim diagnostic contains `req:graph:aleatoric-reference-locality`; + after restoration that label is undefined, and + `every_requirement_citation_is_defined` scans the annex like any other + repository text. Verified: `repository_text_files` recurses everything but + `.git` and `target`, excluding only generated *extensions* — `.md` under + `spec/` is scanned — and `DISCUSSED_NOT_CITED` is exactly the hatch, with no + anti-rot assertion, so an entry is safe both while the label exists (during + M6) and after. **Fixed** by pin 10, which also fixes the **ordering**: the + entry must land *before* M6's transcript is written, or every later + mutation's radius silently gains a spurious failure. Redacting the + diagnostic was rejected — verbatim evidence is the point. +2. **[P1] Test 3 invalidated M5 and M6's exhaustive radii.** Both mutations + remove test 3's pinned label anchor. **Fixed** — both rows now include it. + *This is the cost of an exhaustive-radius rule, working as intended: adding a + guard obliges re-deriving every radius that guard can reach.* +3. **[P1] M14 changed membership as well as order.** No inventory row has the + target set `{live event, anchor target}`, so M14 failed at pair equality and + signed nothing about ordering. **Fixed** — it now permutes a *complete* + existing target (`AnalyticalAnnotation.anchor`), so normalization alone would + recover the expected pair and only the order assertion can catch it. +4. **[P1] M7 did not discriminate the nested extractor from a plausible wrong + one.** Ordinary prose is ignored by both the correct first-environment + extractor and a faulty scan-every-`\item`-in-the-outer-slice one. **Fixed** — + the row moves into a **second nested `itemize`** inside item 10, which the + correct extractor misses and the faulty one still collects. +5. **[P2] Test 3's `\MUST{}` assertion was unsigned.** **Fixed** — M16 weakens + it to `\SHOULD{}`. +6. **[P2] Oracle validation's vocabulary branch was unsigned.** M15 exercised + only uniqueness. **Fixed** — M17 puts an out-of-vocabulary target in the + oracle itself, requiring step 0's vocabulary diagnostic specifically. + +--- + +### REVIEW ROUND 3 — independent. Seven findings, four blocking. ALL ACCEPTED. + +1. **[P1] Pin 4's substantive requirement had no observer.** Label, count, + grammar, area and citation were all watched; the *wording* was not, so "same + region" → "any region" passed every gate. **Fixed** — pin 4a adds test 3, + slicing the requirement by its label, with three content mutations + (M11/M12/M13). Read-checking was available and rejected for the same reason + as round 2 finding 3. +2. **[P1] Only the LaTeX side's target extraction was mutation-signed.** Every + M4 row would still fail if test 2 compared tokens alone, so the Rust doc + could have read `Slur.start_event — declared staff` unchallenged. **Fixed** — + M4d and M4e are the Rust-side equivalents of M1c and M1d. The closed + vocabulary is intended on both sides, so M4e is included rather than + omitted. +3. **[P1] M7 was still not the mutation it claimed to be.** An `\item` moved out + of the nested `itemize` joins the surrounding `enumerate` and *becomes the + next top-level invariant* — leaving item 10 entirely, rather than staying in + it. **Fixed** — M7 now moves the token-target **text** outside the inner + environment as ordinary prose, with no `\item`, so it genuinely remains + inside item 10 while vanishing only from the nested extraction. +4. **[P1] The required evidence had no artifact or destination.** Gates 6 and 7 + demanded verbatim transcripts that the staging allowlist gave nowhere to + live. **Fixed** — `spec/EVIDENCE_P13S26_EXECUTION.md` is pinned as touch row + 8, up front, rather than discovered at acceptance as P13-S16's annex was. +5. **[P2] Eleven code anchors did not resolve to the checker's expressions.** + Verified: `cc` is bound at `invariants.rs:1075`, *after* the slur, tie, beam, + tuplet and spanner loops at `:999`–`:1050`, which use + `self.score.cross_cutting.*`. Exactly eleven rows were wrong. **Fixed** — all + eleven now name the actual expression. +6. **[P2] Alphabetical target order was declared normative but normalized + away.** **Fixed** by keeping the order normative and *enforcing* it: pin 6 + asserts the raw target already equals its normalized form before comparing. + M14 writes an unsorted target and must fail. +7. **[P2] Duplicate validation excluded the oracle itself.** A duplicate inside + `INVARIANT_TEN_SURFACE` vanished on conversion to `BTreeSet`, leaving both + documents and M9/M10 passing. **Fixed** — pin 6 step 0 validates the constant + before it is used as an oracle: unique tokens **and** every target term drawn + from pin 1a's vocabulary. M15 duplicates an inventory entry. + +--- + +### REVIEW ROUND 2 — independent. Seven findings, five blocking. ALL ACCEPTED, and the sweep found two more. + +1. **[P1] The inventory contained invented field paths and had lost the promised + code-anchor column.** Verified against `graph.rs`, `event.rs` and `tempo.rs`: + `DecompositionComponent` **does not exist**, and four more tokens named the + wrong owner. **Fixed** — pin 1's tokens are now actual schema paths, each + carrying a **Code anchor** column back to the emitting control flow. + + **Sweep for the same class, beyond the five cited — two more found:** + - `Repeat.start` / `.end` / `.kind` / `.voltas` → the struct is + **`RepeatStructure`** (`graph.rs@1261`–`@1267`). Four tokens. + - `IndeterminateEvent.alternatives` → the check reads `ie.hints.alternatives`, + so the owner is **`IndeterminacyHints`** (`event.rs@70`). + + Every remaining token was checked the same way and is correct as written. +2. **[P1] `BTreeSet` equality erased duplicates.** **Fixed** — both tests now + extract to a `Vec`, assert the duplicate list is **empty** (naming the + repeats, hard-coding no total), and only then compare sets. New mutations + M9/M10 duplicate a token in each document. +3. **[P1] The guard observed class names but not what they resolve to.** Pin 1's + target column was normative while pins 3 and 5 declared trailing prose free — + so swapping `live event` for `declared staff` passed every gate. **Fixed by + the stronger of the two offered options:** the comparison is now over + **(token, target) pairs**, with targets drawn from a closed canonical + vocabulary (pin 1a). Read-checking was rejected — a rung arguing that + unguarded documentation drifts should not leave half its own repair unguarded. +4. **[P1] M7 could not produce its required failure.** Extending the *outer* + slice past invariant 11 does not change the *nested* `itemize`'s contents, so + a correct extractor returns the same set. **Fixed** — M7 is replaced by a + mutation that targets the nested-environment boundary specifically: move one + `\item` out of the `itemize` into item 10's prose, still inside the outer + slice. +5. **[P1] The mutation table left failure radii for execution to discover.** + **Fixed** — every "Must fail" cell is now exhaustive, and §3 states that any + mismatch with the observed set is itself a finding. Both cited rows verified + in `requirement_labels.rs` and adopted: the count constants are asserted at + `:259`/`:265` (`every_requirement_block_has_one_label`), `:294` + (`requirement_labels_follow_the_grammar`), `:364` + (`requirement_labels_are_unique_across_the_suite`) and `:445` + (`every_requirement_citation_is_defined`) — so M5 trips all four, and M6 + trips `requirement_label_areas_match_their_chapters` plus + `every_requirement_citation_is_defined`, because this contract cites the + original `req:time:` label and the test scans repository text. +6. **[P2] Pin 8 required a rename without pinning the name.** **Fixed** — the + replacement is named exactly. +7. **[P2] M3 did not specify the weakening or which M1 it consumes.** **Fixed** — + the predicate is pinned as `actual.is_subset(&expected)` (the direction that + passes after a deletion; `expected.is_subset(&actual)` still fails), and M1 is + now six individually named executions, with M3 consuming **M1-B**. + +--- + +### REVIEW ROUND 1 — independent, against the untracked draft. Seven findings, five blocking. ALL ACCEPTED. + +1. **[P1] The widened guards did not establish completeness.** Pin 8 required + one needle per group A–F; a guard could pass while group A lost sub-beam + members, volta anchors and repeat-kind anchors together. **Fixed** by + replacing needle spot-checks with **exact set equality over a canonical token + inventory** (pin 1, pin 6). Deletion *and* addition now fail, per token. +2. **[P1] Pin 6's uniqueness claim made M3 impossible.** Pin 6 asserted every + needle occurred only in the item-10 slice; M3 required that an unsliced guard + *pass* because listings contain the needles. Both could not hold. **Fixed** + by dissolving the premise: there are no needles. With set equality, an + unsliced extractor over-collects and fails loudly, so the slice is required + for the guard to *function*, not to have teeth. + *Measured while checking this: `instrument_override` occurs exactly once in + `core_spec.tex` (the `StaffInstance` listing), so the original collision + control could not have worked as written either.* +3. **[P1] Pin 9 had no observer.** **Fixed** — gate 6 is now a staged-diff + boundary gate with explicit forbidden-token assertions. +4. **[P1] The mutation evidence gate was unsatisfiable.** Gate 6 demanded a + failing assertion for every mutation while M3's required outcome is success. + **Fixed** — §3 splits failing-evidence mutations from the single + passing-evidence one, and requires a full workspace run with + `--no-fail-fast` and the complete failure set for each failing mutation. +5. **[P1] The new guard was not pinned as an artifact.** **Fixed** — pin 6 names + the path, both test names, both slice anchors, the normalization rule, and + the complete inventory. +6. **[P2] The final gate dropped repository mechanics.** **Fixed** — clippy and + fmt pinned to `+1.95.0`, the baseline sourced from `CLAUDE.md` rather than + restated, `git diff --cached --check` added. +7. **[P2] Two status statements were false.** **Fixed** in §0.2 and §0.3. + +**Both open questions closed by round 1, and adopted:** + +- **No companion `.tex` restates item 10's surface.** `operation_catalog.tex` + mentions individual referential preconditions but earns no touch row. +- **`core_spec.tex` carries no document-version literal** — already recorded at + `spec/CONTRACT_P13S19_PARTIAL.md` §0 item 7, verified. The version question is + **not applicable**; the Revision History edit stands. + +--- + +## §0. What was verified before drafting + +Everything in this section was measured against the working tree at `fcc3fb6`, +not reasoned from the ledger row. Two of the row's own claims did not survive. + +### 0.1 The row's headline reading is FALSE, and the contract records it as false + +The row states that `invariants.rs`' invariant-10 doc comment *"claims a +specification repair that never landed"*, quoting its aside: + +> genesis tranche G3a repairs this prose to name what the check body already +> enforced + +**That reading is not supported.** `spec/CONTRACT_GENESIS_G3A_ENTITIES.md`'s +pin 6 is titled, verbatim: + +> **Pin 6 — the invariant-10 prose reconciliation (§6.3), and it is doc-only** + +G3a uses "prose" to mean *the doc comment*, and scoped out any `.tex` change +explicitly. `git log -L 75,76:crates/epiphany-core/src/invariants.rs` attributes +the aside to `6c5e69f` — **G3a's own commit**. So "this prose" is +self-referential and the sentence is *true*: a comment announcing its own +repair. It is uselessly self-referential, and it misleads any reader who does +not have G3a pin 6 open — which is how the row came to be filed — but it is not +a false claim about another document. + +**Consequences, and they are binding on the pins below:** + +1. **No mutation may be built around restoring an alleged lie.** There is no lie + to restore. A mutation asserting one would sign for a defect that does not + exist. +2. **The row's "MUST NOT be corrected on its own" rationale does not hold as + written.** It rests on the comment being the tree's only pointer to a + falsehood. It is not. +3. **The two-sided repair survives on a different rationale**, stated in §0.2. +4. The ledger row is corrected by appending, never rewriting (house rule). + +### 0.2 The replacement rationale: both summaries are incomplete mirrors + +Neither the normative enumeration nor the doc comment is a faithful summary of +what the checker enforces, and **the two are incomplete in different places**. + +- `core_spec.tex`' item 10 (the `\item` beginning *"Every cross-cutting + structure's references resolve to extant objects"*, in Chapter 5's graph + invariant `enumerate`) names **no** individual reference class. +- The Rust doc comment (the `/// 10.` block above `CrossCuttingRefsResolve`) + names many, but not all. **Two strengths of omission, kept apart because they + carry different weight:** + + **Definite** — enforced classes the comment's own structure excludes: + + 1. `StaffInstance.instrument_override`. The comment's structural group is a + closed list — *"a staff's declared instrument, a staff's group, a staff + group's members, a part's staves, a view's active layers"* — with no + hedge, and the override is not in it. + 2. `NotatedComponent.tuplet`. Pin 1 group **D** has no counterpart in the + comment at all. + 3. Tempo segment anchor targets. Pin 1 group **F** likewise: the comment does + not mention the tempo map. + + **Hedged** — covered only by a non-exhaustive parenthetical, *"cross-cutting + structures (incl. anchor targets, annotation layers, tuplet parents, graphic + objects)"*: `SubBeam.events`, and `RepeatStructure.kind` / `.voltas`. An + `incl.` list is not wrong about these; it is simply not a summary anyone can + check a specification against, which is what pin 3 needs it to be. + + The comment's meter group and event-internal group are, by contrast, + **complete** — measured against pin 1 groups C and E respectively. + +**Therefore: neither side may be repaired from the other.** Copying the doc +comment into the specification — the obvious repair, and the one the row's +framing invites — would promote an incomplete list to normative status. Pin 1 +makes the derivation from the check bodies the sole origin for both. + +This is the defect class P13-S16 §7 names: **enumerating, or copying an +enumeration that already exists, where completeness requires deriving.** + +### 0.3 The tag multiplexes, and classification is per emitted condition + +`GraphInvariant::CrossCuttingRefsResolve` is emitted from **five sites in four +functions**, not one (enumerated exhaustively, not by a truncated search): +`check_cross_cutting_refs`, `check_tempo_maps`, `check_aleatoric_models`, and +`check_accidental_modification_compatibility`. + +The last of those says so itself, in its own comment: *"Not one of the +spec-enumerated Chapter 5 graph invariants … surfaced under an existing +`GraphInvariant` tag rather than minting a new one."* + +**Directed classification (ruling 2) — per emitted condition, not per function. +Proposed by this DRAFT; ratified only when the contract is:** + +| Emitted condition | Owner | +|---|---| +| Tempo segment `start` / `end` **anchor target existence** | **invariant 10** | +| Tempo segment shape ↔ `end_tempo` consistency | Chapter 3 (see 0.5) | +| Tempo segment start ordering; segment overlap | Chapter 3, `req:time:tempo-segment-order` | +| Aleatoric `ordering` referenced events in the **owning region** | Chapter 3 (new, pin 4) | +| Aleatoric `bounds` key events in the **owning region** | Chapter 3 (new, pin 4) | +| Accidental modification expressibility in a pitch space | Chapter 4, `req:tuning:accidental-modification-compatibility` | + +Invariant 10 stays **reference resolution**. A Chapter 5 invariant is not +expanded to absorb Chapter 3/4 rules because the implementation multiplexes them +through one tag. + +Note the aleatoric conditions are **stronger than resolution**: `in_region` +returns false both for an absent event and for one present in a *different* +region. "Extant object" cannot be stretched to cover that, which is why pin 4 +states it in Chapter 3 rather than folding it into item 10. + +The reversed-bounds condition in the same function already emits +`EventCoordinateModel` (invariant 4) and is **correctly tagged** — it is not part +of the multiplexing defect and this rung does not touch it. + +### 0.4 The multiplexing is a separate live defect. It is recorded, not fixed + +`check_invariant(score, CrossCuttingRefsResolve)` returns Chapter 3/4 failures, +and `impl Display for InvariantViolation` renders them as: + +``` +invariant 10 (CrossCuttingRefsResolve) violated: non-constant tempo segment is missing its end_tempo +``` + +A Chapter 3 tempo rule, attributed in user-visible text to a Chapter 5 graph +invariant, through a **public** filter API. + +**This rung does not repair it, and does not rewrite the specification to +legitimize it.** Pin 7 **will file** it as its own candidate, **P13-S29** (id +verified free). Until execution writes that row it is scoped here and filed +nowhere — the ledger, not this contract, is what "filed" means. Repairing it means either minting tags or re-tagging emissions; +both are behaviour changes, and this rung is documentation-and-guard. + +### 0.5 Two normative homes already exist; one does not + +Measured, so the pins neither duplicate nor invent: + +- **`req:time:tempo-segment-order` already covers ordering *and* non-overlap.** + Pin 3 cross-references it; it needs no amendment. +- **Tempo shape ↔ `end_tempo` consistency is stated only in a listing doc + comment**, not in a labelled requirement. That is a P13-S1-class gap, and it + is also exactly P13-S8's site (the `is_none_or` spelling). **Out of scope + here** — noted so a later rung does not read this contract's silence as + coverage. +- **The aleatoric owning-region rule has no normative statement at all.** + `req:time:aleatoric-anchoring-discipline` governs coordinate *kinds*, a + different subject. Pin 4 adds a new labelled requirement. + +### 0.6 Locators drift; this contract uses symbolic anchors only + +The row cites `core_spec.tex:6570`–`:6572` for item 10 and `invariants.rs:59`– +`:62` (via G3a pin 6) for the doc block. Both have moved — P13-S16 inserted +above each. **No pin, test, or mutation in this contract uses a line number as +an *operative locator* in a file it also changes** — nothing is found, sliced or +asserted by coordinate. Line numbers do appear inside **frozen verbatim +diagnostics**, notably pin 10's `requirement_labels.rs:486:5` panic transcript; +those are quoted evidence, consumed by nothing, and reproducing them exactly is +the point. The claim is about what drifts, and a coordinate nobody follows +cannot. Pin 1's Code-anchor column names functions, loops and +match arms, never lines. The `graph.rs@NNNN` / `event.rs@NN` references in the +round 2 block are *provenance for a one-time verification* against files this +rung does not touch, and are not consumed by any pin. + +### 0.7 One near-miss, recorded as method + +`instrument_override` was first measured as **absent** from `core_spec.tex` by a +grep for the LaTeX-escaped `instrument\_override`. It is present, in a +`lstlisting` where the underscore is bare. The false absence was caught before +it reached a pin. **Any `.tex` search in this rung must be run in both +spellings** — escaped for prose, bare for listings — and pin 6's normalization +rule exists for the same reason. + +--- + +## §1. Pins + +### Pin 1 — the derived reference surface is the sole origin + +The contract carries **one** table of invariant 10's normative reference +surface, derived by reading every emitted condition in the four functions of +§0.3 and keeping those classified to invariant 10. Both repaired documents are +written **from this table**; neither is written from the other. + +**Token** is the actual schema path of the checked field — verified against +`graph.rs`, `event.rs` and `tempo.rs`, never paraphrased. **Target** is drawn +from pin 1a's closed vocabulary. **Code anchor** preserves the derivation back +to control flow, symbolically. One row is one (token, target) pair. + +**A — cross-cutting structures** (`check_cross_cutting_refs`) + +| Token | Target | Code anchor | +|---|---|---| +| `Slur.start_event` | live event | `self.score.cross_cutting.slurs` loop | +| `Slur.end_event` | live event | `self.score.cross_cutting.slurs` loop | +| `Tie.start_event` | live event | `self.score.cross_cutting.ties` loop | +| `Tie.end_event` | live event | `self.score.cross_cutting.ties` loop | +| `Beam.events` | live event | `self.score.cross_cutting.beams` loop, member arm | +| `SubBeam.events` | live event | `self.score.cross_cutting.beams` loop, `sub_beams` arm | +| `Tuplet.members` | live event | `self.score.cross_cutting.tuplets` loop | +| `Tuplet.parent` | extant tuplet | `self.score.cross_cutting.tuplets` loop, `parent` arm | +| `Spanner.staves` | declared staff | `self.score.cross_cutting.spanners` loop | +| `Spanner.start` | anchor target | `self.score.cross_cutting.spanners` loop | +| `Spanner.end` | anchor target | `self.score.cross_cutting.spanners` loop | +| `Marker.anchor` | anchor target | `cc.markers` loop | +| `RepeatStructure.start` | anchor target | `cc.repeats` loop | +| `RepeatStructure.end` | anchor target | `cc.repeats` loop | +| `RepeatStructure.kind` | anchor target | `cc.repeats` loop, `kind_ok` match | +| `RepeatStructure.voltas` | anchor target | `cc.repeats` loop, `voltas` arm | +| `ChordSymbol.anchor` | anchor target | `cc.chord_symbols` loop | +| `AnalyticalAnnotation.anchor` | anchor target, extant region, live event | `cc.analytical` loop, via `annotation_anchor_ok` | +| `AnalyticalAnnotation.layer` | declared analysis layer | `cc.analytical` loop, `layer` arm | +| `Comment.anchor` | anchor target, extant region, live event | `cc.comments` loop, via `annotation_anchor_ok` | +| `GraphicGesture.objects` | stored graphic object | `cc.graphic_gestures` loop | +| `GraphicGesture.anchoring` | anchor target, declared staff, live event | `cc.graphic_gestures` loop, `anchoring` match | +| `LyricLine.events` | live event | `cc.lyrics` loop | + +**B — structural top-level references** + +| Token | Target | Code anchor | +|---|---|---| +| `Staff.instrument` | declared instrument | `score.staves` loop | +| `StaffInstance.instrument_override` | declared instrument | `staff_instances()` loop | +| `Staff.group` | declared staff group | `score.staves` loop, `group` arm | +| `StaffGroup.members` | declared staff | `score.staff_groups` loop | +| `PartDefinition.staves` | declared staff | `score.parts` loop | +| `ViewDefinition.active_layers` | declared analysis layer | `score.views` loop | + +**C — meter / time-signature references, at every level a `MeterChange` appears** + +| Token | Target | Code anchor | +|---|---|---| +| `MetricTimeModel.meters` | declared time signature | region loop, `RegionTimeModel::Metric` arm | +| `StaffBasedContent.default_metric_grid` | declared time signature | region loop, `staff_based()` arm | +| `Measure.time_signature` | declared time signature | region loop, `si.measures` arm | +| `StaffInstance.local_metric_grid` | declared time signature | region loop, `si.local_metric_grid` arm | + +**D — attachment-internal references** + +| Token | Target | Code anchor | +|---|---|---| +| `NotatedComponent.tuplet` | extant tuplet | `score.decomposition_attachments` loop | + +**E — event-internal references** + +| Token | Target | Code anchor | +|---|---|---| +| `IndeterminacyHints.alternatives` | live event | events loop, `Event::Indeterminate` arm | +| `TrajectoryEvent.start` | live pitch | events loop, `Event::Trajectory` arm | +| `TrajectoryEvent.end` | live pitch | events loop, `Event::Trajectory` arm | +| `GraphicEvent.graphics` | stored graphic object | events loop, `Event::Graphic` arm | +| `CueEvent.source` | live event | events loop, `Event::Cue` arm | + +**F — tempo map, invariant-10 conditions only** (`check_tempo_maps`) + +| Token | Target | Code anchor | +|---|---|---| +| `TempoSegment.start` | anchor target | `tm.segments` loop | +| `TempoSegment.end` | anchor target | `tm.segments` loop | + +**No total is stated anywhere in this contract or in either repaired document.** +A count restated beside the structure it counts goes stale silently — the rule +this module's own header already states for `GraphInvariant::all`. Pin 6 +compares *pairs*, never lengths. + +### Pin 1a — the target vocabulary is closed + +A target is a **comma-separated, alphabetically sorted** list of terms from +exactly this vocabulary: + +`anchor target`, `declared analysis layer`, `declared instrument`, +`declared staff`, `declared staff group`, `declared time signature`, +`extant region`, `extant tuplet`, `live event`, `live pitch`, +`stored graphic object` + +A conditional target (one whose resolution depends on the value's form) lists +every term it can require, sorted — which is why `AnalyticalAnnotation.anchor` +reads *"anchor target, extant region, live event"*. + +**The sort order is normative AND enforced.** Pin 6 asserts each raw target +already equals its own normalized form *before* comparing pairs, so an unsorted +document target fails rather than being silently repaired. (Normalizing without +that assertion would have made the ordering rule unobservable — a rule stated in +prose and erased by the comparison.) + +**Vocabulary membership is a *separate* assertion, not a consequence of that +one.** An out-of-vocabulary term sorts perfectly well, so the order check cannot +see it; pin 6 step 4 therefore splits into (a) order and (b) vocabulary, signed +by M14 and by M1d/M4e respectively. *An earlier draft of this pin said a bad +term "fails on the same assertion" — the very error round 3 caught in pin 6, +left uncorrected here.* + +### Pin 2 — invariant 10 stays reference resolution + +Item 10 describes **reference resolution and nothing else**. The three rider +classes of §0.3 are named in neither repaired document as invariant-10 content. +The specification is not rewritten around the implementation's tag reuse. + +### Pin 3 — `core_spec.tex` item 10 is repaired from pin 1 + +Chapter 5's graph-invariant `\item` for invariant 10 keeps its opening sentence +and its re-anchoring exception clause with the `Chapter~\ref{ch:semops}` +cross-reference, and gains a nested `itemize` carrying **every pin-1 row**, one +per `\item`, in exactly this form: + +```latex +\item \texttt{Slur.start\_event} --- live event. +``` + +The **token** is the first `\texttt{}` argument of the `\item`; the **target** +is the text between `---` and the terminating period. Nothing else on the line +is free: pin 1a's vocabulary is closed and pin 6 compares the pair. Group +headings A–F may be rendered as prose lead-ins **outside** the nested `itemize` +and carry no tokens. + +The item cross-references `req:time:tempo-segment-order` for the tempo +conditions that are *not* invariant 10's, so a reader is not left inferring that +ordering is unowned. + +**That cross-reference has no machine observer**, and is named here as such +rather than left to look guarded: pin 6 compares pairs, and a dropped `\ref` is +prose loss the pair set cannot see. It is **gate item 11**, checked by reading. +(Precedent: P13-S16's pin 4b recorded its `.tex` halves as gate items for the +same reason.) A `\ref` to a *deleted* label would still be caught — +`requirement_labels.rs` enforces cited→defined — so the unguarded failure mode +is narrow: silent deletion of the sentence. + +**No new label, no requirement block.** Item 10 is an `\item` in an enumeration, +not a `requirement`; this pin therefore moves no requirement count. + +**`core_spec.tex`'s Revision History gains a row** for this rung, naming pin 3's +item-10 repair and pin 4's new requirement. Touch row 1 authorizes it and **this +pin is what requires it** — a table row authorizing an edit no pin mandates is a +change nobody signed for. It has no machine observer and is **gate item 11**. + +### Pin 4 — Chapter 3 gains the aleatoric reference-locality rule + +A new labelled requirement in Chapter 3's Aleatoric Time subsection — +`req:time:aleatoric-reference-locality` — stating that an aleatoric region's +`ordering` referenced events and `bounds` key events **MUST** be events of that +same region. + +This is the "state it explicitly rather than stretch *extant object*" half of +ruling 2. It is a **normative addition**, so: + +- `requirement_labels.rs`' three counts move. They are **measured at execution, + never predicted** — the contract carries no target number. +- The `CHAPTER_AREAS` assignment must accept it under `time`. Verified: the + Aleatoric Time subsection sits inside `\chapter{Time and Duration}`, which + `CHAPTER_AREAS` maps to area `time`, and the label is `req:time:…`. + +### Pin 4a — the requirement's *wording* is observed, not just its label + +`requirement_labels.rs` watches labels, counts, grammar, chapter area and +citations. **None of that sees the sentence.** Replacing "same region" with "any +region", or dropping either referent, would satisfy every other gate in this +contract. + +**Test 3 — `aleatoric_reference_locality_states_both_referents_and_locality`**, +in pin 6's file, sharing its root helper. + +- *Slice:* `spec/core_spec.tex`, the `requirement` environment containing + `\label{req:time:aleatoric-reference-locality}` — from its `\begin{requirement}` + to the matching `\end{requirement}`. +- *Assert:* the slice names **both** referents (`ordering`, `bounds`), the + locality phrase, and the normative keyword `\MUST{}`. + +This is phrase presence, not exact comparison — **weaker than tests 1 and 2, and +stated as such** rather than presented as equivalent coverage. **Four mutations +sign it: M11 (locality), M12 and M13 (the two referents), and M16 (normative +force).** What it buys is that neither referent, nor the locality claim, nor the +requirement's normative force can silently leave — the last of those being a +weakening that reads as an editorial softening rather than a deletion. + +### Pin 5 — the doc comment is repaired from pin 1, and separated + +The `/// 10.` block is rewritten from pin 1 and carries **every pin-1 row**, one +per line, in exactly this form: + +```rust +/// - Slur.start_event — live event. +``` + +The **token** is the first whitespace-delimited word after `- `; the **target** +is the text between `—` and the terminating period. + +The block then gains an explicitly-marked separation: the normative invariant-10 +surface above, and below it a note that **additional checks are currently +reported under this tag** (§0.3's riders), naming P13-S29 as their owner. The +rider note carries **no** pin-5-shaped lines, so it cannot pollute extraction — +and if it ever did, test 2 would fail on a spurious token, which makes that +constraint self-observing. + +**The G3a aside is removed.** Ruling 1(a): it is ambiguous, not false; its +historical content is preserved in §0.1 and in the ledger append, which is where +a historical aside belongs. Removing it is not a correction of a lie and must +not be described as one. + +### Pin 6 — the guard, pinned as an artifact + +**Path:** `crates/epiphany-testkit/tests/invariant_ten_surface.rs` — testkit, +because it is the crate that already reads both the `.tex` suite and repository +sources, via the `Path::new(env!("CARGO_MANIFEST_DIR")).join("../..")` root +helper that `requirement_labels.rs` uses. + +**The inventory constant.** One +`const INVARIANT_TEN_SURFACE: &[(&str, &str)]` holding pin 1's (Token, Target) +pairs verbatim. This is the ratified origin in machine-readable form, not a +second list: the derivation source is Rust control flow, which is not parseable, +so gate 8 re-derives it by hand. Tests 1 and 2 compare against it; test 3 +does not use it. + +**Tests 1 and 2 share this shape**, and the order matters: + +0. **Validate the oracle, before using it as one.** Assert + `INVARIANT_TEN_SURFACE` has no repeated token, and that every term in every + target is drawn from pin 1a's vocabulary. A duplicate inside the constant + would otherwise vanish when the expected side becomes a `BTreeSet`, leaving + both documents and M9/M10 passing against a silently-collapsed oracle. This + step is shared by tests 1 and 2, so neither can run against an unvalidated + inventory. +1. Slice. +2. Extract to a **`Vec<(String, String)>`**, preserving order and repeats. +3. **Duplicate check:** assert the list of tokens appearing more than once is + **empty**, naming them. This is what set comparison cannot see, and it + hard-codes no total. +4. **Canonical-form check, in two independent parts** — they catch different + mutations and neither implies the other: + - **(a) Order.** Assert each raw target already equals its own normalized + form (split on `,`, trim, sort, rejoin with `, `). Pin 1a's order is + normative, so this is where it is enforced; normalizing without asserting + would erase the rule. *Signed by M14.* + - **(b) Vocabulary.** Assert every term is in pin 1a's closed vocabulary. + **An out-of-vocabulary term sorts perfectly well**, so part (a) cannot + catch it — a single combined check would have left M1d and M4e unable to + produce their required failures. *Signed by M1d and M4e.* +5. `assert_eq!` the resulting `BTreeSet<(String, String)>` against + `INVARIANT_TEN_SURFACE`. **Set equality, never `contains`, never + `is_subset`** — the `left`/`right` diagnostic *is* the observation, naming + missing pairs, spurious pairs, and any pair whose target drifted. + +**Tests 1 and 2 both extract and compare targets, not tokens alone.** M1c/M1d sign this +for the specification; M4d/M4e sign it for the doc comment. A token-only +comparison on either side would let that document's targets drift freely. + +**Test 1 — `specification_item_ten_names_exactly_the_derived_surface`** + +- *Outer anchor — and this is what makes pin 3's opening sentence machine- + observed:* the outer item-10 slice begins at the **complete opening sentence, + matched as an exact literal**, not at a short prefix of it. Pin 3 requires that + sentence to survive verbatim; using the whole of it as the anchor makes the + requirement and its observation the same string, so any deletion or edit fails + the slice rather than passing unnoticed. A prefix anchor would have left the + remainder unguarded. +- *Slice:* `spec/core_spec.tex`, item 10's **nested `itemize` environment** — + from the `\begin{itemize}` that follows that sentence to its matching + `\end{itemize}`. The outer slice bounds the search; extraction reads only + inside the environment. +- *Extract:* per `\item`, the first `\texttt{}` argument as token, the text + between `---` and the period as target. +- *Normalize:* `\_` → `_`; trim; discard empties. +- *Also assert, on the **outer** slice, as **two independent assertions**:* it + contains **no `\label`** and **no `\begin{requirement}`**. Pin 3 forbids both, + and nothing else would catch them — an accidental well-formed label is + absorbed the moment pin 4's count constants are remeasured, which pin 4 + instructs execution to do. + + **Recognition is whitespace-tolerant** (`\label` followed by optional + whitespace then `{`, and likewise `\begin`), because TeX accepts `\label {x}` + and `\begin {requirement}` and **the repository's own parser does not**: + `command_arguments` builds an exact `\{command}{` needle. A guard written to + the same exact-string shape would inherit that blind spot from the checker it + is meant to complement. Signed by M18 (label) and M20 (requirement block), + each spelled in the spaced form for that reason. + +**Test 2 — `implementation_doc_names_exactly_the_derived_surface`** + +- *Slice:* `crates/epiphany-core/src/invariants.rs`, from `/// 10. ` to the + `CrossCuttingRefsResolve,` variant line — G3a's `t12` anchors, already proven + to bound this block. +- *Extract:* per line matching pin 5's form, the token after `- ` and the target + between `—` and the period. + +**Why the slice is mandatory, stated in the tests' own comments:** unsliced, the +extractor collects every `\texttt{}` in `core_spec.tex` and every `- ` line in +`invariants.rs`, and equality fails on a flood of spurious pairs. The slice is +required for the guard to *function*. Its exactness is what M7/M8 control for, +and the strength of the comparison is what M3 controls for. + +### Pin 7 — two defects are filed by execution. Neither is fixed + +`spec/PASS13_CANDIDATES.md` gains **two rows**. Filing is this rung's whole +obligation for both; repairing either is out of scope. + +**P13-S29** — the public `check_invariant` filter and +`Display for InvariantViolation` attribute Chapter 3/4 failures to invariant 10. +Its row **will cite** §0.4's rendered example as its evidence, and **must** +record that repairing it is a behaviour change. + +**P13-S30** — **the repository's ad hoc TeX parsers assume a spelling TeX does +not require.** `\label {x}`, `\begin {requirement}` and `\chapter {X}` are all +legal and all missed. Its row **must carry** the four consequences below, each +traced rather than inferred: + +| Consequence | Site | Severity | +|---|---|---| +| **Requirement blocks are mis-scanned — and both delimiters are matched exactly, so the failure differs by which one is spaced.**

**(i) Spaced opening.** The block is never pushed into `requirements`; no per-block check sees it. **Silent when additive and carrying no `req:` label.** The counts are of different things, which is the subtlety: `CORE_REQUIREMENT_COUNT`/`SUITE_REQUIREMENT_COUNT` count **blocks**, `SUITE_LABEL_COUNT` counts **labels from a whole-text scan**. Re-spacing an *existing* block is loud on the two **block** counts while its label stays visible; adding a hidden block that carries a `req:` label is loud on the **label** counts while the block counts hold. A non-`req:` label is invisible either way.

**(ii) Exact opening, spaced closing.** The scanner finds the opener, then hunts an exact close. **Two outcomes:** *no later exact close in the document* → both parsers **panic** (`"unterminated requirement in …"`; `"every requirement block is closed"`); *a later exact close exists* → the scan **consumes through it**, swallowing the intervening text — including any following block's opener, which is then never recorded separately. Usually loud, because the consuming block inherits ≠1 label. **But there is a second silent case:** an **additive, label-free** block with exact opener and spaced closer, inserted immediately before an existing single-label block **in the same chapter**, inherits exactly that one label, replaces it one-for-one in the block tally, leaves the whole-text label scan untouched, and keeps the same chapter attribution. Every count holds | **Openers:** `load_spec`'s `let begin = r"\begin{requirement}"` (`requirement_labels.rs:166`); `split_once("\\begin{requirement}")` (`text_projection_grammar.rs:647`). **Closers, matched just as exactly:** `let end = r"\end{requirement}"` (`requirement_labels.rs:167`); `split_once("\\end{requirement}").expect(…)` (`text_projection_grammar.rs:649`). Label collection: `all_defined_labels` → `labels()` on `document.text` | **two conditional silent cases; otherwise loud, including two distinct panics** | +| A `req:` label yields a false **"cited but undefined"** diagnosis — missed on the defining side, still found on the citing side | `labels()` via `command_arguments` vs `requirement_strings` | loud, misleading | +| **Chapter association missed or shifted** — `load_spec` binds the requirement to the previous *recognized* chapter. **Four outcomes, by what that predecessor is:** (a) *no* prior recognized chapter → `load_spec` panics, "requirement before first chapter"; (b) predecessor absent from `CHAPTER_AREAS` → the area test panics, "missing chapter-area data"; (c) predecessor mapped to a **different** area → loud mismatch listing the requirement; (d) predecessor mapped to the **same** area → **silent**. Case (d) is reachable: `Graph Value Layouts` (`binary_format.tex:814`) immediately precedes `Operation Wire Forms` (`:1196`) and both map to `binfmt`, so losing the latter's heading leaves the suite green. Of that file's **twelve** chapter headings, **seven** are represented in `CHAPTER_AREAS` | `command_arguments(&text, "chapter")`; `CHAPTER_AREAS`; the area test's `unwrap_or_else` panic | **(a)–(c) loud, (d) silent** | +| **The blind spot is replicated across independently written scanners**, so a fix in one leaves the others unrepaired and the parsers may drift apart. **Divergence is a latent risk, not an observed fact** — the read proves duplication only | **The full known inventory, from an exhaustive grep of exact TeX-form literals across `crates/`, all test-scoped:** `text_projection_grammar.rs` — `\chapter{…}` splits (`:73`, `:363`), exact `\label{…}` guards (`:341`, `:508`, `:538`, `:591`), `\begin{requirement}` scans (`:596`, `:647`) **and the matching exact `\end{requirement}` at `:649`**; `binary_format_history.rs:107`–`:112` — `\chapter{Revision History}` and a `\chapter{` delimiter, whose `.expect` **panics** on a spaced heading; `epiphany-textproj/src/parse.rs:726` — inside `#[cfg(test)] mod tests` | latent | + +**Every site above is test-scope.** No production consumer of the exact-string +assumption was found during scoping; if one is later found, S30's row gains it. +Saying so is part of the filing — an unqualified "no production impact" would be +a claim this scoping did not make. + +**Two of the four consequences have silent cases, and the requirement-block one +has two of them** — spaced-opener (additive, free of `req:` labels) and +exact-opener-with-spaced-closer (additive, label-free, immediately before a +single-label block in the same chapter) — plus the chapter branch in case +(d). That +is what makes the candidate worth filing rather than folding into an existing +misleading-diagnosis row. **Neither is silent unconditionally**, and the row must +carry the conditions: a consequence recorded as "silent" without them invites a +later reader to reproduce it the loud way and conclude the candidate is wrong. +A defect that fails loudly is at worst misdiagnosed; one that passes is not seen +at all. + +**M20 will furnish evidence for consequence (i) only.** Its probe spaces **both** +delimiters, so no parser reaches the opener and none ever hunts a close — it +exhibits the **spaced-opening** blind spot and says nothing about the +exact-open/spaced-close behaviour of consequence (ii). S30's row must not cite +it as evidence for the whole consequence; (ii) is established here by code read, +and a rung that wants it exhibited owes its own probe. + +**M20's evidence, for the row it does cover**, concerns the serious case: a +spaced `\begin {requirement}` is missed by the block scanner with **nothing else +reacting**. That property is established here by code read, and this rung relies +on it to make M20 a clean discriminator — so once M20 runs, the defect is +**exhibited** by the contract's own mutation plan rather than only argued. +**The landed S30 row must cite M20's transcript**; until execution, neither the +transcript nor the row exists. + +**P13-S22 and P13-S25 do not own this**, and neither does a §5 note: a section +of a contract that becomes a historical record at landing is not durable +ownership. The ledger row will be. + +### Pin 8 — `t12` is narrowed to a local non-vacuity check, and renamed exactly + +G3a's `t12_invariant_10_doc_comment_names_the_four_reference_classes` asserted +four needles, all inside the structural class — which is how §0.2's omissions +went unseen. Pin 6 test 2 strictly supersedes it. + +`t12` is **not deleted**: `cargo test -p epiphany-core` must still fail when the +doc block is destroyed, and testkit is a different crate. It is narrowed to +assert only that the block slices cleanly and yields a **non-empty** token list +in pin 5's form. + +**The replacement name is pinned exactly:** +`t12_invariant_10_doc_block_slices_and_is_non_empty`. +M4c and the evidence both reference it by that name. + +**Renaming is permitted here and pinned explicitly**, because this contract owns +the name. (P13-S16's execution renamed a *contract-pinned* test name and had to +revert; the distinction is that the pin is doing the renaming, not the keyboard.) + +### Pin 10 — the evidence annex must not poison the citation gate + +`every_requirement_citation_is_defined` scans **every** repository text file +outside `.git` and `target`, excluding only generated extensions. It therefore +scans `spec/EVIDENCE_P13S26_EXECUTION.md`. + +M6 relabels pin 4's requirement to `req:graph:aleatoric-reference-locality`. Its +diagnostic, recorded verbatim per gate 7, puts that string in the annex — and +**after restoration the label does not exist**, so the citation test fails on +the evidence itself, permanently. + +**The same defect already has a live instance, found sweeping this finding:** +`repository_text_files` reads the **filesystem, not git**, so the untracked +draft of this contract is scanned like any other file. It names both +`req:time:aleatoric-reference-locality` (pin 4) and — since round 4 — +`req:graph:aleatoric-reference-locality` (this pin). **Neither exists.** The +citation gate has therefore been failing since the draft was created, through +three review rounds in which both parties reported a clean worktree. Measured: + +``` +thread 'every_requirement_citation_is_defined' panicked at +crates/epiphany-testkit/tests/requirement_labels.rs:486:5: +req:graph:aleatoric-reference-locality: spec/CONTRACT_P13S26_INVARIANT10_SURFACE.md +req:time:aleatoric-reference-locality: spec/CONTRACT_P13S26_INVARIANT10_SURFACE.md +``` + +**Obfuscating the labels is not available**, and the checker says so itself: +*"Without this escape the check forces prose to become vaguer than the finding +it records: it already rewrote a scoping plan's `req:layoutir:vertical-bands` +into a euphemism to make itself pass."* Naming a label that does not exist is a +legitimate thing for a scoping document to do; the allowlist is the sanctioned +mechanism. + +**The repair, in two rows with different lifetimes:** + +``` +("req:graph:aleatoric-reference-locality", + "not defined in the restored tree; P13-S26's M6 mutation defines it only while + that mutation is applied. Named by + spec/CONTRACT_P13S26_INVARIANT10_SURFACE.md and recorded in + spec/EVIDENCE_P13S26_EXECUTION.md as M6's verbatim diagnostic.") // PERMANENT + +("req:time:aleatoric-reference-locality", + "proposed by spec/CONTRACT_P13S26_INVARIANT10_SURFACE.md; the requirement does + not exist until pin 4 lands.") // TEMPORARY — pin 4 REMOVES this row +``` + +**Pin 4 must delete the second row when it creates the requirement.** Once the +label is defined, the row's own claim — *"discussed, never cited"* — is false, +and this rung does not leave a false claim in a test file to fix a different +one. The row is inert rather than harmful at that point, which is exactly why it +would be easy to forget; gate 12 checks it. + +**The scaffolding must also transition to a truthful landed form, and that is +this pin's obligation, not the keyboard's.** The rows sit under a banner marked +`P13-S26 REVIEW SCAFFOLDING (pre-execution)`. It deliberately claims nothing +about its own staging or tracking state — those change at ratification, and a +banner deleted only at execution would spend that interval false. Of what +remains, **two claims are falsified by landing and the rest merely go +obsolete** — a distinction worth keeping, because only the first kind would be a +lie in the tree: + +**Falsified by landing:** + +- `(pre-execution)` itself; +- *"neither of which exists yet"* — pin 4 creates `req:time:…`. + +**Obsolete, but still true:** *"Authorized as prerequisite review scaffolding, +NOT as dispatch of P13-S26"* is a claim about how the rows were **introduced**, +and landing cannot retroactively re-authorize the past; *"no other pin work is +licensed by it"* remains true; and the abandonment instruction becomes +**inapplicable** rather than false. Note also that only the **permanent** row +survives — pin 4 deletes the temporary one — so it is not the case that "the +rows become dispatched work". + +Deletion is right on either count: two falsified sentences and a set of spent +instructions do not belong in a landed tree, and shipping the surviving row +beneath them would leave the staged tree describing a state it is not in — the +§7 defect P13-S16 closed on. + +Execution therefore: + +1. **Deletes the entire `(pre-execution)` banner**, not merely the temporary + tuple beneath it. +2. **Retains the `req:graph:` row unchanged.** Its wording above is already the + landed form — *not defined in the restored tree* — chosen because the + shorter "never exists" is falsified by M6 for as long as that mutation is + applied. Nothing to edit here; the row is written once, correctly, and + survives the banner's deletion. +3. Leaves the row's reason naming this contract and the annex, so the tuple + remains traceable without the banner. + +Gate 12 asserts the scaffolding marker string is **absent** from the staged +file. + +Touch row 5 already covers `requirement_labels.rs` for pin 4's counts; this pin +extends *why* it is listed. Verified safe in both directions: the allowlist only +removes entries from the undefined set, and nothing asserts its entries *are* +undefined, so a row is inert while its label exists and effective while it +does not. + +**Ordering is part of this pin, not an execution detail.** The entry lands +**before** M6's transcript is written. Otherwise every full-workspace run after +that write gains a spurious `every_requirement_citation_is_defined` failure, and +the exhaustive radii of every later mutation are wrong — turning §3's +mismatch-is-a-finding rule into a generator of false findings. + +**Redaction was rejected.** Paraphrasing the diagnostic would satisfy the +checker and forfeit the verbatim evidence that gate 7 exists to produce. + +### Pin 9 — no tag change, no behaviour change, and gate 6 observes it + +`GraphInvariant` gains no variant. `all()` does not change length. No emission +site changes its tag. No check body changes. `number()` is untouched. + +**Gate 6 is this pin's observer.** Re-deriving the surface cannot detect a +retag; a staged-diff boundary check can. + +### Pin 11 — this contract's own landed form + +Touch row 7 authorizes an edit to this file's status block; **this pin is what +requires it**, on the same rule as pin 3's Revision History row — a table row +licensing a change no pin mandates is a change nobody signed for. + +**Two of these belong to landing; one belongs to ratification and is only +verified at landing.** Conflating them would have made pin 11 demand a hunk that +cannot exist. + +**Landing edits — two:** + +1. The **STATUS** block reads exactly **`STATUS: LANDED by this commit.`** + **It must not contain a hash.** A commit cannot carry its own id — inserting + it changes the tree and therefore the id. P13-S16 shows the shape: `aee4ff9` + itself still read as pre-landing wording, and both hashes in its status line + were written by later commits. If a hash is wanted here, it arrives the same + way, as its own administrative amendment; it is **not** a gate item of this + rung. +2. **All review-round blocks above §0** are marked as a dated historical + record, not current state. + +**Ratified-input invariant — not a landing edit:** + +3. The header line *"Pins are editable until ratification"* is replaced by the + frozen-pins statement **in the ratification commit**, which is where both + precedents do it — P13-S27 (`RATIFIED … DISPATCHED`) and P13-S16 already + carry it, word for word: + + > **THE PINS ARE FROZEN. They may be executed, not edited.** A defect found + > during execution is **reported, not patched in place** — if it needs a pin + > change, that is its own amendment with its own review round. + + By the time execution begins, that line **already exists**. Pin 11 therefore + requires it as an **unchanged input**, verified present and untouched at + landing. + + **Its observer is gate 13, not gate 6.** Gate 6 reads only + `crates/epiphany-core/src/invariants.rs` and never opens this file; naming it + here would have left the invariant unobserved while appearing guarded. + + **And the check is a comparison, not a claim about hunks:** + `git diff --cached -U0 -- spec/CONTRACT_P13S26_INVARIANT10_SURFACE.md` must + contain **no added and no removed line** matching the frozen statement. + *Zero context is what makes this satisfiable.* Pin 11's STATUS edit sits a + few lines above, so at default context the unchanged frozen line appears in + the diff as ordinary context — and a naive "no hunk touches this line" + reading would fail a correct execution. + +Landing edit 2 is not bookkeeping. P13-S16 shipped exactly this defect twice — a +document whose top was updated while its body went on describing the +pre-execution world — and it is the reason that contract has a §7 at all. This +contract accumulates review round after review round of *"Fixed —"* prose that +reads as present-tense work-in-progress; left unmarked, it would describe a +state the landed tree is not in. + +**No count of the rounds appears in this pin or in gate 13.** Round 6 found the +previous wording claiming five rounds while four were recorded — and observed +that repairing the numeral would stale it again the moment another round landed. +The rule is *all of them*, which cannot go stale. + +**Gate 13** checks the two landing edits as edits, and the frozen-pins line as an +invariant. + +--- + +## §2. Touch table + +The staging allowlist. A file that must change and is not listed here silently +drops out of the commit. + +| # | Path | Why | +|---|---|---| +| 1 | `spec/core_spec.tex` | pin 3 (item 10), pin 4 (new requirement), Revision History row | +| 2 | `spec/core_spec.pdf` | tracked build product of row 1 | +| 3 | `crates/epiphany-core/src/invariants.rs` | pin 5 (doc block), pin 8 (`t12` narrowed + renamed) | +| 4 | `crates/epiphany-testkit/tests/invariant_ten_surface.rs` | pin 6 (tests 1 and 2) **and pin 4a (test 3)**, new file | +| 5 | `crates/epiphany-testkit/tests/requirement_labels.rs` | pin 4 moves its three counts — the recurring escapee, listed deliberately. **Also pin 10:** retain the permanent `req:graph:` exception under its landed wording, delete the temporary `req:time:` exception, and delete the `(pre-execution)` scaffolding banner | +| 6 | `spec/PASS13_CANDIDATES.md` | S26 status append; **two new rows, P13-S29 and P13-S30** (pin 7) | +| 7 | `spec/CONTRACT_P13S26_INVARIANT10_SURFACE.md` | **pin 11's two landing edits:** the STATUS block, and marking all review-round blocks above §0 as a dated historical record. *The frozen-pins line is a ratified input, not an edit — written at ratification, and gate 13 verifies it appears as neither an addition nor a deletion in a zero-context staged diff* | +| 8 | `spec/EVIDENCE_P13S26_EXECUTION.md` | **the destination for gates 6 and 7** — every mutation transcript and boundary-gate output, verbatim | + +**Row 8 is pinned before dispatch, deliberately.** P13-S16's evidence annex was +written with no touch row, had to be left untracked at acceptance, and gained +its row only by amendment. Gates 6 and 7 require verbatim transcripts; a +contract that demands them without naming a destination cannot be executed +inside its own allowlist. + +**Rows deliberately absent, with reasons resolved in review round 1:** + +- **No other `.tex`.** No companion document restates item 10's surface. + `operation_catalog.tex` mentions individual referential preconditions and + earns no row. +- **No version-literal row. Not applicable:** `core_spec.tex` carries no + title-page version and never has (`spec/CONTRACT_P13S19_PARTIAL.md` §0 item + 7, verified). The Revision History edit is row 1. +- **No `epiphany-ops` / `epiphany-bundle` row.** Pin 9 forbids behaviour change, + and invariant 10's tag does not reach the wire. + +--- + +## §3. Mutation plan + +Every guard is verified by re-introducing the defect and **observing** the +failure. A compile error observed nothing. Restore by hand-editing, never with +git. + +**Every "Must fail" cell below is exhaustive.** Execution records the observed +set for each mutation, and **any mismatch — a test that fails and is not listed, +or a listed test that passes — is itself a finding**, reported, not absorbed. + +**Failing-evidence mutations.** Each runs against the full workspace with +`--no-fail-fast`, recording the complete failure set. + +| M | Mutation | Must fail — exhaustively | +|---|---|---| +| M1-A | Delete `RepeatStructure.voltas` from item 10's nested `itemize` | test 1 | +| M1-B | Delete `StaffInstance.instrument_override` | test 1 | +| M1-C | Delete `StaffBasedContent.default_metric_grid` | test 1 | +| M1-D | Delete `NotatedComponent.tuplet` | test 1 | +| M1-E | Delete `IndeterminacyHints.alternatives` | test 1 | +| M1-F | Delete `TempoSegment.end` | test 1 | +| M1b | Add a token to item 10 that is not in `INVARIANT_TEN_SURFACE` | test 1 | +| M1c | Change one item-10 target to a different vocabulary term (`live event` → `declared staff` on `Slur.start_event`) | test 1 — **the finding-3 control: token sets are identical, only the pair differs** | +| M1d | Change one item-10 target to a term outside pin 1a's vocabulary | test 1 | +| M2 | Restore item 10 to its pre-rung sentence in full | test 1 | +| M7 | Move one row into a **second nested `itemize`** inside item 10, after the first one closes | test 1 — **the nested-environment boundary control, and it discriminates.** The correct first-environment extractor misses the row and fails; a faulty "scan every `\item` in the outer item-10 slice" extractor still collects it and would pass. Two weaker forms were rejected: an `\item` moved *outside* the `itemize` joins the enclosing `enumerate` and becomes the next top-level invariant, leaving item 10 entirely; and the same text demoted to ordinary prose is ignored by the correct and the faulty extractor alike, so it separates nothing | +| M8 | Narrow test 1's slice to drop the final nested `\item` | test 1 | +| M9 | Duplicate one `\item` inside item 10's nested `itemize` | test 1's **duplicate assertion**, naming the repeated token | +| M4-A…F | Delete the same six tokens, one at a time, from the `/// 10.` block | test 2 | +| M4b | Add a token to the `/// 10.` block that is not in the inventory | test 2 | +| M4c | Destroy the `/// 10.` block entirely | test 2 **and** `t12_invariant_10_doc_block_slices_and_is_non_empty` — the two-crate locality pin 8 exists for | +| M4d | Change one `/// 10.` target to a different vocabulary term (`Slur.start_event — declared staff`) | test 2 — **the Rust-side twin of M1c; without it every M4 row passes on a token-only comparison** | +| M4e | Change one `/// 10.` target to a term outside pin 1a's vocabulary | test 2's canonical-form check — the closed vocabulary is intended on both sides | +| M10 | Duplicate one line in the `/// 10.` block | test 2's **duplicate assertion** | +| M11 | Replace "same region" with "any region" in pin 4's requirement | test 3 | +| M12 | Delete the `ordering` referent from pin 4's requirement | test 3 | +| M13 | Delete the `bounds` referent from pin 4's requirement | test 3 | +| M14 | Permute a **complete** existing target: write `AnalyticalAnnotation.anchor` as `live event, extant region, anchor target` | test 1's canonical-form check **part (a)** — the set is unchanged, so normalization alone would recover the expected pair and only the order assertion can catch it. *An earlier form used a target set no row has, which failed at pair equality and signed nothing about ordering* | +| M15 | Duplicate one entry inside `INVARIANT_TEN_SURFACE` | pin 6 **step 0**, uniqueness branch, in tests 1 and 2 both | +| M16 | Weaken pin 4's requirement from `\MUST{}` to `\SHOULD{}` | test 3's normative-keyword assertion | +| M17 | Put an out-of-vocabulary target on one `INVARIANT_TEN_SURFACE` entry | pin 6 **step 0**, vocabulary branch, in tests 1 and 2 both — without it that branch is asserted and never observed | +| M18 | Add **`\label {tmp:m18}`** — spaced — inside item 10's outer slice. Two pinned choices: **the spaced form**, because it is what discriminates a whitespace-tolerant assertion from an exact-string one that would inherit `command_arguments`' blind spot; and **the non-`req:` namespace**, because a `req:`-shaped literal enters the citation scan, lands in the annex via this mutation's own transcript, and poisons `every_requirement_citation_is_defined` after restoration — pin 10's hazard exactly | test 1's **no-`\label`** assertion, alone. **It is a surrogate, not a demonstration of the remeasured-count condition:** `labels()` keeps only `req:`-prefixed labels, so `tmp:m18` never reaches a count constant and M18 behaves identically before and after remeasurement. It signs the assertion; the count-blindness it guards against is argued, not exhibited | +| M20 | Add, inside item 10's outer slice, exactly:
`\begin {requirement}` / `Probe.` / `\end {requirement}`
**Spaced, additive, label-free — all three pinned. Two serve parser silence, one serves M20's own isolation, and the contract keeps them apart.** *Spaced* (silence): an exact-spelled block is found by `load_spec` and moves the block counts. *Additive* (silence): re-spacing an existing block drops `CORE_REQUIREMENT_COUNT` and `SUITE_REQUIREMENT_COUNT`. *Label-free* (**isolation, not silence**): parser silence needs only the absence of a `req:` label — a `tmp:` one is invisible to `labels()` — but **any** label here would additionally trip test 1's independent no-`\label` assertion and widen M20's radius to two assertions | test 1's **no-`\begin{requirement}`** assertion, **alone** — the independent second assertion M18 does not reach. Spaced + additive keep every other suite quiet; label-free keeps M18's assertion quiet, so test 1's two assertions stay separately signed | +| M19 | Delete one clause from item 10's opening sentence | test 1's **outer anchor** — proves the anchor is the whole sentence, not a prefix | +| M5 | Delete pin 4's requirement from `core_spec.tex` | `every_requirement_block_has_one_label`, `requirement_labels_follow_the_grammar`, `requirement_labels_are_unique_across_the_suite`, `every_requirement_citation_is_defined` — all four assert a count constant this deletion moves — **and `aleatoric_reference_locality_states_both_referents_and_locality`**, whose label anchor the deletion removes | +| M6 | Re-label pin 4's requirement as `req:graph:…` | `requirement_label_areas_match_their_chapters`, `every_requirement_citation_is_defined` (this contract cites the original `req:time:` label, and that test scans repository text), **and `aleatoric_reference_locality_states_both_referents_and_locality`**, whose label anchor the relabel removes. Run **after** pin 10's allowlist entry lands | + +**Passing-evidence mutation — exactly one, and its required outcome is success.** + +| M | Mutation | Required outcome | +|---|---|---| +| M3 | In test 1, replace `assert_eq!(actual, expected)` with `assert!(actual.is_subset(&expected))` — **that direction specifically**, since `expected.is_subset(&actual)` still fails after a deletion — then re-run **M1-B** alone | **M1-B stops failing.** Evidence is the passing guard and a green full-suite run, not an assertion diagnostic. This is the positive control proving equality is load-bearing; without it, nothing distinguishes an exact inventory from a spot-check. | + +**No mutation restores the G3a aside.** Per ruling 1(a) and §0.1, there is no +falsehood to reinstate, and a mutation asserting one would sign for a defect +that does not exist. + +--- + +## §4. Gate + +1. `cargo test --workspace` — **the baseline in `CLAUDE.md` plus this rung's net + new tests**, 0 failed, 0 ignored. The count is sourced there, never restated + here. Use `--no-fail-fast` whenever anything is failing. +2. `cargo +1.95.0 clippy --workspace --all-targets -- -D warnings` — clean. +3. `cargo +1.95.0 fmt -p epiphany-core -p epiphany-testkit --check` — clean. + **Never `--all`.** +4. Every staged path is a §2 row; every §2 row is staged or named unused. +5. `git diff --cached --check` — clean (no whitespace errors). +6. **Pin 9's boundary gate.** On the staged tree: + - `git diff --cached -U0 -- crates/epiphany-core/src/invariants.rs` — every + hunk falls **either** inside the `/// 10.` doc block **or** inside the test + module. No hunk touches a check body. + - The same diff contains **no** added or removed line matching + `InvariantViolation::new` or `fn check_` anywhere, and none matching + `GraphInvariant::` **outside the `/// 10.` doc block and the test module** + — pin 5's rider note may legitimately name the tag it is about, and a gate + that forbade it could not be satisfied. + - Both outputs are recorded verbatim in **`spec/EVIDENCE_P13S26_EXECUTION.md`** + (touch row 8), not summarized. +7. Every failing-evidence mutation of §3 observed, each with the failing + assertion quoted verbatim **and** the complete `--no-fail-fast` failure set, + **compared against the exhaustive "Must fail" cell**; any mismatch reported + as a finding. M3 recorded separately, as a passing guard and a green suite. + All of it lands in `spec/EVIDENCE_P13S26_EXECUTION.md`, which is a **tracked + deliverable of this rung**, not a scratch file. +8. Pin 1's table re-derived against the check bodies **after** all edits, and + confirmed unchanged — the derivation is the origin, so it is the thing that + must still be true at the end. `INVARIANT_TEN_SURFACE` is compared to it pair + by pair, and every Token re-checked against its declaring struct. +9. `cd spec && latexmk -xelatex -interaction=nonstopmode core_spec` — re-run + until *"There were undefined references"* clears. `core_spec.pdf` is tracked + and rebuilt. (This machine needs the `~/.config/fontconfig/fonts.conf` entry + exposing the TeX tree; without it `fontspec` fails with ~46 errors that look + like a broken source.) +10. **Pin 7's complete ledger filing, read against the staged + `spec/PASS13_CANDIDATES.md`, not inferred from row presence:** + - the P13-S26 row has §0.1's correction appended; + - **P13-S29 is present and unresolved**, cites §0.4's rendered example, + states that repairing the attribution requires a **behaviour change**, and + leaves that repair outside S26; + - **P13-S30 is present and unresolved**, names all four consequences **with + the conditions under which each is silent or loud**, and cites M20's + transcript in the annex as evidence for the **spaced-opener** case only, + explicitly **not** for the exact-open/spaced-close case, which M20's + doubly-spaced probe cannot reach; + - P13-S30 also records the scoping boundary exactly: every consumer found + during scoping is **test-scope**, no production consumer was found, and a + later production-site discovery extends the row rather than contradicting + it; + - **P13-S30 preserves the consumer inventory in full** — every site named in + the duplicated-scanner consequence, each with its file and symbolic + position, **including both delimiters of every requirement-block scanner, + opener and closer alike**, none dropped in transcription. *This gate item exists because + two sites surfaced by a scoping grep were then omitted from the row; an + inventory that loses entries between contract and ledger is the defect + this rung is about;* and + - repairing either P13-S29 or P13-S30 remains outside this rung. + + A row id alone is insufficient. A stub row, a row with the required + evidence or scoping result shortened away, or either row marked resolved is + a gate failure. +11. **Read-checked, no machine observer** — each declared as such at its pin, + never left looking guarded. **Tests 1 and 2 observe the token inventories, + item 10's opening sentence and its label-freedom, and nothing else** — test + 3 deliberately observes requirement prose and is not part of this + complement. Every other prose outcome pin 3 and pin 5 require lives here: + - item 10's `req:time:tempo-segment-order` cross-reference is present (pin 3); + - item 10's **re-anchoring exception clause** and its + `Chapter~\ref{ch:semops}` cross-reference are present (pin 3) — both are + deletable with the nested inventory left perfectly intact; + - `core_spec.tex`'s Revision History row is present (pin 3); + - the `/// 10.` block's **rider separation** is present and **names + P13-S29** as the riders' owner (pin 5) — test 2 reads only pin-shaped + token rows and cannot see its absence; + - the **G3a aside is absent** from the `/// 10.` block (pin 5). *§0.1 rules + out a mutation that restores it — there is no falsehood to reinstate — but + declining to mutate it is not the same as observing its removal, and the + aside is not pin-shaped, so test 2 would pass with it still there;* + - pin 6's tests carry the comment stating **why the slice is mandatory**; + - the staged `core_spec.tex` diff adds **exactly one** `\label` — pin 4's — + counted **whitespace-tolerantly**, so `\label {…}` counts too. *Test 1's + assertion covers item 10 only; the count-remeasure blindness round 10 + identified is general, so a label added anywhere else in the file is + equally absorbed. This is the file-wide read-check for it, and it must not + inherit `command_arguments`' exact-needle blind spot;* + - the rider classes of §0.3 appear in neither repaired document as + invariant-10 content (pin 2). +12. **Pin 10's landed form**, in the staged `requirement_labels.rs`: + - the **temporary** `req:time:aleatoric-reference-locality` row is **absent**; + - the **permanent** `req:graph:` row is **present**, under final wording — + *not defined in the restored tree*, never "never exists"; + - that row's reason still names **both provenance paths** — + `spec/CONTRACT_P13S26_INVARIANT10_SURFACE.md` and + `spec/EVIDENCE_P13S26_EXECUTION.md`. A shortened reason satisfies every + other predicate here while stranding the tuple with no route back to why + it exists, which is the whole reason it survives the banner; + - the string `REVIEW SCAFFOLDING` is **absent** from the file. + + Every item here is inert if wrong — a stale row, a stale banner and a stale + reason all pass every other check — which is why they are gated by reading + rather than left to the suite. +13. **Pin 11's landed form of this contract.** Two edits and one invariant, + checked as such: + - *edit* — STATUS reads exactly `STATUS: LANDED by this commit.`, with + **no hash**; + - *edit* — **all** review-round blocks above §0 are marked as a dated + historical record; + - *invariant* — the frozen-pins statement is **present**, and + `git diff --cached -U0 -- spec/CONTRACT_P13S26_INVARIANT10_SURFACE.md` + contains **no added and no removed line** matching it. It was written at + ratification; an execution that touches it is a finding, not a discharge. + Zero context is required — the STATUS edit is close enough that the + unchanged line would otherwise appear as diff context. + + Same inertness argument as gate 12 — nothing in the suite reads this file's + prose. + +--- + +## §5. What ratification does NOT settle + +- **Whether the tag multiplexing gets repaired.** Pin 7 files P13-S29 **at + execution**; its disposition is then its own rung. +- **The tempo shape/`end_tempo` labelling gap** (§0.5). Out of scope, and + entangled with P13-S8's open ruling. +- **Whether item 10's exception clause is itself accurate.** This rung preserves + it verbatim and does not audit the re-anchoring rules it defers to. +- **Whether `INVARIANT_TEN_SURFACE` should eventually be derived mechanically.** + It cannot be today; gate 8's hand re-derivation is the standing compensation. +- **The TeX-syntax / ad-hoc-parser mismatch, which pin 7 files as P13-S30.** + Repairing it is not this rung's work. The analysis **will live** in that + ledger row once execution writes it — deliberately not here, since a §5 + bullet becomes a historical record at landing and that is not where a live + defect should be owned. Two facts belong to *this* contract and stay: its own + guards are whitespace-tolerant and therefore unaffected, and M18's surrogate + status depends on the non-`req:` branch of the defect (a spaced `tmp:` label + reaches no other assertion, leaving test 1 the sole discriminator). diff --git a/spec/PASS13_CANDIDATES.md b/spec/PASS13_CANDIDATES.md index aa6469a..0ac915c 100644 --- a/spec/PASS13_CANDIDATES.md +++ b/spec/PASS13_CANDIDATES.md @@ -121,6 +121,6 @@ evidence in isolation. | P13-S22 | **`OperationKindTag`'s normative tag-to-byte mapping is asserted for a minority of the vocabulary; the rest is defended only incidentally, by frozen byte artifacts that cannot say what broke.** P13-S15 closed the `OperationKind` half with one table covering 0..=39. The tag half has no equivalent. **Semantic locks — a named tag bound to a literal byte — exist only for:** 24–29 (`phase3_tag_discriminants_are_golden`, `payload.rs:2728`), 34 (one assertion inside a G2b reduction test, `reduce.rs:12744`), 35–38 (`t1_g3a_kinds_and_tags_are_35_to_38_in_both_spaces`, `reduce.rs:15931`), 39 (`payload.rs:2936`); **16** (`Registered`, whose corpus row is emitted under its *variant* name at `ops/src/vectors.rs:210` and whose committed literal leads with `0x10` at `spec/vectors/decode_vectors.txt:80`, so the drift comparison binds the association); and — incidentally — 1, spelled in a comment inside a golden blob (`barrier.rs:1058`, *"len 1, DeleteEvent (tag 1)"*). **Tags 0, 2–15, 17–23, and 30–33 have no semantic lock at all.** The derived tests do not supply one: `the_tag_vocabulary_is_complete` (`payload.rs:2652`) proves **completeness, density, and round-trip** — that the payload-free discriminants are exactly `0..unknown` with no gaps and each decodes — which constrains the *shape* of the assignment, not *which tag holds which byte*, and `operation_kind_tag_vocabulary!` guarantees coverage rather than value. **What actually catches a permutation is byte-level goldens that embed the tag by accident**, and they diagnose it as a blob mismatch: three probes, three catches, none of them naming a tag (below) | this file (found 2026-07-30 while closing P13-S15, whose kind-side extension made the asymmetry visible; **the entry was rewritten the same day after its own probes falsified its first draft.** That draft claimed a permutation was invisible and named 32↔33 as the demonstration. Three permutations were then executed against the working tree at `dcb28f0` and **all three failed**, so the claim was wrong and the severity is lower than filed: 32↔33 (`SetCanvasLayoutDefaults`/`SetSpellingPrecedence`) → caught by `the_committed_corpus_matches_the_generator` (`testkit/src/vectors.rs:225`); 1↔2 (`DeleteEvent`/`ModifyEvent`) → caught by `edit_barriers_blob_bytes_are_golden` (`layout-ir/src/barrier.rs:1061`); 2↔3 (`ModifyEvent`/`RespellPitch`) → caught by the corpus again. Each mutation was restored by hand and the suite returned to 1541/0) | **RESOLVED 2026-07-31** (its own rung, no rider). **Disposition A** — a hand-written literal tag→byte table in `crates/epiphany-ops/src/payload.rs` (`tag_wire_discriminants_are_golden`) — ruled 2026-07-31. Disposition B (giving the numbered corpus rows the variant-naming property) was considered and is **deferred to a separate ledger-only commit**; it is no part of this rung. **Corrected inventory, recounted from the tree at `17c1d67`:** semantic locks — a named variant bound to a literal byte — exist for **14 of 40** (1, 16, 24–29, 34, 35–38, 39); unlocked: **26** (0, 2–15, 17–23, 30–33). **Not a live incorrectness, and — confirmed — not an undefended one either:** every tag discriminant was correct throughout, and `binary_format.tex:1548`–`:1552` carries the mapping normatively. **The gap was intent and diagnosis, not exposure** — verified at all six incidental-catch sites (`spec/CONTRACT_P13S22_TAGLOCK.md` §0.4). **The corpus's tag coverage is deliberate, not accidental** — `ops/src/vectors.rs:201`–`:204` emits one row per tag straight from the vocabulary — **but those rows lock byte→byte, not variant→byte**: each is named `tag_{discriminant}` and carries `[discriminant]`, both derived from the value alone, so `tag_32` asserts that `0x20` round-trips — never that `SetCanvasLayoutDefaults` is 32. **The signing mutation, executed:** the **coordinated** 32↔33 permutation — swapping both the discriminant literals *and* the two declaration lines inside `operation_kind_tag_vocabulary!`, so `PAYLOAD_FREE` still emits ascending discriminants and every derived artifact stays byte-identical while the variant→byte association is reversed — run against the working tree at `17c1d67`: `cargo test --workspace` → **1558 passed, 0 failed**, byte-identical to the clean baseline. Two operations exchanged wire discriminants in complete silence. Restored by hand; `git status` verified clean. **The repair, as landed:** `tag_wire_discriminants_are_golden` (`payload.rs`), a hand-typed 40-row `[(OperationKindTag, u8); 40]` literal table transcribed independently from `operation_kind_tag_vocabulary!`'s invocation rather than derived from its output, so that moving a tag now fails by *name*. **Two wording repairs to this row's earlier drafts, both now corrected:** the table supersedes **one** fragment (`phase3_tag_discriminants_are_golden`, retired) — not "the six scattered fragments" as an earlier draft of this row claimed — and deliberately duplicates three more (`payload.rs:3086`, `reduce.rs:12744`, `reduce.rs:15941`) for the locality reason `spec/CONTRACT_P13S22_TAGLOCK.md` pin 5 states; and the mutation that signs the rung is the **coordinated** permutation above, not the literal-only inverse ("delete the proposed table and show that some permutation then passes") this row's original probe-design note proposed — that literal-only form is caught by `the_committed_corpus_matches_the_generator` and proves nothing. **Its own rung, not a rider** | | P13-S23 | **No filed candidate owns "place any anchor pair on a common timeline and measure musical distance along it" — P13-S18 previously mis-cited a narrower capability as its gate.** Two disjoint deficiencies, both owned by this candidate. (1) **No ordering.** The pair is not comparable under any of `measure20_comparable_order`'s five shapes c1-c5 (`invariants.rs:2457`) at all — whether the failure is in the **referent** (distinct `Event` ids; distinct `Measure` ids outside c3's `Start`+`Zero` restriction), the **variant or selector** (`Event` against `Measure`, `Measure` against `Region`, differing `pos`/`edge`), or the **clock** (`Musical` against `WallClock`, including inside `measure20_offset_order`, `:2419`) — this is what invariant 20's A4 and B4 are made of. (2) **Ordering without a usable delta.** The pair IS comparable and still yields no musical distance: c3 supplies a vector index (an order, never a distance), and c5 compares two `WallClock`s, and `measure20_musical_delta` (`:2522`) never returns a `WallClock` delta (`:2527`) — this is what invariant 20's B5 is made of. Scoping this as merely "anchors of differing shapes" or "not directly comparable under c1-c5" would exclude B5 entirely — S5 (distinct-id `Measure` `Start`/`Zero`) is c3-comparable and S1 (`WallClock` measures, `WallClock` meter changes) is c5-comparable, and both still reach B5 — an earlier draft of this filing made exactly that narrower mistake. **Explicitly broader than P11-C5**: P11-C5 (`PASS11_WORKLIST.md:159`) is a re-anchoring proximity metric that resolves "when the graph-mutation phase tracks resolved positions", and covers narrowly the two-distinct-`Event`s case (`CONTRACT_GENESIS_G3B_MEASURE.md:223`, `effect.rs:139`-`:142`'s `PositionOutsideRegion` Reserved note); P13-S23 is the timeline itself, whatever positions get placed on it. Names its dependents: invariant 20's A4, B4 and B5, and `PositionOutsideRegion`'s Reserved status | `spec/CONTRACT_P13S18_MATRIX.md` pin 10 (filed 2026-07-31 during the same rung that corrected P13-S18's over-narrow P11-C5 citation) | **open.** No code owed by this rung. Closing it needs the deferred common-timeline/duration machinery — once a `Measure` end, a distinct-id `Measure`/`Event` referent, or an `Event` position on a wall-clock-placed region can be placed on a common timeline with a musical distance, invariant 20's A4/B4/B5 residue and `PositionOutsideRegion`'s Reserved status shrink together | | P13-S25 | **The committed decode corpus's numbered tag rows lock byte→byte, not variant→byte — one row already has the property the other thirty-nine lack.** `ops/src/vectors.rs:206`–`:209` emits one row per tag as `format!("tag_{:02}", tag.discriminant())` carrying `[discriminant]`: **both the name and the payload derive from the value alone**, so `tag_32` asserts that `0x20` round-trips and never that `SetCanvasLayoutDefaults` is 32. The `Registered` row (`:210`–`:217`) is different — its name is the hard-coded string `"registered"` while its bytes are computed from the variant, so the frozen literal at `spec/vectors/decode_vectors.txt:80` binds the association. **Disposition B of P13-S22:** give the numbered rows the same property. It **does** catch the coordinated permutation — by exactly the `Registered` mechanism, with the committed text serving as the independent statement — and it propagates the property to every implementation that reads the cross-impl corpus, which an in-crate Rust test cannot do | `spec/CONTRACT_P13S22_TAGLOCK.md` (disposition B, considered and deferred during the 2026-07-31 ruling; filed rather than left as a closing remark, per the same discipline that moved P13-S22 out of P13-S15's resolved row) | **open. Complementary to P13-S22, not a replacement for it, and not a re-litigation of it.** P13-S22 landed disposition A (`tag_wire_discriminants_are_golden`, `payload.rs:2730`), which fails **by variant name inside the crate**. B cannot supply that: its failure is still *"spec/vectors/decode_vectors.txt is stale. Regenerate: …"* (`testkit/src/vectors.rs:224`) — the misleading diagnosis P13-S22 was filed about — even though the diff text would now name variants. **What B buys is cross-implementation reach; what it costs is churn in a committed artifact other implementations pin.** Both are wanted; neither substitutes for the other. Sequencing note: run B's own signing mutation as the coordinated permutation (literals *and* declaration lines), since the literal-only form is caught today by row ordering and proves nothing | -| P13-S26 | **A doc comment in shipped code claims a specification repair that never landed, and the claim is guarded on the code side and nowhere on the specification side.** `crates/epiphany-core/src/invariants.rs:69`–`:71` enumerates invariant 10's four reference classes and states that *“genesis tranche G3a repairs this prose to name what the check body already enforced”*. **It did not.** `core_spec.tex:6570`–`:6572`, the normative enumeration item 10, still reads only *“Every cross-cutting structure's references resolve to extant objects in the graph, except where explicit re-anchoring rules permit transient dangling states during edits”* — naming neither a staff's declared instrument, a staff's group, a staff group's members, a part's staves, a view's active layers, nor any of the meter/time-signature references the Rust doc lists and the check body enforces. The repair landed in the Rust doc comment only. **The asymmetry is the defect's sharp edge:** the Rust doc block is protected by a grep-assert, `t12_invariant_10_doc_comment_names_the_four_reference_classes` (`invariants.rs:4554`, needles at `:4562`–`:4566`), so the side that is *wrong about the other* is the side that is **locked**, while the side that is actually stale is unguarded | this file (found 2026-07-31 during P13-S16 reconnaissance, while verifying that row's invariant-10 citations; no ledger entry covered it) | **open.** **Not a live incorrectness** — the check body is correct and enforces every class; only the normative prose under-describes it, and only the doc comment lies about that. **A P13-S9 instance**, and filed deliberately as one: the loud form (a dangling citation) is caught by `requirement_labels.rs`, and this quiet form — a *true-sounding claim about another document's state* — is caught by nothing. **`invariants.rs:69`–`:71` MUST NOT be “corrected” on its own.** It is currently the only artifact in the tree pointing at the `core_spec.tex` gap; softening the Rust claim in isolation would make the specification defect invisible and convert a caught defect into an uncaught one — which is P13-S9's stated failure mode verbatim. **Repair both sides in one rung**, and consider whether the LaTeX enumeration deserves the grep-assert its Rust mirror already has | +| P13-S26 | **A doc comment in shipped code claims a specification repair that never landed, and the claim is guarded on the code side and nowhere on the specification side.** `crates/epiphany-core/src/invariants.rs:69`–`:71` enumerates invariant 10's four reference classes and states that *“genesis tranche G3a repairs this prose to name what the check body already enforced”*. **It did not.** `core_spec.tex:6570`–`:6572`, the normative enumeration item 10, still reads only *“Every cross-cutting structure's references resolve to extant objects in the graph, except where explicit re-anchoring rules permit transient dangling states during edits”* — naming neither a staff's declared instrument, a staff's group, a staff group's members, a part's staves, a view's active layers, nor any of the meter/time-signature references the Rust doc lists and the check body enforces. The repair landed in the Rust doc comment only. **The asymmetry is the defect's sharp edge:** the Rust doc block is protected by a grep-assert, `t12_invariant_10_doc_comment_names_the_four_reference_classes` (`invariants.rs:4554`, needles at `:4562`–`:4566`), so the side that is *wrong about the other* is the side that is **locked**, while the side that is actually stale is unguarded | this file (found 2026-07-31 during P13-S16 reconnaissance, while verifying that row's invariant-10 citations; no ledger entry covered it) | **open.** **Not a live incorrectness** — the check body is correct and enforces every class; only the normative prose under-describes it, and only the doc comment lies about that. **A P13-S9 instance**, and filed deliberately as one: the loud form (a dangling citation) is caught by `requirement_labels.rs`, and this quiet form — a *true-sounding claim about another document's state* — is caught by nothing. **`invariants.rs:69`–`:71` MUST NOT be “corrected” on its own.** It is currently the only artifact in the tree pointing at the `core_spec.tex` gap; softening the Rust claim in isolation would make the specification defect invisible and convert a caught defect into an uncaught one — which is P13-S9's stated failure mode verbatim. **Repair both sides in one rung**, and consider whether the LaTeX enumeration deserves the grep-assert its Rust mirror already has. **CONTRACT RATIFIED 2026-08-11 — `spec/CONTRACT_P13S26_INVARIANT10_SURFACE.md`, on the authority of the repository owner, after independent whole-artifact passes the last of which returned zero findings; PINS FROZEN, executed not edited; NOT YET DISPATCHED.** Two of this row's own claims did not survive scoping and are corrected in that contract's §0.1 and §0.2: (1) the doc comment's G3a aside is **ambiguous, not false** — G3a's pin 6 is titled *doc-only* and `6c5e69f` wrote the aside, so "this prose" is self-referential; the row's "claims a specification repair that never landed" reading is **withdrawn**, and with it the "only artifact pointing at the gap" rationale. (2) The two-sided repair stands on a **different** rationale: normative enumeration and doc comment are **incomplete mirrors of each other**, incomplete in different places, so neither may be repaired from the other — the surface is derived from the check bodies instead. The rung also files **P13-S29** (the invariant-10 tag multiplexes Chapter 3/4 failures through a public API) and **P13-S30** (the repository's ad hoc TeX parsers assume exact `\label{`/`\begin{requirement}`/`\end{requirement}`/`\chapter{` spellings TeX does not require) | | P13-S27 | **The reduction-algorithm-version machinery is self-referential, so the one check that would detect a canonical-semantics change necessarily passes.** `core_spec.tex:11614`–`:11617` is normative — *"Snapshots produced under an earlier algorithm version cannot be used as canonical bases under a later one without rebuilding"* — and `:14369`–`:14372` states that replicas at differing versions *"may produce different canonical states from the same operation set."* The machinery to enforce it appears to exist: `ReductionAlgorithmVersion` (`bundle/src/ids.rs:291`) is a superblock wire field (bytes `68..72`, `superblock.rs:20`); `reduction_version_for` (`bundle.rs:989`) sets a new superblock's value; and `open` (`bundle.rs:396`–`:399`) rejects a mismatch. **But the writer sources the value from the canonical base's own self-report** (mapping the base's `reduction_algorithm_version` through `unwrap_or_default()`), **and the reader compares it only against the superblock that value seeded.** Nothing compares either against the semantics the running implementation actually implements. **The check is not vacuous** — it catches a corrupt or tampered base whose version disagrees with its superblock — but it **necessarily passes for a conformingly propagated stale base**, which is precisely the case the requirement exists to prevent. Supporting: **no constant or accessor anywhere names the implementation's current reduction semantics**, and `ids.rs:288`–`:289` states that *"the algorithm catalog itself lives in `epiphany-ops`"* while nothing of the kind exists in that crate — **a second instance of P13-S26's pattern**, a doc comment asserting a false fact about another module | `spec/CONTRACT_P13S16_PROJECTION.md` pin 0 (found 2026-07-31 while scoping P13-S16, which is a canonical reduction-semantics change and therefore the first rung to need this guarantee; filed in the same ledger edit as the row it blocks) | **UNBLOCKED 2026-08-07 — the format-epoch rung landed; dispatchable, and still blocking P13-S16.** (Was: open, BLOCKED on P13-S28.) **Scoped 2026-07-31 as `spec/CONTRACT_P13S27_REDUCTION_AUTHORITY.md` (DRAFT, not dispatchable).** Rulings taken: a typed `BundleCapabilities` required at both `Bundle::open` and `Bundle::create` and carried on the `Bundle` — no default, so every caller states the semantics it implements — and outright rejection on mismatch via a new `CanonicalBaseRequiresRebuild` error, not read-only and not an integrity anomaly. Storing the capability keeps all 57 `commit` sites unchanged; only `open` (57 sites) and `create` (32) move. **The scoping also falsified this row's first reading that the writer path was test-only:** `epiphany-textproj`'s `serialize_document` (`serialize.rs:119`) and `project.rs:936` are production paths that copy a base's `reduction_algorithm_version` verbatim into a fresh `SnapshotRef`, which `commit_versioned` then stamps into the superblock (`bundle.rs:798`) — so production mints self-consistent stale documents **without ever calling `open`**, and the capability must govern writers too. **What blocks it:** contract pin 2a. Baseline authority `0` does not preserve the corpus (`serialize.rs:327` stamps `1` and round-trips it; `vectors.rs:353`/`:363` likewise), and once P13-S16 moves the authority to `1`, a pre-S27 base that happens to carry `1` is **indistinguishable from a legitimately rebuilt one** — a raw `u32` carries no provenance. Four dispositions are recorded there; `FORMAT_MINOR` as a provenance carrier was proposed and **rejected** (the header never changes after creation, `core_spec.tex:10799`, so a legacy bundle committing a freshly validated base keeps its old minor forever; and a minor change may only append append-safe discriminants, `:12258`, not alter acceptance semantics). The surviving requirement — provenance must ride a container property **old readers cannot silently accept** and **a later commit cannot inherit unchanged** — is a format-epoch design, filed as **P13-S28**. **Scope of the claim, deliberately narrow:** this establishes that the **current implementation** has no detection mechanism. It does **not** establish that no reduction-semantics change in the project's history was ever detectable — that needs a history audit not yet done, and the stronger sentence is deliberately not written here. **What closing it requires:** an authority naming the semantics this build implements, and a rejection-or-rebuild path when a base disagrees with it. Until then any rung changing canonical reduction semantics can record its break in prose but cannot make stale bases unusable — which is why P13-S16's contract is complete, ratifiable as a plan, and **not dispatchable**. **Method note:** an earlier draft of S16's pin 0 claimed no writer path existed at all. That was false, and the way it was false is the point — the search behind it looked for `ReductionAlgorithmVersion(` constructor calls, which cannot find a path that propagates an existing value without constructing one. The instrument could not observe the thing it was used to rule out. **UNBLOCKED 2026-08-07:** the format-epoch rung landed and its pin 8 **resolves pin 2a** — reduction-version authority is meaningful only in major-1 containers, so legacy bases are refused by container epoch and never by version arithmetic. The collision pin 2a identified never has to be adjudicated: a pre-S27 base carrying `1` and a rebuilt S16 base carrying `1` are indistinguishable as numbers but can never meet, because the former exists only in a major-0 container, refused at the epoch boundary before any version is compared. The `u32` never has to carry provenance because the container does. **S27 now additionally owes three inherited items** (both interim refusals converted to validation, M8's deferred laundering demonstration, pin 3c's two suspended conformance assertions), recorded in its contract as required tests. **~~RATIFIED 2026-08-07 after review round 1~~ — RATIFICATION WITHDRAWN 2026-08-07, see round 2 below. **RATIFIED 2026-08-08 on the repository owner's authority, after NINETEEN adversarial review rounds the last of which returned zero findings. PINS FROZEN — executed, not edited; a defect found during execution is reported, not patched in place. DISPATCHED for execution, with the work to be left STAGED and the execution report subject to INDEPENDENT REVIEW before completion is accepted, covering in particular M7's three observations and its control. Not settled by ratification: M7's authority/base leg is unverifiable until this rung is implemented, and every gate, test and mutation is specified while none has been run.** Prior status through the review: NOT RATIFIED, NOT DISPATCHABLE, pins NOT frozen, **awaiting the next independent review round. Which rounds have closed and the running tally live in the contract's own history table and are deliberately NOT restated here — this clause carried a count through two rounds and went stale in both, and the round list went stale the same way**; no execution work may begin.** Round 1 was run because this contract had reached "dispatchable" with **zero** ratification rounds on record, against the standing rule that contracts go through adversarial review before dispatch. Round 1 returned **nine findings, four blocking**, all now carried in the contract. **Correcting this row's own preceding clause:** the three inherited items were *not* all "recorded as required tests" — obligation 2, M8's laundering demonstration, appeared in **neither** the contract's test section nor its mutation plan, while that section's preamble claimed all of them were tests. It is now **M7**, and ruled a **mutation** rather than a capability restoration: the format rung's text refusal is permanent, `COMPANION_VERSION` stays 0.14.0, and the text-projection corpus keeps `canonical_bases` reach 0. The other blocking three: §0.4's `commit`-site count counted a same-named method in `epiphany-editor-core`, **a crate with no `epiphany-bundle` dependency at all** — the third instrument failure recorded in that one section; three independent stale list-counts (the test-section header, gate 1, and three report items) all naming figures the document had outgrown; and `testkit/tests/requirement_labels.rs` missing from the touch table while pin 9 may move `CORE_REQUIREMENT_COUNT` 213 → 214 — **the escapee `CLAUDE.md` names by name**, which also escaped the format-epoch rung. Non-blocking: locator drift since `381c498` (`bc06706` grew `bundle.rs` by 338 lines; pin 5's own `:396`–`:399` confirmed unmoved), pin 2a's corpus evidence superseded by the 2 → 0 rebuild, `Bundle::open(` 57 → **60**, gate 6a's scope widened to `epiphany-testkit`, and a missing **commit-side positive** test now added as test 8. **REVIEW ROUND 2, 2026-08-07, against the frozen contract: six further findings, four blocking — and round 1's ratification was therefore premature.** (1) The call-site correction had been applied to §0.4 only, leaving the "Rung type" paragraph at **57** and touch row 2 claiming `bundle.rs` has **35** opens — a figure that was never `bundle.rs` alone and is stale besides — which made the required reconciliation impossible. (2) §0.4 called `project.rs:936` a **production** bundle writer; `#[cfg(test)]` starts at `:630` and every `Bundle` call in that file is below it, so the writer-path correction stands on `serialize.rs` alone. (3) **M5 was unexecutable**: `serialize_document` refuses bases at `serialize.rs:151`, so its output is base-free, and pin 5 with test 4 require base-free bundles to open at *any* authority — split into **M5a**/**M5b**. (4) **M6's second half was unexecutable**: `open` rejects a stale base, `create` rejects a base-bearing manifest (`bundle.rs:234`), and `commit` validates what it emits, so no caller can hold an open `Bundle` with a stale *inherited* base — replaced by broadening rather than narrowing, with the unreachability itself reported as the stronger result. Non-blocking: pin 3a's justification (*"production code mints a self-consistent stale document"*) is **false in-tree** — zero production paths stage a base — so it now rests on guarding the public `commit_versioned` API; and `serialize.rs:157` is dead code orphaned by the `:151` guard, recorded and explicitly **not** repaired here. **Two of round 2's findings were introduced by round 1** — ruling M7's refusal permanent is what made M5 unexecutable, and test 8 was added without re-deriving M6 against the same reachability. **Method note: an amendment is a change to the system, not a patch to a line**; a round must re-derive every mutation against every ruling the previous round made. **Round 3 is warranted before dispatch — the defect rate has not fallen (9, then 6), and "dispatchable" is a claim requiring evidence of convergence rather than a status reached by running out of findings.** **REVIEW ROUND 3, 2026-08-07, INDEPENDENT, against `b842975`: six findings, four blocking — and every blocking finding was a defect in text rounds 1 and 2 wrote.** (1) **Pin 3a still carried the rationale round 2 retracted** — §0.4 states there is no in-tree production base writer while pin 3a still said "§0.4 shows production code minting a stale document", so the contract asserted a claim and its negation; **the third occurrence of fix-one-site-leave-the-others**. Rewritten onto the footing that survives: `commit`/`commit_versioned` are **public API** and guard out-of-tree callers, not an in-tree path. (2) **M5a had no observation mechanism** — pin 3 required the capability be *stored* and nothing exposed it; `Bundle` carries 17 public accessors and none for capabilities, so no `epiphany-textproj` test could inspect it. **`Bundle::capabilities()` is now pinned** — new scope, flagged for round 4. (3) **M5b could not fail**: if the supplied capability and the base version both derive from `CURRENT_REDUCTION_ALGORITHM_VERSION` — the natural implementation, since `roundtrip.rs:367` currently hardcodes `ReductionAlgorithmVersion(0)` — both operands move together and the comparison passes for every value. **This is §0.1's own tautology reproduced inside the mutation built to detect it.** The base version must now come from a source that does not track the authority (persisted artifact or deliberate literal), with both operands' provenance reported. (4) **M6's replacement named a scenario with no test** — test 6 stops at opening, so nothing asserted that an unrelated commit *succeeds*, and an implementation rejecting every post-base commit passed tests 2/5/6/8 while the broadening had nothing to break; **test 9 added**. Cleanup: touch row 7 listed `generators.rs` as "call sites, real authority" though it has **zero** `Bundle::open`/`create` calls and its `rng.range(0, 8)` versions are precisely the arbitrary wire values pin 3b assigns to *synthetic* capabilities — split to **row 7a**; and §7's call-site attribution credited round 1 alone where rounds 1 and 2 are both load-bearing. **The pattern is now legible and it is not about counts: three separate mutations were unrunnable in three different ways — M5a could not observe, M5b could not fail, M6 had nothing to break. §7 item 4a now requires, for every mutation, the named test it breaks and the provenance of each operand.** **Defect rate across three rounds: 9, 6, 6 — not converging.** The newest text (pin 3's accessor, M5a, M5b, test 9, row 7a) has had **zero** adversarial passes and was written by the same agent whose previous two attempts round 3 falsified. **REVIEW ROUND 4, 2026-08-07, INDEPENDENT, against `53292f6`: five findings, four blocking.** It accepted pin 3's `capabilities()` accessor as **bounded** — the first new text any round has passed — and found the M5 pair defective a third time. (1) **M5b cited the wrong value**: `roundtrip.rs:367` sits in `assert_score_serialization_stable` (`:332`) and versions an **acceleration snapshot**, not a canonical base, while `assert_reduction_serialization_stable` has **no base at all** because pin 3c suspended it — so the value round 3 warned the implementer not to touch was irrelevant to the authority check. **The tautology diagnosis stands; only its evidence was wrong.** (2) **The instrument was left unchosen** — round 3 said "the rung picks one" and offered two, one of which does not exist for the nominated crate, since `craft_image_with_base` is a private `fn` inside `epiphany-bundle`'s `#[cfg(test)]` module (`:1648`). **Now chosen: commit-then-reopen through public API only** — build with `synthetic_for_fixture(0)`, commit a base carrying the literal `0`, reopen those bytes under the real constant. (3) **No test could assert the error fields**: `assert_reduction_serialization_stable` returns `()` and reopens with `.expect` (`:292`), so a mismatch panics rather than yielding a matchable `CanonicalBaseRequiresRebuild { base, current }`. **Test 10b added.** (4) **M5a violated §7 item 4a, the rule round 3 added in the same edit** — it named no test, and its natural assertion compares the constant with itself and cannot fail. **Test 10a added, asserting against a deliberate literal.** **Round 3's error is the one to carry: it grepped `ReductionAlgorithmVersion`, saw a `roundtrip.rs` hit, and attributed it without resolving the enclosing item — the same shape as §0.4's `.commit(` miscount that round 1 had already recorded as a lesson. Recording a defect is not the same as not committing it.** Both literals in tests 10a/10b are **load-bearing as literals**; §7 item 4b now requires confirming neither was tidied into the constant, a failure mode invisible to the suite. **Defect rate: 9, 6, 6, 5 — still not converging after four rounds, and every blocking finding in rounds 3 and 4 was in text written to fix the previous round.** **REVIEW ROUND 5, 2026-08-08, INDEPENDENT, against `df9e528`: four findings, two blocking — the first round in which blocking findings fell below four.** (1) **The status history was numerically stale again** — "amended three times … fifteen findings so far, eight blocking" were the round-2 figures, left standing through rounds 3 and 4 **while the tables recording those very rounds sat directly below them**. This is the **fifth** count-staleness defect in five rounds, and it was in the one block the author edited every round. Replaced with a **table**, so a round appends a row rather than requiring a number to be found and re-derived. (2) **Test 10b could not make the two-field assertion M5b requires**: §3 said only "assert it opens", and under mutation that yields a bare `Err` or a panic — **a `#[test] -> Result` that returns `Err` asserts nothing about that error's fields**, so M5b's required observation had no home in the test M5b names. Both `Result` arms are now pinned, plus a third for the wrong-error case, so the mutation run produces a *verified* observation rather than a stack trace. Smaller: **M5b's "cannot be tidied" claim was false** — keeping `synthetic_for_fixture` while passing `CURRENT_REDUCTION_ALGORITHM_VERSION` as both its argument and the base version preserves the fixture and fully restores the tautology, so the structure does not protect itself and the real protection is §7 item 4b; round 4 asserted a structural guarantee that undercut the procedural check actually doing the work, **which is the same error as reasoning that a mutation would fail instead of running it**. And §3's preamble still said the tests were "in `epiphany-bundle`" after round 4 added two that **cannot** be, since `epiphany-bundle` must not depend on `epiphany-ops` and reaching the real authority is their entire purpose — corrected, with each test's touch-table home named. **Blocking findings by round: 4, 4, 4, 4, 2 — the first movement in four rounds and the first weak evidence of convergence, set against the fact that every round since the third has found blocking defects in text written to fix its predecessor.** **REVIEW ROUND 6, 2026-08-08, INDEPENDENT, against `03c85dd`: three findings, ALL THREE BLOCKING, and all three in text round 5 wrote.** (1) **The amendment tally went stale inside the block round 5 restructured to prevent exactly that** — round 5 turned the review totals into a table and left "amended five times … rounds 1–4" as prose immediately above it. The amendment count is now **the number of rows**, with no separate figure to go stale. (2) **§3's test-home correction was itself false**: round 5 wrote "tests 1–9 in `epiphany-bundle`", but **test 7 *is* `assert_reduction_serialization_stable`**, which the same section names as `testkit/src/roundtrip.rs`. Two wrong versions of that sentence, both written while fixing it; replaced with a per-crate table (1–6/8/9 bundle, 7 and 10b testkit, 10a textproj). (3) **§7 item 4b protected one operand where test 10b has two** — replacing **both** `synthetic_for_fixture(0)` **and** the committed base's `ReductionAlgorithmVersion(0)` with the constant **keeps the synthetic call in place** and fully restores the tautology, and **test 10b's `Err` arm never executes in the unmutated run**, so its literal cannot detect it. Item 4b now enumerates all **three** fixture operands individually and requires each quoted verbatim. **All three findings are one defect in different clothes: a fix applied to the site named rather than to every site the claim covers** — the sixth count-staleness defect in six rounds and the third range-correction that did not check its own range. **The mechanism that works is structural, not vigilant: the review totals stopped going stale when they became a table, the amendment count did not because it stayed prose, and item 4b stopped being under-specified when it became a table.** **Demonstrated a seventh time inside round 6's own amendment**, where the new table's Total row was first written "6 amendments" — a free-standing count, three paragraphs after the sentence declaring no such count exists, and already wrong at seven rows; caught before commit and replaced with "one amendment per row". **Prose invites a number and a table does not; the defence must be the shape of the artifact, not the attention of the editor.** **REVIEW ROUND 7, 2026-08-08, INDEPENDENT, against `c0d896c`: three findings, all blocking, and all three the same defect — a claim living in two places and fixed in one.** (1) **§7 item 4a was unsatisfiable**: it required every mutation to name "the test it breaks", while item 1 four paragraphs above states that **M4 is observed to *compile*** (no test is possible — that is why pin 3's prohibition is a review rule) and **M7's expected outcome is *success***. A report obeying 4a literally could not be written, and the honest response would have been to invent a test. 4a is now a table of what each of the eight mutations owes, with M4 and M7 carved out explicitly. (2) **Round 6's three-literal correction reached §7 and not §3** — §3 still said "**both** literals … tidying **either**", so the contract carried the fixed and the broken version of the same claim, reopening exactly the narrow-scope ambiguity round 6 existed to close. §3 no longer states the count at all; it points at item 4b. (3) **"Rounds 3, 4 and 5 were independent"** went stale the instant round 6 closed, sitting in prose beside the table whose own column records it. Deleted. **Three rounds, one lesson: round 5 fixed the review totals and not the amendment tally beside them, round 6 fixed item 4b and not §3's copy of the same rule, round 7 found the classification sentence duplicating the table's column. The defect is duplication, and every previous remedy was vigilance — "check the other sites too" — which has now failed three rounds running. The remedy adopted here is deletion, not diligence: where a claim had two homes, one is removed and replaced with a pointer. A copy that cannot drift is one that does not exist.** **Findings by round: 9, 6, 6, 5, 4, 3, 3 — flattened rather than still falling. Blocking: 4, 4, 4, 4, 2, 3, 3 — rounds 6 and 7 were both 100% blocking and 100% in the previous round's text. Seven consecutive rounds, no clean round yet. The deduplication is the first structural remedy for this particular defect and therefore the first with a reason to work, but it is untested.** **REVIEW ROUND 8, 2026-08-08, INDEPENDENT, against `9829ae3`: two findings, both blocking — and the first round to reach into a mutation's mechanics rather than its bookkeeping.** (1) **M7 did not describe a runnable observation.** It instructed execution to *construct* a base-bearing `TextDocument`, which **bypasses `parse_document` entirely**, so the parser refusal it ordered removed was irrelevant and the demonstration was not the **import** laundering it is named for; `project_text_document` is the **export** direction (`&TextDocument -> Result`) and is not on the path at all, so "all three sides, since removing one leaves the others refusing and the document never reaches the writer" was simply false for it; and "byte-indistinguishable from one whose base was genuinely validated" named **no comparison artifact and no comparison method**, leaving the central claim a conclusion rather than an observation. Now: the input must be **text and must be parsed**; only the parser (`parse.rs:138`–`:147`) and serializer (`serialize.rs:151`) refusals are removed and restored; the comparison artifact is **test 10b's construction** with the same `FileUuid` and base bytes; and the comparison is a **field-by-field enumeration** of the `canonical_base` `SnapshotRef`, the superblock's reduction version and the header's major/epoch, **reported rather than concluded** — informative in both directions, since a field that *does* differ is a provenance signal nobody knew existed. (2) The round-7 deduplication was incomplete: the status block still carried "rounds 3 and 4 are closed" while declaring the history table the sole authority. **Finding 1 is the most substantive of any round, because every earlier one was about text agreeing with other text — this one is about whether the experiment runs at all, and it did not. M7 had been in the contract since round 1 and survived seven reviews, three of which specifically re-derived mutations, because reading it never required tracing what calls what. An observation stated in the right register can look complete for a long time; "indistinguishable" was a conclusion sitting inside the rung's own demonstration, which is the exact failure mode this rung exists to eliminate.** **Findings by round: 9, 6, 6, 5, 4, 3, 3, 2. Blocking: 4, 4, 4, 4, 2, 3, 3, 2. Eight rounds, none clean. While amending, the author caught a third instance unaided — §7 item 4a's M7 row still said "all three refusals" — which is weak evidence the deduplication rule is being applied rather than merely stated. The M7 rewrite is now the newest and least-reviewed material in the contract, and its predecessor survived seven rounds while being unrunnable.** **REVIEW ROUND 9, 2026-08-08, INDEPENDENT, against `01e76d1`: two findings, both blocking, both in M7's comparator — the text round 8 had just rewritten.** (1) **Test 10b is not the "genuinely validated" reference M7 nominated**: its write-side capability is `synthetic_for_fixture(0)` and only its *reopen* uses the real authority, so M7 would have compared one synthetic fixture against another with the validated half of the claim simply absent. **This is a collision between two of the contract's own designs, not a typo** — round 4 made 10b synthetic-on-write *deliberately* so M5b's operands would be provably independent, and that is exactly what disqualifies it here. **One artifact cannot be both independent of the real authority and committed under it.** M7 now builds its own reference in `epiphany-testkit`, committing a base under `caps` derived from the real constant so pin 3a validates it on the way in. (2) **The field enumeration could not support its conclusion**: it claimed "everything that could carry provenance" while omitting `FixedHeader.file_uuid` — **the field it required to match** — plus the superblock's `generation`, `manifest_offset`, `manifest_length` and `manifest_hash`, and the whole manifest outside `canonical_base`. Replaced with **whole-`image()` byte comparison**, any difference enumerated and classified as justified nondeterminism (normalize, stating why) or as a **provenance signal** (a finding, since the refusal may then be stronger than needed). **Finding 2 retires a technique rather than an instance: a hand-written list of "every field" is a claim about a struct's contents that is wrong the moment the struct changes, and this one was wrong the day it was written. Comparing the whole artifact cannot be incomplete — the tables-over-numbers lesson applied to the experiment instead of the prose.** Three further sites were caught by the author while amending: §7 item 6 still said "M7's three text refusals", surviving round 8's correction of that exact count in two other places; §7 item 4a's M7 row still named the superseded method; and round 8's own disposition cell stated it as current. **Findings by round: 9, 6, 6, 5, 4, 3, 3, 2, 2. Blocking: 4, 4, 4, 4, 2, 3, 3, 2, 2. Nine rounds, none clean. Rounds 8 and 9 both found defects in the immediately preceding round's rewrite of the same paragraph, so M7 has now been wrong in three distinct ways across three consecutive rounds — unrunnable, then wrong-artifact, then wrong-method. The comparator is on its third design and has never been executed.** **REVIEW ROUND 10, 2026-08-08, INDEPENDENT, against `0efd543`: one finding, blocking — the smallest round yet, and again in M7.** **The whole-image comparison had no complete construction alignment.** Round 9 named four things to align, but `serialize_document` also fixes `document_id`, `lineage_id`, `profile_declarations`, every extension's fields and preserved chunks, the envelope payloads, the **staging order** (base root → extension chunks → operation-envelope block), the manifest schema `major` and `epoch_max`, and every chunk ref, hash and offset derived from those. **So a byte difference would have had a third possible cause — "the reference was built differently" — which is neither permitted classification; the result would have been unclassifiable and the comparison meaningless. A result that cannot be classified is not an observation.** M7 is now a **round trip**: build `B` validated under the real authority, export it to text via `document_from_bundle` + the crate-private `render_text_document`, parse that text back, re-serialize as `A` with `B`'s `FileUuid`, and compare whole images. **Alignment is inherited rather than enumerated** — every input `serialize_document` reads is already `B`'s own, so no list can be incomplete and the setup-mismatch category is eliminated by construction rather than by care. It is also the realistic form of the threat: export a validated document to text, re-import it, and observe the re-imported container is indistinguishable from the original having validated only the base's number, never its provenance. **This was the third hand-enumerated "complete set" in this contract and the third wrong on the day it was written — "every field that could carry provenance" (round 8), "every field to align" (round 9), and round 9's list again. The single rule earned across rounds 5–10: where a claim requires completeness, do not enumerate, derive. Tables instead of counts, whole artifacts instead of field lists, one shared origin instead of an alignment list.** No refusal count is stated anywhere in M7 any more; three successive wordings each had a wrong one. **Findings by round: 9, 6, 6, 5, 4, 3, 3, 2, 2, 1. Blocking: 4, 4, 4, 4, 2, 3, 3, 2, 2, 1. Ten rounds, none clean, and three consecutive rounds have found one paragraph — M7 — defective in a new way each time: unrunnable, wrong artifact, wrong method, incomplete alignment. Findings are falling steadily and each of the last three has been narrower than the last, the first sustained convergence signal here. Against that: M7 has never been executed and each of its four designs looked correct when written — the open question for round 11 is whether the next defect is findable by reading at all, or whether M7 must be run against a scratch branch before further paper review can add anything.** **BOUNDED SCRATCH PROBE, 2026-08-08, authorised as an explicit narrow exception in the contract's status block and run on a discarded branch: it FALSIFIED round 10.** First, recorded as evidence in its own right: **M7 cannot be executed at all until S27 lands** — `BundleCapabilities` and `CURRENT_REDUCTION_ALGORITHM_VERSION` do not exist in the tree, being S27's own deliverables, and M7 step 1 needs a base committed under the real authority. M7 is a mutation *of this rung's implementation*, so it runs after the rung. The probe therefore tested the round-trip machinery M7 depends on, base-free — which removes **no** refusal, since both `project_text_document` and `serialize_document` gate on `canonical_base.is_some()`, leaving §1.2 untouched. **Result: the round trip is byte-preserving, but only from a fixed point, and round 10's comparison did not compare from one.** Round 10 compared `A` against a `B` built from the *input* document, which is valid only when that document is already a fixed point of `document_from_bundle ∘ serialize_document`. `minimal_document(42)` happens to be one — so the first probe **passed**, and would have been reported as success — while `minimal_document(99)` was not, and the **one-extension case diverged by 295 bytes from offset 352**. Rebuilt from the fixed point, all three cases are byte-identical (1641 / 1800 / 1894). **The non-idempotent field is `envelopes`, not extensions:** diagnosed field-by-field, `document_id`, `manifest_schema_version`, `lineage_id`, `profiles`, `canonical_base`, `blobs` and `extensions` — including every `TextChunk` payload — survive exactly, while `document_from_bundle` applies a **canonical envelope ordering** (its own test says so), so any other arrival order is not a fixed point and its operation-block bytes differ. **`project_text_document` → `parse_document` proved LOSSLESS** (`b_doc == d` in every case) — the text leg was never the problem; the defect was entirely in which artifact round 10 chose as reference. **What M7 must add, for round 11 to ratify rather than for the probe to assume: an explicit fixed-point normalisation and assertion before any byte comparison, because otherwise a mismatch is round 10's own unclassifiable "third category".** Probe hygiene: the comparison was **mutation-verified** — a different `FileUuid` for `A` produced 20 differing bytes at offsets 32–47 and 60–63, observed, then restored by hand-editing, incidentally confirming round 9's point that `FixedHeader.file_uuid` is byte-visible and round 8's enumeration had omitted it; one file touched, 142 insertions, all inside `#[cfg(test)]`; no refusal removed; no canonical base carried; diff captured, branch deleted. **The methodological result: four paper rounds refined this comparison and none found that it silently depended on an unstated precondition. One execution found it in minutes, via the case a reviewer would least likely hand-pick — a document with an extension. Had the probe stopped at the case round 10 implied, the contract would have been ratified on a comparison that fails for most documents.** **REVIEW ROUND 11, 2026-08-08, INDEPENDENT, against `39f2617` (post-probe): three findings, two blocking. It confirmed the probe contained and its fixed-point result decisive, and kept M7 BLOCKED.** (1) **M7 still lacked a distinct normalised reference.** Round 10 named one artifact where the comparison needs two: build **`B_raw`** under the real authority, then **iterate derive-and-reserialize until `B_fixed` is a byte-level fixed point**, **assert that property explicitly as a hard failure**, and **compare the imported artifact only with `B_fixed`, never with `B_raw`** — otherwise an envelope-order normalisation difference remains **indistinguishable from a provenance result**, and a comparison whose failure mode cannot be told from its success condition decides nothing. Steps 1a–1c added, including a bounded convergence loop (the probe saw one pass suffice for three documents, which is not proof that one pass always suffices) and a required report of the iteration count and whether `B_raw` was already fixed. (2) **The claim was stated more broadly than any observation supports.** M7 read as though every direct bundle is byte-identical to its re-imported form; it is not, and the probe measured 295 differing bytes proving so. The contract now scopes it: M7 proves **the text path carries no provenance marker *after normalisation***, and explicitly **not** that every direct bundle is byte-identical before it — the pre-normalisation differences are `document_from_bundle`'s canonical envelope ordering and have nothing to do with provenance. **Both sentences must appear in the rung's report.** **This finding has consequences beyond M7: its conclusion is the sole evidence for a permanent capability loss — the text refusal that moved `COMPANION_VERSION` to 0.14.0 and took the corpus's `canonical_bases` from 2 to 0 — so justifying a permanent refusal from a claim broader than the result obtained is the same error as concluding instead of observing, one level up: not a false observation, but a true one asked to carry more than it can.** (3) Clarification rather than defect: **the probe cannot pre-verify M7's authority/base leg**, which needs `BundleCapabilities`, `capabilities()` and pin 3a's validation — S27's own deliverables — so it remains an **execution requirement after S27 implementation**, with the probe standing as evidence for the prerequisite and explicitly **not** as a demonstration of laundering, since it carried no base. Recorded as a standing prerequisite table: the round-trip leg is settled, the authority leg is not pre-verifiable by any review or probe. **Findings by round: 9, 6, 6, 5, 4, 3, 3, 2, 2, 1, 3. Blocking: 4, 4, 4, 4, 2, 3, 3, 2, 2, 1, 2. Eleven rounds, none clean. Round 11 broke the falling trend, and did so because the probe supplied evidence that made a previously invisible defect findable — a reason to expect the next round to find more rather than less. The M7 comparator is on its fifth design: four were falsified by reading, the fifth by execution and then rebuilt on that evidence. It is the first with a measured result behind it and the first whose precondition is asserted rather than assumed, and it still cannot be executed end to end until S27 is implemented.** **REVIEW ROUND 12, 2026-08-08, INDEPENDENT, against `74dc994`: two findings, both blocking, and both the same defect — a requirement stated without the decision it requires, leaving execution to make a design choice silently.** (1) **The convergence loop was not actually bounded**: it demanded a bound and named no limit, so execution would have chosen when non-convergence becomes failure, changing what the experiment means. **Now pinned at one normalising step** — with `B₀ = B_raw` and `B₍ₙ₊₁₎ = serialize_document(document_from_bundle(Bₙ), uuid)`, compute at most `B₁` and `B₂`, permitted maximum `n = 1`, with a three-row outcome table (`B₁ == B₀` → already fixed; `B₁ != B₀` and `B₂ == B₁` → `n = 1`, the expected case; **`B₂ != B₁` → HARD FAILURE**, reporting all three image lengths and the first differing offset). **The bound is one step because it is a property, not a tolerance:** `document_from_bundle` canonicalises, so `serialize_document ∘ document_from_bundle` must reach its canonical form in a single application, and if it does not there is **no canonical form**, no principled reference artifact, and **M7 is invalid as a whole** — a finding about the projection rather than a signal to iterate further. A loop that runs until it happens to settle tests nothing; it reports how long it took. Raising the bound needs its own amendment and review round. (2) **M7's location was unchosen**: "in a crate that can reach the real constant" is true of two crates and decisive for neither, and `render_text_document` is **`pub(crate)` to `epiphany-textproj`** (`project.rs:595`), so `epiphany-testkit` could host M7 only via **an unpinned visibility change to another crate's public API**. **The harness is now pinned to `epiphany-textproj`**, which alone has both the renderer and (via its `epiphany-ops` dependency) the real constant — under **existing touch row 9**, no new row. **`render_text_document` stays `pub(crate)`:** handoff §1.3 records it as *the one intentional hole* in the text refusal, existing solely so a negative vector can carry the spelling it asserts is refused, and widening it to host a mutation that gets reverted would leave a permanently widened public surface behind — which is how a temporary harness becomes an API change nobody ratified. **Findings by round: 9, 6, 6, 5, 4, 3, 3, 2, 2, 1, 3, 2. Blocking: 4, 4, 4, 4, 2, 3, 3, 2, 2, 1, 2, 2. Twelve rounds, none clean. The last two rounds found the same kind of defect — a requirement that reads as a decision but is not one — so the next scan should hunt remaining instructions that name a constraint without naming its value. Everything M7 now specifies is pinned to a number, a crate or a named artifact, which is a checkable property a round can test directly.** **REVIEW ROUND 13, 2026-08-08, INDEPENDENT, against `bff9c9a`: one finding, blocking — and it inverted M7's result.** **M7 claimed the capability check "does not fire".** Pin 3a requires `commit`/`commit_versioned` to validate a **newly emitted** canonical base, which is exactly what **both** `B_raw` and the parsed `A` commit — so **the check fires on both paths and ACCEPTS**, because the raw version equals the real authority. **That acceptance is the laundering result:** the base is not slipped past an absent check, it is admitted by a check working correctly that cannot tell a coincidence from a rebuild. **As written, M7 was satisfiable by deleting pin 3a's writer check entirely** — yielding a passing M7 that demonstrated the exact opposite of its purpose. M7 now requires **three observations** (`A.image()` equals `B_fixed.image()`; pin 3a's validation ran and accepted on both commits; and the control) plus a **required control**: in the same run, same harness, repeat the import with a base version deliberately **not** equal to the real authority and observe the commit **REJECTED** with `CanonicalBaseRequiresRebuild`. **M7's removals are now explicitly limited to the text refusals — pin 3a is not among them and may not be weakened, being the thing under observation rather than an obstacle to it.** **This is a new failure shape worth naming: an observation satisfiable by the absence of the thing it observes.** M7's earlier defects were about being unrunnable or comparing the wrong artifacts; this one would have run, passed and reported success on a tree with the writer check removed. **"The check does not fire" cannot distinguish a check that accepts from a check that is not there, and only one of those is the finding.** **Findings by round: 9, 6, 6, 5, 4, 3, 3, 2, 2, 1, 3, 2, 1. Blocking: 4, 4, 4, 4, 2, 3, 3, 2, 2, 1, 2, 2, 1. Thirteen rounds, none clean. Round 13 is the narrowest since the probe, but it found a defect of a kind no earlier round had looked for — not "can this run?" or "does this compare the right things?" but "could this pass for the wrong reason?" — and that question has NOT been asked of M1–M6, M5a or M5b. Every mutation in §4 deserves the same check: what else, besides the intended defect, would make it pass?** **REVIEW ROUND 14, 2026-08-08, INDEPENDENT, against `f579172`: one finding, blocking — a contradiction round 13 created.** **The comparison method still said equal images "complete the observation and require nothing further"** — written in round 9, when byte equality *was* the whole of M7, and not swept when round 13 added the writer-check control. **The contract therefore simultaneously required the control and licensed omitting it, with the permissive sentence sitting earlier and reading as the summary.** Equality is now **necessary but not sufficient**: observation 1 of three, with the control still required, and that paragraph now specifies *how to compare*, never *what suffices*. **A second instance was found while amending, and round 14 reported none:** the "informative in both directions" note read *"if **every field matches**, the refusal is justified"* — the same sufficiency claim in different words, still carrying round 8's *"every field"* vocabulary that round 9 had replaced with whole-image comparison. **A search for "nothing further" or "sufficient" cannot reach a sentence that says "matches"** — the defect `CLAUDE.md` names, *searching one spelling and concluding about all sites*, met inside the fix for a sweep failure; neither the reviewer's search nor the author's first search found it, and a third pass on different terms did. **The round-13 lesson generalises further than round 13 stated: it is not only that a requirement must be swept to every site, but that the permissive statement usually reads *earlier* than the restrictive one, because requirements accumulate downward as a document is amended. A reader following the document in order stops at the first sentence that says "done". Where a later round narrows what suffices, the earlier summary is the site most likely to contradict it and least likely to be searched.** **Findings by round: 9, 6, 6, 5, 4, 3, 3, 2, 2, 1, 3, 2, 1, 1. Blocking: 4, 4, 4, 4, 2, 3, 3, 2, 2, 1, 2, 2, 1, 1. Fourteen rounds, none clean — but the last two are single-finding rounds and round 14's was created by round 13 rather than pre-existing, the narrowest the defect stream has been. Against that, round 13's question — what else, besides the intended defect, would make this pass? — has still not been asked of M1–M6, M5a or M5b, and round 14 did not ask it either. That scan remains outstanding and is the largest known unexamined surface.** **REVIEW ROUND 15, 2026-08-08, INDEPENDENT, against `fa483cf`: one finding, blocking — and it ran the scan rounds 13 and 14 left outstanding.** **M6 accepted "test 5 fails" and "test 9 fails" as its observations.** A test fails for **every** reason, not only the one under test, so an unrelated writer rejection satisfies both exactly as well as the intended cause — M6 could have reported success while demonstrating nothing about pin 3a's scope. Both halves now require the **mutated outcome itself**: after removing pin 3a, test 5's stale commit must be observed to **SUCCEED** and the bundle to reopen at the new generation with the stale base present; after broadening pin 3a, test 9's otherwise-unchanged commit — one that does not touch `canonical_base` — must be observed **rejected specifically by the broadened writer rule**, named in the report, not merely erroring. **The scan is now complete: M1–M5b survive it, M6 did not.** That the one remaining instance was in M6 — the mutation twice rewritten for unexecutability — is worth noting: **a mutation can be made runnable and still not be evidential.** **The principle, stated once so it need not be rediscovered: the evidence a mutation owes is the behaviour it changed, not the assertion it broke. A broken assertion is a symptom with many possible causes; the changed behaviour has one. Every mutation in §4 now names an outcome, not a failure.** **Findings by round: 9, 6, 6, 5, 4, 3, 3, 2, 2, 1, 3, 2, 1, 1, 1. Blocking: 4, 4, 4, 4, 2, 3, 3, 2, 2, 1, 2, 2, 1, 1, 1. Fifteen rounds, none returning zero — but what changed in the last three is the character of the findings, not only the count: round 13 found a defect of a kind never looked for, round 14 found a contradiction round 13 created, and round 15 found the last instance of round 13's kind with the scan reported complete across every mutation. The known unexamined surfaces are now enumerable, which they were not before: §4 is scanned and clean, and M7's authority/base leg remains unverifiable until S27 is implemented by construction, carried as an execution requirement rather than a gap in the document.** **ROUNDS 16–19, 2026-08-08, recorded together.** **Round 16 (independent, 4 findings, all blocking):** round 15 stated a rule covering every mutation and applied it only to M6 — **M1, M2, M3 and M5a still took a broken assertion as evidence**, and each now requires the mutated behaviour itself: the stale base observed *opening*; the corrupt fixture observed returning `CanonicalBaseRequiresRebuild`; the base-free fixture observed rejected by the wrongly widened check, with `base` named as the superblock's no-base default and **that synthetic source prohibited from shipped validation**; and `serialize_document`'s stored capability observed equal to the changed authority. Round 15's completeness claim is marked **FALSIFIED IN ROUND 16** at its original site. **Round 17 (authored-side sweep of §3 and §5, 10 findings, 5 blocking):** round 13's *could-this-pass-for-the-wrong-reason* question had never been asked of the tests or the gates, and both yielded immediately. **Gate 6's derive alternative could never match** — `grep` is line-oriented, so `[[:space:]]*` cannot cross the newline rustfmt puts between `#[derive(…, Default)]` and `pub struct BundleCapabilities`; the likelier violation returned 0 matches and the gate **passed**, while being the sole mechanical guard on the pin-3 prohibition M4 exists for because no test can catch it. **Gate 6a was vacuous under a rename** — pin 3b offered `synthetic_for_fixture` as an example, and the name is now pinned. **Gates 2 and 3 named no toolchain** in a repo whose CI records 1.95/1.97 lint divergence and whose default is 1.97.1; both are now `cargo +1.95.0`. **Gate 4's "staged list exactly §2" was unsatisfiable** with a conditional touch row, now subset-both-ways. **Tests 1, 6, 7, 8 and 9 could all pass on a base-free bundle**, since pin 5 makes base-free the permissive case and base-bearing fixtures are the awkward ones to build — test 1 degenerated into test 4. Also: tests 1/6 given distinct construction routes, test 4's caps asserted unequal, gate 1 requiring **0 ignored**, gate 7 given a method, gate 5 quoting all three dependency tables. **The unifying defect: a gate proving absence is only as strong as the string it searches for — a regex that cannot match, a name that was an example, a clause with no method, all reporting success while checking nothing. The remedy throughout is §4's: require an artifact quoted and read, not a pattern matched.** **Round 18 (independent, 2 findings, both blocking, both created by round 17):** the base-presence rule **demanded the opposite of what test 8 is for** — it grouped tests 8 and 9 as "the ones that commit", but test 8 *introduces* the base and must start `is_none()`, so the rule was either unsatisfiable or satisfiable by a fixture that made the test assert nothing; and **test 6's construction was self-contradictory**, assigned the commit path while required to arrive as its hand-built ancestor did (`bundle.rs:1866` calls `craft_image_with_base` at `:1869`). Fixed by a per-test state table and by **swapping the routes**, which makes the attribution true rather than deleting it. **Round 19 (independent): ZERO FINDINGS — the first clean round in nineteen**, confirming the per-test table, the route swap and the revised gate mechanics. **Running total: 65 findings, 47 blocking, across 19 rounds. A clean round is the criterion named at round 11 and the first evidence of convergence this contract has produced; it is not proof of correctness, and no round has re-derived the whole document. Still open after any ratification: M7's authority/base leg is unverifiable until S27 is implemented, those being S27's own deliverables, and every gate, test and mutation is specified but none has been run.** **RESOLVED — IMPLEMENTED 2026-08-09 (pin 10).** The authority now exists: `epiphany_ops::CURRENT_REDUCTION_ALGORITHM_VERSION` (a plain `u32`, baseline **0**, with its bump discipline beside it and the standing note that **no mechanism can detect a semantics change** — the discipline is the guarantee), wrapped at the composition boundary into a required `BundleCapabilities` that **has no `Default`** and is carried on the `Bundle` so all `commit` sites stay unchanged. Both boundaries validate: `Bundle::open` refuses a base disagreeing with the session's authority (pin 5), and `commit`/`commit_versioned` refuse a **newly emitted or replaced** base that does (pin 3a) — a scope that turns out to be **forced rather than chosen**, since no caller can hold an open `Bundle` whose *inherited* base is stale. Mismatch is `BundleError::CanonicalBaseRequiresRebuild { base, current }`: not read-only, not an integrity anomaly, and kept distinct from the malformed-document failure a base disagreeing with its own superblock produces. The format rung's temporary `ReductionAuthorityUnavailable` is **deleted**; its three negative assertions were re-pointed rather than dropped. **Both rulings taken as scoped:** the baseline stays `0` (starting anywhere else would manufacture the breakage the rung exists to detect), and the writer-path correction of §0.4 holds — though execution found §0.4 **overstated** it: `serialize_document` is a production writer, but `project.rs` is entirely `#[cfg(test)]`, and **no in-tree production path stages a base at all** now that the format rung's pin 3b closed the only one, so pin 3a guards the **public API** against out-of-tree callers rather than an internal path. **Pin 2a's disposition is the container epoch**, settled from outside by the format rung. **Signature change: `open` 60 sites, `create` 32**, reconciling exactly to §0.4's corrected table; `epiphany-bundle` sites take `synthetic_for_fixture`, `epiphany-testkit` and `epiphany-textproj` a named `production_caps()` wrapping the real constant. **`ids.rs`'s claim that "the algorithm catalog lives in `epiphany-ops`" is now true** — pin 8 is the rung that earned the sentence. **Inherited obligations all discharged:** both interim refusals converted to validation (not one); pin 3c's two suspended conformance assertions restored in `assert_reduction_serialization_stable` with the **suspension marker deleted**; and M8's laundering demonstration **performed for the first time** — see below. **Normative:** `core_spec.tex` gains `req:format:reduction-authority` (213 → **214** requirements; suite 284 → **285**, so touch row 12 was used and needed **three** constants, not one) plus a Revision History row, and `core_spec.pdf` is rebuilt. **P13-S16 becomes dispatchable when this lands** — pin 10 as amended in review round 12, its "ratified and tested within this rung" clause having been unsatisfiable by the only route pin 2a permitted. **ACCEPTED AND LANDED 2026-08-09 at `4df8e25`**, after eight independent reviews of the staged execution (95 findings, 70 blocking, across 19 pre-dispatch rounds and 8 post-execution reviews; the last returned zero). Gates re-run cold against the landed tree: **1577 passed / 0 failed / 0 ignored across 42 suites**, clippy and fmt clean on pinned 1.95.0. **Correction to pin 10's own wording:** it said S16 becomes *dispatchable*, but by this repo's definition — ratified and frozen, therefore ready to execute — S16 is **UNBLOCKED, not dispatchable**; its contract is still a DRAFT awaiting ratification. That is the same *unblocked*/*dispatchable* conflation this contract's own round 1 committed and had to disambiguate | | P13-S28 | **No container property distinguishes a document produced under a validated reduction authority from one produced before any authority existed — and the two candidates that look like they would, cannot.** P13-S27 installs an authority and validates it at read and write time, but cannot state what to do with a canonical base that predates the authority: a raw `ReductionAlgorithmVersion` is a bare `u32` (`bundle/src/ids.rs:291`) carrying no provenance, and the text-projection parser accepts an unbounded one from a document (`textproj/src/parse.rs:591`), so no numeric convention — including a deliberately high epoch — is safe from a hand-authored or third-party document declaring it. **`FORMAT_MINOR` does not work either, for two independent reasons:** the header *"never changes after the file is created"* (`core_spec.tex:10799`–`:10800`) and `commit_versioned` publishes only a superblock (`bundle.rs:791`), so a legacy bundle that commits a base S27 just validated keeps its old minor **permanently** — rejecting minor-≤1 bases would then reject a base the authority itself accepted, and accepting them leaves S16's `1` ambiguous; and `core_spec.tex:12258`–`:12262` limits a minor change to appending append-safe discriminants and calls it backward-compatible, whereas making a previously-valid base newly rejectable is a **semantic acceptance change**, with current readers ignoring minor entirely (`header.rs:119` gates on major alone) so the boundary would bind only readers that already comply. **The requirement that survives:** provenance MUST ride a container property that **old readers cannot silently accept** and that **a later commit cannot inherit unchanged** | `spec/CONTRACT_P13S27_REDUCTION_AUTHORITY.md` pin 2a (filed 2026-07-31; the disposition S27 cannot make from inside itself) | **IMPLEMENTED 2026-08-07 (`bc06706`, fix `be244df`). Was the critical path; both P13-S27 and P13-S16 were blocked on it.** **This rung must own all five, and none may be deferred into S27:** (1) an **old-reader rejection boundary** — pre-boundary readers must fail closed rather than silently open a document whose safety check they do not run; (2) **provenance that survives commits correctly**, i.e. is not inherited unchanged by a later generation and is not lost by one; (3) **legacy-base rebuild/repack behaviour**, stated for real artifacts rather than assumed away; (4) **every writer path, including text projection** — `serialize_document`, `project.rs`, and the committed `.txt` vectors, since a text document can declare any version; (5) **the exact format-version and compatibility consequences**, most plausibly a **major**-version boundary or a generation-scoped attestation paired with an incompatibility boundary. **Not a sub-pin of S27 and must not drift into it** — S27's pin 2a carries an explicit prohibition against being amended into a disposition without its own ratification round. **Scoped and RATIFIED 2026-07-31 as `spec/CONTRACT_FORMAT_EPOCH_MAJOR1.md`** after four adversarial review rounds — 11 pins, 11 tests, 11 mutations, 15 touch rows, 7 gate items. **This row is now a dependency record only; the work lives there and P13-S28 does not execute as a Pass 13 rung.** Rulings taken: the carrier is the **format major** (`FORMAT_MAJOR` 0 → 1, `FORMAT_MINOR` 1 → 0), decoded three ways through a named `FormatEpoch` rather than a bool, with **no** generation-scoped attestation in this epoch; legacy resolves to **hard rejection, not read-only**; and an eight-row epoch matrix in which a major-0 bundle with no base may open, one carrying a base is rejected, and one attempting to *add* a base is rejected and told to repack — **the non-inheritance rule that `FORMAT_MINOR` could not express**. All five things this row required the rung to own are pinned: old-reader boundary (pin 2), commit-surviving provenance (pin 3), legacy repack (pins 4, 5), every writer path including text projection (pins 3b, 6), and the exact format/compatibility consequences (pins 1, 7). **Three findings from the review rounds that changed the rung's shape**, none of them visible at filing: (1) **it cannot stamp major 1 before S27's writer enforcement exists**, so pin 3a temporarily refuses *both* boundaries — opening a major-1 bundle already carrying a base, and committing one into it — through a third, temporary `ReductionAuthorityUnavailable` error that must name P13-S27 and must **not** name repack; (2) **text projection launders provenance straight through the boundary** (`serialize_document` stages a carried base into a fresh bundle and `build_manifest` writes it), resolved as **symmetric document-level refusal** — projection, parsing and a new dedicated `SerializeError` variant, none of which existed to be "retained" — which forces `COMPANION_VERSION` 0.13.0 → **0.14.0** and rebuilds the committed corpus to **20 vectors, ten rejection classes, `canonical_bases` reach 2 → 0**, a real and stated capability loss; (3) **corruption precedence binds in both epochs** — a corrupt major-1 base must still fail as malformed, never as the *temporary* authority error a user would reasonably retry. **IMPLEMENTED 2026-08-07** — amended once before dispatch (pin 3c, touch rows 10/11, gate 8) after reconnaissance found pin 3a's refusals reaching a conformance criterion through a file the touch table did not carry. All 11 tests landed under their contract names, all 11 mutations run and observed, workspace green at 1569. **P13-S27 is unblocked and P13-S16 remains blocked on S27** — pin 8 resolved S27's open pin 2a (legacy bases are refused by container epoch, never by version arithmetic), and S27 additionally inherits three obligations recorded in its own contract: converting **both** interim refusals to validation, M8's deferred laundering demonstration, and pin 3c's two suspended conformance assertions. **Two touch-table gaps found during execution, both of the same shape** — a `.tex` requirement addition moves hardcoded counts in `testkit/tests/requirement_labels.rs`, and a companion-version bump moves a second normative version literal spelled `version~0.13.0` rather than `(0 13 0)`; neither file was in any touch table, and the second was caught only because `requirements_name_only_this_companion_version` exists. **A third gap was caught in review, after the rung was committed:** pin 3b's projection refusal had been implemented only on the **bundle** side (`document_from_bundle`), leaving the public `project_text_document` free to emit a `(canonical-base ...)` line for a directly constructed `TextDocument` — text the parser then rejects. A projector that can produce what the parser refuses is exactly the asymmetry pin 3b exists to close, and the refusal is unreachable through a `Bundle` during the interval anyway, so the *only* reachable half was the unguarded one. The public projector now returns `Result` and refuses; a crate-private `render_text_document` retains the base spelling for the one legitimate caller, the `canonical_base_present` negative vector. **The lesson is the rung's own recurring one:** a guard placed on the path that happened to be named, rather than on every path a caller can reach |