stemedb/crates/stemedb-core/src/lib.rs
jordan 152df4b0b4 docs: Mark Phase 2.4, 2.5, 2.6 as complete in roadmap
- 2.4 Visual Hash Query: hamming_distance, visual_near/threshold implemented
- 2.5 Vector Field: N/A (Phase 3 work, scaffolding correct)
- 2.6 E2E Integration Test: e2e_pipeline.rs with 5 comprehensive tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 13:33:03 -07:00

257 lines
9.0 KiB
Rust

//! Core logic and types for Episteme (StemeDB).
//!
//! This crate defines the fundamental data structures like `Assertion`,
//! `SignatureEntry`, and the core traits for the knowledge graph.
/// Zero-copy serialization utilities.
pub mod serde;
/// Shared test helpers (builders, factories) for the workspace.
pub mod testing;
/// Core data types for StemeDB assertions and signatures.
pub mod types;
/// A simple hello world function for testing the core crate.
pub fn hello_world() -> String {
"Hello from Episteme Core!".to_string()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{
Assertion, Epoch, LifecycleStage, ObjectValue, SignatureEntry, SourceClass, Supersession,
SupersessionType, Vote,
};
use rkyv::check_archived_root;
use rkyv::ser::serializers::AllocSerializer;
use rkyv::ser::Serializer;
use rkyv::Deserialize;
#[test]
fn test_hello_world() {
assert_eq!(hello_world(), "Hello from Episteme Core!");
}
#[test]
fn test_assertion_serialization_roundtrip() {
let assertion = Assertion {
subject: "Tesla_Inc".to_string(),
predicate: "has_revenue".to_string(),
object: ObjectValue::Number(96.7),
parent_hash: None,
source_hash: [0u8; 32],
source_class: SourceClass::Clinical,
visual_hash: Some([1u8; 8]),
epoch: Some([2u8; 32]),
lifecycle: LifecycleStage::Approved,
signatures: vec![SignatureEntry {
agent_id: [2u8; 32],
signature: [3u8; 64],
timestamp: 123456789,
}],
confidence: 0.95,
timestamp: 123456789,
vector: Some(vec![0.1, 0.2, 0.3]),
};
// Serialize
let mut serializer = AllocSerializer::<4096>::default();
serializer.serialize_value(&assertion).expect("Failed to serialize");
let bytes = serializer.into_serializer().into_inner();
// Validate
let archived = check_archived_root::<Assertion>(&bytes)
.expect("Failed to validate archived assertion");
// Deserialize
let deserialized: Assertion =
archived.deserialize(&mut rkyv::Infallible).expect("Failed to deserialize");
assert_eq!(assertion, deserialized);
assert_eq!(deserialized.subject, "Tesla_Inc");
assert_eq!(deserialized.visual_hash, Some([1u8; 8]));
assert_eq!(deserialized.epoch, Some([2u8; 32]));
assert_eq!(deserialized.lifecycle, LifecycleStage::Approved);
}
#[test]
fn test_lifecycle_stage_serialization_roundtrip() {
// Test all lifecycle stages survive serialization
let stages = [
LifecycleStage::Proposed,
LifecycleStage::UnderReview,
LifecycleStage::Approved,
LifecycleStage::Deprecated,
LifecycleStage::Rejected,
];
for stage in stages {
let assertion = Assertion {
subject: "test".to_string(),
predicate: "lifecycle_test".to_string(),
object: ObjectValue::Text(format!("{:?}", stage)),
parent_hash: None,
source_hash: [0u8; 32],
source_class: SourceClass::Expert,
visual_hash: None,
epoch: None,
lifecycle: stage,
signatures: vec![],
confidence: 1.0,
timestamp: 0,
vector: None,
};
let mut serializer = AllocSerializer::<4096>::default();
serializer.serialize_value(&assertion).expect("Failed to serialize");
let bytes = serializer.into_serializer().into_inner();
let archived = check_archived_root::<Assertion>(&bytes)
.expect("Failed to validate archived assertion");
let deserialized: Assertion =
archived.deserialize(&mut rkyv::Infallible).expect("Failed to deserialize");
assert_eq!(
deserialized.lifecycle, stage,
"Lifecycle stage {:?} should survive round-trip",
stage
);
}
}
#[test]
fn test_lifecycle_stage_default() {
// Verify that Default gives us Proposed (the safe default)
assert_eq!(LifecycleStage::default(), LifecycleStage::Proposed);
}
#[test]
fn test_epoch_serialization_roundtrip() {
let epoch = Epoch {
id: [1u8; 32],
name: "Newtonian Physics".to_string(),
supersedes: Some([0u8; 32]),
supersession_type: Some(SupersessionType::Refinement),
start_timestamp: 1000,
end_timestamp: None,
};
// Serialize
let mut serializer = AllocSerializer::<4096>::default();
serializer.serialize_value(&epoch).expect("Failed to serialize");
let bytes = serializer.into_serializer().into_inner();
// Validate
let archived =
check_archived_root::<Epoch>(&bytes).expect("Failed to validate archived epoch");
// Deserialize
let deserialized: Epoch =
archived.deserialize(&mut rkyv::Infallible).expect("Failed to deserialize");
assert_eq!(epoch, deserialized);
assert_eq!(deserialized.name, "Newtonian Physics");
assert_eq!(deserialized.supersession_type, Some(SupersessionType::Refinement));
}
#[test]
fn test_supersession_serialization_roundtrip() {
let supersession = Supersession {
target_hash: [1u8; 32],
supersession_type: SupersessionType::Invalidate,
reason: "Proposal treated as approved. See incident INC-2024-001".to_string(),
new_hash: Some([2u8; 32]),
timestamp: 1704067200,
agent_id: [3u8; 32],
signature: [4u8; 64],
};
// Serialize
let mut serializer = AllocSerializer::<4096>::default();
serializer.serialize_value(&supersession).expect("Failed to serialize");
let bytes = serializer.into_serializer().into_inner();
// Validate
let archived = check_archived_root::<Supersession>(&bytes)
.expect("Failed to validate archived supersession");
// Deserialize
let deserialized: Supersession =
archived.deserialize(&mut rkyv::Infallible).expect("Failed to deserialize");
assert_eq!(supersession, deserialized);
assert_eq!(deserialized.target_hash, [1u8; 32]);
assert_eq!(deserialized.supersession_type, SupersessionType::Invalidate);
assert_eq!(deserialized.reason, "Proposal treated as approved. See incident INC-2024-001");
assert_eq!(deserialized.new_hash, Some([2u8; 32]));
}
#[test]
fn test_supersession_type_all_variants() {
// Test all supersession types survive serialization
let types = [
SupersessionType::Invalidate,
SupersessionType::Temporal,
SupersessionType::Refinement,
SupersessionType::RequiresReview,
SupersessionType::Additive,
];
for stype in types {
let supersession = Supersession {
target_hash: [0u8; 32],
supersession_type: stype,
reason: format!("{:?} test", stype),
new_hash: None,
timestamp: 0,
agent_id: [0u8; 32],
signature: [0u8; 64],
};
let mut serializer = AllocSerializer::<4096>::default();
serializer.serialize_value(&supersession).expect("Failed to serialize");
let bytes = serializer.into_serializer().into_inner();
let archived = check_archived_root::<Supersession>(&bytes)
.expect("Failed to validate archived supersession");
let deserialized: Supersession =
archived.deserialize(&mut rkyv::Infallible).expect("Failed to deserialize");
assert_eq!(
deserialized.supersession_type, stype,
"SupersessionType {:?} should survive round-trip",
stype
);
}
}
#[test]
fn test_vote_serialization_roundtrip() {
let vote = Vote {
assertion_hash: [1u8; 32],
agent_id: [2u8; 32],
weight: 0.8,
signature: [3u8; 64],
timestamp: 123456789,
};
// Serialize
let mut serializer = AllocSerializer::<4096>::default();
serializer.serialize_value(&vote).expect("Failed to serialize");
let bytes = serializer.into_serializer().into_inner();
// Validate
let archived =
check_archived_root::<Vote>(&bytes).expect("Failed to validate archived vote");
// Deserialize
let deserialized: Vote =
archived.deserialize(&mut rkyv::Infallible).expect("Failed to deserialize");
assert_eq!(vote, deserialized);
assert_eq!(deserialized.assertion_hash, [1u8; 32]);
assert_eq!(deserialized.weight, 0.8);
}
}
// test hook