|
| 1 | +# bindspace → mailbox_soa — WIRING plan (W2→W7), consolidated (v1) |
| 2 | + |
| 3 | +> **Status:** CONSOLIDATED PLAN — synthesized from 5 specialist consolidation |
| 4 | +> agents (sequencing / read-shim / two-path-discipline / 0-friction-swap / |
| 5 | +> W2-differential). Pending the 3-brutal-critic pass before any wiring lands. |
| 6 | +> **Date:** 2026-06-17. |
| 7 | +> **Parent:** `bindspace-mailbox-soa-dependency-map-v1.md` (W0 map; columns W1+W1b DONE). |
| 8 | +> **Operator constraints (binding):** two paths step by step; never delete the old |
| 9 | +> before the new is tested; CausalEdge64 dedup precise; delete BindSpace LAST. |
| 10 | +> **Column migration is COMPLETE** (W1 sigma/temporal/expert; W1b content/topic/angle). |
| 11 | +> This doc is the **wiring** half — read/write re-point, feature-gate, cutover. |
| 12 | +
|
| 13 | +--- |
| 14 | + |
| 15 | +## 0. The one decision gate for the operator (W2.5 — mutability surface) |
| 16 | + |
| 17 | +The write path today is `Arc::get_mut(&mut st.driver.bindspace)` (4 sites: |
| 18 | +`grpc.rs:136`, `serve.rs:150/601/692`). An **owned `&mut MailboxSoA` is unreachable** |
| 19 | +from a shared `Arc<ShaderDriver>` whose `mailboxes` is a plain field — `&mut self.mailboxes` |
| 20 | +needs `&mut ShaderDriver`, which `Arc<ShaderDriver>` cannot give. So a mutability surface |
| 21 | +must be chosen **before any write re-point (W3/W4b)**. Two clean options: |
| 22 | + |
| 23 | +- **(A) `RwLock<HashMap<MailboxId, MailboxSoA<N>>>` on the driver** — mirrors the existing |
| 24 | + `planes: RwLock<…>` and `awareness: RwLock<…>` already on `ShaderDriver`; keeps the |
| 25 | + `Arc<ShaderDriver>`-shared topology intact; matches the data-flow rule's "interior |
| 26 | + mutability or built once." **Recommended** (lower blast radius, prior art in the same struct). |
| 27 | +- **(B) Move the mailbox set out of `Arc<ShaderDriver>` into the bin handler state** so the |
| 28 | + handler owns `&mut` directly — higher blast radius on `serve.rs`/`grpc.rs`. |
| 29 | + |
| 30 | +**This is the only genuine fork; everything else below is determined.** Default = (A). |
| 31 | + |
| 32 | +--- |
| 33 | + |
| 34 | +## 1. The read-surface decision (productive disagreement, RESOLVED) |
| 35 | + |
| 36 | +Three agents diverged, then reconciled: |
| 37 | +- **convergence:** parameterize `run` over a read trait; extend the contract `MailboxSoaView` |
| 38 | + with `qualia()`/`content_row()`; give `BindSpace` a migration-scoped impl. |
| 39 | +- **container:** do NOT extend the contract — `&dyn MailboxSoaView` adds a per-row indirect |
| 40 | + call in the hot O(n²) Hamming loop (wrong for SIMD); content/qualia on the contract |
| 41 | + over-commits the **cold** `SurrealMailboxView`; a `BindSpace: MailboxSoaView` impl is a |
| 42 | + **sentinel lie** (`mailbox_id`/`w_slot`/`phase` are nonsense for a singleton). |
| 43 | +- **two-path:** gate the **owner**, not the function body (I-LEGACY-API-FEATURE-GATED); one |
| 44 | + `dispatch()` body written against a stable read surface, the flag picks which object is handed in. |
| 45 | + |
| 46 | +**RESOLUTION — a driver-crate-local, MONOMORPHIZED read surface (no `&dyn`, no contract change):** |
| 47 | + |
| 48 | +```rust |
| 49 | +// crates/cognitive-shader-driver/src/backing.rs (new, driver-local) |
| 50 | +enum BackingStore<'a> { |
| 51 | + Singleton(&'a BindSpace), // path A — live default |
| 52 | + Mailbox(&'a MailboxSoA<1024>), // path B — feature-gated |
| 53 | +} |
| 54 | +``` |
| 55 | +exposing exactly the six-read dispatch surface, dispatching over **inherent accessors that |
| 56 | +already exist on both stores** (W1+W1b shipped them with identical signatures): |
| 57 | + |
| 58 | +| shim method | Singleton arm | Mailbox arm | notes | |
| 59 | +|---|---|---|---| |
| 60 | +| `prefilter(win, &MetaFilter) -> Vec<u32>` | `bs.meta_prefilter(win,f)` | inline sweep over `mb.meta_raw()`, **clamped to `n_rows()`** | the one *behavior* (not data) the mailbox lacks; must be byte-identical | |
| 61 | +| `content_row(row) -> &[u64]` | `bs.fingerprints.content_row` | `mb.content_row` | byte-identical stride (`[row*256..]`, `WORDS_PER_FP=256`), proven by W1b test | |
| 62 | +| `qualia_row(row) -> QualiaI4_16D` | `bs.qualia.row` | `mb.qualia_at` | Copy | |
| 63 | +| `edge_raw(row) -> u64` | `bs.edges.get` | `mb.edge(row).0` | driver reattaches `causal_edge::CausalEdge64` ONLY; ndarray twin barred | |
| 64 | +| `entity_type(row) -> u16` | `bs.entity_type[row]` | `mb.entity_type_at` | | |
| 65 | +| `len() -> usize` | `bs.len` | `mb.n_rows()` (=N) | the prefilter bound | |
| 66 | + |
| 67 | +`run()` keeps **one body**, written against `BackingStore` (or a generic `R: DriverRead` — |
| 68 | +equivalent; monomorphized either way). The feature flag selects which variant is constructed, |
| 69 | +**never** `#[cfg]`-branches inside `run`. `ontology` is **NOT** a shim method — it stays a |
| 70 | +shared `Arc<OntologyRegistry>` field on the driver, resolved against the `entity_type(row)` the |
| 71 | +shim returns. **No `lance-graph-contract` change for this migration** (a contract `qualia()` |
| 72 | +lands later, gated on the *planner* consumer, per the `soa_view.rs:71` note — not here). |
| 73 | + |
| 74 | +--- |
| 75 | + |
| 76 | +## 2. Feature-gate invariants (I-LEGACY-API-FEATURE-GATED-safe) |
| 77 | + |
| 78 | +- **I-FG-1 (single arc).** ONE `mailbox-thoughtspace` feature spanning W3→W6. No per-W |
| 79 | + sub-features (a half-migration W4-on/W3-off is a silent-divergence state nobody tests). |
| 80 | +- **I-FG-2 (gate the owner).** The flag selects which `BackingStore` variant is built; it must |
| 81 | + NOT introduce `#[cfg]` branches inside `dispatch()`/`ingest_codebook_indices`/`persist_cycle` |
| 82 | + that read different columns under the same fn name. The §1 monomorphized shim is the |
| 83 | + mechanism that makes the single flag safe. |
| 84 | +- **I-FG-3 (default OFF until W7).** `default = [...]` must NOT include `mailbox-thoughtspace` |
| 85 | + until W7. Singleton stays the default-build behaviour while both paths live; the mailbox path |
| 86 | + is opt-in and differentially tested. (Contrast `causal-edge-v2-layout` = default-ON because |
| 87 | + that migration is *finished*; this one is not.) |
| 88 | + |
| 89 | +--- |
| 90 | + |
| 91 | +## 3. Boundaries that must stay distinct (do NOT flatten early) |
| 92 | + |
| 93 | +| id | distinction held | collapses at | |
| 94 | +|---|---|---| |
| 95 | +| D-DIST-1 | singleton store ≠ mailbox store (two owners, separately allocated; mailbox OWNS, never `&mut`-borrows from the singleton — `E-CE64-MB-4`) | **W7** | |
| 96 | +| D-DIST-2 | read surface ≠ write surface (`MailboxSoaView` vs `MailboxSoaOwner`; cold side read-only) | never | |
| 97 | +| D-DIST-3 | typed `causal_edge::CausalEdge64` ≠ raw `u64` column; ndarray twin **barred**; reattach points do NOT multiply | never (firewall permanent) | |
| 98 | +| D-DIST-4 | ephemeral thoughtspace ≠ calcified shared (ontology/CAM-PQ stay by `&`/`Arc`, never per-mailbox-owned) | never | |
| 99 | +| D-DIST-5 | hot mutation ≠ cold persistence (`persisted_row` is a pointer, not a re-encode; no Arrow/JSON marshalling step) | never | |
| 100 | +| membrane | `engine_bridge` `bind/unbind_busdto`/`busdto_to_binary16k` re-encode seam — the ONE thing allowed to dissolve | re-point W3; body deleted W7 | |
| 101 | + |
| 102 | +--- |
| 103 | + |
| 104 | +## 4. Divergence hazards (the sharp risks) |
| 105 | + |
| 106 | +- **H-DW-1 (headline).** W3 re-points *writers* to the mailbox while W4 still reads the |
| 107 | + singleton → the shader reasons over stale singleton rows; the two stores **diverge silently on |
| 108 | + the same row index**. **Guard:** writers+readers flip **atomically as one gated unit** (collapse |
| 109 | + W3+W4a, or keep the feature non-default-buildable between them AND run the W2 differential on |
| 110 | + every commit in the window). |
| 111 | +- **H-DW-2.** Single-writer-per-logical-row is **absolute**. No dual-write "to keep both warm" |
| 112 | + (a rounding/quantization/`temporal`-no-op disagreement → undetectable divergence). Off → singleton |
| 113 | + sole writer; on → mailbox sole writer; never both. |
| 114 | +- **H-DW-3.** The four `Arc::get_mut(&mut bindspace)` sites convert to owned `&mut MailboxSoA` |
| 115 | + (via the W2.5 surface) in the W4b unit. A surviving `get_mut`/**`make_mut`** on `bindspace` under |
| 116 | + the feature is a migration leak — `make_mut` silently **clones the whole 4096-row singleton** |
| 117 | + = an instant second diverging store. |
| 118 | + |
| 119 | +--- |
| 120 | + |
| 121 | +## 5. The `cycle` (Vsa16kF32) plane — drop discipline |
| 122 | + |
| 123 | +Singleton keeps `write_cycle_fingerprint`/`cycle_row` until **W7**; `MailboxSoA` **never** gets a |
| 124 | +`cycle`/`set_cycle`/`cycle_row` method (compile-level assertion). Under W3: |
| 125 | +- **F-CYCLE-1** `dispatch_busdto` (`engine_bridge.rs:243`) — stop writing the column; compute the |
| 126 | + Binary16K accumulator transiently for the current resonance, discard. |
| 127 | +- **F-CYCLE-3** `persist_cycle` (`engine_bridge.rs:707`) — stop the `write_cycle_fingerprint`; |
| 128 | + keep the legitimate `edges`/`meta` column writes. |
| 129 | +- **F-CYCLE-2** `unbind_busdto` (`engine_bridge.rs:334`) — **REWRITE, do not re-point.** It reads the |
| 130 | + *persisted* cycle plane back; that plane won't exist on the mailbox. Per parent-plan §2.6 it |
| 131 | + must collapse to a column read (`edges`/`qualia`/`meta`). This is the one function that cannot be |
| 132 | + mechanically migrated. |
| 133 | +- **F-CYCLE-5** `driver.rs:288` `cycle_fp` local is the CORRECT transient model (a `[u64;256]` |
| 134 | + Binary16K fold on the stack, never a column). Footprint check: hot cost ≈ 6 KB/row |
| 135 | + (content/topic/angle), NOT ≈ 71.6 KB/row (a leak of the 64 KB cycle plane). |
| 136 | + |
| 137 | +--- |
| 138 | + |
| 139 | +## 6. The hardened W2→W7 sequence (dependency-correct) |
| 140 | + |
| 141 | +``` |
| 142 | +W2 read-parity differential harness (test-only) READY NOW additive ∥-safe |
| 143 | +W2.5 mailbox &mut surface — DECISION GATE (A: RwLock rec) READY NOW additive operator pick |
| 144 | +W3 engine_bridge mailbox writes (feat-gated) BLOCKED:W2.5 behaviour serial +cycle-reader audit +temporal field-isolation |
| 145 | +W4a dispatch READ re-point via BackingStore (feat-gated) BLOCKED:W2,W2.5 behaviour serial (W2-validated; flip atomic w/ W3 per H-DW-1) |
| 146 | +W4b Arc::get_mut → owned &mut (4 sites) BLOCKED:W2.5,W3 behaviour serial (fixes latent "multiple references" 500; never make_mut) |
| 147 | +W5 bins build mailbox set (feat-gated) BLOCKED:W4b behaviour serial +mailbox-set topology sub-gate (N, count, keying) |
| 148 | +W6 death→SPO-G+Lance tombstone HARD-BLOCKED (surrealdb #41) design ∥-only, NO code ← DECOUPLED from W7 |
| 149 | +W7 delete BindSpace + cycle plane; drop feature gate BLOCKED:W3,W4a,W4b,W5 + §2e test migration serial LAST (NOT gated on W6) |
| 150 | +``` |
| 151 | + |
| 152 | +**Per-step gates:** |
| 153 | +- **W2 — READY NOW.** Differential test only; reads the **concrete** `MailboxSoA` (not the contract |
| 154 | + view). Build the mailbox with **populated rows == BindSpace `len`** so the prefilter sweeps the |
| 155 | + same set. Assert `ShaderResonance` **bit-identical** via `f32::to_bits()` (top_k row/distance/ |
| 156 | + predicates/cycle_index + resonance bits, entropy bits, std_dev bits) + `cycle_fingerprint` + |
| 157 | + **`MaterializeProvenance`** (so a future read re-point can't silently drift #515's provenance); |
| 158 | + assert `gate` only with `entity_type == 0` rows (neutralizes the `ontology()` read W2 can't serve). |
| 159 | +- **W2.5 — operator decision (A vs B).** Default A (`RwLock`). The gate for the whole write arc. |
| 160 | +- **W3 — +2 mandatory in-PR gates:** (a) a `read_cycle_fingerprint`/`cycle_row` reader audit |
| 161 | + promoting "cycle→transient" from CONJECTURE to FINDING; (b) the `temporal` field-isolation matrix |
| 162 | + test (write temporal, assert edges/meta/sigma/expert unchanged) per I-LEGACY-API-FEATURE-GATED. |
| 163 | +- **W4a/W4b — split** (read re-point vs ownership-model change). W4a validated by the W2 harness; |
| 164 | + W4b changes handler error semantics (removes the "multiple references" 500). |
| 165 | +- **W5 — sub-gate:** define the mailbox-set builder (`N`, count, keying) before the bin move; do not |
| 166 | + invent the sea-star topology ad-hoc in `main()`. |
| 167 | +- **W6 — external blocker** (surrealdb #41); design proceeds as docs/contract-only, NO code. |
| 168 | +- **W7 — decoupled from W6.** Prereq is **zero singleton readers** + the two §2e singleton-bound |
| 169 | + tests (`end_to_end.rs`, `busdto_bridge_test.rs`) migrated to mailbox FIRST. Then delete |
| 170 | + `bindspace.rs` + the cycle plane + the feature flag in one PR. |
| 171 | + |
| 172 | +--- |
| 173 | + |
| 174 | +## 7. #515 MaterializeProvenance interaction |
| 175 | + |
| 176 | +`materialize_provenance` (now on `main`, `driver.rs`) is **provenance-only** — it consumes |
| 177 | +already-derived observables (`free_energy`, `std_dev`, `top_resonance`, `awareness_skill`, |
| 178 | +hits), reads **no** `BindSpace` directly → adds no new singleton reader and does not gate the |
| 179 | +migration. The one interaction: its inputs come from the dispatch read path W4a re-points, so |
| 180 | +**W2 must assert `MaterializeProvenance` byte-equality** across stores (§6 W2 gate). `ctx_id`/MUL |
| 181 | +still read `ontology()` from the shared `Arc` (not the mailbox). |
| 182 | + |
| 183 | +--- |
| 184 | + |
| 185 | +## 8. Acceptance checks (per the operator's "test the new before delete the old") |
| 186 | + |
| 187 | +- W2 differential: bit-identical `ShaderResonance` + `cycle_fingerprint` + `MaterializeProvenance`, |
| 188 | + singleton-backed vs mailbox-backed, same rows — run in BOTH the W3-landed/W4-pending state and |
| 189 | + the W4-complete state (H-DW-1 window). |
| 190 | +- Field-isolation matrix (W3): each migrated column write leaves all others byte-unchanged. |
| 191 | +- CausalEdge64 firewall (CI): `ndarray::hpc::causal_diff::CausalEdge64` has **zero** imports in |
| 192 | + `cognitive-shader-driver`; an `edges_raw()` reattach round-trips to the typed |
| 193 | + `causal_edge::CausalEdge64` it was packed from. |
| 194 | +- Cycle-drop (compile + bench): `MailboxSoA` exposes no `cycle*` symbol; hot footprint ≈ 6 KB/row. |
| 195 | +- W7 readiness: grep proves zero `self.bindspace.*` reads under feature-on; the two §2e tests pass |
| 196 | + on the mailbox path before deletion. |
0 commit comments