diff --git a/PENDING.md b/PENDING.md index fe1a53e..411a528 100644 --- a/PENDING.md +++ b/PENDING.md @@ -2167,7 +2167,37 @@ Three cases, all discriminated: a rule ran → existing output already says so, **Hole 2 was three sites, not one.** I filed it as *"a suite that crashes instead of failing"*; `test_navigate.py` carried **three** bare `next(...)` calls over generators. Fixed as a class with one guarded helper. **Induced citability break: was a single `StopIteration` traceback → now SEVEN named failures**, each saying what broke and why the dependent checks did not run. The exit code was always right; the legibility is what changed. -**⚠ Option (c) — the census — is NOT done and remains open.** Two holes found without looking is not a base rate. The bounded sweep (7 suites; are other rules asserted-in-prose and unguarded? do other suites crash rather than name?) is still owed, and finding three more `next()` calls in the first suite I opened is weak evidence the class is wider than two. +**⚠ Option (c) — the census — DONE 2026-08-08. Results below.** + +--- + +### AMENDMENT 2 — 2026-08-08, the census (option (c)) — and the class is NOT what the item named + +**Q1 — HOW MANY SUITES CRASH RATHER THAN NAME? THREE OF SEVEN, under THREE distinct triggers. My fix closed ONE of the three.** + +Censused by **mechanism** — driving real degraded states and observing the output shape, not grepping for risky constructs: + +| degraded state | `test_ground` | `test_navigate` | `test_reading_index` | other 4 | +|---|---|---|---|---| +| `index.db` absent | *closed today* | *closed today* | *closed today* | ok | +| manifest has **zero sources** | **CRASH** `KeyError: 'spans'` L88 | **CRASH** `KeyError: 'expression'` L103 | **CRASH** `KeyError: 'alexander…'` L72 | pass | +| a manifested **source file missing** | **CRASH** `FileNotFoundError` L48 | **CRASH** `FileNotFoundError` L93 | **CRASH** `FileNotFoundError` L62 | pass | + +⚠ **Crash origin is SUITE code, not engine code** — direct access to a derived structure without checking it has the assumed shape (`stats["expression"]`, `idxs[]`, unguarded source reads). Same class as hole 2, wider than filed. ⚠ **And the third trigger is the one the live-binding check I added handles correctly** — so **two in-repo precedents now do this right** (`test_retrieve`, `test_ingest_gate`) and three do not. + +**Q2 — HOW MANY RULES ARE ASSERTED-IN-PROSE BUT UNGUARDED? The question as I posed it is unanswerable by inspection, and the answer among those testable is ZERO — but three INERT guards turned up, which is hole 1's real class.** + +- **Token-mention census: 13 of 13 R0 §5a clauses "touched", 0 untouched. ⚠ That number is worthless** — hole 1 lived in a clause that was touched all along. Recorded to show the method fails, not as a result. +- **Mutation census (the only instrument that answers it): 7 mutants on the enumerable R0 clauses → 4 caught, 3 survived.** +- **All 3 survivors verified EQUIVALENT on current data, not coverage gaps:** `cite_type` and `title_source` **defaults never fire** (sentinel substituted: **0 of 327** regions resolve to it — the adapters always supply the key); the emit-promotion mutant is neutralized by the state-based pops. **Positive control run:** a mutant that genuinely changes the emitted artifact **is caught**, by three named checks. Without that control, "survived" would have been uninformative. + +**THE UNIFIED FINDING, and it renames the class.** Hole 1 was never "an unguarded rule". It was a **guard the live corpus cannot exercise** — and the census finds three more of exactly that shape in R0 alone. Mutation escape on R0: **3/7 ≈ 43%, all inert rather than wrong.** The remedy for an inert guard is a synthetic fixture (what hole 1 got), not more assertions over live data — and an inert guard is a **latent** defect: correct today, unprotected the day the corpus grows a case that reaches it. + +**⚠ Errors in the census's own instruments, three of them, each caught by the next step.** The grep construct-count was a proxy that counted **comments** — 3 of `test_navigate`'s 4 `next(` hits were my own prose *about the fix*; AST corrected 4 → 1. The token-mention coverage census returned a meaningless 0. My first emit mutation targeted a `pop` I had wrongly reasoned was unreachable. **A census of instrument quality needed three corrections to its own instruments**, which is the finding underneath the finding. + +**What is now precisely actionable:** **6 crash sites** (3 suites × 2 remaining triggers), each with a file and line. **Not fixed here** — (c) was a census, and its job was the base rate. + +**⚠ Residual, stated.** Three degraded states were driven, not all. The mutation census covers the R0 clauses only — `n0`/`n1`/`v0`/`v1`/`cluster-a` are unmutated, and their escape rate is **unknown, not zero**. ---