[HARDENING] PENDING-126 + PENDING-122 Amendment 2 — what inducing red revealed, and REVIEWED-102..105 placed

All three findings came from contact while building a red fixture for the
REVIEWED-103 acceptance. None was sought; the search for a control that worked is
what exposed them.

The fleet already violates the condition REVIEWED-104 attached to the NEW
live-binding assertion, on a dependency the ruling did not consider. Three suites
crash on a gitignored corpus/index.db with a raw sqlite traceback, and run-fleet
reports FLEET RED indistinguishably from a code defect — while store.py rebuilds
that file in 0.628 seconds and the clone then runs 7/7 green. So the condition is
retroactive, not prospective. And test_retrieve.py already detects the absence and
skips with a named reason, which makes PENDING-124 recommendation (d) concrete: the
honest third state exists in this fleet, in one suite, and three others lack it.

R0's section_end bound is unguarded. Removing it leaves 31/31 passing. That is the
rule R0 was created to establish after two consumers disagreed on 3 of 253 patterns
with neither right — asserted in prose, correct-but-inert on the live corpus, and
therefore invisible to every test.

test_navigate crashes with StopIteration rather than naming a failure. The exit code
was always right; the legibility is missing — REVIEWED-100's own distinction,
recurring where its fix does not reach.

Also commits REVIEWED-102 through -105, placed by the steward and left uncommitted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A35wiD55yRHj5U1ECZAX4t
This commit is contained in:
David F Glidden
2026-08-08 18:15:20 +02:00
co-authored by Claude Opus 5
parent 448ce373ca
commit 2472ed8e88
3 changed files with 214 additions and 2 deletions
+56
View File
@@ -1897,6 +1897,32 @@ The `0 of 17` → `0 of 14` figure the jurist saw is a **secondary** paragraph o
--- ---
### AMENDMENT 2 — 2026-08-08, the condition is ALREADY VIOLATED, by a dependency the ruling did not consider
*Found by contact while running REVIEWED-103's acceptance in a fresh clone — not sought.*
**REVIEWED-104 §1 conditioned the NEW live-binding assertion on three states**, reasoning that *"red on absent trains people to discount fleet red, which is the worst possible outcome for this particular thread."* **That outcome is already the present state**, on a different dependency, with nothing to do with `chamber-library`.
**Measured 2026-08-08 in a fresh `git clone`:**
| suite | with `corpus/index.db` absent |
|---|---|
| `test_ground.py` | **crashes** — raw `sqlite3.OperationalError: unable to open database file` |
| `test_navigate.py` | **crashes** — same |
| `test_reading_index.py` | **crashes** — same |
| `test_retrieve.py` | ✅ **skips, with a named reason** |
| `test_fidelity_v3` · `test_ingest_gate` · `test_verify_quote` | pass (no dependency) |
`run-fleet.sh` reports **FLEET RED**, indistinguishable from a code defect.
**`corpus/index.db` is gitignored on purpose** — the engine's first law is that *the files are authoritative; every index is derived, subordinate, and disposable.* And the disposal is real: **`python3 engine/store.py build` rebuilt it in 0.628 s**, after which the clone ran **7/7 green**. So this red is a **0.6-second-avoidable environment condition, reported as a failure.**
**Three consequences.** **(1)** The condition ruled here is **retroactive, not prospective** — three suites need `bound`/`drifted`/`cannot-assess` today, before any live-binding assertion exists. **(2)** ⚠ **The honest third state ALREADY EXISTS IN THIS FLEET, in one suite:** `test_retrieve.py` detects the absence and skips with a named reason. **That is PENDING-124 recommendation (d) with a live in-repo precedent** — generalize what is implemented rather than mint doctrine beside it. **(3)** **A crash is not a third state.** REVIEWED-100 made every suite name its failures in the summary; an uncaught traceback bypasses that, so these three are invisible to the improvement meant to cover them.
**Files affected (revised):** three suites gain the detect-and-report shape `test_retrieve.py` already has; `scripts/run-fleet.sh` must render `cannot-assess` distinguishably from red.
---
## PENDING-123 — The pre-commit hook cannot distinguish "nothing to check" from "I am disarmed" ## PENDING-123 — The pre-commit hook cannot distinguish "nothing to check" from "I am disarmed"
**Date:** 2026-08-08 **Date:** 2026-08-08
@@ -2046,3 +2072,33 @@ Three cases, all discriminated: a rule ran → existing output already says so,
**Awaiting:** Steward authorization (D-1 lane). **Awaiting:** Steward authorization (D-1 lane).
--- ---
## PENDING-126 — Two holes in the fleet, found by inducing red against it: an untested load-bearing rule, and a suite that crashes instead of failing
**Date:** 2026-08-08
**Tag:** [HARDENING]
**Related:** REVIEWED-103 (whose acceptance surfaced both) · REVIEWED-100 (the failure-naming improvement hole 2 bypasses) · `studium-engine/docs/spec/r0-reading-index-contract.md` §3 · PENDING-122 Amendment 2 (same act, third finding).
**Provenance:** neither was sought. Both surfaced while trying to build a red fixture the fleet would catch — **the search for a working control is what exposed them**, the discrimination gate doing its job one level out.
**Hole 1 — R0's `section_end` bound is not covered by any test, and it is the rule R0 exists for.** `engine/reading_index.py:123` reads `it["line_end"] = min(nxt, section_end) if end is None else min(end, section_end)`. **Removing the `section_end` bound entirely leaves `tests/test_reading_index.py` at 31/31 passing** and the whole fleet green. That bound is not incidental: R0 was created because `measure_rerank.py` and `navigate.py` had each grown their own reader and **disagreed on 3 of 253 Alexander patterns with neither right** — one ran a pattern into the next group, the other into ACKNOWLEDGMENTS. The derived rule *"end = min(next sibling's start − 1, containing section's end)"* is the fix. **It is asserted in prose and unguarded in code.** ⚠ Likely cause: the live corpus never exercises the branch, so the bound is **correct-but-inert**, and a regression would surface only on a corpus shape we do not yet hold.
**Hole 2 — `test_navigate.py` crashes rather than naming a failure.** Forcing `citable = False` at `engine/navigate.py:189` produces an uncaught `StopIteration` at `tests/test_navigate.py:116`. Exit is non-zero, so the fleet correctly goes red and the commit is correctly refused — **but the failure is a traceback, not a named check.** REVIEWED-100's improvement was that *"all seven suites now name failures in the summary"*; a crash bypasses the summary entirely. ⚠ **The exit code was always right; the legibility is what is missing** — the same distinction REVIEWED-100 drew, recurring where its fix does not reach.
**Rationale.** Both holes are invisible to a green fleet by construction, and the trigger landed today makes the fleet the gate on every `engine/` and `tests/` commit. **A gate is only as good as the suites behind it**, and these are two measured ways those suites say less than their green implies — the PENDING-96 family, now inside the fleet rather than around it.
**Options.**
- **(a) Fix both.** A fixture exercising the `section_end` bound (necessarily synthetic — the branch has no live instance), and a guarded lookup in `test_navigate.py` that fails by name instead of raising.
- **(b) Fix hole 2 only.** Cheaper; leaves a load-bearing derived rule unguarded.
- **(c) Census first.** ⚠ Neither hole was sought, so **the base rate is unknown** — how many other asserted-in-prose rules are unguarded, and how many suites crash rather than name?
**Recommendation: (a), then (c) as a bounded sweep.** (a) closes what is measured; (c) is the honest follow-on because **two holes found without looking is not a base rate**, and the census is bounded (7 suites; the contracts are enumerable).
**Check that it worked — both directions required.** Hole 1: the new fixture must go **red** with the bound removed and **green** with it restored — the removal is already proven invisible, so that is the discriminating negative, real and available. Hole 2: the induced citability break must produce a **named** failure in the summary and still exit non-zero; the restore must return 34/34.
**⚠ What this does not establish.** Fixing these two says nothing about the class (option c). And hole 1's fixture is necessarily **synthetic** — the live corpus has no instance of the shape, which is exactly why the gap survived.
**Files affected:** `~/_Dev/studium-engine/tests/test_reading_index.py`, `~/_Dev/studium-engine/tests/test_navigate.py`.
**Awaiting:** Steward authorization (D-1 lane).
---
+126 -1
View File
@@ -1168,4 +1168,129 @@ data in the ratified spec, thin consumer in dotfiles — the pattern REVIEWED-10
→ (a) → (c). Tag commits with REVIEWED-101. The spec amendment gates (a), not (e): (e) reads → (a) → (c). Tag commits with REVIEWED-101. The spec amendment gates (a), not (e): (e) reads
the engine's own manifest and sidecars and needs no cross-repo enumeration. Each stage carries the engine's own manifest and sidecars and needs no cross-repo enumeration. Each stage carries
a both-directions control — drift reported when a binding is stale, clean reported on a a both-directions control — drift reported when a binding is stale, clean reported on a
genuinely clean corpus. genuinely clean corpus.
## REVIEWED-102 — PENDING-119 — REVIEWED-101 condition 6 placed (e)'s consumer in dotfiles, on reasoning the same ruling says (e) does not engage
**Date:** 2026-08-08
**Decision:** AUTHORIZED — option (i), with condition 6 of REVIEWED-101 explicitly narrowed on the record, and one condition on (i).
**Notes:** The ambiguity is mine. Condition 6 was drafted flat, under a heading that read "Placement", and left its scope to be inferred from a
sentence four lines below it. That is a jurist defect, not an executor misreading. The item is right that the two texts admit both readings, and
right that the narrow reading is the one the reasoning supports: condition 6's argument is ownership of a cross-repo invariant, and (e) does not
enumerate one. It follows the engine's own declared pointers into the chamber; the surface list that (a) needs is precisely what (e) does not need.
That was already the stated basis for severing (e) from the spec amendment, and the same severance carries the placement.
1. NARROWING, recorded as a ruling and not as a charitable reading: condition 6 of REVIEWED-101 governs consumers that must ENUMERATE the
cross-repo binding surface — (a) and (c). It does not govern (e). Left unrecorded, the next reader relitigates this.
2. The recorded reason for rejecting (ii) is that it inverts the condition it honours. A ~/dotfiles/scripts/check-source-binding.sh whose body is
one exec of engine/ingest_gate.py --check-only puts an engine path and an engine flag into the global layer. That is the coupling REVIEWED-100
rejected, reintroduced in the name of a condition written to prevent coupling. A rule that produces the outcome it exists to forbid is being read
at the wrong grain.
3. Kept in view, because the reflex runs the other way: the placement question only became live because the executor delegated to ingest_gate
instead of writing a fresh sha-comparing script. Had it duplicated the logic, condition 6 would have been straightforwardly correct. The better
implementation is what made the condition misfit — a rule's misfit is not automatically an implementation error.
4. The fleet-census finding is SPLIT OUT as PENDING-122. "No fleet suite validates live binding" is not evidence for a placement dispute; it is a
standing correction to what fleet-green certifies, owed to anyone reading a green fleet. Filed inside PENDING-119 it dies if PENDING-119 is
deferred. Same objection made at REVIEWED-101 condition 5; consistency requires making it again. It belongs with PENDING-96 as one family — a green
that attests less than its surface suggests.
5. CONDITION on (i): 0.218 s over 14 sources is honest about being burst-sized, and the check is unconditional on every commit, scaling with
sources × file size. State the threshold now with its action — when it exceeds ~1 s, (e) re-scopes or hands off to (a)'s scheduled job. A
per-commit cost that grows unremarked converts a tripwire into a --no-verify habit, which is this thread's own failure class arriving by the back
door.
**If AUTHORIZED:** Proceed with (i) — one line in the engine's .precommit-triggers. Record the condition-5 threshold beside it. Tag commits with
REVIEWED-102.
## REVIEWED-103 — PENDING-120 — The fleet trigger covers corpus/ but not the engine code the fleet exists to test
**Date:** 2026-08-08
**Decision:** AUTHORIZED — option (a), with the scope framing corrected and the acceptance fixture decomposed.
**Notes:** The demonstration is the strongest part: eecc8bb touched the gate and the gate's own test floor, and the hook printed nothing. First
real non-probe commit since the trigger landed, and it ran nothing. That is a measured failure, not a reasoned risk — the same standard
REVIEWED-100 credited PENDING-116 for.
1. CORRECTION to the scope-honesty note. The item says REVIEWED-100 "did not rule the pathspec". As to the ruling, that is accurate — REVIEWED-100
authorizes the mechanism and the both-halves acceptance and says nothing about paths. But PENDING-116's own Costs section does: "the trigger paths
must be scoped tightly (corpus/, corpus/sidecars/) so ordinary docs commits do not pay it." So the pathspec was not silence — it was a cost
commitment in the authorized item. This does not change the outcome; it changes the bar. The widening must be SHOWN to preserve the discrimination
that commitment bought, which is exactly why (b) is correctly rejected and (a) is not. Record it as revising a stated cost-control with its
justification intact, not as filling a gap. In-scope repair is the more comfortable framing and the less accurate one.
2. The acceptance test DECOMPOSES; one fixture cannot meet "neither may be a synthetic probe if a real one is available". (i) Fires on a real
engine change — replay eecc8bb against the widened pathspec; genuinely real, genuinely available, and it is the commit that demonstrated the gap —
but eecc8bb was green, so it proves firing only. (ii) Refuses on red — requires an induced red unless history holds a real red engine/ commit; if
one exists use it, and if not, say the fixture is synthetic rather than letting "real fixture" cover both halves. (iii) Docs-only still runs
nothing — unchanged, and the half that proves discrimination.
3. The adjacent gap was checked and the answer is NO; filed as PENDING-123. The hook does not distinguish "no trigger path matched" from "the
declaration is malformed or its command is missing". Five disarming faults were tested against a positive control, each staging a real corpus/
change the hook must catch: pathspec typo, no separator, empty command, comments-only, empty file. All five silent, all exit 0. A typo disarms the
gate permanently and invisibly. This is also why eecc8bb running nothing went unremarked — its output is byte-identical to a fully disarmed hook's.
4. INTERACTION with PENDING-119. If both land, .precommit-triggers carries two lines with overlapping paths and engine commits pay ~2 s (fleet) +
0.218 s (binding). Declare the order in the file so a red is attributable to one check without reading both.
**If AUTHORIZED:** Proceed with (a) — corpus/ engine/ tests/ scripts/run-fleet.sh. Land the three-part acceptance separately, labelling the
induced-red fixture synthetic if no real red engine/ commit exists. Tag commits with REVIEWED-103.
## REVIEWED-104 — PENDING-122 — What a green fleet certifies, and what it does not: no suite validates live binding
**Date:** 2026-08-08
**Decision:** AUTHORIZED — option (a), with a required third result state.
**Notes:** A live-corpus assertion makes one suite depend on chamber-library being present and reachable. Every other suite builds under tmp and is
portable; this one will not be. The item does not say what happens on a fresh clone with no chamber beside it, and both obvious answers are wrong.
Red on absent is a suite going red for reasons unrelated to the code under test, which trains people to discount fleet red — the worst possible
outcome for this particular thread. Skip on absent is the silent net, in the very assertion added to correct an overstatement.
1. CONDITION: three states — bound / drifted / cannot-assess — and cannot-assess must be distinguishable in the fleet summary, never folded into
green. A green fleet that includes an unassessed binding case is the same overstatement one layer along.
2. The REVIEWED-83 A1 leg of the analogy was challenged as uncorroborated and has been verified against ~/REVIEWED.md, which is authoritative.
REVIEWED-83 Amendment 1 (2026-08-01) IS the classifier layer-error: "The classifier's controls exercise its decision rule … They cannot test
whether three signals are enough … In both cases the control was correct and sat at the wrong layer." The 0 of 17 → 0 of 14 figure visible in
e341242 is a secondary paragraph of that same amendment, labelled "Consequential correction, routed not applied." Third subsystem stands on checked
ground, and the amendment itself names the first two as one shape.
3. Does not prejudge PENDING-121, confirmed from both sides. Option (a) closes the distance between "the gate works" and "the corpus is bound" at
whole-file granularity only; anchor correctness is 121's gate and the two land independently. Recorded with it: REVIEWED-101 §C's "fourth surface —
the reading index carries no hash" was wrong, and 121 corrects it — the runbook binds the index outward by source_sha256, and the real gap is
finer and worse.
4. The three-state requirement is raised to doctrine as PENDING-124 rather than conditioned again per item.
**If AUTHORIZED:** Proceed with (a), three-valued. Tag commits with REVIEWED-104. Land after REVIEWED-105 if both are authorized.
## REVIEWED-105 — PENDING-123 — The pre-commit hook cannot distinguish "nothing to check" from "I am disarmed"
**Date:** 2026-08-08
**Decision:** AUTHORIZED — (b) now, plus (e) in place of a flag; (a)'s full listing retained on --verbose.
**Notes:** The strongest item of this set. Positive control first, six rows, mechanism read from the hook's own source down to the two lines that
swallow the difference — `[ -n "$cmd" ] || continue` and the `2>/dev/null` that erases "git could not resolve this pathspec". And it answers
PENDING-120 §C: "Running pre-commit checks…" with nothing after it is what a fully disarmed hook prints, which is why eecc8bb went unremarked.
1. Instead of (a) behind a flag — a flag nobody sets is a capability nobody has — option (e): print the per-rule line ONLY when a
.precommit-triggers file exists and no rule matched. A rule ran, existing output already says so; nothing matched, one line naming the declared
rules and the staged paths; no triggers file, print nothing, so no noise in any other repo. Zero cost in the normal case, and the line appears in
exactly the ambiguous case. It also partly closes the fifth silence: a typo'd corpuss/ shows as a declared rule that did not match on a commit that
touched corpus/, catchable at the moment the reader is already looking — PENDING-98's mitigation shape, not a log. Keep (a)'s full per-rule
listing on --verbose for the never-yet-matched rule, which stays honestly unknown.
2. The item's own standard, turned on the item: the summary claimed silence when the declaration is "malformed, mis-typed, empty, or absent", and
the table had no absent row, nor one for the hook itself missing or core.hooksPath unset. Rows added. Measuring them found something stronger than
the claim — with the hook file missing, the commit produces ZERO output, not an ambiguous silence — and found the executor wrong in the other
direction: unsetting core.hooksPath locally falls back to an armed global, so that row is a robustness property, not a fault, and is recorded as
one.
3. Blast radius of (b). The hook is global, so a refusal on malformed declarations arms in every repo carrying a triggers file, present and future.
Censused 2026-08-08: exactly one exists today (studium-engine), across ten repos under the global hooksPath. Today's radius is one repo; the
condition is about the future and stands. Required: the refusal message names file, line number and fault, and states --no-verify. A gate that
blocks without saying why is replaced by habit within a week.
4. SEQUENCING across the four open items: land 123 before 119(i) and 120(a). Both add lines to .precommit-triggers; a validator that catches a
malformed line should exist before the file grows. In the other order, the first thing to test the new declarations is the declarations themselves.
**If AUTHORIZED:** Proceed with (b) + (e). Build order: REVIEWED-105 → REVIEWED-102 (i) → REVIEWED-103 (a). Tag commits with REVIEWED-105.
+32 -1
View File
@@ -5,7 +5,7 @@ metadata:
node_type: memory node_type: memory
type: feedback type: feedback
originSessionId: 7d08dad4-626a-484c-870b-8f1a9674db7a originSessionId: 7d08dad4-626a-484c-870b-8f1a9674db7a
modified: 2026-08-08T13:30:54.198Z modified: 2026-08-08T14:45:17.679Z
--- ---
# Session Ledger — 2026-08-08 # Session Ledger — 2026-08-08
@@ -180,6 +180,37 @@ the omission is itself the finding above.*
work; it is that I reach for a check whose vocabulary I have not verified against the thing work; it is that I reach for a check whose vocabulary I have not verified against the thing
it must match** — the substrate-vs-description class, pointed at my own instruments. it must match** — the substrate-vs-description class, pointed at my own instruments.
### ⎯ SESSION 3 authorization moves ⎯
- **REVIEWED-101 (e) — engine half BUILT** (`eecc8bb`), wiring deliberately **NOT** placed.
Built as a **delegation** to `ingest_gate.py`, which already enforced §1.1 on both
named surfaces. Suite 24 → **41**; fleet **221/221, 7/7 exit 0**.
- **PENDING-119** filed `[PROPOSAL]` — condition 6 vs the ruling's own "(e) needs no
cross-repo enumeration". **Steward directed: file, do not wire.**
- **PENDING-120** filed `[HARDENING]` — the trigger's pathspec misses `engine/`+`tests/`.
**Steward directed: file separately.**
- **PENDING-117 AMENDMENT 2** appended — pointer only, nothing above altered.
- Commits: engine `eecc8bb`, `f1bb227`; dotfiles `02c6875`. **Neither repo pushed** —
engine `ahead 2`, dotfiles `ahead 1`; the push belongs to the wrap.
- **T~15:5x — the day's error class has a mechanical root, found at last.** `ls` is
**aliased** in this profile (eza-like; it rejects a bare path as an `--icons` value).
Instance (7) above was never a missing file — the alias ate the argument and returned
exit 2. Switched to `command ls`. Worth keeping: **an exit code from an aliased command
is evidence about the alias, not about the world.** Two of the day's seven check-errors
reduce to this one shell fact.
- **T~16:0x — an eighth, caught and not narrated:** read `${PIPESTATUS[0]}` after an
intervening `echo` had already clobbered it, printing `exit=` empty. Re-ran clean.
Same family: **a check whose vocabulary I had not verified against its own mechanics.**
- **What held, this session.** Every claim that entered a commit message or a governance
filing was substrate-checked first: the fixture-only census by enumerating all six
invocations rather than grepping; the delegation argument by reading L128–131/L150–153;
the write hazard by reasoning about index-vs-worktree and then *not* asserting it
untested; byte-identity of the refactored write by an empty `git diff`; the red
direction on the **real** manifest with a `cmp`-verified restoration. **The errors
stayed in throwaway checks; nothing false reached a durable surface.**
## Sub-agent dialogues ## Sub-agent dialogues
## Bypasses ## Bypasses