Skip to content

Commit 50e0ea7

Browse files
committed
test(d-ign-b): the corpus produced nothing to read — fix the fixture, not the digest
`cargo test -p lance-graph-supervisor --features cycle-driver --test d_ign_b_lenses` failed at L1's can-fire half: z=3 (Kant) and z=4 (Wittgenstein) over byte-identical rows digested identically to 15130871412783076140. That constant is `DefaultHasher::new().finish()` — the hash of zero bytes. Both readouts were EMPTY. Root cause is the corpus, not the lenses. `synthetic_corpus` emitted "d-ign-b synthetic verse {i} token{salt}", which carries no copula, no auxiliary, no modal, no typed relational verb and no `-ed` morphology, so `stance::stream` never arms a predicate and emits zero statements. Empty arena + empty ReadOut => all four `stance_panel` arms empty => every lens digests the same. `stance.rs` is untouched: it was never at fault. Pre-existing at f9206fc, and newly VISIBLE rather than newly broken — without `--features cycle-driver` the module is cfg'd out and the binary runs 0 tests while reporting ok. Arming that CI gate ended a vacuous pass; it did not cause a regression. Explicitly NOT fixed by tagging the digest with a variant discriminant. That turns it green instantly and makes the cross-lens `!=` pass by construction of the tag alone, for any input — the vacuous-assertion shape the falsifiability rule rejects, which `digest()`'s own doc comment forbade in advance. The fix — `synthetic_window(w)` builds each 48-verse owner slice from templates the shipped clause machine genuinely extracts from: * affirm-then-negate / negate-then-affirm pairs on one term (`revise_at` records |f1-f2| = 0.85, clearing Hegel's 0.05 floor, with both flip directions legible from the provenance endpoints for Nietzsche's Transvaluation/Devaluation); * "they knew that they were X" (perception verb + complementizer inside the 3-token window) for Kant's ReadOut::lifts; * "<effect> because <cause>" for Wittgenstein's impl-cause/ impl-effect games, on top of the Inh games every emission feeds. Predicates are `{stem}{window:02}{n:02}` nonsense syllables, chosen to collide with no catalogue the machine consults. The window SHAPE varies with w % 5|3|7, coprime with the stride-4 arming cycle. Three of the four arms fold interned u16 ids, never the strings, so structurally identical slices digest identically however their tokens are spelled — renaming the vocabulary would have left L4's ">=2 distinct digests" half unfalsifiable. L1 also gains a non-emptiness precondition checked BEFORE the digest comparison, naming the corpus provenance. An empty corpus now reports itself as a corpus defect instead of accusing the lenses. Falsifiability verified in both directions, not asserted: * `run_lens(4, ..)` returning the Kant arm => L1 fails on 1790839806321388433, a NON-empty digest, so the assertion still discriminates content; * restoring the pre-fix filler => the new precondition fires with the corpus-defect message. Measured after the fix: all four arms non-empty on the twin base, 4/4 distinct digests over one owner, 4/7/6/6 across each lens's owner set. Full `-p lance-graph-supervisor --features cycle-driver` suite green (30 tests), fmt + clippy clean. Board hygiene in the same commit: * EPIPHANIES E-D-IGN-B-CORPUS-PRODUCED-NOTHING-TO-READ-1 * ISSUES ISS-D-IGN-B-REAL-CORPUS-PATH-IS-UNVERIFIED — the $BLW_KJV_TSV branch has never executed here, and the two corpora do NOT exercise the Hegel/Nietzsche arms alike.
1 parent f9206fc commit 50e0ea7

3 files changed

Lines changed: 237 additions & 13 deletions

File tree

.claude/board/EPIPHANIES.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
## 2026-08-06 — E-D-IGN-B-CORPUS-PRODUCED-NOTHING-TO-READ-1 — arming a CI gate that ran zero tests exposed an empty-vs-empty digest collision; the corpus was the defect, the untagged digest was correct
2+
3+
**Status:** FINDING (reproduced at `f9206fc`, fixed, both mutation directions verified). **Confidence:** High for the mechanism (the empty-hash constant was computed and matched; the pre-fix corpus was traced token-by-token through the clause machine and emits nothing); High for the fix (all four lens arms now measured non-empty, 4/4 distinct digests on one owner). Test-fixture only — `stance.rs` is untouched.
4+
5+
**The failure.** `cargo test -p lance-graph-supervisor --features cycle-driver --test d_ign_b_lenses` panicked at L1's can-fire half: two *different* lenses (z=3 Kant, z=4 Wittgenstein) over byte-identical rows produced the *same* digest `15130871412783076140`. That number is `DefaultHasher::new().finish()` — the hash of **zero bytes**. Both readouts were EMPTY, and `LensReadout::digest` folds contents only, so two empties of any two variants collide.
6+
7+
**Root cause: the corpus, not the lenses.** `load_or_synthesize_corpus` reads `$BLW_KJV_TSV` or `/tmp/kjv_verses.tsv`; neither exists in CI, so it fell back to `synthetic_corpus`, which emitted `"d-ign-b synthetic verse {i} token{salt}"`. That text carries **no copula, no auxiliary, no modal, no typed relational verb and no `-ed` morphology**, so `stance::stream` never sets `armed` and emits **zero** statements. Empty arena + empty `ReadOut` ⇒ all four `stance_panel` arms empty ⇒ every lens digests identically. The test's own risk-check line had been printing `Hegel empty = true, Nietzsche empty = true` all along; nothing was reading it.
8+
9+
**Pre-existing, and newly VISIBLE rather than newly broken — the transferable half.** Reproduced identically at `f9206fc`. Without `--features cycle-driver` the whole test module is `#[cfg]`-ed out and the binary runs **0 tests** — reporting `ok` and passing CI **vacuously**. `LATEST_STATE.md` recorded that step as a standing gate; it was *unarmed*. **A test binary that runs zero tests is indistinguishable from a passing one in CI summary output, and a feature-gated integration test is the standard way to arrive there by accident.** Arming the gate did not cause a regression — it ended one.
10+
11+
**Why the "obvious" fix was the wrong one, explicitly.** Folding a variant-discriminant tag into `digest()` turns the collision green instantly — and makes L1's cross-lens `!=` pass **by construction of the tag alone**, for any input, forever. That is precisely `E-VACUOUS-ASSERTION-IS-THE-HOUSE-STYLE-1`'s shape, and the digest's own doc comment already forbade it in advance (deviation 3 in the module doc records an earlier draft being talked out of exactly this). **A red test whose green fix would delete its own falsifier is telling you the defect is upstream of the assertion.** The untagged digest is what made an empty corpus *observable at all*; tagging it would have hidden the empty corpus permanently while displaying green.
12+
13+
**The fix.** `synthetic_window(w)` builds each 48-verse owner slice from templates the shipped clause machine genuinely extracts from, one per arm: affirm-then-negate and negate-then-affirm pairs on the same term (`revise_at` records `|f₁−f₂| = 0.85` ⇒ Hegel's `> 0.05` floor, with both flip directions legible from the provenance endpoints ⇒ Nietzsche's `Transvaluation`/`Devaluation`), `they knew that they were X` (perception verb + complementizer within the 3-token window ⇒ Kant's `ReadOut::lifts`), and `<effect> because <cause>` (⇒ Wittgenstein's `impl-cause`/`impl-effect` games on top of the `Inh` games every emission already feeds). Predicates are nonsense stems shaped `{stem}{window:02}{n:02}` — chosen to collide with **no** catalogue the machine consults, so a template can only give them the role it means to.
14+
15+
**The non-obvious constraint the fix had to satisfy — worth its own line.** Three of the four arms fold interned **`u16` ids**, never the strings behind them, and the `Interner` assigns ids by order of first appearance. So two owner slices with identical STRUCTURE digest **identically no matter how their tokens are spelled** — renaming the vocabulary buys nothing. L4's "≥2 distinct digests across the in-scope owners" half would have stayed unfalsifiable. The window *shape* is therefore driven by `w % 5|3|7`, deliberately **coprime with the stride-4 arming cycle** (`(id - SPREAD_LO) % 4 + 1`) so each lens's own owner set spans several shapes instead of landing on one residue class. **Generalizable: when a digest folds identity rather than content, varying the content is a no-op; you must vary the structure — and the modulus that varies it must be coprime with whatever modulus selected the sample.**
16+
17+
**The diagnostic the failure lacked, added.** L1 now asserts each readout is non-empty **before** comparing digests, naming the corpus provenance in the message. An empty corpus now reports *"the corpus produced no extractable content … this is a CORPUS defect, not a lens collision"* instead of accusing the lenses. The original message was not merely unhelpful, it was **actively misleading** — it named a conclusion (distinct lenses collide) that the evidence did not support and that sent the first read of this failure looking at `stance.rs`.
18+
19+
**Falsifiability, verified in both directions rather than asserted.** (a) Make `run_lens(4, …)` return the Kant arm: L1 fails on `1790839806321388433` — a **non-empty** digest, so the assertion still discriminates content and did not merely become unreachable. (b) Restore the pre-fix filler corpus: the new precondition fires with the corpus-defect message, not the collision message. Measured after the fix: all four arms non-empty on the twin base, 4/4 distinct digests over one owner, and 4/7/6/6 distinct digests across each lens's owner set.
20+
121
## 2026-08-06 — E-A-REPEATABLE-TRANSFER-IS-NOT-IDEMPOTENCE-OVER-A-MULTI-FILE-DIRECTORY-1 — the PR #901 review round: four corrections, one of them load-bearing on a safety claim
222

323
**Status:** FINDING (review round, PR #901) — corrections to the two entries below, which stay as written so the first draft's claims remain visible. **Confidence:** High for the manifest correction (probe re-run, recorded in the knowledge doc's evidence table) and for the atomicity gap (it follows from Lance datasets being multi-file directories, which is not in dispute); the eviction plan remains a PROPOSAL and nothing in it is measured. Documentation-only.

.claude/board/ISSUES.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,47 @@
11
# Issues Log — Open + Resolved (double-entry, append-only)
22

3+
## ISS-D-IGN-B-REAL-CORPUS-PATH-IS-UNVERIFIED (2026-08-06) — OPEN, SURFACED BY THE CORPUS FIX
4+
5+
`crates/lance-graph-supervisor/tests/d_ign_b_lenses.rs` takes one of two corpus
6+
paths: `$BLW_KJV_TSV` (or `/tmp/kjv_verses.tsv`) if present, else the deterministic
7+
synthetic fallback. **Only the synthetic path has ever been executed here.** The
8+
TSV is absent from this environment and from CI, so the real-corpus branch of
9+
`load_or_synthesize_corpus` is, today, dead code that has never run.
10+
11+
**Why that is not merely a coverage note.** The fix in
12+
`E-D-IGN-B-CORPUS-PRODUCED-NOTHING-TO-READ-1` reshaped the synthetic corpus so
13+
every 48-verse owner slice yields all four lens arms non-empty — which is what
14+
**L3** demands (`empty < total` for each of z=1..4). The file's own module doc
15+
records the opposite measurement for the real corpus: Hegel *"is measured (not
16+
assumed) constant-false on this corpus shape (§12.3a″)"*, and Nietzsche is computed
17+
from Hegel (`stance.rs:483-496`) so it degrades with it. If that measurement still
18+
holds slice-for-slice, **setting `BLW_KJV_TSV` could make L3 fail for z=1 and z=2**
19+
— not because anything is broken, but because the two corpora exercise the arms
20+
differently and only one of them has been checked against the L-suite.
21+
22+
**So the two corpora are NOT interchangeable, and that is stated rather than
23+
papered over.** Kant and Wittgenstein: both corpora feed them (any emission feeds
24+
Wittgenstein; the KJV's `saw that` / `knew that` constructions feed Kant, and the
25+
synthetic corpus plants `they knew that they were X` deliberately). Hegel and
26+
Nietzsche: the synthetic corpus plants a reversal in **every** window by
27+
construction; in the KJV reversals are **rare and localized** (the probe fixture's
28+
whole point is that Genesis 2:17 / 3:4 / 3:6 carry them and the surrounding text
29+
does not), so most 48-verse windows would carry none.
30+
31+
**Deliberately NOT done in this PR.** Obtaining, vendoring or generating a real
32+
corpus is out of scope for a CI-failure fix, and guessing at its slice-by-slice
33+
behaviour would be exactly the paraphrase-instead-of-measurement move the
34+
falsifiability rule rejects. **The honest status is: the real-corpus path is
35+
unverified, and this entry exists so the next session that sets `BLW_KJV_TSV` reads
36+
this before concluding it broke something.** Closing it means running the L-suite
37+
against a real corpus and recording the per-arm measurements — at which point the
38+
module doc's §12.3a″ risk note can be re-confirmed or corrected with evidence.
39+
40+
**Not a defect in the fix.** The synthetic path is the one CI runs, it is green,
41+
and its assertions were verified falsifiable in both directions. This is a gap in
42+
what has been *measured*, recorded as such.
43+
44+
345
## ISS-REMOTE-URI-CONSTRUCTORS-PREDATE-THE-HYDRATION-DOCTRINE (2026-08-06) — OPEN, SURFACED BY REVIEW ON PR #901
446

547
`crates/lance-graph/src/graph/versioned.rs` ships `VersionedGraph::{s3, azure, gcs}`.

0 commit comments

Comments
 (0)