From 860c3df6fdba03d5d0387d8d02b77a0118e38288 Mon Sep 17 00:00:00 2001 From: David F Glidden Date: Mon, 31 Aug 2026 11:49:49 +0200 Subject: [PATCH] [FIX] Build records for PENDING-172 and PENDING-173; kin to PENDING-146 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both marked BUILT and named by their rulings, so built-vs-ruled can see them. 173's kin list carries PENDING-146 per REVIEWED-132 condition 5 — the convention question is routed there and is not decided by this build. Records the two defects the builds' own controls found (the missing json import that would have disabled the digest at every session start; the stopped-job overclaim) and the condition-3 enumeration that caught six false compounds in the new classifier. The residual 82-vs-81 gap is this session's own filing and the filed table is left standing. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Wn9mxsFjHJkzYjuDdrtxwp --- PENDING.md | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) 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.