diff --git a/spec/CONTRACT_P13S27_REDUCTION_AUTHORITY.md b/spec/CONTRACT_P13S27_REDUCTION_AUTHORITY.md index 146d789..e3f4505 100644 --- a/spec/CONTRACT_P13S27_REDUCTION_AUTHORITY.md +++ b/spec/CONTRACT_P13S27_REDUCTION_AUTHORITY.md @@ -1,11 +1,37 @@ # Contract — P13-S27: the reduction version gets an outside witness -**Status:** DRAFT — **UNBLOCKED and dispatchable as of 2026-08-07**; **AMENDED -2026-08-07 (pin 10)**, before dispatch and before ratification. (Was: BLOCKED on -the format-epoch rung, `spec/CONTRACT_FORMAT_EPOCH_MAJOR1.md`, which at the time -was ratified and in implementation but had not yet landed.) **That rung landed at -`bc06706`**, with its pin-3b follow-up at `be244df`. Pins 1 and 3–10 are settled, -internally consistent, and ratifiable as a plan. +**Status:** **RATIFIED 2026-08-07** after **review round 1**, which returned nine +findings — four of them blocking — all now carried in the document; **AMENDED +2026-08-07** twice, both before dispatch: **pin 10** (the unsatisfiable escape +clause) and **round 1's nine**. **Not yet implemented. The pins are now frozen — +they may be executed, not edited.** A defect found during execution is +**reported, not patched in place**. + +(Was: DRAFT, BLOCKED on the format-epoch rung, +`spec/CONTRACT_FORMAT_EPOCH_MAJOR1.md`, which at the time was ratified and in +implementation but had not yet landed. **That rung landed at `bc06706`**, with +its pin-3b follow-up at `be244df`.) + +**Review round 1 — 2026-08-07, at `96b40b2`.** Run because this contract had +reached "dispatchable" with **zero** ratification rounds on record, against a +standing rule that contracts go through adversarial review *before* dispatch — +the format-epoch rung had four, and its fourth is what produced pin 3c. What +round 1 returned: + +| # | Finding | Disposition | +|---|---|---| +| 1 | Inherited obligation 2 was in **neither** §3 nor §4, though §3's preamble claimed all obligations were stated as tests | **M7** added; ruled a mutation, not a capability restoration | +| 2 | §0.4's *"`commit` has 57 sites (including 2 in `epiphany-editor-core`)"* — that crate has no `epiphany-bundle` dependency and the word `Bundle` appears in its `lib.rs` **zero** times | Corrected; recorded as the **third** instrument failure in §0.4 | +| 3 | §3's header, gate 1 and §7 items 1/2/4 each carried a stale count of the same lists | All replaced with "every item in §N"; gate 1 now reports three buckets | +| 4 | `requirement_labels.rs` absent from §2 while pin 9 may move `CORE_REQUIREMENT_COUNT` | Pin 9 must decide explicitly; **touch row 12** added, conditional | +| 5 | Locators verified at `381c498`; `bc06706` grew `bundle.rs` by 338 lines | Correction table in §0; pin 5's own `:396`–`:399` confirmed **unmoved** | +| 6 | Pin 2a cites `vectors.rs:353`/`:363`; the corpus was rebuilt to `canonical_bases` 2 → 0 | Evidence updated; disposition unchanged | +| 7 | `Bundle::open(` is **60**, not 57 | §0.4 table corrected; `create` confirmed still 32 | +| 8 | Gate 6a checked `textproj` only, while touch row 7 gives `testkit` the real authority | Scope widened to both | +| 9 | No **commit-side positive** test, though obligation 1 warns that converting one branch leaves a hole | **Test 8** added | + +**Pins 1 and 3–10 are settled and internally consistent.** Pin 2a is resolved +from outside (below); pin 2 is unchanged. **The pin 10 amendment**, recorded here so it is not read as the original: pin 10 made P13-S16's opening conditional on pin 2a being *"ratified and tested **within @@ -56,6 +82,30 @@ format-epoch rung → **P13-S27** → **P13-S16**, with no open question left in Read out of the working tree at `381c498`. Every line number confirmed by reading the line. +> **Locators re-verified 2026-08-07 in review round 1, at `96b40b2`.** The +> format-epoch rung (`bc06706`) landed *after* `381c498` and grew `bundle.rs` by +> **338 lines**, so the inline citations throughout this contract are as-of +> `381c498`. **This table is authoritative where the two disagree.** +> +> | Cited | Actual at `96b40b2` | Symbol | +> |---|---|---| +> | `bundle.rs:989` | **`:1024`** | `fn reduction_version_for` | +> | `bundle.rs:798` | **`:833`** | `commit_versioned`'s superblock stamp | +> | `bundle.rs:613`–`:621` | **`:622`ff** | `fn verify_canonical_chunks` | +> | `bundle.rs:939` | **`:974`** | `fn profile_is_understood` | +> | `bundle.rs:233`–`:240` | **`:205`ff** | `fn create` (base-bearing refusal) | +> | `error.rs:253` | **`:306`** | `UnsupportedCanonicalChunkMajor` | +> | `serialize.rs:119` | **`:143`** | `fn serialize_document` | +> | `serialize.rs:212`, `:219`–`:222` | **`:239`ff** | `fn build_manifest` | +> | `serialize.rs:347` | **`:379`** | `fn serialize_and_reopen` | +> | `roundtrip.rs:241` | **`:255`** | `assert_reduction_serialization_stable` | +> | `parse.rs:591` | **`:603`** | unbounded `u32` parse | +> +> **Confirmed still exact, and not to be "corrected":** `bundle.rs:396`–`:399` +> (**pin 5's own insertion point**), `:301` (`open`), `:87` +> (`SUPPORTED_PROFILE_MAJOR`), `:389` (`UnsupportedProfile`), `ids.rs:289`, +> `generators.rs:1628`/`:1651`. + ### 0.1 The defect is a tautology, not an absence `reduction_version_for` (`bundle.rs:989`) sets a new superblock's version from @@ -125,11 +175,13 @@ open, which is the gap this revision closes (pin 3a, pin 4a). > exists to fix, committed while scoping it: **an observation that cannot > support the claim drawn from it.** -**Reader surface — 57 `Bundle::open(` sites across 10 files:** +**Reader surface — 60 `Bundle::open(` sites across 10 files.** *(Was 57 at +`381c498`; the format-epoch rung added 3 in `bundle.rs`. Re-counted 2026-08-07 at +`96b40b2`.)* | Crate | Sites | |---|---| -| `epiphany-bundle` (`bundle.rs` 20, `fuzz.rs` 15) | 35 | +| `epiphany-bundle` (`bundle.rs` **23**, `fuzz.rs` 15) | **38** | | `epiphany-testkit` (`bundle_harness.rs` 11, `roundtrip.rs` 4, `benches/bundle.rs` 2, `tests/bundle_reopen.rs` 1) | 18 | | `epiphany-textproj` (`serialize.rs`, `project.rs`) | 2 | | `epiphany-bundle/tests/` | 2 | @@ -139,9 +191,28 @@ open, which is the gap this revision closes (pin 3a, pin 4a). `bundle/tests/crash_recovery.rs` 2, `textproj/serialize.rs` 1, `testkit/tests/bundle_reopen.rs` 1, `testkit/benches/bundle.rs` 1, `bundle/tests/manifest_selection.rs` 1, `bundle/fuzz.rs` 1. +*Re-counted 2026-08-07 at `96b40b2`: still 32, and every per-file figure above +still holds.* -**`commit` has 57 sites** (including 2 in `epiphany-editor-core`) — pin 3's -design keeps every one of them unchanged. +**`commit` sites** — pin 3's design keeps every one of them unchanged. + +> **CORRECTED 2026-08-07 in review round 1. The claim this paragraph made was +> *"`commit` has 57 sites (including 2 in `epiphany-editor-core`)"*, and the +> parenthetical is false.** `epiphany-editor-core` depends on `epiphany-core`, +> `epiphany-ops` and `epiphany-layout-ir` — **not** on `epiphany-bundle` — and +> the string `Bundle` does not appear in its `lib.rs` at all. Its two hits are +> `self.commit(...)` (`editor-core/src/lib.rs:1593`, `:1709`), resolving to its +> own `fn commit(&mut self, new: Vec) -> Result EditorError>` (`:1404`). A textual `.commit(` grep counted a same-named method +> in a crate that cannot reach `Bundle`. +> +> **This is the third instrument failure recorded in this section, and it was +> committed in the same paragraph as the method note above.** The first could not +> see a propagating path; the second asserted a universal negative from `head`- +> truncated output; this one resolved a method name without resolving the type it +> belongs to. Same defect, three shapes: **an observation that cannot support the +> claim drawn from it.** The executing agent MUST count `Bundle`-typed receivers, +> not the token `.commit(`. **Which capability each site supplies is NOT decided by crate dependency.** `epiphany-testkit` and `epiphany-textproj` depend on `epiphany-ops`, but that @@ -194,6 +265,17 @@ committed text-projection vectors carrying the same; `generators.rs:1628`/`:1651 emit `rng.range(0, 8)`. Injecting the real authority `0` at every site would reject part of the present corpus. +> **Evidence updated 2026-08-07 in review round 1 — the disposition is unchanged, +> the corpus is not.** `vectors.rs:353`/`:363` no longer exist. The format-epoch +> rung rebuilt the text-projection corpus to 20 vectors with `canonical_bases` +> reach **2 → 0**, so the two base-carrying vectors this pin cites are gone; the +> single surviving occurrence is `vectors.rs:326`, inside the +> `canonical_base_present` **reject** vector. Pin 2a's conclusion stands (it was +> settled from outside by the container epoch), but **pin 3b's fixture-surface +> reasoning must be re-derived against the current corpus** rather than against +> the two vectors named here. `generators.rs:1628`/`:1651` are unaffected and +> still emit `rng.range(0, 8)`. + Pin 3b handles the *fixtures* — they take named synthetic capabilities. It does **not** handle the real problem: @@ -321,6 +403,25 @@ naming S27 as what reopens them. Both are owed work here, not optional: refusal exists to prevent, and it has never been observed — only reasoned about. + > **RULED 2026-08-07 in review round 1: the removal is a MUTATION, not a + > capability restoration.** As written, *"with pin 3b's text refusal removed"* + > was ambiguous between temporarily lifting the refusal to observe what it + > prevents, and permanently restoring base-bearing text round-trip. The + > sentence's own next clause settles it — *"that is the false provenance the + > text refusal exists to prevent"* — and a guard is not permanently deleted in + > order to demonstrate why it is needed. **The format rung's text refusal is + > permanent and S27 does not lift it.** The demonstration is therefore + > **M7** in §4, removed and restored by hand-editing, and this obligation is + > discharged there rather than by a test. + > + > **Consequences of the ruling, stated so they are not re-litigated:** + > `COMPANION_VERSION` stays **0.14.0**; `parse.rs`, `vectors.rs`, + > `textproj/src/lib.rs` and `spec/text_projection.tex` are **NOT** touched by + > this rung and are deliberately absent from §2; and the corpus keeps + > `canonical_bases` reach **0**. A future rung may restore the capability — + > that is its own contract, with the four touch rows and the companion-version + > bump this one declines. + 3. **Two conformance assertions come back** (format-rung pin 3c). Criterion 4's bookkeeping-projection counterpart, `assert_reduction_serialization_stable` (`testkit/src/roundtrip.rs:241`), keeps its serialize → load → decode → @@ -437,6 +538,23 @@ behaviour normatively, and a Revision History row. There is **no** existing "requires rebuild" error language in either document — verified — so this is new prose, not an amendment. +**Whether this mints a new `\label{req:...}` MUST be decided explicitly, and +stated in the report. AMENDED 2026-08-07 in review round 1.** The pin said "add +the rejection behaviour normatively" without saying whether the behaviour gets +its own requirement label, and the two readings have different touch tables: + +- **If it mints a label**, `core_spec.tex`'s requirement count moves 213 → 214 + and `crates/epiphany-testkit/tests/requirement_labels.rs` **must** change — + `CORE_REQUIREMENT_COUNT = 213` (`:15`) is hardcoded and currently matches the + tree exactly. **Touch row 12 carries it.** +- **If it does not** — the prose lands under an existing requirement — row 12 is + unused and the report says so. + +**This is the escapee `CLAUDE.md` names by name**, and it escaped the +format-epoch rung's touch table too. A file that must change but is not listed +**silently drops out of the commit**, and the resulting failure surfaces on +someone else's branch. + **Pin 10 — the ledger. AMENDED 2026-08-07, before dispatch, on a finding from the machine-move review.** `spec/PASS13_CANDIDATES.md`: P13-S27 → RESOLVED, recording both rulings, the @@ -488,10 +606,27 @@ row may not record S16 as open until those land with this rung**; ratification o | 9 | `crates/epiphany-textproj/src/{serialize,project}.rs` | call sites, real authority | | 10 | `spec/core_spec.tex` (+ `.pdf`) | pin 9 | | 11 | `spec/PASS13_CANDIDATES.md` | pin 10 | +| 12 | `crates/epiphany-testkit/tests/requirement_labels.rs` | **conditional** — pin 9, *only if* it mints a new `\label{req:...}`; `CORE_REQUIREMENT_COUNT` (`:15`) then moves 213 → 214. Added in review round 1. If pin 9 mints no label, leave unmodified and say so in the report | + +**Row 12 is conditional, and that is deliberate.** `CLAUDE.md` names this file as +a recurring escapee, and it escaped the format-epoch rung's table. Carrying it +conditionally costs nothing if unused; omitting it costs a silent drop-out. + +**`spec/text_projection.tex`, `crates/epiphany-textproj/src/{parse,vectors}.rs` +and `crates/epiphany-textproj/src/lib.rs` are deliberately ABSENT** — see the +ruling under inherited obligation 2. M7 edits the refusals temporarily and +restores them by hand; nothing there is staged. **If any of those files shows up +in `git diff --cached`, M7 was not restored** and gate 4 must fail. --- -## §3. Required tests (pin 4's ruling names all four) +## §3. Required tests + +> **Header corrected 2026-08-07 in review round 1.** It read *"(pin 4's ruling +> names all four)"* while the list below carried seven items. The count went +> stale twice — test 5 was added when the first draft's writer-path omission was +> found, then tests 6 and 7 with pin 2a's resolution — and gate 1 inherited the +> stale figure. **Do not reconcile against a fixed number**; see gate 1. Named, permanent, in `epiphany-bundle`: @@ -514,8 +649,11 @@ Named, permanent, in `epiphany-bundle`: intact. A writer check that corrupts the document while refusing is worse than no check. -**Added 2026-08-07 with pin 2a's resolution — the inherited obligations, stated -as tests so they cannot be discharged by prose:** +**Added 2026-08-07 with pin 2a's resolution — inherited obligations 1 and 3, +stated as tests so they cannot be discharged by prose.** *(Corrected in review +round 1: this preamble previously claimed **all** the inherited obligations were +stated as tests. Obligation 2 was in neither §3 nor §4 — it is now **M7**, by +the ruling recorded beside it.)* 6. **`a_major_1_bundle_carrying_a_base_opens_when_the_authority_matches`** — the read-side half of the format rung's pin 3a, converted from temporary refusal @@ -529,11 +667,43 @@ as tests so they cannot be discharged by prose:** names this contract — if the marker is still in the tree when this rung reports, the restoration did not happen. +8. **`committing_a_canonical_base_succeeds_when_the_authority_matches`** — + **ADDED 2026-08-07 in review round 1.** Obligation 1 warns that converting + only one branch "leaves a hole exactly where the format rung's own review + found one", and the write-side **positive** branch had no test: test 1 is + read-positive, test 2 read-negative, test 5 write-negative. The missing branch + is precisely where the format rung's temporary refusal sits + (`bundle.rs:795` → `ReductionAuthorityUnavailable`, asserted by + `a_major_1_bundle_round_trips_and_refuses_to_introduce_a_base` at `:1787`). + Without this test, an implementation that converts the read side and leaves + `commit` refusing categorically passes every other test in this section. + Tests 2 and 3 must be **paired in review**: each asserts the other's error is *not* produced. A test that only checks its own variant cannot show the two paths are distinguishable, which is the whole point of pin 6. -Tests 6 and 2 stand in the same relation to each other. +Tests 6 and 2 stand in the same relation to each other. **So do tests 8 and 5**, +on the write side. + +**Several of these CONVERT existing tests rather than adding new ones — found in +review round 1, and the reason gate 1 no longer names a number.** The format rung +left two tests asserting the interim refusal, and S27 turns each into a matched +pair: + +| Existing test | Becomes | +|---|---| +| `opening_a_major_1_bundle_that_already_carries_a_base_is_refused` (`bundle.rs:1866`) | tests **6** and **2** | +| `a_major_1_bundle_round_trips_and_refuses_to_introduce_a_base` (`:1787`) | tests **8** and **5** | +| `a_corrupt_base_fails_as_malformed_before_any_epoch_error` (`:1840`) | extended into test **3** (adds the "*not* `CanonicalBaseRequiresRebuild`" assertion) | + +**And `ReductionAuthorityUnavailable` is deleted by this rung**, so every site +naming it must move or the crate will not compile: `error.rs:152` (variant), +`:232` (Display arm), `bundle.rs:422` and `:795` (construction), the doc comments +at `:1538` and `:1844`, and **five test assertions** at `:1740`, `:1774`, +`:1834`, `:1861`, `:1879`. The three negative assertions (`:1740`, `:1774`, +`:1861`) exist to prove the legacy and corrupt paths do **not** produce it — they +must be re-pointed at the error that replaces it, **not** deleted, or the +distinction they were written to hold is lost. --- @@ -569,12 +739,50 @@ must fail. Then narrow it to refuse *any* stale inherited base rather than only newly emitted or replaced one, and confirm an unrelated commit on an already-open bundle starts failing — signing that pin 3a's scope is deliberate. +**M7 — the laundering the text refusal prevents, finally observed. ADDED +2026-08-07 in review round 1; this discharges inherited obligation 2.** + +The format rung reasoned about this path and could never run it: its own pin 3a +refused every major-1 base commit categorically, so the observation was +unreachable. Under S27 a base commit succeeds or fails on its version, so it +becomes reachable for the first time. + +Temporarily remove the format rung's pin-3b text refusal — **all three sides**, +since removing one leaves the others refusing and the document never reaches the +writer: `serialize.rs:152` (`SerializeError::CanonicalBaseUnsupported`), +`project.rs`'s `project_text_document` refusal (`:579`), and the parser's +(`parse.rs:138`–`:147`). Then construct a base-bearing `TextDocument` whose raw +`reduction_algorithm_version` **happens to equal** +`CURRENT_REDUCTION_ALGORITHM_VERSION`, serialize it, and **observe** that the +resulting major-1 container is byte-indistinguishable from one whose base was +genuinely validated. + +**What the observation must show, or it has not been made:** that the capability +check does **not** fire — the document launders precisely *because* its number +matches, and no check can tell a coincidence from a rebuild. That is the whole +argument for the refusal, and it has never been run. + +**Restore all three refusals by hand-editing back**, never with git. Record the +result as a **demonstration**, not a guard: nothing in the shipped tree changes, +and the refusal is permanent (see the ruling under inherited obligation 2). + +**This is a mutation whose expected outcome is SUCCESS, not failure.** Every +other mutation here breaks a test; this one makes a refused path succeed, and +the finding is that it succeeds *silently*. Do not report it as a passing gate. + --- ## §5. Gate -1. `cargo test --workspace` — full pass; report the new total and the delta with - its cause (four tests added). +1. `cargo test --workspace` — full pass. **Report the new total and account for + the delta by category — do NOT reconcile against a fixed number.** *(Corrected + in review round 1: this item read "(four tests added)", a figure already stale + twice over, and §3's own header carried the same wrong count. The delta is not + a simple addition: three of §3's tests **convert or extend** existing + format-rung tests, which nets zero, while others are new.)* The baseline is + **1570**. Give the count in three buckets — net-new, converted-from-existing, + restored-assertions — and if they do not sum to the observed delta, **that is + a finding, not an arithmetic error to be papered over**. 2. `cargo clippy --workspace --all-targets -- -D warnings` → clean. 3. `cargo fmt -p epiphany-ops -p epiphany-bundle -p epiphany-testkit -p epiphany-textproj --check` → clean. **`cargo fmt --all` is forbidden.** @@ -586,10 +794,17 @@ already-open bundle starts failing — signing that pin 3a's scope is deliberate `grep -rnE "impl +Default +for +BundleCapabilities|derive\\([^)]*\\bDefault\\b[^)]*\\)[[:space:]]*(pub )?struct BundleCapabilities" crates/epiphany-bundle/src/` → **0 matches.** Run it against production source only; report the count, not a verdict. -6a. **No production composition path uses the fixture constructor:** - `grep -rn "synthetic_for_fixture" crates/epiphany-textproj/src/` shows matches - **only** inside `#[cfg(test)]` modules. Report each match with its enclosing - item. +6a. **No production composition path uses the fixture constructor.** *(Scope + widened in review round 1: this checked `epiphany-textproj` only, while touch + row 7 gives **`epiphany-testkit`** the real authority too — so a + `synthetic_for_fixture` leak there was ungated.)* Run over **both**: + `grep -rn "synthetic_for_fixture" crates/epiphany-textproj/src/ crates/epiphany-testkit/src/` + Report each match with its enclosing item. In `epiphany-textproj` every match + must be inside a `#[cfg(test)]` module. In `epiphany-testkit`, which is a + fixture crate whose non-test code legitimately builds fixtures, each match + must instead be justified against §0.4's rule: **only production composition + paths wrap the real constant**, and `roundtrip.rs` / `bundle_harness.rs` carry + both kinds. Name which kind each site is; an unclassified site is a finding. 7. `spec/vectors/decode_vectors.txt` unmodified; no schema major/minor moved. --- @@ -622,11 +837,30 @@ to 1 belongs to S16. ## §7. Report requirements -1. The five mutations, each with verbatim output (M4 as a recorded prohibition). -2. The seven gate results, each with its command. -3. The staged file list and the test-count delta with its cause. -4. The four required tests by name, each passing, with tests 2 and 3 shown to - produce *different* errors. -5. A count of call sites updated per crate, against §0.4's table — any - discrepancy is a finding. -6. Anything contradicting this contract. +*Counts corrected 2026-08-07 in review round 1 — items 1, 2 and 4 each named a +figure the document had outgrown. This is the same drift as §3's header and gate +1: **three independent stale counts of the same three lists.** Prefer "every item +in §N" to a number.* + +1. **Every mutation in §4** — currently **seven** (M1–M7), each with verbatim + output. **M4 is a recorded prohibition** (observed to compile, then reverted) + and **M7 is a demonstration whose expected outcome is success**. Neither is a + passing guard; do not report them as one. +2. **Every gate item in §5** — currently **eight** (1, 2, 3, 4, 5, 6, 6a, 7), + each with the command that produced it. +3. The staged file list, and the test-count delta in gate 1's three buckets. +4. **Every required test in §3** — currently **eight** — by name, each passing, + with tests 2 and 3 shown to produce *different* errors, and the same for + tests 6/2 and 8/5. +5. A count of call sites updated per crate, against §0.4's table **as corrected + in review round 1** (open **60**, create **32**) — any discrepancy is a + finding. Count `Bundle`-typed receivers, not the token `.commit(`. +6. **Confirmation that M7's three text refusals were restored**, and that none of + `text_projection.tex`, `textproj/src/parse.rs`, `textproj/src/vectors.rs` or + `textproj/src/lib.rs` appears in the staged diff. +7. **Whether pin 9 minted a new requirement label**, and therefore whether touch + row 12 was used. +8. **Confirmation that the pin-3c suspension marker naming this contract is gone + from `testkit/src/roundtrip.rs`.** If it is still in the tree, obligation 3's + restoration did not happen, whatever the prose says. +9. Anything contradicting this contract. diff --git a/spec/PASS13_CANDIDATES.md b/spec/PASS13_CANDIDATES.md index 0b117cc..675e0bc 100644 --- a/spec/PASS13_CANDIDATES.md +++ b/spec/PASS13_CANDIDATES.md @@ -122,5 +122,5 @@ evidence in isolation. | 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-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 | +| 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** — 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. **Pins are frozen; dispatchable** | | 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 |