SNAPKITTYWEST's picture
push from SNAPKITTYWEST/hyperkitty-constraint-dsl
224e773 verified
Raw
History Blame Contribute Delete
10.1 kB
use hyperkitty_core::Glyph;
/// Fixed-width ABI for Rust/C-- boundary
/// All fields must be stable width across targets
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct RouteDecision {
// ABI version for safety check
pub abi_version: u32,
// State representation (0-5 = valid glyphs)
pub current_state: u8,
pub previous_state: u8,
pub next_state: u8,
pub padding_0: u8,
// Validity status
pub accepted: u32,
pub failed_gate: u32,
// Entropy (IEEE 754 f64)
pub entropy: f64,
pub entropy_ok: u32,
pub padding_1: u32,
// Reconciliation status
pub reconciliation_ok: u32,
pub route_valid: u32,
pub invariant_preserved: u32,
// Trace identity (WORM correlation ID)
pub trace_id: u64,
// Proof status
pub proof_ok: u32,
pub padding_2: u32,
}
impl RouteDecision {
pub const ABI_VERSION: u32 = 1;
pub const SIZE: usize = std::mem::size_of::<RouteDecision>();
pub fn new(
current: Glyph,
previous: Glyph,
next: Glyph,
entropy: f64,
accepted: bool,
failed_gate: u32,
entropy_ok: bool,
reconciliation_ok: bool,
route_valid: bool,
invariant_ok: bool,
trace_id: u64,
proof_ok: bool,
) -> Self {
RouteDecision {
abi_version: Self::ABI_VERSION,
current_state: current.to_byte(),
previous_state: previous.to_byte(),
next_state: next.to_byte(),
padding_0: 0,
accepted: if accepted { 1 } else { 0 },
failed_gate,
entropy,
entropy_ok: if entropy_ok { 1 } else { 0 },
padding_1: 0,
reconciliation_ok: if reconciliation_ok { 1 } else { 0 },
route_valid: if route_valid { 1 } else { 0 },
invariant_preserved: if invariant_ok { 1 } else { 0 },
trace_id,
proof_ok: if proof_ok { 1 } else { 0 },
padding_2: 0,
}
}
pub fn from_reconciliation(
decision: &crate::reconciliation::ReconciliationDecision,
) -> Self {
let next = decision.next_state.unwrap_or(Glyph::Lambda);
let failed_gate_code = match decision.failed_gate {
None => 0,
Some(crate::validity::ValidityGate::Balance) => 1,
Some(crate::validity::ValidityGate::Invariant) => 2,
Some(crate::validity::ValidityGate::Entropy) => 3,
Some(crate::validity::ValidityGate::Proof) => 4,
Some(crate::validity::ValidityGate::Reconciliation) => 5,
Some(crate::validity::ValidityGate::Route) => 6,
};
RouteDecision::new(
decision.current_state,
decision.previous_state,
next,
decision.entropy,
decision.accepted,
failed_gate_code,
decision.entropy <= crate::hyperkitty_core::MAX_ENTROPY,
decision.validity_decision.reconciliation_ok,
decision.route_valid,
decision.invariant_preserved,
decision.trace_id,
decision.validity_decision.proof_ok,
)
}
/// Validate ABI header before processing
pub fn validate_header(&self) -> Result<(), String> {
if self.abi_version != Self::ABI_VERSION {
return Err(format!(
"ABI version mismatch: expected {}, got {}",
Self::ABI_VERSION,
self.abi_version
));
}
Ok(())
}
/// Validate glyph indices are valid (0-5)
pub fn validate_glyphs(&self) -> Result<(), String> {
if self.current_state > 5 {
return Err(format!("invalid current_state: {}", self.current_state));
}
if self.previous_state > 5 {
return Err(format!("invalid previous_state: {}", self.previous_state));
}
if self.next_state > 5 {
return Err(format!("invalid next_state: {}", self.next_state));
}
Ok(())
}
/// Validate entropy is finite and within bound
pub fn validate_entropy(&self) -> Result<(), String> {
if !self.entropy.is_finite() {
return Err(format!("nonfinite entropy: {}", self.entropy));
}
if self.entropy < 0.0 {
return Err(format!("negative entropy: {}", self.entropy));
}
if self.entropy > crate::hyperkitty_core::MAX_ENTROPY {
return Err(format!("entropy exceeds bound: {}", self.entropy));
}
Ok(())
}
/// Full validation before C-- commits
pub fn validate_all(&self) -> Result<(), String> {
self.validate_header()?;
self.validate_glyphs()?;
self.validate_entropy()?;
// Consistency check: accepted requires all gates OK
if self.accepted == 1 {
if self.entropy_ok == 0 {
return Err("accepted but entropy_ok=0".to_string());
}
if self.reconciliation_ok == 0 {
return Err("accepted but reconciliation_ok=0".to_string());
}
if self.route_valid == 0 {
return Err("accepted but route_valid=0".to_string());
}
if self.proof_ok == 0 {
return Err("accepted but proof_ok=0".to_string());
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_route_decision_abi_size() {
// Ensure fixed width for ABI safety
assert_eq!(RouteDecision::SIZE, 88);
}
#[test]
fn test_route_decision_construction() {
let decision = RouteDecision::new(
Glyph::Pi,
Glyph::Gamma,
Glyph::Delta,
0.15,
true,
0,
true,
true,
true,
true,
42,
true,
);
assert_eq!(decision.abi_version, RouteDecision::ABI_VERSION);
assert_eq!(decision.accepted, 1);
assert_eq!(decision.entropy, 0.15);
}
#[test]
fn test_route_decision_validate_header_success() {
let decision = RouteDecision::new(
Glyph::Pi,
Glyph::Gamma,
Glyph::Delta,
0.15,
true,
0,
true,
true,
true,
true,
42,
true,
);
assert!(decision.validate_header().is_ok());
}
#[test]
fn test_route_decision_validate_header_fail() {
let mut decision = RouteDecision::new(
Glyph::Pi,
Glyph::Gamma,
Glyph::Delta,
0.15,
true,
0,
true,
true,
true,
true,
42,
true,
);
decision.abi_version = 999;
assert!(decision.validate_header().is_err());
}
#[test]
fn test_route_decision_validate_glyphs_success() {
let decision = RouteDecision::new(
Glyph::Pi,
Glyph::Gamma,
Glyph::Delta,
0.15,
true,
0,
true,
true,
true,
true,
42,
true,
);
assert!(decision.validate_glyphs().is_ok());
}
#[test]
fn test_route_decision_validate_glyphs_fail() {
let mut decision = RouteDecision::new(
Glyph::Pi,
Glyph::Gamma,
Glyph::Delta,
0.15,
true,
0,
true,
true,
true,
true,
42,
true,
);
decision.current_state = 99;
assert!(decision.validate_glyphs().is_err());
}
#[test]
fn test_route_decision_validate_entropy_success() {
let decision = RouteDecision::new(
Glyph::Pi,
Glyph::Gamma,
Glyph::Delta,
0.15,
true,
0,
true,
true,
true,
true,
42,
true,
);
assert!(decision.validate_entropy().is_ok());
}
#[test]
fn test_route_decision_validate_entropy_high() {
let mut decision = RouteDecision::new(
Glyph::Pi,
Glyph::Gamma,
Glyph::Delta,
0.15,
true,
0,
true,
true,
true,
true,
42,
true,
);
decision.entropy = 0.25; // Exceeds MAX_ENTROPY (0.20)
assert!(decision.validate_entropy().is_err());
}
#[test]
fn test_route_decision_validate_entropy_nonfinite() {
let mut decision = RouteDecision::new(
Glyph::Pi,
Glyph::Gamma,
Glyph::Delta,
0.15,
true,
0,
true,
true,
true,
true,
42,
true,
);
decision.entropy = f64::NAN;
assert!(decision.validate_entropy().is_err());
}
#[test]
fn test_route_decision_validate_all_success() {
let decision = RouteDecision::new(
Glyph::Pi,
Glyph::Gamma,
Glyph::Delta,
0.15,
true,
0,
true,
true,
true,
true,
42,
true,
);
assert!(decision.validate_all().is_ok());
}
#[test]
fn test_route_decision_validate_all_consistency_fail() {
let decision = RouteDecision::new(
Glyph::Pi,
Glyph::Gamma,
Glyph::Delta,
0.15,
true, // accepted=1
0,
false, // entropy_ok=0 (contradiction!)
true,
true,
true,
42,
true,
);
assert!(decision.validate_all().is_err());
}
}