Squashed 'vendor/ruvector/' content from commit b64c2172

git-subtree-dir: vendor/ruvector
git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
This commit is contained in:
ruv
2026-02-28 14:39:40 -05:00
commit d803bfe2b1
7854 changed files with 3522914 additions and 0 deletions

View File

@@ -0,0 +1,218 @@
//! Phase 4 Transfer CRDT
//!
//! Distributed transfer-prior propagation using LWW-Map and G-Set CRDTs.
//!
//! * `publish_prior` writes a local prior (cycle = LWW timestamp).
//! * `merge_peer` merges a peer node's state (last-writer-wins).
//! * `promote_via_consensus` runs Byzantine commit before accepting a prior.
use ruvector_domain_expansion::DomainId;
use serde::{Deserialize, Serialize};
use crate::consensus::{byzantine_commit, CommitProof};
use crate::crdt::{GSet, LWWMap};
use crate::{FederationError, Result, StateUpdate};
// ─── types ────────────────────────────────────────────────────────────────────
/// Compact summary of a transfer prior for LWW replication.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransferPriorSummary {
pub src_domain: String,
pub dst_domain: String,
/// Mean reward improvement from the transfer (positive = helpful).
pub improvement: f32,
/// Confidence in the estimate (higher = more observations).
pub confidence: f32,
/// Training cycle at which this summary was captured.
pub cycle: u64,
}
// ─── TransferCrdt ─────────────────────────────────────────────────────────────
/// Distributed transfer-prior store using LWW-Map + G-Set CRDTs.
///
/// Multiple federation nodes each maintain their own `TransferCrdt`; calling
/// `merge_peer` synchronises state using last-writer-wins semantics keyed by
/// cycle count, guaranteeing eventual consistency without coordination.
pub struct TransferCrdt {
/// LWW-Map: key = `"src:dst"`, value = best known prior summary.
priors: LWWMap<String, TransferPriorSummary>,
/// G-Set: all domain IDs ever observed by this node.
domains: GSet<String>,
}
impl TransferCrdt {
pub fn new() -> Self {
Self {
priors: LWWMap::new(),
domains: GSet::new(),
}
}
/// Publish a local transfer prior.
///
/// `cycle` acts as the LWW timestamp so newer cycles always win
/// without requiring wall-clock synchronisation.
pub fn publish_prior(
&mut self,
src: &DomainId,
dst: &DomainId,
improvement: f32,
confidence: f32,
cycle: u64,
) {
let key = format!("{}:{}", src.0, dst.0);
let summary = TransferPriorSummary {
src_domain: src.0.clone(),
dst_domain: dst.0.clone(),
improvement,
confidence,
cycle,
};
self.priors.set(key, summary, cycle);
self.domains.add(src.0.clone());
self.domains.add(dst.0.clone());
}
/// Merge a peer's CRDT state into this node (idempotent, commutative).
pub fn merge_peer(&mut self, other: &TransferCrdt) {
self.priors.merge(&other.priors);
self.domains.merge(&other.domains);
}
/// Retrieve the best known prior for a domain pair (if any).
pub fn best_prior_for(&self, src: &DomainId, dst: &DomainId) -> Option<&TransferPriorSummary> {
let key = format!("{}:{}", src.0, dst.0);
self.priors.get(&key)
}
/// All domain IDs known to this node.
pub fn known_domains(&self) -> Vec<String> {
self.domains.elements().cloned().collect()
}
/// Run Byzantine consensus before promoting a prior across the federation.
///
/// Serialises the prior summary as the `StateUpdate` payload and calls the
/// PBFT-style commit protocol. Requires `peer_count + 1 >= 4` total nodes.
pub async fn promote_via_consensus(
&self,
src: &DomainId,
dst: &DomainId,
peer_count: usize,
) -> Result<CommitProof> {
let key = format!("{}:{}", src.0, dst.0);
let summary = self
.priors
.get(&key)
.ok_or_else(|| FederationError::PeerNotFound(format!("no prior for {key}")))?;
let data = serde_json::to_vec(summary)
.map_err(|e| FederationError::ReconciliationError(e.to_string()))?;
let update = StateUpdate {
update_id: key,
data,
timestamp: current_millis(),
};
byzantine_commit(update, peer_count + 1).await
}
}
impl Default for TransferCrdt {
fn default() -> Self {
Self::new()
}
}
fn current_millis() -> u64 {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_publish_and_retrieve() {
let mut crdt = TransferCrdt::new();
let src = DomainId("retrieval".to_string());
let dst = DomainId("graph".to_string());
crdt.publish_prior(&src, &dst, 0.15, 0.8, 10);
let p = crdt.best_prior_for(&src, &dst).unwrap();
assert_eq!(p.cycle, 10);
assert!((p.improvement - 0.15).abs() < 1e-5);
}
#[test]
fn test_lww_newer_wins() {
let mut node_a = TransferCrdt::new();
let mut node_b = TransferCrdt::new();
let src = DomainId("x".to_string());
let dst = DomainId("y".to_string());
node_a.publish_prior(&src, &dst, 0.1, 0.5, 5); // older cycle
node_b.publish_prior(&src, &dst, 0.2, 0.9, 10); // newer wins
node_a.merge_peer(&node_b);
let p = node_a.best_prior_for(&src, &dst).unwrap();
assert_eq!(p.cycle, 10);
assert!((p.improvement - 0.2).abs() < 1e-5);
}
#[test]
fn test_merge_idempotent() {
let mut crdt = TransferCrdt::new();
let src = DomainId("a".to_string());
let dst = DomainId("b".to_string());
crdt.publish_prior(&src, &dst, 0.3, 0.7, 5);
let snapshot = TransferCrdt::new(); // empty peer
crdt.merge_peer(&snapshot);
// Still has original data
assert!(crdt.best_prior_for(&src, &dst).is_some());
}
#[test]
fn test_gset_domain_discovery() {
let mut crdt = TransferCrdt::new();
crdt.publish_prior(
&DomainId("a".to_string()),
&DomainId("b".to_string()),
0.1,
0.5,
1,
);
crdt.publish_prior(
&DomainId("b".to_string()),
&DomainId("c".to_string()),
0.2,
0.6,
2,
);
let domains = crdt.known_domains();
assert!(domains.contains(&"a".to_string()));
assert!(domains.contains(&"b".to_string()));
assert!(domains.contains(&"c".to_string()));
}
#[tokio::test]
async fn test_promote_via_consensus() {
let mut crdt = TransferCrdt::new();
let src = DomainId("retrieval".to_string());
let dst = DomainId("graph".to_string());
crdt.publish_prior(&src, &dst, 0.3, 0.9, 20);
// 6 peers + 1 local = 7 total nodes; for n=7: f=2, threshold=5, verify=(16/3)=5 ✓
let proof = crdt.promote_via_consensus(&src, &dst, 6).await.unwrap();
assert!(proof.verify(7));
}
}