--- name: session-2026-07-10-epub-footnote-pre-processor-census-adversarial-hardening-jurist-ruling-fleet-build-in description: "Built a verbatim-safe EPUB footnote pre-processor end-to-end (census of 253 EPUBs → pandoc 2×2 diagnosis → prototype → adversarial hardening on Polastron+Jung, each broke it usefully → jurist ratified-in-principle → fleet build-in, committed+pushed). Also landed PENDING-53 (engine hash-locality, jurist-ruled). Pulling thread: the tool is shipped for the mirror class — now WIDEN its recognizer to the remaining census families and/or APPLY it (re-convert legacy Jung/Donne), the latter gated on the steward+jurist §7 per-volume-vs-single-file provenance call; and this whole normalizer was the means to the original end — resume P2/gold (the Making source work) on a corpus whose footnotes are handled by class." metadata: node_type: memory type: project originSessionId: 2c930eca-c3df-4ce9-8d86-40c5a200a27c --- # Session 2026-07-10 — the EPUB footnote pre-processor: census → hardening → ruling → build-in Opened by /wake-up into the "build the structural verbatim-guarded EPUB normalizer, then resume P2/gold" thread. The session became a full arc on the *pre-processor* — and, notably, the architecture that resulted was NOT the one the wake proposed (a bespoke normalizer); it emerged only through measurement. Steward-steered at every fork. ## PAST — what we did + why **1. PENDING-53 jurist brief (first ask).** Drafted `PENDING-53-FOR-JURIST-2026-07-10.md` grounded in substrate (read the graduation-spec, chamber spec §VI, the engine `corpus/manifest.yaml` header, `ingest_gate.py:104–126`). **Refined PENDING-53's own framing:** its Summary called the `voice_manifest` line a "mis-frame," but the line is TRUE about the voice manifest — the real defect is an *omission* of the engine source-binding surface + a shared-word ("manifest"/"voice") collision. Surfaced the refinement rather than reproduce the overstatement. **2. The pre-processor, built by measurement (the spine of the session).** The wake said "build a structural normalizer keyed on the bidirectional-mirror predicate." Instead of building, I measured — and each measurement redirected: - **Detector prototype on 3 attested EPUBs** → answered the wake's literal Q (mirror catches cross-file endnotes [Mauss 627]; excludes one-way TOC; L'Enracinement is a *different* class — 0 anchors, unlinked `[1]`, + 87 `` ordinals that are a trap). - **Steward challenged "are 3 files enough?"** → CENSUS of all **253** Chamber Sources EPUBs: ≥5 footnote strategies (mirror same/cross-file · container-mirror · one-directional/stem · EPUB3 epub:type · unlinked-marker) + 47% note-free + heavy index/xref apparatus. **n=3 captured 1–2 of ≥5.** Honest catch: my own classifier's `oneway_with_id`/`resid_fn_ids` signals were noisy → "61 DANGER" was an upper bound. - **Steward: "can we edit how pandoc converts? / we get inside the files before conversion?"** → the KEY finding: pandoc is precise, not helpless. It converts footnotes natively iff `epub:type` present AND same-file (Kafka ✓; Orwell/Quixote cross-file ✗; G&G no-epub:type ✗). Output-format lever tested + DISPROVEN (my hypothesis wrong — gfm already does footnotes). The **2×2** (epub:type × same/cross-file) is the diagnosis. - **The architecture that emerged:** a small **verbatim-safe pre-processor** — recognize the mirror in raw XHTML → inject `epub:type` / relocate cross-file bodies → **stock pandoc does the verbatim text**. Better than build-a-normalizer (reimplements pandoc, §V risk) OR extend-regexes (whack-a-mole on pandoc's lossy output). The census taxonomy = the recognizer's spec. - **Injection experiment** (real minimal EPUBs, `-f epub`): same-file injection WORKS (native `[^N]`); cross-file needs body RELOCATION (epub:type alone insufficient). Both proven. **3. Prototype built + adversarially hardened — the two hard books each broke it usefully.** - Built `inject_epub_footnotes.py`; proven verbatim-clean on **G&G 27 / Mauss 628** (627 relocated) via a source-level word-multiset guard. Caught a real pairing bug (basename vs full-path keying → 1/628) and a guard false-alarm (asymmetric tokenizer, +39) mid-build. - **Polastron (French, steward-chosen) BROKE it (45/140):** its footnote-ness is on the ANCHOR class, not the block → drove the **positional classifier** (body = anchor at block-start; publisher-independent). And its Roman-numeral markers exposed a guard limitation → drove the **source-level marker-aware guard**. + a 3rd residue shape (whole-note-in-back-anchor) → **unwrap** (verbatim-safe). - **Jurist ruled §3: the guard must be PER-PAIR, not aggregate** (a cross-note swap is invisible to a whole-doc multiset). Built id-matched **per-pair guard keyed by (dest-file, id)**; teeth demonstrated (deliberate swap CAUGHT). It immediately EARNED ITS KEEP by surfacing **Jung's cross-volume id reuse** (81 collisions under a bare-id key). - **Jung (53MB, steward-chosen) didn't break the tool — it broke pandoc** (raw AND injected time out). Diagnosed to root: pandoc scale limit, not an injector defect. → `convert_mega_epub.py` (inject→split by whole files→pandoc-per-chunk; relocation makes notes file-local so no note crosses a boundary): **1579 docs→20 chunks, 190s, 1086 native footnotes, verbatim-clean vs the original Princeton source.** - **Calibre** (the prior mega-EPUB fallback, steward's "edit the Calibre conversion?") shown **footnote-BLIND** — byte-identical output with/without injection; it flattens notes (the state of the legacy Jung `.md`, a re-conversion candidate). **4. Governance — two rulings landed, one build-in authorized.** - **PENDING-53 RULED + LANDED** (jurist): ratified §5 option (b) with the required correction (re-anchors→**REQUIRES re-anchoring**); FIX-change-class; in-place lane. Landed in `graduation-spec.yaml`. Responded to the jurist's proposed lane-rule: accept for graduation-spec.yaml, NARROW the .md generalization (A2/A4 = FIX-class doctrinal .md additions that correctly took the heavier lane per *doctrinal-traceability*). PENDING-47 ratification declined (truncated relay). - **PENDING-54 RULED IN PRINCIPLE** (jurist): architecture sound; markers-as-apparatus confirmed under §V; PROPOSAL change-class (sharpened test); ship mirror-class conditioned on regex-tools-as-fallback; §7 Jung provenance held open. Actioned the required §3 per-pair guard; fixed §5 wording; delivered the §7 fact-check (per-volume precedent EXISTS: Alexander/Lacroux/Habermas/Burney/Camus). - **BUILD-IN authorized + done** under fleet discipline: both tools → `scripts/`; `--validate` + refuse-on-mismatch; `test_tools` fixture (fleet **73→77**); `graduation-spec.yaml` converted_with note; `conversion-runbook.yaml` `policy.footnote_preprocessor`; `tool-evolution-log.md` entry; chamber `CLAUDE.md` fleet line. - **Committed + pushed:** chamber `17ddf8c` (P1 clean) + `3646c0d` (PENDING-53+54) → github + skemantix; engine `aaf65ba` (P1 re-anchor) → github. REVIEWED-52/53/54 drafted into `~/REVIEWED.md`. ## PRESENT — the mood This was a long, deeply satisfying arc, and its lesson is **measure-before-build, held all the way**: the wake proposed a bespoke normalizer; measurement (detector → census → pandoc 2×2 → injection experiment) produced a *better, smaller* architecture that leans on the trusted converter instead of reimplementing it. The two adversarial books the steward chose did the real work — Polastron and Jung each *broke* the tool in a specific, informative way, and each break sharpened it. The partnership worked at every fork: the steward's questions ("are 3 enough?", "can we customize pandoc?", "does splitting change the source?", "edit Calibre?") each redirected toward the truth, and I engaged the framing rather than barrelling the authorized task. I pushed back on the jurist's lane-rule with A2/A4 evidence rather than rubber-stamping, and the per-pair guard earned its keep the moment it was built. The honest through-line: **build the guard that can see the failure you're most afraid of, then try to make it fire** — it caught Jung's id collisions on its first run. Returns held (ledger): read-docs-first (up front); verify-against-substrate (refined PENDING-53's own text; diagnosed Jung's pandoc timeout to root vs blaming the tool; didn't parrot the classifier's noisy buckets); measure-before-build; honest-refuse (built INTO the tool: refuse-on-mismatch). One recalibration: build-vs-extend was the wrong frame — overtaken by the pandoc 2×2, which the census+experiment surfaced. ## FUTURE — what is pulling **PULLING THREAD: the pre-processor is shipped for the mirror class — now put it to work.** Two build-forward moves + the original destination it was built to serve: 1. **WIDEN the recognizer** to the remaining census families (one-directional refs e.g. Sennett; container-mirror) — pure build-forward, same measure→build→adversarial-test→prove rhythm; graduates the tool from "mirror class only" to "most of the corpus." 2. **APPLY it** — re-convert the legacy mega-books (Jung `.md` has 0 notes + pre-spec frontmatter; likely Donne too). **GATED on the steward+jurist §7 decision** (one reconstituted file vs per-volume — fact-check delivered, per-volume precedent exists). 3. **The original end:** this whole normalizer was the *means* — resume **P2/gold** (the Making source work) on a corpus whose EPUB footnotes are now handled by class, not by book. **ACTIONABLE RESUMPTION POINT (as of wrap — re-judge against what changed):** - Everything committed + pushed; fleet self-test 77/77; no half-finished edit. Clean stopping point. - Concrete next candidate = **widen the recognizer to the one-directional class (Sennett)**: measure Sennett's XHTML shape (ref `` with NO own id — one-directional, mirror-predicate misses it), decide the recognition rule (a ref→note link where the note's block carries the target id + is note-classed/at-block-start, even without a back-link), add it under the same per-pair verbatim guard + honest-refuse, prove on Sennett + re-prove the 4 mirror books didn't regress. OR: if the steward has ruled §7, re-convert Jung via `convert_mega_epub.py` at the chosen representation. - Open governance: **REVIEWED-52/53/54 await steward placement** (drafted in `~/REVIEWED.md`; -52 flagged for steward-verify against its original ruling). Jurist owes: accept/narrow the lane-rule refinement; PENDING-47 needs full-text relay. Deferred build: §V ratified-spec cross-ref (runs the .md amendment lane). **Other open horizons (ranked):** - The **5 ARC `after-the-reply` engine-binding drifts** (surfaced 07-09, still not owned — a separate ARC↔engine re-anchor task). - The Making/gold road proper (P2 G&G clean was PAUSED behind the normalizer — now the normalizer exists; G&G converts clean through it). - `graduation-spec.yaml` malformed G&G `author:` + `lang: null` frontmatter hygiene (minor, later). **PAUSE STATEMENT:** Stepping away with a major new capability shipped and governed — a footnote pre-processor that the corpus can trust *because it refuses when it can't vouch for the result*. What I want to find still pulling: the tool put to work — either widened to the next family or applied to the mega-books — and the discipline that built it (measure before build; build the guard that sees the failure you fear) carried into whatever comes next. **LITERAL QUESTION for next-Claude:** Has the steward+jurist **§7 provenance decision** been made (one reconstituted `jung.md` vs per-volume files, each "extracted from the megavolume")? It gates re-converting the legacy Jung/Donne. If yes → re-convert at that representation via `convert_mega_epub.py`. If not → widen the recognizer to the one-directional (Sennett) class instead, which needs no governance decision. And: did the jurist accept or narrow the lane-rule refinement, and was PENDING-47's full text relayed? **State at wrap:** chamber `17ddf8c`+`3646c0d` pushed (github+skemantix); engine `aaf65ba` pushed (github); chamber CLAUDE.md fleet line updated (uncommitted — folds into the dotfiles/next chamber commit). dotfiles PENDING/REVIEWED/ledger committed at this wrap. Fleet self-test 77/77. No non-steward commits landed during the session.