Handoff for the machine move, and two stale ledger openers
spec/HANDOFF_2026-08-07.md: state of the spec / Pass-13 / format-epoch
thread at be244df, the live constraint that no bundle may carry a
canonical base until P13-S27 lands, the S28 -> S27 -> S16 chain with
S27's three inherited obligations, the working agreements that are not
derivable from the code, and the environment notes the other machine
needs (xelatex not pdflatex, the 1.95.0/1.85 toolchain pins, the
cargo fmt --all trap and why its --check form is safe).
Section 2 covers the parallel editor/T4 thread and is explicitly
bounded: those files were out of bounds for this session all along, so
it records only what shared git history shows plus leads to verify, and
says plainly that it is not a substitute for that session's own handoff.
It does name the one place the threads can collide — the canonical-base
interval — which neither side can see from its own side.
Ledger: P13-S27 and P13-S28 both still opened with "open" while their
resolutions sat further down the cell. S27 is UNBLOCKED and
dispatchable; S28 is IMPLEMENTED. The cells are appended to rather than
rewritten, so an opener can lag the truth by several rungs; the handoff
records that as a reading hazard.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QjsEnYhm1gPpf6ii2iFxFV
This commit is contained in:
parent
be244df6a0
commit
ada751cc9f
|
|
@ -0,0 +1,292 @@
|
|||
# Handoff — 2026-08-07
|
||||
|
||||
Written at `be244df`, pushed to `origin/main`. Working tree clean.
|
||||
|
||||
**Scope note, stated first because it bounds everything below.** This handoff is
|
||||
written from the **spec / Pass-13 / format-epoch** session. A second session has
|
||||
been committing to this repository in parallel, and its files were placed
|
||||
explicitly out of bounds for this one — `spec/PLAN_EDITOR_APP.md`,
|
||||
`spec/CONTRACT_EDITOR_*.md`, `spec/ANALYSIS_*.md`, `spec/DRAFT_T4_FIXTURE_RECIPE.md`,
|
||||
the whole `spikes/` tree, `crates/epiphany-editor-gui/**`,
|
||||
`crates/epiphany-render-svg/**`, `crates/epiphany-glyphs/**`, and the root
|
||||
`Cargo.toml`. **§2 is therefore not an assessment of that work.** It is what is
|
||||
visible from outside the boundary, plus a precise list of what to ask that
|
||||
session for. Do not treat §2 as authoritative the way §1 is.
|
||||
|
||||
---
|
||||
|
||||
## §1. This thread — spec, Pass 13, format epoch
|
||||
|
||||
### 1.1 What just landed
|
||||
|
||||
The container format major became **1**, and the header now carries a
|
||||
provenance **epoch** rather than only a wire-layout discriminator.
|
||||
|
||||
| Commit | What |
|
||||
|---|---|
|
||||
| `79a2507` | P13-S27 contract (DRAFT, blocked) |
|
||||
| `818a16f` | P13-S28 filed as the critical path |
|
||||
| `8bb917d` | Format-epoch contract ratified |
|
||||
| `973297e` | Amended pre-dispatch: pin 3c, touch rows 10/11, gate 8 |
|
||||
| `bc06706` | **The rung**: container major 1 |
|
||||
| `be244df` | Pin 3b's projection refusal made symmetric |
|
||||
|
||||
`FORMAT_MAJOR` 0 → 1, `FORMAT_MINOR` 1 → 0. `FixedHeader::decode` classifies
|
||||
three ways through a named `FormatEpoch` (legacy / current / unsupported) carried
|
||||
on the header, so major 0 is decoded **deliberately as legacy**, not rejected.
|
||||
|
||||
**Why the major carries it.** The header never changes after creation
|
||||
(`req:format:fixed-header-integrity`), so the epoch is a property of the file's
|
||||
creation that no later commit can confer. That immutability is exactly what
|
||||
disqualified `FORMAT_MINOR` as a provenance field — a legacy bundle committing a
|
||||
freshly validated base keeps its old minor forever — and exactly what makes the
|
||||
major sound. Old readers already fail closed on an unknown major, so that half
|
||||
needed no new mechanism.
|
||||
|
||||
Normative text: `core_spec.tex` §"The Container Epoch" +
|
||||
`req:format:container-epoch`; `binary_format.tex` header table (0.17.0);
|
||||
`text_projection.tex` `req:textproj:roundtrip` (0.14.0).
|
||||
|
||||
### 1.2 The live constraint — read this before touching bases
|
||||
|
||||
**Until P13-S27 lands, no bundle anywhere may carry a canonical base** — not in
|
||||
production, not in tests, not in the conformance suite. Four rules compose to
|
||||
that: `Bundle::create` already rejected a base-bearing manifest; matrix row 3
|
||||
refuses committing one into major 0; row 6i refuses committing one into major 1;
|
||||
rows 2 and 5i refuse *opening* one in either epoch.
|
||||
|
||||
Consequences you will hit immediately if you forget:
|
||||
|
||||
- A base-bearing `Bundle` is **unconstructible**. Fixtures must be hand-built
|
||||
images — see `craft_image` / `craft_image_with_base` in
|
||||
`crates/epiphany-bundle/src/bundle.rs`, which write header, manifest, and
|
||||
superblock bytes directly through the public `encode()`s.
|
||||
- Two refusal tests are unit tests over `Manifest`, not end-to-end, for exactly
|
||||
this reason, and say so in their doc comments.
|
||||
- Conformance criterion 4's canonical-base wiring is **suspended**, marked in
|
||||
`crates/epiphany-testkit/src/roundtrip.rs` with a comment naming P13-S27. Its
|
||||
serialize → load → decode → reserialize cycle still runs via a direct
|
||||
`ChunkRef` read. **Do not "restore" it by re-homing the snapshot to
|
||||
`acceleration_snapshots`** — nothing in `open` or `verify_canonical_chunks`
|
||||
reads that field, so it would verify nothing while looking like coverage. Pin
|
||||
3c forbids it explicitly.
|
||||
|
||||
Three errors exist, none of which may degrade to read-only:
|
||||
`LegacyBundleHasCanonicalBase` and `LegacyBaseIntroductionRejected` (permanent,
|
||||
both name **repack**), and `ReductionAuthorityUnavailable` (temporary, names
|
||||
**P13-S27**, must **not** say repack — a major-1 container is already the right
|
||||
epoch).
|
||||
|
||||
### 1.3 Text projection lost a capability, deliberately
|
||||
|
||||
Base-bearing documents no longer round-trip through text. All three sides refuse
|
||||
— projection, parsing, and a dedicated `SerializeError::CanonicalBaseUnsupported`.
|
||||
None of the three pre-existed; an earlier contract draft wrongly claimed
|
||||
serialization already refused.
|
||||
|
||||
- `COMPANION_VERSION` → **0.14.0** (`crates/epiphany-textproj/src/lib.rs`).
|
||||
- Corpus rebuilt: **20 vectors**, 10 accept / 10 reject, **ten** rejection
|
||||
classes, `canonical_bases` reach **2 → 0**.
|
||||
- The base spelling survives only as the `canonical_base_present` reject vector,
|
||||
built through the crate-private `render_text_document`. **That function is the
|
||||
one intentional hole in the refusal** and exists solely so a negative vector
|
||||
can contain the spelling it asserts is refused. Keep it private.
|
||||
|
||||
### 1.4 Chain state
|
||||
|
||||
```
|
||||
format-epoch rung (LANDED) → P13-S27 (UNBLOCKED, dispatchable) → P13-S16 (blocked on S27)
|
||||
```
|
||||
|
||||
**P13-S27** — `spec/CONTRACT_P13S27_REDUCTION_AUTHORITY.md`, still DRAFT, now
|
||||
dispatchable. Its pin 2a was **resolved from outside** by the format rung's pin
|
||||
8, exactly as pin 2a's own prohibition required: *legacy bases are refused by
|
||||
container epoch, never by version arithmetic.* The number collision it feared —
|
||||
a pre-S27 base carrying `1` versus a rebuilt S16 base carrying `1` — never has to
|
||||
be adjudicated, because the two can never meet: the former exists only in a
|
||||
major-0 container, refused before any version is compared.
|
||||
|
||||
S27 **owes three inherited items**, written as required tests in its contract so
|
||||
they cannot be discharged in prose:
|
||||
|
||||
1. Convert **both** interim refusals (open-side and commit-side) to real
|
||||
capability validation. Converting one leaves the hole the format rung's own
|
||||
review found.
|
||||
2. **M8's deferred laundering demonstration** — with pin 3b's text refusal
|
||||
removed, show that a base-bearing document whose raw version happens to match
|
||||
the authority serializes into a major-1 container indistinguishable from a
|
||||
validated one. Never observed, only reasoned about.
|
||||
3. **Pin 3c's two suspended conformance assertions** — `verify_canonical_chunks`'s
|
||||
base branch with its `base.hash != base.root.hash` cross-check, and the
|
||||
reopened manifest carrying the base. Restoring them means **deleting the
|
||||
suspension marker**; if the marker is still in the tree when S27 reports, the
|
||||
restoration did not happen.
|
||||
|
||||
**P13-S16** — `spec/CONTRACT_P13S16_PROJECTION.md`, complete and ratifiable as a
|
||||
plan, blocked purely on sequencing now rather than on an open question, for the
|
||||
first time since it was filed.
|
||||
|
||||
### 1.5 Other open Pass-13 candidates
|
||||
|
||||
Ledger: `spec/PASS13_CANDIDATES.md`. Ten rows still read **open**: S4, S8, S9,
|
||||
S16, S18, S19, S23, S24, S25, S26. Beyond those, **S27 is UNBLOCKED and
|
||||
dispatchable** and **S28 is IMPLEMENTED** — both status openers were corrected in
|
||||
this handoff commit, because each still began "open" while its resolution sat
|
||||
further down the cell. Worth knowing the shape of that staleness: the ledger's
|
||||
status cells are appended to rather than rewritten, so **the opener can lag the
|
||||
truth by several rungs**. Scan the whole cell, not its first clause.
|
||||
|
||||
**S8 is the one needing a ruling, not an implementation.** It is a semantic fork:
|
||||
two byte spellings are currently both accepted. Normalizing one on encode
|
||||
violates `req:binfmt:decode-vectors`' injectivity rule, so the repair must either
|
||||
**reject one spelling** or **explicitly retain both as distinct canonical
|
||||
values**. Nothing else in the ledger is waiting on a decision like this.
|
||||
|
||||
### 1.6 Working agreements that must survive the move
|
||||
|
||||
These were established through this session and are not derivable from the code:
|
||||
|
||||
- **Contracts are ratified through adversarial review rounds before dispatch,
|
||||
and frozen after.** A defect found during execution is **reported, not patched
|
||||
in place** — that is how pin 3c came to exist rather than being absorbed
|
||||
silently.
|
||||
- **Subagent claims are verified independently before anything is committed.**
|
||||
This session's agents were substantively accurate but got three things wrong:
|
||||
one misattributed four test failures to "a concurrent session" (it was this
|
||||
session's own spec edit), one missed a fifth failing suite entirely, and the
|
||||
implementation left pin 3b's guard on the unreachable path. All three surfaced
|
||||
only because the work was re-run rather than relayed.
|
||||
- **Mutation-first.** Every regression is verified by re-introducing its bug and
|
||||
*observing* the failure. Reasoning that a mutation would fail signs nothing. A
|
||||
**compile error is not a test failure**.
|
||||
- **Staging is explicit, never `git add -A`**, and the touch table is the staging
|
||||
allowlist — which is why a file that must change but isn't listed silently
|
||||
drops out of the commit.
|
||||
|
||||
### 1.7 The recurring defect, named so it can be watched for
|
||||
|
||||
**Searching one spelling and concluding about all sites.** Four instances in this
|
||||
rung alone:
|
||||
|
||||
- grepped a **hex-encoded** corpus for plaintext, got 0, nearly reported it
|
||||
unaffected;
|
||||
- grepped `(0 13 0)`, missed a normative `version~0.13.0`;
|
||||
- searched for `ReductionAlgorithmVersion(` **constructor** calls to prove no
|
||||
writer existed — a *propagating* path never constructs;
|
||||
- ran `grep … | head -14` and asserted a universal negative from truncated output.
|
||||
|
||||
Before believing a zero, ask **what this search would miss if the claim were
|
||||
false**, and never pipe through `head` when the conclusion is a universal
|
||||
negative.
|
||||
|
||||
**And its sibling:** a guard placed on the path the spec sentence *names* rather
|
||||
than on every path a caller can reach. In `be244df` the named path
|
||||
(`document_from_bundle`) was additionally the unreachable one, so the only
|
||||
reachable entry point was unguarded — and the new corpus vector was being built
|
||||
straight through the hole.
|
||||
|
||||
---
|
||||
|
||||
## §2. The other thread — editor / T4 spike (BOUNDED VIEW ONLY)
|
||||
|
||||
**I could not read any of this thread's files.** What follows is inferred from
|
||||
shared git history and from notes carried in earlier sessions. Verify all of it
|
||||
against that session before relying on any of it.
|
||||
|
||||
Visible in shared history: a `f33673d` merge of `editor-t4-packet2b` ("the T4
|
||||
spike's Round 2 text packet"), `e2979df` ("Packet 2B: both candidates consume the
|
||||
frozen text apparatus"), and `694d135` ("Packet 2B apparatus: the neutral
|
||||
candidatekit and the check-5 oracle"). That reads as a **toolkit-selection spike
|
||||
running two candidates against a neutral harness** with a frozen text apparatus
|
||||
and scored checks.
|
||||
|
||||
Carried notes (**stale, from before this session**; treat as leads only):
|
||||
|
||||
- `spec/PLAN_EDITOR_APP.md` holds the rulings and a tranche ladder T1a → T4, with
|
||||
all three Ruling-A prerequisites (W1/W2/W3) discharged and **T4 the toolkit
|
||||
spike** as the next item.
|
||||
- An `epiphany-editor-core` crate exists with a hit-test contract and an edit
|
||||
loop, gated by a conformance `[7c]` UI-seam item.
|
||||
- `epiphany-engrave` + `epiphany-render-svg` carry a renderer-vs-stub scaffold.
|
||||
- Golden PNGs live in `crates/epiphany-editor-gui/goldens/`, gated in CI by the
|
||||
`golden-gate` feature.
|
||||
|
||||
**Ask that session for**, since none of it is visible from here: the current T4
|
||||
candidate scores and whether a selection was made; whether Packet 2B closed or
|
||||
has open rounds; what `spikes/` currently contains and whether any of it is meant
|
||||
to graduate into `crates/`; and whether anything there depends on the
|
||||
canonical-base interval in §1.2 — that is the one place the two threads can
|
||||
collide, and neither side can see the collision from its own side.
|
||||
|
||||
**One known cross-thread interaction, already handled:** this thread's spec edits
|
||||
move hardcoded counts in `crates/epiphany-testkit/tests/requirement_labels.rs`
|
||||
(`CORE_REQUIREMENT_COUNT`, `SUITE_REQUIREMENT_COUNT`, `SUITE_LABEL_COUNT`). Any
|
||||
`.tex` requirement added by either thread breaks four tests there until those
|
||||
constants move. It is in no contract's touch table.
|
||||
|
||||
---
|
||||
|
||||
## §3. Reproducing the environment on the other machine
|
||||
|
||||
### 3.1 Toolchains
|
||||
|
||||
`.github/workflows/ci.yml` pins **stable 1.95.0** for lint/GUI/conformance and
|
||||
**1.85** as the MSRV floor. The MSRV job runs
|
||||
`cargo test --workspace --exclude epiphany-editor-gui --all-targets` plus
|
||||
`--doc`; excluding the GUI crate is what keeps `resvg`'s raster stack out of the
|
||||
MSRV dependency closure.
|
||||
|
||||
### 3.2 LaTeX — the one non-obvious dependency
|
||||
|
||||
The spec builds with **`xelatex`, not `pdflatex`**. `pdflatex` fails immediately:
|
||||
`fontspec` requires XeTeX or LuaTeX. Build with:
|
||||
|
||||
```
|
||||
cd spec && latexmk -xelatex -interaction=nonstopmode <doc>.tex
|
||||
```
|
||||
|
||||
`core_spec.tex` needed a **second pass** before newly added labels resolved;
|
||||
check the log for `undefined references` rather than trusting the first exit
|
||||
code. Six PDFs are tracked in git and must be rebuilt whenever their `.tex`
|
||||
changes.
|
||||
|
||||
### 3.3 GUI build deps (Linux)
|
||||
|
||||
`libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev
|
||||
libssl-dev`.
|
||||
|
||||
### 3.4 The formatting trap
|
||||
|
||||
**Never run `cargo fmt --all`** while a parallel spike workspace is present — it
|
||||
reaches the root crates through path dependencies and reformats across
|
||||
workspaces. Use `cargo fmt -p <crate>` for writes. Note the asymmetry: CI runs
|
||||
`cargo fmt --all -- --check`, and the **check** form is safe; it is the *writing*
|
||||
form that crosses.
|
||||
|
||||
### 3.5 Green baseline to reproduce first
|
||||
|
||||
```
|
||||
cargo test --workspace # 1570 passing, 0 failed
|
||||
cargo clippy --workspace --all-targets -- -D warnings # clean
|
||||
cargo fmt -p epiphany-bundle -p epiphany-textproj -p epiphany-testkit --check
|
||||
```
|
||||
|
||||
If `cargo test --workspace` is not 1570 on arrival, reconcile that **before**
|
||||
starting new work — this thread's whole method depends on a known-green baseline
|
||||
to mutate against.
|
||||
|
||||
---
|
||||
|
||||
## §4. Recommended first moves after the move
|
||||
|
||||
1. Reproduce the green baseline in §3.5.
|
||||
2. Get the other session's own handoff for §2. This one is deliberately not a
|
||||
substitute for it.
|
||||
3. **Dispatch P13-S27.** It is the only unblocked item on the critical path, its
|
||||
contract is complete, and it discharges the three inherited obligations plus
|
||||
ends the no-canonical-base interval that currently constrains every other
|
||||
piece of bundle work.
|
||||
4. Then **P13-S16**, which becomes dispatchable the moment S27 lands.
|
||||
5. Take a ruling on **S8** (reject one spelling vs. keep both canonical) whenever
|
||||
convenient — it needs a decision, not an implementation, and it blocks nothing
|
||||
in the meantime.
|
||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue