core/core-rs/src/vault.rs
Shay 92ea9ee6f5 feat(vault): lock Delta-CRDT reference contract (ADR-0180 -> Accepted, gate G1)
Establishes the canonical Delta-CRDT reference contract so a future native
(Rust/Zig) backend is gate-G1-eligible under ADR-0196 — the ZC-0 'contract
pinning' slice. No Zig code; ZC-1+ remains gated at G2.

- vault/crdt.py: canonical Python reference (ArenaEntry, Delta, LocalArena,
  merge_kernel, canonical_bytes, delta_hash). Pure content law — content-
  addressed by IEEE-754 bits then provenance; no normalization, no versor
  closure, no global Vault writes.
- ZC-0 contract tests (semilattice C-1..C-5; content ordering / signed-zero /
  NaN bit-addressing; C-7 no-global-write) — all failable (mutation-checked:
  no-dedup breaks C3/C5, arrival-order breaks C1).
- Golden fixture corpus (tests/fixtures/crdt/) regenerated deterministically
  from the reference; single source of truth also emits the Rust expected hex.
- core-rs: Delta::canonical_bytes + test_crdt_hash_parity.rs proving Rust
  produces byte-identical canonical_bytes to the Python reference.
- ADR-0180 -> Accepted: locked contract, byte layout, obligation map, and the
  explicit boundary that no Zig is authorized.

Verification: ZC-0 21 passed, Rust arena+parity 16 passed, architectural
invariants 40 passed, smoke 67 passed. Serving frozen: 7/8 lane SHAs match;
the public_demo miss is a pre-existing wall-clock budget overrun (ADR-0099,
~46-48s > 30s) reproduced identically on clean main — environmental.
2026-05-31 16:25:21 -07:00

356 lines
12 KiB
Rust

//! VaultStore hot path: parallel top-k CGA inner product scan.
//!
//! Uses Rayon for data-parallel scoring across all stored versors.
//! Each worker computes the diagonal-metric CGA inner product
//! independently — no shared state, no locks. Top-k is finalised
//! with a stable sort that mirrors Python's ascending-index
//! tie-break (ADR-0019 Stage 1 + ADR-0020 first-surface parity).
//!
//! The CGA inner product in Cl(4,1) is structurally diagonal with
//! ±1 metric values, so per-versor scoring collapses to
//!
//! ```text
//! sum_i metric[i] * v[i] * q[i]
//! ```
//!
//! which is 32 multiplies + 32 adds, not the 1024-op full
//! geometric_product the reference scalar path computes. Bit-
//! identity with Python's vectorised path is preserved because
//! the serial fold order is identical (i = 0..32, left-to-right
//! accumulation) and float32 multiply/add are deterministic.
use rayon::prelude::*;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum VaultError {
#[error("CGA error during recall: {0}")]
Cga(String),
}
/// Diagonal Cl(4,1) CGA inner-product metric. Derived once at
/// build time from cga_inner(e_i, e_i) over the 32-component
/// basis. See `tests/test_vault_recall_vectorised.py` (Python
/// side) for the empirical derivation that pins this vector.
const CGA_INNER_METRIC: [f32; 32] = [
1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0,
-1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0,
-1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0,
1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0,
];
/// Per-versor diagonal-metric CGA inner product. Same arithmetic
/// order as Python's `(metric[i] * v[i]) * q[i]` serial fold —
/// bit-identical scores by construction.
#[inline(always)]
fn diagonal_score(v: &[f32; 32], q: &[f32; 32]) -> f32 {
let mut s: f32 = 0.0;
for i in 0..32 {
let t = CGA_INNER_METRIC[i] * v[i];
s += t * q[i];
}
s
}
/// Zero-copy parallel top-k recall by CGA inner product, over a
/// flat (N*32,) f32 slice viewed directly from a numpy buffer.
///
/// `versors_flat` must hold N consecutive [f32; 32] blocks in C
/// (row-major) order. No copies are made; Rayon scores straight
/// off the source slice with stride 32.
pub fn vault_recall_flat(
versors_flat: &[f32],
n: usize,
query: &[f32; 32],
top_k: usize,
) -> Result<Vec<(usize, f32)>, VaultError> {
if n == 0 {
return Ok(vec![]);
}
debug_assert_eq!(versors_flat.len(), n * 32);
let mut scores: Vec<(usize, f32)> = (0..n)
.into_par_iter()
.map(|i| {
let v = &versors_flat[i * 32..(i + 1) * 32];
let mut s: f32 = 0.0;
for j in 0..32 {
let t = CGA_INNER_METRIC[j] * v[j];
s += t * query[j];
}
(i, s)
})
.collect();
let k = top_k.min(scores.len());
if k < scores.len() {
scores.select_nth_unstable_by(k.saturating_sub(1), |a, b| {
b.1.partial_cmp(&a.1)
.unwrap_or(std::cmp::Ordering::Equal)
.then(a.0.cmp(&b.0))
});
scores.truncate(k);
}
scores.sort_by(|a, b| {
b.1.partial_cmp(&a.1)
.unwrap_or(std::cmp::Ordering::Equal)
.then(a.0.cmp(&b.0))
});
Ok(scores)
}
/// Parallel top-k recall by CGA inner product.
///
/// versors: slice of [f32; 32] stored versors
/// query: [f32; 32] query versor
/// top_k: number of results to return
///
/// Returns Vec<(index, score)> sorted descending by score, with
/// ascending-index tie-break. Thread-safe: Rayon spawns workers
/// per chunk, no locks required.
pub fn vault_recall_raw(
versors: &[[f32; 32]],
query: &[f32; 32],
top_k: usize,
) -> Result<Vec<(usize, f32)>, VaultError> {
if versors.is_empty() {
return Ok(vec![]);
}
// Score all versors in parallel via the diagonal kernel.
let mut scores: Vec<(usize, f32)> = versors
.par_iter()
.enumerate()
.map(|(i, v)| (i, diagonal_score(v, query)))
.collect();
// Stable top-k order: descending score, ascending index on
// ties (mirrors Python list.sort with key=lambda x: -x[1] on
// an enumerated list).
let k = top_k.min(scores.len());
if k < scores.len() {
scores.select_nth_unstable_by(k.saturating_sub(1), |a, b| {
b.1.partial_cmp(&a.1)
.unwrap_or(std::cmp::Ordering::Equal)
.then(a.0.cmp(&b.0))
});
scores.truncate(k);
}
scores.sort_by(|a, b| {
b.1.partial_cmp(&a.1)
.unwrap_or(std::cmp::Ordering::Equal)
.then(a.0.cmp(&b.0))
});
Ok(scores)
}
/// Batch reproject: null-project all versors in parallel.
/// Returns new Vec of reprojected versors.
pub fn vault_reproject_parallel(versors: &[[f32; 32]]) -> Vec<[f32; 32]> {
use crate::cga::null_project_raw;
versors.par_iter().map(|v| null_project_raw(v)).collect()
}
// ===========================================================================
// Delta-CRDT substrate (ADR-0180 §2.1 / §2.2)
// ===========================================================================
//
// `LocalArena` is the thread-local, share-nothing write cache each modality
// adapter accumulates into (ADR-0180 §2.1). It never touches global state, so
// it needs no locks: concurrency safety comes from each thread owning its own
// arena, not from synchronisation.
//
// `SemilatticeDelta` is the join-semilattice contract (ADR-0180 §2.2): the
// merge of deltas is commutative, associative, and idempotent. The Merge
// Kernel folds deltas into a single content-addressed, deduplicated, totally
// ordered set — the order-invariant state the Vault consumes. Ordering is by
// content (the versor's IEEE-754 bit pattern + provenance bytes), never
// arrival order, per the §2.2 content-addressed-tiebreak amendment
// (2026-05-29). That is precisely what makes
// `hash(Sequential_Ingest) == hash(Concurrent_CRDT_Ingest)` (§4.3) reachable:
// the merged state cannot depend on the order deltas arrived in.
//
// This is the pure-CPU Rust substrate (§1.5.5); no MLX/UMA handshake and no
// Python binding land here — those are downstream (ADR-0180 §4.1 item 2, and
// ADR-0181 PR-5 respectively).
use std::cmp::Ordering;
/// One write accumulated into an arena: a Cl(4,1) versor plus opaque
/// provenance bytes. Provenance is part of the content key, so two writes of
/// the same versor under different provenance are distinct semilattice
/// elements (both retained); two byte-identical writes collapse (the
/// idempotence leg of the join semilattice, ADR-0180 §2.2).
#[derive(Clone, Debug)]
pub struct ArenaEntry {
pub versor: [f32; 32],
pub provenance: Vec<u8>,
}
impl ArenaEntry {
pub fn new(versor: [f32; 32], provenance: Vec<u8>) -> Self {
Self { versor, provenance }
}
}
/// Total, arrival-independent content order over arena entries.
///
/// f32 has no total `Ord` (NaN is unordered), so we compare the raw IEEE-754
/// bit patterns — which is exactly what "content-addressed" means here:
/// byte-identical versors sort together and deduplicate, and `-0.0`/`+0.0`
/// (distinct bytes) are treated as distinct content, as a byte-addressed merge
/// requires. Ties on the versor fall through to the provenance bytes, giving a
/// total order (ADR-0180 §2.2 amendment, mirroring ADR-0181 §2.2's merge key).
fn content_cmp(a: &ArenaEntry, b: &ArenaEntry) -> Ordering {
for i in 0..32 {
let o = a.versor[i].to_bits().cmp(&b.versor[i].to_bits());
if o != Ordering::Equal {
return o;
}
}
a.provenance.cmp(&b.provenance)
}
#[inline]
fn content_eq(a: &ArenaEntry, b: &ArenaEntry) -> bool {
content_cmp(a, b) == Ordering::Equal
}
/// A snapshot of newly-ingested entries (ADR-0180 §2.2). A `Delta` is always
/// held in content-addressed order with byte-identical duplicates removed, so
/// it is a canonical join-semilattice element regardless of the order its
/// entries were inserted in.
#[derive(Clone, Debug, Default)]
pub struct Delta {
entries: Vec<ArenaEntry>,
}
impl Delta {
/// Canonicalise an arbitrary entry list into a Delta: sort by content,
/// drop byte-identical duplicates. `sort_by` is stable, but the dedup key
/// is the *whole* content, so stability is immaterial to the result.
pub fn from_entries(mut entries: Vec<ArenaEntry>) -> Self {
entries.sort_by(content_cmp);
entries.dedup_by(|a, b| content_eq(a, b));
Self { entries }
}
pub fn entries(&self) -> &[ArenaEntry] {
&self.entries
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
/// Canonical little-endian serialization of this Delta — the cross-language
/// G1 contract (ADR-0180 / ADR-0196). Byte-for-byte identical to the
/// Python reference `vault.crdt.canonical_bytes`; parity is pinned by
/// `tests/test_crdt_hash_parity.rs`. Layout:
///
/// ```text
/// u64 entry_count
/// per entry (canonical order):
/// 32 x f32 versor components (IEEE-754, little-endian, 4 bytes each)
/// u64 provenance_length
/// bytes provenance
/// ```
pub fn canonical_bytes(&self) -> Vec<u8> {
let mut out = Vec::new();
out.extend_from_slice(&(self.entries.len() as u64).to_le_bytes());
for entry in &self.entries {
for component in &entry.versor {
out.extend_from_slice(&component.to_le_bytes());
}
out.extend_from_slice(&(entry.provenance.len() as u64).to_le_bytes());
out.extend_from_slice(&entry.provenance);
}
out
}
}
/// The join-semilattice contract for Delta-CRDT state (ADR-0180 §2.2).
///
/// Implementors must satisfy, with content-addressed equality (`content_cmp`):
/// * commutativity `a.join(b) == b.join(a)`
/// * associativity `a.join(b).join(c) == a.join(b.join(c))`
/// * idempotence `a.join(a) == a`
///
/// These are exercised as failable tests in `tests/test_arena.rs`; if `join`
/// ever orders by arrival instead of content, or stops deduplicating, those
/// tests fail loudly (CLAUDE.md §Schema-Defined Proof Obligations).
pub trait SemilatticeDelta: Sized {
fn join(&self, other: &Self) -> Self;
}
impl SemilatticeDelta for Delta {
fn join(&self, other: &Self) -> Self {
let mut merged =
Vec::with_capacity(self.entries.len() + other.entries.len());
merged.extend_from_slice(&self.entries);
merged.extend_from_slice(&other.entries);
Delta::from_entries(merged)
}
}
/// Thread-local, share-nothing write cache for one modality adapter
/// (ADR-0180 §2.1). Adapters push entries here lock-free; nothing is ever
/// written to global state from an arena. `snapshot` emits the order-invariant
/// `Delta` the Merge Kernel folds.
#[derive(Clone, Debug, Default)]
pub struct LocalArena {
entries: Vec<ArenaEntry>,
}
impl LocalArena {
pub fn new() -> Self {
Self {
entries: Vec::new(),
}
}
/// Lock-free local write. Push order is irrelevant: `snapshot`
/// canonicalises into content-addressed order.
pub fn push(&mut self, versor: [f32; 32], provenance: Vec<u8>) {
self.entries.push(ArenaEntry::new(versor, provenance));
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
/// Emit a canonical Delta of everything accumulated so far. Does not drain
/// the arena — flush/GC policy is the Merge Kernel's concern, not the
/// arena's.
pub fn snapshot(&self) -> Delta {
Delta::from_entries(self.entries.clone())
}
}
/// The Merge Kernel (ADR-0180 §2.2): fold a batch of deltas into a single
/// content-addressed, deduplicated, totally ordered entry set. The result is
/// invariant under any permutation of `deltas` (commutativity + associativity)
/// and under duplicate deltas (idempotence) — the property §4.3's
/// `hash(Sequential) == hash(Concurrent)` proof rides on.
///
/// Implemented as a single canonicalisation of the union rather than a
/// `fold(join)` chain; `tests/test_arena.rs` pins that the two are
/// content-equal, so the cheap path can never silently diverge from the
/// semilattice fold it stands in for.
pub fn merge_kernel(deltas: &[Delta]) -> Delta {
let mut all = Vec::new();
for d in deltas {
all.extend_from_slice(&d.entries);
}
Delta::from_entries(all)
}