From e9cdc0539de6535f3e45f742bb2550c8c169af9e Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 17 Jun 2026 12:00:13 +0000 Subject: [PATCH 1/3] =?UTF-8?q?docs(plan):=20bindspace=E2=86=92mailbox=5Fs?= =?UTF-8?q?oa=20dependency=20map=20+=20two-path=20wiring=20preflight?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Read-based map (full reads of every critical-path consumer + two Opus inventory agents for the rest) produced BEFORE any wiring, per operator directive ("map the many dependencies before"; "two paths step by step, never delete the old before testing the new"; "CausalEdge64 is duplicated — no handwaving"). Contents: - §0 the two stores side by side; the D-MBX-A2 gap (content/topic/angle dense hot per OQ-1-resolved, sigma, temporal, expert); cycle (Vsa16kF32) = DROP. - §1 the CausalEdge64 duplication mapped exactly: causal_edge::edge (SPO baton, used everywhere) vs ndarray::hpc::causal_diff (weight-diff codec, NEVER imported into lance-graph). Both repr(transparent)/u64 → silent-corruption hazard at the u64 level. The contract's raw-u64 edges_raw() is the firewall; typed reattach only at MailboxSoA owner + p64-bridge. v2 layout drops temporal. - §2 consumer map: column hot-spots (engine_bridge + driver), the 2 bin construction sites, the 4 Arc::get_mut single-owner hazards, the singleton- bound tests, and the ALREADY-built cold side (surreal_container SurrealMailboxView impls MailboxSoaView; scheduler generic over it; the driver already holds both bindspace + mailboxes). - §3 W0–W7 delete-last wiring sequence (parity test W1, differential test W2, feature-gated engine_bridge/dispatch swap W3/W4, bins W5, tombstone W6, delete BindSpace LAST W7). - §4 dedup hazards checklist. No source wired. Branch off main (post-#516), independent of the open #515. Co-Authored-By: Claude --- ...bindspace-mailbox-soa-dependency-map-v1.md | 186 ++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 .claude/plans/bindspace-mailbox-soa-dependency-map-v1.md diff --git a/.claude/plans/bindspace-mailbox-soa-dependency-map-v1.md b/.claude/plans/bindspace-mailbox-soa-dependency-map-v1.md new file mode 100644 index 00000000..80a7dc7e --- /dev/null +++ b/.claude/plans/bindspace-mailbox-soa-dependency-map-v1.md @@ -0,0 +1,186 @@ +# bindspace → mailbox_soa — dependency map & two-path wiring preflight (v1) + +> **Status:** MAP / preflight. No source wired yet. Read-based (full reads of +> every consumer on the critical path; two Opus inventory agents for the rest). +> **Date:** 2026-06-17. +> **Operator constraints (binding):** +> 1. The BindSpace → `MailboxSoA` replacement is a **given** (decided; not re-litigated here). +> 2. **Two parallel paths, step by step** — never delete the old until the new is tested. +> ("better 2 paths step by step than one deleted and 'oops what did that do'.") +> 3. **Map dependencies before wiring/dedup.** Be careful. +> 4. **`CausalEdge64` is duplicated — handle it precisely, no handwaving.** +> **Parent plan:** `bindspace-singleton-to-mailbox-soa-v1.md` (S0–S5 staging; §8 OQ-1 RESOLVED). +> **Companion:** `soa-migration-diff-resolution-2026-06-13.md`. + +--- + +## 0. The two stores, side by side (what migrates) + +`BindSpace` (`crates/cognitive-shader-driver/src/bindspace.rs`) — the singleton, **still live**: + +| column | type | per-row | → MailboxSoA destination | status | +|---|---|---|---|---| +| `fingerprints.content` | `Box<[u64]>` (256/row) | 2 KB | **own, dense, hot** (OQ-1 RESOLVED §2.7) | **GAP** | +| `fingerprints.topic` | `Box<[u64]>` (256/row) | 2 KB | own, dense, hot | **GAP** | +| `fingerprints.angle` | `Box<[u64]>` (256/row) | 2 KB | own, dense, hot | **GAP** | +| `fingerprints.cycle` | `Box<[f32]>` (16 384/row, `Vsa16kF32`) | **64 KB** | **DROP** — transient local, never a column | n/a | +| `fingerprints.sigma` | `Box<[u8]>` (1/row) | 1 B | own `[u8; N]` (Σ-codebook ref) | **GAP** | +| `edges` | `EdgeColumn(Box<[u64]>)` (**raw u64**) | 8 B | own `[CausalEdge64; N]` (typed) | **SHIPPED** | +| `qualia` | `QualiaI4Column` | 8 B | own `[QualiaI4_16D; N]` | **SHIPPED** | +| `meta` | `MetaColumn(Box<[u32]>)` | 4 B | own `[MetaWord; N]` | **SHIPPED** | +| `temporal` | `Box<[u64]>` | 8 B | own `[u64; N]` (OQ-2 fallback; v2 edge can't carry it) | **GAP** | +| `expert` | `Box<[u16]>` | 2 B | subsume into `mailbox_id`/`w_slot`, or `[u16; N]` | **GAP** | +| `entity_type` | `Box<[u16]>` | 2 B | own `[u16; N]` | **SHIPPED** | +| `ontology` | `Option>` | shared | **stays shared** (cold Zone-2, by `&`/`Arc`) | n/a | + +`MailboxSoA` (`crates/cognitive-shader-driver/src/mailbox_soa.rs`) — the target, **already shipped (D-MBX-A1)**: +`mailbox_id`, `energy: [f32;N]`, `plasticity_counter: [u8;N]`, `last_active_cycle: [u32;N]`, +`edges: [CausalEdge64;N]`, `qualia: [QualiaI4_16D;N]`, `meta: [MetaWord;N]`, +`entity_type: [u16;N]`, `current_cycle`, `w_slot`, `threshold`, `phase` (Rubicon). +Implements `MailboxSoaView` + `MailboxSoaOwner` (contract), with the `repr(transparent)` +`edges_raw()`/`meta_raw()` zero-copy casts (const-asserted). + +**The D-MBX-A2 gap (what S1 still owes):** `content`/`topic`/`angle` (dense, hot — NOT a tiny +ref; OQ-1 resolved), `sigma`, `temporal`, `expert`. Note the content planes are **heap** +(`Box<[u64]>` of `N*256`, like BindSpace) — they cannot be `[u64; N]` stack arrays and +`[u64; N*256]` is not stable; design choice is a parallel `Box<[u64]>` field or a small +`FingerprintColumns`-shaped sub-struct owned by the mailbox. + +--- + +## 1. The `CausalEdge64` duplication (precise — no handwaving) + +**Two distinct types, same name, both `#[repr(transparent)]` over `u64`, both with `pub .0`:** + +| | `causal_edge::edge::CausalEdge64` | `ndarray::hpc::causal_diff::CausalEdge64` | +|---|---|---| +| file | `crates/causal-edge/src/edge.rs:155` | `/home/user/ndarray/src/hpc/causal_diff.rs:151` | +| semantics | **SPO / thinking edge** — the EdgeColumn baton: S/P/O palette + NARS⟨f,c⟩ + Pearl 2³ + plasticity + (v2) signed inference mantissa + 6-bit W-slot | **weight-diff codec** — which transformer (block, projection) row shifted how far (L1) between two model checkpoints + verb | +| `pack` | 10 scalars | one `&WeightEdge` | +| `frequency`/`confidence` | `/255` | `/1023` | +| `truth()` | `TrustTexture` (2-bit, v2) | `NarsTruth` (f32 pair) | +| `w_slot` / `inference_mantissa` | **yes** | **no** | +| imported into lance-graph? | **yes, everywhere** | **NEVER** (0 imports; only a doc cross-ref at `edge.rs:154`) | + +**The hazard:** both are bare `u64` newtypes with public `.0` and overlapping method *names* +carrying *incompatible* semantics. A raw `u64` baton packed by one codec and unpacked by the +other round-trips with **zero compile error and zero runtime signal** — same silent-corruption +class as `I-LEGACY-API-FEATURE-GATED`. Today there is **no** dual-import site; the only vector +is `u64`-level. + +**The firewall (already in place — keep it):** +- The contract carries the edge column as **raw `u64`**: `MailboxSoaView::edges_raw() -> &[u64]` + (`soa_view.rs:46`, "kept raw so the contract stays zero-dep — `causal-edge` is not a contract + dep"). +- Typed `causal_edge::CausalEdge64` is reattached **only at two trusted boundaries**: the hot + owner `MailboxSoA` (via the const-asserted `repr(transparent)` cast in `edges_raw()`, + `mailbox_soa.rs:377`) and the conversion template `p64-bridge` (`lib.rs:19`, typed). +- The cold view (`surreal_container::SurrealMailboxView`) and `LanceVersionScheduler` stay on + the raw `u64` path and never name the type. + +**Safe rule (lock for the migration):** the *only* `CausalEdge64` that may be +`CausalEdge64(raw)`-reconstructed from an `edges_raw()` slice is +`causal_edge::edge::CausalEdge64`; the ndarray twin is **barred** from the mailbox/baton path. +Do not `use ndarray::hpc::causal_diff::CausalEdge64` anywhere in `cognitive-shader-driver`. The +contract's raw-`u64` edge column is the dedup firewall — it keeps the contract zero-dep and +confines the typed reattach to one trusted crate boundary. + +**v1/v2 layout note (relevant to `temporal`):** the protocol edge has a feature-gated layout +(`causal-edge-v2-layout`, **default-on** since 0.2.0). v2 **drops `temporal`** (bits 52-63 +reclaimed for signed mantissa / plasticity-shift / W-slot / truth-lens / spare; `set_temporal` +is a no-op). ⇒ the BindSpace `temporal` column **cannot** fold into the v2 edge — the `[u64; N]` +fallback (OQ-2) is the correct destination. Any code touching `temporal` must obey +`I-LEGACY-API-FEATURE-GATED` (no v1 temporal accessor under v2). + +--- + +## 2. Consumer dependency map (who touches what) + +### 2a. The column hot-spots — `engine_bridge.rs` + `driver.rs` (the bulk) +- **`driver.rs` dispatch hot path** reads the singleton per cycle: `self.bindspace.fingerprints.content_row(row)` (resonance/Hamming search — the heaviest read), `self.bindspace.edges.get(row)` → `CausalEdge64(raw)`, `self.bindspace.meta.get`, `self.bindspace.qualia.row`, `self.bindspace.entity_type`, `self.bindspace.ontology()`. +- **`engine_bridge.rs`** = the re-encode membrane (S2 dissolve target): `ingest_codebook_indices(&mut BindSpace)` (writes content/meta/temporal), `dispatch_busdto`/`unbind_busdto` (with-engine; cycle/qualia/meta/expert), `persist_cycle(&mut BindSpace)` (cycle/edges/meta), `write_qualia_observed`/`read_qualia_decomposed` (qualia). + +### 2b. Other BindSpace consumers (driver crate) +- `serve.rs` — `bs.fingerprints.set_content` in `encode_handler`; `qualia` read via `read_qualia_decomposed`; `bs.len`. +- `grpc.rs` — `ingest_codebook_indices`; `qualia` read; `bs.len`. +- **Everything else is comment-only or zero-coupling:** `wire.rs`, `sigma_rosetta.rs`, `cypher_bridge.rs`, `auto_style.rs` (test-only `QUALIA_DIMS`), `codec_*`, `decode_kernel.rs`, `rotation_kernel.rs`, `token_agreement.rs`, `planner_bridge.rs`. +- ⚠️ **`attention_mask.rs` / `attention_mask_actor.rs` define their OWN `AttentionMaskSoA`** — share only the `MailboxId`/`w_slot` vocabulary. **Do NOT conflate** with `MailboxSoA`. + +### 2c. Construction (allocation) sites — S3 must change +- `bin/grpc.rs:31` — `Arc::new(BindSpace::zeros(4096))` +- `bin/serve.rs:31` — `Arc::new(BindSpace::zeros(4096))` +- (`BindSpaceBuilder` is used only inside `bindspace.rs` tests.) + +### 2d. The structural ownership hazard — `Arc::get_mut` +Four sites assume single ownership of the singleton and **break under per-mailbox ownership**: +`grpc.rs:136`, `serve.rs:150`, `serve.rs:601`, `serve.rs:692` (`Arc::get_mut(&mut …bindspace)` +→ `&mut BindSpace`). Each errors if the `Arc` has >1 ref. These are the write escape hatches +the mailbox model replaces with owned `&mut MailboxSoA`. + +### 2e. Tests bound to the singleton (must keep green through both paths) +- `tests/end_to_end.rs` — `BindSpace::zeros`, `ingest_codebook_indices`, `write_qualia_17d`, `read_qualia_decomposed`, `bindspace(Arc)`, `persist_cycle`, `bs.meta.get`. +- `tests/busdto_bridge_test.rs` (with-engine) — `BindSpace::zeros` ×4, `dispatch_busdto`/`unbind_busdto`, `bs.meta.get`. + +### 2f. The cold side is ALREADY built (the second path's far end) +- `surreal_container::SurrealMailboxView` (`view.rs:159`) **already implements `MailboxSoaView`** (read-only; deliberately NOT `MailboxSoaOwner`). Reads `energy: &[f32]`, **`edges_raw: &[u64]`** (raw path — never names `CausalEdge64`), `meta_raw: &[u32]`, `entity_type: &[u16]` + scalars. The kv-lance projection (`read_via_kv_lance`) is a stub (`BlockedColdBuild`) until the surrealdb fork dep lands; the trait surface + `from_columns` are complete. +- `lance-graph::graph::scheduler::LanceVersionScheduler` is **generic over `MailboxSoaView`** (OUT-direction; "propose, not dispose"); never names `CausalEdge64` or `BindSpace`. +- `p64-bridge` maps **typed** `causal_edge::CausalEdge64` → palette (storage-ward template). + +### 2g. The two-path scaffold is ALREADY in `driver.rs` +`ShaderDriver` holds **both**: `bindspace: Arc` (live) **and** +`mailboxes: HashMap>` (`driver.rs:88`, "transitional per-mailbox +routing surface (slice A2)… purely additive… Removed at cutover (plan S3)"), with `with_mailbox` +builder + `mailbox(id)` accessor. **Dispatch still reads the singleton** — the mailboxes are +populated but not yet consumed by the hot path. This is exactly the "two paths" cradle. + +--- + +## 3. Two-path, step-by-step wiring sequence (delete-last) + +Each step keeps **both paths live** and adds tests for the new before removing anything. + +- **W0 (this doc).** Map ratified. — *here.* +- **W1 — D-MBX-A2 columns (additive, tested).** Add `content`/`topic`/`angle` (dense hot, + `Box<[u64]>`), `sigma`/`temporal`/`expert` to `MailboxSoA` + accessors + `reset_row`. + **Test:** a parity round-trip — write matched per-row values to a `BindSpace` window and a + `MailboxSoA`, assert every migrated column reads back identically (content planes included; + `cycle` deliberately absent). Deletes nothing. +- **W2 — read-parity harness on the hot path.** Add a *read shim* so a `MailboxSoaView` can + serve the columns `driver.rs` reads (content_row, edge, meta, qualia, entity_type). Run the + dispatch resonance read against BOTH the singleton and a mailbox built from the same rows; + assert identical hits/resonance. (Differential test — proves the new before any swap.) +- **W3 — `engine_bridge` onto mailbox rows (feature `mailbox-thoughtspace`).** Re-point + `ingest_codebook_indices`/`persist_cycle`/`write_qualia_*` to write a `MailboxSoA` row behind + the feature; v1 `&mut BindSpace` path stays as the default. `cycle` becomes a transient local. + Field-isolation matrix tests on the temporal/expert boundary (`I-LEGACY-API-FEATURE-GATED`). +- **W4 — driver dispatch reads the mailbox (feature-gated).** Behind the feature, the hot path + reads `mailboxes` instead of `bindspace`; the four `Arc::get_mut` write hatches become owned + `&mut MailboxSoA`. Both bins still allocate the singleton when the feature is off. +- **W5 — bins stop allocating the singleton.** `bin/{serve,grpc}.rs` build a mailbox set + (sea-star) instead of `BindSpace::zeros(4096)`, under the feature. +- **W6 — death → SPO-G + Lance tombstone-witness** (gated on `surreal_container` unblock OR the + `lance-graph-callcenter` path). The cold `SurrealMailboxView` is the read end. +- **W7 — delete `BindSpace` + the `cycle` plane; remove the feature gate.** Only after W1–W6 are + green and the singleton has no remaining readers (the two tests in 2e migrated to mailbox). + +**Guardrails baked into the order:** the new path is *tested against the old* at W1 (parity) and +W2 (differential) **before** any behaviour swaps at W3/W4; the singleton is removed **last** (W7), +not first. CausalEdge64 stays typed only at the two trusted boundaries; the ndarray twin never +enters. `attention_mask*` SoA is never touched. The content planes stay hot (OQ-1). + +--- + +## 4. Dedup hazards checklist (carry into every wiring PR) + +1. **CausalEdge64:** never `use ndarray::hpc::causal_diff::CausalEdge64` in the driver; the + mailbox edge is `causal_edge::edge::CausalEdge64`; cross-boundary stays raw `u64`. +2. **`temporal` under v2:** no v1 temporal accessor; `[u64; N]` column is the home (v2 edge + dropped temporal). `I-LEGACY-API-FEATURE-GATED` field-isolation tests mandatory. +3. **`Arc::get_mut` (4 sites):** each is a single-owner assumption that must become owned + `&mut MailboxSoA` — they are the precise spots that "oops" if the store is shared. +4. **`attention_mask*`:** independent `AttentionMaskSoA`; do not fold into the migration. +5. **`cycle` plane:** never migrate; compute transiently. Dropping it is what makes the 64k–256k + hot working set fit (§2.7). +6. **Content planes stay hot:** `content`/`topic`/`angle` own dense per-row in the mailbox + (~6 KB/thought), not a 6 B ref (OQ-1 resolved). The CAM-PQ/codebook ref is the *cold* form. +7. **Singleton deleted LAST (W7):** the two singleton-bound tests (2e) migrate before deletion. From 22f5120a1009afefd4c62f3c4da3bcab21aebd76 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 17 Jun 2026 12:04:03 +0000 Subject: [PATCH 2/3] =?UTF-8?q?feat(mailbox=5Fsoa):=20W1=20=E2=80=94=20mig?= =?UTF-8?q?rate=20temporal/expert/sigma=20columns=20(additive,=20parity-te?= =?UTF-8?q?sted)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First wiring step of the bindspace→mailbox_soa arc. ADDITIVE only — the singleton BindSpace is untouched; nothing deleted (operator: "two paths step by step, never delete the old before testing the new"). - MailboxSoA gains temporal: [u64;N], expert: [u16;N], sigma: [u8;N] (the D-MBX-A2 small columns) + accessors (temporal_at/set_temporal/…) + reset_row. temporal is a standalone column NOT folded into the edge: the v2 CausalEdge64 layout dropped temporal bits (I-LEGACY-API-FEATURE-GATED). sigma is a Σ-codebook index (reference, not content; I-VSA-IDENTITIES). - test_mailbox_soa_column_parity_with_bindspace: writes matched per-row values to a BindSpace window and a MailboxSoA, asserts edges/qualia/meta/entity_type + temporal/expert/sigma read back identically. The "test the new" proof. - 13 mailbox_soa tests green, clippy clean. W1b (dense content/topic/angle hot planes — the driver's resonance read) is the next step; the cycle (Vsa16kF32) plane is never migrated (OQ-1/§2.7). Dependency map + W0–W7 sequence: bindspace-mailbox-soa-dependency-map-v1.md. Co-Authored-By: Claude --- ...bindspace-mailbox-soa-dependency-map-v1.md | 21 ++- .../src/mailbox_soa.rs | 148 ++++++++++++++++++ 2 files changed, 163 insertions(+), 6 deletions(-) diff --git a/.claude/plans/bindspace-mailbox-soa-dependency-map-v1.md b/.claude/plans/bindspace-mailbox-soa-dependency-map-v1.md index 80a7dc7e..2952caf5 100644 --- a/.claude/plans/bindspace-mailbox-soa-dependency-map-v1.md +++ b/.claude/plans/bindspace-mailbox-soa-dependency-map-v1.md @@ -139,12 +139,21 @@ populated but not yet consumed by the hot path. This is exactly the "two paths" Each step keeps **both paths live** and adds tests for the new before removing anything. -- **W0 (this doc).** Map ratified. — *here.* -- **W1 — D-MBX-A2 columns (additive, tested).** Add `content`/`topic`/`angle` (dense hot, - `Box<[u64]>`), `sigma`/`temporal`/`expert` to `MailboxSoA` + accessors + `reset_row`. - **Test:** a parity round-trip — write matched per-row values to a `BindSpace` window and a - `MailboxSoA`, assert every migrated column reads back identically (content planes included; - `cycle` deliberately absent). Deletes nothing. +- **W0 (this doc).** Map ratified. — *DONE.* +- **W1 — D-MBX-A2 small columns (additive, tested). DONE.** Added `temporal: [u64;N]`, + `expert: [u16;N]`, `sigma: [u8;N]` to `MailboxSoA` + accessors + `reset_row` + + `test_mailbox_soa_column_parity_with_bindspace` (writes matched per-row values to a + `BindSpace` window and a `MailboxSoA`, asserts `edges`/`qualia`/`meta`/`entity_type` + + `temporal`/`expert`/`sigma` read back identically). 13 mailbox_soa tests green, clippy clean. + Deletes nothing. +- **W1b — D-MBX-A2 dense identity planes (additive, tested).** Add `content`/`topic`/`angle` + (dense, hot, heap `Box<[u64]>` of `N*256`, mirroring `FingerprintColumns` minus `cycle`) to + `MailboxSoA` + a zero-copy `content_row(row)->&[u64]` accessor + parity test vs + `BindSpace.fingerprints`. **Design note:** the mailbox is otherwise all-stack `[T;N]`; the + planes MUST be heap (`N*256` u64 ≈ 2 MB at N=1024 cannot be stack, and `[u64; N*256]` is not + stable) — own them as `Box<[u64]>` fields or a small `MailboxFingerprints` sub-struct. The + `cycle` (`Vsa16kF32`) plane is NEVER added (OQ-1/§2.7). This is the hot-path-critical column + (`driver.rs` resonance read = `content_row`), so it gets its own focused step. - **W2 — read-parity harness on the hot path.** Add a *read shim* so a `MailboxSoaView` can serve the columns `driver.rs` reads (content_row, edge, meta, qualia, entity_type). Run the dispatch resonance read against BOTH the singleton and a mailbox built from the same rows; diff --git a/crates/cognitive-shader-driver/src/mailbox_soa.rs b/crates/cognitive-shader-driver/src/mailbox_soa.rs index e88df3d2..936b792b 100644 --- a/crates/cognitive-shader-driver/src/mailbox_soa.rs +++ b/crates/cognitive-shader-driver/src/mailbox_soa.rs @@ -94,6 +94,31 @@ pub struct MailboxSoA { /// The registry itself stays `Arc` (cold Zone-2, not owned here). pub entity_type: [u16; N], + // ── NEW: D-MBX-A2 migrated columns (W1 — temporal / expert / sigma) ── + /// Per-row temporal stamp (`u64`, 8 B/row). Migrated from `BindSpace.temporal`. + /// + /// Kept as a standalone column (OQ-2 fallback), NOT folded into the edge: the + /// v2 `causal_edge::CausalEdge64` layout reclaimed the old temporal bits + /// (`I-LEGACY-API-FEATURE-GATED`), so the edge cannot carry it. `current_cycle` + /// is the mailbox-level clock; this is the per-row event stamp. + pub temporal: [u64; N], + + /// Per-row expert/corpus id (`u16`, 2 B/row). Migrated from `BindSpace.expert`. + /// + /// Often subsumed by `mailbox_id` / `w_slot` (the mailbox *is* an expert), but + /// kept per-row for multi-expert mailboxes during the migration. + pub expert: [u16; N], + + /// Per-row Σ-codebook index (`u8`, 1 B/row). Migrated from + /// `BindSpace.fingerprints.sigma`. + /// + /// A *reference* (1-byte index) into the 256-entry Σ codebook owned by + /// `lance-graph-contract::sigma_propagation` — content, like the ontology and + /// the CAM-PQ codebook, stays shared/cold and is NOT copied per row + /// (`I-VSA-IDENTITIES`: indices, not content). The dense content/topic/angle + /// identity planes are a separate W1b step. + pub sigma: [u8; N], + /// Monotonic cycle stamp; advanced by `tick()`. pub current_cycle: u32, @@ -152,6 +177,10 @@ impl MailboxSoA { qualia: [QualiaI4_16D::ZERO; N], meta: [MetaWord(0); N], entity_type: [0u16; N], + // ── NEW D-MBX-A2 columns — zero-initialised (W1) ── + temporal: [0u64; N], + expert: [0u16; N], + sigma: [0u8; N], // Pre-Rubicon: every mailbox starts in deliberation. phase: KanbanColumn::Planning, } @@ -242,6 +271,10 @@ impl MailboxSoA { self.qualia[row] = QualiaI4_16D::ZERO; self.meta[row] = MetaWord(0); self.entity_type[row] = 0; + // ── NEW D-MBX-A2 columns reset (W1) ── + self.temporal[row] = 0; + self.expert[row] = 0; + self.sigma[row] = 0; } // ── Read-only inspectors ────────────────────────────────────────────────── @@ -336,6 +369,46 @@ impl MailboxSoA { pub fn set_entity_type(&mut self, row: usize, t: u16) { self.entity_type[row] = t; } + + // ── D-MBX-A2 column accessors (W1: temporal / expert / sigma) ──────────── + + /// Per-row temporal stamp for `row`. + #[inline] + pub fn temporal_at(&self, row: usize) -> u64 { + self.temporal[row] + } + + /// Set the per-row temporal stamp for `row`. (Distinct from the v2 + /// `CausalEdge64::set_temporal` no-op — this is the mailbox's standalone + /// temporal column, the legitimate home per `I-LEGACY-API-FEATURE-GATED`.) + #[inline] + pub fn set_temporal(&mut self, row: usize, t: u64) { + self.temporal[row] = t; + } + + /// Per-row expert/corpus id for `row`. + #[inline] + pub fn expert_at(&self, row: usize) -> u16 { + self.expert[row] + } + + /// Set the per-row expert/corpus id for `row`. + #[inline] + pub fn set_expert(&mut self, row: usize, e: u16) { + self.expert[row] = e; + } + + /// Per-row Σ-codebook index for `row`. + #[inline] + pub fn sigma_at(&self, row: usize) -> u8 { + self.sigma[row] + } + + /// Set the per-row Σ-codebook index for `row`. + #[inline] + pub fn set_sigma(&mut self, row: usize, s: u8) { + self.sigma[row] = s; + } } // ── Contract trait impls: MailboxSoA IS the in-RAM Rubicon owner ────────────── @@ -749,4 +822,79 @@ mod tests { "meta_raw is zero-copy (same backing pointer)" ); } + + // ── test 13: W1 column parity — MailboxSoA carries what BindSpace carries ── + + /// **The "test the new before deleting the old" proof (W1).** Write distinct + /// per-row values to a `BindSpace` window AND mirror them into a `MailboxSoA`, + /// then assert every migrated LE-contract column reads back identically: + /// `edges` / `qualia` / `meta` / `entity_type` (D-MBX-A1) plus the new + /// `temporal` / `expert` / `sigma` (D-MBX-A2, W1). This proves the mailbox is a + /// faithful carrier for these columns — it deletes nothing and touches no + /// dispatch path. The dense `content`/`topic`/`angle` identity planes are the + /// separate W1b step; the deprecated `cycle` (Vsa16kF32) plane is never migrated. + #[test] + fn test_mailbox_soa_column_parity_with_bindspace() { + use crate::bindspace::BindSpace; + use lance_graph_contract::cognitive_shader::MetaWord; + use lance_graph_contract::qualia::QualiaI4_16D; + + const N: usize = 8; + let mut bs = BindSpace::zeros(N); + let mut mb: MailboxSoA = MailboxSoA::new(1, 0, 1.0); + + for row in 0..N { + let edge = 0xABCD_0000u64 | row as u64; + let q = QualiaI4_16D::ZERO + .with(0, (row % 7) as i8) + .with(7, -((row % 8) as i8)); + let m = MetaWord::new( + (row % 12) as u8, + 1, + (row * 10) as u8, + (row * 7) as u8, + (row % 6) as u8, + ); + let etype = (100 + row) as u16; + let temporal = 0xDEAD_0000u64 | row as u64; + let expert = (200 + row) as u16; + let sigma = (row * 3) as u8; + + // BindSpace side (the singleton, source). + bs.edges.set(row, edge); + bs.qualia.set(row, q); + bs.meta.set(row, m); + bs.entity_type[row] = etype; + bs.temporal[row] = temporal; + bs.expert[row] = expert; + bs.fingerprints.write_sigma(row, sigma); + + // MailboxSoA side (the migrated owner). + mb.set_edge(row, CausalEdge64(edge)); + mb.set_qualia(row, q); + mb.set_meta(row, m); + mb.set_entity_type(row, etype); + mb.set_temporal(row, temporal); + mb.set_expert(row, expert); + mb.set_sigma(row, sigma); + } + + for row in 0..N { + assert_eq!(mb.edge(row).0, bs.edges.get(row), "edges[{row}]"); + assert_eq!(mb.qualia_at(row), bs.qualia.row(row), "qualia[{row}]"); + assert_eq!(mb.meta_at(row).0, bs.meta.get(row).0, "meta[{row}]"); + assert_eq!( + mb.entity_type_at(row), + bs.entity_type[row], + "entity_type[{row}]" + ); + assert_eq!(mb.temporal_at(row), bs.temporal[row], "temporal[{row}]"); + assert_eq!(mb.expert_at(row), bs.expert[row], "expert[{row}]"); + assert_eq!( + mb.sigma_at(row), + bs.fingerprints.sigma_at(row), + "sigma[{row}]" + ); + } + } } From fc7849c33bb574e4abffacae5bc4b6310c76079f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 17 Jun 2026 12:51:48 +0000 Subject: [PATCH 3/3] fix(#517 review): align doc A2 status markers + add reset_row A2 test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit review on #517, both valid: - doc: sigma/temporal/expert were marked **GAP** in the §0 mapping table while the W1 DONE section said shipped — contradiction. Updated the three rows to **SHIPPED (W1)** and the "remaining D-MBX-A2 gap" line to content/topic/angle only (the W1b heap planes). - test: added test_mailbox_soa_reset_row_clears_a2_columns — reset_row() must clear the new temporal/expert/sigma columns (migration-invariant regression guard). 14 mailbox_soa tests green. Also rebased onto post-#515 main (e0634167) so W1b will wire against the merged driver (which now carries the MaterializeProvenance provenance wire). Co-Authored-By: Claude --- ...bindspace-mailbox-soa-dependency-map-v1.md | 16 ++++++++-------- .../src/mailbox_soa.rs | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/.claude/plans/bindspace-mailbox-soa-dependency-map-v1.md b/.claude/plans/bindspace-mailbox-soa-dependency-map-v1.md index 2952caf5..ee03cf05 100644 --- a/.claude/plans/bindspace-mailbox-soa-dependency-map-v1.md +++ b/.claude/plans/bindspace-mailbox-soa-dependency-map-v1.md @@ -24,12 +24,12 @@ | `fingerprints.topic` | `Box<[u64]>` (256/row) | 2 KB | own, dense, hot | **GAP** | | `fingerprints.angle` | `Box<[u64]>` (256/row) | 2 KB | own, dense, hot | **GAP** | | `fingerprints.cycle` | `Box<[f32]>` (16 384/row, `Vsa16kF32`) | **64 KB** | **DROP** — transient local, never a column | n/a | -| `fingerprints.sigma` | `Box<[u8]>` (1/row) | 1 B | own `[u8; N]` (Σ-codebook ref) | **GAP** | +| `fingerprints.sigma` | `Box<[u8]>` (1/row) | 1 B | own `[u8; N]` (Σ-codebook ref) | **SHIPPED (W1)** | | `edges` | `EdgeColumn(Box<[u64]>)` (**raw u64**) | 8 B | own `[CausalEdge64; N]` (typed) | **SHIPPED** | | `qualia` | `QualiaI4Column` | 8 B | own `[QualiaI4_16D; N]` | **SHIPPED** | | `meta` | `MetaColumn(Box<[u32]>)` | 4 B | own `[MetaWord; N]` | **SHIPPED** | -| `temporal` | `Box<[u64]>` | 8 B | own `[u64; N]` (OQ-2 fallback; v2 edge can't carry it) | **GAP** | -| `expert` | `Box<[u16]>` | 2 B | subsume into `mailbox_id`/`w_slot`, or `[u16; N]` | **GAP** | +| `temporal` | `Box<[u64]>` | 8 B | own `[u64; N]` (OQ-2 fallback; v2 edge can't carry it) | **SHIPPED (W1)** | +| `expert` | `Box<[u16]>` | 2 B | subsume into `mailbox_id`/`w_slot`, or `[u16; N]` | **SHIPPED (W1)** | | `entity_type` | `Box<[u16]>` | 2 B | own `[u16; N]` | **SHIPPED** | | `ontology` | `Option>` | shared | **stays shared** (cold Zone-2, by `&`/`Arc`) | n/a | @@ -40,11 +40,11 @@ Implements `MailboxSoaView` + `MailboxSoaOwner` (contract), with the `repr(transparent)` `edges_raw()`/`meta_raw()` zero-copy casts (const-asserted). -**The D-MBX-A2 gap (what S1 still owes):** `content`/`topic`/`angle` (dense, hot — NOT a tiny -ref; OQ-1 resolved), `sigma`, `temporal`, `expert`. Note the content planes are **heap** -(`Box<[u64]>` of `N*256`, like BindSpace) — they cannot be `[u64; N]` stack arrays and -`[u64; N*256]` is not stable; design choice is a parallel `Box<[u64]>` field or a small -`FingerprintColumns`-shaped sub-struct owned by the mailbox. +**The remaining D-MBX-A2 gap (post-W1):** `content`/`topic`/`angle` (dense, hot — NOT a tiny +ref; OQ-1 resolved). `sigma`/`temporal`/`expert` shipped in **W1** (see the W-sequence below). +Note the content planes are **heap** (`Box<[u64]>` of `N*256`, like BindSpace) — they cannot be +`[u64; N]` stack arrays and `[u64; N*256]` is not stable; design choice is a parallel +`Box<[u64]>` field or a small `FingerprintColumns`-shaped sub-struct owned by the mailbox (W1b). --- diff --git a/crates/cognitive-shader-driver/src/mailbox_soa.rs b/crates/cognitive-shader-driver/src/mailbox_soa.rs index 936b792b..8ee4f043 100644 --- a/crates/cognitive-shader-driver/src/mailbox_soa.rs +++ b/crates/cognitive-shader-driver/src/mailbox_soa.rs @@ -897,4 +897,23 @@ mod tests { ); } } + + // ── test 14: reset_row clears the W1 A2 columns ────────────────────────── + + /// `reset_row()` must clear the new `temporal` / `expert` / `sigma` columns + /// (migration-invariant regression guard — a reset that forgot a new column + /// would leak stale per-row state into a reused mailbox row). + #[test] + fn test_mailbox_soa_reset_row_clears_a2_columns() { + let mut mb: MailboxSoA<4> = MailboxSoA::new(1, 0, 1.0); + mb.set_temporal(2, 123); + mb.set_expert(2, 77); + mb.set_sigma(2, 9); + + mb.reset_row(2); + + assert_eq!(mb.temporal_at(2), 0, "temporal[2] must reset to 0"); + assert_eq!(mb.expert_at(2), 0, "expert[2] must reset to 0"); + assert_eq!(mb.sigma_at(2), 0, "sigma[2] must reset to 0"); + } }