414 lines
23 KiB
Markdown
414 lines
23 KiB
Markdown
# Plan — G-minor: the chunk schema minor
|
||
|
||
**Filed as** P13-S14. **Ruled** 2026-07-28: its own rung, sequenced **after
|
||
G2a and before G2b** (`spec/PLAN_GENESIS_OPS.md` §4).
|
||
|
||
**Status:** policy ratified 2026-07-28 (§4); vocabulary audit landed (§5.1,
|
||
`spec/AUDIT_GMINOR_VOCABULARIES.md`, c63258d); **epoch ladder ratified
|
||
2026-07-28** (§4, minors 2–9). **Every gate in §5 is now discharged — the
|
||
implementation contract may be drafted.** Two things it must carry that are
|
||
not yet designed: the manifest derivation seam across bundle-opaque barrier
|
||
bytes (§4), and the `binary_format.tex:2373` correction (§5.1).
|
||
|
||
> **Revision 2026-07-28.** The first draft of this plan got three things wrong
|
||
> and recommended a policy that cannot work. Corrections are marked inline
|
||
> rather than deleted, because two of them are the kind of mistake that
|
||
> regenerates: an under-scoped vocabulary, and a corpus claim asserted from a
|
||
> `grep -c` over *lines* rather than occurrences.
|
||
|
||
---
|
||
|
||
## 1. The requirement, and the evidence it has never been met
|
||
|
||
`binary_format.tex:2330` (§"Schema Versioning", the Minor bullet):
|
||
|
||
> **Minor** = additive. v0 readers verify the major only; the minor is a
|
||
> *record*, not a gate — but it is a mandatory record: a writer **MUST** raise
|
||
> the chunk schema minor when it emits any discriminant appended after the
|
||
> minor it otherwise declares, so that a decode failure on an unknown appended
|
||
> discriminant is attributable to a version skew rather than corruption.
|
||
|
||
Three facts, each read from the tree:
|
||
|
||
1. `SchemaVersion::for_major` (`bundle/src/ids.rs:204`) maps a major to a
|
||
**fixed constant** and **accepts only a major**. There is no parameter a
|
||
minor could travel through. (`V0` is `{0, 1}`; `V1`/`V2`/`V3` are `{n, 0}`.
|
||
The baselines are already inconsistent — §4 preserves that rather than
|
||
normalising it.)
|
||
2. Both writer-side staging paths derive only the major
|
||
(`testkit/src/bundle_harness.rs:32`, `textproj/src/serialize.rs:189`).
|
||
3. So every post-baseline append ships with no additive record. The
|
||
requirement's own failure mode is what the gap produces: a reader meeting an
|
||
unknown discriminant cannot distinguish a stale vocabulary from damaged
|
||
bytes.
|
||
|
||
## 2. The minor is content-addressed
|
||
|
||
`chunk_content_hash` (`bundle/src/chunk.rs:177`) pushes
|
||
`schema.canonical_bytes()` into the preimage, and that is "major then minor,
|
||
little-endian" (`ids.rs:222`). `chunk_id` dispatches to the same function
|
||
(`chunk.rs:202`).
|
||
|
||
**So raising a chunk's minor changes its `ChunkId`**, which changes the
|
||
manifest body naming it, and therefore the `ManifestId`. This is real **address
|
||
churn** — a migration and deduplication cost — and it is the main reason the
|
||
rung is not a patch.
|
||
|
||
> **Correction 1 (was P2).** The first draft framed this as tension with
|
||
> `req:format:manifest-id`. It is not. That requirement
|
||
> (`core_spec.tex:11213`) promises that *"two conforming writers committing
|
||
> **the same manifest body** at the same generation of the same document derive
|
||
> identical `ManifestId`s"*. Once a `ChunkRef` changes, the bodies are not the
|
||
> same, so the promise does not apply. Two conforming writers both following
|
||
> one normative minor derivation still agree — and the *historical* writer was
|
||
> already violating the minor MUST. Churn, not a broken guarantee.
|
||
|
||
> **Correction 2 (was P1).** The first draft claimed op-block minor changes
|
||
> reach the text projection and therefore force another `COMPANION_VERSION`
|
||
> bump. **They do not.** Operation blocks are decoded into envelopes and the
|
||
> block's physical schema is discarded during projection
|
||
> (`textproj/src/project.rs:424`); schemas are carried only for preserved
|
||
> extension chunks and the canonical base (`:454`). The committed corpus holds
|
||
> **seven** decoded `(schema 0 1)` forms in its *accepted* vectors — not the
|
||
> six the draft claimed, an error from counting matching lines instead of
|
||
> occurrences — and every one belongs to an extension chunk or a canonical
|
||
> base. **No companion bump is required for op-block stamping.** A separate
|
||
> decision to move canonical-base or extension schemas would change this;
|
||
> 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
|
||
sixteen files (corrected from 62), concentrated in `epiphany-bundle`,
|
||
`epiphany-testkit`, and `epiphany-textproj`. Most are fixtures stamping `V0`
|
||
and are unaffected; the load-bearing ones are the two staging paths.
|
||
|
||
## 3. The vocabulary inventory — the part the first draft missed
|
||
|
||
> **Correction 3 (was P1), and the reason the recommended policy died.** The
|
||
> draft reduced the problem to a per-`OperationKind` minor. An envelope also
|
||
> emits the **independent outer `OperationPayload` discriminant**, and
|
||
> `ResolveEquivocation` — appended at 3 — contains no `OperationKind` at all
|
||
> (`ops/src/payload.rs:60`). So the ambiguity lives **inside one role and one
|
||
> block**, not merely between roles, and no per-kind method can discharge the
|
||
> MUST.
|
||
|
||
`binary_format.tex:2369` states that the *only* minor-additive mechanism in
|
||
schema major 0 is appending discriminants to open vocabularies, and enumerates
|
||
them:
|
||
|
||
| Vocabulary | Appends at | Note |
|
||
|---|---|---|
|
||
| `OperationKind` | ≥ 30 | 24–27, 28–29 also took this mechanism |
|
||
| `OperationKindTag` | ≥ 30 | independent space from `OperationKind` |
|
||
| `OperationPayload` | ≥ 4 | **outer**; `ResolveEquivocation` carries no kind |
|
||
| value-layer unions closed under `req:binfmt:frozen-layout` | — | appendable only via a ratified revision of that document; also minor-additive |
|
||
|
||
Two boundaries that keep the audit finite:
|
||
|
||
* **`ChunkKind` is closed.** It has no `Registered` variant and its
|
||
discriminant enters every chunk's hash preimage, so a new chunk kind is a
|
||
format-**major** event, not a minor one.
|
||
* **`Registered` escape variants are not schema changes at all** — the wire
|
||
form is already defined. `binary_format.tex` lists fifteen carriers
|
||
(`RepairKind`, `ReanchorReason`, `PreconditionFailureReason`,
|
||
`IntegrityAnomalyKind`, `TypedObjectId`, …). Extension through an escape is
|
||
out of scope; **appending a native variant to one of those same enums is
|
||
not**, and that distinction is the audit's sharpest edge.
|
||
|
||
**The rung must inventory every append-only discriminant reachable from each
|
||
affected chunk payload**, not just `OperationKind`. For the canonical base that
|
||
means the vocabularies `MaterializedState` actually emits — `OperationEffect`,
|
||
`NoOpReason`, `PreconditionFailureReason`, `ConflictKind`,
|
||
`IntegrityAnomalyKind`, `PendingReason`, `ObjectState`, `TypedObjectId` —
|
||
which notably do **not** include `OperationKind`.
|
||
|
||
## 4. Policy — RATIFIED 2026-07-28
|
||
|
||
**A global additive epoch with content-minimal stamping.** The first draft
|
||
recommended "minor = highest discriminant emitted"; that is rejected. It cannot
|
||
represent multiple independent vocabularies in one block, and generalising it
|
||
to "highest from any vocabulary" is worse than useless — an old `OperationKind`
|
||
23 would numerically mask a newly appended `OperationPayload` 3.
|
||
|
||
The ratified rule:
|
||
|
||
1. Each additive format revision receives **one globally meaningful minor
|
||
epoch**.
|
||
2. Every appended variant is annotated with the epoch that introduced it.
|
||
3. An **envelope's** required minor is the maximum across its outer payload
|
||
variant, its primitive kind, and every nested additive variant **actually
|
||
emitted**.
|
||
4. A **block** takes the maximum required minor over its envelopes. The major
|
||
remains the independent maximum of `schema_major()`.
|
||
5. Old content keeps its major's baseline — `V0`'s existing `1`, the others'
|
||
`0`. Baselines are not normalised.
|
||
|
||
**Per-major counters are also rejected:** a mixed block can carry a new major-0
|
||
kind beside a major-2 payload, so independently numbered minor namespaces do
|
||
not compose after the block takes `max_major`.
|
||
|
||
**Maintenance risk, and how it is controlled.** The obvious objection to an
|
||
epoch table is the one this track keeps proving — hand-maintained parallel
|
||
lists go stale (four at Push 4a, six found during G2a). The control is to
|
||
**co-locate `introduced_minor` with each discriminant in an exhaustive macro or
|
||
match with no wildcard arm**, so a newly appended variant *cannot compile*
|
||
without being assigned an epoch. That assignment is an unavoidable schema
|
||
decision, not a fallible parallel list — the same reasoning that made
|
||
`operation_kind_tag_vocabulary!` safe.
|
||
|
||
### The epoch mapping — RATIFIED 2026-07-28
|
||
|
||
**These are schema-minor epochs, not companion semver numbers.** The two
|
||
numbering spaces are unrelated and must not be cross-read.
|
||
|
||
| Minor | Additive event | Variants introduced |
|
||
|---|---|---|
|
||
| 2 | M2c | `PreconditionFailureReason::ContainerNotEmpty` = 10 |
|
||
| 3 | Push 3 | `OperationPayload::ResolveEquivocation` = 3 |
|
||
| 4 | Phase-3 first tranche | `OperationKind`/`OperationKindTag` 24–27; `PreconditionFailureReason::TempoMapMalformed` = 11 |
|
||
| 5 | Pass-12 G-pass | `ReanchorReason::SameCanvasNearer` = 6; `PreconditionFailureReason` 12–13 |
|
||
| 6 | Schema-major-2 repeat revision | `OperationKind`/`OperationKindTag` 28–29 |
|
||
| 7 | Push 4a | `OperationKind`/`OperationKindTag` 30; `PreconditionFailureReason` 14–15 |
|
||
| 8 | Genesis G1 | `OperationKind`/`OperationKindTag` 31 |
|
||
| 9 | Genesis G2a | `OperationKind`/`OperationKindTag` 32–33 |
|
||
| 10 | Genesis G2b | `OperationKind`/`OperationKindTag` 34 (`SetTuningContext`) |
|
||
| 11 | Genesis G3a | `OperationKind`/`OperationKindTag` 35–38 (`CreateStaffGroup`, `CreatePartDefinition`, `CreateAnalysisLayer`, `CreateView`) |
|
||
| 12 | Genesis G3b | `OperationKind`/`OperationKindTag` 39 (`CreateMeasure`); `PreconditionFailureReason` 16–18 (`MeasureMeterMismatch`, `MeasureOutOfOrder`, `MeasureOrderUnverifiable`) |
|
||
|
||
> **Epoch 10 ratified 2026-07-28**, with G2b as the event. This is the **first
|
||
> exercise of the ladder's own growth path**: G-minor's `introduced_minor()` is
|
||
> exhaustive with no wildcard arm, so kind/tag 34 *cannot compile* without an
|
||
> epoch — the control working exactly as designed. The ladder stays monotonic
|
||
> (G2b follows G2a) and prefix-closed. Epoch assignment remains a ratified
|
||
> schema decision, never an implementer's choice.
|
||
|
||
> **Epoch 11 ratified 2026-07-29**, with G3a as the event (introducing
|
||
> `6c5e69f`, per the same discipline as the G2a correction above). One epoch for
|
||
> the whole rung's four kinds, the G2a precedent (two kinds at epoch 9) rather
|
||
> than one epoch per kind. The ladder stays monotonic (G3a follows G2b) and
|
||
> prefix-closed.
|
||
|
||
> **Epoch 12 ratified 2026-07-30**, with G3b as the event — the final rung of
|
||
> the genesis ladder (`spec/PLAN_GENESIS_OPS.md`), closing it. One epoch for
|
||
> the rung's one kind and its three `PreconditionFailureReason` appends
|
||
> together, the Push 4a precedent (epoch 7 bundled kind 30 with reasons 14–15)
|
||
> rather than a kind epoch and a reason epoch separately. The introducing
|
||
> commit is **`e64a4b7`** (2026-07-30), where kind/tag 39 enters
|
||
> `ops/src/payload.rs` and reasons 16–18 enter `ops/src/effect.rs` — G3b landed
|
||
> across six commits, and the four that follow packet 1 introduce no
|
||
> discriminant, exactly the distinction the 2026-07-28 correction below draws
|
||
> between `7df5ca1` and `55eff00`. The rung *completes* at **`d58eee8`**
|
||
> (2026-07-30), the documentation half; the hash was filled in by the pre-push
|
||
> repair that follows it, because a commit cannot cite itself and G3b closed
|
||
> the ladder, leaving no later rung to do it.
|
||
> The ladder stays monotonic (G3b follows G3a) and prefix-closed.
|
||
|
||
**The ladder is complete against the audit** — every post-baseline variant in
|
||
`AUDIT_GMINOR_VOCABULARIES.md` appears exactly once: all fifteen kind/tag pairs
|
||
through G3a, plus G3b's kind/tag 39 and its three reason appends,
|
||
`OperationPayload` 3, `ReanchorReason` 6, and all nine
|
||
`PreconditionFailureReason` appends.
|
||
|
||
**And it is monotonic in real time**, which is what makes a minor prefix-closed
|
||
(a reader declaring minor *n* supports every epoch ≤ *n*). Verified against the
|
||
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) →
|
||
repeat pair `9b5339f` (07-07) → Push 4a `2740a6c` (07-09) → G1 `3b09595`
|
||
(07-24) → **G2a `7df5ca1`** (07-28) → **G2b `13c3d2f`** (07-29) → **G3a
|
||
`6c5e69f`** (07-29) → **G3b `e64a4b7`** (07-30, the packet-1 commit where
|
||
kind/tag 39 and reasons 16–18 enter the vocabularies; the rung completes at
|
||
`d58eee8`, which introduces no discriminant). The two
|
||
events sharing 2026-07-07 are ordered correctly: the G-pass precedes the
|
||
repeat revision. The two events sharing 2026-07-29 are ordered **by ancestry,
|
||
not timestamp**: `13c3d2f` is an ancestor of `6c5e69f` (verified with
|
||
`git merge-base --is-ancestor 13c3d2f 6c5e69f`), so G2b precedes G3a. **Only
|
||
these three additive events — G2b, G3a, G3b — extend the chain past G2a**;
|
||
G-minor (`ff9bd0f`) built the epoch machinery itself and introduced no
|
||
additive variant, and P13-S17's restoration commit (`6170015`) repaired a
|
||
document's history rather than appending vocabulary, so neither belongs in
|
||
this chain.
|
||
|
||
> **Correction 2026-07-28.** G2a's introducing commit is **`7df5ca1`**, where
|
||
> kinds/tags 32–33 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
|
||
minor 0. **Baseline variants impose no additive override** — M2c's *operation
|
||
kinds* stay baseline (inside the golden-locked 0..=23) while M2c's
|
||
`PreconditionFailureReason` append requires epoch 2. Same tranche, two
|
||
vocabularies, two answers.
|
||
|
||
**Baseline boundaries are per vocabulary, even though the epoch space is
|
||
global.** The epoch answers *which revision introduced this variant*; the
|
||
boundary answers *was this variant present at that vocabulary's own lock*.
|
||
Only the second varies. M2c is the proof: it is baseline for `OperationKind`
|
||
(absorbed into the golden-locked 0..=23) and a genuine append for
|
||
`PreconditionFailureReason` (`ContainerNotEmpty` = 10, whose 0..=9 lock
|
||
predates it). So `introduced_minor` is assigned **per variant, per
|
||
vocabulary**.
|
||
|
||
### Consequent calls, all ruled 2026-07-28
|
||
|
||
* **Keep the MUST.** Its cost is lower than §2 first claimed.
|
||
* **No text-companion bump** for op-block stamping.
|
||
* **The canonical base does not move merely because its source operations are
|
||
newer.** It never emits the op-kind discriminant. It retains its current
|
||
minor while its payload uses only baseline vocabulary, and rises only when
|
||
the `MaterializedState` bytes themselves emit a later-added discriminant —
|
||
a newly introduced effect or reason variant, say. *(This replaces the first
|
||
draft's "is the base exempt?", which was the wrong binary question: the base
|
||
is neither exempt nor automatically dragged, it is content-minimally
|
||
stamped like everything else.)*
|
||
* ~~**`Manifest::SCHEMA` stays unchanged.**~~ **NARROWLY SUPERSEDED
|
||
2026-07-28 by the audit.** The original call was right about its own case and
|
||
wrong as an unconditional rule:
|
||
|
||
* **Still true** — a changed child `ChunkRef` is changed manifest *data*, not
|
||
a new manifest-layout discriminant. Its body, id, and chunk hash move
|
||
naturally, and **that alone never raises the manifest minor.**
|
||
* **Newly true** — **emitted barrier tags do.** The manifest reaches
|
||
`OperationKindTag` through `ExtensionDeclaration::edit_barriers` →
|
||
`EditBarrier::prohibited_operation_kinds` (audit §5.1, Part 2). **A
|
||
manifest containing an edit barrier naming a tag in 24–33 takes that tag's
|
||
epoch**; a manifest naming only baseline tags retains its baseline. This is
|
||
content-minimal stamping applied to the manifest like any other role — the
|
||
original call had simply not seen that the manifest emits an additive
|
||
vocabulary at all.
|
||
* **The manifest major remains 0.**
|
||
|
||
**The implementation contract must design the derivation seam across the
|
||
bundle-opaque barrier bytes**, and this is the hard part, not a detail.
|
||
`epiphany-bundle`'s entire dependency list is `epiphany-determinism` and
|
||
`zstd` — **not `epiphany-ops`, not `epiphany-layout-ir`, not
|
||
`epiphany-core`**. It holds `edit_barriers` as `Vec<u8>` by design
|
||
(`manifest.rs:283`: the barrier family "is owned by Agents C and E"), so the
|
||
layer that stamps the manifest is structurally incapable of reading the tags
|
||
that determine its minor. Three shapes are available — a new dependency edge,
|
||
a producer-supplied minor travelling beside the blob, or a
|
||
decode-and-inspect step at a higher layer that already depends on both —
|
||
and choosing among them is contract work.
|
||
|
||
**The genuinely hard case:** barrier bytes are *preserved verbatim* across
|
||
writes, including for extensions the writer does not understand. A repack can
|
||
carry a barrier blob it cannot decode, whose tags it cannot enumerate, and
|
||
whose epoch it cannot derive. **"Decode and inspect" silently does not cover
|
||
this** — it looks complete until a foreign extension is present.
|
||
|
||
### The seam — RULED 2026-07-28
|
||
|
||
**`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
|
||
repacked blocks are stamped correctly.
|
||
* **Scope is "all additive discriminants reachable in affected chunk
|
||
payloads"**, not "kinds 24–33".
|
||
|
||
## 5. What must complete before a contract
|
||
|
||
1. ~~**The vocabulary audit** (§3).~~ **DONE** — see §5.1.
|
||
2. ~~Ratify the epoch mapping.~~ **DONE** — §4, minors 2–9, complete against
|
||
the audit and monotonic in real time.
|
||
3. Confirm no third staging path has appeared beside the two in §1. *(Contract
|
||
work — cheap, but a check rather than an assumption.)*
|
||
4. Decide whether `decode_vectors.txt` moves — it is value-level, so it should
|
||
not, but that is a check rather than an assumption. *(Contract work.)*
|
||
|
||
Items 3 and 4 are checks the contract performs, not gates on drafting it.
|
||
|
||
### 5.1 The audit landed — and it is the governing inventory
|
||
|
||
**`spec/AUDIT_GMINOR_VOCABULARIES.md` (c63258d) supersedes §3's table as the
|
||
authoritative reachability inventory.** §3 is retained as the reasoning that
|
||
led here; where the two differ, the audit governs. It walks each of the nine
|
||
`ChunkKind` roles' payload encoders and classifies every variant of every
|
||
vocabulary actually emitted.
|
||
|
||
What it changes about §3:
|
||
|
||
* **The canonical base reaches more than §3 lists** — add `RepairKind`,
|
||
`ReanchorReason`, and `SpellingNominal`. Not cosmetic: `ReanchorReason` is
|
||
one of only two vocabularies in the tree with a real native append.
|
||
* **The manifest reaches `OperationKindTag` with no operation envelope in
|
||
it**, through `ExtensionDeclaration::edit_barriers` →
|
||
`EditBarrier::prohibited_operation_kinds` (`layout-ir/src/barrier.rs:392`).
|
||
§3's table is framed entirely around the operation layer and names neither
|
||
the role nor the path. A manifest declaring a barrier on `CreateInstrument`
|
||
(tag 31) needs the same stamping discipline as a block containing one, and
|
||
the manifest chunk's minor is a separate decision from any op block's.
|
||
* **`CompressionAlgorithm` is excluded** (audit §1.8): `ChunkRef` transport
|
||
metadata, not a discriminant emitted by the payload `SchemaVersion` governs,
|
||
and deliberately outside the chunk-identity preimage. Recorded so it is not
|
||
reopened.
|
||
* **`binary_format.tex:2373` is stale narrative** — "append at ≥30" with a
|
||
history stopping at 29, while 30–33 are taken. The normative tables
|
||
(`:1443`, `:1526`) are current and authoritative. **The `.tex` correction is
|
||
owed by the G-minor implementation**, which must edit that paragraph
|
||
regardless, since it defines the mechanism the rung implements.
|
||
|
||
*Related: `spec/AUDIT_GMINOR_VOCABULARIES.md`,
|
||
`spec/PASS13_CANDIDATES.md` (P13-S14), `spec/PLAN_GENESIS_OPS.md` §4,
|
||
`spec/binary_format.tex` §"Schema Versioning" / §"What ``Additive'' Means
|
||
Here".*
|