@@ -33,6 +33,11 @@ use lance_graph_contract::kanban::{ExecTarget, KanbanColumn, KanbanMove};
3333use lance_graph_contract:: qualia:: QualiaI4_16D ;
3434use lance_graph_contract:: soa_view:: { MailboxSoaOwner , MailboxSoaView } ;
3535
36+ /// Canonical named-fingerprint plane width: 256 × u64 = 16,384 bits
37+ /// (mirrors `bindspace::WORDS_PER_FP`; defined locally so the mailbox does NOT
38+ /// depend on the singleton it is migrating off of — W7 deletes BindSpace, not this).
39+ pub const WORDS_PER_FP : usize = 256 ;
40+
3641/// Spatial-temporal accumulator for per-row edge receipts.
3742///
3843/// `N` is the maximum number of neuron rows this mailbox can serve.
@@ -119,6 +124,26 @@ pub struct MailboxSoA<const N: usize> {
119124 /// identity planes are a separate W1b step.
120125 pub sigma : [ u8 ; N ] ,
121126
127+ // ── NEW: D-MBX-A2 dense identity planes (W1b) ──
128+ // The content/topic/angle Hamming identity planes stay HOT in the mailbox
129+ // (~6 KB/thought; OQ-1 RESOLVED §2.7 — NOT reduced to a tiny ref). They are
130+ // HEAP `Box<[u64]>` of `N * WORDS_PER_FP` (a `[u64; N*256]` stack array is not
131+ // expressible on stable Rust and would be ~2 MB/plane at N=1024). The
132+ // deprecated `Vsa16kF32` `cycle` plane is NEVER migrated — compute it
133+ // transiently if a step needs it.
134+ /// Per-row content identity fingerprint (`WORDS_PER_FP` u64/row). Migrated from
135+ /// `BindSpace.fingerprints.content`. This is the heaviest column and the one the
136+ /// driver's resonance search reads (`content_row`).
137+ pub content : Box < [ u64 ] > ,
138+
139+ /// Per-row topic identity plane (`WORDS_PER_FP` u64/row). Migrated from
140+ /// `BindSpace.fingerprints.topic`.
141+ pub topic : Box < [ u64 ] > ,
142+
143+ /// Per-row angle identity plane (`WORDS_PER_FP` u64/row). Migrated from
144+ /// `BindSpace.fingerprints.angle`.
145+ pub angle : Box < [ u64 ] > ,
146+
122147 /// Monotonic cycle stamp; advanced by `tick()`.
123148 pub current_cycle : u32 ,
124149
@@ -181,6 +206,10 @@ impl<const N: usize> MailboxSoA<N> {
181206 temporal : [ 0u64 ; N ] ,
182207 expert : [ 0u16 ; N ] ,
183208 sigma : [ 0u8 ; N ] ,
209+ // ── NEW D-MBX-A2 dense identity planes — heap, zero-initialised (W1b) ──
210+ content : vec ! [ 0u64 ; N * WORDS_PER_FP ] . into_boxed_slice ( ) ,
211+ topic : vec ! [ 0u64 ; N * WORDS_PER_FP ] . into_boxed_slice ( ) ,
212+ angle : vec ! [ 0u64 ; N * WORDS_PER_FP ] . into_boxed_slice ( ) ,
184213 // Pre-Rubicon: every mailbox starts in deliberation.
185214 phase : KanbanColumn :: Planning ,
186215 }
@@ -275,6 +304,12 @@ impl<const N: usize> MailboxSoA<N> {
275304 self . temporal [ row] = 0 ;
276305 self . expert [ row] = 0 ;
277306 self . sigma [ row] = 0 ;
307+ // ── NEW D-MBX-A2 dense identity planes reset (W1b) ──
308+ let lo = row * WORDS_PER_FP ;
309+ let hi = lo + WORDS_PER_FP ;
310+ self . content [ lo..hi] . fill ( 0 ) ;
311+ self . topic [ lo..hi] . fill ( 0 ) ;
312+ self . angle [ lo..hi] . fill ( 0 ) ;
278313 }
279314
280315 // ── Read-only inspectors ──────────────────────────────────────────────────
@@ -409,6 +444,61 @@ impl<const N: usize> MailboxSoA<N> {
409444 pub fn set_sigma ( & mut self , row : usize , s : u8 ) {
410445 self . sigma [ row] = s;
411446 }
447+
448+ // ── D-MBX-A2 dense identity-plane accessors (W1b) ────────────────────────
449+
450+ /// Zero-copy view of `row`'s content identity fingerprint (`WORDS_PER_FP`
451+ /// u64). This is the hot read the driver's resonance/Hamming search performs
452+ /// (the BindSpace equivalent is `FingerprintColumns::content_row`).
453+ #[ inline]
454+ pub fn content_row ( & self , row : usize ) -> & [ u64 ] {
455+ & self . content [ row * WORDS_PER_FP ..( row + 1 ) * WORDS_PER_FP ]
456+ }
457+
458+ /// Write `row`'s content identity fingerprint. Panics if `words.len() != WORDS_PER_FP`.
459+ #[ inline]
460+ pub fn set_content ( & mut self , row : usize , words : & [ u64 ] ) {
461+ assert_eq ! (
462+ words. len( ) ,
463+ WORDS_PER_FP ,
464+ "content fingerprint must be WORDS_PER_FP u64"
465+ ) ;
466+ self . content [ row * WORDS_PER_FP ..( row + 1 ) * WORDS_PER_FP ] . copy_from_slice ( words) ;
467+ }
468+
469+ /// Zero-copy view of `row`'s topic identity plane (`WORDS_PER_FP` u64).
470+ #[ inline]
471+ pub fn topic_row ( & self , row : usize ) -> & [ u64 ] {
472+ & self . topic [ row * WORDS_PER_FP ..( row + 1 ) * WORDS_PER_FP ]
473+ }
474+
475+ /// Write `row`'s topic identity plane. Panics if `words.len() != WORDS_PER_FP`.
476+ #[ inline]
477+ pub fn set_topic ( & mut self , row : usize , words : & [ u64 ] ) {
478+ assert_eq ! (
479+ words. len( ) ,
480+ WORDS_PER_FP ,
481+ "topic plane must be WORDS_PER_FP u64"
482+ ) ;
483+ self . topic [ row * WORDS_PER_FP ..( row + 1 ) * WORDS_PER_FP ] . copy_from_slice ( words) ;
484+ }
485+
486+ /// Zero-copy view of `row`'s angle identity plane (`WORDS_PER_FP` u64).
487+ #[ inline]
488+ pub fn angle_row ( & self , row : usize ) -> & [ u64 ] {
489+ & self . angle [ row * WORDS_PER_FP ..( row + 1 ) * WORDS_PER_FP ]
490+ }
491+
492+ /// Write `row`'s angle identity plane. Panics if `words.len() != WORDS_PER_FP`.
493+ #[ inline]
494+ pub fn set_angle ( & mut self , row : usize , words : & [ u64 ] ) {
495+ assert_eq ! (
496+ words. len( ) ,
497+ WORDS_PER_FP ,
498+ "angle plane must be WORDS_PER_FP u64"
499+ ) ;
500+ self . angle [ row * WORDS_PER_FP ..( row + 1 ) * WORDS_PER_FP ] . copy_from_slice ( words) ;
501+ }
412502}
413503
414504// ── Contract trait impls: MailboxSoA IS the in-RAM Rubicon owner ──────────────
@@ -916,4 +1006,93 @@ mod tests {
9161006 assert_eq ! ( mb. expert_at( 2 ) , 0 , "expert[2] must reset to 0" ) ;
9171007 assert_eq ! ( mb. sigma_at( 2 ) , 0 , "sigma[2] must reset to 0" ) ;
9181008 }
1009+
1010+ // ── test 15: W1b dense identity planes — parity with BindSpace ───────────
1011+
1012+ /// **The W1b "test the new" proof.** The content/topic/angle Hamming identity
1013+ /// planes stay hot in the mailbox (OQ-1). For `content` — the migration-critical
1014+ /// plane the driver's resonance search reads — assert byte parity against a
1015+ /// `BindSpace` window written with the same words. For `topic`/`angle`, BindSpace
1016+ /// exposes no public setter (they default zero there), so assert full round-trip
1017+ /// correctness on the mailbox. The deprecated `cycle` (Vsa16kF32) plane is never
1018+ /// migrated. Deletes nothing.
1019+ #[ test]
1020+ fn test_mailbox_soa_dense_planes_parity_with_bindspace ( ) {
1021+ use crate :: bindspace:: BindSpace ;
1022+
1023+ const N : usize = 4 ;
1024+ let mut bs = BindSpace :: zeros ( N ) ;
1025+ let mut mb: MailboxSoA < N > = MailboxSoA :: new ( 1 , 0 , 1.0 ) ;
1026+
1027+ // Distinct per-row, per-plane bit patterns so a cross-row or cross-plane
1028+ // mixup fails. Set words across the full 256-word span.
1029+ let mk = |row : usize , plane : u64 | -> [ u64 ; WORDS_PER_FP ] {
1030+ let mut w = [ 0u64 ; WORDS_PER_FP ] ;
1031+ w[ 0 ] = 0x1000_0000_0000_0000 | ( ( row as u64 ) << 8 ) | plane;
1032+ w[ row % WORDS_PER_FP ] |= 1u64 << ( row as u32 % 64 ) ;
1033+ w[ WORDS_PER_FP - 1 ] = plane. wrapping_mul ( 0x9E37_79B9 ) ^ row as u64 ;
1034+ w
1035+ } ;
1036+ for row in 0 ..N {
1037+ let ( c, t, a) = ( mk ( row, 1 ) , mk ( row, 2 ) , mk ( row, 3 ) ) ;
1038+ // content: write to BOTH (BindSpace exposes set_content) → true parity.
1039+ bs. fingerprints . set_content ( row, & c) ;
1040+ mb. set_content ( row, & c) ;
1041+ // topic/angle: mailbox-only round-trip (no public BindSpace setter).
1042+ mb. set_topic ( row, & t) ;
1043+ mb. set_angle ( row, & a) ;
1044+ }
1045+
1046+ for row in 0 ..N {
1047+ // content: byte-identical to the BindSpace plane (the hot read path).
1048+ assert_eq ! (
1049+ mb. content_row( row) ,
1050+ bs. fingerprints. content_row( row) ,
1051+ "content[{row}] plane parity vs BindSpace"
1052+ ) ;
1053+ // topic/angle: full-slice round-trip on the mailbox.
1054+ assert_eq ! (
1055+ mb. topic_row( row) ,
1056+ & mk( row, 2 ) [ ..] ,
1057+ "topic[{row}] round-trip"
1058+ ) ;
1059+ assert_eq ! (
1060+ mb. angle_row( row) ,
1061+ & mk( row, 3 ) [ ..] ,
1062+ "angle[{row}] round-trip"
1063+ ) ;
1064+ }
1065+ }
1066+
1067+ // ── test 16: reset_row clears the W1b dense planes ───────────────────────
1068+
1069+ /// `reset_row()` must zero the content/topic/angle plane spans for the row.
1070+ #[ test]
1071+ fn test_mailbox_soa_reset_row_clears_dense_planes ( ) {
1072+ const N : usize = 4 ;
1073+ let mut mb: MailboxSoA < N > = MailboxSoA :: new ( 1 , 0 , 1.0 ) ;
1074+ let mut w = [ 0u64 ; WORDS_PER_FP ] ;
1075+ w[ 0 ] = 0xDEAD_BEEF ;
1076+ w[ WORDS_PER_FP - 1 ] = 0xCAFE ;
1077+ mb. set_content ( 2 , & w) ;
1078+ mb. set_topic ( 2 , & w) ;
1079+ mb. set_angle ( 2 , & w) ;
1080+
1081+ mb. reset_row ( 2 ) ;
1082+
1083+ assert ! (
1084+ mb. content_row( 2 ) . iter( ) . all( |& x| x == 0 ) ,
1085+ "content row cleared"
1086+ ) ;
1087+ assert ! ( mb. topic_row( 2 ) . iter( ) . all( |& x| x == 0 ) , "topic row cleared" ) ;
1088+ assert ! ( mb. angle_row( 2 ) . iter( ) . all( |& x| x == 0 ) , "angle row cleared" ) ;
1089+ // A neighbouring row must be untouched by the reset (span isolation).
1090+ mb. set_content ( 3 , & w) ;
1091+ mb. reset_row ( 2 ) ;
1092+ assert_eq ! (
1093+ mb. content_row( 3 ) [ 0 ] ,
1094+ 0xDEAD_BEEF ,
1095+ "row 3 content must survive row-2 reset"
1096+ ) ;
1097+ }
9191098}
0 commit comments