diff --git a/.claude/board/AGENT_LOG.md b/.claude/board/AGENT_LOG.md index 305f334cf..919209739 100644 --- a/.claude/board/AGENT_LOG.md +++ b/.claude/board/AGENT_LOG.md @@ -1,3 +1,18 @@ +## 2026-09-07 — D-SPG-1: one Sonnet worker from a 60-line spec, orchestrator-gated (contract `AlphaMask::{words, from_words}`) + +- **Worker (Sonnet, `general-purpose`, edit-only, guardrails §1 verbatim, one file `crates/lance-graph-contract/src/alpha.rs`):** two methods after `materialize_ordinals`, three tests in the existing module; reported its own unverified items honestly ("not compiled, not run — orchestrator gates"; `PartialEq` presence checked by reading the derive). +- **Gates run centrally:** `cargo fmt -p lance-graph-contract -- --check` 0; `cargo clippy -p lance-graph-contract --all-targets -- -D warnings` 0; `cargo test -p lance-graph-contract` 1326/1326 (+7 +12 doc/integration). **Mutation:** `if tail != 0` disabled → `from_words_clears_phantom_tail_bits` fails on `count == 200` (restored; diff back to +78/−0). +- **Board (same commit):** STATUS_BOARD D-SPG-1 → Shipped; plan §5 row; LATEST_STATE contract delta. PR opened for the merge because MedCare-rs pins the contract to git `main`. + +## 2026-09-07 — SPOG alpha channel, Phase 0: four read-only Sonnet inventories, orchestrator-verified, spec written on the main thread + +- **Why:** operator: *"probe autoattended autonomous decision making until you get MedCare-rs SPOG alpha channel to work / … rows are experimental in lance 11 and only required for tombstones which we avoid by having sealed batch per cycle / … the relevant bakes in S3 might not be per domain separate …"*, plus *"check Mississippi queen hexagon board game effect vs masking algebra ternlogq chaining amortization / same bit that masks mq might 'mask' SPO 'angle'"*, plus *"use Sonnet agents for grindwork"*, plus (mid-turn) *"make sure to migrate the handrolled MedCare-rs alpha to LG 1198 alpha"* — folded into the spec as F9 and the broadened D-SPG-5 before the commit. +- **Agents (all `general-purpose` Sonnet, read-only, no cargo, no edits, guardrails §1 pasted verbatim, each writing ONLY its own inventory file under the session scratchpad — banked OUTSIDE the public repo because two of them quote a private consumer; the orchestrator is the sole writer of every board file):** (1) lgj-abi kernels/hop/plan_eval + ndarray mask primitives; (2) contract `alpha`/`spog_tenants`/`alpha_tunnel`/`wave_dispatch`/`rung_horizon` + consumer census; (3) MedCare-rs alpha consumer side (`frontier_dispatch`, `patient_shadow`, `attention`, `quad_tenant`/`quad_slab`, domain/TUI config); (4) MedCare-rs bake state (`bakes.tsv`, ranges, `ontology_map.tsv`, fetch/upload scripts, `obo_store`/`rails` tails, local `.data`). +- **Orchestrator verification (every claim that entered a board file):** re-grepped `ternlog|AND3` over lgj-abi src (0 hits) and read `lgj_hop`'s two AND sites; confirmed `popcount_batch_u64` re-export at ndarray `simd.rs:739` (the agent had flagged it NOT FOUND); confirmed `quad_slab::write` has no bake-path caller under `crates/*/src` and that the quad is stamped on 3,551 rows per `bakes.tsv`; confirmed `RailStore` per-classid grouping at `rails.rs:965-975`; confirmed `MONDO_CLASSID = 0x9101_0000` (domain form live) at `crosswalk.rs:173`. **Reshaped two agent claims:** agent 4 called `RailProjection` "variants" absent — correct, it is a struct; agent 2's `AlphaAllocation` hits in `rung_horizon.rs` are all test-module (verified). +- **Synthesis on the main thread (accumulation):** `alpha-channel-rung-overlay-v1.md` (1,222 lines, read in full — D-ACR-1 shipped as `attention_facet::RowFocusMask`, a facet-prefix set distinct from `AlphaMask`; §3k operator ruling: explicit mask ABI traversal, never VSA), `.claude/temporal/06–09` (Stage 2 placement, P3 bounds, seed.clone ruling, rung collision), ndarray `gemm-ternlog-mask-consolidation-v1.md` §11–§13 (K0..K7 as immediate; D-GTM-0k/0l results; the "OGAR-minted address space" next probe), STATUS_BOARD D-ACR-*/D-RLR-5, IDEAS 2026-09-07 cards. +- **Board writes (this commit):** plan `spog-alpha-channel-v1.md` (new, 302 lines, Phase-0 spec: frozen decisions F1–F8, D-SPG-0..8, gates a–h); STATUS_BOARD section; INTEGRATION_PLANS prepend; EPIPHANIES `E-THE-FUSED-AND3-HOP-WAS-NEVER-SHIPPED-LGJ-HOP-IS-TWO-ANDS-1`; E-NXG-8 Confidence line correction pointer; IDEAS PROBE-CROSSWALK-MASK-1 status → In progress; `.claude/knowledge/membrane-tiers.md` §"The polyfill is the worked instance" ⊘ in place; LATEST_STATE delta. Gates: `append_only_gate.py origin/main`, `citation_decay.py --since origin/main`, supersession index regenerated LAST. +- **Not done, on purpose:** no code (D-SPG-1 is the next commit); no MedCare-rs edits in this repo; no PR opened (not asked); no force-push anywhere. + ## 2026-09-07 — plan inventory: 5 read-only Sonnet agents, orchestrator-verified, consolidated to `PLAN-INVENTORY-2026-09-07.md` - **Why:** operator: *"create an inventory about the plans in `.claude` `.claude/V3` `.claude/plans` — use sonnet agent for grindwork — what is still open what is closed — check if v3 already has the harvest and update — write down any epiphanies and expansion ideas"*, plus *"check the synergies of mississippi queen board game blasgraph shannon EWA known unknowns ternlogq etc"*, plus the per-domain-table / chain-of-masks / SPOG-bakes dialogue. diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index e03499801..2df8d855f 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -1,3 +1,368 @@ +## 2026-09-07 — E-A-DYNAMIC-DOMAIN-MASK-IS-A-SECOND-WITNESS-AND-ITS-ALIGNMENT-IS-CALIBRATION-1 — the "horseshoe" was a category error; the equality I called a coincidence is a known-answer target + +**Status:** OPERATOR RULING (2026-09-07, two messages: *"horseshoe mask is a +category error … the only way your horseshoe mask is perfectly correct if you +created a lazylock mask from TUI over CUI to separate"*, then *"basically Dynamic +domain mask with accidentally so perfect snomedid alignment that … you could even +use it for calibration"*). Verified against the shipped consumer code and the +consumer's own census numbers. **⊘ 2026-09-07, later the same day — the +dissolution SHIPPED in the consumer on the operator's go.** `domain(D) = +static(D) ∪ dynamic(D)`; `horseshoe_mask` retired; the value half is ONE +`LazyLock` pass over the immutable bake resolving all eight domains at once. +The falsifier below RAN: the two lab masks are disjoint and equal in count +(103,291), the non-aligned domains are proper subsets in count, and the +refusal — 38,953 lane rows whose witness names no domain — is counted rather +than lost between the halves. Address half → union: lab 103,291 → 206,582 · +substance 131,582 → 254,485 · anatomy 119,684 → 119,732 · procedure 38,956 → +40,340. Three disable runs fire. The crosswalk-sweep half of the falsifier is +D-SPG-4 and has NOT run — it needs the CURIE→address resolver. +**Confidence:** High on the category error (three concrete defects, below). High +on the calibration READING. The bijection itself is CONSISTENT WITH the +cardinalities, not proven by them — proving it is what the probe is for. + +**The category error, concretely.** The consumer shipped two functions: +`domain_mask(tenants, D)` — OR over tenants whose every classid resolves to `D` +by address — and `horseshoe_mask(rows, D)` — a per-row scan of the one lane +whose address is deliberately under-determined (`FacetRegime::PerRowTui`), +assigning rows to `D` by a value-side witness at `value[0..2]`. Three defects: + +1. **`domain_mask(D)` is not the domain.** Its all-single-facet filter SKIPS the + under-determined lane, so the function named "domain" silently returns the + domain minus its multi-facet part. Substance: 131,582 returned, 122,903 more + unreported. +2. **No caller unions them.** The sole consumer prints them as two adjacent + columns. The API makes the union the caller's job and never says so. +3. **The error was written into a test as an invariant** — *"CUI is a horseshoe + lane, never folded into a domain_mask"* — which is why it survived review. + +The U-turn is a property of the VOCABULARY, not of the rows. A row in that lane +witnessing anatomy IS anatomy. "Horseshoe" names a category with no referent in +the ontology: there are domains, and one lane needs a second witness to be +assigned to them. + +**The dissolution: a LazyLock partition.** Compute once, over the immutable bake, +the value-witness → domain partition of the under-determined lane: N masks, one +per domain. Then `domain(D) = static(D) ∪ dynamic(D)`, one uniform mask, and a +row that arrived by value-witness is indistinguishable at the point of use from +one that arrived by address. `horseshoe_mask` has nothing left to return. This +also repairs a performance path: the shipped version rescans 762,041 rows per +call against tenant masks that are computed once — contradicting the same +deliverable's own gate (h) result (0.0019 amortization ratio). + +**Static vs dynamic is the two-witness rule at mask level.** `Domain::of_row` +already carries the operator's 2026-08-10 contract — *"classid AND the TUI +witness must agree"* — per row. Lifted to masks, the address-derived partition +and the value-derived partition are two INDEPENDENT readings of the same +question. Where they agree the substrate is sound; where they disagree the +disagreement LOCALISES. + +**The alignment, and what I got backwards.** Measured on the real image (consumer +census, D-SPG-2): static lab = 103,291 rows (the address-determined lab tenant); +dynamic lab = 103,291 rows (the under-determined lane's rows with a lab +value-witness). Disjoint row sets, identical cardinality. I recorded this in the +consumer ledger as *Koinzidenz* and warned readers not to derive a bridge from +it. That was the wrong direction: two disjoint sets of equal size, produced by a +bake that constructs the multi-facet lane FROM the single-facet ones, is what a +1:1 crosswalk looks like from the outside — looks like, not is: equal +cardinality on disjoint sets is CONSISTENT WITH a bijection and proves none +(two different 103,291-row sets satisfy it equally). The other domains do NOT align +(substance 131,582 vs 122,903; anatomy 119,684 vs 48; procedure 38,956 vs +1,384) — and that is not error but COVERAGE: the multi-facet lane carries only +part of those domains. Alignment is consistent with bijection; misalignment +measures reach. The bijection itself is proven only at row level — set equality +of the crosswalk's survivors against the scalar reference, or a unique +bidirectional mapping — which is exactly what gate (a) runs. Lab is the one +domain where the cardinalities PERMIT the bake to be bijective — "accidentally +so perfect" is the hypothesis the probe tests, not its result. + +**Why that is calibration, in the strict sense.** A known-answer CARDINALITY +target derived from the data's own structure — a necessary condition every +correct chain must meet, never the sufficient one; the set-level oracle stays the +scalar reference in gate (a): + +- **D-SPG-4 gate (a)** gains a target it lacked. The crosswalk is a chain of + masked equality sweeps (`spog-alpha-channel-v1.md` §3.2). A sweep from the + lab tenant across the bridge must land on exactly 103,291 rows. Any other + count is a defect in the chain — not "a number to report". The right count is + not a pass: the survivor SET must still equal the scalar reference's. +- **Gate (f) — the K0..K7 "angle"** — gains a discriminator. Until now the + immediate could only be checked for self-consistency (the eight minterms + partition the population). A bijective domain makes the CORRECT immediate the + one whose survivor set equals the reference's; every wrong immediate can now + be caught EARLY, by count alone, before the set comparison runs — a count + match admits an immediate to the set test, it does not pass it. +- **Bake drift becomes detectable for free**: `popcount(static_lab) != + popcount(dynamic_lab)` after a re-bake means the artifact or a witness moved. + One popcount, 20 ns, no join — one-directional: inequality proves drift, + equality proves nothing. + +**Falsifier.** After the LazyLock partition lands: the two lab masks must be +disjoint AND equal in count (can-fire: a bake that breaks either); a crosswalk +sweep from the lab tenant must reproduce 103,291 exactly AND its survivor set +must equal the scalar reference's (can-fire: swap the immediate; can-fire on the +set half: a wrong-but-equinumerous chain); and for a non-aligned domain the dynamic mask must be a proper subset +in count of the static one (coverage, not error — a dynamic count EXCEEDING the +static one would be the real anomaly). + +**Scoping (Foundry rule).** The value-witness → domain table is domain knowledge +and stays in the consumer. The PATTERN — an address-under-determined lane +completed by a value-side witness, resolved once into address-shaped masks, with +cross-witness cardinality as a built-in calibration — is agnostic and belongs +upstream. Read with `E-SPOG-IS-FOUNDRY-WITH-AN-ABI-SHAPED-SUBSTRATE-1` (same +day): this is the one place the address is NOT the whole ontology, and the fix +is to make it so at bake-read time rather than at every query. + +--- + +## 2026-09-07 — E-SPOG-IS-FOUNDRY-WITH-AN-ABI-SHAPED-SUBSTRATE-1 — the ontology is the ADDRESS, so cross-domain is an ordinal problem and never an integration one + +**Status:** OPERATOR RULING (2026-09-07, verbatim: *"SPOG is similar to Palantir +foundry across the Domains, with the difference that our Substrat is ABI +shaped"*). Completes `E-LANCE-GRAPH-OWNS-THE-AGNOSTIC-THINKING-CONSUMERS-BIND-DOMAIN-1` +(same day), which named WHO owns the thinking; this names WHAT SHAPE it has. +**Confidence:** High. The partition below is measured on the real artifact; the +consequences are entailments of the shape, not projections. + +**The comparison, and the one difference that changes everything.** + +| | Palantir Foundry | SPOG | +|---|---|---| +| how domains are unified | mapped onto a shared **object** layer | mapped onto a shared **address** space | +| what a domain is | an object type in an ontology | a tenant `G = classid >> 16`, a contiguous ordinal window | +| relating two domains | traverse links / **join** object sets | **AND two masks** over the same base ordinals | +| what the engine must do | plan and execute the join | index a ternlog immediate | + +Foundry's ontology is a semantic layer ABOVE heterogeneous stores, so +cross-domain reasoning is an integration act and a join is the mechanism. SPOG's +ontology IS the address, so cross-domain reasoning is an ordinal-range act and +there is nothing to integrate. + +**This is why "no joins ever" is substrate, not preference** (operator, same +session: *"datafusion does joins, we do masking Ops, no joins ever"*). A join +materialises the rejected world — two relations in, a third out. Under an +ABI-shaped substrate the operand is already one image, so the complement holds: +`resident ⊗ A ⊗ B ⊗ C`, and the rejected volume never becomes a representation. +A query planner here is not an expensive way to do the job; it is a way to do a +DIFFERENT job that this substrate does not have. + +**Measured, and it is the ontology layer.** `all-lanes.soa`: 762,041 rows, 16 +distinct `graph_of` tenants that **PARTITION** the image — Σ of tenant counts and +the union of tenant masks are both 762,041, no row in two tenants, every tenant +count equal to the sum of its constituent classids' windows (D-SPG-2). A Foundry +deployment would call that an object-type registry; here it is a fact about where +bytes sit, and the tenant mask is 95,256 B — L2-resident, so the whole "ontology" +is a cache-resident bitmask. + +**Consequence — the SPO triple is ABI-shaped too.** A triple is not three columns +to be joined. `mask_ternlog::(S, P, O)` computes `IMM[(s<<2)|(p<<1)|o]`, so +the immediate's eight bits ARE the eight presence-projections K0..K7 of one quad +row, and the six wirings of S/P/O onto A/B/C are the six angles (§4 of +`spog-alpha-channel-v1.md`). Subject, predicate and object are three bits +indexing a constant, not three relations. + +**Consequence — it renames the open problem, which changes what gets built.** +MedCare-rs #620 measured **0.00 % cross-tenant `is_a` edges**: a disease's +subsumption spine never leaves the disease ontology, and only the RO cross-axis +crosses. Read as Foundry, that is a missing link type and the fix is a +crosswalk join table. Read as SPOG, it is an **address** result: the CURIE→address +resolver has not minted so that a cross-domain relation is address adjacency. +Same measurement, opposite deliverable — and the second one is the one that keeps +the 20 ns regime. + +**Falsifier.** Any design that reaches for a join, a lookup table, or a +mapping service to relate two domains, instead of asking what address layout +makes them adjacent in one image. Also: any claim that a tenant boundary needs +enforcement machinery — the partition is a property of the keys, and +`AlphaFocus::cross`'s `ptr::eq` on the base slice is the only "provenance" the +shape admits (gate (d), re-scoped this session). + +--- + +## 2026-09-07 — E-LANCE-GRAPH-OWNS-THE-AGNOSTIC-THINKING-CONSUMERS-BIND-DOMAIN-1 — the Foundry split, and why four separate violations in one session were one violation + +**Status:** OPERATOR RULING (2026-09-07, verbatim: *"lance-graph owns the +agnostic thinking / Akin to Palantir foundry"*), given after catching a +consumer-side reimplementation of the owned writer. +**Confidence:** High on the rule. The `spog_masks` consequence below is measured +against the shipped code; the BBB placement question it raises is OPEN. + +**The rule.** lance-graph is the platform: it owns the ontology (contract types) +and the OPERATIONS — the mask algebra, the cognitive cycle, the writer, the +temporal read. A consumer (MedCare-rs, woa-rs, smb-office-rs, …) is a tenant: it +supplies DOMAIN SHAPE and consumes thinking. It never implements thinking, and it +never implements substrate. This restates MedCare's own commitment #4 (*"Thinking +lives only in lance-graph. No `medcare-thinking` duplicate crate"*) as a +POSITIVE architecture rather than a prohibition, which is what makes it +checkable. + +**Why it is worth an entry: it collapses four findings into one.** In a single +session the same agent wrote, in a consumer, a sealed-cycle writer +(`LanceCycleWriter`'s job), a version successor (`sealed_version = base_version ++ 1`, a verified identity inside that sink), a version-pinned read +(`temporal::QueryReference::at`), and a second copy of the rung × tenant cross +(`AlphaFocus::{cell, unlooked}`, shipped upstream in #1220). Each was caught +separately and each looked like its own mistake. Under the Foundry split they +are one mistake with four faces: **platform-side work performed tenant-side.** + +**The demarcation is not "is it generic code", it is "is it domain +knowledge".** Measured on `medcare-cohorts::spog_masks`: + +| function | what it does | owner | +|---|---|---| +| `tenant_masks` | sweep distinct classids, OR-fold per `graph_of` | **agnostic** — nothing medical | +| `rung_tenant_cell` | AND of a rung mask and a tenant mask | **agnostic** | +| `unlooked` | tenant AND-NOT any-rung | **agnostic** | +| `domain_mask` | which Gs constitute a `Domain` | **tenant** — domain knowledge | +| `horseshoe_mask` | the per-row TUI fence for the U-shaped lane | **tenant** — domain knowledge | + +Three of five are thinking that a tenant is carrying. The tell is that the first +three can be written without knowing the word "medical", and the last two cannot. + +**Consequence — F5 in `spog-alpha-channel-v1.md` was mis-framed by its own +author.** It recorded "scalar `AlphaFocus` (#1220) vs SIMD `spog_masks` +(D-SPG-3)" as an open OPERATOR PREFERENCE. It is not a preference: the cell +belongs upstream by this rule, and the tenant should call it. What remains open +is narrower and genuinely architectural — **where an ndarray-backed mask algebra +can live**, given that `lance-graph-planner` is BBB-forbidden in a customer +binary (Iron Rule 1) and `lance-graph-contract` is zero-dep by construction. A +contract feature-gate, or a new BBB-allowed crate between them, are the two +shapes; neither is this entry's to choose. + +**Falsifier.** For any function in a consumer crate, ask whether it can be +written without naming the domain. If yes and it is not a thin call into +lance-graph, it is thinking in the wrong repo — regardless of how mechanical it +looks. Masks feel like data and slipped through this test for a whole +deliverable. + +Read with `E-A-CONSUMER-THAT-OPENS-A-DATASET-HAS-ALREADY-LOST-1` (same day, the +writer half) and `E-TOPOLOGY-MASKS-MAGNITUDE-COMPOSE-NEVER-COLLAPSE-1` (same +day, what the operations ARE). + +--- + +## 2026-09-07 — E-A-CONSUMER-THAT-OPENS-A-DATASET-HAS-ALREADY-LOST-1 — re-deriving a proven identity from outside the thing that proves it is the tell + +**Status:** FINDING, operator-caught (2026-09-07: *"that's not a convenience you're +violating lance-graph 879 batchwriter SOA owned"*, then *"879 909..912 1049 1198"*). +Verified against the merged code and the #879/#911/#912 arc entries. +**Confidence:** High — every property below is quoted from the shipped module docs. + +**What I did.** Writing D-SPG-6 ("sealed batch per cycle"), I wrote a free +`async fn seal_alpha_cycle` in a CONSUMER that did `Dataset::open` to read the +version, computed `cycle = version + 1`, and `Dataset::write`-appended. I +justified it as convenience — reaching for the `lance` umbrella crate because a +neighbouring function already did. That framing was wrong twice over: it was not +convenience, it was an ownership violation; and the DataFusion weight I was +worrying about was a symptom, not the disease. + +**What already existed.** `LanceCycleWriter` (`lance-graph::graph::cycle_sink`, +#911 → #912 Phase A) is the **SOLE application writer**, and the topology is +enforced by the TYPE: non-`Clone` (a second handle cannot be minted), +`commit_cycle(&mut self, …)` (two commits cannot interleave), one long-lived +owned `Dataset` handle (no per-operation reopen). The 64k SoA owners are +*"parallel PRODUCERS (fire-and-forget: they cast on behalf of their mailbox and +receive no acknowledgement), never Lance writers"*. + +**The four properties my version lacked**, each hard-won in a review round: + +| shipped | mine | +|---|---| +| sole writer, non-`Clone`, owned handle | a second unowned writer, reopening per call | +| producers cast, never write | consumer written as a Lance writer | +| **no semantic change → no write → no version** (#911's empty-cycle versioning REMOVED) | wrote unconditionally — an empty saccade mints a version | +| no rollback; durable `(cycle, batch_hash)`, reconcile FIRST, `HashConflict` fails closed | read-version-then-append: a TOCTOU | + +**The sharpest of them.** `Append` in Lance **rebases even on a single attempt** +(strict no-rebase exists only for `Overwrite` — measured in +`lance-9.0.0/src/io/commit.rs`). So my "refuse, not renumber" guard — read the +version, compare, then append — *cannot do what its own error message claims*. +#911 first fixed this with a compensating `Dataset::delete`; #912 then REMOVED +that too, because a published manifest is HISTORY and a delete is another +version, not a rollback. I had reinvented a mechanism that was already tried and +already superseded. + +**The transferable tell, and it is cheap to check.** +`sealed_version = base_version + 1` is recorded on the #911 entry as *"a verified +identity, not an assumption"* — verified INSIDE the sink, which is what lets +readers derive the cycle↔version mapping from the co-committed frame row with +zero sidecar state. **I re-derived that identity from outside the component that +proves it.** Whenever code computes `next = current + 1` for state another +component owns, the question is not "is the arithmetic right" (it was) but "who +is entitled to say this" — and if the answer is a type you did not call, the +write is already an orphan. A consumer that reaches for `Dataset::open` has +answered that question wrongly before writing a line. + +**Why the DataFusion weight was the symptom.** Bypassing the owned writer meant +reaching for the `lance` umbrella crate, which drags the query engine in +transitively. Operator, same session: *"datafusion does joins, we do masking Ops, +no joins ever"* — a join materialises the rejected world (two relations in, a +third out), which is the exact complement of `resident ⊗ A ⊗ B ⊗ C`. The +crosswalk is the thing that LOOKS like a join (MONDO ↔ CUI ↔ LOINC ↔ SNOMED) and +is a chain of masked equality sweeps (§3.2), so there is no join formulation to +carry. Read against +`E-TOPOLOGY-MASKS-MAGNITUDE-COMPOSE-NEVER-COLLAPSE-1` (same day): a query engine +on this path is a fourth thing that collapses topology, masks and magnitude back +into relational algebra. + +**Falsifier.** Any consumer-side `Dataset::open` / `Dataset::write` in the +alpha/mask chain; any caller computing a version successor for state it does not +own; any cast payload carrying owned rows rather than a `(mailbox, row-range, +cycle)` descriptor. The withdrawn implementation is banked in the session +scratchpad as the worked example rather than deleted, because the four-row table +above is only legible next to the code that got each row wrong. + +--- + +## 2026-09-07 — E-TOPOLOGY-MASKS-MAGNITUDE-COMPOSE-NEVER-COLLAPSE-1 — Mississippi Queen, TERNLOG chaining and BLASGraph pay for ONE operation, from three sides + +**Status:** OPERATOR RULING (2026-09-07, verbatim in substance), recorded on +`spog-alpha-channel-v1.md` §4 and here; the boundary rule is binding for +D-SPG-4/5/6 and for the F5 open question. +**Confidence:** High on the decomposition and the boundary (operator's word; +consistent with the measured 0.0019 amortization ratio of D-SPG-2 and the +corrected #620 fan-out result). [H] on the per-rung independent propagation — +argued, not yet measured. + +**The operation.** *Deciding what remains eligible without materializing the +rejected world.* Three mechanisms pay for it: **Mississippi Queen** = reveal +geometry / exploration budget (topology says where activity MAY go); +**TERNLOG masks** = Boolean eligibility / inhibition (where activity IS ALLOWED +to go); **BLASGraph** = numeric propagation over the survivors (HOW MUCH goes +there). The hexagon was never the point — degree-6 was falsified repeatedly; +what survived is the economics of revealing only what can matter next, which is +why fan-out loses in #620 (reconvergence forces inspection of redundant edges), +not because hex degree is special. TERNLOG's prize is **amortized +eligibility** — `resident ⊗ A ⊗ B ⊗ C`, the rejected volume never becoming a +second representation — and it wins only while the working masks stay resident +(gate (h): 0.0019 of a rebuild), collapsing toward bandwidth parity as depth +blows the cache. **Alpha is the sparse, readable record of which part of the +potential field actually fired** — the readout plane, not plumbing. + +**The boundary rule (binding).** The three COMPOSE and never collapse: the MQ +hexagon does not become a TERNLOG immediate; the immediate does not become a +neural weight; BLASGraph is never used for Boolean elimination because a matmul +can encode it. *Topology chooses neighborhood, masks choose admissibility, BLAS +chooses magnitude.* Reads back onto §4's "same bit" interjection: mask bit = +projection bit (true by the immediate's index construction) — NEVER mask bit = +weight. + +**The consequence for the rung × G cross (#1220, D-SPG-3).** The numeric leg +can run independently per rung, `R_r × G → mask → propagation`, r ∈ 0..=9, with +alpha as the common readout plane where the ten fields overlap — so +meta-awareness observes field INTERSECTIONS instead of "running the ten rungs". +This is the frame for F5's open question (scalar `AlphaFocus` vs SIMD +`spog_masks`): the cell is a readout surface, the propagation a separate rail; +neither owns the other. + +**Falsifier.** A design in which one of the three does another's job — a +ternlog immediate carrying magnitude, a BLAS kernel doing set elimination, a +topology hop encoded as a mask constant — is the collapse this entry forbids; +the reviewer's question on every D-SPG PR is "which of the three is this, and +does it do only that". Motto as given: *"Don't compute the world. Narrow what +can matter, then spend arithmetic only there."* + +--- + ## 2026-09-07 — E-AN-EMPTY-RANGE-AFTER-A-RESET-IS-NOT-EVIDENCE-1 — the check that certified the loss it was run to prevent **Status:** FINDING, measured. The orphaned commit was recovered; the board it @@ -41,6 +406,58 @@ be the same PR that adds the file — never a later one, and never a claim that prior PR added it. --- +## 2026-09-07 — E-THE-FUSED-AND3-HOP-WAS-NEVER-SHIPPED-LGJ-HOP-IS-TWO-ANDS-1 — a mapping entry cited a call site that does not exist + +**Status:** FINDING, measured (grep `ternlog|AND3` over `lance-graph-java/native/lgj-abi/src/*.rs` at lgj `dbac826`: **0 hits**; `lgj_hop` read in full, `exports.rs:1712-1860`). Corrects `E-NXG-8` (2026-09-05, below) and `.claude/knowledge/membrane-tiers.md` §"The polyfill is the worked instance" (⊘ in place, same commit). Spec that carries the correction forward: `.claude/plans/spog-alpha-channel-v1.md` §1a/§4/§8. +**Confidence:** High. The absence is a full-file read plus an exhaustive grep, not an inference. + +> **⊘ 2026-09-07 — FALSIFIED BY CURRENT LGJ HEAD `8720d1d`. The finding held only for the stale pin.** +> +> The measurement above is correct *for `dbac826`* and wrong as a statement about +> lgj. At `8720d1d` (2026-09-05, the branch tip this repo can reach), `lgj_hop` +> dispatches ONE call — +> `kernels::simd_mask_ternlog_assign::<{ kernels::ternlog::AND3 }>` (`exports.rs:1816`) +> — `kernels.rs:100` is `pub use ndarray::simd::ternlog;`, `kernels.rs:111` is the +> `simd_mask_ternlog_assign` wrapper, and **`simd_mask_and_assign` +> does not appear in `exports.rs` at all**. The two-AND → ternlog collapse shipped +> **2026-09-04** (lgj `LATEST_STATE.md`, measured 3.5–5× on the columnar-hop bench). +> +> So the correct historical statement is not *"fused `AND3` never shipped"* but +> **"fused `AND3` was absent at the stale pin `dbac826`, and shipped before this +> audit was written."** Three consequences: +> +> 1. **`E-NXG-8`'s `AND3` row is VINDICATED, not regraded.** *"`AND3` = conjunctive +> narrowing (`lgj_hop`, shipped)"* is true at HEAD. The ⊘ this entry put on its +> Confidence line is itself withdrawn (second dated note there). +> 2. **`membrane-tiers.md`'s original sentence is RESTORED.** *"`exports.rs` names +> `kernels::ternlog::AND3`"* was true; the "correction" replaced a true sentence +> with a false one — and did so four lines above that file's own Provenance +> paragraph, which names *"the two-AND→ternlog conjunction (T2 hand-composing a +> T1 op; fixed by naming the op at T1)"* as **one of the two fixes the tier +> doctrine was derived from**, dated 2026-09-04. The entry denied the doctrine's +> own founding receipt. +> 3. **"the FIRST production-shaped ternlog consumer" is wrong.** `lgj_hop` is, and +> has been since 2026-09-04. The SPOG cross would be the second. +> +> The regrade to OPPORTUNITY is withdrawn; the P3 amortization caveat survives on +> its own terms (it is about the SPOG cross's shape, not about lgj). +> +> **What survives, and it is the more useful half:** a board claim pinned to a +> foreign repo's sha decays silently, and the decay is invisible from inside this +> repo — no gate here reads lgj. Evidence is now repinned to `8720d1d`; the +> `dbac826` measurement stands as historical evidence of that commit only. This is +> the mechanism `ISS-LGJ-CROSS-REPO-CITATION-GOES-STALE-SILENTLY` (lgj's own +> ISSUES.md) names from the other side — the same defect, found independently in +> both directions within four days. + +**The claim.** `E-NXG-8` mapped the eight named ternlog immediates to cognitive homes and wrote *"`AND3` = conjunctive narrowing (`lgj_hop`, shipped)"*; `membrane-tiers.md` illustrated the T1/T2 stacking with *"`exports.rs` names `kernels::ternlog::AND3`, never `ndarray::simd` directly."* Both read as shipped code. + +**The tree.** `lgj_hop` composes `selected_f = src ∧ class_f ∧ struct_f` as TWO sequential `kernels::simd_mask_and_assign` calls (`exports.rs:1818` then `:1822`), after `simd_rowstore_u32_eq_mask` for each of `class_f` and `struct_f`. `kernels.rs` (1,409 lines, read in full) exports `simd_eq_u32_to_mask`, `simd_gt_i32_to_mask`, `simd_mask_{and,or,andnot}(_assign)`, `simd_masked_sum_i32`, `simd_popcount`, `simd_rowstore_u32_eq_mask`, `simd_rowstore_classid_mask`, `simd_rowstore_facet_match`, `masked_facet_sum` — no ternlog wrapper of any name. The named immediates ARE consumed by name in this repo, at exactly one place: `crates/lance-graph-planner/examples/probe_nxg_hist_1.rs:51-136` (`AND_ANDNOT2` as the bucket, `AND3` as its can-it-fire twin). That is a probe, not a hop. + +**Regrade.** The fused `AND3` hop is an **OPPORTUNITY** (and a measurable one — the P3 amortization reads 0.50 at K ≥ 8 only while the masks fit L2, temporal 09), not a shipped site. `E-NXG-8`'s other seven rows are untouched by this finding; only the `AND3` row's parenthetical is wrong. Consequence for the doctrine doc: a T1/T2 illustration must cite a line that exists — the corrected line in `membrane-tiers.md` now names the real stacking (`exports.rs` → `kernels::simd_mask_and_assign` → `ndarray::simd::mask_and_assign`), which is the same shape and true. + +**Why it matters beyond one sentence.** The SPOG alpha spec (`spog-alpha-channel-v1.md`) builds a rung × tenant cross on `mask_ternlog` and had inherited "the hop already does this" as a premise. It does not; the cross is the FIRST production-shaped ternlog consumer if it lands, and its gate (c) measures whether fusion pays on that shape rather than assuming it from a citation. + ## 2026-09-07 — E-A-PLAN-INVENTORY-FINDS-THE-BOARD-LAGS-THE-TREE-IN-BOTH-DIRECTIONS-1 — status cells decay at the rate of the tree, not of the file **Status:** FINDING, measured (five read-only Sonnet agents; every claim below re-verified by the orchestrator at a tree line, not a tag-file line). Full census: `.claude/board/PLAN-INVENTORY-2026-09-07.md`; evidence: `exec-runs/plan-inventory-2026-09-07-*.md`. @@ -1228,7 +1645,7 @@ families is a violation at the seal. ## 2026-09-05 — E-NXG-8 — the eight named immediates already have cognitive homes **Status:** FINDING (mapping of shipped code). -**Confidence:** High on the mapping. +**Confidence:** High on the mapping. **⊘ 2026-09-07:** the `AND3` row's parenthetical *"(`lgj_hop`, shipped)"* is FALSE — lgj-abi has no ternlog call site; see `E-THE-FUSED-AND3-HOP-WAS-NEVER-SHIPPED-LGJ-HOP-IS-TWO-ANDS-1` (2026-09-07). The other seven rows stand. **⊘⊘ WITHDRAWN, same day:** that ⊘ was measured at the stale pin `dbac826`. At lgj HEAD `8720d1d` the row is TRUE — `lgj_hop` is one `simd_mask_ternlog_assign::` (`exports.rs:1816`), shipped 2026-09-04. **All eight rows stand.** `AND3` = conjunctive narrowing (`lgj_hop`, shipped); `AND_ANDNOT2` = bucket / annulus / known-false (`domain ∧ ¬result`); `MAJ3` = quorum @@ -2309,7 +2726,7 @@ records Pillar 11 activated since PR #348; `sigker/examples/ cubature_vs_randomized.rs` already exercises production-carrier widths (PATH_DIM=4, PATH_LEN=64, N_PATHS=256, "OSINT-typical") — it had simply never been *run*. Both doc sites that said otherwise -(`crates/sigker/src/lib.rs:50`, the ndarray-vertical-simd-alien-magic.md +(`crates/sigker/src/lib.rs` module doc, the `"OSINT-typical"` line; the ndarray-vertical-simd-alien-magic.md W1.5 section) were stale relative to jc's own status and are corrected in this same pass. @@ -7757,7 +8174,7 @@ verified against `MedCare-rs/data/config/bakes.tsv`). **Confidence:** High — every number is a count over 512-byte rows, tiers read at bytes 4..10. The board headline (`INTEGRATION_PLANS.md` ARC-B entry) and -`EPIPHANIES.md:899` both state HHTL is **"zero on every baked row in both +`EPIPHANIES.md` §`E-THE-OU-COLUMN-EXISTS-AND-NOTHING-WRITES-IT-1` (was cited as `:899`; re-anchored to the heading, line numbers shift under prepend) both state HHTL is **"zero on every baked row in both production bakes"**, citing `ogar-obo` (68,797 rows) and MedCare's `join-map.md` (68,797 rows). Both citations are correct. **Both describe the same artifact set of two.** A third pinned artifact exists: @@ -7806,7 +8223,7 @@ a field must also name which READING of it was counted, when the accessor picks between two registers. Full table: plan §8a ⊘ correction. Cross-ref: `.claude/plans/dismech-causality-v3-v1.md` §8a; ARC-B -`docs/architecture/ARC-B-OWNERSHIP-AND-ADDRESSING-REASSESSMENT.md:23` (regraded +`docs/architecture/ARC-B-OWNERSHIP-AND-ADDRESSING-REASSESSMENT.md` §0 "THE ONE-PARAGRAPH FINDING" (regraded in place: its conclusion holds for `obo-core`/`spine`, needs the `all-lanes` qualifier); `EPIPHANIES.md:899`. diff --git a/.claude/board/IDEAS.md b/.claude/board/IDEAS.md index 185299cd3..4921d6670 100644 --- a/.claude/board/IDEAS.md +++ b/.claude/board/IDEAS.md @@ -93,7 +93,7 @@ Agents filter by `@`-mention or domain to see what's theirs. Operator (2026-09-07): *"every domain is just another table keyed by CUI STDID (snomed) loinc etc — its a chain effect with masking, no datafusion joins ever."* Mechanically: bake one artifact per G (`graph_of(addr)`, `spog_tenants.rs:38`) instead of stamping the category into `value[96]` and re-reading it per row (medcare `obo_store.rs:16-18,77,681`); read the quad's 4×24 slots as four pre-resolved foreign keys (slot 0 = own key; CUI present in 8/8 domains = the hub; FMA = the anatomy↔imaging bridge; ICD↔MONDO inside disease). A crosswalk is then `eq_u32_to_mask` (`ndarray/src/simd_int_ops.rs:562`) on the FK column of table n, `mask_ternlog` (`:983`) with the incoming survivor mask, and the survivors' key set becomes the needle set for table n+1 — never a mask-AND across two tables (nexgen room 18). **Probe, pre-registered:** on medcare's baked OBO tables, run CUI→SNOMED→LOINC as a mask chain and as the existing DataFusion path; assert (a) survivor SETS identical, (b) 0 bytes/step under the counting allocator (D-GTM-0k's instrument), (c) per-hop ns flat in chain length while every mask fits L2 (the D-GTM-0n bound rides with the claim), (d) a deliberately cross-family AND is REJECTED at the seal (can-it-fire), (e) a hop with < 0.1 % survivors is routed to the sparse arm (0n's other bound). Falsifier: (a) fails ⇒ the FK reading of the slots is wrong, not the mask algebra. -**Status:** Open — needs the medcare-side bake shape first (step 1 of `PLAN-INVENTORY-2026-09-07.md` §9). +**Status:** ~~Open~~ **In progress 2026-09-07** — pre-registered as D-SPG-4 in `.claude/plans/spog-alpha-channel-v1.md` §6 (gates a–e kept, f–h added). Correction: the "existing DataFusion path" named above as the reference does not exist for this chain in MedCare-rs; the reference is the scalar quad/sidecar path (spec §8.2). The bake shape is decided: domain = mask over the combined image (spec F2), not a per-G file. **⊘ Two decisions in the title and body above are SUPERSEDED by the spec (CodeRabbit on #1221 asked for this to be explicit):** (1) *"one artifact per G"* → NO per-G artifact; the tenant is a MASK over the combined image (`eq_u32_strided_to_mask` per distinct classid, OR-folded per `graph_of`) — the shared ordinal image is never split; (2) *"as the existing DataFusion path"* → there is no DataFusion crosswalk in MedCare-rs; gate (a)'s reference is the scalar quad/sidecar path. The body stays as the pre-registration record (this ledger is append-only); the decisions live in `spog-alpha-channel-v1.md` F2 and §8.2. ## 2026-09-07 — DataFusion containment: pin `with_row_id`/`with_row_addr` OFF with a test that fails when either flips; no new surface diff --git a/.claude/board/INTEGRATION_PLANS.md b/.claude/board/INTEGRATION_PLANS.md index d66c97241..da5bfce3d 100644 --- a/.claude/board/INTEGRATION_PLANS.md +++ b/.claude/board/INTEGRATION_PLANS.md @@ -30,6 +30,21 @@ until that ruling lands; the strict order is the harness's anti-mush protocol. Draft v2 also owes F9, the #1202 dating rule. Phase numbering is the 5+3 harness's single 0-5 ladder (`.claude/agents/5plus3-council.md`), stated in the plan's own header. Superseded by v2/v3 when they land. +## 2026-09-07 — `spog-alpha-channel-v1` (SPEC, Phase 0 — the MedCare-rs SPOG alpha channel) + +`.claude/plans/spog-alpha-channel-v1.md`. Operator mandate: *"probe autoattended +autonomous decision making until you get MedCare-rs SPOG alpha channel to +work"*, with two constraints honoured as frozen decisions — no Lance row ids +(sealed batch per cycle, `cycle = version + 1`) and the S3 bakes are NOT +per-domain files (`all-lanes.soa` = 12 lanes / 762,041 rows; `obo-core.soa` = +5 classids), so **domain = a mask over the combined image** (`eq_u32_strided_ +to_mask` per G, `OR` per domain, TUI fence for the horseshoe lanes). G is the +contract's `graph_of` (canon-high u16), MedCare's `Domain` is a grouping of Gs +by mask `OR`; the rung byte carries the attention rung only. D-SPG-0..8; order +0 → 1 (one contract method: `AlphaMask::words`/`from_words`) → 2 → 4 (PROBE- +CROSSWALK-MASK-1, gates a–h incl. the ternlog-immediate-as-K0..K7 "angle" gate) +→ 3 → 5 → 6 → 7/8. Corrects two standing claims while landing: `lgj_hop` never +used `AND3` (E-NXG-8 row, `.claude/knowledge/membrane-tiers.md` §"The polyfill is the worked instance"). ## 2026-09-06 — temporal / delta / alpha staged plan → `.claude/temporal/09-plan.md` diff --git a/.claude/board/LATEST_STATE.md b/.claude/board/LATEST_STATE.md index a2da64cb5..ed0ea4309 100644 --- a/.claude/board/LATEST_STATE.md +++ b/.claude/board/LATEST_STATE.md @@ -1,3 +1,24 @@ +## 2026-09-07 — `SpogTenants::merged_rows` + D-SPG-5 shipped (F9 migration complete on the consumer side) + +- **Contract inventory — delta:** `contract::spog_tenants::SpogTenants::merged_rows(&self) -> Vec` — `merge()` in row form (global `seq`, stamp at value slot 0, edges reserved-and-zeroed), the exact sibling of `AlphaTunnel::merged_rows`, so a tenant aufstellung, a tunnel and one overlay are ONE table to any writer. This is the row builder D-SPG-6's sealed batch is built from. Test `merged_rows_is_merge_in_row_form` (two shadows, the two per-shadow zeros must NOT leak). +- **Consumer state (MedCare-rs, private; shas only):** D-SPG-5 shipped in two halves — #621 (`attention::WatchedRows` → `SpogTenants::over_census`, `domain_rung` deleted) and `e722dd1` (PR #622: `claim_domain_and_patient` returns `SpogTenants`, patient = tenant `graph_of(patient_address)`; `nodesoa::alpha::{tenants_to_batch, write_alpha_tenants}` over `merged_rows` — **⊘ same day: `write_alpha_tenants` withdrawn (consumer-side Lance writer; the sole writer is `LanceCycleWriter`, the producer casts a descriptor); `tenants_to_batch` stays as an arrow encode**; `FrontierDispatch::tenants()`). D-SPG-2/3 pushed as `29d4792` / `e5febf9`. STATUS_BOARD rows regraded. +- **Operator ruling banked:** `E-TOPOLOGY-MASKS-MAGNITUDE-COMPOSE-NEVER-COLLAPSE-1` (EPIPHANIES) + `spog-alpha-channel-v1.md` §4 addendum — Mississippi Queen / TERNLOG / BLASGraph = reveal geometry / Boolean eligibility / numeric magnitude; compose, never collapse; alpha = the readout plane; per-rung `R_r × G → mask → propagation` is the frame for F5's open question. + +## 2026-09-07 — D-SPG-1 shipped: `AlphaMask::{words, from_words}` — the one contract seam the SPOG cross needs + +- **Contract inventory — delta:** `contract::alpha::AlphaMask` gains `words(&self) -> &[u64]` (a borrow, not a materializer) and `from_words(Box<[u64]>, u32) -> Self` (release-mode `assert_eq!` on `words.len() == len.div_ceil(64)`, tail bits past `len` CLEARED — the `not()` law applied at the boundary). No new type, module, field or lane; `materialize_ordinals` stays the one named materializer. 3 tests (can-fire `should_panic`, round-trip at `len % 64 != 0`, phantom-tail clear); mutation-fired (clearing disabled → the count assertion fails). +- **Why now:** MedCare-rs consumes `lance-graph-contract` from git `main` (`Cargo.toml:145`; the `vendor/lance-graph` symlink is gone), so D-SPG-2/3 on the private side cannot compile until this is on `main`. Plan: `.claude/plans/spog-alpha-channel-v1.md` §5. + +## 2026-09-07 — `spog-alpha-channel-v1` spec landed (Phase 0, no code); one correction to shipped-code claims + +- **New plan:** `.claude/plans/spog-alpha-channel-v1.md` (D-SPG-0..8 on STATUS_BOARD). Frozen: no row ids / sealed batch per cycle; domain = mask over the combined `all-lanes.soa`; G = `graph_of` (contract), Domain = `OR` of Gs; rung byte = attention rung only; the ternlog cross lives one crate out of the contract (MedCare-side, `medcare-cohorts` has `ndarray`; `lance-graph-planner` stays out of the customer binary). +- **Contract inventory — net delta:** none yet. D-SPG-1 (queued, next) adds exactly two methods on the existing `contract::alpha::AlphaMask` (`words`, `from_words`) — no type, no module, no lane. +- **Correction:** `E-THE-FUSED-AND3-HOP-WAS-NEVER-SHIPPED-LGJ-HOP-IS-TWO-ANDS-1` — lgj-abi has zero `ternlog`/`AND3` symbols; `lgj_hop` = two `simd_mask_and_assign` (`exports.rs:1818,1822`). E-NXG-8's `AND3` row and `.claude/knowledge/membrane-tiers.md` §"The polyfill is the worked instance" regraded; the only in-repo consumer of the named immediates is `planner/examples/probe_nxg_hist_1.rs`. +- **Consumer census recorded (private repo, numbers only):** `SpogTenants` / `TenantClaim` / `claim_admitted` / `AlphaMask` — 0 consumers in MedCare-rs; `dispatch_thought` — 1 (`frontier_dispatch.rs:81`, `cycle` = constant 1); `overlay_to_batch` / `write_alpha_overlay` — 0 callers outside their tests. +- **Operator instruction folded in (F9 / D-SPG-5):** the hand-rolled MedCare alpha (`attention::WatchedRows` + `domain_rung`, `backreference::combined_base`, the bare-overlay `nodesoa::alpha` writer) migrates ONTO the #1198 contract alpha (`AlphaTunnel` lanes + `SpogTenants` + `merge()`), not beside it. +- **IDEAS:** PROBE-CROSSWALK-MASK-1 card → In progress (the "existing DataFusion path" it named as reference does not exist for that chain; reference regraded to the scalar quad/sidecar path — spec §8.2). +- **Integration update after #1220 + MedCare-rs #621 merged (both 2026-09-07, this branch rebased onto `main` `a4f661a`):** D-SPG-8 → **Shipped** (row identity OFF at the production `LanceTableProvider::new` call site, with a red-if-flipped test that observes THAT site and a grep fence that stays sharp); D-SPG-5 → **Partial** (leg (i), the `domain_rung` squat, retired; legs (ii)–(iv) measured still open — 16 + 15 bare `AlphaOverlay` references in `backreference` and `medcare-nodesoa::alpha`, plus two sites the spec never named). The contract also gained a cross of its own in #1220 (`alpha_focus::AlphaFocus`, scalar): F5 rules the SIMD/ternlog cross and is unchanged, but `cell` / `unlooked` now exist in two repos — recorded as an open operator question, not answered. Plan §8 item 7. + ## 2026-09-07 — #1217 MERGED (b518dbf1): the orphaned SPEC v1, recovered — and the check that certified its loss | PR | merge | content | @@ -27,6 +48,10 @@ and NOT DataFusion — the same class numbers are stale after the `mint_for` V2/V3 drift; re-anchor to symbols before Phase 3. +## 2026-09-07 — PR #1218 merged (`7bb393ef`): plan inventory + V3 harvest mirrors are on `main` + +- The 2026-09-07 "plan inventory landed" delta below is now merged history. Post-review deltas since that delta was written: `TD-NDARRAY-SIMD-GATHER` is **PARTIAL** (not SHIPPED); W1b reads 0 of 5 TD entries closed / 1 of 7 files migrated; ENTROPY M1–M27 reconciles to 9 + 7 + 10 + 1; COMPONENT-MAP `StepMask` row and the Sonnet guardrails `StepMask` / `0x1000` rows regraded; the nexgen `(classid, version)` key is marked PROPOSED (shipped `NestedBands` is version-only). Arc entry: `PR_ARC_INVENTORY.md` under PR #1218. + ## 2026-09-07 — plan inventory landed: `PLAN-INVENTORY-2026-09-07.md` + the V3 folder now sees the harvest - **New board document:** `.claude/board/PLAN-INVENTORY-2026-09-07.md` — 211 plans (149 OPEN / 13 CLOSED / 9 SUPERSEDED / 40 AMBIGUOUS), V3 waves W0–W6, ENTROPY M1–M27, W1a/W1b/W1.5 + jc pillars vs code, top-level `.claude/*.md` (71; 9 orphans), board dashboards. Mints no D-ids (a snapshot, not a plan). Evidence: five verbatim tag-files under `exec-runs/plan-inventory-2026-09-07-*.md`. diff --git a/.claude/board/PR_ARC_INVENTORY.md b/.claude/board/PR_ARC_INVENTORY.md index 4daa07393..d2496771d 100644 --- a/.claude/board/PR_ARC_INVENTORY.md +++ b/.claude/board/PR_ARC_INVENTORY.md @@ -58,6 +58,75 @@ three append-only readings (each quoted from `CLAUDE.md` and accepted by the reviewer that had argued the opposite). The audit itself still reaches NO verdict — nothing was implemented. +## 2026-09-07 — lance-graph PR #1218 (merged `7bb393ef`, branch `claude/medcare-rs-continue-ufsazd`) — the plan inventory: the board lagged the tree in both directions, and the review found the inventory lagging too + +- **Added:** `.claude/board/PLAN-INVENTORY-2026-09-07.md` (211 plans: 149 OPEN / + 13 CLOSED / 9 SUPERSEDED / 40 AMBIGUOUS; V3 waves W0–W6; ENTROPY M1–M27 = + 9 shipped + 7 in-flight + 10 queued + 1 ruling-needed; W1a/W1b/W1.5 + the jc + registry against ndarray `b9afcb9b`; 71 top-level docs, 9 orphans), the five + verbatim Sonnet tag-files under `exec-runs/plan-inventory-2026-09-07-*.md`, + four EPIPHANIES entries + (`E-A-PLAN-INVENTORY-FINDS-THE-BOARD-LAGS-THE-TREE-IN-BOTH-DIRECTIONS-1`, + `E-EVERY-DOMAIN-IS-A-TABLE-AND-A-CROSSWALK-IS-A-CHAIN-OF-MASKS-1` — operator-ruled, + `E-SEVEN-HARVEST-SOURCES-ONE-OBJECT-THE-VERSION-KEYED-MASK-SET-1`, + `E-RUNG-BAND-AND-PLASTICITY-ARE-THREE-AXES-NEVER-ONE-LEVEL-FIELD-1`), five IDEAS + entries (PROBE-CROSSWALK-MASK-1; DataFusion containment pin; `ogar-r2il` first + consumer via `RANK` + `TERNLOG 0x86`; hop order by popcount; known unknown = + survivor mask with popcount > 1), and nine `.claude/v3/` mirrors (README + doc-map row + two-collision note, primer §5/§6, INTEGRATION-PLAN W6 ⊘, + routing §5 ⊘, compiled-templates, COMPONENT-MAP `NestedBands` + `StepMask` + rows, ENTROPY M2/M24/M27, FUTURE-DESIGN block, witness-nibble-lane P5, + guardrails `StepMask` + `0x1000` rows). Doc + board only; no code; no D-ids + minted — the inventory is a snapshot outside `supersession_index.py`'s scan + by design. +- **Status cells regraded (append-only, strike-through + date, tree line + cited):** TECH_DEBT W1a #1/#2/#4/#5 → SHIPPED (primitive side), **#3 GATHER → + PARTIAL** (API on all backends, x86 body a scalar polyfill by its own doc), + W1.5 #7 → SHIPPED + consumed, TD-SIMD-SWEEP-W2 → half done; STATUS_BOARD + D-LNC-5a / D-MW-P2 → Shipped (#1198); ENTROPY M2 → SHIPPED, M24 regrade. +- **Locked:** (1) `.claude/board/exec-runs/` tag-files are verbatim agent + evidence — a transcription defect gets an appended **"Orchestrator errata"** + block, never a rewrite (CodeRabbit recorded this as a repo learning during + review). (2) The nexgen plan's `(classid, version)` key is its PROPOSED + room-26 shape; the shipped D-NXG-1 `NestedBands` is version-keyed over one + column with no classid (Codex P2). (3) jc's EWA pair carries two numberings + INSIDE jc — module docs Pillar 6/7, `lib.rs` header 9/9b — so the board's + "Pillar-6/7" rows do mean jc (Codex P2). (4) W1b is counted two ways and + both must be stated: 0 of 5 TD entries closed, 1 of 7 files migrated. + (5) `0x1000` is a permanent monitor and P4 an operator checkpoint everywhere + the V3 folder speaks of it — the README summary and the Sonnet guardrails + row had still said "temporary" nine weeks after the rescission. +- **Deferred (recorded, not done):** adjudicating W2a (INTEGRATION-PLAN + Addendum-12a vs -15 — needs `canonical_node.rs` + owner wiring read); the M18 + ruling (sigma chain vs six kanban phases); the 17 STATUS_BOARD rows outside + the nine leading-token classes (stated as unclassified, not re-bucketed); the + 65 unlabeled pre-Kanban TECH_DEBT rows; `self-reasoning-substrate-v1.md`'s + "doc-only" header over four Shipped D-SRS rows (owner's plan); `CLAUDE.md`'s + "61 top-level docs" and the nonexistent `SESSION_CAPSTONE.md` (operator-owned + file). No `tenants.md` row for D-NXG — no consumer sits in a `ValueTenant` + yet. +- **Review record:** Codex 2 × P2 (both right, both fixed, 162963a5); + CodeRabbit 12 (round 1, f5d530bb) + 2 (round 2, 14047c35; one declined — + an in-place edit to an entry this PR itself added is not an altered + historical entry — and withdrawn by the reviewer). Its third review never + posted (its own hourly review cap). All 16 threads resolved before merge. +- **Docs / gates:** `append_only_gate.py origin/main` OK on every commit; + `citation_decay.py --since origin/main` 0 new on every commit (three decays + introduced and fixed before the first commit: two backtick-pairing anchor + flips on my own citations — keep the true symbol ADJACENT to its + `file:line` — and one real prepend-shifted line range, replaced by a heading + anchor); `SUPERSESSION-INDEX.md` regenerated LAST after each board write, + byte-identical every time. Two operational lessons banked here rather than + as epiphanies: GitHub's GraphQL budget for the account ran out under a burst + of 12 replies + 12 resolves (the REST comment listing and REST replies kept + working, so replies posted and resolves waited an hour); and a burst of + parallel bot-review echoes arrives as ~60 notifications that must be drained + before acting. +- **Confidence:** HIGH on every status cell touched (each cites a tree line + re-read by the orchestrator, not the agent report); MEDIUM on the four + agent-side counts the review corrected (now re-measured per file and + recorded in the errata blocks); the synergy entries are [S]/[H] syntheses + with their probes named, not findings. ## 2026-09-07 — lance-graph PR #1211 (merged `c3bb095b`, branch `claude/ndarray-simd-tract-o3jfrn`) — the V1 guard was tested; the V3 guard that replaced it was not diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index 62d114f95..03bdd150b 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -1,3 +1,19 @@ +## spog-alpha-channel-v1 (D-ids minted 2026-09-07 with the spec) + +`.claude/plans/spog-alpha-channel-v1.md`. The SPOG alpha channel in MedCare-rs: domain = a mask over the combined `all-lanes.soa` image (never a per-domain file), cycle = one sealed `FixedSizeBinary(512)` append per Lance version (no row ids, no delete), rung byte = the attention rung only (`domain_rung` retired as a writer), the rung × tenant cross via `mask_ternlog` one crate out of the zero-dep contract. Operator mandate 2026-09-07 ("probe autoattended … until you get MedCare-rs SPOG alpha channel to work"). Order: 0 → 1 → 2 → 4 → 3 → 5 → 6 → 7/8. + +| D-id | deliverable | status | falsifier | +|---|---|---|---| +| D-SPG-0 | The spec; the lgj `AND3` correction (E-NXG-8 regraded, `.claude/knowledge/membrane-tiers.md` §"The polyfill is the worked instance" ⊘); IDEAS PROBE-CROSSWALK-MASK-1 → In progress | **Shipped 2026-09-07** (this commit) | citation-decay + append-only gates green | +| D-SPG-1 | `AlphaMask::words(&self) -> &[u64]` + `from_words(Box<[u64]>, u32)` with the release-mode length law | ~~Queued — next~~ **Shipped 2026-09-07** (Sonnet worker from spec, orchestrator-gated: fmt 0, clippy `-D warnings` 0, contract 1326/1326; mutation-fired — tail-clear disabled → `from_words_clears_phantom_tail_bits` fails on `count == 200`) | can-fire: wrong word count refused; can-stay-silent: round-trip on `len % 64 != 0`; the 10 existing alpha tests untouched | +| D-SPG-2 | Tenant masks over the combined image (`eq_u32_strided_to_mask` per declared G over `soa_map()`), domain = `OR`, horseshoe = TUI fence; `SpogTenants::over` declared from non-empty Gs | ~~Queued~~ **Shipped 2026-09-07** (MedCare-rs `d64854b` — was `c6a9095` before the rebase onto #620 —, `medcare-cohorts::spog_masks`, feature `spog`; orchestrator-built, gated: fmt/clippy `--no-deps -D warnings` clean, 6 tests, 2 disable runs fire). **Measured on the real image:** 762,041 rows → 16 tenants that PARTITION it (sum AND union both 762,041); every tenant count == its `node_rows_for_classid` windows; masks are 95,256 B (L2-resident); gate (h) amortization ratio **0.0019** (10 rungs reading cached masks vs rebuilding). `SpogTenants::over` from the Gs was demonstrated the same day by the sibling session's MedCare-rs #620 (`examples/spog_alpha_cardiac.rs`: 16 tenants, 512 claims routed, 0 misrouted, one sealed `merge()` batch; `spog_alpha_crosswalk.rs`: 0.00 % cross-tenant `is_a` edges, so the cross-tenant hop needs a CURIE→address resolver first) — as EXAMPLES, no `src/` change; D-SPG-5's remaining scope is the F9 migration of the `src/` drivers **⊘ 2026-09-07 (rebase onto `main` after #1220):** the cited MedCare-rs sha is on NO pushed ref. Checked against every `origin/*` ref of AdaWorldAPI/MedCare-rs at main `9f9b7be` (i.e. after #620 AND #621 merged): `git cat-file -t` reports MISSING for `d64854b`, `c6a9095`, and `spog_masks` appears in no ref. The measurements below stand as reported by the session that ran them; the STATUS is regraded **Shipped-unpushed** — it is not verifiable from any public history until that branch is pushed. **⊘ 2026-09-07, later the same day: PUSHED.** Rebased onto MedCare-rs `main` `9f9b7be` (after #621); the per-crate lance pin fix was dropped in favour of #621's workspace entry. D-SPG-2 is now MedCare-rs `29d4792` on `origin/claude/medcare-rs-continue-ufsazd` (`spog_masks` present in that ref); gates re-run after the rebase: 11/11, clippy `--no-deps -D warnings` 0, fmt 0. Status: **Shipped** (pushed, PR open, merge pending #1221 on lance-graph `main`). **⊘ 2026-09-07, operator — `horseshoe_mask` is a CATEGORY ERROR and `domain_mask` is not the domain.** `domain_mask(D)` skips the multi-facet lane (all-single-facet filter), so it returns the domain MINUS its multi-facet part (substance: 131,582 returned, 122,903 unreported); no caller unions the two; and a test comment codified the split as an invariant. Fix: a `LazyLock` partition of that lane by its value-side witness into per-domain masks, `domain(D) = static(D) ∪ dynamic(D)`, `horseshoe_mask` dissolved. The MEASUREMENTS stand (the partition, the counts); the domain VIEW as shipped is a half-answer. Pending in the consumer. **⊘ 2026-09-07, operator go — DONE.** `domain(D) = static(D) ∪ dynamic(D)` shipped; `horseshoe_mask` retired; the value half is one `LazyLock` pass resolving all eight domains, cached over the immutable bake, selected by pointer identity. Address half → union: lab 103,291 → 206,582 · substance 131,582 → 254,485 · anatomy 119,684 → 119,732 · procedure 38,956 → 40,340; 38,953 lane rows REFUSED and now counted. Calibration is a test (static lab == dynamic lab == 103,291 on disjoint sets, a cardinality target — not a bijection proof); every other domain asserted value ≤ address. Gates: clippy `--no-deps -D warnings` 0, 625 crate tests pass, 3 disable runs fire. | `count == Σ_{c : graph_of(c)==G} node_rows_for_classid(c).len()` per G (G is `graph_of`, not a classid — wording corrected 2026-09-07); `Σ count == n_rows` (no row in two tenants) | +| D-SPG-3 | Rung × tenant cross via `mask_ternlog` on `words()`; `unlooked[D]` read (temporal 09 Stage 2) | ~~Queued~~ **Shipped 2026-09-07** (MedCare-rs `6bf7764`, `spog_masks::{rung_tenant_cell, unlooked}`; Sonnet worker from spec, orchestrator-gated: fmt/clippy clean, 11/11 incl. the Codex #1221 sibling-classid falsifier; mutation-fired: AND2→AND_ANDNOT2 in the cell fails the count test). Measured on the real image: 500 MONDO + 300 CUI claims at rung 3 → cell counts equal the materialized scanpath filter in all 16 tenants; `unlooked(disease) = disease − 500`, disjoint from `any_rung`, tiles the domain with the cell **⊘ 2026-09-07 (rebase onto `main` after #1220):** the cited MedCare-rs sha is on NO pushed ref. Checked against every `origin/*` ref of AdaWorldAPI/MedCare-rs at main `9f9b7be` (i.e. after #620 AND #621 merged): `git cat-file -t` reports MISSING for `6bf7764`, and `spog_masks` appears in no ref. The measurements below stand as reported by the session that ran them; the STATUS is regraded **Shipped-unpushed** — it is not verifiable from any public history until that branch is pushed. **⊘ 2026-09-07, later the same day: PUSHED** as MedCare-rs `e5febf9` on `origin/claude/medcare-rs-continue-ufsazd` (rebased onto `9f9b7be`; 11/11 after the rebase). Status: **Shipped** (pushed, PR open, merge pending #1221). | cell count == materialized scanpath filter; `AND_ANDNOT2 ≠ AND3` wherever `any_rung` non-empty | +| D-SPG-4 | PROBE-CROSSWALK-MASK-1, gates (a)–(h), real image; W0 pins FK columns before timing | Queued (MedCare-rs probe; record here) | plan §6 **+ calibration target (2026-09-07):** static lab == dynamic lab == 103,291 on disjoint rows — a CARDINALITY target (necessary, not sufficient; equal counts prove no bijection): the crosswalk sweep from the lab tenant must reproduce it exactly AND its survivor set must equal gate (a)'s scalar reference; the correct K-immediate is the one that passes both. See `E-A-DYNAMIC-DOMAIN-MASK-IS-A-SECOND-WITNESS-AND-ITS-ALIGNMENT-IS-CALIBRATION-1`. | +| D-SPG-5 | **Migrate the hand-rolled MedCare alpha onto the #1198 contract alpha** (operator 2026-09-07, spec F9): `attention::WatchedRows`' `RefCell` + `domain_rung` writer, `backreference::combined_base`, and the bare-overlay `nodesoa::alpha` writer all become consumers of `AlphaTunnel` lanes + `SpogTenants` G routing + `merge()`; frontier dispatch routes through tenants over `all-lanes.soa`; `reflection` = tenant `attended_mask` OR; `domain_rung` retired as rung writer | ~~Queued~~ **Partial 2026-09-07** — MedCare-rs #621 merged (`9f9b7be`). **Leg (i) SHIPPED:** `attention::WatchedRows` holds a `SpogTenants` built by `over_census`, `land()` claims at the CALLER's processing rung, `reflection(tenants, domain)` concatenates the shadows whose block resolves to that domain through the one `Domain::of_classid` mapping, and `domain_rung`/`rung_for` are gone — `origin/main` carries those names only inside a historical doc comment. The equivalence gate was met as stated: 48/48, with BOTH reflection tests and every order-sensitive `obo::` test unchanged. **Legs (ii)–(iv) NOT done**, measured on `origin/main` rather than assumed: `backreference::combined_base` still builds its own base (16 bare `AlphaOverlay` references); `medcare-nodesoa::alpha::{overlay_to_batch, write_alpha_overlay}` still take a bare overlay (15 references — and still ZERO callers outside their own module, so that leg migrates a writer nothing calls yet); `frontier_dispatch` still calls `dispatch_thought(base, …)` with no tenants. Two bare-overlay sites this row never named also remain: `medcare-cohorts::graph_feed` and `medcare-soa::patient` (1 reference each). **⊘ later the same day (this session): legs (ii)–(iv) SHIPPED** — MedCare-rs `e722dd1` (PR #622, draft, blocked on this PR): (ii) `claim_domain_and_patient` borrows the caller's `AlphaAllocation` and returns `SpogTenants::over_census` over the combined base, the patient = tenant `graph_of(patient_address)` (falsifier: `NoTenant` when its G is undeclared; `PatientSession` unchanged — it only hands out the allocation); (iii) `nodesoa::alpha::{tenants_to_batch, write_alpha_tenants}` over the new contract `SpogTenants::merged_rows` (this PR) **⊘ later the same day: `write_alpha_tenants` WITHDRAWN (pending gate) — a consumer-side Lance writer violates the sole-writer topology (`LanceCycleWriter`, #911 → #912). The persistence path is a descriptor `cast`, never materialized rows to a writer; `tenants_to_batch` stays as an arrow encode only. See D-SPG-6 row + `E-A-CONSUMER-THAT-OPENS-A-DATASET-HAS-ALREADY-LOST-1`** — the bare-overlay pair stays as the single-overlay form; (iv) `FrontierDispatch::tenants()`, the per-G reading of the agnostic dispatcher's scanpath (the dispatcher itself stays agnostic — `dispatch_thought` is upstream). Gates: first-thought 49/49, nodesoa 15+8, clippy 0. **Still open from the count above:** the two sites `graph_feed` / `medcare-soa::patient` (1 reference each) — **read, same hour: both are DOC COMMENTS, not code** (`graph_feed.rs:4313` describes what the debugger reads; `medcare-soa/src/patient.rs:179` is a doc link, and a stale one — it names `medcare_nodesoa::alpha::AlphaOverlay`, a path that moved upstream in #1112). Zero code references ⇒ the F9 residue is **0**; D-SPG-5 reads **Shipped**. What stays open is F5's scalar-vs-SIMD cell question (operator) and the stale doc link (trivial, MedCare-side). | tenant reflection == `domain_rung` reflection as sets ×8 domains; stamps carry ladder rungs 1..=9 | +| D-SPG-6 | ~~Sealed batch per cycle: `merge()` → `node_rows_to_batch` → one append; `cycle = version + 1`~~ **⊘ RE-SCOPED 2026-09-07 (operator: "879 909..912 1049 1198" — the batch writer is SoA-owned).** The write was never MedCare's. `LanceCycleWriter` (`graph::cycle_sink`, #911 → #912 Phase A) is the **SOLE application writer** (non-`Clone`, `commit_cycle(&mut self, …)`, one owned long-lived `Dataset` handle); SoA owners are fire-and-forget PRODUCERS that `cast` and are never Lance writers; `sealed_version = base_version + 1` is a **verified identity inside that sink**, not caller arithmetic; and **no semantic change → no write → no version** (#911's empty-cycle versioning was REMOVED). Correct scope: MedCare's alpha cycle CASTS a descriptor `(mailbox, dirty row-range, cycle)` — never owned rows — and reads back via `temporal::QueryReference::at` + deinterlace. | **Blocked (lance-graph), not queued (MedCare-rs)** — the gap is upstream and already on the ledger: `LanceShardSink` does not exist (#879 honesty ledger: "durability FAKE"), and the read side is unwired (#1198: `deinterlace` has no production caller, `cast()` has zero production call sites). A withdrawn consumer-side implementation is banked in the session scratchpad as the worked example of the violation. | ~~two cycles = two versions; a read at v never sees v+1; `enable_stable_row_ids` grep-fenced~~ — re-registered when the cast path is wired: an empty cycle performs ZERO Lance operations (`CommitOutcome::NoChange`), a re-submitted identical batch reconciles (`Reconciled`), and a same-cycle different-hash batch fails closed (`HashConflict`) | +| D-SPG-7 | ogar-r2il consumer (`RANK` + `TERNLOG 0x86`) via `lance-graph-ogar` | Queued — gates on D-SPG-4 | lifted program survivor mask == hand chain bit-for-bit | +| D-SPG-8 | DataFusion containment (`with_row_id`/`with_row_addr` OFF + red-if-flipped test) | ~~Queued~~ **Shipped 2026-09-07** — MedCare-rs #621 merged (`9f9b7be`). Production registers `LanceTableProvider::new(ds, /* with_row_id */ false, /* with_row_addr */ false)` (`medcare-server/src/state.rs:945`), and the red-if-flipped test OBSERVES that call site instead of building its own provider: `row_identity_columns_are_absent_from_the_registered_provider` (`:1264`) reads the schema of the table `AppState::build_session_context` actually registered, and asserts a hand-built identity-on provider over the SAME dataset carries exactly two more columns — two-sided, so the silent half is a measurement and not a statement about an empty schema. Its own doc records that an earlier draft hardcoded the flags in the test and stayed GREEN when production was flipped; the disable run is what caught it. The pre-existing grep fence `row_identity_containment::no_lance_row_identity_consumer_exists` (`:1609`) stays sharp because the test deliberately never spells either column name in code. **Gate met:** the scan schema carries neither identity column, and a flip is detectable. | scan schema carries neither `_rowid` nor `_rowaddr` | + ## lance-convergence-staged-migration-v1 (D-ids minted 2026-09-05 with the plan) `.claude/plans/lance-convergence-staged-migration-v1.md`. The staged lance diff --git a/.claude/plans/spog-alpha-channel-v1.md b/.claude/plans/spog-alpha-channel-v1.md new file mode 100644 index 000000000..3f6127eae --- /dev/null +++ b/.claude/plans/spog-alpha-channel-v1.md @@ -0,0 +1,413 @@ +# spog-alpha-channel-v1 — the SPOG alpha channel in MedCare-rs: domain is a mask, the cycle is a sealed batch, the rung is read from the stamp + +> **Status:** SPEC (Phase 0), 2026-09-07. Register-before-code. Every "exists" +> claim below was read this session (four Sonnet inventories, orchestrator- +> verified where cited; banked outside the public repo because they quote a +> private consumer). Every "absent" claim names the search that backs it. +> +> **Operator mandate (2026-09-07, verbatim):** *"probe autoattended autonomous +> decision making until you get MedCare-rs SPOG alpha channel to work / keep in +> mind that rows are experimental in lance 11 and only required for tombstones +> which we avoid by having sealed batch per cycle / also keep in mind that the +> relevant bakes in S3 might not be per domain separate (palpitations hpo, +> heart uberon/FMA, heart attack (mondo disease), nitroglycerin (UMCU), triage, +> bypass (snomed actions, interventions) heart rate (Loinc), chebi, mesh +> (research) statistical normalization (cob, iobc) dismech"* — plus *"also +> check Mississippi queen hexagon board game effect vs masking algebra ternlogq +> chaining amortization / same bit that masks mq might 'mask' SPO 'angle' akin +> to multidisciplinary blasgraph"*. +> +> **READ BY:** anyone touching `contract::spog_tenants`, `contract::alpha`, +> `contract::alpha_tunnel`, `contract::wave_dispatch`, MedCare-rs +> `medcare-nodesoa` / `medcare-first-thought::attention` / `medcare-cohorts:: +> {quad_slab,rails,bake_data}`, or citing "SPOG", "alpha channel", "rung × +> tenant", "per-domain bake", "sealed batch per cycle". +> +> **Standing rulings this spec rests on:** `E-EVERYTHING-WIRES-TO-SOA-V3-CE64- +> IS-ALU-LEGACY-1` (R2); `E-PLANNING-MIGRATES-TO-LOCO-R2IL-DATAFUSION-IS-GRACE- +> PERIOD-1`; the alpha-overlay governing choice (`alpha-channel-rung-overlay- +> v1.md` §3k: *"explizite masking ABI traversal wie bei java … sonst verwässern +> wir unsere Architektur"*); the temporal plan (`.claude/temporal/09-plan.md` +> Stage 2: the rung × tenant cross lives ONE crate out of the zero-dep +> contract); `ndarray/.claude/rules/data-flow.md` (no `&mut self` during +> computation — the alpha `claim` sites are the operator's standing exception, +> recorded in temporal 06, not re-litigated here). + +## §0 — What "the SPOG alpha channel works" means, measurably + +The alpha channel is `AlphaAllocation` → `AlphaOverlay::claim` → 16-byte +`AlphaStamp` in value slot 0 (`crates/lance-graph-contract/src/alpha.rs:72-88`). +SPOG adds the fourth coordinate G — *"No fourth column: G is read from the +key"* — as the canon-high concept half of the classid, `graph_of` +(`crates/lance-graph-contract/src/spog_tenants.rs:38-40`, body +`(addr.classid() >> 16) as u16`), and routes each claim to the tenant owning +that G in `SpogTenants::claim` (`spog_tenants.rs:85-94`). Today +`SpogTenants` has **zero consumers** anywhere (grep `SpogTenants` over +`lance-graph/crates` and `medcare-rs/crates`: hits only in its own file and +tests). The consumer that exists calls the lower entry point instead: +`dispatch_thought(base, &seed, &keys, cycle)` at MedCare-rs +`crates/medcare-nodesoa/src/frontier_dispatch.rs:81`, over the 60,478-row +`obo-core.soa` spine, with `cycle` pinned to the constant `SHADOW_CYCLE = 1` +(`patient_shadow.rs`) and the scanpath read only by the debug HTML view +(`medcare-server/src/views/reasoning_debugger.rs:877-903`). + +"Works" is therefore five measured facts, each with a falsifier (§6): + +1. **Routed.** A real patient frontier's claims land in per-G tenants over the + full 762,041-row `all-lanes.soa` — every claim is `TenantClaim::Routed`, or + is `NoTenant(g)` for a G the caller did not declare (never silently absorbed). +2. **Domain is a mask.** A domain view is `OR` over its G tenants' masks + (disease = MONDO ∪ ICD-10-GM ∪ Orphanet ∪ …), never a file boundary; a + horseshoe lane (CUI, SNOMED) is fenced per row by a TUI-equality mask over + `value[0..2]`, never assigned to a domain wholesale. +3. **Rung × tenant is observable.** The 10 × N `AlphaMask` matrix exists as a + computation (ternlog on the masks' words), and "this domain was addressable + and no rung looked" is one read. +4. **Sealed batch per cycle.** One `FixedSizeBinary(512)` append per cycle, + `cycle = dataset.version() + 1`; no stable row ids, no delete, no merge. +5. **The rung byte is the attention rung.** `AlphaStamp.rung` is written by + exactly one writer semantics (the ladder step), and the domain reflection + MedCare reads today through `domain_rung` becomes a tenant read. + +## §1 — Input inventory (verified this session) + +### 1a. The contract surface, verbatim signatures + +| symbol | file:line | shape | +|---|---|---| +| `AlphaMask { words: Box<[u64]>, len: u32 }` | `alpha.rs:224-230` | private fields; `and/or/xor/and_not/not` via `zip` with a **release-mode** `assert_eq!(self.len, other.len)` (`:289`); `materialize_ordinals` the one named materializer (`:345`) | +| `AlphaAllocation::over(&[NodeRow])`, `ordinal`, `mask_of` | `alpha.rs:377,394,417` | ordinal = base position, lazily indexed | +| `AlphaOverlay::{over_shared,new,claim,claim_path,attended_mask,scanpath,rows}` | `alpha.rs:498-661` | `claim(&mut self, addr, rung) -> Result` | +| `graph_of`, `SpogTenants::{over,claim,tenant,concepts,claimed_len,merge}`, `TenantClaim::{Routed,NoTenant,Substrate}` | `spog_tenants.rs:38,74,85,98,107,113,123,44-52` | tenants = `Vec<(u16, AlphaOverlay)>`, linear `find` per claim | +| `AlphaTunnel::{over,lane,lane_mut,run_wave,run_wave_parallel,merge,merged_rows}` | `alpha_tunnel.rs:82-223` | `merge` is `(rung, seq)`-ordered with NO sort (`debug_assert!` at `:197-200`) | +| `dispatch_thought(base, seed, keys, cycle) -> WaveDispatchOutcome{scanpath, waves}` | `wave_dispatch.rs:62-67` | constructs allocation + tunnel internally; `seed.clone()` per lane is RULED intentional (temporal 06) | +| `RowFocusMask` (D-ACR-1) | `attention_facet.rs:370-455` | a **facet-prefix** set (`AttentionFocusFacet`, `covers`/`common_prefix`), NOT a row bitmask — a different object from `AlphaMask`; both stay | + +**Absent, by search (state BEFORE D-SPG-1, kept as the record of why D-SPG-1 exists):** no `AlphaMask::words()` accessor (grep `fn words\|as_words\|from_words` in `alpha.rs` at `2d111623`: 0 hits) — the words were unreachable from any crate, so nothing outside the contract could run a SIMD op on them. **Closed by D-SPG-1 (`1d90183c`): `words()` + `from_words()` exist now.** No `mask_ternlog`/`AND3` call anywhere in `lance-graph-java/native/lgj-abi/src/*.rs` (grep `ternlog|AND3`: 0 hits at lgj `dbac826`); `lgj_hop` is two sequential `simd_mask_and_assign` (`exports.rs:1818,1822`). **This falsifies two standing claims** — `EPIPHANIES.md` E-NXG-8 *"`AND3` = conjunctive narrowing (`lgj_hop`, shipped)"* and `.claude/knowledge/membrane-tiers.md` §"The polyfill is the worked instance" *"`exports.rs` names `kernels::ternlog::AND3`"* — corrected on the board with this spec. **⊘ 2026-09-07, same day — WITHDRAWN at lgj HEAD `8720d1d`:** the grep was run at the stale pin `dbac826`. At HEAD `lgj_hop` is ONE `kernels::simd_mask_ternlog_assign::<{ kernels::ternlog::AND3 }>` (`exports.rs:1816`); `simd_mask_and_assign` is absent from `exports.rs`; the collapse shipped 2026-09-04 (lgj `LATEST_STATE.md`, 3.5–5× measured). E-NXG-8's `AND3` row and `membrane-tiers.md`'s original sentence are both restored — nothing was falsified. Board: `E-THE-FUSED-AND3-HOP-WAS-NEVER-SHIPPED-LGJ-HOP-IS-TWO-ANDS-1` ⊘ block. + +### 1b. The consumer surface (MedCare-rs, private — quoted minimally) + +| fact | where | +|---|---| +| ONE production caller of the alpha channel: `dispatch_thought(base, &seed, &keys, cycle)` | `medcare-nodesoa/src/frontier_dispatch.rs:81`; `base = obo_store::store().node_rows()` (60,478 rows) | +| `cycle` is the constant `SHADOW_CYCLE = 1` — *"Fest, damit zwei Requests … byte-gleich sind"*; there is no cycle loop anywhere (temporal 06: `git grep` for `cycle + 1\|cycle++\|for cycle in` → 0 hits) | `medcare-nodesoa/src/patient_shadow.rs` | +| The second `AlphaStamp.rung` writer: `domain_rung(classid) = Domain::of_classid(..) as u8 + 1` (1..=8) at the claim site `self.overlay.borrow_mut().claim(*addr, rung)`; `reflection(ov, domain)` filters the scanpath on `stamp_of(r).rung == domain as u8 + 1` | `medcare-first-thought/src/attention.rs:127,153,221` | +| Persist path: `overlay_to_batch` / `write_alpha_overlay` → `node_rows_to_batch(rows, cycle)` → one column `node: FixedSizeBinary(512)` NOT NULL, append | `medcare-nodesoa/src/alpha.rs:26,77`, `lib.rs:46-56` — both functions have **zero callers outside their own tests** | +| Domain grouping: `Domain::of_classid` → `FacetRegime::{Single(d), PerRowTui, Unassigned}`; `Domain::of_row` resolves `PerRowTui` via `cui::tui_of_row(row)` (`value[0..2]`) | `medcare-cohorts/src/quad_tenant.rs`; `cui.rs:123` | +| Row-resident FK register: the 4×u24 identity quad at value `[32,44)`; `quad_slab::project(rows, domain, from, to)` is the scalar reference — filters `Domain::of_row(r) == Some(domain)` FIRST (codex P1 on #410), then compares slot values | `quad_slab.rs:63,78` | +| Per-domain rail bakes: `RailStore { bakes: BTreeMap }` keyed by classid, grouped `je_domaene` before baking — *"every domain needs a separate bake, the nodes dilute"* (operator 2026-08-12; 180,107 false cross-domain ancestries measured otherwise) | `medcare-cohorts/src/rails.rs:928,965-975` | +| Combined artifacts: `obo-core.soa` = 5 classids in one file (`obo-core-combined-bake-confounds-facets`, RAIL_OFFENE_POSTEN); `all-lanes.soa` = **every lane** node-matched, 762,041 rows, classid-major numeric sort, `soahead:762041@0x03040000`; mmap via `bake_data::soa_map()` / `node_rows_for_classid()` (partition_point over the sorted classids) | `data/config/bakes.tsv`; `bake_data.rs:307,356` | +| Live classids are the domain form for 7 lanes (MONDO `0x9101_0000`, HP `0x9202_0000`, UBERON `0x9303_0000`, LOINC `0x9407_0000`, … OPS `0x9811`), legacy `0x03xx` for PATO/RO/CUI/Orphanet/ATC/RxNorm; readers fold via `domain_block` | `crosswalk.rs:173-177`, `loinc.rs:36`, `cui.rs:95`, `ontology_map.tsv` | + +**The operator's hint is confirmed, not assumed:** the bakes in S3 are NOT +per-domain files. `all-lanes.soa` is one image for twelve lanes, `obo-core.soa` +one image for five. Per-domain separation exists today only in `RailStore`'s +in-memory grouping. So "domain" must be a **mask over the combined image**, +and this spec makes it exactly that (§3). + +### 1c. The kernels (ndarray T1, verbatim) + +`eq_u32_to_mask(values, needle, out)` `simd_int_ops.rs:562`; +`eq_u32_strided_to_mask(bytes, first_offset, stride_bytes, count, needle, out)` +`:629`; `mask_and/or/andnot(_assign)` `:775-932`; +`mask_ternlog::(a, b, c, dst)` `:983`; `mask_ternlog_assign::(a, b, c)` +`:1015`; the eight named immediates `simd.rs:570-587` (`AND3 0x80`, +`AND2_ANDNOT 0x40`, `AND_ANDNOT2 0x10`, `OR2_AND 0xA8`, `XOR3 0x96`, `MAJ3 0xE8`, +`AND2 0xC0`, `OR3 0xFE`). Convention: `index = (a<<2)|(b<<1)|c`, result bit = +`(IMM >> index) & 1`. Existing in-repo consumer of the named immediates: +`lance-graph-planner/examples/probe_nxg_hist_1.rs:51-136`. + +Measured bounds that ride with every speed claim below (temporal 09 P3, +2026-09-06, `--release`): chaining amortization holds (T3/T1 → 0.50 at K ≥ 8, +K = 1 control reads 1.03), **contingent on the mask set fitting L2**; mask +loses to a sparse arm below 0.1 % active. A 762,041-bit mask is 11,907 words = +**93 KiB**; ten rungs × one tenant = 0.93 MiB (fits a 2 MiB L2); the full +10 × 15 matrix does not, and never needs to be resident at once (§3.3). + +## §2 — Frozen decisions (each cited; none re-opened below) + +| # | decision | why / source | +|---|---|---| +| F1 | **No Lance row ids, no delete, no tombstone.** The alpha channel APPENDS one sealed batch per cycle; addressing is `(version, NodeGuid)`. `enable_stable_row_ids` stays OFF everywhere. | operator 2026-09-07; temporal 01/05 (all three delta arms need stable ids — measured D-LNC-5a); alpha never deletes | +| F2 | **Domain = mask over the combined image**, never a per-domain file. Tenant mask for G = `eq_u32_strided_to_mask(bytes, 0, 512, n_rows, classid, out)` over the mmap; domain view = `OR` over its Gs; horseshoe lanes fenced by a `value[0..2]` TUI-equality mask. **⊘ 2026-09-07 (operator: "horseshoe mask is a category error"):** the multi-facet lane is not fenced, it is PARTITIONED — once, `LazyLock`, over the immutable bake — by its value-side witness (`value[0..2]`) into per-domain masks, and `domain(D) = static(D) ∪ dynamic(D)` is ONE mask. There is no horseshoe category: a row of that lane witnessing anatomy IS anatomy. The shipped `domain_mask` silently EXCLUDES the lane (all-single-facet filter) and `horseshoe_mask` rescans 762,041 rows per call; both are defects. **⊘ FIXED in the consumer 2026-09-07** (measured, address half → union): lab 103,291 → 206,582 · substance 131,582 → 254,485 · anatomy 119,684 → 119,732 · procedure 38,956 → 40,340; the lane resolves 227,626 of 266,579 rows and the remaining 38,953 are REFUSED (witness names no domain) and counted rather than lost between the halves; gate (h) unchanged at 0.0020; three disable runs fire. `horseshoe_mask` no longer exists. See `E-A-DYNAMIC-DOMAIN-MASK-IS-A-SECOND-WITNESS-AND-ITS-ALIGNMENT-IS-CALIBRATION-1`. | §1b; operator hint; `RailStore`'s grouping is the in-memory precedent | +| F3 | **G is the contract's `graph_of` (canon-high u16)**, one tenant per G. MedCare's coarser `Domain` (byte `0x91..0x9D`, `domain_block.rs`) is a GROUPING of Gs, expressed as mask `OR` — no second G reading is minted, and no bit math on a composed classid appears in consumer code (`Domain::of_classid` already answers it). | `spog_tenants.rs:38-40`; worker rule 4 | +| F4 | **The rung byte carries the attention rung only.** `domain_rung` (Domain+1) is retired as a rung writer once the tenant read replaces `reflection` — the domain is `graph_of(addr)`, read from the key, never from the stamp. Until D-SPG-5 lands, the two writers stay separate overlays (they do today). Trap: never carry a rung ordinal in the residue band (temporal 09 Stage 2). | D-RLR-5 (a); temporal 06 "unrecorded semantic collision" | +| F5 | **Placement:** the SIMD cross cannot live in the zero-dep contract. The contract gains ONE method (`AlphaMask::words(&self) -> &[u64]`, plus the paired `from_words` constructor guarded by the same length law) — a method on the carrier, not a type. The live cross runs in MedCare (`medcare-cohorts` already depends on `ndarray`; the BBB rule keeps `lance-graph-planner` out of the customer binary). An agnostic synthetic probe may live in `lance-graph-planner/examples/`. **⊘ 2026-09-07 (rebase onto `main` after #1220):** the contract now ships a cross of its own — `alpha_focus::AlphaFocus::{cell, matrix, unlooked, rung_reach}`, scalar `and`/`and_not` over the two masks, no `ndarray`. F5's ruling is about the **SIMD/ternlog** cross and is unchanged by that; but `cell` and `unlooked` now exist in two places, and which one a consumer should reach for was recorded here as an open operator preference — **⊘ that framing was wrong (operator, 2026-09-07: *"lance-graph owns the agnostic thinking / Akin to Palantir foundry"*).** It is not a preference: the cell is agnostic thinking and belongs upstream; the tenant calls it. `tenant_masks` / `rung_tenant_cell` / `unlooked` are all writable without naming the domain and are therefore platform-side; only the DOMAIN BINDING is MedCare's — which Gs make a `Domain` (`static(D)`) and the value-witness → domain table that partitions the multi-facet lane once (`dynamic(D)`), composed as `domain(D) = static(D) ∪ dynamic(D)` per F2 (⊘). Not `horseshoe_mask`: F2 retires it, and this row first named it as MedCare's after F2 had already ruled it a category error — corrected 2026-09-07 (CodeRabbit on #1221). What stays genuinely open is narrower: **where ndarray-backed mask algebra can live**, given `lance-graph-planner` is BBB-forbidden in a customer binary and `lance-graph-contract` is zero-dep — a contract feature-gate, or a BBB-allowed crate between them. See `E-LANCE-GRAPH-OWNS-THE-AGNOSTIC-THINKING-CONSUMERS-BIND-DOMAIN-1`. | temporal 09 Stage 2; CLAUDE.md litmus (method on carrier) | +| F6 | **Cycle = Lance version.** ~~`cycle = dataset.version() + 1` at seal time~~ — the identity is right and the AGENT was wrong: `sealed_version = base_version + 1` is a **verified identity inside `LanceCycleWriter`** (`graph::cycle_sink`, #911), not something a consumer computes by opening the dataset. A caller that reads a version and then appends has written a TOCTOU: Lance's `Append` rebases even on a single attempt (measured, `lance-9.0.0/src/io/commit.rs`), so a read-then-write "refuse, don't renumber" guard cannot do what it claims. Idempotency is durable instead — `(cycle, batch_hash)` in the same commit, reconcile FIRST. `SHADOW_CYCLE = 1` stays for the byte-identical debug view. | operator "sealed batch per cycle"; #911/#912 Phase A; temporal 06 | +| F7 | **Explicit mask ABI traversal, never VSA.** Nothing here bundles; `I-VSA-IDENTITIES`' niche is untouched. | alpha-overlay plan §3k (operator, 2026-08-21) | +| F8 | **DataFusion is not extended.** Step 5 (containment: `with_row_id`/`with_row_addr` OFF + a test) comes AFTER the tenant masks exist, or the flags are the only identity the consumer has. | IDEAS 2026-09-07 containment card; grace-period ruling | +| F9 | **The hand-rolled MedCare alpha migrates ONTO the #1198 contract alpha, not beside it.** Operator, 2026-09-07 (verbatim): *"make sure to migrate the handrolled MedCare-rs alpha to LG 1198 alpha"*. "LG 1198 alpha" = the contract path as audited and staged in lance-graph #1198 (`.claude/temporal/`, merged `3797237b`; Stage 1b landed in the successor PR): `AlphaAllocation` → `AlphaTunnel` rung lanes → `SpogTenants` G routing → `merge()` in `(rung, seq)` order → `wave_dispatch`. The hand-rolled surfaces are MedCare's own overlay drivers that bypass that path: `attention::WatchedRows { overlay: RefCell }` with its `domain_rung` writer and `into_overlay`, `backreference::combined_base` (a second base assembled per patient), and the `medcare-nodesoa::alpha` writer that takes a bare `AlphaOverlay`. Each becomes a consumer of the contract path (D-SPG-5, broadened) — no MedCare-local overlay driver survives the migration, and behaviour is preserved by the set-equality falsifier. | operator 2026-09-07; PR_ARC_INVENTORY 2026-09-06 (#1198); temporal 06 ("the second rung writer") | + +## §3 — The design, in the order the operator gave (1 probe → 2 masks → 3 lane-local → 4 r2il → 5 containment) + +### 3.1 Tenant masks are the per-G bakes (step 2, the unblock) + +Over `all-lanes.soa` mmapped (`bake_data::soa_map()`), for every declared G: + +```text +// pseudocode — the shipped form is medcare-cohorts::spog_masks::tenant_masks +sweep[c] = eq_u32_strided_to_mask(bytes, 0, 512, n_rows, c, out_c) // one per DISTINCT full classid c in the image +tenant_mask[G] = OR_{c : graph_of(c) == G} sweep[c] // fold per canon-high half — never a single full-u32 equality standing in for G +domain_mask[D] = OR_{G ∈ D} tenant_mask[G] // disease = MONDO ∪ ICD-10-GM ∪ Orphanet ∪ OMIM… +horseshoe[D] = { rows r : regime(classid(r)) == PerRowTui ∧ Domain::of_row(r) == D } // per row; scalar today +// ⊘ 2026-09-07: "horseshoe" is a category error. Read instead as +// dynamic[D] = LazyLock{ partition of the PerRowTui lane by value[0..2] }[D] // once per bake, N masks +// domain[D] = static[D] ∪ dynamic[D] // ONE mask; no fence, no per-call scan +// and the shipped `domain_mask` (which SKIPS the PerRowTui lane) is the bug this line was hiding. +``` + +Codex (P1 on #1221) named the trap the fold avoids: a full-`u32` equality per G +would MISS every classid whose custom low half is non-zero or whose legacy +encoding shares a canon-high half (`CLASSID_OSINT_V3 = 0x0701_1000` vs +`graph_of == 0x0701`), so a claim could route to a G tenant via +`SpogTenants::claim` while its row is absent from that tenant's mask. The +shipped code sweeps every DISTINCT classid and ORs per G; the falsifier is a +synthetic image with two classids sharing one canon-high half (landed with +D-SPG-3). + +Computed ONCE per Lance version (the mask generation), served to every rung — +the Mississippi-Queen M1b amortization stated as a cache key +`(generation, G)` (§4). `SpogTenants::over(alloc, cycle, &concepts)` is then +declared with exactly the Gs that have a non-empty tenant mask; a claim to any +other G is `NoTenant(g)` — visible, not absorbed. The `AlphaAllocation` is +`AlphaAllocation::over(node_rows)` over the whole image, so ordinals are image +positions and every mask in this spec shares one `len`. + +### 3.2 The crosswalk is a chain of masked equality sweeps (step 1's subject) + +Row-resident FKs only — a sidecar `HashMap` join (`cui::mondo_to_cui`) is the +scalar REFERENCE, never the mechanism. Hop n (pseudocode; the real T1 call is +the four-argument `mask_ternlog::(a, b, c, dst)` or the in-place +`mask_ternlog_assign::(a, b, c)`, `ndarray/src/simd_int_ops.rs:983,1015`): +`eq_u32_strided_to_mask` on the FK column of tenant n's rows for each needle of +the incoming survivor key set, `OR`-accumulated into `sweep`, then +`mask_ternlog::(&sweep, &tenant_mask[n], &rung_gate, &mut survivors)` — +the survivors' key set is the needle set of hop n+1. The forbidden move is a +mask-`AND` across two tables (nexgen room 18; `E-…-CHAIN-OF-MASKS` on the +board). Which FK columns are u32-aligned in the real image (key tail at byte +12; quad slots are u24 at value 32..44 and are NOT eq_u32-addressable without +a masked compare) is pinned by the probe's own W0 read, not guessed here — +see D-SPG-4's pre-registration rule. + +### 3.3 The rung × tenant cross (step 2's meta-awareness layer, temporal Stage 2) + +```text +// pseudocode over the four-argument T1 call mask_ternlog::(a, b, c, dst) +cell[rung r][G] : mask_ternlog::(lane_r.attended_mask().words(), tenant_mask[G].words(), tenant_mask[G].words(), &mut dst) // AND2 ignores c +unlooked[D] : mask_ternlog::(domain_mask[D].words(), any_rung.words(), any_rung.words(), &mut dst) // = domain & !any_rung +// dst is rebuilt as AlphaMask::from_words(dst, len) so the contract's tail law re-applies +``` + +No new stored state: both operands are recomputed projections; the cross is +computed per read for the (r, G) pairs asked, so at most three 93 KiB masks +are live per op (fits L2 — the P3 bound). Kill condition (temporal 09): if +P3-style amortization does not show on THIS shape, build it scalar and say so; +the shape win stands without the speed win. + +### 3.4 Sealed batch per cycle (step 3's write side) + +`SpogTenants::merge()` → the merged `NodeRow`s (stamp in value slot 0, key +unchanged) → `node_rows_to_batch(rows, cycle)` → ONE `write_node_soa_dataset` +append. `cycle = dataset.version() + 1`, read before the append, asserted equal +to the committed version after. Time travel = read at version; no row identity +is ever needed because the key IS the identity (P0 canon). + +### 3.5 Steps 4 and 5, queued behind the probe + +Step 4: the first `ogar-r2il` consumer through `lance-graph-ogar` = `RANK` to +admit + `TERNLOG 0x86` per hop, with the falsifier that a lifted crosswalk +program yields the hand-written chain's survivor mask bit-for-bit (IDEAS +2026-09-07). Step 5: DataFusion containment (F8). Neither starts before D-SPG-4 +is green. + +## §4 — Mississippi Queen, ternlog chaining, and the SPO "angle" (the operator's second check) + +Source: `ndarray/.claude/plans/gemm-ternlog-mask-consolidation-v1.md` §9/§11 +(M1 reveal-ahead [G], M1b tile-serves-every-boat [G] = the amortization with +cache key `(mask generation, panel)`, M2 lookahead [H], M3 coal budget [H], R1 +hexagon [H]; §11.5 `TriadicProjection {Abc, AbAskC, AcAskB, BcAskA, AOnly, +BOnly, COnly, Background}` = K0..K7 as a ternlog immediate indexed +`(a<<2)|(b<<1)|c`, graded [H] *pending one operator word*). + +**The mapping, stated as something that can fail.** `mask_ternlog::(S, P, +O)` computes per row `IMM[(s<<2)|(p<<1)|o]`. So the immediate's eight bits ARE +the eight K-projections of one quad row (which of S/P/O are present), and the +six ways of wiring the S/P/O presence columns onto the kernel's A/B/C inputs +are the six hex directions — the "angle". A crosswalk hop's immediate, read as +a K-set, is the hop's declared projection; `AND3` is K7 alone (all three +present), `AND_ANDNOT2` is K4 (A only). The operator's interjection — *"same +bit that masks mq might 'mask' SPO 'angle'"* — is the claim that the mask bit +and the projection bit are one bit. It is true by construction of the +immediate's index; whether it is USEFUL is what gate (f) measures: the eight +minterm masks must partition the row population, and a permutation of the +wiring must permute the immediate's bits without changing any set. + +**Reveal-ahead = mask generation.** M1's "reveal the tile ahead of the cursor" +is the tenant masks being computed once per Lance version, before any rung +reads (§3.1); M3's coal budget is the per-cycle re-chain budget — how many +hops a rung may run before the next seal. Both are cache-key statements, not +new mechanism. D-GTM-0l's own next probe (*"re-run the identical instrument +against an OGAR-minted address space"*) IS this spec's probe: `all-lanes.soa` +keys are minted from the concept hierarchy, which is the substrate the prefix- +routing hypothesis was proposed for and never measured on. + +**lgj correction carried here — ⊘ ITSELF WITHDRAWN, see below:** the fused `AND3` hop is an OPPORTUNITY, not +shipped code — `lgj_hop` does two ANDs (`exports.rs:1818,1822`) **at `dbac826`; at HEAD `8720d1d` it is one ternlog (`exports.rs:1816`)**. Whether the +fusion pays on the hop's shape is gate (c) below; nothing in this spec assumes +it does. + +**⊘ 2026-09-07, operator (later the same day) — the three-way decomposition, +recorded as the reading this section is to be held against.** What converges is +not "hexagons are good": Mississippi Queen, TERNLOG mask chaining and BLASGraph +are three ways of paying for ONE operation — *deciding what remains eligible +without materializing the rejected world.* Operator's table, verbatim in +substance: + +| mechanism | pays for | says | +|---|---|---| +| Mississippi Queen | reveal geometry / exploration budget | where activity **MAY** go (topology) | +| TERNLOG masks | Boolean eligibility / inhibition | where activity **IS ALLOWED** to go | +| BLASGraph | numeric propagation over the survivors | **HOW MUCH** activity goes there | + +The hexagon was never magical (degree-6 falsified repeatedly, ndarray +`gemm-ternlog-mask-consolidation-v1.md`); what survived is the economics of +revealing only what can matter next — which is exactly why the corrected #620 +result reads as it does: fan-out loses because reconvergence makes you inspect +redundant edges, not because hex degree is special. TERNLOG's prize is +**amortized eligibility** (resident state ⊗ mask A ⊗ mask B ⊗ mask C, the +rejected volume never becoming a second representation), and gate (h)'s 0.0019 +already shows the limit: it wins while the working masks stay resident and +collapses toward bandwidth parity when depth blows the cache budget. Alpha is +then not plumbing but **the sparse, readable record of which part of the +potential field actually fired.** One cycle: topology reveals a candidate +region → resident masks narrow (TERNLOG) → active survivors → BLASGraph numeric +rail → strength/score → **alpha delta** → next-cycle focus. + +**The boundary rule (operator, binding):** the three COMPOSE, they do not +collapse. The MQ hexagon does not become a TERNLOG immediate; the immediate does +not become a neural weight; BLASGraph is not used for Boolean elimination just +because a matmul can encode it. *Topology chooses neighborhood, masks choose +admissibility, BLAS chooses magnitude.* This sharpens §4's own claim above: the +"same bit" of the interjection is the mask bit = the projection bit (true by +index construction), NOT the mask bit = a weight — gate (f) measures the former +and must never be read as licensing the latter. + +**Consequence for the cross (#1220 / D-SPG-3):** with the rung × G cross the +numeric leg can run INDEPENDENTLY per rung — `R_r × G → mask → numeric +propagation` for r in 0..=9 — and alpha is the common readout plane where those +independently computed fields overlap. Meta-awareness then need not "run the +ten rungs"; it observes the field intersections. That is the reading D-SPG-4's +gate (c)/(h) and the F5 open question (scalar `AlphaFocus` vs SIMD +`spog_masks`) should be decided under: the cell is the READOUT surface, the +propagation is a separate rail, and neither owns the other. Motto, as given: +*"Don't compute the world. Narrow what can matter, then spend arithmetic only +there."* + +## §5 — Deliverables + +| D-id | scope | repo | gate / falsifier | +|---|---|---|---| +| **D-SPG-0** | This spec; the lgj `AND3` correction on the board — **⊘ withdrawn 2026-09-07, the correction was itself wrong at lgj HEAD; E-NXG-8 and `membrane-tiers.md` are restored** — (E-NXG-8 regraded, `.claude/knowledge/membrane-tiers.md` §"The polyfill is the worked instance" corrected in place); IDEAS PROBE-CROSSWALK-MASK-1 card → In progress | lance-graph | citation-decay + append-only gates green | +| **D-SPG-1** | **SHIPPED 2026-09-07** (`alpha.rs`, +78 lines: two methods, three tests). `AlphaMask::words(&self) -> &[u64]` + `AlphaMask::from_words(words: Box<[u64]>, len: u32) -> Self` (same tail-clearing law as `not()`; a `words.len() != len.div_ceil(64)` input is REFUSED, release-mode). No other contract change. | lance-graph | can-fire: `from_words` with a wrong word count panics; can-stay-silent: round-trip `from_words(m.words().into(), m.len()) == m` for `len % 64 != 0`; existing 10 alpha tests untouched | +| **D-SPG-2** | **SHIPPED 2026-09-07** (MedCare-rs `c6a9095`: `medcare-cohorts::spog_masks::{tenant_masks, domain_mask, horseshoe_mask}` + `bake_data::soa_image`, feature `spog`; census probe `examples/spog_tenant_census.rs`). Tenant masks over the combined image: `eq_u32_strided_to_mask` per distinct classid over `soa_image()` bytes, folded per `graph_of`; domain = `OR`; horseshoe = per-row `Domain::of_row` fence (scalar; a SIMD `eq_u16` sweep is a T1 addition, not a reading change). Measured: 762,041 rows, 16 tenants, partition holds both ways, gate (h) ratio 0.0019. `SpogTenants::over` from the Gs moves to D-SPG-5 **⊘ 2026-09-07:** sha unverifiable — see `STATUS_BOARD.md` D-SPG-2 (regraded Shipped-unpushed). **⊘ later the same day:** pushed as MedCare-rs `29d4792` (rebased onto `9f9b7be`, after #621); see STATUS_BOARD D-SPG-2 — Shipped. | MedCare-rs| every `tenant_mask[G].count()` equals **Σ over the distinct full classids `c` with `graph_of(c) == G` of `node_rows_for_classid(c).len()`** (the partition_point answer per classid is the independent reference; G is `graph_of`, not a classid — the shipped test `every_tenant_mask_counts_exactly_its_classid_windows` sums exactly this way; wording corrected 2026-09-07 after CodeRabbit); `Σ_G count == n_rows` over the declared set (anti-vacuity: the union is the whole image, no row in two tenants) | +| **D-SPG-3** | **SHIPPED 2026-09-07** (MedCare-rs `6bf7764`: `spog_masks::{rung_tenant_cell, unlooked}`, 4 tests + the Codex sibling-classid falsifier, mutation-fired). The rung × tenant cross via `mask_ternlog` on `words()` (§3.3), with the `unlooked[D]` read **⊘ 2026-09-07:** sha unverifiable — see `STATUS_BOARD.md` D-SPG-3 (regraded Shipped-unpushed). **⊘ later the same day:** pushed as MedCare-rs `e5febf9` (rebased onto `9f9b7be`, after #621); see STATUS_BOARD D-SPG-3 — Shipped. | MedCare-rs| `cell[r][G].count() == lane_r.scanpath().filter(graph_of == G).count()` for every (r, G) (materialized reference); `AND_ANDNOT2` differs from `AND3` on the same operands wherever `any_rung` is non-empty (the immediate is not decoration) | +| **D-SPG-4** | **PROBE-CROSSWALK-MASK-1**, gates (a)–(h) below, on the real image. **Pre-registration rule:** the probe's W0 READ pins the exact FK columns (byte offsets, widths, needle encoding) in its own header BEFORE any timing runs; a column that is not u32-aligned is either read through a documented masked compare or excluded and said so | MedCare-rs (probe) + lance-graph (record) | §6 | +| **D-SPG-5** | **PARTIAL 2026-09-07** — leg (i) shipped by MedCare-rs #621 (merged `9f9b7be`); legs (ii)–(iv) measured still open on `origin/main`. See `STATUS_BOARD.md` D-SPG-5 for the per-leg measurement. **⊘ later the same day:** legs (ii)–(iv) shipped in MedCare-rs `e722dd1` (PR #622); residue = the two unnamed sites (`graph_feed`, `medcare-soa::patient`) + F5. See STATUS_BOARD. Original scope: **The migration (F9):** every hand-rolled MedCare alpha driver moves ONTO the #1198 contract path — (i) `attention::WatchedRows`' `RefCell` + `domain_rung` writer → claims routed through `SpogTenants` inside an `AlphaTunnel` lane whose rung is the attention rung; `reflection(domain)` = `OR` over the domain's tenants' `attended_mask()`; (ii) `backreference::combined_base` → one `AlphaAllocation` over the image, patient rows as a declared tenant, never a second base; (iii) ~~`medcare-nodesoa::alpha::{overlay_to_batch, write_alpha_overlay}` take the tunnel/tenants' `merge()` rows, not a bare overlay~~ **⊘ 2026-09-07: WRONG as written — it directs a consumer to hand materialized rows to a Lance writer.** The persistence path is: the producer `cast`s a descriptor `(mailbox, dirty row-range, cycle)`; sealed-batch construction, freeze, coalescing and reconciliation are `LanceCycleWriter`'s alone (#911 → #912). `merge()` / `merged_rows()` are readings and arrow-encode sources, never a writer's input. `write_alpha_tenants` (shipped in `e722dd1`) is withdrawn on that basis; `tenants_to_batch` survives as an encode; (iv) `frontier_dispatch` routes through tenants over `all-lanes.soa`. `domain_rung` retired as a rung writer (F4) | MedCare-rs | on a fixed frontier, tenant-read reflection == `domain_rung` reflection as address SETS for all 8 domains (the migration is behaviour-preserving) AND the stamps' `rung` bytes now carry ladder values 1..=9 (can-fire: a fixture where the two would differ if the byte were still Domain+1) | +| **D-SPG-6** | **⊘ RE-SCOPED 2026-09-07 — the write is SoA-owned, not the consumer's.** The alpha cycle is a PRODUCER: it `cast`s a descriptor `(mailbox, dirty row-range, cycle)` — never owned rows — and the sealed commit belongs to `LanceCycleWriter`, the sole application writer. Read-back is `temporal::QueryReference::at` + deinterlace. Original scope (a consumer-side `merge()` → `node_rows_to_batch` → append) is withdrawn; see `STATUS_BOARD.md` D-SPG-6 for the four properties it violated. | lance-graph (blocked: no `LanceShardSink`, read side unwired) | empty cycle ⇒ ZERO Lance ops (`NoChange`); re-submitted identical batch ⇒ `Reconciled`; same cycle + different hash ⇒ `HashConflict` fails closed | +| **D-SPG-7** | ogar-r2il consumer (`RANK` + `TERNLOG 0x86`) through `lance-graph-ogar` | lance-graph | lifted program's survivor mask == hand chain, bit-for-bit — **Queued, gates on D-SPG-4** | +| **D-SPG-8** | **SHIPPED 2026-09-07** (MedCare-rs #621, merged `9f9b7be`). DataFusion containment (F8) | MedCare-rs | schema of the scan carries neither `_rowid` nor `_rowaddr`; a test that flips red if either flag returns — **met**, and the test observes the PRODUCTION call site (`state.rs:945` / `:1264`). See `STATUS_BOARD.md` D-SPG-8 | + +**Order is not negotiable:** D-SPG-0 → D-SPG-1 (the one contract line) → +D-SPG-2 (masks exist) → D-SPG-4 (probe; may run its synthetic arm before +D-SPG-2 lands, its real arm after) → D-SPG-3 → D-SPG-5 → D-SPG-6 → D-SPG-7 / +D-SPG-8. The loop per the autoattended pattern: plan → preflight → sprint → +review → fix P0 → commit → repeat; every board write in the same commit as +the code it describes; MedCare-rs edits stay in MedCare-rs (private). + +## §6 — PROBE-CROSSWALK-MASK-1, gates (pre-registered; the IDEAS card's (a)–(e) plus three) + +| gate | pass condition | what a fail means | +|---|---|---| +| (a) sets | survivor SETS of the mask chain == the scalar reference (`quad_slab::project` / sidecar path), every hop, as `materialize_ordinals` vectors **+ calibration (2026-09-07):** the lab domain is the one where static and dynamic cardinalities coincide (103,291 == 103,291, disjoint row sets) — consistent with a 1:1 crosswalk by the bake's construction. A masked sweep from the lab tenant across the bridge must therefore land on EXACTLY 103,291 rows; any other count is a defect in the chain, not a number to report. The count is NECESSARY, not sufficient: equal cardinality on disjoint sets is consistent with a bijection and proves none, so the pass condition stays the set equality against the scalar reference at the head of this row — 103,291 is the cheap early filter in front of it, never a substitute. For a non-aligned domain the dynamic count must be ≤ the static one (coverage, not error); a dynamic count EXCEEDING static is the anomaly. | the FK reading of the columns is wrong — never the mask algebra | +| (b) bytes | 0 bytes/step under the counting allocator (D-GTM-0k's instrument, `hex_trie_vs_gemm_probe.rs`) on the hop hot path | something materializes | +| (c) flat | per-hop ns flat in chain length K while the live masks fit L2; report the K = 1 control (must read ≈ 1.0) and the bandwidth column | the win is residency, not chaining — say so | +| (d) seal | ~~a deliberately cross-family `AND` (two tenants' FK masks) is REJECTED at the seal (can-fire) AND a same-family `AND` passes (can-stay-silent)~~ **⊘ re-scoped 2026-09-07 (CodeRabbit on #1221, correct):** an `AlphaMask` carries words and `len` and nothing else — `and`/`zip` and `mask_ternlog` refuse a LENGTH mismatch only (can-fire, shipped: `a_cell_over_two_allocations_is_refused`, `two_readings_of_different_images_are_refused`), and two same-length masks from two different images are indistinguishable at the mask. So the cross-family rejection is NOT measurable at the seal and is not claimed. Provenance is STRUCTURAL and the caller's: one `soa_image()` and one `AlphaAllocation` per cycle, every mask derived from it — the contract-side form of the same rule is `AlphaFocus::cross`'s `ptr::eq` on the base slice (#1220). Gate (d) now reads: the probe holds exactly one image handle per cycle (grep fence: one `soa_image()` call in the probe), and the length fence fires on a deliberately mismatched image (can-fire) while same-image masks pass (can-stay-silent) | the fence is decoration; or a second image handle appears in the probe | +| (e) sparse | a hop with < 0.1 % survivors is routed to the sparse arm and the two arms agree on the set | the density crossover moved | +| (f) angle | the eight `mask_ternlog::(S,P,O)` minterm masks over the quad-stamped rows are pairwise disjoint and sum to the population; each of the six S/P/O→A/B/C wirings permutes the immediate's bits without changing any set; a wrong immediate (`0x80` vs `0xC0`) differs on real data **+ calibration (2026-09-07):** self-consistency (partition) is not correctness. On the lab domain the CORRECT immediate reproduces the known 103,291 AND its survivor set equals the gate (a) scalar-reference set; a wrong immediate fails the count already (early, 20 ns), and an immediate that matches the count but not the set is caught by the set half. The count is the first cheap external reference gate (f) has had; the set equality is the proof. | K0..K7-as-immediate is not a projection basis on this substrate — the §4 mapping is regraded | +| (g) reflection | tenant-read reflection == `domain_rung` reflection as sets (D-SPG-5's falsifier, run early as a read-only comparison) | the G grouping and the Domain grouping disagree somewhere — find the row | +| (h) amortization | tenant masks computed once per generation and reused across 10 rungs cost ≤ 1/10 + ε of recomputing per rung | M1b does not hold on this shape — cache key regraded | + +Falsifiers are two-sided where a guard is involved ((d), (f)); assertions run +at the END so every claim is measured before any can abort (probe_nxg_hist_1's +lesson); the K = 1 control and the counting allocator are mandatory arms. + +## §7 — Non-goals + +- **No new type.** `AlphaMask`, `SpogTenants`, `AlphaTunnel`, `RowFocusMask` + all stay; `words()`/`from_words()` are methods on an existing carrier. +- **No stable row ids, no `cleanup_old_versions`, no retention** (temporal 09 + non-goals 3 and 5). **Do not touch `temporal.rs`.** +- **No band derivation.** `ReasoningBand` is never `RungLevel`; the rung byte + never encodes a domain (F4) and never lands in the residue band. +- **No VSA.** F7. +- **No DataFusion extension.** F8; containment only, after the masks exist. +- **No per-domain bake FILES.** F2 — the operator's hint is honoured by making + the domain a mask, not by splitting artifacts. +- **No claim about recall or proof.** The alpha channel is a pruner (alpha- + overlay plan piece 7); the SPOG channel does not change that. + +## §8 — Corrections banked while writing this spec + +1. **`lgj_hop` does NOT use `AND3`.** ⊘ **WITHDRAWN 2026-09-07 — it does, at HEAD.** **⊘ 2026-09-07, same day — WITHDRAWN at lgj HEAD `8720d1d`:** the grep was run at the stale pin `dbac826`. At HEAD `lgj_hop` is ONE `kernels::simd_mask_ternlog_assign::<{ kernels::ternlog::AND3 }>` (`exports.rs:1816`); `simd_mask_and_assign` is absent from `exports.rs`; the collapse shipped 2026-09-04 (lgj `LATEST_STATE.md`, 3.5–5× measured). E-NXG-8's `AND3` row and `membrane-tiers.md`'s original sentence are both restored — nothing was falsified. Board: `E-THE-FUSED-AND3-HOP-WAS-NEVER-SHIPPED-LGJ-HOP-IS-TWO-ANDS-1` ⊘ block. Kept as the record of a board claim pinned to a foreign repo's sha decaying silently; that is the durable finding, not the AND3 claim. + + *(original text follows)* E-NXG-8 and `.claude/knowledge/membrane-tiers.md` §"The polyfill is the worked instance" said + it did; lgj-abi at `dbac826` has zero `ternlog`/`AND3` symbols. Regraded on + the board (dated entry) and in the knowledge doc (⊘ in place). +2. **IDEAS PROBE-CROSSWALK-MASK-1 named "the existing DataFusion path" as the + reference.** No DataFusion crosswalk exists for this chain in MedCare-rs + (DataFusion sits in `medcare-analytics` RLS/column-mask and `medcare-server` + `state.rs`/`seed.rs`/`routes/patient.rs` — patient scans, not ontology + crosswalks). The reference is the scalar sidecar/quad path (§3.2, gate (a)). +3. **The quad slab is populated on a SUBSET.** `all-lanes.soa` carries quads + on 3,551 stamped rows (bakes.tsv 2026-08-10 repin note), not on every row — + gate (f)'s population is those rows, declared as such. +4. **The horseshoe lanes in the image are ONE (the CUI lane, 266,579 rows), not two.** The census shows LOINC (`0x9407`) resolving single-facet to `lab` (its OR view = 103,291 rows), so the spec's "horseshoe lanes (CUI, SNOMED)" reads as: CUI is the only per-row-TUI lane baked today; SNOMED has no lane (`ontology_map.tsv` UNALLOCATED). The CUI lane's per-row fence lands 122,903 rows in substance, 103,291 in lab, 1,384 in procedure, 48 in anatomy, 0 in disease — the lab count equals the LOINC lane's row count by coincidence (asserted disjoint: the fence lies wholly inside the CUI lane). +5. **PATO (`0x0304`) and RO (`0x0305`) are tenants with no domain** (`FacetRegime::Unassigned`): 1,891 rows addressable, in no domain view. Recorded, not fixed — a domain for qualities/relations is an operator mint question. +6. **The sibling session landed the first `SpogTenants` consumer the same day (MedCare-rs #620, merged `da77cde`), as two EXAMPLES:** routing 512 claims across the 16 tenants with 0 misrouted and one sealed `merge()` batch, and a cardiac `is_a` walk measuring **0.00 % cross-tenant edges** in the baked `is_a` lane — so the chain-of-masks crosswalk (§3.2, D-SPG-4 gate (a)) cannot hop tenants on `obo_full_edges`; the cross-tenant hop lives in the bridge lanes (`mondo_cui`, `snomed_mondo_bridge`, `abnormality_edges`), addressed by CURIE strings, and a **CURIE → address resolver is the prerequisite** for gate (a)'s multi-tenant chain. #620 also corrected its own Mississippi-Queen metric (coverage/cost = 1.000 was a tautology; measured 1.176–1.508 with `edges_examined` as cost; fan-out reconverges 14.29 %) and answered the operator's "same bit" question as THREE widths that compose (MQ reveal 1 bit per `(generation, panel)`, SPO angle 3 bits, TERNLOG imm8 8 bits) — consistent with §4 here, which makes the same distinction between the 3-bit index and the 8-bit table. D-SPG-5's remaining scope is therefore exactly F9: migrating the `src/` drivers, not demonstrating the consumer. + +7. **INTEGRATION UPDATE (2026-09-07, after this branch was rebased onto `main`): + the two PRs this spec was written alongside have MERGED, and both move rows + in §5.** lance-graph **#1220** (`a4f661a`) landed `SpogTenants::{census, + over_census, block_of, tenants_in_block, tenant_mask, attended_mask, + allocation, unattended, merge_in_claim_order}` plus a new `alpha_focus` + module; MedCare-rs **#621** (`9f9b7be`) landed the consumer on top of it. + Three consequences for this spec, each measured on the merged trees rather + than inferred from the PR bodies: + - **D-SPG-5 is PARTIAL, not queued.** Leg (i) — the `domain_rung` squat — + is retired: `attention::WatchedRows` now holds a `SpogTenants` from + `over_census` and claims at the caller's rung, and `origin/main` carries + `domain_rung`/`rung_for` only inside a historical doc comment. Legs + (ii)–(iv) are untouched, and the count is on the board: 16 bare + `AlphaOverlay` references in `backreference`, 15 in `medcare-nodesoa::alpha` + (whose two writers still have zero callers outside their own module), plus + two sites this spec never named (`graph_feed`, `medcare-soa::patient`). + **⊘ later the same day:** (ii)–(iv) landed (`e722dd1`, PR #622); the two + unnamed sites are the remaining count, to be read before being counted as + F9 scope. + - **D-SPG-8 is SHIPPED, ahead of its position in the order.** Its gate is met + at the production call site, not at a test-local one, and the test's own + doc records the disable run that caught the first draft asserting against + a provider it had built itself. + - **The contract now carries a cross of its own.** #1220's + `AlphaFocus::{cell, matrix, unlooked, rung_reach}` is scalar `and`/`and_not` + over the two masks, so F5 — which rules on the *SIMD/ternlog* cross — is + unchanged by it. But `cell` and `unlooked` now exist in two repos, and + which one a consumer should reach for was recorded here as an OPEN operator + question. **⊘ later the same day — answered, not by preference but by + ownership** (operator: *"lance-graph owns the agnostic thinking"*): the + cell and `unlooked` are agnostic and belong upstream; a consumer CALLS + `AlphaFocus`, never carries a second copy. The only thing still open is + where an ndarray-backed SIMD/ternlog mask algebra may live given the BBB + barrier (planner forbidden in the customer binary, contract zero-dep). + See the F5 row in §2 and + `E-LANCE-GRAPH-OWNS-THE-AGNOSTIC-THINKING-CONSUMERS-BIND-DOMAIN-1`. diff --git a/crates/lance-graph-contract/src/alpha.rs b/crates/lance-graph-contract/src/alpha.rs index 467c27661..fb3012747 100644 --- a/crates/lance-graph-contract/src/alpha.rs +++ b/crates/lance-graph-contract/src/alpha.rs @@ -345,6 +345,42 @@ impl AlphaMask { pub fn materialize_ordinals(&self) -> Vec { (0..self.len).filter(|&o| self.contains(o)).collect() } + + /// The packed words, one bit per ordinal, tail bits beyond `len` zero. + /// + /// A BORROW, not a materializer: the words are the mask. This is the seam + /// a crate with SIMD (ndarray's `mask_ternlog_assign` takes `&[u64]`) reads + /// through; the contract itself stays zero-dep. + #[must_use] + pub fn words(&self) -> &[u64] { + &self.words + } + + /// Rebuild a mask from words produced outside the contract (an `eq_*_to_mask` + /// sweep, a ternlog result) over an allocation of `len` addresses. + /// + /// Refuses, in every build, a word count that does not match `len` + /// (`words.len() != len.div_ceil(64)` is a caller mixing allocations — the + /// same law `zip` enforces). Tail bits at and past `len` are CLEARED, never + /// trusted: a sweep that wrote the phantom tail would otherwise invent up to + /// 63 addresses the spine never had (the [`Self::not`] rule, applied at the + /// boundary). + #[must_use] + pub fn from_words(words: Box<[u64]>, len: u32) -> Self { + assert_eq!( + words.len(), + (len as usize).div_ceil(64), + "word count does not match the allocation length" + ); + let mut words = words; + let tail = u64::from(len % 64); + if tail != 0 { + if let Some(last) = words.last_mut() { + *last &= (1u64 << tail) - 1; + } + } + Self { words, len } + } } /// The **address space** of an overlay, derived from a base spine. @@ -786,6 +822,48 @@ mod tests { let _ = wide.and(&narrow); } + /// `from_words`'s own release-mode length guard — mirrors `zip`'s: a word + /// count that does not match `len.div_ceil(64)` is a caller mixing + /// allocations, refused loudly rather than folded into a wrong answer. + #[test] + #[should_panic(expected = "word count does not match the allocation length")] + fn from_words_refuses_a_word_count_that_does_not_match_the_length() { + // len=200 needs 4 words (200.div_ceil(64) == 4); 3 is short. + let _ = AlphaMask::from_words(vec![0u64; 3].into_boxed_slice(), 200); + } + + /// The can-stay-silent half: a genuine word count for a non-multiple-of-64 + /// length round-trips exactly through `words()` / `from_words`. + #[test] + fn from_words_round_trips_words_for_a_length_that_is_not_a_multiple_of_64() { + let mut m = AlphaMask::empty(200); + m.set(7); + m.set(130); + m.set(199); + + assert_eq!(m.words().len(), 4, "200.div_ceil(64) == 4 words"); + let rebuilt = AlphaMask::from_words(m.words().to_vec().into_boxed_slice(), m.len()); + assert_eq!(rebuilt, m, "from_words(m.words(), m.len()) must round-trip"); + } + + /// Tail bits past `len` are CLEARED, never trusted — a sweep that wrote the + /// phantom tail would otherwise invent addresses the spine never had. + #[test] + fn from_words_clears_phantom_tail_bits() { + // len=200 -> 200 % 64 == 8, so only the low 8 bits of the 4th word are + // real; the rest of that word plus everything past it is phantom. + let m = AlphaMask::from_words(vec![u64::MAX; 4].into_boxed_slice(), 200); + + assert_eq!(m.count(), 200, "count must exclude the phantom tail bits"); + assert!(m.contains(199), "the last real ordinal must survive"); + assert!(!m.contains(200), "ordinal 200 is past len and must be gone"); + assert_eq!( + m.words()[3], + (1u64 << 8) - 1, + "200 % 64 == 8; only the low 8 bits of the tail word are real" + ); + } + /// The silence half of the guard above — equal lengths must still work, /// including the `len % 64 != 0` case where the tail word is partial. #[test] diff --git a/crates/lance-graph-contract/src/spog_tenants.rs b/crates/lance-graph-contract/src/spog_tenants.rs index 9a00a8cbe..84585c5e2 100644 --- a/crates/lance-graph-contract/src/spog_tenants.rs +++ b/crates/lance-graph-contract/src/spog_tenants.rs @@ -344,6 +344,53 @@ impl<'a> SpogTenants<'a> { } out } + + /// The merged saccade as canonical rows — the SAME shape [`AlphaOverlay`] + /// writes and [`crate::alpha_tunnel::AlphaTunnel::merged_rows`] returns, + /// so a tenant aufstellung, a tunnel and a single overlay are ONE table to + /// any READER: the stamp is [`merge`](Self::merge)'s (globally + /// re-sequenced), the edge block stays reserved-and-zeroed, nothing else + /// is materialized. + /// + /// # NOT a write payload — do not hand this to a Lance writer + /// + /// An earlier version of this comment called it *"the row form the sealed + /// batch per cycle is built from"*. That sentence was wrong and it pointed + /// at a structural violation, so it is corrected here rather than deleted. + /// + /// The sealed batch per cycle belongs to `LanceCycleWriter` + /// (`lance-graph::graph::cycle_sink`, #911 → #912 Phase A), the **SOLE + /// application writer**: non-`Clone`, `commit_cycle(&mut self, …)`, one + /// long-lived owned `Dataset` handle. SoA owners are fire-and-forget + /// PRODUCERS that `cast` on behalf of a mailbox; they are never Lance + /// writers. And the cast payload is a DESCRIPTOR — `(mailbox, dirty + /// row-range, cycle)` — never owned delta bytes: deltas stay in the SoA + /// backing store and the sink reads them through `NodeRowPacket:: + /// as_le_bytes` at flush (zero-copy sink ruling, + /// `lance-graph-planner::batch_writer` module doc). + /// + /// So this method is a READING — a debugger/replay surface and an + /// arrow-encode source — and materializing it in order to write it is the + /// error. `sealed_version = base_version + 1` is likewise a verified + /// identity INSIDE that sink, never arithmetic a caller performs from + /// outside it. + #[must_use] + pub fn merged_rows(&self) -> Vec { + self.merge() + .into_iter() + .map(|(addr, st)| { + let mut row = NodeRow { + key: addr, + edges: crate::canonical_node::EdgeBlock::default(), + value: [0u8; 480], + }; + row.value[crate::alpha::ALPHA_STAMP_OFFSET + ..crate::alpha::ALPHA_STAMP_OFFSET + crate::alpha::ALPHA_STAMP_BYTES] + .copy_from_slice(&st.to_le_slot()); + row + }) + .collect() + } } #[cfg(test)] @@ -589,4 +636,28 @@ mod tests { assert_eq!(seqs, vec![0, 1, 2], "seq re-issued globally"); assert_eq!(t.merge(), m, "deterministic"); } + + /// `merged_rows` is `merge` in row form and nothing more: same addresses + /// in the same order, the stamp readable back from value slot 0 with the + /// GLOBAL seq (not the per-shadow one), the edge block zero. Two-sided: + /// a per-shadow seq leaking through would show as a repeated `0` here. + #[test] + fn merged_rows_is_merge_in_row_form() { + let b = base(); + let alloc = AlphaAllocation::over(&b); + let mut t = SpogTenants::over(&alloc, 5, &[0x0301, 0x0302]); + assert!(t.claim(b[4].key, 2).routed()); // 0x0302 — seq 0 in ITS shadow + assert!(t.claim(b[0].key, 3).routed()); // 0x0301 — seq 0 in ITS shadow + let merged = t.merge(); + let rows = t.merged_rows(); + assert_eq!(rows.len(), merged.len(), "one row per merged claim"); + assert_eq!(rows.len(), 2, "anti-vacuity: two shadows, two rows"); + for ((addr, st), row) in merged.iter().zip(&rows) { + assert_eq!(row.key, *addr, "the address is carried verbatim"); + assert_eq!(crate::alpha::stamp_of(row), *st, "the stamp reads back"); + assert_eq!(row.edges, EdgeBlock::default(), "edges stay reserved"); + } + let seqs: Vec = rows.iter().map(|r| crate::alpha::stamp_of(r).seq).collect(); + assert_eq!(seqs, vec![0, 1], "the GLOBAL seq, not two per-shadow zeros"); + } }