Amend the G-minor plan: the seam ruling, the companion consequence, one wrong hash

Documentation only. No Rust, no .tex, no vectors, no implementation.

Corrects G2a's introducing commit to 7df5ca1 - where kinds/tags 32-33 enter
payload.rs. 55eff00 is the later review-fix and introduces no discriminant.
The hashes are the evidence for the ladder's monotonicity, so citing a
follow-up commit would make it unverifiable at exactly the rung a reader is
most likely to re-derive.

Records the ruled seam. epiphany-bundle stays opaque with no ops or layout-ir
dependency; producers supply the aggregate manifest SchemaVersion explicitly
and CommitContext exposes the previous one so unchanged barrier content
preserves it. That is new plumbing rather than a read-through: Manifest has no
schema-version field, it lives in the superblock, and it must not gain one -
a field addition is schema-major regardless of type, which would defeat the
rung. Changed barrier bytes require an aware producer that computes the exact
epoch or refuses, because blindly retaining the previous aggregate
over-stamps once a contributing barrier is removed.

Manifest::SCHEMA becomes a baseline constant rather than the universally
emitted version. Three sites silently select it, not two - bundle.rs:220,
bundle.rs:724, and the re-exported helper manifest_chunk_hash at bundle.rs:1298,
which is public API and so a signature change. The major-only check at
bundle.rs:301 stays as-is and stays correct.

Consequently G-minor does bump the text companion: 0.9.0 to 0.10.0 with corpus
regeneration, because TextDocument must carry the manifest SchemaVersion that
the projection cannot derive from deliberately opaque bytes. Not because of
op-block stamping, which remains projection-invisible - section 2's "no
companion bump" correction is still true about op blocks and now carries a
forward pointer so it does not read as the whole answer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QjsEnYhm1gPpf6ii2iFxFV
This commit is contained in:
Levi Neuwirth 2026-07-28 18:47:42 -04:00
parent 2c7191482a
commit 0def1563c8
1 changed files with 79 additions and 10 deletions

View File

@ -75,6 +75,13 @@ rung is not a patch.
> base. **No companion bump is required for op-block stamping.** A separate > base. **No companion bump is required for op-block stamping.** A separate
> decision to move canonical-base or extension schemas would change this; > decision to move canonical-base or extension schemas would change this;
> the op-block sweep alone does not. > the op-block sweep alone does not.
>
> **Still true, but no longer the whole answer (2026-07-28).** The manifest
> seam ruling (§4) makes the manifest's aggregate `SchemaVersion` a *carried*
> `TextDocument` attribute, so **G-minor does bump the companion** — 0.9.0 →
> 0.10.0, with corpus regeneration. Op-block stamping remains
> projection-invisible exactly as this correction says; the bump comes from the
> manifest, not from it.
**Sixty-six** occurrences of `SchemaVersion::{V0, new, for_major}` across **Sixty-six** occurrences of `SchemaVersion::{V0, new, for_major}` across
sixteen files (corrected from 62), concentrated in `epiphany-bundle`, sixteen files (corrected from 62), concentrated in `epiphany-bundle`,
@ -181,8 +188,15 @@ numbering spaces are unrelated and must not be cross-read.
introducing commits rather than assumed: M2c `a207077` (2026-06-25) → Push 3 introducing commits rather than assumed: M2c `a207077` (2026-06-25) → Push 3
`92aaccf` (07-02) → Phase-3 `0316160` (07-02) → G-pass `e4edea6` (07-07) → `92aaccf` (07-02) → Phase-3 `0316160` (07-02) → G-pass `e4edea6` (07-07) →
repeat pair `9b5339f` (07-07) → Push 4a `2740a6c` (07-09) → G1 `3b09595` repeat pair `9b5339f` (07-07) → Push 4a `2740a6c` (07-09) → G1 `3b09595`
(07-24) → G2a `55eff00` (07-28). The two events sharing 2026-07-07 are ordered (07-24) → **G2a `7df5ca1`** (07-28). The two events sharing 2026-07-07 are
correctly: the G-pass precedes the repeat revision. ordered correctly: the G-pass precedes the repeat revision.
> **Correction 2026-07-28.** G2a's introducing commit is **`7df5ca1`**, where
> kinds/tags 3233 enter `ops/src/payload.rs`. `55eff00` is the *subsequent
> review-fix* commit and introduces no discriminant. The distinction matters
> because these hashes are the evidence for monotonicity: citing a follow-up
> commit would make the ladder unverifiable at the one rung a future reader is
> most likely to re-derive.
**Existing major baselines are unchanged**: `V0` uses minor 1; `V1``V3` use **Existing major baselines are unchanged**: `V0` uses minor 1; `V1``V3` use
minor 0. **Baseline variants impose no additive override** — M2c's *operation minor 0. **Baseline variants impose no additive override** — M2c's *operation
@ -240,14 +254,69 @@ vocabulary**.
decode-and-inspect step at a higher layer that already depends on both — decode-and-inspect step at a higher layer that already depends on both —
and choosing among them is contract work. and choosing among them is contract work.
**The genuinely hard case, which must not be discovered during **The genuinely hard case:** barrier bytes are *preserved verbatim* across
implementation:** barrier bytes are *preserved verbatim* across writes, writes, including for extensions the writer does not understand. A repack can
including for extensions the writer does not understand. A repack can carry a barrier blob it cannot decode, whose tags it cannot enumerate, and
therefore carry a barrier blob it cannot decode, whose tags it cannot whose epoch it cannot derive. **"Decode and inspect" silently does not cover
enumerate, and whose epoch it cannot derive. The contract must rule on that this** — it looks complete until a foreign extension is present.
case explicitly — preserve the manifest's existing minor, refuse, or require
the minor to travel with the blob — because the "decode and inspect" shape ### The seam — RULED 2026-07-28
silently does not cover it.
**`epiphany-bundle` stays opaque. No `epiphany-ops` or `epiphany-layout-ir`
dependency is added.** The producer supplies the aggregate manifest
`SchemaVersion` explicitly; the bundle records it and never derives it.
1. **Producers supply the aggregate.** The write paths take the manifest's
`SchemaVersion` as an input instead of selecting a constant.
2. **`CommitContext` must expose the previous manifest version**, so unchanged
barrier content can preserve it. **This is new plumbing, not a read-through:**
`CommitContext::previous_manifest` is a `&Manifest`, and **`Manifest` has no
schema-version field at all** — the version lives in the *superblock*
(`superblock.rs:174`, bytes 64..68). It must therefore travel as its own
`CommitContext` field.
3. **It must NOT become a `Manifest` body field.** Adding one is a schema
**major** change regardless of type (`binary_format.tex:2360`), which would
defeat the entire rung. The wire slot already exists in the superblock; no
new one is needed.
4. **Changed barrier bytes require an aware producer.** One that cannot
establish the exact new epoch **must refuse**. Blindly retaining the previous
aggregate is specifically wrong: **removing the sole barrier that contributed
the maximum would leave the manifest over-stamped**, claiming a vocabulary
level its content no longer needs.
5. **Ordinary repacks preserving the complete barrier content preserve the
carried version exactly.** This is the common path and it stays cheap.
**Consequently `Manifest::SCHEMA` becomes a baseline constant, not the
universally emitted version** (`manifest.rs:591`, currently
`SchemaVersion::V0` = `{0, 1}`). **Three** sites silently select it for
hashing and must stop: `bundle.rs:220` (create), `bundle.rs:724` (commit), and
the re-exported harness helper `manifest_chunk_hash` (`bundle.rs:1298`) —
which is **public API**, so it is a signature change, not an internal edit. The
major check at `bundle.rs:301` stays as-is and stays correct: it compares
`.major` only, which is exactly the v0 rule that the minor is a record, not a
gate.
### The text-projection consequence — G-minor DOES need a companion bump
**This reverses Correction 2 in §2 for the manifest, though not for op
blocks.** The reasoning there still holds where it was aimed: op-block minors
are discarded during projection, so **op-block stamping remains
projection-invisible**. But the seam above makes the manifest's aggregate
`SchemaVersion` a **carried document attribute**, and the projection cannot
derive it — that is the whole point of keeping the bundle opaque, and the bytes
may come from a future or unknown extension.
So `TextDocument` gains the carried manifest `SchemaVersion`
(it currently has no such field), which is a document-surface change:
* **`COMPANION_VERSION` 0.9.0 → 0.10.0** (`textproj/src/lib.rs:34`).
* **Corpus regeneration**, on the same reasoning as G2a's kind-production bump:
holding the version while changing the surface would leave two incompatible
grammars both claiming `(0 9 0)`.
**Not** because of op-block stamping — because the manifest schema becomes a
carried attribute. Recording the distinction because §2's "no companion bump"
is otherwise still true and will read as contradictory.
* **No migration.** Existing bundles are accepted as-is; newly emitted or * **No migration.** Existing bundles are accepted as-is; newly emitted or
repacked blocks are stamped correctly. repacked blocks are stamped correctly.
* **Scope is "all additive discriminants reachable in affected chunk * **Scope is "all additive discriminants reachable in affected chunk