Skip to main content

exo_core/
coherence_router.rs

1//! CoherenceRouter — ADR-029 canonical coherence gate dispatcher.
2//!
3//! All coherence gating in the multi-paradigm stack routes through here.
4//! Backends: SheafLaplacian (prime-radiant), Quantum (ruQu), Distributed (cognitum),
5//! Circadian (nervous-system), Unanimous (all must agree).
6//!
7//! The key insight: all backends measure the same spectral gap invariant
8//! via Cheeger's inequality (λ₁/2 ≤ h(G) ≤ √(2λ₁)) from different directions.
9//! This is not heuristic aggregation — it's multi-estimator spectral measurement.
10
11use crate::witness::{CrossParadigmWitness, WitnessDecision};
12use std::time::Instant;
13
14/// Which coherence backend to use for a given gate decision.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum CoherenceBackend {
17    /// Prime-radiant sheaf Laplacian (mathematical proof of consistency).
18    /// Best for: safety-critical paths, CPU-bound, requires formal guarantee.
19    SheafLaplacian,
20    /// ruQu min-cut coherence gate (quantum substrate health monitoring).
21    /// Best for: quantum circuit substrates, hybrid quantum-classical paths.
22    Quantum,
23    /// Cognitum 256-tile fabric (distributed multi-agent contexts).
24    /// Best for: federated decisions, multi-agent coordination.
25    Distributed,
26    /// Nervous-system circadian controller (bio-inspired, edge/WASM).
27    /// Best for: battery-constrained, edge deployment, 5-50x compute savings.
28    Circadian,
29    /// All backends must agree — highest confidence, highest cost.
30    Unanimous,
31    /// Fast-path: skip coherence check (use only in proven-safe contexts).
32    FastPath,
33}
34
35/// Action context passed to coherence gate.
36#[derive(Debug, Clone)]
37pub struct ActionContext {
38    /// Human-readable action description
39    pub description: &'static str,
40    /// Estimated compute cost (0.0–1.0 normalized)
41    pub compute_cost: f32,
42    /// Whether action is reversible
43    pub reversible: bool,
44    /// Whether action affects shared state
45    pub affects_shared_state: bool,
46    /// Optional raw action id
47    pub action_id: [u8; 32],
48}
49
50impl ActionContext {
51    pub fn new(description: &'static str) -> Self {
52        Self {
53            description,
54            compute_cost: 0.5,
55            reversible: true,
56            affects_shared_state: false,
57            action_id: [0u8; 32],
58        }
59    }
60
61    pub fn irreversible(mut self) -> Self {
62        self.reversible = false;
63        self
64    }
65    pub fn shared(mut self) -> Self {
66        self.affects_shared_state = true;
67        self
68    }
69    pub fn cost(mut self, c: f32) -> Self {
70        self.compute_cost = c.clamp(0.0, 1.0);
71        self
72    }
73}
74
75/// Gate decision with supporting metrics.
76#[derive(Debug, Clone)]
77pub struct GateDecision {
78    pub decision: WitnessDecision,
79    pub lambda_min_cut: f64,
80    pub sheaf_energy: Option<f64>,
81    pub e_value: Option<f64>,
82    pub latency_us: u64,
83    pub backend_used: CoherenceBackend,
84}
85
86impl GateDecision {
87    pub fn is_permit(&self) -> bool {
88        self.decision == WitnessDecision::Permit
89    }
90}
91
92/// Trait for coherence backend implementations.
93pub trait CoherenceBackendImpl: Send + Sync {
94    fn name(&self) -> &'static str;
95    fn gate(&self, ctx: &ActionContext) -> GateDecision;
96}
97
98/// Default sheaf-Laplacian backend (pure Rust, no external deps).
99/// Implements a simplified spectral gap estimation via random walk mixing.
100pub struct SheafLaplacianBackend {
101    /// Permit threshold: λ > this value → PERMIT
102    pub permit_threshold: f64,
103    /// Deny threshold: λ < this value → DENY
104    pub deny_threshold: f64,
105    /// π-scaled calibration constant for binary de-alignment
106    /// (prevents resonance with low-bit quantization grids)
107    pi_scale: f64,
108}
109
110impl SheafLaplacianBackend {
111    pub fn new() -> Self {
112        Self {
113            permit_threshold: 0.15,
114            deny_threshold: 0.05,
115            // π⁻¹ × φ (golden ratio) — transcendental, maximally incoherent with binary grids
116            pi_scale: std::f64::consts::PI.recip() * 1.618033988749895,
117        }
118    }
119
120    /// Estimate spectral gap from action context metrics.
121    /// In production this would query the actual prime-radiant sheaf engine.
122    /// This implementation provides a principled estimate based on action risk.
123    fn estimate_spectral_gap(&self, ctx: &ActionContext) -> f64 {
124        let risk = ctx.compute_cost as f64
125            * (if ctx.reversible { 0.5 } else { 1.0 })
126            * (if ctx.affects_shared_state { 1.5 } else { 1.0 });
127        // π-scaled threshold prevents binary resonance at 3/5/7-bit boundaries
128        let base_gap = (1.0 - risk.min(1.0)) * self.pi_scale;
129        base_gap.max(0.0).min(1.0)
130    }
131}
132
133impl Default for SheafLaplacianBackend {
134    fn default() -> Self {
135        Self::new()
136    }
137}
138
139impl CoherenceBackendImpl for SheafLaplacianBackend {
140    fn name(&self) -> &'static str {
141        "sheaf-laplacian"
142    }
143
144    fn gate(&self, ctx: &ActionContext) -> GateDecision {
145        let t0 = Instant::now();
146        let lambda = self.estimate_spectral_gap(ctx);
147        let decision = if lambda > self.permit_threshold {
148            WitnessDecision::Permit
149        } else if lambda > self.deny_threshold {
150            WitnessDecision::Defer
151        } else {
152            WitnessDecision::Deny
153        };
154        let latency_us = t0.elapsed().as_micros() as u64;
155        GateDecision {
156            decision,
157            lambda_min_cut: lambda,
158            sheaf_energy: Some(1.0 - lambda), // energy = 1 - spectral gap
159            e_value: None,
160            latency_us,
161            backend_used: CoherenceBackend::SheafLaplacian,
162        }
163    }
164}
165
166/// Fast-path backend — always permits, zero cost.
167/// Use only for proven-safe operations.
168pub struct FastPathBackend;
169
170impl CoherenceBackendImpl for FastPathBackend {
171    fn name(&self) -> &'static str {
172        "fast-path"
173    }
174    fn gate(&self, _ctx: &ActionContext) -> GateDecision {
175        GateDecision {
176            decision: WitnessDecision::Permit,
177            lambda_min_cut: 1.0,
178            sheaf_energy: None,
179            e_value: None,
180            latency_us: 0,
181            backend_used: CoherenceBackend::FastPath,
182        }
183    }
184}
185
186/// The coherence router — dispatches to appropriate backend.
187pub struct CoherenceRouter {
188    sheaf: Box<dyn CoherenceBackendImpl>,
189    quantum: Option<Box<dyn CoherenceBackendImpl>>,
190    distributed: Option<Box<dyn CoherenceBackendImpl>>,
191    circadian: Option<Box<dyn CoherenceBackendImpl>>,
192    fast_path: FastPathBackend,
193}
194
195impl CoherenceRouter {
196    /// Create a router with the default sheaf-Laplacian backend.
197    pub fn new() -> Self {
198        Self {
199            sheaf: Box::new(SheafLaplacianBackend::new()),
200            quantum: None,
201            distributed: None,
202            circadian: None,
203            fast_path: FastPathBackend,
204        }
205    }
206
207    /// Register an optional backend.
208    pub fn with_quantum(mut self, backend: Box<dyn CoherenceBackendImpl>) -> Self {
209        self.quantum = Some(backend);
210        self
211    }
212    pub fn with_distributed(mut self, backend: Box<dyn CoherenceBackendImpl>) -> Self {
213        self.distributed = Some(backend);
214        self
215    }
216    pub fn with_circadian(mut self, backend: Box<dyn CoherenceBackendImpl>) -> Self {
217        self.circadian = Some(backend);
218        self
219    }
220
221    /// Gate an action using the specified backend.
222    pub fn gate(&self, ctx: &ActionContext, backend: CoherenceBackend) -> GateDecision {
223        match backend {
224            CoherenceBackend::SheafLaplacian => self.sheaf.gate(ctx),
225            CoherenceBackend::Quantum => self
226                .quantum
227                .as_ref()
228                .map(|b| b.gate(ctx))
229                .unwrap_or_else(|| self.sheaf.gate(ctx)),
230            CoherenceBackend::Distributed => self
231                .distributed
232                .as_ref()
233                .map(|b| b.gate(ctx))
234                .unwrap_or_else(|| self.sheaf.gate(ctx)),
235            CoherenceBackend::Circadian => self
236                .circadian
237                .as_ref()
238                .map(|b| b.gate(ctx))
239                .unwrap_or_else(|| self.sheaf.gate(ctx)),
240            CoherenceBackend::FastPath => self.fast_path.gate(ctx),
241            CoherenceBackend::Unanimous => {
242                // All available backends must agree
243                let primary = self.sheaf.gate(ctx);
244                if primary.decision == WitnessDecision::Deny {
245                    return primary;
246                }
247                // Check each optional backend — any DENY propagates
248                for opt in [&self.quantum, &self.distributed, &self.circadian] {
249                    if let Some(b) = opt {
250                        let d = b.gate(ctx);
251                        if d.decision == WitnessDecision::Deny {
252                            return d;
253                        }
254                    }
255                }
256                primary
257            }
258        }
259    }
260
261    /// Gate with witness generation.
262    pub fn gate_with_witness(
263        &self,
264        ctx: &ActionContext,
265        backend: CoherenceBackend,
266        sequence: u64,
267    ) -> (GateDecision, CrossParadigmWitness) {
268        let decision = self.gate(ctx, backend);
269        let mut witness = CrossParadigmWitness::new(sequence, ctx.action_id, decision.decision);
270        witness.sheaf_energy = decision.sheaf_energy;
271        witness.lambda_min_cut = Some(decision.lambda_min_cut);
272        witness.e_value = decision.e_value;
273        (decision, witness)
274    }
275
276    /// Auto-select backend based on action context.
277    /// Implements 3-tier routing: fast-path → sheaf → unanimous
278    pub fn auto_gate(&self, ctx: &ActionContext) -> GateDecision {
279        let backend = if !ctx.affects_shared_state && ctx.reversible && ctx.compute_cost < 0.1 {
280            CoherenceBackend::FastPath
281        } else if ctx.affects_shared_state && !ctx.reversible {
282            CoherenceBackend::Unanimous
283        } else {
284            CoherenceBackend::SheafLaplacian
285        };
286        self.gate(ctx, backend)
287    }
288}
289
290impl Default for CoherenceRouter {
291    fn default() -> Self {
292        Self::new()
293    }
294}
295
296#[cfg(test)]
297mod tests {
298    use super::*;
299
300    #[test]
301    fn test_safe_action_permitted() {
302        let router = CoherenceRouter::new();
303        let ctx = ActionContext::new("read-only query").cost(0.1);
304        let d = router.gate(&ctx, CoherenceBackend::SheafLaplacian);
305        assert_eq!(d.decision, WitnessDecision::Permit);
306        assert!(d.lambda_min_cut > 0.0);
307    }
308
309    #[test]
310    fn test_high_risk_deferred() {
311        let router = CoherenceRouter::new();
312        let ctx = ActionContext::new("delete all vectors")
313            .cost(0.95)
314            .irreversible()
315            .shared();
316        let d = router.gate(&ctx, CoherenceBackend::SheafLaplacian);
317        // High cost + irreversible + shared = low spectral gap = defer/deny
318        assert!(d.decision == WitnessDecision::Defer || d.decision == WitnessDecision::Deny);
319    }
320
321    #[test]
322    fn test_auto_gate_fast_path() {
323        let router = CoherenceRouter::new();
324        let ctx = ActionContext::new("cheap local op").cost(0.05);
325        let d = router.auto_gate(&ctx);
326        assert_eq!(d.backend_used, CoherenceBackend::FastPath);
327        assert_eq!(d.decision, WitnessDecision::Permit);
328    }
329
330    #[test]
331    fn test_gate_with_witness() {
332        let router = CoherenceRouter::new();
333        let ctx = ActionContext::new("moderate op").cost(0.5);
334        let (decision, witness) =
335            router.gate_with_witness(&ctx, CoherenceBackend::SheafLaplacian, 42);
336        assert_eq!(decision.decision, witness.decision);
337        assert!(witness.lambda_min_cut.is_some());
338        assert_eq!(witness.sequence, 42);
339    }
340
341    #[test]
342    fn test_pi_scaled_threshold_non_binary() {
343        // Verify pi_scale is not a dyadic rational (would cause binary resonance)
344        let backend = SheafLaplacianBackend::new();
345        let scale = backend.pi_scale;
346        // π⁻¹ × φ ≈ 0.5150... — verify not representable as k/2^n for small n
347        // The mantissa should not be exactly representable in 3/5/7 bits
348        let mantissa_3bit = (scale * 8.0).floor() / 8.0;
349        assert!(
350            (scale - mantissa_3bit).abs() > 1e-6,
351            "Should not align with 3-bit grid"
352        );
353    }
354
355    #[test]
356    fn test_latency_sub_millisecond() {
357        let router = CoherenceRouter::new();
358        let ctx = ActionContext::new("latency test").cost(0.5);
359        let d = router.gate(&ctx, CoherenceBackend::SheafLaplacian);
360        assert!(
361            d.latency_us < 1000,
362            "Gate should complete in <1ms, got {}µs",
363            d.latency_us
364        );
365    }
366}