exo-core 0.1.1

Core traits and types for EXO-AI cognitive substrate - IIT consciousness measurement and Landauer thermodynamics
Documentation
//! CoherenceRouter — ADR-029 canonical coherence gate dispatcher.
//!
//! All coherence gating in the multi-paradigm stack routes through here.
//! Backends: SheafLaplacian (prime-radiant), Quantum (ruQu), Distributed (cognitum),
//! Circadian (nervous-system), Unanimous (all must agree).
//!
//! The key insight: all backends measure the same spectral gap invariant
//! via Cheeger's inequality (λ₁/2 ≤ h(G) ≤ √(2λ₁)) from different directions.
//! This is not heuristic aggregation — it's multi-estimator spectral measurement.

use crate::witness::{CrossParadigmWitness, WitnessDecision};
use std::time::Instant;

/// Which coherence backend to use for a given gate decision.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CoherenceBackend {
    /// Prime-radiant sheaf Laplacian (mathematical proof of consistency).
    /// Best for: safety-critical paths, CPU-bound, requires formal guarantee.
    SheafLaplacian,
    /// ruQu min-cut coherence gate (quantum substrate health monitoring).
    /// Best for: quantum circuit substrates, hybrid quantum-classical paths.
    Quantum,
    /// Cognitum 256-tile fabric (distributed multi-agent contexts).
    /// Best for: federated decisions, multi-agent coordination.
    Distributed,
    /// Nervous-system circadian controller (bio-inspired, edge/WASM).
    /// Best for: battery-constrained, edge deployment, 5-50x compute savings.
    Circadian,
    /// All backends must agree — highest confidence, highest cost.
    Unanimous,
    /// Fast-path: skip coherence check (use only in proven-safe contexts).
    FastPath,
}

/// Action context passed to coherence gate.
#[derive(Debug, Clone)]
pub struct ActionContext {
    /// Human-readable action description
    pub description: &'static str,
    /// Estimated compute cost (0.0–1.0 normalized)
    pub compute_cost: f32,
    /// Whether action is reversible
    pub reversible: bool,
    /// Whether action affects shared state
    pub affects_shared_state: bool,
    /// Optional raw action id
    pub action_id: [u8; 32],
}

impl ActionContext {
    pub fn new(description: &'static str) -> Self {
        Self {
            description,
            compute_cost: 0.5,
            reversible: true,
            affects_shared_state: false,
            action_id: [0u8; 32],
        }
    }

    pub fn irreversible(mut self) -> Self {
        self.reversible = false;
        self
    }
    pub fn shared(mut self) -> Self {
        self.affects_shared_state = true;
        self
    }
    pub fn cost(mut self, c: f32) -> Self {
        self.compute_cost = c.clamp(0.0, 1.0);
        self
    }
}

/// Gate decision with supporting metrics.
#[derive(Debug, Clone)]
pub struct GateDecision {
    pub decision: WitnessDecision,
    pub lambda_min_cut: f64,
    pub sheaf_energy: Option<f64>,
    pub e_value: Option<f64>,
    pub latency_us: u64,
    pub backend_used: CoherenceBackend,
}

impl GateDecision {
    pub fn is_permit(&self) -> bool {
        self.decision == WitnessDecision::Permit
    }
}

/// Trait for coherence backend implementations.
pub trait CoherenceBackendImpl: Send + Sync {
    fn name(&self) -> &'static str;
    fn gate(&self, ctx: &ActionContext) -> GateDecision;
}

/// Default sheaf-Laplacian backend (pure Rust, no external deps).
/// Implements a simplified spectral gap estimation via random walk mixing.
pub struct SheafLaplacianBackend {
    /// Permit threshold: λ > this value → PERMIT
    pub permit_threshold: f64,
    /// Deny threshold: λ < this value → DENY
    pub deny_threshold: f64,
    /// π-scaled calibration constant for binary de-alignment
    /// (prevents resonance with low-bit quantization grids)
    pi_scale: f64,
}

impl SheafLaplacianBackend {
    pub fn new() -> Self {
        Self {
            permit_threshold: 0.15,
            deny_threshold: 0.05,
            // π⁻¹ × φ (golden ratio) — transcendental, maximally incoherent with binary grids
            pi_scale: std::f64::consts::PI.recip() * 1.618033988749895,
        }
    }

    /// Estimate spectral gap from action context metrics.
    /// In production this would query the actual prime-radiant sheaf engine.
    /// This implementation provides a principled estimate based on action risk.
    fn estimate_spectral_gap(&self, ctx: &ActionContext) -> f64 {
        let risk = ctx.compute_cost as f64
            * (if ctx.reversible { 0.5 } else { 1.0 })
            * (if ctx.affects_shared_state { 1.5 } else { 1.0 });
        // π-scaled threshold prevents binary resonance at 3/5/7-bit boundaries
        let base_gap = (1.0 - risk.min(1.0)) * self.pi_scale;
        base_gap.max(0.0).min(1.0)
    }
}

impl Default for SheafLaplacianBackend {
    fn default() -> Self {
        Self::new()
    }
}

impl CoherenceBackendImpl for SheafLaplacianBackend {
    fn name(&self) -> &'static str {
        "sheaf-laplacian"
    }

    fn gate(&self, ctx: &ActionContext) -> GateDecision {
        let t0 = Instant::now();
        let lambda = self.estimate_spectral_gap(ctx);
        let decision = if lambda > self.permit_threshold {
            WitnessDecision::Permit
        } else if lambda > self.deny_threshold {
            WitnessDecision::Defer
        } else {
            WitnessDecision::Deny
        };
        let latency_us = t0.elapsed().as_micros() as u64;
        GateDecision {
            decision,
            lambda_min_cut: lambda,
            sheaf_energy: Some(1.0 - lambda), // energy = 1 - spectral gap
            e_value: None,
            latency_us,
            backend_used: CoherenceBackend::SheafLaplacian,
        }
    }
}

/// Fast-path backend — always permits, zero cost.
/// Use only for proven-safe operations.
pub struct FastPathBackend;

impl CoherenceBackendImpl for FastPathBackend {
    fn name(&self) -> &'static str {
        "fast-path"
    }
    fn gate(&self, _ctx: &ActionContext) -> GateDecision {
        GateDecision {
            decision: WitnessDecision::Permit,
            lambda_min_cut: 1.0,
            sheaf_energy: None,
            e_value: None,
            latency_us: 0,
            backend_used: CoherenceBackend::FastPath,
        }
    }
}

/// The coherence router — dispatches to appropriate backend.
pub struct CoherenceRouter {
    sheaf: Box<dyn CoherenceBackendImpl>,
    quantum: Option<Box<dyn CoherenceBackendImpl>>,
    distributed: Option<Box<dyn CoherenceBackendImpl>>,
    circadian: Option<Box<dyn CoherenceBackendImpl>>,
    fast_path: FastPathBackend,
}

impl CoherenceRouter {
    /// Create a router with the default sheaf-Laplacian backend.
    pub fn new() -> Self {
        Self {
            sheaf: Box::new(SheafLaplacianBackend::new()),
            quantum: None,
            distributed: None,
            circadian: None,
            fast_path: FastPathBackend,
        }
    }

    /// Register an optional backend.
    pub fn with_quantum(mut self, backend: Box<dyn CoherenceBackendImpl>) -> Self {
        self.quantum = Some(backend);
        self
    }
    pub fn with_distributed(mut self, backend: Box<dyn CoherenceBackendImpl>) -> Self {
        self.distributed = Some(backend);
        self
    }
    pub fn with_circadian(mut self, backend: Box<dyn CoherenceBackendImpl>) -> Self {
        self.circadian = Some(backend);
        self
    }

    /// Gate an action using the specified backend.
    pub fn gate(&self, ctx: &ActionContext, backend: CoherenceBackend) -> GateDecision {
        match backend {
            CoherenceBackend::SheafLaplacian => self.sheaf.gate(ctx),
            CoherenceBackend::Quantum => self
                .quantum
                .as_ref()
                .map(|b| b.gate(ctx))
                .unwrap_or_else(|| self.sheaf.gate(ctx)),
            CoherenceBackend::Distributed => self
                .distributed
                .as_ref()
                .map(|b| b.gate(ctx))
                .unwrap_or_else(|| self.sheaf.gate(ctx)),
            CoherenceBackend::Circadian => self
                .circadian
                .as_ref()
                .map(|b| b.gate(ctx))
                .unwrap_or_else(|| self.sheaf.gate(ctx)),
            CoherenceBackend::FastPath => self.fast_path.gate(ctx),
            CoherenceBackend::Unanimous => {
                // All available backends must agree
                let primary = self.sheaf.gate(ctx);
                if primary.decision == WitnessDecision::Deny {
                    return primary;
                }
                // Check each optional backend — any DENY propagates
                for opt in [&self.quantum, &self.distributed, &self.circadian] {
                    if let Some(b) = opt {
                        let d = b.gate(ctx);
                        if d.decision == WitnessDecision::Deny {
                            return d;
                        }
                    }
                }
                primary
            }
        }
    }

    /// Gate with witness generation.
    pub fn gate_with_witness(
        &self,
        ctx: &ActionContext,
        backend: CoherenceBackend,
        sequence: u64,
    ) -> (GateDecision, CrossParadigmWitness) {
        let decision = self.gate(ctx, backend);
        let mut witness = CrossParadigmWitness::new(sequence, ctx.action_id, decision.decision);
        witness.sheaf_energy = decision.sheaf_energy;
        witness.lambda_min_cut = Some(decision.lambda_min_cut);
        witness.e_value = decision.e_value;
        (decision, witness)
    }

    /// Auto-select backend based on action context.
    /// Implements 3-tier routing: fast-path → sheaf → unanimous
    pub fn auto_gate(&self, ctx: &ActionContext) -> GateDecision {
        let backend = if !ctx.affects_shared_state && ctx.reversible && ctx.compute_cost < 0.1 {
            CoherenceBackend::FastPath
        } else if ctx.affects_shared_state && !ctx.reversible {
            CoherenceBackend::Unanimous
        } else {
            CoherenceBackend::SheafLaplacian
        };
        self.gate(ctx, backend)
    }
}

impl Default for CoherenceRouter {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_safe_action_permitted() {
        let router = CoherenceRouter::new();
        let ctx = ActionContext::new("read-only query").cost(0.1);
        let d = router.gate(&ctx, CoherenceBackend::SheafLaplacian);
        assert_eq!(d.decision, WitnessDecision::Permit);
        assert!(d.lambda_min_cut > 0.0);
    }

    #[test]
    fn test_high_risk_deferred() {
        let router = CoherenceRouter::new();
        let ctx = ActionContext::new("delete all vectors")
            .cost(0.95)
            .irreversible()
            .shared();
        let d = router.gate(&ctx, CoherenceBackend::SheafLaplacian);
        // High cost + irreversible + shared = low spectral gap = defer/deny
        assert!(d.decision == WitnessDecision::Defer || d.decision == WitnessDecision::Deny);
    }

    #[test]
    fn test_auto_gate_fast_path() {
        let router = CoherenceRouter::new();
        let ctx = ActionContext::new("cheap local op").cost(0.05);
        let d = router.auto_gate(&ctx);
        assert_eq!(d.backend_used, CoherenceBackend::FastPath);
        assert_eq!(d.decision, WitnessDecision::Permit);
    }

    #[test]
    fn test_gate_with_witness() {
        let router = CoherenceRouter::new();
        let ctx = ActionContext::new("moderate op").cost(0.5);
        let (decision, witness) =
            router.gate_with_witness(&ctx, CoherenceBackend::SheafLaplacian, 42);
        assert_eq!(decision.decision, witness.decision);
        assert!(witness.lambda_min_cut.is_some());
        assert_eq!(witness.sequence, 42);
    }

    #[test]
    fn test_pi_scaled_threshold_non_binary() {
        // Verify pi_scale is not a dyadic rational (would cause binary resonance)
        let backend = SheafLaplacianBackend::new();
        let scale = backend.pi_scale;
        // π⁻¹ × φ ≈ 0.5150... — verify not representable as k/2^n for small n
        // The mantissa should not be exactly representable in 3/5/7 bits
        let mantissa_3bit = (scale * 8.0).floor() / 8.0;
        assert!(
            (scale - mantissa_3bit).abs() > 1e-6,
            "Should not align with 3-bit grid"
        );
    }

    #[test]
    fn test_latency_sub_millisecond() {
        let router = CoherenceRouter::new();
        let ctx = ActionContext::new("latency test").cost(0.5);
        let d = router.gate(&ctx, CoherenceBackend::SheafLaplacian);
        assert!(
            d.latency_us < 1000,
            "Gate should complete in <1ms, got {}µs",
            d.latency_us
        );
    }
}