Skip to content

Commit 7c7575d

Browse files
committed
feat: add unified 2x8192 CogRecord schema module
Single source of truth for the CogRecord layout shared across all 3 runtimes. Provides RECORD_BYTES=2048, field descriptors with byte offsets, and const field lookup. crewai-rust and n8n-rs re-export this via `contract::schema` when compiled with the `ladybug` feature. https://claude.ai/code/session_01NocTBszN8LoR9Vrc7Tn4oF
1 parent d4f2391 commit 7c7575d

2 files changed

Lines changed: 272 additions & 0 deletions

File tree

crates/ladybug-contract/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
//! | [`delegation`] | `DelegationRequest` / `DelegationResponse` |
3030
//! | [`wire`] | Binary wire protocol: `CogPacket` (8+8/4096 command packets, no JSON) |
3131
//! | [`legacy`] | V1 JSON backward compatibility (external wire format only) |
32+
//! | [`schema`] | Unified 2×8192 CogRecord schema constants and field descriptors |
3233
3334
pub mod container;
3435
pub mod geometry;
@@ -47,6 +48,7 @@ pub mod temporal;
4748
pub mod delegation;
4849
pub mod legacy;
4950
pub mod wire;
51+
pub mod schema;
5052

5153
// === Convenience re-exports ===
5254
pub use container::{Container, CONTAINER_BITS, CONTAINER_BYTES, CONTAINER_WORDS};
Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
//! Unified CogRecord Schema — the canonical 2×8192-bit record layout.
2+
//!
3+
//! This module is the **single source of truth** for the CogRecord schema
4+
//! shared across ladybug-rs, crewai-rust, and n8n-rs when compiled together.
5+
//!
6+
//! ```text
7+
//! CogRecord = [Container; 2] = 2 KB = 16,384 bits
8+
//! ┌─────────────────────────────────────────────────────────────┐
9+
//! │ meta (8,192 bits = 1 KB) │
10+
//! │ ├── identity: words[0..8] dn_addr, container_count │
11+
//! │ ├── nars: words[8..16] frequency, confidence │
12+
//! │ ├── edges: words[16..48] adjacency bitfield │
13+
//! │ ├── rung/rl: words[48..64] consciousness rung, RL │
14+
//! │ ├── qualia: words[64..72] 8 affect channels │
15+
//! │ └── repr: words[72..128] geometry-specific fields │
16+
//! ├─────────────────────────────────────────────────────────────┤
17+
//! │ content (8,192 bits = 1 KB) │
18+
//! │ └── searchable fingerprint (Hamming / SIMD / XOR-bind) │
19+
//! └─────────────────────────────────────────────────────────────┘
20+
//! = 2 KB = 1 DN tree node = 1 Redis value = 1 Fingerprint
21+
//! ```
22+
//!
23+
//! # Usage from Other Runtimes
24+
//!
25+
//! ```rust,no_run
26+
//! // In crewai-rust or n8n-rs (with `ladybug` feature):
27+
//! use ladybug_contract::schema;
28+
//!
29+
//! assert_eq!(schema::RECORD_BYTES, 2048);
30+
//! assert_eq!(schema::RECORD_CONTAINERS, 2);
31+
//! assert_eq!(schema::CONTAINER_BITS_EACH, 8192);
32+
//! ```
33+
34+
use crate::container::{CONTAINER_BITS, CONTAINER_BYTES, CONTAINER_WORDS};
35+
36+
// =============================================================================
37+
// RECORD SCHEMA CONSTANTS
38+
// =============================================================================
39+
40+
/// Total bits in a CogRecord: 2 × 8,192 = 16,384.
41+
pub const RECORD_BITS: usize = 2 * CONTAINER_BITS;
42+
43+
/// Total bytes in a CogRecord: 2 × 1,024 = 2,048.
44+
pub const RECORD_BYTES: usize = 2 * CONTAINER_BYTES;
45+
46+
/// Total u64 words in a CogRecord: 2 × 128 = 256.
47+
pub const RECORD_WORDS: usize = 2 * CONTAINER_WORDS;
48+
49+
/// Number of containers in a CogRecord (always 2: meta + content).
50+
pub const RECORD_CONTAINERS: usize = 2;
51+
52+
/// Bits per container: 8,192.
53+
pub const CONTAINER_BITS_EACH: usize = CONTAINER_BITS;
54+
55+
/// Bytes per container: 1,024.
56+
pub const CONTAINER_BYTES_EACH: usize = CONTAINER_BYTES;
57+
58+
/// Words per container: 128.
59+
pub const CONTAINER_WORDS_EACH: usize = CONTAINER_WORDS;
60+
61+
// =============================================================================
62+
// METADATA FIELD LAYOUT (word offsets within meta container)
63+
// =============================================================================
64+
65+
/// Word range: identity (dn_addr, container_count, geometry).
66+
pub const META_IDENTITY: (usize, usize) = (0, 8);
67+
68+
/// Word range: NARS truth values (frequency, confidence, expectation).
69+
pub const META_NARS: (usize, usize) = (8, 16);
70+
71+
/// Word range: edge adjacency bitfield (2048-bit neighborhood).
72+
pub const META_EDGES: (usize, usize) = (16, 48);
73+
74+
/// Word range: consciousness rung + RL state.
75+
pub const META_RUNG_RL: (usize, usize) = (48, 64);
76+
77+
/// Word range: qualia channels (8 affect dimensions × 8 bytes).
78+
pub const META_QUALIA: (usize, usize) = (64, 72);
79+
80+
/// Word range: representation / geometry-specific fields.
81+
pub const META_REPR: (usize, usize) = (72, 128);
82+
83+
// =============================================================================
84+
// SCHEMA FIELD DESCRIPTORS
85+
// =============================================================================
86+
87+
/// A descriptor for a field in the CogRecord schema.
88+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89+
pub struct FieldDescriptor {
90+
/// Human-readable field name.
91+
pub name: &'static str,
92+
/// Container index (0 = meta, 1 = content).
93+
pub container: u8,
94+
/// Start word offset within the container.
95+
pub word_start: usize,
96+
/// End word offset (exclusive).
97+
pub word_end: usize,
98+
/// Bit width of the field (word_count × 64).
99+
pub bits: usize,
100+
}
101+
102+
impl FieldDescriptor {
103+
/// Number of u64 words this field occupies.
104+
pub const fn word_count(&self) -> usize {
105+
self.word_end - self.word_start
106+
}
107+
108+
/// Byte offset from the start of the CogRecord.
109+
pub const fn byte_offset(&self) -> usize {
110+
(self.container as usize) * CONTAINER_BYTES + self.word_start * 8
111+
}
112+
113+
/// Byte length of this field.
114+
pub const fn byte_length(&self) -> usize {
115+
self.word_count() * 8
116+
}
117+
}
118+
119+
/// The complete CogRecord field layout.
120+
pub const FIELDS: &[FieldDescriptor] = &[
121+
FieldDescriptor {
122+
name: "identity",
123+
container: 0,
124+
word_start: META_IDENTITY.0,
125+
word_end: META_IDENTITY.1,
126+
bits: (META_IDENTITY.1 - META_IDENTITY.0) * 64,
127+
},
128+
FieldDescriptor {
129+
name: "nars",
130+
container: 0,
131+
word_start: META_NARS.0,
132+
word_end: META_NARS.1,
133+
bits: (META_NARS.1 - META_NARS.0) * 64,
134+
},
135+
FieldDescriptor {
136+
name: "edges",
137+
container: 0,
138+
word_start: META_EDGES.0,
139+
word_end: META_EDGES.1,
140+
bits: (META_EDGES.1 - META_EDGES.0) * 64,
141+
},
142+
FieldDescriptor {
143+
name: "rung_rl",
144+
container: 0,
145+
word_start: META_RUNG_RL.0,
146+
word_end: META_RUNG_RL.1,
147+
bits: (META_RUNG_RL.1 - META_RUNG_RL.0) * 64,
148+
},
149+
FieldDescriptor {
150+
name: "qualia",
151+
container: 0,
152+
word_start: META_QUALIA.0,
153+
word_end: META_QUALIA.1,
154+
bits: (META_QUALIA.1 - META_QUALIA.0) * 64,
155+
},
156+
FieldDescriptor {
157+
name: "repr",
158+
container: 0,
159+
word_start: META_REPR.0,
160+
word_end: META_REPR.1,
161+
bits: (META_REPR.1 - META_REPR.0) * 64,
162+
},
163+
FieldDescriptor {
164+
name: "content",
165+
container: 1,
166+
word_start: 0,
167+
word_end: CONTAINER_WORDS,
168+
bits: CONTAINER_BITS,
169+
},
170+
];
171+
172+
/// Look up a field descriptor by name.
173+
pub const fn field(name: &str) -> Option<&'static FieldDescriptor> {
174+
let mut i = 0;
175+
while i < FIELDS.len() {
176+
if const_str_eq(FIELDS[i].name, name) {
177+
return Some(&FIELDS[i]);
178+
}
179+
i += 1;
180+
}
181+
None
182+
}
183+
184+
/// Const-compatible string equality.
185+
const fn const_str_eq(a: &str, b: &str) -> bool {
186+
let a = a.as_bytes();
187+
let b = b.as_bytes();
188+
if a.len() != b.len() {
189+
return false;
190+
}
191+
let mut i = 0;
192+
while i < a.len() {
193+
if a[i] != b[i] {
194+
return false;
195+
}
196+
i += 1;
197+
}
198+
true
199+
}
200+
201+
// =============================================================================
202+
// TESTS
203+
// =============================================================================
204+
205+
#[cfg(test)]
206+
mod tests {
207+
use super::*;
208+
209+
#[test]
210+
fn test_record_constants() {
211+
assert_eq!(RECORD_BITS, 16_384);
212+
assert_eq!(RECORD_BYTES, 2048);
213+
assert_eq!(RECORD_WORDS, 256);
214+
assert_eq!(RECORD_CONTAINERS, 2);
215+
assert_eq!(CONTAINER_BITS_EACH, 8192);
216+
assert_eq!(CONTAINER_BYTES_EACH, 1024);
217+
assert_eq!(CONTAINER_WORDS_EACH, 128);
218+
}
219+
220+
#[test]
221+
fn test_meta_fields_cover_all_words() {
222+
// Meta container is words 0..128. Verify fields are contiguous.
223+
assert_eq!(META_IDENTITY.0, 0);
224+
assert_eq!(META_IDENTITY.1, META_NARS.0);
225+
assert_eq!(META_NARS.1, META_EDGES.0);
226+
assert_eq!(META_EDGES.1, META_RUNG_RL.0);
227+
assert_eq!(META_RUNG_RL.1, META_QUALIA.0);
228+
assert_eq!(META_QUALIA.1, META_REPR.0);
229+
assert_eq!(META_REPR.1, CONTAINER_WORDS);
230+
}
231+
232+
#[test]
233+
fn test_field_descriptors() {
234+
assert_eq!(FIELDS.len(), 7); // 6 meta fields + 1 content
235+
assert_eq!(FIELDS[0].name, "identity");
236+
assert_eq!(FIELDS[6].name, "content");
237+
assert_eq!(FIELDS[6].container, 1);
238+
assert_eq!(FIELDS[6].bits, 8192);
239+
}
240+
241+
#[test]
242+
fn test_field_byte_offsets() {
243+
let identity = &FIELDS[0];
244+
assert_eq!(identity.byte_offset(), 0);
245+
assert_eq!(identity.byte_length(), 64); // 8 words × 8 bytes
246+
247+
let content = &FIELDS[6];
248+
assert_eq!(content.byte_offset(), 1024); // starts at container 1
249+
assert_eq!(content.byte_length(), 1024); // full container
250+
}
251+
252+
#[test]
253+
fn test_field_lookup() {
254+
let nars = field("nars").unwrap();
255+
assert_eq!(nars.word_start, 8);
256+
assert_eq!(nars.word_end, 16);
257+
258+
let content = field("content").unwrap();
259+
assert_eq!(content.container, 1);
260+
assert_eq!(content.bits, 8192);
261+
262+
assert!(field("nonexistent").is_none());
263+
}
264+
265+
#[test]
266+
fn test_total_field_bits() {
267+
let total: usize = FIELDS.iter().map(|f| f.bits).sum();
268+
assert_eq!(total, RECORD_BITS);
269+
}
270+
}

0 commit comments

Comments
 (0)