Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .claude/board/EPIPHANIES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
## 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

**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.

**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.

**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.

**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.

**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.

**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.

**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.**

**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`.

**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.

## 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

**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.
Expand Down
41 changes: 41 additions & 0 deletions .claude/board/ISSUES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,46 @@
# Issues Log — Open + Resolved (double-entry, append-only)

## ISS-D-IGN-B-REAL-CORPUS-PATH-IS-UNVERIFIED (2026-08-06) — OPEN, SURFACED BY THE CORPUS FIX

`crates/lance-graph-supervisor/tests/d_ign_b_lenses.rs` takes one of two corpus
paths: `$BLW_KJV_TSV` (or `/tmp/kjv_verses.tsv`) if present, else the deterministic
synthetic fallback. **Only the synthetic path has ever been executed here.** The
TSV is absent from this environment and from CI, so the real-corpus branch of
`load_or_synthesize_corpus` is, today, dead code that has never run.

**Why that is not merely a coverage note.** The fix in
`E-D-IGN-B-CORPUS-PRODUCED-NOTHING-TO-READ-1` reshaped the synthetic corpus so
every 48-verse owner slice yields all four lens arms non-empty — which is what
**L3** demands (`empty < total` for each of z=1..4). The file's own module doc
records the opposite measurement for the real corpus: Hegel *"is measured (not
assumed) constant-false on this corpus shape (§12.3a″)"*, and Nietzsche is computed
from Hegel (`stance.rs:483-496`) so it degrades with it. If that measurement still
holds slice-for-slice, **setting `BLW_KJV_TSV` could make L3 fail for z=1 and z=2**
— not because anything is broken, but because the two corpora exercise the arms
differently and only one of them has been checked against the L-suite.

**So the two corpora are NOT interchangeable, and that is stated rather than
papered over.** Kant and Wittgenstein: both corpora feed them (any emission feeds
Wittgenstein; the KJV's `saw that` / `knew that` constructions feed Kant, and the
synthetic corpus plants `they knew that they were X` deliberately). Hegel and
Nietzsche: the synthetic corpus plants a reversal in **every** window by
construction; in the KJV reversals are **rare and localized** (the probe fixture's
whole point is that Genesis 2:17 / 3:4 / 3:6 carry them and the surrounding text
does not), so most 48-verse windows would carry none.

**Deliberately NOT done in this PR.** Obtaining, vendoring or generating a real
corpus is out of scope for a CI-failure fix, and guessing at its slice-by-slice
behaviour would be exactly the paraphrase-instead-of-measurement move the
falsifiability rule rejects. **The honest status is: the real-corpus path is
unverified, and this entry exists so the next session that sets `BLW_KJV_TSV` reads
this before concluding it broke something.** Closing it means running the L-suite
against a real corpus and recording the per-arm measurements — at which point the
module doc's §12.3a″ risk note can be re-confirmed or corrected with evidence.

**Not a defect in the fix.** The synthetic path is the one CI runs, it is green,
and its assertions were verified falsifiable in both directions. This is a gap in
what has been *measured*, recorded as such.

## ISS-IDENTITY-CODEBOOK-ORDINAL-STABILITY (2026-08-06) — OPEN, PARTIALLY MITIGATED, RAISED BY REVIEW

`IdentityCodebook` derives each ordinal from a **sorted position**, so the ordinal
Expand Down
Loading