From df9e528e997506cab0124691158a25aabad7c1d4 Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Fri, 7 Aug 2026 23:41:15 +0200 Subject: [PATCH] P13-S27 round 4: the M5 pair was defective a third time Independent review against 53292f6. Five findings, four blocking. Round 4 accepted pin 3's capabilities() accessor as bounded -- the first new text any round has passed -- and rejected both halves of M5 again. M5b cited the wrong value, and this one is mine. roundtrip.rs:367 sits in assert_score_serialization_stable, not assert_reduction_serialization_stable, and it versions an acceleration snapshot, not a canonical base. The latter has no base at all, because pin 3c suspended it. So the value round 3 told the implementer not to touch was irrelevant to the authority check, and mutating it could not have failed anything. Round 3 grepped ReductionAlgorithmVersion across testkit/src/, saw a roundtrip.rs hit, and attributed it to the function it was already thinking about without resolving the enclosing item -- the same shape as section 0.4's .commit( miscount, which round 1 had already recorded as a lesson. Recording a defect is not the same as not committing it. The tautology diagnosis stands; only its evidence was wrong. M5b left the instrument unchosen. Round 3 said "the rung picks one" and named two routes, one of which does not exist for the nominated crate: craft_image_with_base is a private fn inside epiphany-bundle's cfg(test) module. Now chosen, through public API only: build with synthetic_for_fixture(0), commit a base carrying the literal 0, take the bytes, reopen under the real constant. The operands are provably independent and neither can be tidied into the other. M5b had no test that could assert the error fields. assert_reduction_serialization_stable returns () and reopens with .expect, so a mismatch panics instead of yielding a matchable CanonicalBaseRequiresRebuild { base, current }. Test 10b added, named and returning a matchable Result. M5a violated section 7 item 4a -- the rule round 3 added in the same edit. It named no test, and its natural assertion compares CURRENT_REDUCTION_ALGORITHM_VERSION with itself, which holds for every value. Test 10a added, asserting against a deliberate literal. Round 3 diagnosed M5b's tautology and wrote the identical tautology into M5a in the same edit, then added a rule and immediately broke it. Both literals are load-bearing as literals. Section 7 item 4b now requires confirming neither was rewritten as the constant -- tidying either makes its mutation vacuous while every test stays green. Minor: status prose said the pins were open to round 3's findings after round 3 closed. Still NOT RATIFIED, NOT DISPATCHABLE. Defect rate 9, 6, 6, 5 -- not converging, and every blocking finding in rounds 3 and 4 was in text written to fix the previous round. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x --- spec/CONTRACT_P13S27_REDUCTION_AUTHORITY.md | 195 +++++++++++++++----- spec/PASS13_CANDIDATES.md | 2 +- 2 files changed, 150 insertions(+), 47 deletions(-) diff --git a/spec/CONTRACT_P13S27_REDUCTION_AUTHORITY.md b/spec/CONTRACT_P13S27_REDUCTION_AUTHORITY.md index 41ccdbb..d51b135 100644 --- a/spec/CONTRACT_P13S27_REDUCTION_AUTHORITY.md +++ b/spec/CONTRACT_P13S27_REDUCTION_AUTHORITY.md @@ -1,9 +1,9 @@ # Contract — P13-S27: the reduction version gets an outside witness -**Status:** **NOT RATIFIED. NOT DISPATCHABLE.** Independent **review round 3 -closed** against `b842975` — six findings, four blocking, all now carried. -**Awaiting review round 4**; the pins remain open and no execution work may -begin. +**Status:** **NOT RATIFIED. NOT DISPATCHABLE.** Independent **review rounds 3 and +4 closed** — six findings then five, eight blocking between them, all now +carried. **Awaiting review round 5**; the pins remain open and no execution work +may begin. **Round 1's ratification is WITHDRAWN.** It was claimed on 2026-08-07 after a single round; round 2 then found four more blocking defects against the @@ -12,8 +12,10 @@ ratification that a subsequent round falsifies that quickly was not a ratification, and leaving the claim standing would make the status field mean nothing. -**The pins are therefore NOT frozen.** They are open to round 3's findings. -Freezing follows ratification; it does not precede it, and it does not survive a +**The pins are therefore NOT frozen.** They are open to **the current round's** +findings — rounds 3 and 4 are closed, and this sentence named a specific round +until round 4 caught it going stale the moment that round closed. Freezing +follows ratification; it does not precede it, and it does not survive a withdrawal. **No execution work may begin** — not implementation, not staging, not partial work against "the settled pins." @@ -44,14 +46,38 @@ mutation is easy; establishing that it *can run* requires deriving its observation, its failure condition, and the test it breaks, and none of the three was done. §7 item 4a now demands all three. -**What round 4 should weigh, stated against interest:** the defect rate is **not -converging** — nine, six, six — and every blocking finding in round 3 was in text -written to fix round 2's findings. **The amendment process has produced defects -at a steady rate for three rounds.** 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 just falsified. **Treat -"dispatchable" as a claim requiring evidence of convergence, not a status reached -by running out of findings.** +**Review round 4 — 2026-08-07, independent, against `53292f6`.** Five findings, +four blocking. It judged pin 3's accessor **bounded** — the first piece of new +text any round has accepted — and the M5 pair defective again. + +| # | Finding | Disposition | +|---|---|---| +| 1 | **M5b cited the wrong value.** `roundtrip.rs:367` is in `assert_score_serialization_stable` (`:332`) and versions an **acceleration snapshot**, not the canonical base. `assert_reduction_serialization_stable` has **no base at all** — pin 3c suspended it | Evidence corrected. **The tautology diagnosis stands; only its evidence was wrong** | +| 2 | **M5b left the instrument unchosen** — it said "the rung picks one" and named two, one of which does not exist for the nominated crate: `craft_image_with_base` is private to `epiphany-bundle`'s test module (`:1648`) | **Chosen**: commit-then-reopen through public API only | +| 3 | **M5b had no test that could assert the error fields.** `assert_reduction_serialization_stable` returns `()` and reopens with `.expect` (`:292`) — a mismatch panics and cannot match `CanonicalBaseRequiresRebuild { base, current }` | **Test 10b** added, named and returning a matchable `Result` | +| 4 | **M5a violated §7 item 4a — the rule round 3 added in the same edit.** It named no test, and its natural assertion (`capabilities() == CURRENT_REDUCTION_ALGORITHM_VERSION`) compares the constant with itself and cannot fail | **Test 10a** added; the comparison is now against a deliberate **literal** | +| — | Status prose said the pins were open to *round 3's* findings after round 3 closed | Now "the current round's" | + +**Round 3's error is the one to carry.** It grepped `ReductionAlgorithmVersion` +across `testkit/src/`, saw a `roundtrip.rs` hit, and attributed it to the function +it was already thinking about **without resolving the enclosing item** — the same +shape as §0.4's `.commit(` miscount, which round 1 recorded as a lesson and round +3 then repeated. **Recording a defect is not the same as not committing it.** + +**And both M5 halves failed the same way twice.** Round 3 diagnosed M5b's +tautology and wrote M5a with an identical tautology *in the same edit*, then +added §7 item 4a and immediately violated it. A rule written and broken in one +sitting is evidence the author is pattern-matching the finding rather than +applying it. + +**What round 5 should weigh, stated against interest:** the defect rate is **nine, +six, six, five** — still not converging after four rounds, though round 4 accepted +its first piece of new text. **Every blocking finding in rounds 3 and 4 was in +text written to fix the previous round.** The newest material — tests 10a/10b, +M5b's chosen instrument, §7 item 4b — has had **zero** adversarial passes, and the +M5 pair has now been rewritten **three times** and found defective each time. +**Treat "dispatchable" as a claim requiring evidence of convergence, not a status +reached by running out of findings.** (Was: DRAFT, BLOCKED on the format-epoch rung, `spec/CONTRACT_FORMAT_EPOCH_MAJOR1.md`, which at the time was ratified and in @@ -852,6 +878,35 @@ the ruling recorded beside it.)* asserting the behaviour it breaks. **A mutation is only as good as the test it breaks**, and §4 must name that test for every entry. +**Added in review round 4 — the two tests M5a and M5b break. Round 3 wrote both +mutations without them, in the same edit that added §7 item 4a requiring them.** + +10a. **`serialize_document_supplies_the_real_reduction_authority`** — in + `epiphany-textproj`. Serialize a base-free document and assert + `bundle.capabilities().current_reduction_version == ReductionAlgorithmVersion(0)`. + + **The `0` MUST be a literal, and the test MUST carry a comment saying why.** + Comparing against `CURRENT_REDUCTION_ALGORITHM_VERSION` would compare the + constant with itself and hold for every value — the assertion would be + unfalsifiable and M5a could not break it. **This test is expected to fail when + S16 bumps the authority**, and that is correct: the literal is a tripwire on + the production wiring, and S16 updating it is S16 stating that it moved. + +10b. **`a_base_bearing_bundle_reopened_under_the_real_authority_validates`** — in + `epiphany-testkit`, which may use the real constant. Build the fixture with + `synthetic_for_fixture(0)`, commit a base carrying the literal + `ReductionAlgorithmVersion(0)`, take the bytes, and reopen them with `caps` + built from `CURRENT_REDUCTION_ALGORITHM_VERSION`. Assert it opens. + + **This is the only place in the rung where the real authority meets a + canonical base**, which is why M5b needs it and why no existing test could + serve. It must be a named test returning a matchable `Result`, **not** an + assertion inside a void conformance helper. + +**Both literals are load-bearing as literals.** A later reader "tidying" either +into `CURRENT_REDUCTION_ALGORITHM_VERSION` makes the corresponding mutation +vacuous while leaving every test green. Say so in both doc comments. + 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. @@ -926,9 +981,21 @@ REVIEW ROUND 3.** > observation depends on scaffolding that is not in the shipped tree observes the > scaffolding, not the tree. -Change `CURRENT_REDUCTION_ALGORITHM_VERSION`. In a test over -`serialize_document`, assert via **`bundle.capabilities().current_reduction_version`** -that the value the production path supplied moved with the constant. +**The test it breaks is test 10a** (§3), and the comparison **MUST be against a +literal, not against the constant. CORRECTED IN ROUND 4.** + +> **Round 3 wrote "assert that the value moved with the constant", which is +> unfalsifiable.** Asserting `capabilities() == CURRENT_REDUCTION_ALGORITHM_VERSION` +> compares the constant with itself laundered through one function call: mutate +> the constant and **both sides move**, so the assertion holds for every value. +> That is the same tautology round 3 caught in M5b, in the mutation *next to it*, +> written in the same edit. Round 3 also violated its own new §7 item 4a by +> naming no test at all. + +Change `CURRENT_REDUCTION_ALGORITHM_VERSION` from `0` to any other value. Test +10a must **fail**, because it asserts +`bundle.capabilities().current_reduction_version == ReductionAlgorithmVersion(0)` +with `0` written as a **deliberate literal** — the independent operand. Assert on the capability **only** — not on an open or commit outcome. There is no base, so no check fires and none should. **If an open or commit outcome moves @@ -938,39 +1005,71 @@ and that is a finding.** **M5b — the authority is load-bearing where a base exists. REWRITTEN IN REVIEW ROUND 3; as written it could not fail.** -> **This is the rung's own defect, reproduced inside the mutation built to detect -> it.** Round 2 named `assert_reduction_serialization_stable` -> (`testkit/src/roundtrip.rs:255`) as the failing test. But that harness stamps -> its base version at `roundtrip.rs:367`, today a literal -> `ReductionAlgorithmVersion(0)`, and the natural S27 implementation replaces that -> literal with `CURRENT_REDUCTION_ALGORITHM_VERSION` **while also sourcing the -> supplied capability from it**. Both operands then move together and the -> comparison passes for every value of the constant — **precisely §0.1's -> tautology**: *"both operands descend from the same source, so for any -> conformingly-written document the comparison is a tautology."* A mutation that -> cannot fail is not a mutation. +> **Round 3's diagnosis was right and its evidence was wrong. CORRECTED IN ROUND +> 4.** Round 3 said the base version was stamped at `roundtrip.rs:367`. **It is +> not.** `:367` sits inside `assert_score_serialization_stable` (`:332`) and +> pushes to **`acceleration_snapshots`** — a different harness and a different +> field. `assert_reduction_serialization_stable` (`:255`) has **no canonical base +> at all** today; pin 3c suspended it, and the harness reads its snapshot chunk +> directly by `ChunkRef`. So the value round 3 told the implementer not to touch +> has nothing to do with the authority check, and mutating it could not have +> failed anything. +> +> **How the error was made, since it is this rung's own subject:** round 3 grepped +> `ReductionAlgorithmVersion` across `testkit/src/`, saw a `roundtrip.rs` hit, and +> attributed it to the function it was already thinking about **without resolving +> the enclosing item**. That is the fourth instrument failure in this contract and +> the second of exactly this shape — §0.4's `.commit(` miscount was the first. +> +> **The underlying tautology finding stands.** If the supplied capability and the +> base version both descend from `CURRENT_REDUCTION_ALGORITHM_VERSION`, both +> operands move together and the comparison passes for every value — §0.1's +> defect inside the mutation built to detect it. Only the cited evidence was +> wrong. -**The base version MUST come from a source that does not move with the -constant.** Two acceptable instruments, and the rung picks one and says which: +**The instrument is CHOSEN, not offered. ROUND 4.** Round 3 said "the rung picks +one" and named two routes, which is not a decision — and one of them does not +exist: **`craft_image_with_base` is a private `fn` inside `epiphany-bundle`'s +`#[cfg(test)]` module (`bundle.rs:1648`, module opens at `:1407`)**, so +`epiphany-testkit` cannot call it. The routes also carry different fixture and +touch-table consequences, so leaving the choice to execution would have put a +design decision in the implementer's hands. -- **a persisted artifact** — a committed fixture image whose base version is - fixed on disk, built via `craft_image_with_base`; or -- **an explicit literal** in the test, written as a literal *because* it must not - track the authority, with a comment saying so. +**The chosen instrument — commit-then-reopen, entirely through public API:** -**What must be observed:** with the constant mutated, the capability and the base -version now disagree, and the test fails with **`CanonicalBaseRequiresRebuild`**, -both fields asserted. **Record the two operands' provenance in the report** — -naming where each came from is the only way to show they are independent, and -that independence is the whole content of this mutation. +1. Build a bundle with `caps = synthetic_for_fixture(0)` and commit a canonical + base whose version is the **deliberate literal** `ReductionAlgorithmVersion(0)`. + This succeeds by test 8's path. +2. Take the bytes. +3. **Reopen them with `caps` built from the real + `CURRENT_REDUCTION_ALGORITHM_VERSION`.** + +Unmutated, the real constant is `0`, the operands agree, and the bundle opens. +Mutated, the constant is not `0`, and the reopen fails with +**`CanonicalBaseRequiresRebuild { base: 0, current: }`**. + +**Why this shape and not another:** the two operands are provably independent — +one is a synthetic literal written into a fixture, the other is the real constant +read at the reopen — and **neither can be "tidied" into the other** without +deleting the synthetic capability the fixture is built on. It needs no private +helper, no new fixture file, and no touch-table row. It isolates the real +constant on the **read** side only, so pin 3a's writer check cannot fire first +and mask the result. + +**The test it breaks is test 10b** (§3) — a **named** test, not the void +conformance helper. **ROUND 4:** round 3 nominated +`assert_reduction_serialization_stable`, which returns `()` and whose reopen is +`.expect("reopen bundle")` (`roundtrip.rs:292`). A mismatch there **panics**; it +cannot match on `CanonicalBaseRequiresRebuild { base, current }`, so the required +two-field assertion was impossible in the nominated site. + +**Report the provenance of both operands.** Naming where each came from is the +only way to show they are independent, and that independence is the whole content +of this mutation. **Preserved from the original M5:** if only `synthetic_for_fixture` tests move, pin 3b has been applied backwards, and that is a finding. -> **Note for whoever implements the restoration.** `roundtrip.rs:367`'s literal -> `0` must **not** be mechanically swapped for the constant. It is load-bearing -> as a literal. Doing the "obvious tidy-up" there is what makes M5b vacuous. - **Both halves are required.** M5a alone shows the constant is read but never that it matters; M5b alone shows it matters but never that production reads it. The original mutation conflated the two because, at `381c498`, one path did both. @@ -1125,9 +1224,13 @@ in §N" to a number.* 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 **nine** *(test 9 added in round 3)* - — by name, each passing, with tests 2 and 3 shown to produce *different* - errors, and the same for tests 6/2 and 8/5. +4. **Every required test in §3** — currently **eleven** (1–9 plus **10a** and + **10b**, added in round 4) — by name, each passing, with tests 2 and 3 shown + to produce *different* errors, and the same for tests 6/2 and 8/5. +4b. **Confirmation that the literal `0` in tests 10a and 10b is still a literal**, + and that neither was rewritten as `CURRENT_REDUCTION_ALGORITHM_VERSION`. + Tidying either makes M5a or M5b vacuous while every test stays green — the + failure mode is invisible to the suite and visible only here. 4a. **For every mutation in §4, the named test it breaks**, and for M5a/M5b the **provenance of each operand** — where the capability came from and where the base version came from. Three mutations were found unrunnable across rounds 2 diff --git a/spec/PASS13_CANDIDATES.md b/spec/PASS13_CANDIDATES.md index f169e49..536db2c 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. **~~RATIFIED 2026-08-07 after review round 1~~ — RATIFICATION WITHDRAWN 2026-08-07, see round 2 below. Current status: NOT RATIFIED, NOT DISPATCHABLE, pins NOT frozen, **independent review round 3 CLOSED against `b842975` (six findings, four blocking), awaiting review round 4**; 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 | +| 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. Current status: NOT RATIFIED, NOT DISPATCHABLE, pins NOT frozen, **independent review rounds 3 and 4 CLOSED (six findings then five, eight blocking between them), awaiting review round 5**; 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** | | 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 |