diff --git a/PENDING.md b/PENDING.md index 29daf6e..ccab1ea 100644 --- a/PENDING.md +++ b/PENDING.md @@ -6769,3 +6769,68 @@ for head, start, end in wd.item_spans(text): **Files affected:** `scripts/governance-mcp.py` (`t_item`, its controls). **Awaiting:** Steward authorization. + +## PENDING-176 — Every PDF routes to V-SCAN by file extension, so the verbatim gate abstains on 14 born-digital canonicals +**Date:** 2026-08-31 +**Tag:** [HARDENING] +**Summary:** `verify_body_conservation._tier_from_source()` decides the evidence tier from the file *extension* — `.pdf` → `V-SCAN`, unconditionally — so the body-conservation gate ABSTAINS on every PDF-sourced work, including the born-digital ones where ground-truth text exists and the deterministic V-TEXT check is available. +**Rationale:** The constitution defines the tier by the work's *origin* (V-TEXT = born-digital, source IS ground truth; V-SCAN = no ground-truth text → honest abstain). The mechanism derives it from a filename suffix. For a born-digital PDF the two disagree, and the disagreement is silent and always in the permissive direction: the file graduates with no verbatim check while the gate reports an honest-looking `ABSTAIN`. This is the gate's self-assessment being truthful for scans and wrong for the class it cannot distinguish — and it is invisible precisely because abstention looks like correct behaviour. +Measured exposure: of 63 PDF-sourced canonicals in Chamber Sources, **14 are born-digital** and 49 are genuine scans. The 14 are the same population the ratified spec already names in v2.9.1's "0 of 14 born-digital Chamber-Sources PDFs are two-column" — reproduced here independently by re-running `classify_pdf_origin.py` over the manifest. +The repo **already owns the missing signal**: `classify_pdf_origin.py` exists, was ratified-adjacent under REVIEWED-83 A1, and its own docstring says it is "NOT wired to any gate." Classifier and gate are both present and not connected. +⚠ One of the 14, `orthotypographie-vol-1-a-f-lacroux`, independently carries 48 sites of mid-word split-diacritic damage (`ambigu ï té`) — a born-digital PDF whose verbatim gate abstained, holding exactly the defect class the gate exists to catch. Suggestive, not proof of causation; the other 13 have not been examined. +**Options:** + (a) Wire `classify_pdf_origin.py` into `_tier_from_source()`: `.pdf` → V-TEXT when born-digital, V-SCAN otherwise. Requires deciding the V-TEXT *reference converter* for PDFs — v2.6.0 names `pandoc -t markdown-smart`, which has no PDF reader. `pdftotext` is the obvious candidate but is a **new declared reference** for a format class, i.e. change-class PROPOSAL, not FIX. + (b) Leave routing, but make the abstain **loud**: report `ABSTAIN (born-digital — a check was available and not run)` so the silence stops reading as coverage. Cheap, honest, no new reference converter. + (c) Both: (b) now, (a) behind the reference-converter ruling. +**Recommendation:** (c). (b) is a same-day FIX that removes the false reassurance; (a) is the real repair but cannot land until the PDF-class reference converter is ruled, because a reference chosen by the executor would re-encode a convention independently of the spec — the drift the amendment process names. +**Files affected:** `scripts/verify_body_conservation.py` (`_tier_from_source`, `verify_candidate`); `scripts/classify_pdf_origin.py` (wiring); `_curation/graduation-spec.yaml` (`body_conservation:` declared data); `docs/chamber-library-specification.md` §Tiering & Fence if (a). +**Awaiting:** Steward authorization. + +## PENDING-177 — The runbook's PDF recipe selects a backend that splits words, and docling flattens curly quotes on the whole PDF class +**Date:** 2026-08-31 +**Tag:** [PROPOSAL] +**Summary:** Two findings from the first measured run of the `pdf_textlayer` lane. (i) The recipe as written (`docling --to md`) uses docling's default backend, which splits diacritics mid-word — `Türk` → `T ü rk`, 16/16 sites in a 20-page pilot; `pypdfium2` renders all 16 correctly. (ii) *Every* docling backend flattens curly quotation marks to spaced straight apostrophes, which engages REVIEWED-67's front-end eligibility clause for the PDF class. +**Rationale:** On (i): this is the same mid-word-space Tier-3 damage that the 2026-07-19 interim ruling barred docling's EPUB path for. That ruling recorded the defect as a property of docling's *EPUB* parse and left the scan/PDF tiers with docling. The defect is **not EPUB-specific**. Because the runbook names no backend, the damaging default is what the recipe selects, and `verify_conversion` returns 5/5 PASS on the damaged output — no existing gate sees it. +On (ii): REVIEWED-67 ruled that *a front-end that silently applies prophylactic Tier-2 / Tier-3 alteration to a format class is not eligible*, census-before-adoption, boundary by mechanism. Curly→straight quote flattening is uncatalogued Tier-2, applied prophylactically to the whole class, and is backend-independent (measured across four backends), so it is a docling property rather than a parser artifact. The corpus norm is the opposite: **1061 of 1297 canonicals carry curly quotes**; only 222 are straight-only. Adopting docling for PDFs as-is would silently move every future PDF work into the minority class and destroy the opening/closing distinction — load-bearing in a book that quotes period treatises throughout. +This is a *prospective* finding, not a blocker report: it is raised before any PDF work graduates on this lane, which is what census-before-adoption asks for. +**Options:** + (a) Amend the runbook recipe to `docling --to md --pdf-backend pypdfium2 --image-export-mode placeholder` and declare quote-flattening as a catalogued Tier-2 normalization in `graduation-spec.yaml` `normalize:`. Cheapest; but cataloguing an alteration to make a front-end eligible is exactly the move REVIEWED-67 was written to prevent, unless argued and ruled. + (b) Recipe fix + a post-conversion quote-restoration step verified against the source's own quote positions. Restores fidelity; needs a new tool and its negatives. + (c) Recipe fix + accept the flattening as a declared, argued exposure for the PDF class (the "explicit argued acceptance" branch the spec already uses for order attestation), bounded by the measured figure and revisited when a quote-faithful PDF front-end exists. + (d) Reject docling for born-digital PDFs; find a quote-faithful front-end (`pdftotext` preserves all 134 curly quotes but yields no structure — 174 headings recovered by docling, 0 by pdftotext). +**Recommendation:** (a) for the backend flag immediately — it is a pure FIX, strictly reduces damage, and needs no ruling. For the quote question, (c): the alteration is uniform, mechanically detectable, and reversible in principle, and (d) trades 134 quote marks for the entire heading structure — a worse bargain. But (c) is a spec-visible acceptance and is the steward's and jurist's to make, not the executor's. +**Files affected:** `_curation/conversion-runbook.yaml` (`pipelines.pdf_textlayer`); `_curation/graduation-spec.yaml` (`normalize:`); `docs/chamber-library-specification.md` §V if (c); `_curation/tool-evolution-log.md` (entry filed 2026-08-31). +**Blocks:** graduation of `classical-and-romantic-performing-practice-brown` (converted + `verify_conversion` 5/5 PASS, staged in `scratch/making/`), and any other born-digital PDF on this lane. +**Awaiting:** Steward authorization; jurist design gate on the eligibility question. + +--- + +### PENDING-172 — BUILD RECORD: (e), (c) and (b) landed under REVIEWED-131 +**Date:** 2026-08-31 +**Placement note:** appended, not inserted, per the PENDING-164 AMENDMENT 2 precedent of today. +**Status:** **BUILT** — `70440db`, under REVIEWED-131 (2026-08-31). + +- **(e)** `parked_workers()` in `scripts/wake-digest.py` reads `~/.claude/jobs//state.json`'s `respawnFlags`. Fails to **NOT ESTABLISHED**, never to safety: a missing directory, an unparseable `state.json`, and an absent `respawnFlags` are each enumerated by name rather than counted as *no parked workers* (cond. 1). +- **(c)** `human_turns()` counts genuine human turns in a transcript, discounting hook-injected records and `/clear`/`/exit`. **Retrospective by construction** — at SessionStart the current session has no turns — and the output says so. Each line names the control that produced it (cond. 2). +- **(b)** the `OPEN QUESTION` field is now emitted as *"orientation, not an instruction — inherited from a human and answerable to one"*, marked in the code as **annotation and explicitly not a control** (cond. 3). + +**⚠ Two defects found by the build's own controls, recorded rather than smoothed.** `json` was never imported, which the digest's top-level guard would have turned into `WAKE DIGEST UNAVAILABLE` **at every session start** — caught by the selftest on its first run. And the first draft asserted that a **stopped** job carrying the flag would take a turn on restart; whether the daemon respawns a stopped job is **not established**, and the output now says exactly that rather than claiming it is live or claiming it is safe. + +**Controls:** both directions, and two drawn from the event itself — `human_turns` returns **0** on `b7e7eb39` (the unattended session) and **>0** on the attended session that found it. Those two report **NOT ESTABLISHED** rather than passing if the transcripts age out of the 30-day window. + +**Live at time of filing:** 1 job dir read, 0 unreadable, 1 ARMED — `acaabadf`, `state=stopped`, reported as *neither live nor proven safe*. + +--- + +### PENDING-173 — BUILD RECORD: option (a) landed under REVIEWED-132, and condition 3 found a defect in it +**Date:** 2026-08-31 +**Kin:** **PENDING-146** — the prospective-convention question is routed there by REVIEWED-132 condition 5 and is deliberately **not** decided by this build. Also PENDING-145 (`ruled_pendings`), PENDING-175 (`governance_item`), and PENDING-110. +**Status:** **BUILT** — `cb8cab5`, under REVIEWED-132 (2026-08-31). + +The control now reads all three registers at `##` and `###`, recognises `ADDENDUM`, and computes `originals` **across** registers, because a parent may be archived while its amendment stays open. Condition 1's default is inverted: a header is an original **only** if it carries an identifier and no marker, so a compound or an addendum is excluded from `originals` rather than admitted to it. + +**⚠ CONDITION 3 FIRED ON MY OWN CODE, WHICH IS WHAT IT IS FOR.** Enumerating rather than counting returned **89** against ADDENDUM 1's **82**. The surplus was six prose titles — *"Citation amendment (#2)"*, *"Dream amendment"*, *"PENDING-46 addendum (2026-07-03)"* — matched by an upper-casing containment test and **struck off `originals`**. That is the mirror of the bug being repaired: it would have raised **false** *"the record was replaced"* findings against six genuine originals. Markers are now uppercase standalone tokens, with controls in both directions. The enumeration then agrees with ADDENDUM 1's method at **82**. + +**The residual disagreement is reported, not reconciled.** ADDENDUM 1's filed table says **81**; its own method re-run today says **82**. The difference is **this session's later filing**, not a classifier difference. **The table stands as filed.** + +**Live at time of filing:** 32 attributable blocks checked, all resolve; **50 NOT ESTABLISHED** (48 unnumbered headers, 2 in-body) — counted, never passed. 59/59 instrument controls pass.