Skip to main content

elara_core/
science.rs

1//! ELARA System Science — Foundational Constructs v1
2//!
3//! Whitepaper-grade conceptual specification defining:
4//! - Node Ontology
5//! - Event Algebra
6//! - Presence Metric
7//! - Degradation Ladder
8//! - Chaos Test Specification
9//!
10//! This module provides the scientific foundations of ELARA.
11
12use std::fmt;
13use std::ops::BitOr;
14
15// ============================================================================
16// I. NODE ONTOLOGY
17// ============================================================================
18
19/// Ontological class of an ELARA node.
20///
21/// ELARA does not recognize "client", "server", "peer" as ontological classes.
22/// A node is classified by its relationship to reality events.
23///
24/// A single node can belong to multiple classes simultaneously.
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
26#[repr(u8)]
27pub enum NodeClass {
28    /// Origin Node: Generates primary events.
29    /// Examples: Human, sensor, AI agent, simulation.
30    /// Properties: private perception, local clock, cryptographic root, event genesis.
31    Origin = 0b0001,
32
33    /// Transmutation Node: Transforms the shape of reality.
34    /// Examples: Codec, AI reconstructor, summarizer, emotion extractor.
35    /// Properties: never source of truth, produces derivatives, preserves lineage.
36    Transmutation = 0b0010,
37
38    /// Propagation Node: Facilitates reality continuity.
39    /// Examples: Relay, mesh router, buffer swarm, cache.
40    /// Properties: no authority, no final state, replaceable, blind by default.
41    Propagation = 0b0100,
42
43    /// Witness Node: Gives meaning and memory.
44    /// Examples: User device, archive, timeline builder, auditor.
45    /// Properties: perspective-bound, builds projections, evaluates trust.
46    Witness = 0b1000,
47}
48
49impl NodeClass {
50    /// Get the class name
51    pub fn name(&self) -> &'static str {
52        match self {
53            NodeClass::Origin => "Origin",
54            NodeClass::Transmutation => "Transmutation",
55            NodeClass::Propagation => "Propagation",
56            NodeClass::Witness => "Witness",
57        }
58    }
59
60    /// Get all classes
61    pub fn all() -> &'static [NodeClass] {
62        &[
63            NodeClass::Origin,
64            NodeClass::Transmutation,
65            NodeClass::Propagation,
66            NodeClass::Witness,
67        ]
68    }
69}
70
71/// A set of node classes (a node can belong to multiple classes).
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
73pub struct NodeClassSet(u8);
74
75impl NodeClassSet {
76    /// Empty set
77    pub fn empty() -> Self {
78        Self(0)
79    }
80
81    /// Add a class to the set
82    pub fn with(self, class: NodeClass) -> Self {
83        Self(self.0 | class as u8)
84    }
85
86    /// Check if the set contains a class
87    pub fn contains(&self, class: NodeClass) -> bool {
88        (self.0 & class as u8) != 0
89    }
90
91    /// Check if the set is empty
92    pub fn is_empty(&self) -> bool {
93        self.0 == 0
94    }
95
96    /// Common configurations
97    pub fn smartphone() -> Self {
98        Self::empty()
99            .with(NodeClass::Origin)
100            .with(NodeClass::Transmutation)
101            .with(NodeClass::Witness)
102    }
103
104    pub fn relay_server() -> Self {
105        Self::empty().with(NodeClass::Propagation)
106    }
107
108    pub fn ai_agent() -> Self {
109        Self::empty()
110            .with(NodeClass::Origin)
111            .with(NodeClass::Transmutation)
112            .with(NodeClass::Witness)
113    }
114
115    pub fn archive() -> Self {
116        Self::empty()
117            .with(NodeClass::Propagation)
118            .with(NodeClass::Witness)
119    }
120}
121
122impl BitOr for NodeClass {
123    type Output = NodeClassSet;
124
125    fn bitor(self, rhs: Self) -> Self::Output {
126        NodeClassSet::empty().with(self).with(rhs)
127    }
128}
129
130impl BitOr<NodeClass> for NodeClassSet {
131    type Output = NodeClassSet;
132
133    fn bitor(self, rhs: NodeClass) -> Self::Output {
134        self.with(rhs)
135    }
136}
137
138// ============================================================================
139// II. EVENT ALGEBRA (Conceptual - operators defined symbolically)
140// ============================================================================
141
142/// Event algebra operators.
143///
144/// These are symbolic representations of the algebraic operations on events.
145#[derive(Debug, Clone, Copy, PartialEq, Eq)]
146pub enum EventOperator {
147    /// ⊕ Composition: Combining partial realities
148    /// e₁ ⊕ e₂ → new experience (not necessarily commutative)
149    Composition,
150
151    /// ⊗ Transmutation: Shape change without changing origin
152    /// f ⊗ e → e′ (voice → emotion → text → avatar motion)
153    Transmutation,
154
155    /// ≺ Causal Precedence: Reality dependency, not time
156    /// e₁ ≺ e₂ → e₂ meaningless without e₁
157    CausalPrecedence,
158
159    /// ∥ Co-presence: Events coexisting though not synchronized
160    /// e₁ ∥ e₂ → perceptual simultaneity
161    CoPresence,
162
163    /// ⊘ Degradation: Form reduction without meaning destruction
164    /// e ⊘ δ → e′ (voice → noise → breath → silence)
165    Degradation,
166}
167
168impl EventOperator {
169    /// Get the mathematical symbol
170    pub fn symbol(&self) -> &'static str {
171        match self {
172            EventOperator::Composition => "⊕",
173            EventOperator::Transmutation => "⊗",
174            EventOperator::CausalPrecedence => "≺",
175            EventOperator::CoPresence => "∥",
176            EventOperator::Degradation => "⊘",
177        }
178    }
179
180    /// Get the operator name
181    pub fn name(&self) -> &'static str {
182        match self {
183            EventOperator::Composition => "Composition",
184            EventOperator::Transmutation => "Transmutation",
185            EventOperator::CausalPrecedence => "Causal Precedence",
186            EventOperator::CoPresence => "Co-presence",
187            EventOperator::Degradation => "Degradation",
188        }
189    }
190}
191
192// ============================================================================
193// III. PRESENCE METRIC
194// ============================================================================
195
196/// Presence vector measuring "whether reality still feels alive".
197///
198/// ELARA does not measure quality with bitrate, FPS, jitter, or packet loss.
199/// ELARA measures presence as a 5-dimensional vector.
200///
201/// P = ⟨L, I, C, R, E⟩
202#[derive(Debug, Clone, Copy, PartialEq)]
203pub struct PresenceVector {
204    /// L - Liveness: Is there still a sign of existence? [0.0 - 1.0]
205    pub liveness: f32,
206
207    /// I - Immediacy: How close to "now"? [0.0 - 1.0]
208    pub immediacy: f32,
209
210    /// C - Coherence: Can it still be understood? [0.0 - 1.0]
211    pub coherence: f32,
212
213    /// R - Relational Continuity: Does the relationship still feel intact? [0.0 - 1.0]
214    pub relational_continuity: f32,
215
216    /// E - Emotional Bandwidth: Is emotional meaning still carried? [0.0 - 1.0]
217    pub emotional_bandwidth: f32,
218}
219
220impl PresenceVector {
221    /// Create a new presence vector
222    pub fn new(
223        liveness: f32,
224        immediacy: f32,
225        coherence: f32,
226        relational: f32,
227        emotional: f32,
228    ) -> Self {
229        Self {
230            liveness: liveness.clamp(0.0, 1.0),
231            immediacy: immediacy.clamp(0.0, 1.0),
232            coherence: coherence.clamp(0.0, 1.0),
233            relational_continuity: relational.clamp(0.0, 1.0),
234            emotional_bandwidth: emotional.clamp(0.0, 1.0),
235        }
236    }
237
238    /// Full presence (all components at maximum)
239    pub fn full() -> Self {
240        Self::new(1.0, 1.0, 1.0, 1.0, 1.0)
241    }
242
243    /// Minimal presence (barely alive)
244    pub fn minimal() -> Self {
245        Self::new(0.1, 0.0, 0.0, 0.0, 0.0)
246    }
247
248    /// Zero presence (dead - should never happen in ELARA)
249    pub fn zero() -> Self {
250        Self::new(0.0, 0.0, 0.0, 0.0, 0.0)
251    }
252
253    /// Calculate overall presence score [0.0 - 1.0]
254    pub fn score(&self) -> f32 {
255        (self.liveness
256            + self.immediacy
257            + self.coherence
258            + self.relational_continuity
259            + self.emotional_bandwidth)
260            / 5.0
261    }
262
263    /// Check if presence is alive (any component > 0)
264    pub fn is_alive(&self) -> bool {
265        self.liveness > 0.0
266            || self.immediacy > 0.0
267            || self.coherence > 0.0
268            || self.relational_continuity > 0.0
269            || self.emotional_bandwidth > 0.0
270    }
271
272    /// Get the components as an array [L, I, C, R, E]
273    pub fn as_array(&self) -> [f32; 5] {
274        [
275            self.liveness,
276            self.immediacy,
277            self.coherence,
278            self.relational_continuity,
279            self.emotional_bandwidth,
280        ]
281    }
282
283    /// Get the minimum component value
284    pub fn min_component(&self) -> f32 {
285        self.as_array().into_iter().fold(f32::MAX, f32::min)
286    }
287
288    /// Get the maximum component value
289    pub fn max_component(&self) -> f32 {
290        self.as_array().into_iter().fold(f32::MIN, f32::max)
291    }
292}
293
294impl Default for PresenceVector {
295    fn default() -> Self {
296        Self::full()
297    }
298}
299
300impl fmt::Display for PresenceVector {
301    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
302        write!(
303            f,
304            "P = ⟨L:{:.2}, I:{:.2}, C:{:.2}, R:{:.2}, E:{:.2}⟩ (score: {:.2})",
305            self.liveness,
306            self.immediacy,
307            self.coherence,
308            self.relational_continuity,
309            self.emotional_bandwidth,
310            self.score()
311        )
312    }
313}
314
315// ============================================================================
316// IV. DEGRADATION LADDER
317// ============================================================================
318
319/// Degradation level in the ELARA reality ladder.
320///
321/// ELARA defines an official degradation ladder. No improvisation per engineer.
322/// There is no "disconnected" - only the most minimal form of reality.
323#[allow(non_camel_case_types)]
324#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
325#[repr(u8)]
326pub enum DegradationLevel {
327    /// L0: Full Perception - Continuous voice/video, rich semantics
328    L0_FullPerception = 0,
329
330    /// L1: Distorted Perception - Noise, blur, jitter, but continuous
331    L1_DistortedPerception = 1,
332
333    /// L2: Fragmented Perception - Chunks, drops, approximations
334    L2_FragmentedPerception = 2,
335
336    /// L3: Symbolic Presence - Text, emotion tokens, activity traces
337    L3_SymbolicPresence = 3,
338
339    /// L4: Minimal Presence - Breath, pulse, alive-signal, echoes
340    L4_MinimalPresence = 4,
341
342    /// L5: Latent Presence - Signed silence, delayed reality, memory
343    L5_LatentPresence = 5,
344}
345
346impl DegradationLevel {
347    /// Get the level number (0-5)
348    pub fn level(&self) -> u8 {
349        *self as u8
350    }
351
352    /// Get the level name
353    pub fn name(&self) -> &'static str {
354        match self {
355            Self::L0_FullPerception => "Full Perception",
356            Self::L1_DistortedPerception => "Distorted Perception",
357            Self::L2_FragmentedPerception => "Fragmented Perception",
358            Self::L3_SymbolicPresence => "Symbolic Presence",
359            Self::L4_MinimalPresence => "Minimal Presence",
360            Self::L5_LatentPresence => "Latent Presence",
361        }
362    }
363
364    /// Get the next lower level (more degraded)
365    /// Returns None if already at L5 (cannot degrade further)
366    pub fn degrade(&self) -> Option<Self> {
367        match self {
368            Self::L0_FullPerception => Some(Self::L1_DistortedPerception),
369            Self::L1_DistortedPerception => Some(Self::L2_FragmentedPerception),
370            Self::L2_FragmentedPerception => Some(Self::L3_SymbolicPresence),
371            Self::L3_SymbolicPresence => Some(Self::L4_MinimalPresence),
372            Self::L4_MinimalPresence => Some(Self::L5_LatentPresence),
373            Self::L5_LatentPresence => None, // Cannot degrade further - this is the floor
374        }
375    }
376
377    /// Get the next higher level (less degraded)
378    /// Returns None if already at L0 (best quality)
379    pub fn improve(&self) -> Option<Self> {
380        match self {
381            Self::L0_FullPerception => None, // Already at best
382            Self::L1_DistortedPerception => Some(Self::L0_FullPerception),
383            Self::L2_FragmentedPerception => Some(Self::L1_DistortedPerception),
384            Self::L3_SymbolicPresence => Some(Self::L2_FragmentedPerception),
385            Self::L4_MinimalPresence => Some(Self::L3_SymbolicPresence),
386            Self::L5_LatentPresence => Some(Self::L4_MinimalPresence),
387        }
388    }
389
390    /// Check if this level is worse than another
391    pub fn is_worse_than(&self, other: Self) -> bool {
392        self.level() > other.level()
393    }
394
395    /// Check if this level is better than another
396    pub fn is_better_than(&self, other: Self) -> bool {
397        self.level() < other.level()
398    }
399
400    /// Get all levels from best to worst
401    pub fn all() -> &'static [Self] {
402        &[
403            Self::L0_FullPerception,
404            Self::L1_DistortedPerception,
405            Self::L2_FragmentedPerception,
406            Self::L3_SymbolicPresence,
407            Self::L4_MinimalPresence,
408            Self::L5_LatentPresence,
409        ]
410    }
411}
412
413impl fmt::Display for DegradationLevel {
414    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
415        write!(f, "L{}: {}", self.level(), self.name())
416    }
417}
418
419// ============================================================================
420// V. CHAOS TEST SPECIFICATION
421// ============================================================================
422
423/// Category of chaos test.
424///
425/// ELARA must be tested with existential chaos, not just latency/throughput.
426#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
427pub enum ChaosCategory {
428    /// Ontological Chaos: Identity and reality coherence
429    /// Tests: node identity change, forked history, AI hallucination, sensor spoofing
430    Ontological,
431
432    /// Temporal Chaos: Time model resilience
433    /// Tests: massive clock skew, delayed days, brutal reordering, causal holes
434    Temporal,
435
436    /// Topological Chaos: Network resilience
437    /// Tests: extended partition, swarm join/leave, mobile blackout, relay death
438    Topological,
439
440    /// Adversarial Chaos: Security model
441    /// Tests: malicious injection, replay attack, perception poisoning, presence forgery
442    Adversarial,
443
444    /// Perceptual Chaos: Human experience continuity
445    /// Tests: extreme jitter, half streams, emotional desync, semantic loss
446    Perceptual,
447}
448
449impl ChaosCategory {
450    /// Get the category name
451    pub fn name(&self) -> &'static str {
452        match self {
453            Self::Ontological => "Ontological Chaos",
454            Self::Temporal => "Temporal Chaos",
455            Self::Topological => "Topological Chaos",
456            Self::Adversarial => "Adversarial Chaos",
457            Self::Perceptual => "Perceptual Chaos",
458        }
459    }
460
461    /// Get the minimum presence floor for this chaos type
462    pub fn presence_floor(&self) -> DegradationLevel {
463        match self {
464            Self::Ontological => DegradationLevel::L4_MinimalPresence,
465            Self::Temporal => DegradationLevel::L3_SymbolicPresence,
466            Self::Topological => DegradationLevel::L5_LatentPresence,
467            Self::Adversarial => DegradationLevel::L3_SymbolicPresence,
468            Self::Perceptual => DegradationLevel::L2_FragmentedPerception,
469        }
470    }
471
472    /// Get all categories
473    pub fn all() -> &'static [Self] {
474        &[
475            Self::Ontological,
476            Self::Temporal,
477            Self::Topological,
478            Self::Adversarial,
479            Self::Perceptual,
480        ]
481    }
482}
483
484/// Success criteria for a chaos test.
485#[derive(Debug, Clone)]
486pub struct ChaosSuccessCriteria {
487    /// Minimum presence that must be maintained
488    pub min_presence: PresenceVector,
489
490    /// Maximum degradation level allowed
491    pub max_degradation: DegradationLevel,
492
493    /// Lineage must remain intact
494    pub lineage_intact: bool,
495
496    /// Identity must remain continuous
497    pub identity_continuous: bool,
498}
499
500impl ChaosSuccessCriteria {
501    /// Default criteria: presence alive, lineage and identity intact
502    pub fn default_for(category: ChaosCategory) -> Self {
503        Self {
504            min_presence: PresenceVector::minimal(),
505            max_degradation: category.presence_floor(),
506            lineage_intact: true,
507            identity_continuous: true,
508        }
509    }
510}
511
512/// Result of a chaos test.
513#[derive(Debug, Clone)]
514pub struct ChaosTestResult {
515    /// Did the test pass?
516    pub passed: bool,
517
518    /// Final presence vector
519    pub presence: PresenceVector,
520
521    /// Final degradation level
522    pub level: DegradationLevel,
523
524    /// Was lineage maintained?
525    pub lineage_intact: bool,
526
527    /// Was identity continuous?
528    pub identity_continuous: bool,
529}
530
531impl ChaosTestResult {
532    /// Check if the result meets the success criteria
533    pub fn meets_criteria(&self, criteria: &ChaosSuccessCriteria) -> bool {
534        self.presence.is_alive()
535            && self.level <= criteria.max_degradation
536            && (!criteria.lineage_intact || self.lineage_intact)
537            && (!criteria.identity_continuous || self.identity_continuous)
538    }
539}
540
541#[cfg(test)]
542mod tests {
543    use super::*;
544
545    #[test]
546    fn test_node_class_set() {
547        let smartphone = NodeClassSet::smartphone();
548        assert!(smartphone.contains(NodeClass::Origin));
549        assert!(smartphone.contains(NodeClass::Transmutation));
550        assert!(smartphone.contains(NodeClass::Witness));
551        assert!(!smartphone.contains(NodeClass::Propagation));
552
553        let relay = NodeClassSet::relay_server();
554        assert!(relay.contains(NodeClass::Propagation));
555        assert!(!relay.contains(NodeClass::Origin));
556    }
557
558    #[test]
559    fn test_presence_vector() {
560        let full = PresenceVector::full();
561        assert_eq!(full.score(), 1.0);
562        assert!(full.is_alive());
563
564        let minimal = PresenceVector::minimal();
565        assert!(minimal.is_alive());
566        assert!(minimal.score() < 0.1);
567
568        let zero = PresenceVector::zero();
569        assert!(!zero.is_alive());
570    }
571
572    #[test]
573    fn test_degradation_ladder() {
574        let mut level = DegradationLevel::L0_FullPerception;
575
576        // Degrade through all levels
577        let mut count = 0;
578        while let Some(next) = level.degrade() {
579            assert!(next.is_worse_than(level));
580            level = next;
581            count += 1;
582        }
583        assert_eq!(count, 5);
584        assert_eq!(level, DegradationLevel::L5_LatentPresence);
585
586        // Improve back up
587        while let Some(prev) = level.improve() {
588            assert!(prev.is_better_than(level));
589            level = prev;
590        }
591        assert_eq!(level, DegradationLevel::L0_FullPerception);
592    }
593
594    #[test]
595    fn test_chaos_categories() {
596        for category in ChaosCategory::all() {
597            let floor = category.presence_floor();
598            // All floors should be valid degradation levels
599            assert!(floor.level() <= 5);
600        }
601    }
602
603    #[test]
604    fn test_event_operators() {
605        assert_eq!(EventOperator::Composition.symbol(), "⊕");
606        assert_eq!(EventOperator::CausalPrecedence.symbol(), "≺");
607        assert_eq!(EventOperator::Degradation.symbol(), "⊘");
608    }
609
610    #[test]
611    fn test_presence_display() {
612        let p = PresenceVector::new(0.9, 0.8, 0.7, 0.6, 0.5);
613        let display = format!("{}", p);
614        assert!(display.contains("L:0.90"));
615        assert!(display.contains("score:"));
616    }
617}