From b25aa0f189319855a096aef507215795759c68e5 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 17 Oct 2026 08:33:01 +0000 Subject: [PATCH 1/2] test(causal_diff): pin CausalEdge64 to the canonical CE64 regions (red) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_011BMJnDBAfzcuQh5HeT39Rh --- src/hpc/causal_diff.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/hpc/causal_diff.rs b/src/hpc/causal_diff.rs index e2478021..acb513c6 100644 --- a/src/hpc/causal_diff.rs +++ b/src/hpc/causal_diff.rs @@ -1571,6 +1571,39 @@ mod tests { assert_eq!(std::mem::size_of::(), 8); } + fn weight_edge(block: u32, row_idx: u32, l1_distance: u32, verb: Verb) -> WeightEdge { + WeightEdge { + tensor_name: String::new(), + row_idx, + block: Some(block), + projection: Projection::FfnGate, + layer_type: LayerType::Attention, + verb, + l1_distance, + truth: NarsTruth::new(0.72, 0.95), + } + } + + // The canonical CausalEdge64 regions (lance-graph `causal_edge::layout`): + // 0..23 S/P/O codes, 24..31 frequency, 32..39 confidence, 40..63 contract. + #[test] + fn causal_edge64_packs_the_canonical_regions() { + let w = CausalEdge64::pack(&weight_edge(17, 42, 1234, Verb::Contradicts)).0; + assert_eq!(w & 0xFF, 17, "S = block"); + assert_eq!((w >> 8) & 0xFF, 5, "P = projection code (FfnGate)"); + assert_eq!((w >> 16) & 0xFF, 2, "O = verb code (Contradicts)"); + assert_eq!((w >> 24) & 0xFF, (0.72f32 * 255.0).round() as u64, "F at 24..31"); + assert_eq!((w >> 32) & 0xFF, (0.95f32 * 255.0).round() as u64, "C at 32..39"); + assert_eq!(w >> 40, 0, "the 24-bit contract stays at its default"); + } + + #[test] + fn row_and_l1_do_not_change_the_word() { + let a = CausalEdge64::pack(&weight_edge(3, 1, 10, Verb::Becomes)); + let b = CausalEdge64::pack(&weight_edge(3, 60_000, 65_000, Verb::Becomes)); + assert_eq!(a, b, "row and L1 stay in WeightEdge, not in the word"); + } + #[test] fn test_causal_edge64_all_projections() { for (proj, val) in [ From 8a1747efc6b56cc2a6edbbb3faf11a7611e7a7a8 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 17 Oct 2026 08:35:46 +0000 Subject: [PATCH 2/2] fix(causal_diff): pack CausalEdge64 in the canonical CE64 layout The word now carries S = block, P = projection code and O = verb code in bits 0..23, NARS frequency at 24..31 and confidence at 32..39, with the contract bits 40..63 left at their default. The row index and L1 distance have no field in the canonical word; they stay in WeightEdge. row_idx() and l1_distance() keep their signatures, are deprecated and return 0. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_011BMJnDBAfzcuQh5HeT39Rh --- .claude/blackboard.md | 7 ++++++ src/hpc/causal_diff.rs | 55 +++++++++++++++++++++--------------------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/.claude/blackboard.md b/.claude/blackboard.md index 8d733430..0a6460bc 100644 --- a/.claude/blackboard.md +++ b/.claude/blackboard.md @@ -1,3 +1,10 @@ +## 2026-10-17 — `causal_diff::CausalEdge64` packs the canonical CE64 layout + +- **Change:** `CausalEdge64::pack` writes lance-graph's canonical regions (`causal_edge::layout`): S = block (0..7), P = projection code (8..15), O = verb code (16..23), NARS frequency u8 at 24..31, confidence u8 at 32..39; bits 40..63 stay 0. `block()`, `projection()`, `verb()`, `frequency()` and `confidence()` read those regions. Before, the word used its own layout (block at 58, projection at 54, verb at 52, row at 36, L1 at 20, 10-bit truth at 10/0), so the same type name meant two different words in the two repos. +- **Kept, not renamed:** `row_idx()` and `l1_distance()` keep their signatures, are `#[deprecated]` and return 0. Row index and L1 distance have no field in the canonical word and stay in `WeightEdge`. Truth precision drops from 10 to 8 bits; the round-trip test's 0.002 tolerance still holds. +- **Tests:** `causal_edge64_packs_the_canonical_regions`, `row_and_l1_do_not_change_the_word`. Disable runs, each red: block back at 58, row written into bits 40.., frequency at 10. +- **Sibling:** the jina `pack_edge` rewrite (#365) is the same restoration for `hpc::jina::causal`. Still to do: `p64_bridge::causal_edge_compat`, the `entropy_ladder` Pearl bit order and spare-bit docs, the four OGIT leaf `basinSignature` values. + ## 2026-10-14 — Haiku cards for supervised harvest and mapping - Five Haiku cards added (`harvest-scout`, `census-counter`, `quote-harvester`, `substrate-mapper`, `spec-editor`); rules in `.claude/rules/agent-model-tiers.md`, CLAUDE.md Agent Protocol item 7. No `guarded-executor`: only the orchestrator compiles here. diff --git a/src/hpc/causal_diff.rs b/src/hpc/causal_diff.rs index acb513c6..eb70bc69 100644 --- a/src/hpc/causal_diff.rs +++ b/src/hpc/causal_diff.rs @@ -131,20 +131,19 @@ pub struct WeightEdge { // CausalEdge64 — compact u64 packed edge for L1/p64 operations // ============================================================================ -/// Compact causal edge packed into a single `u64`. -/// -/// Layout (64 bits): +/// Compact causal edge packed into a single `u64`, in the canonical +/// CausalEdge64 layout (lance-graph `causal_edge::layout`): /// ```text -/// [63:58] block 6 bits → 0–63 layers -/// [57:54] projection 4 bits → Projection enum (0–9) -/// [53:52] verb 2 bits → Verb enum (0–2) -/// [51:36] row_idx 16 bits → 0–65535 rows -/// [35:20] l1_dist 16 bits → L1 distance (capped at 65535) -/// [19:10] freq 10 bits → NARS frequency × 1023 -/// [ 9: 0] conf 10 bits → NARS confidence × 1023 +/// [ 7: 0] S block (layer) +/// [15: 8] P projection code (Projection enum, 0–9) +/// [23:16] O verb code (Becomes 0, Supports 1, Contradicts 2) +/// [31:24] frequency NARS frequency × 255 +/// [39:32] confidence NARS confidence × 255 +/// [63:40] contract left at its default (0) /// ``` /// -/// Total: 6 + 4 + 2 + 16 + 16 + 10 + 10 = 64 bits. +/// The row index and L1 distance have no field in that word; they stay in +/// the [`WeightEdge`] the edge was packed from. /// /// Fits in a register. Can be used as a p64 `Palette64` row key: /// `edge.0 & palette.rows[i]` → POPCNT → attention score. @@ -156,59 +155,61 @@ impl CausalEdge64 { /// Pack a WeightEdge into a compact u64. #[inline] pub fn pack(edge: &WeightEdge) -> Self { - let block = (edge.block.unwrap_or(0) & 0x3F) as u64; + let block = (edge.block.unwrap_or(0) & 0xFF) as u64; let proj = projection_to_u4(&edge.projection) as u64; let verb = match edge.verb { Verb::Becomes => 0u64, Verb::Supports => 1, Verb::Contradicts => 2, }; - let row = (edge.row_idx as u64) & 0xFFFF; - let l1 = (edge.l1_distance.min(65535) as u64) & 0xFFFF; - let freq = ((edge.truth.frequency * 1023.0).round() as u64) & 0x3FF; - let conf = ((edge.truth.confidence * 1023.0).round() as u64) & 0x3FF; + let freq = ((edge.truth.frequency.clamp(0.0, 1.0) * 255.0).round() as u64) & 0xFF; + let conf = ((edge.truth.confidence.clamp(0.0, 1.0) * 255.0).round() as u64) & 0xFF; - Self((block << 58) | (proj << 54) | (verb << 52) | (row << 36) | (l1 << 20) | (freq << 10) | conf) + Self(block | (proj << 8) | (verb << 16) | (freq << 24) | (conf << 32)) } /// Unpack block number. #[inline] pub fn block(self) -> u32 { - ((self.0 >> 58) & 0x3F) as u32 + (self.0 & 0xFF) as u32 } /// Unpack projection type. #[inline] pub fn projection(self) -> Projection { - u4_to_projection(((self.0 >> 54) & 0xF) as u8) + u4_to_projection(((self.0 >> 8) & 0xFF) as u8) } /// Unpack verb. #[inline] pub fn verb(self) -> Verb { - match (self.0 >> 52) & 0x3 { + match (self.0 >> 16) & 0xFF { 0 => Verb::Becomes, 1 => Verb::Supports, _ => Verb::Contradicts, } } - /// Unpack row index. + /// The canonical word has no row field, so this always returns 0. Read + /// `WeightEdge::row_idx`. + #[deprecated(note = "CausalEdge64 has no row field; read WeightEdge::row_idx")] #[inline] pub fn row_idx(self) -> u32 { - ((self.0 >> 36) & 0xFFFF) as u32 + 0 } - /// Unpack L1 distance. + /// The canonical word has no L1 field, so this always returns 0. Read + /// `WeightEdge::l1_distance`. + #[deprecated(note = "CausalEdge64 has no L1 field; read WeightEdge::l1_distance")] #[inline] pub fn l1_distance(self) -> u32 { - ((self.0 >> 20) & 0xFFFF) as u32 + 0 } /// Unpack NARS frequency. #[inline] pub fn frequency(self) -> f32 { - ((self.0 >> 10) & 0x3FF) as f32 / 1023.0 + ((self.0 >> 24) & 0xFF) as f32 / 255.0 } /// Unpack NARS confidence. #[inline] pub fn confidence(self) -> f32 { - (self.0 & 0x3FF) as f32 / 1023.0 + ((self.0 >> 32) & 0xFF) as f32 / 255.0 } /// Reconstruct NarsTruth. #[inline] @@ -1562,8 +1563,6 @@ mod tests { assert_eq!(packed.block(), 17); assert_eq!(packed.projection(), Projection::Q); assert_eq!(packed.verb(), Verb::Becomes); - assert_eq!(packed.row_idx(), 42); - assert_eq!(packed.l1_distance(), 1234); assert!((packed.frequency() - 0.72).abs() < 0.002); assert!((packed.confidence() - 0.95).abs() < 0.002);