diff --git a/.claude/board/AGENT_LOG.md b/.claude/board/AGENT_LOG.md index 7aec0a961..b06bd8bab 100644 --- a/.claude/board/AGENT_LOG.md +++ b/.claude/board/AGENT_LOG.md @@ -1,3 +1,14 @@ +## 2026-07-29 — #867 merged + ZC-2a meta_basin lens migration (main thread only, no subagents) + +- **#867 closed the loop on a post-merge review.** CodeRabbit's #866 review finished after #866 had merged; its three real findings shipped as #867 and CodeRabbit re-reviewed #867 with **no blocking findings**, independently confirming all three points I asked it to re-check (bypass scope now accurate rather than overstated; concept-blindness sweep clean incl. docs and tests; `Resolution.axes` non-vacuously divergent at bundle=3/winner=2). It stated explicitly that it ran no Cargo commands — the test/clippy/fmt results are mine, which is the honest split. Merged `5373b00` after all 5 checks went green. +- **ZC-2a: 7 gathered `window:` parameters migrated IN PLACE** in `planner/nars/meta_basin.rs` — no twins, no deprecation window, because the family has zero callers outside its own file. Verified structurally rather than by grep alone: the lib compiled clean while only the in-file test module broke (21 call sites). +- **The finding is about the proof, not the migration** (EPIPHANIES `E-THE-EQUALITY-PASSED-WHILE-AN-AXIS-WAS-CONSTANT-1`): the equivalence test PASSED on two successive fixtures while the run still failed, both times on the per-axis anti-vacuity clause — every row graded `quorum = 0`, first because a sparse window has no quorum by construction, then because `quorum_mantissa` rounds `agreed*15/(peers*14)` DOWN and one agreeing locus floors to zero. An equality assert is satisfied by a constant axis; only the vacuity clause can see that the question was never posed. +- **Corrected a readiness label I inherited.** The ZC-2 tag file called these 7 "migratable NOW" on the strength of the lens twins existing. True, and it measured the callee: the fixtures use SPARSE positions and `resolve_chain` hops by absolute stream position, so every fixture had to be rebuilt around a dense row array + `visible`, not merely re-called. +- **Revert-tested, not argued:** deleting the `visible` filter fails 3 tests incl. `sparse: row count diverged at max_hops=0`. +- **Cross-repo:** OGAR pulled (18 commits, adds `ogar-fma` + `ogar-cpic` public reference surfaces); tesseract-rs local checkout noted 56 commits behind `origin/master` and deliberately NOT moved — another session may hold that branch. +- **Gates (central):** `cargo test -p lance-graph-planner` **324 + 4 passed / 0 failed**; `clippy -p lance-graph-planner --all-targets -- -D warnings` clean; `cargo fmt --check` clean. Scoped `-p` throughout. + + ## 2026-07-29 — medcare-rs reasoning seam: `lance_graph::reasoning` (main thread only, no subagents) - **Checked before designing, and that was the whole value.** Five of medcare's six asks already existed (`TruthValue`'s five NAL ops, `BeliefArena`, `rcr_abduce`, counterfactual substitution). The real finding was reachability: `lance-graph` declares `lance-graph-planner` optional behind `planner`, uses it internally in `lance_native_planner.rs`, and **never `pub use`s it** — medcare had the feature enabled and could reach nothing. A feature that pulls a crate in while exposing no path looks exactly like a working dependency until the first `use`. diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index 0d3ca450a..a201fd485 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -1,6 +1,25 @@ +## 2026-07-29 — E-THE-EQUALITY-PASSED-WHILE-AN-AXIS-WAS-CONSTANT-1 — the anti-vacuity clause found what the equivalence assert structurally could not + +**Status:** IN PR. **Confidence:** High — the vacuity was caught twice, by the guard, on two different fixtures, and the revert-test proves the equality assert bites when the behaviour actually diverges. + +**The ZC-2 `meta_basin` migration** moved 7 gathered `window: &[(usize, CausalWitnessFacet)]` parameters onto `(&WitnessLens, &impl Fn(usize) -> bool)`, reading registers through a cast into each row's own bytes. It migrated IN PLACE — no twins, no deprecation window — because the family has **zero callers outside its own file**, and the lens-migration card is explicit that a migration is finished only when the materializing path is *gone*, not merely unused. + +**The finding is about the proof, not the migration.** The equivalence test compares the pre-migration body (retained verbatim as a `#[cfg(test)]` oracle) against the lens form, field-by-field, across six hop budgets. It passed on the first fixture. It passed on the second. **Both times the run still failed — on the anti-vacuity clause**, which asserts that the compared gradings actually vary along each axis they claim to cover: + +1. **First fixture (sparse only).** Every row graded `quorum = 0`. A sparse window has no quorum *by construction* — no two rows are close enough to converge on an absolute target — so the quorum half of every comparison was `0 == 0`. The equality assert cannot notice this: constant-vs-constant is the most reliably passing comparison there is. +2. **Second fixture (dense, one agreeing locus).** Still all zeros. `quorum_mantissa` scales `agreed * 15 / (peers * 14)` and rounds **down**, so a single agreeing locus floors to 0. The fix was four agreeing loci — a number derived from the formula, not chosen until the formula was read. + +**Generalization: an equivalence test proves two implementations agree, never that they were asked anything.** Its assert is satisfied by an axis that is constant across the entire fixture space, and that is precisely the axis nobody checked. Every equality-style test therefore needs a companion clause per axis — *this comparison observed more than one value here* — or it certifies agreement on a question that was never posed. This is the `E-VACUOUS-ASSERTION-IS-THE-HOUSE-STYLE-1` family, but the mechanism is sharper: **the vacuity is not in the assert, it is in the fixture**, so reading the assert can never reveal it. + +**Second finding: "migratable NOW" was a statement about the twins, not about the work.** The ZC-2 tag file listed these 7 as unblocked because `quorum_mantissa_lens` / `trajectory_of_lens` had landed. True, and it understated the job — the fixtures use **sparse, non-contiguous positions** (`[0,1,2, 10,11,12, 20,21,22,23]`), and `resolve_chain` walks hops by absolute stream POSITION, so the gathered form could name positions with no rows between them. A lens indexes a dense row array, so the gaps have to *exist as rows* and be excluded by `visible`. Same result, different mechanism — and it means every fixture had to be rebuilt, not re-called. **A readiness label that measures the callee says nothing about the caller's shape.** + +**What the revert-test proved (the claim would otherwise be an argument).** Deleting the `visible` filter from `grade_rows` fails 3 tests, including the equivalence test with `sparse: row count diverged at max_hops=0` — so the equality assert does bite on real divergence, and the gap-exclusion test (`an_invisible_gap_is_excluded_even_though_the_row_exists`, which pins that row 1 EXISTS and is addressable while being invisible) fails independently. + +Cross-ref: `.claude/knowledge/zero-copy-lens-law.md`, `.claude/board/exec-runs/lens-migration-zc2.md` (outstanding table updated), `CLAUDE.md` § falsifiability rule. + ## 2026-07-29 — E-A-GUARANTEE-WITH-A-RE-EXPORTED-BYPASS-IS-NOT-A-GUARANTEE-1 — CodeRabbit's #866 review landed AFTER the merge; three of its five findings were real and one of them broke my headline claim -**Status:** IN PR (#867). **Confidence:** High — all five findings were checked against the code; the two declined are demonstrably false positives, the three accepted are fixed with tests still green. +**Status:** SHIPPED (#867, merged `5373b00`; CodeRabbit re-reviewed and approved with no blocking findings — it independently confirmed the rescoped bypass claim, the clean concept-blindness sweep, and the non-vacuous `Resolution.axes` divergence). **Confidence:** High — all five findings were checked against the code; the two declined are demonstrably false positives, the three accepted are fixed with tests still green. **Process finding first: a review that arrives after the merge still has to be worked.** CodeRabbit's #866 review (Run ID `16a75898`) was still processing when the operator merged, so the findings landed against code that was already on `main`. Nothing in the webhook stream says "you merged before the review finished" — the subscription simply ends. **When a PR merges with a review in flight, the review must be pulled explicitly**; otherwise findings against shipped code are silently dropped. Two of the three real ones here were rated Major. diff --git a/.claude/board/TECH_DEBT.md b/.claude/board/TECH_DEBT.md index 175eec7a2..6043d760d 100644 --- a/.claude/board/TECH_DEBT.md +++ b/.claude/board/TECH_DEBT.md @@ -1,5 +1,112 @@ # Technical Debt Log — Open + Paid (double-entry, append-only) +## TD-LENS-QUORUM-SCANS-THE-WHOLE-LENS (2026-07-29) + +**Codex P2 on #868, verified and MEASURED.** The lens migration changes the +complexity of grading, and Codex was right about the direction that regresses. + +`quorum_mantissa_lens(focal, lens, visible)` scans `0..lens.len()`, where the +gathered `quorum_mantissa(idx, window)` scanned the `k`-entry window. Grading +`k` visible rows therefore goes **Θ(k²) → Θ(N·k)** in peer work. Measured with a +counting `visible` closure (deterministic, unlike a wall-clock assert): + +| | N=512, k=8 sparse | +|---|---| +| lens: `visible` probes | **4608** | +| gathered: peer comparisons | **64** | + +**The half Codex did not mention — the trade is genuinely two-sided.** Gathered +`resolve_chain` resolved each hop with `window.iter().position(..)`, a linear +O(k) scan **per hop**; `resolve_chain_lens` uses `lens.at(pos)`, which is O(1). +So hop work goes **Θ(hops·k) → Θ(hops)**. + +Net per graded row: gathered `k·(1 + hops)` vs lens `N + hops`. The lens **wins** +whenever `N < k·(1 + hops) − hops` — dense windows and deep chains — and **loses** +when a small window is viewed through a large row array. That second case is +real: the zero-copy law says the row array IS the projection, so a full-table +lens with a handful of visible rows is a natural, not pathological, usage. + +> **⊘ OPERATOR CORRECTION (2026-07-29): the framing below is wrong, and so was +> the "needs a lifetime parameter" verdict on the BLOCKED `WitnessWindow.rows`.** +> Operator: *"lifetime parameter is the wrong lens — you have a corpus, you have +> hard facts, you just need to avoid circular reasoning."* +> +> The circularity: I assumed a window must HOLD facets, so getting off a gathered +> copy means holding a BORROW, so a lifetime propagates into `PlanContext`. Both +> horns are the same bad premise. **A window carries ADDRESSES, not data.** The +> corpus is the durable thing and already exists; positions are the hard facts; +> facets are resolved from them at read time. No copy, no borrow, no lifetime, +> nothing propagates — `WitnessWindow { rows: Vec<(usize, CausalWitnessFacet)> }` +> becomes a position list, and `PlanContext` is untouched. +> +> **This dissolves the P2 too, and the two were never separate problems.** The +> Θ(N·k) scan exists only because the selection is expressed as a PREDICATE over +> the whole corpus (`0..lens.len()` filtered by `visible`) instead of as the +> addresses already in hand. Iterate the position list and peer work is Θ(k) — +> bounded by the window, not the corpus — which is exactly the "bounded/visible +> position view" Codex asked for. The measured 4608-vs-64 figure stands as the +> cost of the predicate form; it is not a cost of lensing. +> +> Both items therefore collapse into one follow-up: **addresses-not-data**. +> Superseded reasoning retained below per append-only. + +**Not fixed in #868, deliberately.** The scan lives in `quorum_mantissa_lens`, +which is **shipped `lance-graph-contract` API** landed by the previous ZC-2 run +and already consumed by `dispatch_guard` — so meta_basin inherited the cost +rather than introducing it. Fixing it means either a new bounded-position-view +variant on that contract surface (additive, but a design call about what the +peer domain *is*) or having callers narrow the lens, which absolute positioning +makes non-trivial. Neither belongs inside a behaviour-preserving refactor. + +**Guarded meanwhile:** `grading_cost_scales_with_lens_length_not_window_size` +pins the shape — it asserts the Θ(N·k) sweep happens AND that the cost is not +superlinear in N. If someone lands the bounded view, that test fails and gets +updated deliberately rather than the improvement passing unnoticed. + +**AGENTS.md compliance:** the repo requires timing notes for performance- +sensitive changes. The counted-probe figures above are that note; no `cargo +bench` harness exists for this crate, and a wall-clock number on a shared runner +would have been less informative than the invariant count. + + +## TD-PLANNER-DEPENDENTS-NO-CI-BUILD (2026-07-29) + +**Two crates depend on `lance-graph-planner` by path and are built by NO CI job.** +Found while verifying ZC-2a's "zero external callers" claim (#868) — the claim +held, but the *gate* I first cited could not have caught a violation. + +| dependent | why CI misses it | +|---|---| +| `crates/lance-graph-osint` | **workspace-`exclude`d** — no workspace command reaches it, and no workflow names it via `--manifest-path` | +| `crates/cognitive-shader-driver` | workspace member, but its planner dep is `optional = true` behind `with-planner`, a feature **no** workflow enables | + +The other dependents are covered: `lance-graph` deps the planner through its +default `planner` feature and IS built (`build.yml` / `rust-test.yml` via +`--manifest-path crates/lance-graph/Cargo.toml`). + +**Why this is debt, not a one-off.** CI here is deliberately per-crate +(`--manifest-path`), not `--workspace` — a reasonable choice for build time. The +cost is that adding a crate, or gating a dep behind a feature, silently removes +it from coverage with no signal. A breaking change to a planner API can go green +through every gate and break `lance-graph-osint` on someone's next local build. + +**Verified manually for #868** (both clean, exit 0): +`cargo check -p cognitive-shader-driver --features with-planner` · +`cargo check --manifest-path crates/lance-graph-osint/Cargo.toml`. +Doing that by hand is exactly the thing that does not survive contact with the +next session, which is why this is filed rather than considered handled. + +**Options, cheapest first:** (a) add both to `style.yml`/`rust-test.yml` as two +more `--manifest-path` / `--features` steps; (b) a single +`cargo check --workspace --all-features` job accepting the build-time cost; +(c) leave as-is and accept that excluded crates are consumer-maintained — but +then say so in `CLAUDE.md`, because today nothing does. + +**Not scheduled** — needs a call on which option, and the shader-driver +`serve.rs`-in-two-bin-targets warning shows up in the same area and may want +fixing together. + + ## TD-LANCE-GRAPH-ALL-FEATURES-DELTA-BREAK (2026-07-29) `cargo clippy -p lance-graph --lib --all-features` fails: diff --git a/.claude/board/agent-tags/copy-codec.md b/.claude/board/agent-tags/copy-codec.md new file mode 100644 index 000000000..1213aeda0 --- /dev/null +++ b/.claude/board/agent-tags/copy-codec.md @@ -0,0 +1,163 @@ +# `copy-codec` — `derive(Clone, Copy)` verdicts across the four codec crates + +**Run:** 2026-07-29 · branch `claude/x265-x266-plans-review-h9osnl` +**Scope:** every `Copy` derive in `crates/holograph`, `crates/highheelbgz`, +`crates/bgz17`, `crates/bgz-tensor`. +**Mode:** EDIT ONLY. No cargo run (orchestrator compiles centrally). +**Census consumed:** `.claude/board/exec-runs/copy-derive-blast-radius.txt`. +**Mandatory reads honoured:** `zero-copy-lens-law.md`, +`ndarray/.claude/rules/data-flow.md` (the file is in the ndarray repo, not +lance-graph — noted for the next brief), `encoding-ecosystem.md` (P0 before +codec work), `AGENT_LOG.md` (read, not written). + +--- + +## Headline + +**Zero edits made — and that is the correct outcome, not an abstention.** + +- **1 VIOLATION in scope** (`holograph/src/bitpack.rs` `VectorSlice<'a>`) — it + was **already fixed by the concurrent `copy-tierA` sibling** between my first + and second read of the file. I verified the fix rather than duplicating it. + Re-writing it would have been a lost-write race on a shared checkout. +- **93 other Copy derives: all LEGITIMATE** owned-value microcopies + (data-flow.md §2). None carries a lifetime, a generic, or a reference field. +- **3 graded ELEVATED** (higher awareness stage than every input; one of them + is legitimately stored today). +- **2 findings the derive census structurally cannot see** — reported below, + not acted on. + +## Census correction (mechanical) + +The census's grep matched the literal `derive(Clone, Copy)`. An +order-insensitive scan of the same four crates finds **95 sites** (94 live + +`VectorSlice`'s, already stripped), where the census lists **73**. The **22 +missed** all spell it `derive(Debug, Clone, Copy)`: + +- `holograph` (10): `query/parser.rs:28,88,155,170,204` · `hamming.rs:37,209` + · `graphblas/mod.rs:55` · `hdr_cascade.rs:82,612` +- `highheelbgz` (6): `lib.rs:86,151,176` · `simd_hardened.rs:62,73,86` +- `bgz17` (2): `clam_bridge.rs:37,197` +- `bgz-tensor` (4): `fractal_descriptor.rs:30,275` · `zipper.rs:50,161` + +All 22 are value types, so no *verdict* changes — but the count does, and a +future sweep should match `Copy` as a word, not `Clone, Copy` as a phrase. +Extrapolated repo-wide, the true total is meaningfully above 369. + +## The one VIOLATION — verified, not re-fixed + +`crates/holograph/src/bitpack.rs:552` `VectorSlice<'a> { words: &'a [u64] }`. + +Borrow-carrying, and worse than the general case: `as_words(&self) -> &'a [u64]` +re-exports the borrow at the **full** `'a`, so a `Copy` duplicate outlives every +scope a reviewer can see. The owner is an Arrow/mmap buffer some other mailbox +holds. Exactly the `WitnessLens` shape stripped in `b3515ba`. + +The sibling's replacement comment is sound and cites the same ruling. I +independently verified the removal does not break anything, since that is the +half a report cannot establish for itself: + +- Every consumer takes the slice **by reference**: `Belichtung::meter_ref(q, + &slice)`, `StackedPopcount::compute_with_threshold_ref(q, &slice, r)`, + `xor_ref(&slice, key)` — `storage.rs:541,590,641,797`, `navigator.rs:1098`, + `bitpack.rs:946,964,993`. +- The only by-value use is a terminal **move**: `ZeroCopyCursor::next` returns + `Some((id, slice, total))` after its borrows end (`navigator.rs:1098-1119`). + `collect_all` drops it with `_`. +- **The sibling's own open worry (their tag line 108) is closed:** an exhaustive + match for `VectorSlice` + `.clone()` / `: VectorSlice` field / `Vec<…>` / + `[…]` over all of `crates/` returns **only** `get_slice -> Option>` + and two `use` lines. There is no `.clone()` in the `datafusion-storage`-gated + block or anywhere else, and the type is never stored in a field. + +## SIMD exception — checked, and NOT tripped (the P0 the other way) + +`VectorSlice` *is* the type that exists to hand SIMD a slice — it is the whole +`Arrow buffer → &[u64] → cascade` path in `bitpack.rs:520-548`. Removing `Copy` +does **not** convert it to an owned copy: the `&[u64]` into the backing store is +untouched, and every SIMD consumer already takes `&dyn VectorRef`. The +data-flow.md §1 / borrow-strategy.md invariant survives the fix intact. This +needed saying out loud, because "the lens is the SIMD path" is precisely the +argument that would have been used to keep the derive. + +## ELEVATED (higher rung than every input) + +Honest framing first: none of these sit on the `persona-vs-rung-ladder` content +rungs (0–1 observation · 2 verb atoms · 3 NARS recipes · 4 StyleFamily). They +clear the law's *structural* bar — a value of a different KIND, computed across +multiple reads, not reproducible by a cast of any single lane. + +| site | why it is an elevation | +|---|---| +| `bgz-tensor/src/hhtl_cache.rs:35` `RouteAction` | **Stored** (`HhtlCache.routes: Vec`, k×k). `build_route_table` (`:402`) computes each cell from the pair's distance **plus the population-relative p25/p75 of the whole k×k distribution, per-entry perceptual weights, and a triangle-shortcut search over every intermediate `c`**. A fact about the SET; no cast reproduces it. Inputs = measured L1 distances (observation); output = a cascade verdict. The doc-comment already says it: *"NOT just distance — it's the routing decision."* | +| `bgz17/src/clam_bridge.rs:197` `Lfd` + `generative.rs:51` `LfdProfile` | Local fractal dimension over a neighbourhood + `lfd_median` across the scope + CHAODA `anomaly_score` — all population-relative. Transient today, so no lane is claimed; **tenant-eligible** if ever persisted. | +| `bgz17/src/clam_bridge.rs:37` `LayerStats` | Aggregate over the whole run (per-layer resolution counts). Population fact, not a member. | + +## LEGITIMATE — the bulk (90 sites) + +All owned, no borrow, all small. Representative groups: + +- **Addresses** (the thing the law says you *should* copy instead of a view): + `highheelbgz` `SpiralAddress` / `SpiralAddr` / `NeuronPrint` ×2. `simd_hardened.rs:14` + states it outright: *"Not a projection — a READ INSTRUCTION into source data."* + `holograph` `PackedDn(u64)` / `EdgeDescriptor(u64)`, both `repr(transparent)`. +- **Palette bytes + codebook indices** (VALUES, per the brief): `PaletteEdge` + (3 B), `CodebookIndex(u16)`, `ScentByte(u8)`, `CrystalTriple`, `InlineEdge`, + `HhtlDEntry` (4 B), `HhtlF32Entry` (1 B), `SlotL` ([i8;8]). +- **Bands / caps / modes** — named verbatim in data-flow.md §2: + `bgz-tensor::belichtungsmesser::Band`, `QuarterSigmaBand` (its doc: *"NOT + stored — computed on the fly"*), `bgz17::simd::SimdLevel` (a `CpuCaps`), + `Precision`, `PaletteResolution`, `SigmaBand`, `CoarseBand`, all the + `GrB*` op enums, `Dimension`, `BlockMask`. +- **Wire/storage headers** — `holograph/storage_transport.rs` `StorageHeader` + (32 B, `repr(C, packed)`), `TransportHeader` (8 B), `StorageFlags`, + `MetaBlock128`, `VersionFlags` + the `repr(u8)` enums. These are the + *definition* of the bytes (round-tripped by `transmute_copy`), not a second + reading of bytes that already have a projection. See the finding below. +- **Inline schema markers** — all 12 in `holograph/width_16k/schema.rs`. Each + is a `pack`/`unpack` of bits held *inside* u64 words; no byte-aligned `&Self` + cast exists to prefer, so there is no lens being passed over. +- **Cascade results** — `StackedPopcount`, `Belichtung`, `MexicanHat`, + `TransformSpectrum`, `FractalDescriptor`, `ZipperDescriptor`, `PhaseDescriptor`. + +## Findings the derive census cannot see (REPORTED, not acted on) + +**F1 — `bgz-tensor/src/morton_cascade/mod.rs:34` `L4Tenant`: the missing lens +twin.** `from_bytes(&[u8; 12]) -> Self` materializes the V3 L4 palette tenant — +the same 12-byte-register-in-a-512-byte-stride geometry as `CausalWitnessFacet`, +which has the free `from_register_ref` cast. `L4Tenant` **cannot** have that +twin as written: it is not `repr(C)`/`repr(transparent)`, and its +`[(u8, u8); 3]` fields are Rust tuples, which carry **no layout guarantee** — a +`&[u8;12] → &Self` cast today would be unsound, not merely absent. The repair is +`repr(C)` (or a `[u8;12]` newtype) + a `ref_from_bytes`, which is a design change +well past "remove a derive," so I left it. Removing `Copy` here would be the +worst of both: it forbids the microcopy without creating the lens. +Also note `L4Tenant` has **zero consumers outside its own module tree** — +adjacent to the law's *"a type whose only constructors are `#[cfg(test)]` is a +shadow of storage"* clause. Worth a producer check before it grows. + +**F2 — `bgz17/src/scope.rs:31-45` `Bgz17Scope` stores THREE per-edge lanes over +the same edges** (`scent: Vec`, `palette_indices: Vec`, +`base_patterns: Vec`). By the letter of *"a projection is never +stored"* this is three stored readings of one content. **I did not rule on it, +because ruling would decide an architecture question above my brief:** this is +the certified HHTL precision ladder (`Precision::{Scent, Palette, Base, Exact}`, +`bgz17/src/lib.rs:78`; the atlas chain in `encoding-ecosystem.md` with measured +ρ = 0.937 / 0.965 / 0.992). Each rung is *lossy* relative to the one above, so +no cast reproduces a coarse lane from a fine one, and the coarse lane exists +precisely to avoid reading the fine one. Two readings are available and they +disagree: **(a)** a lossy coarsening is the same awareness stage at lower +resolution → "never stored"; **(b)** `scent` here is computed against the +**scope centroid** (`scope.rs:79-88`), making it population-relative and thus +elevation-shaped, not a pure coarsening. This is a live tension between the +zero-copy law and a FINDING-graded certified cascade, and it deserves an +operator ruling, not a subagent's edit. No `Copy` derive causes it — the `Vec` +fields do. + +## Coordination note + +`copy-tierA` and I overlapped on exactly one file. The shared checkout has no +lease, so the only thing that prevented a lost write was re-reading the file +before editing. Recommend the orchestrator partition future sweeps by **file**, +not by tier — a tier cuts across every crate boundary, so tier-scoped and +crate-scoped workers are guaranteed to collide. diff --git a/.claude/board/agent-tags/copy-cognitive.md b/.claude/board/agent-tags/copy-cognitive.md new file mode 100644 index 000000000..4d411fa83 --- /dev/null +++ b/.claude/board/agent-tags/copy-cognitive.md @@ -0,0 +1,220 @@ +# copy-cognitive — `derive(Clone, Copy)` verdicts for cognitive / callcenter / ontology + +Run date: 2026-07-29. Branch `claude/x265-x266-plans-review-h9osnl`. +Operator order: *"copies are forbidden, borrows are only for the same mailbox"*; +*"only cognitive achievements > tenant"*. +Mandatory reads done: `zero-copy-lens-law.md`, `data-flow.md` §2 (via ndarray +CLAUDE.md), `borrow-strategy.md` (q2 rules). Census: +`.claude/board/exec-runs/copy-derive-blast-radius.txt`. + +EDIT ONLY — no cargo run. 2 derives changed, both zero-cascade. + +## Headline + +**Not one `Copy` type in the three crates carries a borrow.** Zero lifetime +parameters across all 119 `Copy` sites. The four Tier-A callcenter entries the +census flagged (`FamilyEntry`, `OwlPivot`, `MetaAnchors`, `SuperDomainEntry`) +were flagged on `&'static`, which is the *opposite* of the violation — see +§"The `'static` verdict". + +The two VIOLATIONs found are a different shape than the census heuristic +looked for: **mutable state records whose `Copy` forks a single-writer +invariant.** Both were invisible to a lifetime-or-`&` grep. + +## ⚠ The census is INCOMPLETE for these three crates + +`copy-derive-blast-radius.txt` claims *"matches both orderings"*. It does not. +It matched `derive(Clone, Copy…` only; every `derive(Debug, Clone, Copy…)` +site is absent. Measured: + +| crate | in census | actual `Copy` derives | missing | +|---|---:|---:|---:| +| lance-graph-cognitive | 23 | 44 | 21 | +| lance-graph-callcenter | 22 | 30 | 8 | +| lance-graph-ontology | 11 | 45 | 34 | +| **total (these 3)** | **56** | **119** | **63** | + +Whole files are missing: `cognitive/fabric/{zero_copy,firefly_frame,gel,scheduler}.rs`, +`cognitive/spo/gestalt.rs`, `cognitive/search/{causal,certificate,hdr_cascade}.rs`, +`callcenter/{policy,rls,savant_reasoners,audit}.rs`, +`callcenter/transcode/{cam_pq_decode,spo_filter}.rs`, +`ontology/odoo_blueprint/**` (~30 sites), `ontology/soa_bake/mod.rs`. +**The global 369 figure is therefore a floor, not a count.** I screened all 63 +extras here myself (none carry a borrow); the other crates' extras have not +been screened by anyone. + +Reproduce: `grep -rn "Copy" --include=*.rs -A2 /src | grep derive`. + +## Changes made (2) + +### 1. VIOLATION — `ChunkHeader`, `lance-graph-cognitive/src/core_full/scent.rs:24` + +`#[derive(Clone, Copy, Debug)]` → `#[derive(Clone, Debug)]`. + +A record OF the substrate, not a value. `ScentIndexL1` **owns** +`Box<[ChunkHeader; BUCKETS]>` and **mutates it in place** — `on_append` writes +`scent`/`count`/`last_access`, `set_decision` and `set_plasticity` write their +fields. `Copy` mints a second, immediately-stale reading of live bucket state, +and `scent` is itself a lossy projection of fingerprints that already live in +the data file (a second stored reading of bytes that already have one). + +Cascade: **none.** Every read path is already `&`-borrowed (`headers.iter()`, +`&mut self.headers[i]`); construction is `std::array::from_fn` (no `Copy` +needed — a `[expr; N]` repeat literal would have required it, and none exists); +`write_headers`/`read_headers` take `&`/`&mut` slices; `ScentIndexL2` composes +`ScentIndexL1` by value. `ChunkHeader` appears in **no other file in the +workspace** (grepped). `Clone` retained. + +### 2. VIOLATION — `AuditChain`, `lance-graph-callcenter/src/unified_audit.rs:196` + +`#[derive(Clone, Copy, Debug)]` → `#[derive(Clone, Debug)]`. + +The strongest finding of the run. `AuditChain` is single-writer chain state +(`advance(&mut self)` writes `self.last_root`), and `last_root` is a second +holding of a root **already durably recorded** on the last emitted event. A +`Copy` silently forks the chain: two advancers stamping distinct events as +successors of the same `prev_merkle` — which is precisely the tamper signature +`verify_chain` exists to detect, minted by the type system with nothing in a +diff to point at. §13.4's cross-domain unlinkability rests on one salt, one +root, one writer. + +The correct shape is already present structurally and the derive was +undermining it: `UnifiedBridge` holds `Mutex` (one-writer mailbox) +and the sanctioned way to continue a chain elsewhere is +`AuditChain::resume(.., last_root)`, which is *explicit about which root it +claims*. + +Cascade: **none.** All uses are `AuditChain::new` / `::resume` / +`Mutex::new` / `&mut` `advance` through the lock; `audit_root()` copies out +`last_root` (an `AuditMerkleRoot` u64 — untouched). `Clone` retained +deliberately: it forks too, but an explicit `.clone()` is greppable and +nothing calls it today. **Follow-up worth an operator call: `Clone` on a +merkle advancer is arguably also forbidden** — I did not remove it because +that exceeds the stated scope (the census targets `Copy`). + +## The `'static` verdict (why the 4 Tier-A callcenter entries are NOT violations) + +`FamilyEntry` (`family_table.rs:132`), `OwlPivot` (`odoo_alignment.rs:65`), +`MetaAnchors` (`super_domain.rs:125`), `SuperDomainEntry` (`super_domain.rs:181`) +were flagged for `&'static str` / `&'static [u8]` / `&'static [OgitFamily]`. + +**A `'static` borrow has no mailbox to escape.** It borrows immutable rodata +that outlives every compartment and has no writer, so it can neither dangle +nor drift. And `Copy` on a struct of `&'static` fields duplicates **pointers, +not content** — the grey/white fence in `zero-copy-lens-law.md`: *"cross-tenant +pointers are legitimate; cross-tenant values are not."* These rows are the +already-correct shape; forcing them to own their strings would be the +violation. Same verdict covers ontology's ~30 `odoo_blueprint` baked rows — +`OdooEntityPairing` holds `&'static OdooEntity`, the reference-not-copy shape +done right. + +## ELEVATED — the audit-event question, answered + +The operator asked whether `UnifiedAuditEvent` / `AuditChain` / +`AuditMerkleRoot` are higher-rung achievements or copies of something already +recorded. They split three ways, which is the interesting part: + +- **`AuditMerkleRoot` (`:94`) — ELEVATED.** `chain(prev_root, salt, bytes)` is + a **cross-term**: a computation across multiple reads yielding a value of a + different KIND — a fact about the *sequence*, not a member of it. It is + reproducible by no cast from any lane. This is exactly the Gadamer-refined + test in `zero-copy-lens-law.md` and the shape of the shipped + `Locus::Quorum` / `Contradiction` precedent. Rung: inputs are rung 0–1 + observation (one authorize() decision); output is a chain-integrity witness + about the history of decisions. Storing is legitimate — that IS the + calcification. `Copy` kept (a `u64`). +- **`UnifiedAuditEvent` (`:135`) — ELEVATED, keep `Copy`.** Its input fields + (tenant/owl/op/role-hash) do each exist elsewhere, so field-by-field it + looks like a copy — but the event *as emitted* carries `merkle_root` + + `prev_merkle`, so the row is the elevation, not a gathered view of the + request. It is **immutable once stamped**, so a copy cannot drift; 42 bytes + of scalars, no borrow. The doc-comment's stated reason for hashing the role + rather than storing `&'static str` ("so the event is `Copy` + fixed-size") + is a durability argument, and it holds. +- **`AuditChain` (`:196`) — VIOLATION.** Not the witness; the *advancer*. See + change 2. The distinction that matters: **the witness may be copied because + it is finished; the advancer may not because it is still being written.** + +## Full verdict table + +`L` = LEGITIMATE (owned value microcopy, data-flow.md §2 REQUIRES `Copy`), +`E` = ELEVATED, `V` = VIOLATION (edited). + +### lance-graph-cognitive + +| path:line | type | V | reason | +|---|---|---|---| +| container_bs/adjacency.rs:33 | `PackedDn` | L | newtype over `u64`; a hierarchical address = white matter (a displacement, not content). Watch-listed as "record OF substrate" — it is not: no backing store exists that it projects; the packed `u64` IS the address. | +| container_bs/adjacency.rs:232 | `InlineEdge` | L | 2 bytes (`verb`,`target_hint`), produced by `unpack(u16)` and consumed immediately; never gathered (`grep` finds no `Vec` / `[InlineEdge; N]`). The stored form is the `u16` in container words 16-31; this is a transient decode, not a second store. | +| container_bs/adjacency.rs:282 | `EdgeDescriptor` | L | newtype over `u64`, same argument. The zero-copy views in this file are the separate `*View<'a>` types (lines 336-606) — correctly **not** `Copy`. | +| core_full/index.rs:74 | `Key` | L | `#[repr(transparent)] u64`. | +| core_full/index.rs:134 | `Entry` | L | 3×`u64` = `(prefix, offset, target)`: pure displacements, write-once, read via `&Entry` iterators. White matter. | +| core_full/scent.rs:24 | `ChunkHeader` | **V** | mutable owned substrate state + a content projection (`scent`). **Edited.** | +| core_full/scent.rs:480 | `BucketAddr` | L | ≤3-byte address enum (`L1/L2/L3`). | +| fabric/subsystem.rs:4 | `Subsystem` | L | fieldless enum. | +| grammar/causality.rs:37 | `DependencyType` | L | fieldless enum. | +| search/cognitive.rs:63 | `QualiaVector` | L | 8×`f32` + `RelevanceScores`; the `QualiaColumn` read of the AGI-as-glove doctrine, passed by value. Textbook data-flow §2. | +| search/cognitive.rs:280 | `SearchVia` | L | fieldless enum. | +| search/cognitive.rs:309 | `RelevanceScores` | L | 5×`f32` score record. | +| spo/cognitive_codebook.rs:22,70,95,112,129,148,165,190,215 | `CognitiveDomain`, `NsmCategory`, `QualiaChannel`, `NarsCopula`, `NarsInference`, `CausalityType`, `TemporalRelation`, `YamlTemplate`, `ThematicRole` | L | 9 fieldless codebook enums — the register (I-VSA-IDENTITIES Test 0). | +| spo/cognitive_codebook.rs:248 | `CognitiveAddress` | L | packed `u64` address. | +| spo/sentence_crystal.rs:85 | `Coord5D` | L | 5×`usize` grid coordinate. | +| *(21 uncensused)* fabric/{zero_copy,firefly_frame,gel,scheduler}.rs, spo/gestalt.rs, search/{causal,certificate,hdr_cascade}.rs | `AddrRef`, `EdgeRef`, `FrameHeader`, `ConditionFlags`, `Instruction`, `LanguagePrefix`, `ExecutionContext`, `Location`, `MexicanHat`, `AntialiasedSigma`, `TiltReport`, `GestaltState`, … | L | screened: all scalar/address/enum, no lifetimes, no `&` fields. `zero_copy.rs`'s `EdgeRef` = 2×`AddrRef` + `u32`; the borrowing type there (`ZeroCopyExecutor<'a>`) is correctly not `Copy`. | + +### lance-graph-callcenter + +| path:line | type | V | reason | +|---|---|---|---| +| family_table.rs:132 | `FamilyEntry` | L | `&'static` baked row — see §"The `'static` verdict". | +| odoo_alignment.rs:65 | `OwlPivot` | L | same. | +| super_domain.rs:125 | `MetaAnchors` | L | same (`Option<&'static str>` ×2). | +| super_domain.rs:181 | `SuperDomainEntry` | L | same (`&'static [OgitFamily]`). | +| audit_sink/composite.rs:16 | `FanoutMode` | L | fieldless enum. | +| audit_sink/mod.rs:63 | `NoopAuditSink` | L | ZST. | +| dn_path.rs:18 | `DnPath` | L | 6×`u64` segment hashes = an address (heel/hip/branch/twig/leaf). | +| family_table.rs:62 | `OwlCharacteristics` | L | `#[repr(transparent)] u8` bitfield. | +| family_table.rs:181 | `PerFamilyCodebook` | L | ZST placeholder. | +| lance_membrane.rs:117 | `ActorState` | L | 3 scalars; its doc says it exists to be an **atomic snapshot under one lock** defeating the F-01 identity-tear race — i.e. the owned-microcopy pattern applied deliberately, and `Copy` is what makes the tear-free read cheap. | +| super_domain.rs:45,109,153 | `SuperDomain`, `DolceMarker`, `ComplianceRegime` | L | `#[repr(u8)]` fieldless enums. | +| transcode/parallelbetrieb.rs:71 | `DriftKind` | L | fieldless enum. | +| transcode/zerocopy.rs:48 | `ArrowTypeCode` | L | enum, one `usize` payload variant. | +| unified_audit.rs:51,71 | `AuthOp`, `AuthDecision` | L | `#[repr(u8)]` fieldless enums. | +| unified_audit.rs:94 | `AuditMerkleRoot` | **E** | the cross-term. See §ELEVATED. | +| unified_audit.rs:135 | `UnifiedAuditEvent` | **E** | finished witness, immutable once stamped. See §ELEVATED. | +| unified_audit.rs:196 | `AuditChain` | **V** | single-writer advancer; `Copy` forks the merkle chain. **Edited.** | +| unified_bridge.rs:75,110,169 | `OgitFamily`, `OwlIdentity`, `TenantId` | L | 1/3/4-byte identity addresses; `OgitFamily`'s own doc: *"Pure address. No reasoning, no string lookup."* | +| *(8 uncensused)* policy.rs, rls.rs, savant_reasoners.rs, audit.rs, transcode/{cam_pq_decode,spo_filter}.rs | `PolicyKind`, `Op`, `RedactionMode`, `DpMechanism`, `RegistryMode`, `SavantError`, `StatementKind`, `PassthroughDecoder` | L | screened: enums + a ZST decoder. | + +### lance-graph-ontology + +| path:line | type | V | reason | +|---|---|---|---| +| bridge.rs:144 | `EntityRef` | L | despite the name, carries **no borrow** — wraps `SchemaPtr` (2×`u32`). A pointer by value. | +| bridge.rs:149 | `EdgeRef` | L | same. | +| hydrators/dolce_odoo.rs:32 | `DolceCategory` | L | fieldless enum. | +| hydrators/owl.rs:139 | `Format` | L | fieldless enum. | +| namespace.rs:26 | `NamespaceId` | L | `u8` newtype. | +| namespace.rs:124 | `SchemaPtr` | L | packed `u32` + context `u32`; the canonical address. Note `with_context_id` returns a new value — builder, not a compute path. | +| namespace.rs:196 | `SchemaKind` | L | `#[repr(u8)]` enum. | +| proposal.rs:114 | `IdentityCodec` | L *(with a note)* | 19 bytes of owned scalars, no borrow → `Copy` is not the violation. **But** structurally it is 4 lossy readings (`cam_pq_code`, `base17_head`, `palette_key`, `scent`) of a fingerprint whose warm form its own doc says *"stays on `BindSpace`"* — the "second stored projection" silhouette. That is a **storage-design** question (the HHTL codec ladder needs all rungs resident to skip), not something removing a derive fixes. Reported, not edited; flagging it here so it is not rediscovered as a derive problem. | +| proposal.rs:124 | `QualiaMeta` | L *(with a note)* | 80 bytes of scalars, transient dispatch bundle → derive is fine. Note for the record: it bundles three of the four SoA axes (`qualia`/`meta`/`edge`), which the AGI-as-glove doctrine warns against wrapping ("breaks the SIMD sweep"). Again a shape question, not a `Copy` question. | +| proposal.rs:213 | `MappingHandle` | L | receipt = `(SchemaPtr, row_index)`; addresses. | +| ttl_parse.rs:560 | `SubjectKind` | L | fieldless enum. | +| *(34 uncensused)* odoo_blueprint/** (~30), soa_bake/mod.rs (3), … | `OdooEntity`, `OdooField`, `OdooMethod`, `OdooStateMachine`, `OdooEntityPairing`, `StructuralSignature`, `SchemaVersion`, `EdgePair`, … | L | screened: all `&'static` baked blueprint rows (see §`'static`) plus scalar signatures. `OdooEntityPairing` holds `&'static OdooEntity` — reference-not-copy, the shape we want. | + +## Cascades refused + +**None.** Both edits are confined to their declaring file; no caller relied on +`Copy` for either type. Nothing was left unremoved for cascade reasons. + +The one thing I deliberately did **not** do: remove `Clone` from `AuditChain` +(see change 2). Scope call, flagged for the operator rather than taken +unilaterally. + +## Gates + +Not run — brief is EDIT ONLY; orchestrator compiles centrally. Expected +surface: `cargo test -p lance-graph-cognitive`, `-p lance-graph-callcenter`. +Both edits remove a trait impl, so any breakage would appear as +`error[E0507]: cannot move out of ...` / `use of moved value` at a call site — +I found no such site by grep, but the compiler is the authority. diff --git a/.claude/board/agent-tags/copy-contract.md b/.claude/board/agent-tags/copy-contract.md new file mode 100644 index 000000000..23e0c6070 --- /dev/null +++ b/.claude/board/agent-tags/copy-contract.md @@ -0,0 +1,64 @@ +# copy-contract — v3-envelope-auditor run (2026-07-29) + +Branch `claude/x265-x266-plans-review-h9osnl`. Scope: `derive(*Copy*)` sites in +`crates/lance-graph-contract/`. Operator order: *"copies are forbidden, borrows +are only for the same mailbox"*, *"only cognitive achievements > tenant"*. + +## Census correction + +The blast-radius file lists ~80 contract sites; the crate actually has **316** +`derive(… Copy …)` sites (the census matched two literal orderings, not the +general form). Verdicts below cover the full 316 by class; every site ≥ 512 B or +carrying a lifetime was read individually. + +## Verdicts (summary) + +- **VIOLATION — 3.** `AwarenessPlane16K` (2 KB), `SplatPlaneSet` (12 KB), + `NodeRow` (512 B). +- **ELEVATED — the cycle/basin aggregates** (`ShaderResonance`, `AlphaComposite`, + `MetaSummary`, `MaterializeProvenance`, `RungElevator`, `SpoBase17`, `Heel`, + `CamSplatCertificate`) — all strictly above the tenants they derive from, so + `Copy` is the operator's own carve-out. +- **LEGITIMATE — everything else**, incl. the two the brief flagged as suspects: + `ColumnWindow` (an index pair, not a view — its "zero-copy borrow" doc-comment + is a misnomer) and `ShaderHit` (a row ADDRESS + computed measures, the + I-VSA-IDENTITIES points-to-content shape). `QualiaI4_16D` is `repr(C) u64` — + data-flow §2 verbatim. + +## Changes made (edit-only; not compiled here) + +`crates/lance-graph-contract/src/splat.rs` +- `AwarenessPlane16K` (was :87) — `Copy` removed, `Clone` retained, why-comment added. +- `SplatPlaneSet` (was :176) — `Copy` removed, `Clone` retained, why-comment added. + +Cascade for both: **zero**. Every consumer (`crates/jc/examples/splat_*.rs`, +`crates/lance-graph-contract/src/splat.rs` tests) uses `&` / `&mut` / +`vec![… ; n]` / `::zero()` / `::default()`. `vec!`-repeat needs `Clone`, not +`Copy`. + +## Refused (cascade reported, not executed) + +`NodeRow` (`canonical_node.rs:724`) — ruled VIOLATION, derive LEFT IN PLACE. +Removal is one line here plus **5 call-site fixes in a crate outside this +scope**, all in `crates/lance-graph-planner/examples/probe_sudoku_teacher.rs`: +`:307`, `:355`, `:1818` (`[blank_row(); 81]` → `core::array::from_fn(|_| blank_row())`) +and `:425`, `:793` (`let mut world = *grid;` → `grid.clone()`). +`:425` carries the comment `// NodeRow is Copy — an explicit, deliberate clone` +— the author needed a clone and `Copy` handed over a 41 KB substrate duplicate +for free. That comment is the finding. + +## Handed to the Tier-A agent (census gap) + +The Tier-A list found only `NodeRowPacket<'a>` in this crate. Six more +lifetime-carrying `Copy` types exist: `class_view.rs:1181 RenderRow<'a>`, +`class_view.rs:1202 ValueRow<'a>`, `unicharset_adapter.rs:54 UniCharCall<'a>`, +`:77 UniCharOut<'a>`, `recoder_adapter.rs:88 RecoderCall<'a>`, `:120 RecoderOut<'a>`. +Untouched by me. (`cognition/entity.rs:55/61/68/75` are `&'static str` handles — +program-image data, not substrate; not Tier A.) + +## Naming hazard recorded + +`recipe_substrate.rs:45 SubstrateView` — named "View", doc'd "the substrate a +recipe reasons over", carries NO borrow (three owned facets, 40 B, built by +`::new` and immediately `.project()`ed). LEGITIMATE, but it is the one name in +the crate that trains a reader to believe a gathered copy is a borrow. diff --git a/.claude/board/agent-tags/copy-deepnsm.md b/.claude/board/agent-tags/copy-deepnsm.md new file mode 100644 index 000000000..6ec5297b1 --- /dev/null +++ b/.claude/board/agent-tags/copy-deepnsm.md @@ -0,0 +1,254 @@ +# copy-deepnsm — `derive(Clone, Copy)` verdicts for deepnsm / deepnsm-v2 / reader-lm + +**Agent:** copy-derive auditor (deepnsm family) +**Branch:** `claude/x265-x266-plans-review-h9osnl` +**Mode:** EDIT ONLY. No `cargo build`/`check`/`test`/`clippy`; no worktree. +**Read first:** `AGENT_LOG.md`, `.claude/knowledge/zero-copy-lens-law.md`, +`.claude/rules/data-flow.md` §2 (in `/home/user/ndarray/`), +`.claude/rules/borrow-strategy.md` (in `/home/user/q2/`) — neither rules file +exists under `lance-graph/.claude/rules/`; both were loaded from the sibling +repos where they live. + +--- + +## Headline — 63 sites, 1 violation, 0 edits by me (a sibling session got there first) + +The census listed **26** sites for these three crates. My own exhaustive grep +found **63**. The census missed `deepnsm/src/comprehension.rs`, +`markov_bundle.rs`, `quantum_mode.rs`, and **all of `deepnsm-v2` except +`shape.rs:311`** (20 sites) — it matched only `#[derive(Clone, Copy…)]`, and +deepnsm-v2's house style is `#[derive(Debug, Clone, Copy…)]`. + +Of the 63, exactly **one** carries a borrow: `Collapse<'a>`. It was already +fixed by the concurrent `copy-tierA` session (see § Overlap). The other 62 are +LEGITIMATE or ELEVATED. **No derive should be removed from any of them** — and +for many, removing it would break `data-flow.md` §2 in the other direction. + +The most valuable finding is NOT a derive at all: **`WitnessStream::window_at` / +`window_range` (`deepnsm-v2/src/wave.rs:133,146`) return +`Vec<(usize, CausalWitnessFacet)>`** — byte-for-byte the gathered-window shape +the zero-copy law names as its canonical measured instance. See § The real one. + +--- + +## Method — provenance, not size + +The operator's question per site: *is this a VALUE the reasoning layer passes by +value, or a RECORD of substrate bytes owned elsewhere?* Size does not decide it. +Three mechanical tests, in order: + +1. **Declared lifetime parameter** (`struct X<'a>` / `enum X<'a>`). A scripted + scan over all 66 `.rs` files extracting each `Copy` type's full body and + testing for `&'`, `: &`, `&[`, `&str`, `&mut` **and** a lifetime on the decl + returned **exactly one hit** — `Collapse<'a>`. Zero `&'static` false + positives in these three crates (unlike the workspace-wide Tier A list). +2. **Byte-slab constructor.** Grepped every + `from_bytes` / `from_le_bytes` / `from_slice` / `ref_from` in scope. Three + hits, none on a `Copy` type in the census (`similarity.rs` only). + Specifically: **`Fingerprint16K` has no byte-slab constructor at all** — every + path (`ZERO`, `from_centroid(u16)`, `from_centroid_semantic`, `xor`, `bundle`) + is generative, and `as_bytes` is the *outgoing* lens. +3. **Gather-out-of-a-live-store.** Grepped `.copied()` / `.cloned()` / + `.to_vec()` / deref-in-map across the three crates. This is what surfaced + `wave.rs`. + +--- + +## The one VIOLATION + +| site | type | verdict | reason | +|---|---|---|---| +| `crates/deepnsm/examples/homograph_collapse.rs:63` | `Collapse<'a>` | **VIOLATION** | `Unique(&'a str, u32)` holds a borrow into `Sense::lemma`, a `String` owned by the caller's `HashMap>`. A `Copy` borrow duplicates silently — it can be stored beside the original and leave the compartment owning the sense table with no move and nothing in a diff to point at. | + +**Cascade: none.** `Collapse` is only ever matched or moved — `match +collapse(…)` (l.120), the tuple moves `(p, so)` (l.157) and +`(collapse(…), collapse(…))` (l.213). No `.clone()` anywhere. Both `Clone` and +`Copy` are removable together with zero call-site change. + +**I did not make this edit** — see § Overlap. The landed edit is correct and is +what I would have written. + +## The watch-list — all LEGITIMATE, with the provenance that settles each + +The operator flagged six as "LARGE `Copy` structs". **Four of the six are not +large**, and none is a record of bytes owned elsewhere: + +| site | type | size | verdict | reason | +|---|---|---|---|---| +| `deepnsm/src/fingerprint16k.rs:19` | `Fingerprint16K` | **2048 B** | LEGITIMATE | The only genuinely large one. **Generative provenance**: a pure function of a `u16` centroid (golden-ratio hash), or an XOR/bundle of other fingerprints. No slab it could be a second reading of. This is `data-flow.md` §2's named `Fingerprint` microcopy at 16 K width. *Separate finding:* **zero consumers outside its own module** (see § Adjacent). | +| `deepnsm/src/signed_crystal.rs:222` | `Crystal4096` | **2 B** | LEGITIMATE | `#[repr(transparent)] (u16)`. Three packed 4-bit axes, computed from three `i8` offsets. A coordinate the reasoning layer passes by value. | +| `deepnsm/src/signed_crystal.rs:320` | `SignedSentenceCrystal` | **16 B** | LEGITIMATE | `P64` (u64) + `Crystal4096` (u16). The two fields are **independent** — `coord` is built from window offsets, not folded out of `p64` — so it is not a projection of its own other field. Contrast `Sentence64` below. | +| `deepnsm/src/sentence_transformer64.rs:108` | `P64` | **8 B** | LEGITIMATE | `#[repr(transparent)] (u64)`. 8 lanes computed from vocabulary ranks + grammar tags + NSM masks. | +| `deepnsm/src/sentence_transformer64.rs:260` | `Cam4096` | **2 B** | LEGITIMATE *as a type* | `#[repr(transparent)] (u16)`. But see § Stored projections — it is *stored beside* the `P64` it folds from. | +| `deepnsm/src/sentence_transformer64.rs:341` | `Perturbation4x4` | **16 B** | LEGITIMATE | `[u8; 16]` of signed nibble pairs, built by `local_tile` from two step sizes. A generated tile, owned by nothing else. | +| `deepnsm/src/sentence_transformer64.rs:560` | `Sentence64` | **~14 B** | LEGITIMATE *as a type* | See § Stored projections for the `cam` field. | +| `deepnsm/src/cam64.rs:37` | `Cam64` | **8 B** | LEGITIMATE | `(u64)`, 8 lanes. The module doc is explicit that it is a locality **key**, not the truth — an identity, per `I-VSA-IDENTITIES`. | +| `deepnsm/src/window.rs:87` | `WindowEntry` | **~24 B** | LEGITIMATE | Holds **vocabulary ranks** (`[u16; 4]`), i.e. identity pointers into the vocabulary, never content — exactly the `I-VSA-IDENTITIES` shape. Its owner `SentenceWindow` is a `[WindowEntry; 11]` ring buffer that owns them outright and is itself **not** `Clone`/`Copy`. Pulling one entry out of a ring you own is a microcopy, not an escape. | + +## LEGITIMATE — small owned value microcopies (`data-flow.md` §2 REQUIRES `Copy`) + +PoS tags, parser states, roles, flags, small SPO records. Removing `Copy` here +breaks the rule in the other direction. 39 sites: + +- **deepnsm/src** — `pos.rs:7 PoS` · `parser.rs:19 State`, `:34 ModRelation` · + `morphology.rs:36 MorphFlags(u16)` · `spo.rs:23 SpoTriple(u64 packed)` · + `episodic_spo.rs:39 DependencyRole`, `:54 ClauseRole`, `:67 DiscourseRole` · + `reader_state.rs:50 LeftCornerTrigger` · `crystal_neighborhood.rs:50 + NeighborhoodMetric` · `markov_bundle.rs:18 Kernel`, `:37 GrammaticalRole` · + `quantum_mode.rs:14 PhaseTag(u128)`, `:49 HolographicMode` · + `signed_crystal.rs:88 HorizonPolarity`, `:152 SignedOffset4(u8)` · + `window.rs:62 ExpectedReason`, `:79 ExpectedSlot` · + `sentence_transformer64.rs:401 SplatNeighbour`, `:521 EpisodicSpoHint` +- **deepnsm/examples** — `causal_edge_v3_facet.rs:52 Verb`, `:69 CausalEdgeV3` · + `gridlake_coca_wire.rs:17 Cell` · `gridlake_spo_ngrams.rs:21 Cell` · + `homograph_collapse.rs:37 Role` · `spo_anaphora_nibble.rs:49 Noun`, + `:56 Pron` · `spo_markov_kg.rs:59 Truth`, `:79 Role` +- **deepnsm-v2/src** — `belief.rs:32 Stamp(u64)`, `:55 Copula`, `:79 CStmt` · + `fsm.rs:39 Pos`, `:65 Tagged`, `:82 State`, `:95 Rel` · `spo.rs:12 Spo` · + `shape.rs:49 ShapeClass`, `:65 Representation`, `:311 Color` (a `#[cfg(test)]` + DFS colour) +- **reader-lm/src** — `classifier.rs:5 HtmlStructure` + +Two worth naming individually: + +- `deepnsm/examples/causal_edge_v3_facet.rs:69 CausalEdgeV3 + { classid: u32, payload: [u8; 12] }` — the canonical V3 4+12 facet. **Not** a + lens violation: the probe *constructs* it via `new()` + bit setters and is the + origin of its own bytes; there is no substrate slab underneath it to be a + second reading of. (In the real substrate the read path is + `from_register_ref(&[u8;12]) -> &Self`, a cast — that contract is unaffected.) +- `gridlake_*.rs Cell` — accumulators in a `Vec` the example owns, and + already accessed through `&grid[c]` / `&mut grid[rank]`. The `Copy` is + **unexercised** (only `Clone` is needed, for `vec![Cell::default(); GRID]`). + Latent, not live. + +## ELEVATED — strictly higher rung than every input + +Facts *about* a set of beliefs/edges, not members of it. Per the 2026-07-29 +refinement of the rung test these are reproducible only by a computation across +multiple reads yielding a value of a **different kind** — the +`Locus::Quorum` / `Contradiction` shape. Storing and passing them by value is +correct. 12 sites, all `deepnsm-v2/src`: + +| site | type | rung it lifts to | +|---|---|---| +| `shape.rs:103 ShapeReport` | graph-shape class + recommended representation over an edge set | rung 3 — a tactic recipe selection (`Representation`), not an observation | +| `shape.rs:188 MeasuredShape` | + measured coverage / amortization / residue | rung 3, with the measurement attached | +| `reason.rs:58 GateReport` | resolvability %, acyclicity, termination over a derivation | rung 3 — a property of the inference run | +| `evidence.rs:338 ForwardGateReport` | real vs null vs baseline ρ | rung 3 — the falsifier's verdict, exists in no belief | +| `evidence.rs:39 EvidenceBasin` | per-subject confidence/contradiction/rung aggregates | rung 2→3 — a fact about the belief population | +| `basin.rs:41 BasinCode` | basin width + members + contradiction | rung 2→3, same shape | +| `basin.rs:152 HeldOutGate` | held-out ρ vs floor | rung 3 — a gate verdict | +| `introspect.rs:24 ProvenanceReport` | derived vs composed counts | rung 3 — a fact about derivation history | +| `introspect.rs:79 ConfidenceAnswer` | c1/c2/δ across two version reads | rung 3 — **a cross-term**: exists in neither read alone | +| `belief.rs:110 ReviseOutcome` | Admitted / Revised{synthesis_c, depth} / Chosen | rung 3 — the NARS revision verdict; `synthesis_c` is the Horizontverschmelzung cross-term | +| `episodic_spo.rs:215 BasinClassification` | Reinforcement / Novelty / Wisdom / Contradiction / Epiphany | rung 3 — a contradiction is strictly higher than the observations it reconciles | +| `episodic_spo.rs:90 EpisodicSpoFrame` | the auditable witness row | **origin**, not a projection — produced by `ReadingState::step()`; nothing else holds these bytes first | + +`EpisodicSpoFrame` deserves the explicit note: at ~90 B it is the largest +non-fingerprint `Copy` here and it *is* stacked in `Vec` for a +SIMD sweep, which superficially reads like a gathered window. It is not — the +frame is the **producer** of its own bytes, not a reading of a slab that owns +them. The `Vec` is the substrate, not a copy of one. + +--- + +## ★ The real one — a gathered window, not a derive (`wave.rs`, REPORTED not fixed) + +```rust +// crates/deepnsm-v2/src/wave.rs:133 and :146 +pub fn window_at(&self, ref_version: u64) -> Vec<(usize, CausalWitnessFacet)> { + self.events.iter().enumerate() + .filter(|(_, (v, _))| pov.admits(*v)) + .map(|(pos, (_, r))| (pos, *r)) // ← the copy + .collect() +} +``` + +This is **the canonical measured instance of the zero-copy law, re-created**: +the doc's §"canonical measured instance" names `window: &[(usize, +CausalWitnessFacet)]` in `witness_fabric` as the ~768 KB-per-resolve violation +that motivated the whole law. `self.events` stays alive across the call, so +every returned facet is a second stored reading of bytes that already have one. +`ground_at` (l.173) and l.192 feed the result straight into +`standing_wave_grounded(idx, &window, …)` — the exact function the ZC-2 +migration was about. + +**Why I stopped rather than fixed it** (per the brief's cascade rule): + +- The `_lens` twins already exist (`standing_wave_grounded_lens`, + `resolve_chain_lens`, taking `&WitnessLens<'_>` + `visible: impl Fn(usize) -> + bool`), so this *looks* like a one-line swap. **It is not.** +- `WitnessLens<'a>` borrows `&'a [NodeRow]` and casts at + `NODE_ROW_STRIDE`-strided offsets. `WitnessStream::events` is + `Vec<(u64, CausalWitnessFacet)>` — a tuple vec, **not** a strided row slab. + `WitnessLens::at(pos)` cannot cast into it. +- This is precisely the doc's §"Not every gathered slice is a window" warning: + *name the source before writing the twin.* The repair is either (a) reshape + `WitnessStream::events` to a row slab so the existing lens applies, or + (b) write a stream-shaped lens over `&[(u64, CausalWitnessFacet)]` with the + predicate filter. Both change `WitnessStream`'s public API and touch + `ground_at`, l.192, `examples/bible_wave.rs:267`, and 6 in-file tests. + +**The correct shape already exists one file over**, which is the strongest +evidence this was a miss rather than a decision: `TemporalStream::window_at` +(`deepnsm-v2/src/lib.rs:201`) returns `impl Iterator + '_` — +borrowed, filtered by predicate, zero copies. Two `window_at` methods in one +crate, one right and one wrong. + +## Stored projections — real, but the `Copy` derive is not the lever + +Two fields are **recompute-equal by construction**, i.e. a cache with a +correctness liability rather than a memory: + +- `Sentence64.cam` (`sentence_transformer64.rs:563`) — the *only* constructor is + `Sentence64::new`, which sets `cam: Cam4096::from_p64(p64)`. So `cam` is + always exactly `Cam4096::from_p64(self.p64)`; a pure 3-nibble fold of a field + the same struct already holds. Same kind (an address), same rung (a coarsening + of the meaning field, not an elevation) → **projection stored**. +- `SplatNeighbour.cam` (`:406`) — identical shape, both construction sites. +- `EpisodicSpoHint` (`:521`) — re-stores `subject`/`predicate`/`object`/`role` + read out of an `EpisodicSpoFrame` that outlives the read in + `project_frames(frames: &[EpisodicSpoFrame]) -> Vec`. + +**Refused, deliberately.** Removing `derive(Copy)` from these does nothing about +it — the duplication is in the struct's *shape*, not its copy-ability. The +repair is `fn cam(&self) -> Cam4096 { Cam4096::from_p64(self.p64) }` (delete the +field) and a borrow for the hint; that is a struct-layout change rippling +through `project`, `project_from_frame`, `project_frames`, `splat_p64`, +`same_basin_as` and the module's tests. Reported for a scoped follow-up. + +## Adjacent — `Fingerprint16K` has no consumers + +`grep -rn 'Fingerprint16K'` over all of `crates/` returns **zero hits outside +`src/fingerprint16k.rs`** (it is `pub mod`-exported at `lib.rs:116`). Not the +doc's `#[cfg(test)]`-only "shadow of storage" — the constructors are real +production functions — but the same smell one step milder: a 2 KB `Copy` type +with a full API, 19 functions, its own test module, and nothing reaching for it. +Worth a decision (wire it or delete it) before it grows. + +## Overlap with the concurrent `copy-tierA` session — no duplicate edit made + +While I was reading, `crates/deepnsm/examples/homograph_collapse.rs` changed +under me (mtime `14:53:49`); `git status` showed it modified alongside an +untracked `.claude/board/agent-tags/copy-tierA.md`. A sibling session is running +the **Tier A sweep across all crates**; its scope and mine intersect at exactly +this one site, and it landed the removal with a why-comment first. + +Per the `AGENT_LOG` 2026-07-29 lesson (`--force-with-lease` refusing a duplicate +fix; resolved by discarding the duplicate and taking the sibling's as canonical) +I **left their edit untouched and wrote nothing of my own**. I verified it is +correct: both `Clone` and `Copy` are gone, no `.clone()` on `Collapse` exists, +and every use site is a match or a move — it compiles with no call-site change. + +Their independent conclusion also **confirms my method**: they report the +"declared lifetime parameter ONLY" heuristic is exact workspace-wide +(3 hits / 3 true positives / 0 false positives), and the `&'static` field is the +false-positive generator. My scripted scan over these three crates found exactly +1 lifetime-param site and 0 `&'static` fields — consistent, from a different +direction. + +## Edits made by me + +**None.** The single violation in scope was already fixed by the sibling +session. The other 62 sites are LEGITIMATE or ELEVATED and must keep `Copy`. diff --git a/.claude/board/agent-tags/copy-planner.md b/.claude/board/agent-tags/copy-planner.md new file mode 100644 index 000000000..9681dc85b --- /dev/null +++ b/.claude/board/agent-tags/copy-planner.md @@ -0,0 +1,49 @@ +# copy-planner — `Copy`-derive verdicts across the 8 planner/core crates (2026-07-29) + +Operator order: *"copies are forbidden, borrows are only for the same mailbox"*; +*"only cognitive achievements > tenant"*. Reads: `zero-copy-lens-law.md`, +`data-flow.md` §2 (in `/home/user/ndarray/.claude/rules/`), `borrow-strategy.md` +(in `/home/user/q2/.claude/rules/`). EDIT-ONLY; no cargo run. + +## Scope + census correction + +Scope: `lance-graph`, `lance-graph-planner`, `lance-graph-supervisor`, +`lance-graph-turbovec`, `lance-graph-python`, `lance-graph-arm-discovery`, +`cognitive-shader-driver`, `causal-edge` — `src` AND `examples`. + +**195 `Copy`-derive sites in scope**, not the 38 the census lists for these +crates. The census grepped `derive(Clone, Copy)`; the real spelling varies +(`derive(Clone, Copy, Debug, …)`, `derive(Debug, Clone, Copy, …)`, +`derive(Clone, Copy, PartialEq, Eq, Hash)`). **The census also MISSED the one +real violation in scope** — `AdjacencyBatch<'a>` carries two borrows and was not +in Tier A. + +## Changes made (1) + +- `crates/lance-graph-planner/src/adjacency/batch.rs:23` — `AdjacencyBatch<'a>`: + `#[derive(Debug, Clone, Copy)]` → `#[derive(Debug)]` + why-comment. + Fields are `store: &'a AdjacencyStore` and `source_ids: &'a [u64]` — every + field is a borrow, i.e. the exact `WitnessLens<'a>` shape stripped in + `b3515ba`. Zero cascade: all 4 call sites already pass `&AdjacencyBatch<'_>`; + no `.clone()`, no by-value use anywhere. + +## Cascades refused (report, do not touch) + +1. **`SpoHead`** (`lance-graph-planner/src/cache/nars_engine.rs:29`) — + VIOLATION, but the repair is type deletion, not a derive removal. +2. **`InteractionKinematic`** (`cognitive-shader-driver/src/sigma_rosetta.rs:904`) + — VIOLATION (stores two diagonal terms); repair is a field-shape change. +3. **`WitnessWindow`** (`lance-graph-planner/src/traits.rs:89`) — the separate + high-priority item; blast radius below. + +Both (1) and (2) plus `WitnessWindow` are **test-only-producer types** — the +`zero-copy-lens-law.md` "shadow of storage" signature, three instances in scope. + +## WitnessWindow blast radius (address-only migration) + +Total surface: **1 decl, 1 read of `.rows`, 1 production consumer, 25 +`witness: None` sites, 3 test constructors.** The lens twin +`standing_wave_stratified_lens` ALREADY EXISTS (`witness_fabric.rs:852`). +The one real migration hazard is the `focal_idx` (index into the gather) vs +`focal_pos` (absolute stream position) semantic swap. Full detail in the +session report. diff --git a/.claude/board/agent-tags/copy-thinking.md b/.claude/board/agent-tags/copy-thinking.md new file mode 100644 index 000000000..1d7855fa3 --- /dev/null +++ b/.claude/board/agent-tags/copy-thinking.md @@ -0,0 +1,187 @@ +# copy-thinking — `derive(*, Copy)` verdicts for the 6 cognition/compute crates + +**Agent:** zero-copy warden (thinking / learning / jc / helix / perturbation-sim / codec-research) +**Branch:** `claude/x265-x266-plans-review-h9osnl` +**Mode:** EDIT ONLY. No `cargo build`/`check`/`test`/`clippy` run; no worktree created. +**Read first:** `AGENT_LOG.md`, `.claude/knowledge/zero-copy-lens-law.md`, +`.claude/rules/data-flow.md` §2, `.claude/rules/borrow-strategy.md`. + +> **Mandatory-read path correction.** `data-flow.md` and `borrow-strategy.md` do +> NOT exist under `lance-graph/.claude/rules/` — that directory is absent. The +> canonical copies are `/home/user/ndarray/.claude/rules/data-flow.md` and +> `/home/user/q2/.claude/rules/borrow-strategy.md`, both auto-loaded into session +> context via the workspace CLAUDE.md chain. I read them there. The census file +> and the `copy-tierA` tag both cite the non-existent lance-graph path; worth +> fixing at the source so the next worker does not go looking. + +--- + +## Headline — 0 violations in 123 sites. Nothing removed, nothing refused. + +**No type in any of the six crates holds a borrow, and no type in any of the six +crates reads SoA lane bytes at all.** The second fact is what makes the first +structural rather than lucky: you cannot store a second projection of lane bytes +if you never touch a lane. Mechanical confirmation — + +``` +grep -rl 'NodeRow|SoaEnvelope|from_register_ref|CausalWitnessFacet| + value_offset|NODE_ROW_STRIDE|ValueTenant' +→ crates/perturbation-sim/src/columns.rs (one DOC-COMMENT occurrence, line 107, + the prose word "ValueTenant"; no code) +``` + +So the violation shape the law names — *a struct that holds what a lane holds, +stored beside the lane* — has no instance here. These crates compute over owned +values and hand owned values back. + +## The census undercounts my scope by 43 % + +`copy-derive-blast-radius.txt` lists **~70** sites for these six crates. The real +count is **123**. The census misses every `#[derive(Debug, Clone, Copy, …)]` +where `Debug` comes first — e.g. all 25 `perturbation-sim/src` sites, all 8 +`helix/src` sites, `learning/src/scm.rs:31`, `learning/src/cognitive_styles.rs` +×3, `thinking-engine/src/{reranker_lens,inference_backend,silu_correction}.rs`. +The census header claims *"This census matches both orderings"* — it does not. +**The 369 global total is therefore a floor, not a count.** None of the 53 +missing sites changed a verdict (all are value types), but the next sweep should +re-run with `derive\([^)]*\bCopy\b` rather than a two-ordering literal match. + +Per-crate: thinking-engine 34 · learning 33 · jc 14 · helix 14 · +perturbation-sim 27 · lance-graph-codec-research 1 = **123**. + +## Method + +Two passes, because neither alone is sufficient: + +1. **Mechanical** — parsed all 123 declaration bodies (brace-balanced, comments + stripped) and searched field positions for a lifetime parameter or any `&`. + Result: **2 candidates, both `&'static str`, 0 lifetime parameters.** +2. **By hand** — read the declaration + surrounding impl for ~85 sites + individually, including every site you named to watch, plus the producer + function wherever a type looked like it might be a materialization + (`BufferResidue`, `FastBusDto`, `CascadeChannels8`, `PathResult`). + +The sharper heuristic `copy-tierA` proposed (flag on a **declared lifetime +parameter only**) reproduces my result exactly on this corpus: 0 hits, 0 +violations, 0 misses. + +--- + +## The two `&` sites — LEGITIMATE, and they are the census's own false-positive mechanism + +| path:line | type | VERDICT | reason | +|---|---|---|---| +| `crates/perturbation-sim/src/columns.rs:58` | `SoaMemberSpec` | LEGITIMATE | `name: &'static str` + 2×`u32` + enum + 2×`bool`. Backs the `const CONTINGENCY_FACTORS: [SoaMemberSpec; 5]` and `const INERTIA` spec tables. | +| `crates/perturbation-sim/src/columns.rs:136` | `InertiaPromotion` | LEGITIMATE | `member` + `signoff` are `&'static str`; backs `const INERTIA_PROMOTION`. | + +`&'static str` is a pointer into `.rodata` that outlives every mailbox, so there +is no compartment for it to escape *from* — the same ruling `copy-tierA` reached +independently on 8 sites. These are const **descriptor rows** (widths, encodings, +sign-off provenance), not data. Note the irony worth recording: `SoaMemberSpec` +is the type that *describes* SoA value tenants, and it is the only thing in these +six crates the borrow-heuristic could latch onto — it names lanes, it never +reads them. + +## LEGITIMATE families — stated once each, per your instruction + +| family | n | why, once | +|---|---|---| +| **`learning/src/cam_ops.rs` op enums** (`OpCategory`, `LanceOp`, `SqlOp`, `CypherOp`, `HammingOp`, `NarsOp`, `FilesystemOp`, `CrystalOp`, `NsmOp`, `ActrOp`, `RlOp`, `CausalOp`, `QualiaOp`, `RungOp`, `MetaOp`, `VerbOp`, `MemoryOp`, `UserOp`, `LearnOp`) | 19 | **Read, not assumed** — I brace-scanned all 19 full bodies (18–131 lines each) for variants carrying a payload. **Zero payload variants across all 19**: every one is C-like with explicit `= 0xNNN` discriminants. An op code is an address, and a discriminant is a value. | +| **Fieldless dispatch/state enums** — `GateState`, `Viscosity`, `Archetype`, `GestaltRole`, `GestaltState`, `GhostType`, `PersonaMode`, `SourceType`, `ThinkingScale`, `QuorumLevel`, `ModelId`, `TableType`, `ThinkingPreset`, `CognitiveOpKind`, `CognitiveAuthResult`, `BackendGrade`, `GatePolicy`, `NarsCopula`, `ActrBuffer`, `CausalRelation`, `QualiaChannel`, `Rung`, `NarsInferenceType`, `CausalEdgeType`, `Operator`, `Atom`, `StyleOrigin`, `FloorBand`, `BusKind`, `DataLevel`, `Regime`, `Encoding`, `GuardrailVerdict`, `IccForm`, `AudioQualia`, `Sign`, `Verdict`, `Mode`, `Kind`, `PBasis`, `PQuant`, `PMode`, `PRank` | ~43 | Nullary variants. Nothing to borrow; `Copy` is the identity function on a discriminant. | +| **Small scalar records** — `StyleParams`, `SelfState`, `UserState`, `FieldState`, `HdrResonance`, `Temperature`, `SpiralSegment`, `SplatField`, `CrossModelResult`, `TruthValue`(×2), `Cx`, `AcBus`, `AcLine`, `CascadeConfig`, `Resilience`, `Splat`, `Edge`, `Yield`, `MetaHop`, `RollingFloor`, `ContingencyFeatures`, `Stats`, `Slab`, `Region`, `Sprite`(×2), `SpriteParams`, `HemispherePoint`, `Similarity`, `CurveRuler`, `ResidueEdge`, `Signed360`, `CascadeKey`, `CascadeKeyV3`, `IsaPath`, `HhtlKey`, `InertiaProvenance`, `Mat2`(×3), `Sym2`, `NounF`, `Pron`, `Lane` | ~53 | `f32`/`f64`/`u8`/`u16`/`u64`/small fixed arrays. **This is `data-flow.md` §2 verbatim** — reasoning = owned `Copy` microcopies, stack-allocated, no heap, no lifetime tracking. Removing `Copy` here breaks the rule in the *other* direction. | + +### `learning/src/cognitive_frameworks.rs:18 TruthValue` — MUST STAY `Copy`, explicitly + +You asked me to say this rather than skip it. `TruthValue { f: f32, c: f32 }` — 8 +bytes, owned, no borrow. It is **named in `data-flow.md` §2 as the canonical +reasoning microcopy**, and `borrow-strategy.md` uses it as the worked example of +the required pattern (`let mut local_truth = hit.record.truth;` → revise on the +owned copy → gated write-back). Stripping `Copy` would force reasoning paths onto +`&mut` during computation, which is the P0 that rule exists to prevent. **Not +touched.** (`learning/src/scm.rs:31` declares a second, independent `TruthValue` +with fields `frequency`/`confidence` — same verdict, but flagging the duplicate +since `docs/TYPE_DUPLICATION_MAP.md` does not list it.) + +--- + +## The four you named to watch + +**`thinking-engine/src/contract_bridge.rs:155 FastBusDto` — LEGITIMATE. A value.** +Your question was the right one to ask. `#[repr(C)]`, a `SIZE` const, a `≤24 B` +test — the author is clearly thinking in bytes. But it is not a record *of bytes +that already have a lens*: `from_thought(…)` takes computed `f32`s from the +cascade and **quantizes them** (`dissonance * 255.0 as u8`, `top3` from a slice, +`gate` from a match). The inputs are transient computation outputs, not lane +bytes, and the DTO is the only stored form — it lives as `A2APayload::Thought(FastBusDto)` +on an `A2AMessage`. It crosses an **agent** boundary, not a tenant boundary, and +your own rule (*"borrows are only for the same mailbox"*) is exactly why an owned +value is the correct shape there: removing `Copy` pushes toward a reference, the +forbidden direction. **Standing watch, not a finding:** if a SoA lane ever holds +these same 12 fields, `FastBusDto` becomes the second projection that day. + +**`thinking-engine/src/layered.rs:45 CascadeChannels8` — LEGITIMATE.** +`pub struct CascadeChannels8(pub u64)` — a newtype over one `u64`, 8 signed byte +channels read by shift+mask. `u64` is in `data-flow.md` §2's list by name. It is +not a second projection: it is the **accumulator during L1→L2→L3 propagation**, +and it is *transcoded* into `causal_edge::CausalEdge64` at the L3 commit boundary +(the impl block says so), never stored beside it. Converted, not duplicated. The +one field holding it (`domino.rs:90 pub edge: CascadeChannels8`) owns it outright +— there is no lane backing those bytes. + +**`jc/src/ewa_sandwich.rs:104 Spd2` — LEGITIMATE, and `Copy` is load-bearing.** +3×`f64` = 24 B. The propagation loop is `sigma = sandwich(&m, &sigma)` — a +**value assignment**, which is precisely `data-flow.md`'s *"engines return +results; they do not mutate themselves while computing"*. Removing `Copy` forces +in-place `&mut` mutation of `sigma`, i.e. the P0 in the other direction. Same +verdict for the identical `Spd2` in `koestenberger.rs:99`, `Spd3` in +`ewa_sandwich_3d.rs:99`, `Sym2` in `sigma_codebook_probe.rs:77`, and the three +`Mat2` copies in jc examples. + +**`jc/src/ewa_sandwich.rs:232 PathResult` — LEGITIMATE, with an elevation note.** +`{ final_sigma: Spd2, log_norm_sq: f64, psd_hops: usize }`. `final_sigma` is a +member (same rung as its inputs); `log_norm_sq` and `psd_hops` are **facts about +the set of hops, not members of it** — the Gadamer-refinement shape that *would* +be elevation-eligible. But the rung test is not triggered, because **nothing here +is stored**: `propagate_path` returns it to a local. Same for +`ewa_sandwich_3d.rs:384`. + +## ELEVATED — I am claiming this verdict for nothing, and here is why + +The rubric licenses a store. Nothing in these six crates writes a tenant, so no +site can earn it. Two sites are **elevation-*shaped*** and I name them so the +question is on the record before someone stores them: + +- `jc/src/ewa_sandwich.rs:232` / `_3d.rs:384` `PathResult.{log_norm_sq, psd_hops}` + — set-facts over a hop sequence. +- `perturbation-sim/src/place_buffer.rs:81 BufferResidue { lanes: [u16; 8] }` — + the strongest candidate. `buffer_residue()` computes effective resistance from + the Laplacian pseudo-inverse to **every** other bus, sorts, takes the 8 nearest, + quantizes to BF16. That is a computation across many reads yielding a value of a + different KIND (a node's neighbourhood permeability, which lives in no single + coupling) — not reproducible by a cast. Today it is returned, not stored. The + module doc already describes it as *"the BF16 residue … a helix-residue value + slot on the HHTL-OGAR key"*, and `columns.rs:136 INERTIA_PROMOTION` records a + `RatifiedReuse` verdict promoting it into a `ResidueEdge` slot. **So the store + is planned.** When it lands it needs the rung named explicitly, per the law's + one-exception test — it should not inherit "it was already a `Copy` struct" as + its licence. + +--- + +## Changes: none. Cascades refused: none. + +No derive removed, no file edited in the six crates. The only artifact of this run +is this tag file. Nothing to compile beyond what you were already going to build. + +## Honest limits + +- Verdicts are from reading declarations, impls, and (for the watched types) + producers and call sites — **not from a compiler**. Since I changed nothing, + there is no build risk either way. +- The mechanical borrow scan reads field *positions* after stripping `//` + comments. A borrow hidden behind a type alias (`type Foo = &'a [u8];`) would + evade it. I grepped for alias declarations in the six crates and found none in + the 123 bodies, but I did not resolve aliases transitively. +- I judged 123 sites in six crates. The census's own count for these crates was + wrong by 53; **the other crates' Tier-B counts are likely wrong the same way** + and should be re-derived before anyone reports "369 audited". diff --git a/.claude/board/agent-tags/copy-tierA.md b/.claude/board/agent-tags/copy-tierA.md new file mode 100644 index 000000000..d069af5b8 --- /dev/null +++ b/.claude/board/agent-tags/copy-tierA.md @@ -0,0 +1,151 @@ +# copy-tierA — verdict on the 11 "TIER A borrow-carrying `derive(Clone, Copy)`" candidates + +**Agent:** zero-copy warden (Tier A verification) +**Branch:** `claude/x265-x266-plans-review-h9osnl` +**Input:** `.claude/board/exec-runs/copy-derive-blast-radius.txt` § TIER A (11 sites) +**Mode:** EDIT ONLY. No `cargo build`/`check`/`test`/`clippy` run; no worktree created. +**Read first:** `AGENT_LOG.md`, `.claude/knowledge/zero-copy-lens-law.md`, +`.claude/rules/data-flow.md` §2, the `WitnessLens` comment at +`crates/lance-graph-contract/src/witness_fabric.rs:108-122` (fixed in `b3515ba`). + +--- + +## Headline — the Tier A list is 8/11 FALSE POSITIVE. Overturned, not confirmed. + +You were right to expect false positives, and the mechanism is exactly the one +you named: **the heuristic's second disjunct ("a field spelled `&`") fires on +`&'static`.** All eight non-violations were flagged on a `&'static str`, +`Option<&'static str>`, or `&'static [T]` field. **Not one of the eight carries +a lifetime parameter** — the first disjunct (the discriminating one) fires on +exactly the three types I confirmed, and on nothing else. + +`&'static` is not a mailbox borrow. It points into baked program data (rodata / +a `const` table) that outlives every mailbox, so there is no compartment for it +to escape *from*. Stripping `Copy` from those eight buys zero containment and +costs real friction (they are const-table rows and by-value `self` methods). +Worse, per `.claude/rules/data-flow.md` §2 those eight ARE the "reasoning = +owned `Copy` microcopy" category the rule *requires* `Copy` on — removing it +breaks the law in the other direction. + +**A sharper heuristic for the next pass:** flag on a declared lifetime parameter +ONLY (`struct X<'a>` / `enum X<'a>`). On this corpus that rule is exact: +3 hits, 3 true positives, 0 false positives, 0 misses. + +I also spot-checked the Tier-B names most likely to hide a borrow behind a +misleading identifier — `EntityRef`, `EdgeRef`, `SchemaPtr`, `MappingHandle`, +`ColumnWindow`. All are packed-integer value types (`u32`/`u16` fields, no +pointers). **The heuristic did not under-count**; it only over-counted. + +--- + +## Per-site table + +| # | path:line | type | VERDICT | one-line reason | +|---|---|---|---|---| +| 1 | `crates/lance-graph-contract/src/canonical_node.rs:1492` | `NodeRowPacket<'a>` | **VIOLATION — FIXED** | holds `rows: &'a [NodeRow]` — byte-identical field to the already-fixed `WitnessLens<'a>`; `as_le_bytes` re-exports the whole 512 B-strided slab | +| 2 | `crates/holograph/src/bitpack.rs:552` | `VectorSlice<'a>` | **VIOLATION — FIXED** | holds `words: &'a [u64]` into an Arrow/mmap buffer owned elsewhere; `as_words(&self) -> &'a [u64]` re-exports at full `'a` | +| 3 | `crates/deepnsm/examples/homograph_collapse.rs:63` | `Collapse<'a>` | **VIOLATION — FIXED** | `Unique(&'a str, u32)` borrows the caller's `&[Sense]` table | +| 4 | `crates/lance-graph-callcenter/src/family_table.rs:132` | `FamilyEntry` | NOT-A-VIOLATION | no lifetime param; `&'static str` + `&'static [u8]` into the TTL-baked hydration table | +| 5 | `crates/lance-graph-callcenter/src/odoo_alignment.rs:65` | `OwlPivot` | NOT-A-VIOLATION | `pivot_uri: &'static str` + `u16` + two 1-byte enums; a §2 microcopy, `identity(self)` is `const fn` by value | +| 6 | `crates/lance-graph-callcenter/src/super_domain.rs:125` | `MetaAnchors` | NOT-A-VIOLATION | `Option<&'static str>` ×2 + marker + `Option`; a `const EMPTY` row | +| 7 | `crates/lance-graph-callcenter/src/super_domain.rs:181` | `SuperDomainEntry` | NOT-A-VIOLATION | `&'static [OgitFamily]` + `&'static str`; explicitly documented "lives in static memory, ~30 B × 8 entries" | +| 8 | `crates/lance-graph-contract/examples/foveated_awareness.rs:159` | `Card` | NOT-A-VIOLATION | `&'static str` name + 2 enums + `f32` + `u8`; its own doc already says "rides as owned microcopy" — a §2 citation, correct | +| 9 | `crates/lance-graph-contract/src/cognitive_shader.rs:143` | `StyleSelector` | NOT-A-VIOLATION | enum, `Named(&'static str)` variant only; a dispatch tag | +| 10 | `crates/lance-graph-contract/src/mul.rs:233` | `MulThresholdProfile` | NOT-A-VIOLATION (one phrasing note, below) | 3×`f32` + `label: &'static str`; three `const` profiles | +| 11 | `crates/lance-graph/examples/causal_knowledge_transfer.rs:33` | `Trajectory` | NOT-A-VIOLATION | three `&'static str` relation names; a relation *signature*, not a substrate read | + +--- + +## What I changed (3 files, derive removed + comment added, no logic touched) + +Each removal mirrors the `WitnessLens` wording you fixed in `b3515ba` (the +"a `Copy` borrow is a borrow that duplicates itself silently" paragraph), with +one site-specific sentence naming *which* compartment owns the bytes. + +1. **`canonical_node.rs:1492` `NodeRowPacket<'a>`** — the one that matters most. + Same `&'a [NodeRow]` field as `WitnessLens`, and a **wider** exposure: the + lens hands out one 12-byte register per call, whereas + `SoaEnvelope::as_le_bytes` hands out the entire contiguous 512 B-strided + backing slab. A `Copy` packet is a silently duplicable second holder of one + mailbox's whole store. Comment says exactly that. +2. **`bitpack.rs:552` `VectorSlice<'a>`** — the archetypal lens, and the file's + own doc-comment already argues the zero-copy case at length ("copies 0 bytes + for the 999,000 that fail the cascade"). The `Copy` derive was the hole in + that argument: `as_words(&self) -> &'a [u64]` re-exports the borrow at the + full `'a`, so a duplicated slice outlives every scope visible at the call + site. +3. **`homograph_collapse.rs:63` `Collapse<'a>`** — smallest, and I did NOT + downgrade it for that. Size is not a mitigation under this law. + +## What broke at the call sites: **nothing, on inspection.** + +I traced every construction and consumption of the three types. This is a +static read, not a compile — the central build is yours. + +- **`NodeRowPacket`** — 12 sites (`symbiont/src/bridge.rs:129`, + `contract/src/ocr.rs:211`, 10 in-file tests, re-export in `lib.rs:190`). + Every one is `let pkt = NodeRowPacket::new(&rows, c);` followed by `&self` + methods (`n_rows`, `cycle`, `as_le_bytes`, `row_le`, `verify_layout`) or the + associated-const path ` as SoaEnvelope>::LAYOUT_VERSION`. + No by-value pass, no field storage, no `.clone()`. **`SoaEnvelope` declares no + `Copy`/`Clone` supertrait bound** (`soa_envelope.rs:170`), so the impl is + unaffected. +- **`VectorSlice`** — `storage.rs` (`get_slice`, `cascaded_knn`, + `from_bytes_or_copy` at :797), `navigator.rs` (`ZeroCopyCursor::next`), + `hamming.rs`, plus 4 tests. Every consumer takes `&dyn VectorRef` or + `&slice`. The one move is `return Some((id, slice, stacked.total))` in + `ZeroCopyCursor::next` — a genuine move *after* both borrows have ended, which + is fine without `Copy`. **`VectorRef` declares no `Copy`/`Clone` bound**, and + there are no by-value operator impls for `VectorSlice` (the `BitXor`/`BitAnd`/ + `BitOr`/`Not` impls at `bitpack.rs:696-730` are all on `BitpackedVector`). + Note `ZeroCopyCursor.query` is `&'a BitpackedVector`, not a `VectorSlice` — it + was the one field that would have forced a cascade, and it does not. +- **`Collapse`** — 6 sites, all in-file. The only by-value use is + `if let (Collapse::Unique(_, pr), Collapse::Unique(_, sr)) = (p, so)` at :157, + which moves `p`/`so` into a tuple and never touches them again. Legal. + +**No cascade found, so none refused.** If the central build disagrees, the most +likely single point is a `.clone()` I did not spot on `VectorSlice` inside a +`datafusion-storage`-gated block — `holograph/src/navigator.rs` and +`storage.rs` are partly behind that feature, and my read covered the gated code +textually but a feature-off build never type-checks it either way. + +--- + +## One phrasing note (NOT an edit, but you should see it) + +`crates/lance-graph-contract/src/mul.rs:229-230`, on `MulThresholdProfile`: + +> *"The struct is `Copy` so it can sit on the BindSpace per-row carrier +> **without indirection**."* + +That is one clause away from the trapped formulation the warden card lists +verbatim ("12 B inline beats a 16 B pointer plus indirection"), and +`zero-copy-lens-law.md` § "Projection is not chasing" rules the indirection term +**zero in this substrate**. I did not edit it, for two reasons I want on the +record rather than assumed: + +1. The type is a §2 microcopy (3 `f32` + a `&'static str` label) and `Copy` on + it is required, not merely allowed — so the *conclusion* is right even though + the *argument* is one of the trapped ones. +2. The sentence describes a hypothetical ("so it **can** sit on"). Today + `for_context(id)` is a `const fn` returning a value; nothing stores a profile + in a per-row lane. + +**But if that hypothetical is ever built, it is a violation**, and the doc +already pre-argues for it: the profile is a pure `const fn` of +`ontology_context_id`, so a per-row profile lane would be a projection of a lane +that already exists — `output_rung == max(input_rungs)`, no elevation. Worth a +line in the law's cost-argument catalogue as a live in-tree instance; your call +whether that is this task's business. + +## Honest limits of this run + +- Verdicts are from reading the type definitions and every call site, not from + a compiler. "Nothing broke" means "nothing in the call graph I read requires + `Copy`", not "it compiles". +- I judged the 11 named sites only. Tier B (358 sites) is unaudited beyond the + five spot-checks named above. +- `crates/holograph` is not in the `[workspace] members` list in `CLAUDE.md`; + if it is built standalone or excluded, gate it explicitly rather than assuming + the workspace run covers it. diff --git a/.claude/board/exec-runs/copy-derive-blast-radius.txt b/.claude/board/exec-runs/copy-derive-blast-radius.txt new file mode 100644 index 000000000..1638822d2 --- /dev/null +++ b/.claude/board/exec-runs/copy-derive-blast-radius.txt @@ -0,0 +1,484 @@ +# Blast radius — `derive(Clone, Copy)` census (2026-07-29) + +Operator order: *"copies are forbidden, borrows are only for the same mailbox"*, +*"WitnessLens is forbidden as a copy. period"*, *"only cognitive achievements > +tenant"*, *"grep Clone, Copy and remove it"*, *"log the blast radius"*. + +**930 sites.** Regenerated order-insensitively and multi-line-aware, per +CodeRabbit's Major finding — matching any `#[derive(...)]` block containing +`Copy`, rather than a literal two-token spelling on one line. + +### The count's own history is the lesson + +| figure | method | wrong by | +|---|---|---| +| 26 | one literal spelling, one line | 36× | +| 369 | both orderings, still single-line | 2.5× | +| **930** | any derive block containing `Copy`, multi-line | — | + +Each number was published as a *correction* to the previous one, and each +inherited the same defect: a regex chosen for the shape I expected rather than +the shape the language allows. The 369 pass even carried the sentence "this +census matches both orderings" — the claim was about the fix I had just made, +not about what the regex could see. CodeRabbit caught it by cross-checking +against the agents' own tag files (119/95/123 for cognitive/codec/thinking +against the 56/73/70 recorded here), which is the check I should have run +myself: **I had a second, independent source in the repo and never compared.** + +Per-crate (top 8): `lance-graph-contract` 313 · `lance-graph-planner` 92 · +`lance-graph` 61 · `holograph` 51 · `lance-graph-ontology` 45 · +`lance-graph-cognitive` 44 · `deepnsm` 41 · `thinking-engine` 34. + +**The Tier A/B tables below were built from the 369 pass and are therefore a +SUBSET, not a census.** They remain useful as the audited starting set — every +row in them was verified by an agent — but a site's absence from them means +"not yet looked at", never "not a violation". The 7-agent verdicts +(24 → ~16 real / 26 LEGITIMATE / 13 ELEVATED) are likewise verdicts over the +subset. + +## The discriminator (why this is not a blanket removal) + +`Copy` on a **borrow-carrying** type is the violation: a `Copy` borrow duplicates +itself silently, so it can be stored beside the original and carried out of the +compartment that owns the bytes with no move and nothing in a diff to point at. +That is exactly how a same-mailbox borrow escapes its mailbox. + +`Copy` on a **small owned value** is not only allowed but REQUIRED by +`.claude/rules/data-flow.md` §2 (reasoning = owned `Copy` microcopies: +`TruthValue`, `Fingerprint`, `u64`, `Band`, `CpuCaps`, `ScanParams` — passed by +value, no heap, no lifetime tracking). Removing those would break the rule in +the other direction. + +So each site needs a verdict, not a sweep. The Tier A/B split below is a +mechanical FIRST PASS by a crude heuristic (does the declaration carry a +lifetime parameter, or a field spelled `&`), not a verdict — several Tier A +entries have no lifetime in the decl line and were flagged on a `&'static` or a +doc-comment `&`. Verification is the agents' job. + +## Already fixed + +`WitnessLens<'a>` (`lance-graph-contract/src/witness_fabric.rs`) — derive +removed in `b3515ba`; contract gates clean (1134 tests, clippy `-D warnings`, +fmt). It does not appear below. + +## ⊘ CARVE-OUT — `lance-graph-ontology` is NOT in scope (operator, 2026-07-29) + +Operator: *"copying ontology is desired, RDF is converted to KV."* + +The zero-copy law governs **substrate** bytes — rows, registers, tenant lanes: +things that already carry a projection, where a second stored reading is an +ontological duplication (`zero-copy-lens-law.md`: "there is no neutral byte +layer underneath"). + +**RDF/TTL is not substrate. It is an external format at a membrane.** The +`lance-graph-ontology` hydrators (`ttl_parse.rs`, `hydrators/owl.rs`, +`hydrators/dolce_odoo.rs`, `namespace.rs`, `proposal.rs`) exist to CONVERT +oxrdf triples into the canonical KV/classid form. A transcode's entire job is to +produce a new representation; the copy there is the FIRST projection, not a +second one. Applying the copy ban to it would forbid ingest itself. + +This is the same line `compilation-vs-runtime-substrate.md` and +`assembler-vs-storage-substrate.md` draw elsewhere: serialization is legal AT a +membrane and illegal on the hot path. Ontology hydration is a membrane. + +**Consequence for any future sweep:** ALL **45** derive-with-`Copy` sites under +`crates/lance-graph-ontology/` are **out of scope** (the "13" written here +earlier came from the undercounting 369 pass — same defect, propagated into the +carve-out). +Do not de-`Copy` them, and do not file them as debt. The 13 clippy errors in +that crate (deprecated `oxrdf::Subject` alias, doc-list indentation) are +ordinary lint drift on an un-linted crate — unrelated to the copy law, and NOT +evidence of a violation. + +This carve-out was nearly missed: the sweep reached ontology only because +`lance-graph-callcenter` deps it, and the reflex on seeing `Copy` + a parse type +is to treat it as a materialization. Recorded because that reflex will recur. + +--- + +TOTAL derive(Clone,Copy) sites: 369 +HOLDS-A-BORROW (lifetime param or & field): 11 + +=== TIER A — carries a borrow (Copy lets it escape its mailbox) === +crates/deepnsm/examples/homograph_collapse.rs:63 Collapse + enum Collapse<'a> { +crates/holograph/src/bitpack.rs:552 VectorSlice + pub struct VectorSlice<'a> { +crates/lance-graph-callcenter/src/family_table.rs:132 FamilyEntry + pub struct FamilyEntry { +crates/lance-graph-callcenter/src/odoo_alignment.rs:65 OwlPivot + pub struct OwlPivot { +crates/lance-graph-callcenter/src/super_domain.rs:125 MetaAnchors + pub struct MetaAnchors { +crates/lance-graph-callcenter/src/super_domain.rs:181 SuperDomainEntry + pub struct SuperDomainEntry { +crates/lance-graph-contract/examples/foveated_awareness.rs:159 Card + struct Card { +crates/lance-graph-contract/src/canonical_node.rs:1492 NodeRowPacket + pub struct NodeRowPacket<'a> { +crates/lance-graph-contract/src/cognitive_shader.rs:143 StyleSelector + pub enum StyleSelector { +crates/lance-graph-contract/src/mul.rs:233 MulThresholdProfile + pub struct MulThresholdProfile { +crates/lance-graph/examples/causal_knowledge_transfer.rs:33 Trajectory + struct Trajectory { + +=== TIER B — no borrow visible (value type; judge against data-flow.md §2) === +crates/bgz-tensor/src/adaptive_codec.rs:27 RowPrecision +crates/bgz-tensor/src/belichtungsmesser.rs:18 Band +crates/bgz-tensor/src/cascade.rs:33 CascadeLevel +crates/bgz-tensor/src/cascade.rs:132 ScentByte +crates/bgz-tensor/src/codebook4096.rs:22 CodebookIndex +crates/bgz-tensor/src/fisher_z.rs:27 FamilyGamma +crates/bgz-tensor/src/had_cascade.rs:50 TensorRegime +crates/bgz-tensor/src/hdr_belichtung.rs:39 QuarterSigmaBand +crates/bgz-tensor/src/hhtl_cache.rs:35 RouteAction +crates/bgz-tensor/src/hhtl_d.rs:31 HeelBasin +crates/bgz-tensor/src/hhtl_d.rs:64 HhtlDEntry +crates/bgz-tensor/src/hhtl_f32.rs:36 HhtlF32Entry +crates/bgz-tensor/src/matryoshka.rs:32 BandPrecision +crates/bgz-tensor/src/morton_cascade/mod.rs:34 L4Tenant +crates/bgz-tensor/src/morton_cascade/mod.rs:72 Reading +crates/bgz-tensor/src/morton_cascade/mod.rs:80 Backend +crates/bgz-tensor/src/neuron_hetero.rs:68 ThinkingStyleFingerprint +crates/bgz-tensor/src/neuron_hetero.rs:377 TransformSpectrum +crates/bgz-tensor/src/slot_l.rs:35 SlotL +crates/bgz-tensor/src/variance.rs:11 Role +crates/bgz17/src/container.rs:242 CrystalTriple +crates/bgz17/src/container.rs:339 InlineEdge +crates/bgz17/src/generative.rs:51 LfdProfile +crates/bgz17/src/lib.rs:78 Precision +crates/bgz17/src/palette.rs:20 PaletteEdge +crates/bgz17/src/palette.rs:542 PaletteResolution +crates/bgz17/src/simd.rs:16 SimdLevel +crates/causal-edge/src/edge.rs:159 CausalEdge64 +crates/causal-edge/src/edge_v3.rs:57 CausalEdgeV3 +crates/causal-edge/src/layout.rs:112 TrustTexture +crates/cognitive-shader-driver/src/attention_mask.rs:24 AttentionMaskEntry +crates/deepnsm-v2/src/shape.rs:311 Color +crates/deepnsm/examples/causal_edge_v3_facet.rs:52 Verb +crates/deepnsm/examples/causal_edge_v3_facet.rs:69 CausalEdgeV3 +crates/deepnsm/examples/gridlake_coca_wire.rs:17 Cell +crates/deepnsm/examples/gridlake_spo_ngrams.rs:21 Cell +crates/deepnsm/examples/homograph_collapse.rs:37 Role +crates/deepnsm/examples/spo_anaphora_nibble.rs:49 Noun +crates/deepnsm/examples/spo_anaphora_nibble.rs:56 Pron +crates/deepnsm/examples/spo_markov_kg.rs:59 Truth +crates/deepnsm/examples/spo_markov_kg.rs:79 Role +crates/deepnsm/src/cam64.rs:37 Cam64 +crates/deepnsm/src/crystal_neighborhood.rs:50 NeighborhoodMetric +crates/deepnsm/src/episodic_spo.rs:39 DependencyRole +crates/deepnsm/src/episodic_spo.rs:54 ClauseRole +crates/deepnsm/src/episodic_spo.rs:67 DiscourseRole +crates/deepnsm/src/episodic_spo.rs:90 EpisodicSpoFrame +crates/deepnsm/src/episodic_spo.rs:215 BasinClassification +crates/deepnsm/src/fingerprint16k.rs:19 Fingerprint16K +crates/deepnsm/src/morphology.rs:36 MorphFlags +crates/deepnsm/src/parser.rs:19 State +crates/deepnsm/src/parser.rs:34 ModRelation +crates/deepnsm/src/pos.rs:7 PoS +crates/deepnsm/src/reader_state.rs:50 LeftCornerTrigger +crates/deepnsm/src/sentence_transformer64.rs:108 P64 +crates/deepnsm/src/sentence_transformer64.rs:260 Cam4096 +crates/deepnsm/src/sentence_transformer64.rs:341 Perturbation4x4 +crates/deepnsm/src/sentence_transformer64.rs:401 SplatNeighbour +crates/deepnsm/src/sentence_transformer64.rs:521 EpisodicSpoHint +crates/deepnsm/src/sentence_transformer64.rs:560 Sentence64 +crates/deepnsm/src/signed_crystal.rs:88 HorizonPolarity +crates/deepnsm/src/signed_crystal.rs:152 SignedOffset4 +crates/deepnsm/src/signed_crystal.rs:222 Crystal4096 +crates/deepnsm/src/signed_crystal.rs:320 SignedSentenceCrystal +crates/deepnsm/src/spo.rs:23 SpoTriple +crates/deepnsm/src/window.rs:62 ExpectedReason +crates/deepnsm/src/window.rs:79 ExpectedSlot +crates/deepnsm/src/window.rs:87 WindowEntry +crates/helix/examples/fire_forget_replay_probe.rs:98 Region +crates/helix/examples/hevc_headtohead.rs:85 Sprite +crates/helix/examples/hevc_moving_scene.rs:40 Sprite +crates/helix/examples/mu_hydration_probe.rs:98 Slab +crates/helix/examples/rdo_arbiter_probe.rs:143 Mode +crates/helix/examples/rdo_arbiter_probe.rs:169 Kind +crates/highheelbgz/src/lib.rs:36 SpiralAddress +crates/highheelbgz/src/lib.rs:189 NeuronPrint +crates/highheelbgz/src/simd_hardened.rs:16 SpiralAddr +crates/highheelbgz/src/simd_hardened.rs:141 NeuronPrint +crates/holograph/src/crystal_dejavu.rs:50 Coord5D +crates/holograph/src/crystal_dejavu.rs:406 SigmaBand +crates/holograph/src/dn_sparse.rs:165 PackedDn +crates/holograph/src/dn_sparse.rs:518 EdgeDescriptor +crates/holograph/src/dntree.rs:267 VerbCategory +crates/holograph/src/dntree.rs:298 CogVerb +crates/holograph/src/epiphany.rs:73 EpiphanyZone +crates/holograph/src/graphblas/descriptor.rs:25 DescField +crates/holograph/src/graphblas/sparse.rs:19 SparseFormat +crates/holograph/src/graphblas/types.rs:109 GrBType +crates/holograph/src/graphblas/types.rs:169 GrBUnaryOp +crates/holograph/src/graphblas/types.rs:192 GrBBinaryOp +crates/holograph/src/graphblas/types.rs:241 GrBMonoid +crates/holograph/src/graphblas/types.rs:284 GrBSelectOp +crates/holograph/src/mindmap.rs:60 NodeType +crates/holograph/src/neural_tree.rs:89 NeuralLayer +crates/holograph/src/storage_transport.rs:35 StorageHeader +crates/holograph/src/storage_transport.rs:61 StorageFlags +crates/holograph/src/storage_transport.rs:95 MetaBlock128 +crates/holograph/src/storage_transport.rs:144 SemanticTier +crates/holograph/src/storage_transport.rs:248 TransportHeader +crates/holograph/src/storage_transport.rs:280 MessageType +crates/holograph/src/storage_transport.rs:300 VersionFlags +crates/holograph/src/storage_transport.rs:330 CompressionType +crates/holograph/src/width_16k/schema.rs:23 AniLevels +crates/holograph/src/width_16k/schema.rs:102 NarsTruth +crates/holograph/src/width_16k/schema.rs:171 NarsBudget +crates/holograph/src/width_16k/schema.rs:220 EdgeTypeMarker +crates/holograph/src/width_16k/schema.rs:270 NodeKind +crates/holograph/src/width_16k/schema.rs:291 NodeTypeMarker +crates/holograph/src/width_16k/schema.rs:326 InlineQValues +crates/holograph/src/width_16k/schema.rs:390 InlineRewards +crates/holograph/src/width_16k/schema.rs:440 StdpMarkers +crates/holograph/src/width_16k/schema.rs:467 InlineHebbian +crates/holograph/src/width_16k/schema.rs:513 CompressedDnAddr +crates/holograph/src/width_16k/schema.rs:532 NeighborBloom +crates/holograph/src/width_16k/schema.rs:593 GraphMetrics +crates/holograph/src/width_16k/search.rs:48 BlockMask +crates/holograph/src/width_32k/search.rs:46 Dimension +crates/holograph/src/width_32k/search.rs:72 DimWeights +crates/holograph/src/width_32k/search.rs:426 ProbeTarget +crates/jc/examples/l9_loci_real_text.rs:210 NounF +crates/jc/examples/l9_loci_real_text.rs:228 Pron +crates/jc/examples/osint_edge_traversal.rs:37 Mat2 +crates/jc/examples/splat_perturbationslernen.rs:80 Mat2 +crates/jc/examples/splat_to_ewa_bridge.rs:62 Mat2 +crates/jc/src/ewa_sandwich.rs:104 Spd2 +crates/jc/src/ewa_sandwich.rs:232 PathResult +crates/jc/src/ewa_sandwich_3d.rs:99 Spd3 +crates/jc/src/ewa_sandwich_3d.rs:384 PathResult +crates/jc/src/koestenberger.rs:99 Spd2 +crates/jc/src/reliability.rs:251 IccForm +crates/jc/src/sigma_codebook_probe.rs:77 Sym2 +crates/jc/src/sigma_codebook_probe.rs:145 EdgeFields +crates/lance-graph-arm-discovery/examples/meta_awareness_probe.rs:211 EdgeRef +crates/lance-graph-arm-discovery/examples/meta_awareness_probe.rs:233 Meta +crates/lance-graph-callcenter/src/audit_sink/composite.rs:16 FanoutMode +crates/lance-graph-callcenter/src/audit_sink/mod.rs:63 NoopAuditSink +crates/lance-graph-callcenter/src/dn_path.rs:18 DnPath +crates/lance-graph-callcenter/src/family_table.rs:62 OwlCharacteristics +crates/lance-graph-callcenter/src/family_table.rs:181 PerFamilyCodebook +crates/lance-graph-callcenter/src/lance_membrane.rs:117 ActorState +crates/lance-graph-callcenter/src/super_domain.rs:45 SuperDomain +crates/lance-graph-callcenter/src/super_domain.rs:109 DolceMarker +crates/lance-graph-callcenter/src/super_domain.rs:153 ComplianceRegime +crates/lance-graph-callcenter/src/transcode/parallelbetrieb.rs:71 DriftKind +crates/lance-graph-callcenter/src/transcode/zerocopy.rs:48 ArrowTypeCode +crates/lance-graph-callcenter/src/unified_audit.rs:51 AuthOp +crates/lance-graph-callcenter/src/unified_audit.rs:71 AuthDecision +crates/lance-graph-callcenter/src/unified_audit.rs:94 AuditMerkleRoot +crates/lance-graph-callcenter/src/unified_audit.rs:135 UnifiedAuditEvent +crates/lance-graph-callcenter/src/unified_audit.rs:196 AuditChain +crates/lance-graph-callcenter/src/unified_bridge.rs:75 OgitFamily +crates/lance-graph-callcenter/src/unified_bridge.rs:110 OwlIdentity +crates/lance-graph-callcenter/src/unified_bridge.rs:169 TenantId +crates/lance-graph-codec-research/src/lib.rs:79 AudioQualia +crates/lance-graph-cognitive/src/container_bs/adjacency.rs:33 PackedDn +crates/lance-graph-cognitive/src/container_bs/adjacency.rs:232 InlineEdge +crates/lance-graph-cognitive/src/container_bs/adjacency.rs:282 EdgeDescriptor +crates/lance-graph-cognitive/src/core_full/index.rs:74 Key +crates/lance-graph-cognitive/src/core_full/index.rs:134 Entry +crates/lance-graph-cognitive/src/core_full/scent.rs:24 ChunkHeader +crates/lance-graph-cognitive/src/core_full/scent.rs:480 BucketAddr +crates/lance-graph-cognitive/src/fabric/subsystem.rs:4 Subsystem +crates/lance-graph-cognitive/src/grammar/causality.rs:37 DependencyType +crates/lance-graph-cognitive/src/search/cognitive.rs:63 QualiaVector +crates/lance-graph-cognitive/src/search/cognitive.rs:280 SearchVia +crates/lance-graph-cognitive/src/search/cognitive.rs:309 RelevanceScores +crates/lance-graph-cognitive/src/spo/cognitive_codebook.rs:22 CognitiveDomain +crates/lance-graph-cognitive/src/spo/cognitive_codebook.rs:70 NsmCategory +crates/lance-graph-cognitive/src/spo/cognitive_codebook.rs:95 QualiaChannel +crates/lance-graph-cognitive/src/spo/cognitive_codebook.rs:112 NarsCopula +crates/lance-graph-cognitive/src/spo/cognitive_codebook.rs:129 NarsInference +crates/lance-graph-cognitive/src/spo/cognitive_codebook.rs:148 CausalityType +crates/lance-graph-cognitive/src/spo/cognitive_codebook.rs:165 TemporalRelation +crates/lance-graph-cognitive/src/spo/cognitive_codebook.rs:190 YamlTemplate +crates/lance-graph-cognitive/src/spo/cognitive_codebook.rs:215 ThematicRole +crates/lance-graph-cognitive/src/spo/cognitive_codebook.rs:248 CognitiveAddress +crates/lance-graph-cognitive/src/spo/sentence_crystal.rs:85 Coord5D +crates/lance-graph-contract/examples/foveated_awareness.rs:67 Style +crates/lance-graph-contract/src/a2a_blackboard.rs:36 ExpertCapability +crates/lance-graph-contract/src/a2a_blackboard.rs:157 RoutingStrategy +crates/lance-graph-contract/src/a2a_blackboard.rs:182 ConsensusStrategy +crates/lance-graph-contract/src/canonical_node.rs:724 NodeRow +crates/lance-graph-contract/src/cognitive_shader.rs:42 MetaWord +crates/lance-graph-contract/src/cognitive_shader.rs:80 MetaFilter +crates/lance-graph-contract/src/cognitive_shader.rs:119 ColumnWindow +crates/lance-graph-contract/src/cognitive_shader.rs:155 RungLevel +crates/lance-graph-contract/src/cognitive_shader.rs:271 RungElevator +crates/lance-graph-contract/src/cognitive_shader.rs:364 ShaderDispatch +crates/lance-graph-contract/src/cognitive_shader.rs:416 EmitMode +crates/lance-graph-contract/src/cognitive_shader.rs:433 ShaderHit +crates/lance-graph-contract/src/cognitive_shader.rs:465 ShaderResonance +crates/lance-graph-contract/src/cognitive_shader.rs:506 AlphaComposite +crates/lance-graph-contract/src/cognitive_shader.rs:582 MetaSummary +crates/lance-graph-contract/src/cognitive_shader.rs:602 MaterializeProvenance +crates/lance-graph-contract/src/collapse_gate.rs:18 MergeMode +crates/lance-graph-contract/src/collapse_gate.rs:58 GateDecision +crates/lance-graph-contract/src/container.rs:56 ContentGeometry +crates/lance-graph-contract/src/content_store.rs:50 ContentId +crates/lance-graph-contract/src/content_store.rs:77 SourceSpan +crates/lance-graph-contract/src/content_store.rs:122 ContentError +crates/lance-graph-contract/src/cycle_accumulator.rs:56 AccumulatorAction +crates/lance-graph-contract/src/external_membrane.rs:38 ExternalRole +crates/lance-graph-contract/src/external_membrane.rs:59 ExternalEventKind +crates/lance-graph-contract/src/faculty.rs:63 FacultyRole +crates/lance-graph-contract/src/head2head.rs:40 WinnerCriterion +crates/lance-graph-contract/src/head2head.rs:63 CompetitionOutcome +crates/lance-graph-contract/src/head2head.rs:78 Head2Head +crates/lance-graph-contract/src/high_heel.rs:46 SpoBase17 +crates/lance-graph-contract/src/high_heel.rs:133 Heel +crates/lance-graph-contract/src/mul.rs:699 SimdCapsShim +crates/lance-graph-contract/src/ocr.rs:37 Bbox +crates/lance-graph-contract/src/ocr.rs:46 BlockKind +crates/lance-graph-contract/src/ontology.rs:28 Locale +crates/lance-graph-contract/src/ontology.rs:246 ModelHealth +crates/lance-graph-contract/src/persona.rs:84 RoutingHint +crates/lance-graph-contract/src/property.rs:17 PropertyKind +crates/lance-graph-contract/src/property.rs:309 Cardinality +crates/lance-graph-contract/src/property.rs:353 PrefetchDepth +crates/lance-graph-contract/src/property.rs:409 ActionTrigger +crates/lance-graph-contract/src/property.rs:770 Marking +crates/lance-graph-contract/src/property.rs:829 DatePrecision +crates/lance-graph-contract/src/property.rs:838 GeoFormat +crates/lance-graph-contract/src/property.rs:890 AuditAction +crates/lance-graph-contract/src/proprioception.rs:59 ProprioceptionAxes +crates/lance-graph-contract/src/proprioception.rs:159 StateAnchor +crates/lance-graph-contract/src/proprioception.rs:217 AnchorState +crates/lance-graph-contract/src/proprioception.rs:259 DriveMode +crates/lance-graph-contract/src/proprioception.rs:334 StateReport +crates/lance-graph-contract/src/qualia.rs:174 QualiaI4_16D +crates/lance-graph-contract/src/rbac.rs:40 ScopeSpec +crates/lance-graph-contract/src/rbac.rs:196 OpMask +crates/lance-graph-contract/src/rbac.rs:252 ClassGrant +crates/lance-graph-contract/src/reasoning.rs:29 ReasoningKind +crates/lance-graph-contract/src/reasoning.rs:45 Budget +crates/lance-graph-contract/src/sigma_propagation.rs:94 Spd2 +crates/lance-graph-contract/src/sla.rs:30 SlaPolicy +crates/lance-graph-contract/src/sla.rs:40 SlaPriority +crates/lance-graph-contract/src/splat.rs:31 SplatChannel +crates/lance-graph-contract/src/splat.rs:68 TriadicProjection +crates/lance-graph-contract/src/splat.rs:77 ReasoningWitness64 +crates/lance-graph-contract/src/splat.rs:87 AwarenessPlane16K +crates/lance-graph-contract/src/splat.rs:122 CamPlaneSplat +crates/lance-graph-contract/src/splat.rs:176 SplatPlaneSet +crates/lance-graph-contract/src/splat.rs:226 CamSplatCertificate +crates/lance-graph-contract/src/splat.rs:250 SplatDecision +crates/lance-graph-contract/src/splat.rs:298 ThetaDecision +crates/lance-graph-contract/src/tax.rs:18 TaxPeriod +crates/lance-graph-contract/src/tax.rs:27 PeriodKind +crates/lance-graph-contract/src/tax.rs:34 Jurisdiction +crates/lance-graph-contract/src/world_map.rs:42 WorldMapDto +crates/lance-graph-contract/src/world_model.rs:35 SelfState +crates/lance-graph-contract/src/world_model.rs:65 UserState +crates/lance-graph-contract/src/world_model.rs:86 FieldState +crates/lance-graph-contract/src/world_model.rs:111 GestaltState +crates/lance-graph-ontology/src/bridge.rs:144 EntityRef +crates/lance-graph-ontology/src/bridge.rs:149 EdgeRef +crates/lance-graph-ontology/src/hydrators/dolce_odoo.rs:32 DolceCategory +crates/lance-graph-ontology/src/hydrators/owl.rs:139 Format +crates/lance-graph-ontology/src/namespace.rs:26 NamespaceId +crates/lance-graph-ontology/src/namespace.rs:124 SchemaPtr +crates/lance-graph-ontology/src/namespace.rs:196 SchemaKind +crates/lance-graph-ontology/src/proposal.rs:114 IdentityCodec +crates/lance-graph-ontology/src/proposal.rs:124 QualiaMeta +crates/lance-graph-ontology/src/proposal.rs:213 MappingHandle +crates/lance-graph-ontology/src/ttl_parse.rs:560 SubjectKind +crates/lance-graph-planner/examples/insight_reason_wired.rs:183 Graph +crates/lance-graph-planner/examples/insight_relation_read.rs:221 Relation +crates/lance-graph-planner/examples/probe_babel_stances.rs:555 PhaseVec +crates/lance-graph-planner/examples/probe_babel_stances.rs:626 Frame +crates/lance-graph-planner/examples/probe_sudoku_teacher.rs:437 Election +crates/lance-graph-planner/examples/probe_sudoku_teacher.rs:490 Policy +crates/lance-graph-planner/examples/probe_sudoku_teacher.rs:641 Grade +crates/lance-graph-planner/examples/probe_sudoku_teacher.rs:801 ForkOutcome +crates/lance-graph-planner/examples/probe_sudoku_teacher.rs:878 Verdict +crates/lance-graph-planner/src/cache/candidate_pool.rs:10 Phase +crates/lance-graph-planner/src/cache/candidate_pool.rs:21 HeadAddress +crates/lance-graph-planner/src/cache/nars_engine.rs:28 SpoHead +crates/lance-graph-planner/src/cache/nars_engine.rs:161 Inference +crates/lance-graph-planner/src/cache/triple_model.rs:13 Plasticity +crates/lance-graph-planner/src/cache/triple_model.rs:56 DkPosition +crates/lance-graph-planner/src/strategy/chat_bundle.rs:112 CacheRoute +crates/lance-graph-python/src/graph.rs:45 ExecutionStrategy +crates/lance-graph-python/src/graph.rs:64 SqlDialect +crates/lance-graph-python/src/graph.rs:92 DistanceMetric +crates/lance-graph-supervisor/src/lifecycle_audit.rs:37 LifecycleEventType +crates/lance-graph-supervisor/src/lifecycle_audit.rs:56 LifecycleAuditEvent +crates/lance-graph-supervisor/src/lifecycle_audit.rs:89 NoopLifecycleSink +crates/lance-graph-turbovec/src/lib.rs:66 Kernel +crates/lance-graph/examples/causal_knowledge_transfer.rs:41 Meaning +crates/lance-graph/src/graph/arigraph/witness_corpus.rs:62 WitnessId +crates/lance-graph/src/graph/audio/hhtl_bridge.rs:32 AudioCascadeLevel +crates/lance-graph/src/graph/audio/hhtl_bridge.rs:45 AudioCascadeResult +crates/lance-graph/src/graph/audio/hhtl_bridge.rs:169 CascadeStats +crates/lance-graph/src/graph/audio/node.rs:26 AudioNode +crates/lance-graph/src/graph/audio/node.rs:154 TemporalEdge +crates/lance-graph/src/graph/hydrate.rs:19 TensorRole +crates/learning/src/cam_ops.rs:106 OpCategory +crates/learning/src/cam_ops.rs:155 LanceOp +crates/learning/src/cam_ops.rs:243 SqlOp +crates/learning/src/cam_ops.rs:324 CypherOp +crates/learning/src/cam_ops.rs:407 HammingOp +crates/learning/src/cam_ops.rs:478 NarsOp +crates/learning/src/cam_ops.rs:577 FilesystemOp +crates/learning/src/cam_ops.rs:675 CrystalOp +crates/learning/src/cam_ops.rs:767 NsmOp +crates/learning/src/cam_ops.rs:871 ActrOp +crates/learning/src/cam_ops.rs:950 RlOp +crates/learning/src/cam_ops.rs:1035 CausalOp +crates/learning/src/cam_ops.rs:1119 QualiaOp +crates/learning/src/cam_ops.rs:1219 RungOp +crates/learning/src/cam_ops.rs:1317 MetaOp +crates/learning/src/cam_ops.rs:1406 VerbOp +crates/learning/src/cam_ops.rs:1464 MemoryOp +crates/learning/src/cam_ops.rs:1499 UserOp +crates/learning/src/cam_ops.rs:1564 LearnOp +crates/learning/src/causal_ops.rs:37 CausalOp +crates/learning/src/cognitive_frameworks.rs:18 TruthValue +crates/learning/src/cognitive_frameworks.rs:64 NarsCopula +crates/learning/src/cognitive_frameworks.rs:188 ActrBuffer +crates/learning/src/cognitive_frameworks.rs:499 CausalRelation +crates/learning/src/cognitive_frameworks.rs:577 QualiaChannel +crates/learning/src/cognitive_frameworks.rs:701 Rung +crates/learning/src/feedback.rs:86 NarsInferenceType +crates/learning/src/rl_ops.rs:45 RlOp +crates/perturbation-sim/examples/comma_awareness.rs:132 Edge +crates/perturbation-sim/examples/comma_awareness.rs:191 Lane +crates/reader-lm/src/classifier.rs:5 HtmlStructure +crates/thinking-engine/examples/codec_rnd_bench.rs:2096 PBasis +crates/thinking-engine/examples/codec_rnd_bench.rs:2101 PQuant +crates/thinking-engine/examples/codec_rnd_bench.rs:2110 PMode +crates/thinking-engine/examples/codec_rnd_bench.rs:2115 PRank +crates/thinking-engine/examples/qwopus_nars_gate.rs:152 Mode +crates/thinking-engine/src/awareness_dto.rs:58 GestaltState +crates/thinking-engine/src/bridge_gate.rs:29 CognitiveOpKind +crates/thinking-engine/src/bridge_gate.rs:61 CognitiveAuthResult +crates/thinking-engine/src/builder.rs:32 Temperature +crates/thinking-engine/src/builder.rs:70 ThinkingPreset +crates/thinking-engine/src/builder.rs:121 TableType +crates/thinking-engine/src/cognitive_stack.rs:41 StyleParams +crates/thinking-engine/src/cognitive_stack.rs:182 GateState +crates/thinking-engine/src/contract_bridge.rs:155 FastBusDto +crates/thinking-engine/src/cronbach.rs:108 QuorumLevel +crates/thinking-engine/src/dto.rs:11 SourceType +crates/thinking-engine/src/dto.rs:25 ThinkingScale +crates/thinking-engine/src/ghosts.rs:35 GhostType +crates/thinking-engine/src/layered.rs:45 CascadeChannels8 +crates/thinking-engine/src/meaning_axes.rs:131 Viscosity +crates/thinking-engine/src/meaning_axes.rs:158 Archetype +crates/thinking-engine/src/meaning_axes.rs:253 GestaltRole +crates/thinking-engine/src/meaning_axes.rs:261 HdrResonance +crates/thinking-engine/src/persona.rs:31 PersonaMode +crates/thinking-engine/src/spiral_segment.rs:19 SpiralSegment +crates/thinking-engine/src/splat_ops.rs:20 SplatField +crates/thinking-engine/src/tokenizer_registry.rs:18 ModelId +crates/thinking-engine/src/world_model.rs:23 SelfState +crates/thinking-engine/src/world_model.rs:53 UserState +crates/thinking-engine/src/world_model.rs:72 FieldState +crates/thinking-engine/src/world_model.rs:93 GestaltState diff --git a/.claude/board/exec-runs/hot-path-copy-removal.md b/.claude/board/exec-runs/hot-path-copy-removal.md new file mode 100644 index 000000000..b753967ff --- /dev/null +++ b/.claude/board/exec-runs/hot-path-copy-removal.md @@ -0,0 +1,244 @@ +# Hot-path copy removal — what was done, what was removed (2026-07-29) + +Operator order: *"copies are forbidden, borrows are only for the same mailbox"*, +*"only cognitive achievements > tenant"*, *"we're talking about copy in hot +path"*, *"fix"*. + +Commits: `b3515ba` (WitnessLens), `eba7ed6` (7-agent Copy census), +`12a23d3` (ontology carve-out), `0310122` (hot-path allocations). + +--- + +## 1. What was WRONG (stated plainly) + +PR #868 changed the `window:` parameter of 7 `meta_basin` functions from a +gathered `&[(usize, CausalWitnessFacet)]` to a `WitnessLens`, and its body +claimed **"the materializing path is GONE."** That claim was false. The input +parameter changed; the copy machinery *below* it was never looked at. + +Two distinct defects, both mine, both authored the same day: + +| # | defect | where | status | +|---|---|---|---| +| A | `WitnessLens` derived `Clone, Copy` — a borrow that duplicates silently and can leave its mailbox | `witness_fabric.rs` (introduced `df69d87`, THIS session) | FIXED `b3515ba` | +| B | `GradedRow` materialised and re-copied through the whole pipeline | `meta_basin.rs` | FIXED `0310122` (partially — see §4) | + +Defect A is the one the operator had to name twice. It was created hours earlier +under a task titled *"`#[repr(transparent)]` + borrowed view — make the cast +real"* — i.e. the zero-copy fix itself shipped the copy. + +--- + +## 2. What was REMOVED + +### `WitnessLens` — `#[derive(Clone, Copy)]` (b3515ba) +A `Copy` borrow can be stored beside the original and carried out of the +compartment owning the rows with **no move and nothing in a diff to point at**. +That is exactly how a same-mailbox borrow escapes its mailbox. Removing the +derive forces pass-by-reference; reach is now bounded by the borrow it was built +from. Contract gates clean with it gone — nothing was relying on duplicating it. + +### The head-of-chain allocation (0310122) +`grade_rows` returned `Vec` → now a **lazy iterator**. Grading is a +projection over addresses; there was never anything to accumulate. + +### The second, redundant allocation (0310122) +`tail(graded: &[GradedRow]) -> Vec` did `.iter().copied().collect()` +— a full second copy of rows that had just been built. It now consumes the +grading iterator directly. **The chain allocates ONCE**, at the clustering +boundary. + +### The per-probe re-grading allocation (0310122) +`stable_under_perturbation` rebuilt a whole `Vec` inline, and +`stability_sweep` calls it **once per budget** — 11 allocations per basin on the +default range. Replaced by `grade_shapes_at`, which is lazy **and** skips the +quorum sweep entirely: `meta_cluster` reads only `.trajectory`, so the expensive +half was being computed and discarded on every probe. + +### A redundant `#[must_use]` (0310122) +The returned iterator already carries it; clippy `-D warnings` caught the +duplicate. + +### 8 `derive(Clone, Copy)` on borrow-carrying types (eba7ed6) +Across contract / callcenter / cognitive / holograph / planner / deepnsm, from +the 7-agent census. + +--- + +## 3. What was DELIBERATELY NOT removed + +- **`Copy` on value types.** `.claude/rules/data-flow.md` §2 *requires* it for + reasoning microcopies (`TruthValue`, `Fingerprint`, `u64`, `Band`, `CpuCaps`, + `ScanParams`). Stripping those breaks the law from the other side. The census + ruled 26 LEGITIMATE on exactly this ground. +- **13 ELEVATED sites** — a value at a strictly higher rung than every input it + derives from earns its store (`Locus::Quorum` / `Contradiction` precedent). +- **`lance-graph-ontology`** — operator-carved: *"copying ontology is desired, + RDF is converted to KV."* RDF is an external format at a membrane; the + hydrators' copy is the FIRST projection, not a second one. Banning it would + forbid ingest. See the carve-out in `copy-derive-blast-radius.txt`. +- **The one remaining collect at the clustering boundary.** `meta_cluster` and + `density_scores` need random access over the tail; that is a genuine + accumulation, not a projection. + +--- + +## 4. What is STILL a copy (honest remainder) + +`MetaBasin.members: Vec` and `MiniBasin.members: Vec` +still hold **copies of rows that already exist in the tail buffer**. The +correct shape is indices into that buffer. Not done here: it cascades through +`meta_cluster` / `mini_basins` / `coarse_flags` and every test fixture, and +after today's record I am not landing a refactor that size unreviewed in the +same pass as the fix. + +`reasoning.rs` has `PremiseBundle.premises: Vec<..>` and +`arena(&self) -> BeliefArena`. + +> **⊘ SELF-CORRECTION (same session, before acting on it).** An earlier revision +> of this line called `arena()` "a per-call materialisation" and listed it as a +> copy to remove. **That was wrong, and it was the same reflex that nearly +> mis-flagged `lance-graph-ontology`:** seeing an owned return value and reading +> it as a duplication without asking what it derives from. +> +> `arena()` is a **fold**, not a copy. It takes premises and produces pooled +> beliefs with NARS-revised truth — `TruthValue::revise` on disjoint stamps, +> CHOICE on overlapping ones. The output is at a strictly higher rung than every +> input: no premise carries pooled confidence; the arena is where that comes into +> existence. That is exactly the ELEVATED carve-out (`Locus::Quorum` / +> `Contradiction` precedent) and it EARNS its store. Removing it would delete the +> reasoning, not a duplication. +> +> **The real finding, which is smaller and different:** `resolve()` (`:176`) and +> `differential()` (`:208`) each call `self.arena()`, so a caller that does both +> — which is the expected consumer shape — folds the same premises **twice**. +> That is redundant computation, not a copy. `.claude/rules/data-flow.md` +> permits the fix (*"caches use interior mutability (`RwLock`, `LazyLock`) or are +> built once"*), so a `OnceCell` is in-doctrine. NOT done here: it +> changes a public struct's shape, and it is a different category of defect from +> the one this record is about. Filed, not smuggled in. + +`PremiseBundle.premises` remains a stored `Vec` and is genuinely unexamined. + +--- + +## 5. Measurements, not claims + +- Sweep path: **2 allocations per graded row → 1**. +- Perturbation probe: **11 allocations per basin → 0** on the default budget + range, plus the quorum computation dropped from every probe. +- Scan cost characterised separately (Codex P2): **4608 `visible` probes at + N=512/k=8 vs 64 peer comparisons gathered** — the Θ(N·k) predicate form. That + is NOT fixed here; the operator's ruling is that the peer domain should be the + **address list**, which bounds it by k. Tracked in + `TD-LENS-QUORUM-SCANS-THE-WHOLE-LENS` with the corrected framing. + +## 6. Gates + +`cargo test -p lance-graph-planner` **325 passed / 0 failed** · +`clippy --all-targets -- -D warnings` clean · `cargo fmt --check` clean · +`cargo test -p lance-graph-contract --lib` 1134 passed. + +## 7. Standing status + +**PR #868 remains draft / DO-NOT-MERGE.** The hot-path allocations are fixed; +the `MetaBasin.members` copies and the `arena()` materialisation are not, and +the PR's original "materializing path is GONE" claim is retracted in its body. + +--- + +## 8. HOW it was redone without the copy — and why the result is BETTER, not merely equal + +The instinct when told "remove the copy" is to fear a trade: fewer allocations, +more indirection, same work. That is not what happened. In every case the copy +was **carrying nothing**, so removing it removed work rather than moving it. + +### 8.1 `grade_rows` — the copy was a materialised map + +**Before.** `(0..len).filter(visible).enumerate().map(grade).collect()` — build +every `GradedRow`, put them in a heap buffer, hand the buffer back. The caller +then walked the buffer. + +**After.** Identical expression, `.collect()` deleted, return type +`impl Iterator + 'a`. The `map` closure is unchanged. + +**Why better, not equal.** A `map` over a range IS the projection; `collect` was +adding a heap buffer between two loops that were already fused. Removing it +gives: no allocation, no second pass, and — the real win — **grading now happens +lazily, so a consumer that short-circuits never pays for the rows it does not +reach.** `tail` filters on `quorum <= tail_below`; under the old shape every row +was fully graded (quorum sweep AND chain walk) before the first was tested. + +**The lifetime is the whole trick.** `grade_rows<'a>(lens: &'a …, visible: &'a impl Fn…)` +with `move` on both closures ties the iterator to the borrows it reads. No +`Copy`, no clone, no owned capture — the iterator is a *description* of work +over borrowed state, which is exactly the lens argument one level up. + +### 8.2 `tail` — the copy was a filter that reallocated + +**Before.** `tail(graded: &[GradedRow], …) -> Vec` doing +`.iter().copied().filter(..).collect()` — a full second buffer, from a buffer +built one line earlier. + +**After.** `tail(graded: impl IntoIterator, …) -> Vec`. + +**Why better.** Taking `IntoIterator` instead of `&[T]` means the filter fuses +into the grading iterator: **one pass, one allocation, for what was two of each.** +And it is strictly more general — a slice still satisfies `IntoIterator`, so +nothing that could call it before cannot call it now. The remaining `Vec` is +kept deliberately: `meta_cluster` and `density_scores` need random access, and +an accumulation that a real algorithm requires is not a copy in the sense the +law forbids. + +### 8.3 `stable_under_perturbation` — the copy hid a computation nobody read + +This is the one that produced a genuine algorithmic win rather than an +allocation win. + +**Before.** Inline, per probe: +``` +let reperturbed: Vec = (0..lens.len()).filter(visible).enumerate() + .map(|(idx,pos)| GradedRow { idx, pos, quorum: 0, trajectory: … }).collect(); +``` + +**After.** `grade_shapes_at(lens, visible, locus, hops).collect()` — a named, +lazy, shape-only grading. + +**Why better.** Naming the operation exposed what the inline version obscured: +`meta_cluster` reads **only `.trajectory`**. The inline code already knew this — +it hardcoded `quorum: 0` — but the *general* `grade_rows` next to it did not, +and `stability_sweep` calls this **once per budget**, 11 times on the default +range. Extracting it made the asymmetry legible and let the quorum sweep be +skipped structurally rather than by a magic literal. + +So the change removes, per basin per sweep: **11 heap allocations** AND **11 +full quorum passes** — where a quorum pass is the expensive half (it scans peers; +the chain walk is `lens.at` lookups). The copy was not the cost. The copy was +hiding the cost. + +### 8.4 The pattern worth keeping + +In all three the copy was **an artefact of how the code was written, not of what +it had to compute**. A `collect` between two loops that are already fused; a +filter that reallocates what it filters; an inline rebuild that obscures which +half of the work is dead. None of them was a trade-off being paid for +correctness — which is why removing them costs nothing and buys laziness, +generality, and one dead computation deleted. + +**This is the substance of the operator's ruling.** "The lens is the performance +floor — a materialization is strictly worse on BOTH axes" is not a slogan about +memory. Here it was literally true: the copies were slower AND they concealed +that a quorum was being computed 11 times per basin and thrown away. + +**Measured, both directions:** + +| path | before | after | +|---|---|---| +| grading → tail | 2 allocations/row, eager | 1 allocation total, lazy | +| perturbation sweep (default range) | 11 allocs + 11 quorum passes per basin | 0 allocs, 0 quorum passes | +| generality of `tail` | `&[GradedRow]` only | any `IntoIterator` (slices still work) | + +Gates unchanged and green throughout: **325 planner tests**, clippy +`-D warnings` clean, fmt clean. No test was weakened to accommodate the change — +the equivalence and anti-vacuity tests from #868 still pass against the same +oracles. diff --git a/.claude/board/exec-runs/lens-migration-zc2.md b/.claude/board/exec-runs/lens-migration-zc2.md index 086f52409..d153a03a2 100644 --- a/.claude/board/exec-runs/lens-migration-zc2.md +++ b/.claude/board/exec-runs/lens-migration-zc2.md @@ -171,3 +171,38 @@ an example call site (`probe_dcsw2_basin_rung.rs`) — leaving **14 live migratable parameters** (`contract/witness_fabric.rs` gathered originals: 7; `planner/nars/meta_basin.rs`: 7), which is what the "14" was counting. Unit made explicit: parameters, not call sites. + +## ⊘ Update (2026-07-29, ZC-2a — orchestrator, main thread) + +`planner/nars/meta_basin.rs`'s **7** gathered `window:` parameters are +**MIGRATED IN PLACE** — `grade_rows`, `stable_under_perturbation`, +`stability_sweep`, `stability_around`, `outlier_suggestions`, `coarse_flags`, +`ranked_outlier_suggestions` now take `(&WitnessLens<'_>, &impl Fn(usize) -> bool)`. +No twins were added and no gathered form was retained: the family has zero +callers outside its own file (confirmed — the lib compiled clean while only the +in-file test module broke, 21 call sites), so the deprecation window that +`witness_fabric`'s 7 need does not apply here. + +**Live migratable `window:` parameters: 14 → 7** (the `contract/witness_fabric.rs` +gathered originals, still retained for their outside callers). BLOCKED (1, +`WitnessWindow.rows`) and the example call site (1) are unchanged. + +**Scoping correction to "migratable NOW" above:** that label measured twin +availability, not caller shape. These fixtures use SPARSE positions +(`[0,1,2, 10,11,12, 20,21,22,23]`) and `resolve_chain` hops by absolute stream +position, so the gathered form could name positions with no rows between them. +The lens indexes a dense row array, so the gaps must exist as rows and be +excluded by `visible`. Every fixture was rebuilt (`rows_from` + `vis_of`), not +merely re-called. + +**Proof:** `lens_grading_matches_the_gathered_oracle_on_sparse_and_dense_windows` +(pre-migration body retained verbatim as the `grade_rows_gathered` oracle; +field-by-field across 6 budgets × 2 fixtures, with a per-axis anti-vacuity clause +that caught a constant quorum axis TWICE — see EPIPHANIES +`E-THE-EQUALITY-PASSED-WHILE-AN-AXIS-WAS-CONSTANT-1`) and +`an_invisible_gap_is_excluded_even_though_the_row_exists`. Revert-test: deleting +the `visible` filter fails 3 tests incl. `sparse: row count diverged at max_hops=0`. + +**Gates (central):** `cargo test -p lance-graph-planner` **324 + 4 passed, 0 failed**; +`cargo clippy -p lance-graph-planner --all-targets -- -D warnings` clean; +`cargo fmt --check` clean. Scoped `-p` throughout. diff --git a/crates/deepnsm/examples/homograph_collapse.rs b/crates/deepnsm/examples/homograph_collapse.rs index be006ae12..d4a36d24a 100644 --- a/crates/deepnsm/examples/homograph_collapse.rs +++ b/crates/deepnsm/examples/homograph_collapse.rs @@ -60,7 +60,15 @@ impl Role { } /// Result of applying a role mask to a surface word's sense superposition. -#[derive(Clone, Copy)] +// +// NOT `Clone`, NOT `Copy` — operator-ruled 2026-07-29: *"copies are forbidden, +// borrows are only for the same mailbox"* (same ruling as +// `lance_graph_contract::witness_fabric::WitnessLens`). `Unique` carries an +// `&'a str` borrowed from the caller's `&[Sense]`; a `Copy` borrow duplicates +// itself silently, so it can be stored beside the original and carried out of +// the compartment that owns the sense table with no move and nothing in a +// review to point at. Without the derive the verdict moves once, and its reach +// is bounded by the borrow it was built from. enum Collapse<'a> { /// Superposition collapsed to a single reading: `(lemma, lemRank centroid)`. Unique(&'a str, u32), diff --git a/crates/holograph/src/bitpack.rs b/crates/holograph/src/bitpack.rs index fcb2cb21c..6e8a08949 100644 --- a/crates/holograph/src/bitpack.rs +++ b/crates/holograph/src/bitpack.rs @@ -549,7 +549,22 @@ impl VectorRef for BitpackedVector { /// The borrowed slice must be at least `VECTOR_WORDS` u64s long and the data /// must be valid (padding bits in word[156] must be masked). Arrow columns /// built with `PaddedVectorBuilder` satisfy both invariants. -#[derive(Clone, Copy)] +// NOT `Clone`, NOT `Copy` — operator-ruled 2026-07-29: *"copies are forbidden, +// borrows are only for the same mailbox"* (the same ruling that stripped the +// derive from `lance_graph_contract::witness_fabric::WitnessLens`). +// +// A `Copy` borrow is a borrow that duplicates itself silently. It can be handed +// to a second holder, stored beside the first, and carried out of the +// compartment that owns the bytes — with no move, no diagnostic, and nothing in +// a review to point at. Here the owner is an Arrow/mmap buffer that some other +// mailbox holds, and `as_words` re-exports the borrow at the FULL `'a`, so a +// duplicated slice outlives every scope the reviewer can see. +// +// Without the derive the slice must be passed by reference and cannot be +// duplicated into a second owner, so its reach is bounded by the borrow it was +// built from. Every consumer here already takes `&dyn VectorRef`; a holder that +// wants the vector elsewhere takes an ADDRESS (row index into the batch) and +// re-resolves it there — never a second view of the same bytes. pub struct VectorSlice<'a> { words: &'a [u64], } diff --git a/crates/lance-graph-callcenter/src/graph_gremlin.rs b/crates/lance-graph-callcenter/src/graph_gremlin.rs index c314becbd..ed876bc72 100644 --- a/crates/lance-graph-callcenter/src/graph_gremlin.rs +++ b/crates/lance-graph-callcenter/src/graph_gremlin.rs @@ -229,7 +229,10 @@ mod tests { fn values_kind_projects_node_property() { let s = sample(); // A's "member-of" neighbour is the family hub → kind "Family". - assert_eq!(g(&s).v(&["A"]).out_e("member-of").values_kind(), vec!["Family"]); + assert_eq!( + g(&s).v(&["A"]).out_e("member-of").values_kind(), + vec!["Family"] + ); } #[test] diff --git a/crates/lance-graph-callcenter/src/graph_table.rs b/crates/lance-graph-callcenter/src/graph_table.rs index cabf0499c..81285fc5e 100644 --- a/crates/lance-graph-callcenter/src/graph_table.rs +++ b/crates/lance-graph-callcenter/src/graph_table.rs @@ -96,10 +96,7 @@ pub fn edges_table(snap: &GraphSnapshot) -> DfResult { pub fn graph_tables( snap: &GraphSnapshot, ) -> DfResult<(Arc, Arc)> { - Ok(( - Arc::new(nodes_table(snap)?), - Arc::new(edges_table(snap)?), - )) + Ok((Arc::new(nodes_table(snap)?), Arc::new(edges_table(snap)?))) } /// Register `nodes` + `edges` into a DataFusion `SessionContext`, so a consumer @@ -180,7 +177,10 @@ mod tests { let member_a = snap .nodes .iter() - .find(|n| n.kind == "OSINT/Gotham" && n.props.iter().any(|(k, v)| k == "family" && v == "00000a")) + .find(|n| { + n.kind == "OSINT/Gotham" + && n.props.iter().any(|(k, v)| k == "family" && v == "00000a") + }) .unwrap() .id .clone(); @@ -192,13 +192,13 @@ mod tests { .unwrap(); let batches = df.collect().await.unwrap(); let rows: usize = batches.iter().map(|b| b.num_rows()).sum(); - assert_eq!(rows, 1, "the out-of-family adapter edge is queryable via SQL"); + assert_eq!( + rows, 1, + "the out-of-family adapter edge is queryable via SQL" + ); // GROUP BY over node kinds: 2 OSINT members + 2 family nodes. - let df = ctx - .sql("SELECT count(*) AS n FROM nodes") - .await - .unwrap(); + let df = ctx.sql("SELECT count(*) AS n FROM nodes").await.unwrap(); let batches = df.collect().await.unwrap(); assert_eq!(batches[0].num_rows(), 1); } diff --git a/crates/lance-graph-callcenter/src/odoo_alignment.rs b/crates/lance-graph-callcenter/src/odoo_alignment.rs index effd34ab6..630c5617e 100644 --- a/crates/lance-graph-callcenter/src/odoo_alignment.rs +++ b/crates/lance-graph-callcenter/src/odoo_alignment.rs @@ -684,14 +684,20 @@ mod tests { #[test] fn resolve_odoo_style_chains_class_to_cluster() { // res.partner → SmbFoundryCustomer (0x80) → Empathic. - assert_eq!(resolve_odoo_style("res.partner"), Some(StyleCluster::Empathic)); + assert_eq!( + resolve_odoo_style("res.partner"), + Some(StyleCluster::Empathic) + ); // product.pricelist → ProductCatalog (0x64) → Analytical. assert_eq!( resolve_odoo_style("product.pricelist"), Some(StyleCluster::Analytical) ); // hr.employee → HRFoundation (0x90) → Empathic. - assert_eq!(resolve_odoo_style("hr.employee"), Some(StyleCluster::Empathic)); + assert_eq!( + resolve_odoo_style("hr.employee"), + Some(StyleCluster::Empathic) + ); // unmapped class → None. assert_eq!(resolve_odoo_style("stock.move"), None); } diff --git a/crates/lance-graph-callcenter/src/savant_reasoners.rs b/crates/lance-graph-callcenter/src/savant_reasoners.rs index f007e5e61..c8ba9e0fb 100644 --- a/crates/lance-graph-callcenter/src/savant_reasoners.rs +++ b/crates/lance-graph-callcenter/src/savant_reasoners.rs @@ -20,8 +20,8 @@ //! No serialization anywhere in this module. JSON exists only at the //! callcenter ↔ MedCareV2 FFI boundary, never on these types. -use std::borrow::Cow; use core::future::Future; +use std::borrow::Cow; use lance_graph_contract::exploration::NarsTruth; use lance_graph_contract::nars::QueryStrategy; @@ -91,30 +91,56 @@ pub enum SavantSuggestion { fn suggestion_for(savant: &Savant) -> SavantSuggestion { use SavantSuggestion::*; match savant.id { - 1 => SelectFromTable { candidate_table: Cow::Borrowed("account_fiscal_position") }, + 1 => SelectFromTable { + candidate_table: Cow::Borrowed("account_fiscal_position"), + }, 2 => PolicyChoice, - 3 => SelectFromTable { candidate_table: Cow::Borrowed("product.pricelist") }, - 4 => Distribution { over_table: Cow::Borrowed("account.analytic.distribution.model") }, - 5 => SelectFromTable { candidate_table: Cow::Borrowed("account.analytic.distribution.model") }, + 3 => SelectFromTable { + candidate_table: Cow::Borrowed("product.pricelist"), + }, + 4 => Distribution { + over_table: Cow::Borrowed("account.analytic.distribution.model"), + }, + 5 => SelectFromTable { + candidate_table: Cow::Borrowed("account.analytic.distribution.model"), + }, 6 => Anomaly, - 7 => SelectFromTable { candidate_table: Cow::Borrowed("account.account") }, + 7 => SelectFromTable { + candidate_table: Cow::Borrowed("account.account"), + }, 8 => PolicyChoice, - 9 => RankedSet { from_table: Cow::Borrowed("res.currency") }, + 9 => RankedSet { + from_table: Cow::Borrowed("res.currency"), + }, 10 => PolicyChoice, - 11 => SelectFromTable { candidate_table: Cow::Borrowed("stock.rule") }, + 11 => SelectFromTable { + candidate_table: Cow::Borrowed("stock.rule"), + }, 12 => AdvancePeriod, - 13 => Distribution { over_table: Cow::Borrowed("stock.warehouse.orderpoint") }, - 14 => SelectFromTable { candidate_table: Cow::Borrowed("stock.route") }, + 13 => Distribution { + over_table: Cow::Borrowed("stock.warehouse.orderpoint"), + }, + 14 => SelectFromTable { + candidate_table: Cow::Borrowed("stock.route"), + }, 15 => PolicyChoice, 17 => Gate, 18 => AdvancePeriod, - 19 => RankedSet { from_table: Cow::Borrowed("account.move.line") }, - 20 => SelectFromTable { candidate_table: Cow::Borrowed("account.reconcile.model") }, + 19 => RankedSet { + from_table: Cow::Borrowed("account.move.line"), + }, + 20 => SelectFromTable { + candidate_table: Cow::Borrowed("account.reconcile.model"), + }, 21 => Gate, 22 => Gate, - 23 => SelectFromTable { candidate_table: Cow::Borrowed("product.pricelist.item") }, + 23 => SelectFromTable { + candidate_table: Cow::Borrowed("product.pricelist.item"), + }, 24 => PolicyChoice, - 25 => RankedSet { from_table: Cow::Borrowed("stock.move") }, + 25 => RankedSet { + from_table: Cow::Borrowed("stock.move"), + }, 26 => Gate, _ => PolicyChoice, } @@ -174,8 +200,10 @@ fn kind_matches(a: ReasoningKind, b: ReasoningKind) -> bool { /// A kind with a single roster savant ignores the namespace; a kind with /// several resolves via [`DISPATCH_NS`] first, then by `namespace == savant.name`. pub fn resolve_savant(kind: ReasoningKind, namespace: &str) -> Option<&'static Savant> { - let candidates: Vec<&'static Savant> = - SAVANTS.iter().filter(|s| kind_matches(s.kind, kind)).collect(); + let candidates: Vec<&'static Savant> = SAVANTS + .iter() + .filter(|s| kind_matches(s.kind, kind)) + .collect(); match candidates.len() { 0 => None, 1 => Some(candidates[0]), @@ -339,19 +367,37 @@ mod tests { } fn budget() -> Budget { - Budget { max_tokens: 1000, max_ms: 100, max_evidence_rows: 100 } + Budget { + max_tokens: 1000, + max_ms: 100, + max_evidence_rows: 100, + } } fn ev(table: &'static str, rows: u64) -> EvidenceRef<'static> { - EvidenceRef { table, schema_fingerprint: 0, rows } + EvidenceRef { + table, + schema_fingerprint: 0, + rows, + } } - fn ctx<'a>(kind: ReasoningKind, ns: &'a str, evidence: &'a [EvidenceRef<'a>]) -> ReasoningContext<'a> { - ReasoningContext { namespace: ns, kind, evidence, budget: budget() } + fn ctx<'a>( + kind: ReasoningKind, + ns: &'a str, + evidence: &'a [EvidenceRef<'a>], + ) -> ReasoningContext<'a> { + ReasoningContext { + namespace: ns, + kind, + evidence, + budget: budget(), + } } #[test] fn resolves_ambiguous_kind_by_savant_name() { // PostingAnomaly has 3 savants → namespace=name disambiguates. - let s = resolve_savant(ReasoningKind::PostingAnomaly, "SequenceGapAnomalyDetector").unwrap(); + let s = + resolve_savant(ReasoningKind::PostingAnomaly, "SequenceGapAnomalyDetector").unwrap(); assert_eq!(s.id, 6); let s2 = resolve_savant(ReasoningKind::PostingAnomaly, "LockDateAdvancer").unwrap(); assert_eq!(s2.id, 18); @@ -359,8 +405,16 @@ mod tests { #[test] fn other_reconcile_match_splits_by_namespace() { - let a = resolve_savant(ReasoningKind::Other(other_kind::RECONCILE_MATCH), "erp.k3.reconcile_match").unwrap(); - let b = resolve_savant(ReasoningKind::Other(other_kind::RECONCILE_MATCH), "erp.k3.payment_reconcile").unwrap(); + let a = resolve_savant( + ReasoningKind::Other(other_kind::RECONCILE_MATCH), + "erp.k3.reconcile_match", + ) + .unwrap(); + let b = resolve_savant( + ReasoningKind::Other(other_kind::RECONCILE_MATCH), + "erp.k3.payment_reconcile", + ) + .unwrap(); assert_eq!(a.id, 19, "ReconcileMatchSelector"); assert_eq!(b.id, 21, "PaymentToInvoiceMatcher"); } @@ -368,14 +422,25 @@ mod tests { #[test] fn other_single_candidate_ignores_namespace() { // PRICELIST_ASSIGNMENT (code 1) has one savant — namespace irrelevant. - let s = resolve_savant(ReasoningKind::Other(other_kind::PRICELIST_ASSIGNMENT), "whatever").unwrap(); + let s = resolve_savant( + ReasoningKind::Other(other_kind::PRICELIST_ASSIGNMENT), + "whatever", + ) + .unwrap(); assert_eq!(s.id, 3, "PricelistAssignmentAgent"); } #[test] fn conclusion_strategy_follows_inference_type() { let fiscal = savant_by_name("FiscalPositionResolver").unwrap(); - let c = build_conclusion(fiscal, &ctx(ReasoningKind::CustomerCategory, "FiscalPositionResolver", &[ev("account_fiscal_position", 3)])); + let c = build_conclusion( + fiscal, + &ctx( + ReasoningKind::CustomerCategory, + "FiscalPositionResolver", + &[ev("account_fiscal_position", 3)], + ), + ); assert_eq!(c.savant_id, 1); // Deduction → CamExact. assert_eq!(c.query_strategy, QueryStrategy::CamExact); @@ -384,11 +449,28 @@ mod tests { #[test] fn confidence_is_monotone_in_evidence() { let s = savant_by_name("AutopostRecommender").unwrap(); - let low = build_conclusion(s, &ctx(ReasoningKind::PostingAnomaly, "AutopostRecommender", &[ev("account_move", 1)])); - let hi = build_conclusion(s, &ctx(ReasoningKind::PostingAnomaly, "AutopostRecommender", &[ev("account_move", 50)])); + let low = build_conclusion( + s, + &ctx( + ReasoningKind::PostingAnomaly, + "AutopostRecommender", + &[ev("account_move", 1)], + ), + ); + let hi = build_conclusion( + s, + &ctx( + ReasoningKind::PostingAnomaly, + "AutopostRecommender", + &[ev("account_move", 50)], + ), + ); assert!(hi.confidence.frequency >= low.confidence.frequency); assert!(hi.confidence.confidence > low.confidence.confidence); - assert!(hi.confidence.confidence <= 0.99, "NarsTruth caps confidence"); + assert!( + hi.confidence.confidence <= 0.99, + "NarsTruth caps confidence" + ); } #[test] @@ -406,13 +488,16 @@ mod tests { #[test] fn other_reasoner_rejects_non_other_kind() { - let err = block_on(OtherReasoner.reason(ctx(ReasoningKind::CustomerCategory, "x", &[]))).unwrap_err(); + let err = block_on(OtherReasoner.reason(ctx(ReasoningKind::CustomerCategory, "x", &[]))) + .unwrap_err(); assert_eq!(err, SavantError::KindMismatch); } #[test] fn wrong_reasoner_for_kind_is_mismatch() { - let err = block_on(PostingAnomalyReasoner.reason(ctx(ReasoningKind::NextBestAction, "x", &[]))).unwrap_err(); + let err = + block_on(PostingAnomalyReasoner.reason(ctx(ReasoningKind::NextBestAction, "x", &[]))) + .unwrap_err(); assert_eq!(err, SavantError::KindMismatch); } @@ -422,7 +507,11 @@ mod tests { // named in slot 1 of its spec (`account_fiscal_position`). let fiscal = build_conclusion( savant_by_name("FiscalPositionResolver").unwrap(), - &ctx(ReasoningKind::CustomerCategory, "FiscalPositionResolver", &[]), + &ctx( + ReasoningKind::CustomerCategory, + "FiscalPositionResolver", + &[], + ), ); assert!(matches!( &fiscal.suggestion, @@ -431,7 +520,11 @@ mod tests { // SequenceGapAnomalyDetector → Anomaly. let gap = build_conclusion( savant_by_name("SequenceGapAnomalyDetector").unwrap(), - &ctx(ReasoningKind::PostingAnomaly, "SequenceGapAnomalyDetector", &[]), + &ctx( + ReasoningKind::PostingAnomaly, + "SequenceGapAnomalyDetector", + &[], + ), ); assert_eq!(gap.suggestion, SavantSuggestion::Anomaly); // AutopostRecommender / PaymentToInvoiceMatcher → Gate. diff --git a/crates/lance-graph-callcenter/src/unified_audit.rs b/crates/lance-graph-callcenter/src/unified_audit.rs index cd3b99a65..2bad459f2 100644 --- a/crates/lance-graph-callcenter/src/unified_audit.rs +++ b/crates/lance-graph-callcenter/src/unified_audit.rs @@ -193,7 +193,27 @@ impl UnifiedAuditEvent { /// Tracks the prior merkle root for one super domain so each new event /// can chain off it. Construct one per super-domain context the /// `UnifiedBridge` operates against. -#[derive(Clone, Copy, Debug)] +// NOT `Copy` (removed 2026-07-29): this is single-writer chain STATE, and +// `last_root` is a second holding of a root already durably recorded on the +// last emitted event. A `Copy` silently forks the chain — two advancers +// stamping distinct events as successors of the same `prev_merkle`, which is +// exactly the tamper signature `verify_chain` exists to detect, minted by the +// type system with nothing in a diff to point at. The one-writer invariant is +// enforced structurally instead: `UnifiedBridge` holds it in a +// `Mutex` and advances through `&mut`; the sanctioned way to +// continue a chain elsewhere is `AuditChain::resume(.., last_root)`, which is +// explicit about which root it claims. `Clone` is retained (nothing calls it +// today) because an explicit `.clone()` is greppable; `Copy` is not. +// +// ⊘ CORRECTED (CodeRabbit Major, #868): that reasoning was insufficient and +// `Clone` is now gone too. "Greppable" and "no current callers" are not +// invariants for an EXPORTED type — a public `Clone` lets any future caller +// fork single-writer merkle-chain state, which is the same defect `Copy` had, +// only slower to reach. A second chain sharing a prefix is not a copy of a +// value; it is two writers claiming one lineage. If a new chain is ever +// legitimately needed, it must come from an explicit constructor that +// establishes a fresh lineage, never from duplicating an existing one. +#[derive(Debug)] pub struct AuditChain { pub super_domain: SuperDomain, /// Per-super-domain salt — looked up from the super-domain registry diff --git a/crates/lance-graph-cognitive/src/core_full/scent.rs b/crates/lance-graph-cognitive/src/core_full/scent.rs index 6a866e10c..c25c356b9 100644 --- a/crates/lance-graph-cognitive/src/core_full/scent.rs +++ b/crates/lance-graph-cognitive/src/core_full/scent.rs @@ -21,7 +21,16 @@ pub const SCENT_BYTES: usize = 5; pub const BUCKETS: usize = 256; /// Chunk header with embedded scent and cognitive markers -#[derive(Clone, Copy, Debug)] +// NOT `Copy` (removed 2026-07-29): this is a record OF the substrate, not a +// value. `ScentIndexL1` OWNS the `[ChunkHeader; BUCKETS]` array and mutates +// it in place (`on_append` / `set_decision` / `set_plasticity` write +// `scent` / `count` / `last_access` / `plasticity` / `decision`), so a `Copy` +// silently mints a second, immediately-stale reading of live bucket state — +// including `scent`, which is itself a lossy projection of fingerprints that +// already live in the data file. Every read path here is already `&`-borrowed +// (`headers.iter()`), which is the correct shape: borrow inside the index's +// own mailbox, never carry an owned duplicate out of it. +#[derive(Clone, Debug)] #[repr(C)] pub struct ChunkHeader { /// Chunk ID (0-255) diff --git a/crates/lance-graph-contract/src/canonical_node.rs b/crates/lance-graph-contract/src/canonical_node.rs index a2ea5d0d9..4cdcf808d 100644 --- a/crates/lance-graph-contract/src/canonical_node.rs +++ b/crates/lance-graph-contract/src/canonical_node.rs @@ -1489,7 +1489,25 @@ pub fn classid_read_mode(classid: u32) -> ReadMode { /// top-level slots (key / edges / value). Internal structure within each /// slot is the canon's concern (`NodeGuid` for the key, `EdgeBlock` for the /// edges, registry `ClassView` for the value carve-out). -#[derive(Clone, Copy)] +// NOT `Clone`, NOT `Copy` — operator-ruled 2026-07-29: *"copies are forbidden, +// borrows are only for the same mailbox"*. This is the same ruling that stripped +// the derive from [`crate::witness_fabric::WitnessLens`], which holds the +// IDENTICAL `&'a [NodeRow]` field. +// +// A `Copy` borrow is a borrow that duplicates itself silently. It can be handed +// to a second holder, stored beside the first, and carried out of the +// compartment that owns the rows — with no move, no diagnostic, and nothing in +// a review to point at. That is precisely the escape the mailbox rule exists to +// close: a borrow is legitimate INSIDE one mailbox and illegitimate the moment +// it crosses an ownership boundary, and `Copy` is what lets it cross unnoticed. +// The exposure is WIDER here than on the lens: [`SoaEnvelope::as_le_bytes`] +// hands out the raw 512-byte-strided backing slab, so a duplicated packet is a +// second holder's view of one mailbox's entire store. +// +// Without the derive the packet must be passed by reference and cannot be +// duplicated into a second owner, so its reach is bounded by the borrow it was +// built from. Anything that wants the rows elsewhere takes an ADDRESS and +// resolves it against the corpus there — never a second view of the same bytes. pub struct NodeRowPacket<'a> { rows: &'a [NodeRow], cycle: u32, diff --git a/crates/lance-graph-contract/src/splat.rs b/crates/lance-graph-contract/src/splat.rs index 695f8e378..ec1e250a3 100644 --- a/crates/lance-graph-contract/src/splat.rs +++ b/crates/lance-graph-contract/src/splat.rs @@ -83,8 +83,18 @@ pub struct ReasoningWitness64(pub u64); /// q8 deposition writes to dedicated accumulators, not floats. /// This is the same width as `Vsa16kF32` and `Binary16K` (the canonical /// switchboard carriers). +/// +/// **NOT `Copy` (zero-copy law, 2026-07-29).** This is a 2 KB in-place +/// accumulator its owner deposits into (`&mut self`) — substrate, not a +/// reasoning microcopy (`.claude/rules/data-flow.md` §2 licenses `Copy` for +/// `u64`/`Band`/`ScanParams`-sized owned values, not for a 16 Kbit tile). +/// `Copy` let `let p2 = p1;` duplicate the whole pressure tile with no move +/// visible in a diff — the exact mechanism by which a 16 Kbit carrier leaves +/// the compartment that owns it (CLAUDE.md: a 16 Kbit carrier never crosses a +/// mailbox boundary). `Clone` stays: a deliberate duplicate is still allowed, +/// it just has to be written down. #[repr(C)] -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Debug)] pub struct AwarenessPlane16K(pub [u64; 256]); impl Default for AwarenessPlane16K { @@ -172,8 +182,12 @@ impl CamPlaneSplat { // ── SplatPlaneSet: the 6 channel planes ──────────────────────────────────── /// One [`AwarenessPlane16K`] per channel. 12 KB total per set. +/// +/// **NOT `Copy`** — same reason as [`AwarenessPlane16K`], six times over: this +/// is 12 KB of owned accumulator, and `Copy` made a whole-set duplicate +/// indistinguishable from a read. `Clone` retained. #[repr(C)] -#[derive(Clone, Copy, Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct SplatPlaneSet { pub support: AwarenessPlane16K, pub contradiction: AwarenessPlane16K, diff --git a/crates/lance-graph-contract/src/witness_fabric.rs b/crates/lance-graph-contract/src/witness_fabric.rs index 5a60fd0d0..148d4cda6 100644 --- a/crates/lance-graph-contract/src/witness_fabric.rs +++ b/crates/lance-graph-contract/src/witness_fabric.rs @@ -105,14 +105,49 @@ const _: () = assert!( /// /// No `#[derive(Debug)]`: [`NodeRow`] itself does not implement `Debug` (out /// of scope for this lens to add), so a derived impl here would not compile. -#[derive(Clone, Copy)] +// NOT `Clone`, NOT `Copy` — operator-ruled 2026-07-29: *"copies are forbidden, +// borrows are only for the same mailbox"*, and *"WitnessLens is forbidden as a +// copy. period."* +// +// A `Copy` borrow is a borrow that duplicates itself silently. It can be handed +// to a second holder, stored beside the first, and carried out of the +// compartment that owns the rows — with no move, no diagnostic, and nothing in +// a review to point at. That is precisely the escape the mailbox rule exists to +// close: a borrow is legitimate INSIDE one mailbox and illegitimate the moment +// it crosses an ownership boundary, and `Copy` is what lets it cross unnoticed. +// +// Without the derive the lens must be passed by reference and cannot be +// duplicated into a second owner, so its reach is bounded by the borrow it was +// built from. Anything that wants the rows elsewhere takes an ADDRESS and +// resolves it against the corpus there — never a second view of the same bytes. pub struct WitnessLens<'a> { rows: &'a [NodeRow], } impl<'a> WitnessLens<'a> { - /// Wrap a row slice as a witness lens. Stores the reference only — zero + /// Wrap the corpus as a witness lens. Stores the reference only — zero /// cost, no scan, no copy. + /// + /// # Invariant — `rows` is the WHOLE standing wave, never a sub-slice + /// + /// Every position on this type is an **absolute stream address**, and + /// `at(pos)` resolves it as `rows[pos]`. That identity holds only while + /// `rows[0]` IS stream position 0. Passing `&rows[start..]` with + /// `start != 0` does not shift the addressing — it silently redefines it: + /// `GradedRow::pos` then names the wrong corpus row, and a `visible` + /// predicate keyed by absolute positions selects the wrong domain or + /// nothing. The gathered `(stream_position, facet)` form this replaced + /// carried the address explicitly and so could not be misread this way. + /// + /// **Do not "fix" this by adding a base offset.** The standing wave is a + /// single ~16 MB array (32k rows × 512 B) and is lensed whole; a sub-slice + /// is not a supported narrowing, it is the error. Narrowing is expressed by + /// the caller's ADDRESS SET (the `visible` predicate today, an explicit + /// address list once `TD-LENS-QUORUM-SCANS-THE-WHOLE-LENS` lands) — never + /// by moving where the array starts. + /// + /// Found by Codex on #868, whose fixtures all built from position 0, so the + /// distinction was constant across every comparison in the suite. #[inline] #[must_use] pub const fn new(rows: &'a [NodeRow]) -> Self { diff --git a/crates/lance-graph-planner/src/adjacency/batch.rs b/crates/lance-graph-planner/src/adjacency/batch.rs index f86784a67..ab82de2b4 100644 --- a/crates/lance-graph-planner/src/adjacency/batch.rs +++ b/crates/lance-graph-planner/src/adjacency/batch.rs @@ -20,7 +20,20 @@ use super::csr::AdjacencyStore; /// /// Holds no adjacency data of its own — every accessor reads through to the /// store's resident CSR arrays. -#[derive(Debug, Clone, Copy)] +// NOT `Clone`, NOT `Copy` — operator-ruled 2026-07-29: *"copies are forbidden, +// borrows are only for the same mailbox"*. Same ruling that stripped the derive +// from `lance_graph_contract::witness_fabric::WitnessLens` (`b3515ba`); this is +// the same shape — a struct whose every field is a borrow. +// +// A `Copy` borrow duplicates itself silently: it can be handed to a second +// holder, stored beside the first, and carried out of the compartment that owns +// the CSR arrays with no move, no diagnostic, and nothing in a review to point +// at. That is exactly the escape the mailbox rule closes. Without the derive the +// view must be passed by reference (every call site here already does), so its +// reach is bounded by the borrow it was built from; anything that wants the +// adjacency elsewhere takes the source ids as ADDRESSES and re-derives the view +// against the store there — never a second view of the same bytes. +#[derive(Debug)] pub struct AdjacencyBatch<'a> { /// The store the view reads through. store: &'a AdjacencyStore, diff --git a/crates/lance-graph-planner/src/nars/meta_basin.rs b/crates/lance-graph-planner/src/nars/meta_basin.rs index ce9a1fab7..b282d11af 100644 --- a/crates/lance-graph-planner/src/nars/meta_basin.rs +++ b/crates/lance-graph-planner/src/nars/meta_basin.rs @@ -56,8 +56,10 @@ //! ([`ranked_outlier_suggestions`]) only RANKS what the coarse path could merely //! list. -use lance_graph_contract::causal_witness::{CausalWitnessFacet, Locus}; -use lance_graph_contract::witness_fabric::{quorum_mantissa, trajectory_of, TrajectorySignature}; +use lance_graph_contract::causal_witness::Locus; +use lance_graph_contract::witness_fabric::{ + quorum_mantissa_lens, trajectory_of_lens, TrajectorySignature, WitnessLens, +}; /// A row of the window, carried with the two gradings this module computes. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -329,23 +331,34 @@ pub struct OutlierSuggestion { pub anomaly: u32, } -/// Grade every row of a window: passive quorum + causal trajectory. -#[must_use] -pub fn grade_rows( - window: &[(usize, CausalWitnessFacet)], +/// Grade every visible row of a lens: passive quorum + causal trajectory. +/// +/// **Zero-copy (`zero-copy-lens-law.md`).** The row array IS the projection, so +/// this reads registers through [`WitnessLens::at`] — a bounds-checked cast into +/// each row's own bytes — instead of taking a caller-gathered +/// `&[(usize, CausalWitnessFacet)]` slab. No register byte is copied out. +/// +/// The visible domain is `{ pos ∈ 0..lens.len() | visible(pos) }`, visited +/// **ascending**: [`GradedRow::idx`] is the dense counter over that set and +/// [`GradedRow::pos`] is the absolute row position. `visible` is what carries a +/// SPARSE selection — the gathered form could hold positions `[0, 1, 2, 10, 20]` +/// with no rows between, and the lens expresses exactly that as a row array with +/// `visible` false on the gaps. +pub fn grade_rows<'a>( + lens: &'a WitnessLens<'_>, + visible: &'a impl Fn(usize) -> bool, locus: Locus, max_hops: u8, -) -> Vec { - window - .iter() +) -> impl Iterator + 'a { + (0..lens.len()) + .filter(move |&pos| visible(pos)) .enumerate() - .map(|(idx, &(pos, _))| GradedRow { + .map(move |(idx, pos)| GradedRow { idx, pos, - quorum: quorum_mantissa(idx, window), - trajectory: trajectory_of(idx, window, locus, max_hops), + quorum: quorum_mantissa_lens(pos, lens, visible), + trajectory: trajectory_of_lens(pos, lens, visible, locus, max_hops), }) - .collect() } /// **Ride the tail** — the rows the quorum does not cover. @@ -354,14 +367,33 @@ pub fn grade_rows( /// the tail. This deliberately returns rows rather than discarding them: the /// tail is the input to meta-clustering, not a reject pile. #[must_use] -pub fn tail(graded: &[GradedRow], tail_below: u8) -> Vec { +pub fn tail(graded: impl IntoIterator, tail_below: u8) -> Vec { graded - .iter() - .copied() + .into_iter() .filter(|r| r.quorum <= tail_below) .collect() } +/// Shape-only grading for the perturbation path: `meta_cluster` reads ONLY +/// `.trajectory`, so the quorum sweep — the expensive half — is skipped +/// entirely rather than computed and discarded. Lazy, like [`grade_rows`]. +fn grade_shapes_at<'a>( + lens: &'a WitnessLens<'_>, + visible: &'a impl Fn(usize) -> bool, + locus: Locus, + hops: u8, +) -> impl Iterator + 'a { + (0..lens.len()) + .filter(move |&pos| visible(pos)) + .enumerate() + .map(move |(idx, pos)| GradedRow { + idx, + pos, + quorum: 0, + trajectory: trajectory_of_lens(pos, lens, visible, locus, hops), + }) +} + /// Cluster rows into [`MetaBasin`]s by causal shape. /// /// Every basin is returned — including singletons. Returning only the large @@ -446,7 +478,8 @@ impl MetaBasin { #[must_use] pub fn stable_under_perturbation( &self, - window: &[(usize, CausalWitnessFacet)], + lens: &WitnessLens<'_>, + visible: &impl Fn(usize) -> bool, locus: Locus, perturbed_hops: u8, ) -> bool { @@ -460,16 +493,8 @@ impl MetaBasin { // budget — not just this basin's members — so a row that joins from // outside is visible. `quorum` is irrelevant to shape-clustering // (`meta_cluster` only reads `.trajectory`), so it is left at `0`. - let reperturbed: Vec = window - .iter() - .enumerate() - .map(|(idx, &(pos, _))| GradedRow { - idx, - pos, - quorum: 0, - trajectory: trajectory_of(idx, window, locus, perturbed_hops), - }) - .collect(); + let reperturbed: Vec = + grade_shapes_at(lens, visible, locus, perturbed_hops).collect(); meta_cluster(&reperturbed).into_iter().any(|b| { let mut got: Vec = b.members.iter().map(|m| m.idx).collect(); @@ -492,14 +517,15 @@ impl MetaBasin { #[must_use] pub fn stability_sweep( &self, - window: &[(usize, CausalWitnessFacet)], + lens: &WitnessLens<'_>, + visible: &impl Fn(usize) -> bool, locus: Locus, budgets: &[u8], ) -> Stability { Stability { stable: budgets .iter() - .filter(|&&p| self.stable_under_perturbation(window, locus, p)) + .filter(|&&p| self.stable_under_perturbation(lens, visible, locus, p)) .count(), probed: budgets.len(), } @@ -517,12 +543,13 @@ impl MetaBasin { #[must_use] pub fn stability_around( &self, - window: &[(usize, CausalWitnessFacet)], + lens: &WitnessLens<'_>, + visible: &impl Fn(usize) -> bool, locus: Locus, max_hops: u8, ) -> Stability { let budgets: Vec = stability_around_window(max_hops).collect(); - self.stability_sweep(window, locus, &budgets) + self.stability_sweep(lens, visible, locus, &budgets) } } @@ -549,16 +576,16 @@ fn stability_around_window(max_hops: u8) -> std::ops::RangeInclusive { /// may be an artifact", not "this row is wrong". #[must_use] pub fn outlier_suggestions( - window: &[(usize, CausalWitnessFacet)], + lens: &WitnessLens<'_>, + visible: &impl Fn(usize) -> bool, locus: Locus, max_hops: u8, perturbed_hops: u8, tail_below: u8, ) -> Vec { - let graded = grade_rows(window, locus, max_hops); - let tail_rows = tail(&graded, tail_below); + let tail_rows = tail(grade_rows(lens, visible, locus, max_hops), tail_below); let scores = density_scores(&tail_rows, DensityConfig::default()); - coarse_flags(window, locus, perturbed_hops, &tail_rows) + coarse_flags(lens, visible, locus, perturbed_hops, &tail_rows) .into_iter() .map(|(row, reason, basin_size)| OutlierSuggestion { row, @@ -573,7 +600,8 @@ pub fn outlier_suggestions( /// metric path can reuse it verbatim rather than restate it (a restated rule /// drifts; a reused one cannot). fn coarse_flags( - window: &[(usize, CausalWitnessFacet)], + lens: &WitnessLens<'_>, + visible: &impl Fn(usize) -> bool, locus: Locus, perturbed_hops: u8, tail_rows: &[GradedRow], @@ -581,7 +609,7 @@ fn coarse_flags( let mut out = Vec::new(); for basin in meta_cluster(tail_rows) { let size = basin.members.len(); - let stable = basin.stable_under_perturbation(window, locus, perturbed_hops); + let stable = basin.stable_under_perturbation(lens, visible, locus, perturbed_hops); for mini in mini_basins(&basin) { for &row in &mini.members { let reason = if !stable { @@ -626,17 +654,17 @@ fn anomaly_of(scores: &[DensityScore], idx: usize) -> u32 { /// it, and nothing here prunes, commits, or mutates the window. #[must_use] pub fn ranked_outlier_suggestions( - window: &[(usize, CausalWitnessFacet)], + lens: &WitnessLens<'_>, + visible: &impl Fn(usize) -> bool, locus: Locus, max_hops: u8, perturbed_hops: u8, tail_below: u8, cfg: DensityConfig, ) -> Vec { - let graded = grade_rows(window, locus, max_hops); - let tail_rows = tail(&graded, tail_below); + let tail_rows = tail(grade_rows(lens, visible, locus, max_hops), tail_below); let scores = density_scores(&tail_rows, cfg); - let coarse = coarse_flags(window, locus, perturbed_hops, &tail_rows); + let coarse = coarse_flags(lens, visible, locus, perturbed_hops, &tail_rows); let mut out: Vec = coarse .iter() @@ -673,6 +701,10 @@ pub fn ranked_outlier_suggestions( mod tests { use super::*; + use lance_graph_contract::canonical_node::{EdgeBlock, NodeGuid, NodeRow}; + use lance_graph_contract::causal_witness::CausalWitnessFacet; + use lance_graph_contract::witness_fabric::{quorum_mantissa, trajectory_of}; + fn w(edges: &[(Locus, i8)]) -> CausalWitnessFacet { let mut f = CausalWitnessFacet::ZERO; for &(l, o) in edges { @@ -681,6 +713,51 @@ mod tests { f } + /// A sparse `(pos, facet)` fixture rendered as the dense row array the lens + /// projects. Positions the fixture skips exist as rows (a row array has no + /// holes) but are excluded by [`vis_of`], which is how the lens expresses + /// the sparse selection the gathered form carried in its position column. + fn rows_from(regs: &[(usize, CausalWitnessFacet)]) -> Vec { + let max_pos = regs.iter().map(|&(p, _)| p).max().unwrap_or(0); + let mut rows: Vec = (0..=max_pos) + .map(|_| NodeRow { + key: NodeGuid::local(1), + edges: EdgeBlock::default(), + value: [0u8; 480], + }) + .collect(); + for &(pos, facet) in regs { + WitnessLens::write_register(&mut rows[pos], &facet); + } + rows + } + + /// The visibility predicate for a sparse fixture: exactly the positions it + /// names, so the lens domain equals the gathered window's position set. + fn vis_of(regs: &[(usize, CausalWitnessFacet)]) -> impl Fn(usize) -> bool + '_ { + move |p| regs.iter().any(|&(q, _)| q == p) + } + + /// The PRE-MIGRATION gathered body, kept verbatim as the oracle the lens + /// form is proven against. Retaining it is the point: an equivalence test + /// whose reference is a paraphrase proves the paraphrase, not the migration. + fn grade_rows_gathered( + window: &[(usize, CausalWitnessFacet)], + locus: Locus, + max_hops: u8, + ) -> Vec { + window + .iter() + .enumerate() + .map(|(idx, &(pos, _))| GradedRow { + idx, + pos, + quorum: quorum_mantissa(idx, window), + trajectory: trajectory_of(idx, window, locus, max_hops), + }) + .collect() + } + #[test] fn grading_carries_both_axes_and_the_tail_is_the_low_quorum_rows() { let win = vec![ @@ -688,15 +765,18 @@ mod tests { (1, CausalWitnessFacet::ZERO), (2, w(&[(Locus::Antecedent, 1)])), ]; - let graded = grade_rows(&win, Locus::Antecedent, 8); + let rows = rows_from(&win); + let lens = WitnessLens::new(&rows); + let vis = vis_of(&win); + let graded: Vec = grade_rows(&lens, &vis, Locus::Antecedent, 8).collect(); assert_eq!(graded.len(), 3); for g in &graded { assert!(g.quorum <= 15, "mantissa out of i4 range"); } // A high threshold takes everything; a threshold of 0 takes only the // rows nobody agrees with. The tail is a VIEW, never a discard. - assert_eq!(tail(&graded, 15).len(), 3); - assert!(tail(&graded, 0).len() <= 3); + assert_eq!(tail(graded.iter().copied(), 15).len(), 3); + assert!(tail(graded.iter().copied(), 0).len() <= 3); } #[test] @@ -708,7 +788,10 @@ mod tests { (3, CausalWitnessFacet::ZERO), (4, w(&[(Locus::Antecedent, 7)])), // escalates → its own shape ]; - let graded = grade_rows(&win, Locus::Antecedent, 8); + let rows = rows_from(&win); + let lens = WitnessLens::new(&rows); + let vis = vis_of(&win); + let graded: Vec = grade_rows(&lens, &vis, Locus::Antecedent, 8).collect(); let basins = meta_cluster(&graded); assert!(basins.len() >= 2, "escalating row was merged away"); // Every row survives clustering — nothing is silently dropped. @@ -727,7 +810,10 @@ mod tests { (2, w(&[(Locus::Antecedent, 1)])), (3, CausalWitnessFacet::ZERO), ]; - let graded = grade_rows(&win, Locus::Antecedent, 8); + let rows = rows_from(&win); + let lens = WitnessLens::new(&rows); + let vis = vis_of(&win); + let graded: Vec = grade_rows(&lens, &vis, Locus::Antecedent, 8).collect(); for b in meta_cluster(&graded) { let minis = mini_basins(&b); let total: usize = minis.iter().map(|m| m.members.len()).sum(); @@ -741,15 +827,18 @@ mod tests { (0, w(&[(Locus::Antecedent, 1)])), (1, CausalWitnessFacet::ZERO), ]; - let graded = grade_rows(&win, Locus::Antecedent, 8); + let rows = rows_from(&win); + let lens = WitnessLens::new(&rows); + let vis = vis_of(&win); + let graded: Vec = grade_rows(&lens, &vis, Locus::Antecedent, 8).collect(); for b in meta_cluster(&graded) { // Singletons have nothing to dissolve — never reported unstable. if b.members.len() < 2 { - assert!(b.stable_under_perturbation(&win, Locus::Antecedent, 1)); + assert!(b.stable_under_perturbation(&lens, &vis, Locus::Antecedent, 1)); } // The call is total: any budget, no panic. for p in [0u8, 1, 2, 8, 255] { - let _ = b.stable_under_perturbation(&win, Locus::Antecedent, p); + let _ = b.stable_under_perturbation(&lens, &vis, Locus::Antecedent, p); } } } @@ -788,7 +877,10 @@ mod tests { (22, w(&[(Locus::Antecedent, 1)])), (23, CausalWitnessFacet::ZERO), ]; - let graded = grade_rows(&win, Locus::Antecedent, 8); + let rows = rows_from(&win); + let lens = WitnessLens::new(&rows); + let vis = vis_of(&win); + let graded: Vec = grade_rows(&lens, &vis, Locus::Antecedent, 8).collect(); let basins = meta_cluster(&graded); let basin = basins .iter() @@ -820,7 +912,7 @@ mod tests { // the 3-hop chain) now shares that same post-perturbation shape too — // the basin's true membership grew. assert!( - !basin.stable_under_perturbation(&win, Locus::Antecedent, 1), + !basin.stable_under_perturbation(&lens, &vis, Locus::Antecedent, 1), "a row outside the basin converged onto its post-perturbation shape — \ this is a merge, not stability, and must be reported false" ); @@ -838,8 +930,11 @@ mod tests { (3, CausalWitnessFacet::ZERO), (4, w(&[(Locus::Antecedent, 7)])), ]; + let rows = rows_from(&win); + let lens = WitnessLens::new(&rows); + let vis = vis_of(&win); let before = win.len(); - let sug = outlier_suggestions(&win, Locus::Antecedent, 8, 2, 15); + let sug = outlier_suggestions(&lens, &vis, Locus::Antecedent, 8, 2, 15); // Advisory: the window is untouched (it is `&`, so this is a statement // about intent as much as memory). assert_eq!(win.len(), before); @@ -851,7 +946,10 @@ mod tests { assert!(s.row.idx < win.len()); } // Deterministic: same input, same suggestions — auditable, not a draw. - assert_eq!(sug, outlier_suggestions(&win, Locus::Antecedent, 8, 2, 15)); + assert_eq!( + sug, + outlier_suggestions(&lens, &vis, Locus::Antecedent, 8, 2, 15) + ); } /// A suggester that can never suggest is as useless as a gate that never @@ -867,7 +965,10 @@ mod tests { (4, w(&[(Locus::Antecedent, 7)])), (5, w(&[(Locus::Kausal, -1)])), ]; - let sug = outlier_suggestions(&win, Locus::Antecedent, 8, 2, 15); + let rows = rows_from(&win); + let lens = WitnessLens::new(&rows); + let vis = vis_of(&win); + let sug = outlier_suggestions(&lens, &vis, Locus::Antecedent, 8, 2, 15); assert!( !sug.is_empty(), "outlier suggester never fires — inert channel" @@ -1084,10 +1185,13 @@ mod tests { (2, w(&[(Locus::Antecedent, 1)])), (3, w(&[(Locus::Antecedent, 7)])), ]; - let graded = grade_rows(&win, Locus::Antecedent, 8); + let rows = rows_from(&win); + let lens = WitnessLens::new(&rows); + let vis = vis_of(&win); + let graded: Vec = grade_rows(&lens, &vis, Locus::Antecedent, 8).collect(); let budgets: Vec = (0..=10).collect(); for b in meta_cluster(&graded) { - let sweep = b.stability_sweep(&win, Locus::Antecedent, &budgets); + let sweep = b.stability_sweep(&lens, &vis, Locus::Antecedent, &budgets); assert_eq!(sweep.probed, budgets.len()); assert!(sweep.stable <= sweep.probed); assert!(sweep.fraction_milli() <= DENSITY_SCALE); @@ -1095,7 +1199,7 @@ mod tests { // count must equal the number of budgets the bool wrapper accepts. let by_wrapper = budgets .iter() - .filter(|&&p| b.stable_under_perturbation(&win, Locus::Antecedent, p)) + .filter(|&&p| b.stable_under_perturbation(&lens, &vis, Locus::Antecedent, p)) .count(); assert_eq!(sweep.stable, by_wrapper); // Singletons have nothing to dissolve at ANY budget. @@ -1103,8 +1207,11 @@ mod tests { assert_eq!(sweep.fraction_milli(), DENSITY_SCALE); } // Deterministic, and total over the default range. - assert_eq!(sweep, b.stability_sweep(&win, Locus::Antecedent, &budgets)); - let _ = b.stability_around(&win, Locus::Antecedent, 255); + assert_eq!( + sweep, + b.stability_sweep(&lens, &vis, Locus::Antecedent, &budgets) + ); + let _ = b.stability_around(&lens, &vis, Locus::Antecedent, 255); } // An empty sweep falsifies nothing, so it claims full stability. let lone = MetaBasin { @@ -1112,7 +1219,7 @@ mod tests { members: vec![], }; assert_eq!( - lone.stability_sweep(&win, Locus::Antecedent, &[]) + lone.stability_sweep(&lens, &vis, Locus::Antecedent, &[]) .fraction_milli(), DENSITY_SCALE ); @@ -1159,9 +1266,12 @@ mod tests { (4, w(&[(Locus::Antecedent, 7)])), (5, w(&[(Locus::Kausal, -1)])), ]; + let rows = rows_from(&win); + let lens = WitnessLens::new(&rows); + let vis = vis_of(&win); let before = win.clone(); let cfg = DensityConfig::default(); - let sug = ranked_outlier_suggestions(&win, Locus::Antecedent, 8, 2, 15, cfg); + let sug = ranked_outlier_suggestions(&lens, &vis, Locus::Antecedent, 8, 2, 15, cfg); assert!( !sug.is_empty(), "ranked suggester never fires — inert channel" @@ -1187,7 +1297,7 @@ mod tests { } assert_eq!( sug, - ranked_outlier_suggestions(&win, Locus::Antecedent, 8, 2, 15, cfg), + ranked_outlier_suggestions(&lens, &vis, Locus::Antecedent, 8, 2, 15, cfg), "ranking is not deterministic" ); } @@ -1204,9 +1314,19 @@ mod tests { (4, w(&[(Locus::Antecedent, 7)])), (5, w(&[(Locus::Kausal, -1)])), ]; - let coarse = outlier_suggestions(&win, Locus::Antecedent, 8, 2, 15); - let ranked = - ranked_outlier_suggestions(&win, Locus::Antecedent, 8, 2, 15, DensityConfig::default()); + let rows = rows_from(&win); + let lens = WitnessLens::new(&rows); + let vis = vis_of(&win); + let coarse = outlier_suggestions(&lens, &vis, Locus::Antecedent, 8, 2, 15); + let ranked = ranked_outlier_suggestions( + &lens, + &vis, + Locus::Antecedent, + 8, + 2, + 15, + DensityConfig::default(), + ); for c in &coarse { let r = ranked .iter() @@ -1217,4 +1337,141 @@ mod tests { } assert!(ranked.len() >= coarse.len()); } + + /// **The migration's proof** — the lens form reproduces the PRE-MIGRATION + /// gathered body exactly, over two fixtures chosen so that BOTH graded axes + /// are actually exercised. + /// + /// The risk the sparse fixture pins is SPARSITY. `resolve_chain` walks hops + /// by absolute stream POSITION (`cur_pos + off`), so a gathered window could + /// name positions `[0,1,2, 10,11,12, 20,21,22,23]` with nothing in between; + /// the lens instead indexes a dense row array, and the gaps must be excluded + /// by `visible` rather than by simply not existing. If those two notions of + /// "not addressable" ever diverged, that fixture is where it would show. + /// + /// But a sparse window has NO quorum: no two rows are close enough to agree + /// on an absolute target, so every row grades `quorum = 0` and the quorum + /// half of the comparison is vacuous. (That is not a hypothesis — the + /// anti-vacuity assert below caught exactly this while the migration was + /// being written, on a version of this test that used the sparse fixture + /// alone.) The dense fixture supplies the agreement the sparse one cannot. + #[test] + fn lens_grading_matches_the_gathered_oracle_on_sparse_and_dense_windows() { + // Sparse: three chains with gaps between them; every chain hops into a + // position the window does not name. + let sparse = vec![ + (0, w(&[(Locus::Antecedent, 1)])), + (1, w(&[(Locus::Antecedent, 1)])), + (2, CausalWitnessFacet::ZERO), + (10, w(&[(Locus::Antecedent, 1)])), + (11, w(&[(Locus::Antecedent, 1)])), + (12, CausalWitnessFacet::ZERO), + (20, w(&[(Locus::Antecedent, 1)])), + (21, w(&[(Locus::Antecedent, 1)])), + (22, w(&[(Locus::Antecedent, 1)])), + (23, CausalWitnessFacet::ZERO), + ]; + // Dense: rows 0 and 1 converge on absolute position 2 across FOUR content + // loci, so they grade a non-zero quorum; row 2 agrees with nobody and + // grades 0. Four loci is not decoration — `quorum_mantissa` scales + // `agreed * 15 / (peers * 14)` and rounds DOWN, so a single agreeing + // locus floors to 0 and the axis would still be untested. + let dense = vec![ + ( + 0, + w(&[ + (Locus::Temporal, 2), + (Locus::Kausal, 2), + (Locus::Modal, 2), + (Locus::Lokal, 2), + ]), + ), + ( + 1, + w(&[ + (Locus::Temporal, 1), + (Locus::Kausal, 1), + (Locus::Modal, 1), + (Locus::Lokal, 1), + ]), + ), + (2, w(&[(Locus::Antecedent, 1)])), + ]; + + let mut saw_escalation = false; + let mut quorums = std::collections::BTreeSet::new(); + let mut saw_sparsity = false; + + for (label, win) in [("sparse", &sparse), ("dense", &dense)] { + let rows = rows_from(win); + let lens = WitnessLens::new(&rows); + let vis = vis_of(win); + saw_sparsity |= rows.len() > win.len(); + + for hops in [0u8, 1, 2, 3, 8, 255] { + let gathered = grade_rows_gathered(win, Locus::Antecedent, hops); + let lensed: Vec = + grade_rows(&lens, &vis, Locus::Antecedent, hops).collect(); + assert_eq!( + gathered.len(), + lensed.len(), + "{label}: row count diverged at max_hops={hops}" + ); + for (g, l) in gathered.iter().zip(lensed.iter()) { + assert_eq!(g, l, "{label}: grading diverged at max_hops={hops}"); + saw_escalation |= g.trajectory.escalated; + quorums.insert(g.quorum); + } + } + } + + // Anti-vacuity, one clause per axis the comparison claims to cover. An + // all-identical grading would make the equality pass for reasons that + // have nothing to do with the migration. + assert!( + saw_sparsity, + "no fixture was actually sparse — the gap-exclusion path went unchecked" + ); + assert!( + saw_escalation, + "no fixture escalated — the escalation axis went unchecked" + ); + assert!( + quorums.len() > 1, + "every row got the same quorum ({quorums:?}) — the quorum axis went unchecked" + ); + } + + /// A position the fixture SKIPS must read as unaddressable, exactly as a + /// position absent from a gathered window did. This is the half the + /// equivalence test above cannot state directly: it proves the gaps are + /// excluded because `visible` says so, not because the rows are empty. + #[test] + fn an_invisible_gap_is_excluded_even_though_the_row_exists() { + let win = vec![ + (0, w(&[(Locus::Antecedent, 1)])), + (2, CausalWitnessFacet::ZERO), + ]; + let rows = rows_from(&win); + let lens = WitnessLens::new(&rows); + let vis = vis_of(&win); + + assert_eq!( + lens.len(), + 3, + "row 1 must EXIST for the exclusion to mean anything" + ); + assert!(lens.at(1).is_some(), "row 1 is addressable by the lens"); + assert!(!vis(1), "row 1 must be invisible"); + + let graded: Vec = grade_rows(&lens, &vis, Locus::Antecedent, 8).collect(); + assert_eq!(graded.len(), 2, "the invisible row leaked into the grading"); + assert_eq!( + graded.iter().map(|g| g.pos).collect::>(), + vec![0, 2], + "positions must be the visible ones, ascending" + ); + // `idx` is the dense counter over the VISIBLE set, not the position. + assert_eq!(graded.iter().map(|g| g.idx).collect::>(), vec![0, 1]); + } }