293 lines
14 KiB
Markdown
293 lines
14 KiB
Markdown
# 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.
|