Skip to main content

maple_runtime/fabrics/
presence.rs

1//! Presence Fabric - manages gradient presence state
2//!
3//! Presence is NOT binary (online/offline). It's a gradient.
4
5use crate::config::PresenceConfig as PresenceFabricConfig;
6use crate::types::*;
7use dashmap::DashMap;
8use std::time::{Duration, Instant};
9
10/// Presence Fabric manages presence states for all Resonators
11///
12/// Key insight: Presence is multidimensional, not binary.
13/// A Resonator can be:
14/// - Discoverable but not responsive
15/// - Responsive but not accepting new couplings
16/// - Present but in silent mode
17pub struct PresenceFabric {
18    /// Presence states for all Resonators
19    states: DashMap<ResonatorId, PresenceStateWithMetadata>,
20
21    /// Configuration
22    config: PresenceFabricConfig,
23}
24
25struct PresenceStateWithMetadata {
26    state: PresenceState,
27    last_update: Instant,
28}
29
30impl PresenceFabric {
31    fn initial_last_update(min_signal_interval_ms: u64) -> Instant {
32        // Registration/restore should not consume the first explicit signal budget.
33        Instant::now()
34            .checked_sub(Duration::from_millis(min_signal_interval_ms))
35            .unwrap_or_else(Instant::now)
36    }
37
38    pub fn new(config: &PresenceFabricConfig) -> Self {
39        Self {
40            states: DashMap::new(),
41            config: config.clone(),
42        }
43    }
44
45    /// Initialize presence for a new Resonator
46    pub async fn initialize_presence(
47        &self,
48        resonator: &ResonatorId,
49        config: &PresenceConfig,
50    ) -> Result<(), String> {
51        let mut state = PresenceState::new();
52        state.discoverability = config.initial_discoverability;
53        state.responsiveness = config.initial_responsiveness;
54        state.silent_mode = config.start_silent;
55
56        let metadata = PresenceStateWithMetadata {
57            state,
58            last_update: Self::initial_last_update(self.config.min_signal_interval_ms),
59        };
60
61        self.states.insert(*resonator, metadata);
62
63        tracing::debug!("Initialized presence for {}", resonator);
64        Ok(())
65    }
66
67    /// Signal presence (MUST be low-cost and non-intrusive)
68    ///
69    /// ARCHITECTURAL RULE: Presence signaling must not be burdensome.
70    pub async fn signal_presence(
71        &self,
72        resonator: ResonatorId,
73        state: PresenceState,
74    ) -> Result<(), PresenceError> {
75        // Check rate limiting
76        if let Some(existing) = self.states.get(&resonator) {
77            let elapsed = existing.last_update.elapsed();
78            let min_interval = std::time::Duration::from_millis(self.config.min_signal_interval_ms);
79
80            if elapsed < min_interval {
81                return Err(PresenceError::RateLimitExceeded);
82            }
83        }
84
85        // Validate: presence signal must be low-cost
86        // (In real implementation, would check if signal is too large, etc.)
87
88        let metadata = PresenceStateWithMetadata {
89            state,
90            last_update: Instant::now(),
91        };
92
93        self.states.insert(resonator, metadata);
94
95        Ok(())
96    }
97
98    /// Enable silent presence (existence without active signaling)
99    ///
100    /// This is important: a Resonator may be present without actively participating.
101    /// Presence does NOT imply willingness to interact.
102    pub async fn enable_silent_presence(&self, resonator: ResonatorId) {
103        if let Some(mut entry) = self.states.get_mut(&resonator) {
104            entry.state.silent_mode = true;
105            entry.state.discoverability = 0.1; // Minimal discoverability
106        }
107    }
108
109    /// Disable silent mode
110    pub async fn disable_silent_presence(&self, resonator: ResonatorId) {
111        if let Some(mut entry) = self.states.get_mut(&resonator) {
112            entry.state.silent_mode = false;
113            entry.state.discoverability = 0.5; // Return to default
114        }
115    }
116
117    /// Get presence state
118    pub fn get_presence(&self, resonator: &ResonatorId) -> Option<PresenceState> {
119        self.states.get(resonator).map(|r| r.state.clone())
120    }
121
122    /// Check if Resonator is present
123    pub fn is_present(&self, resonator: &ResonatorId) -> bool {
124        self.states.contains_key(resonator)
125    }
126
127    /// Update presence gradient (called periodically to adjust presence based on behavior)
128    pub async fn update_presence_gradient(
129        &self,
130        resonator: &ResonatorId,
131        adjustment: PresenceAdjustment,
132    ) {
133        if let Some(mut entry) = self.states.get_mut(resonator) {
134            match adjustment {
135                PresenceAdjustment::IncreaseResponsiveness(delta) => {
136                    entry.state.responsiveness = (entry.state.responsiveness + delta).min(1.0);
137                }
138                PresenceAdjustment::DecreaseResponsiveness(delta) => {
139                    entry.state.responsiveness = (entry.state.responsiveness - delta).max(0.0);
140                }
141                PresenceAdjustment::IncreaseStability(delta) => {
142                    entry.state.stability = (entry.state.stability + delta).min(1.0);
143                }
144                PresenceAdjustment::DecreaseStability(delta) => {
145                    entry.state.stability = (entry.state.stability - delta).max(0.0);
146                }
147                PresenceAdjustment::SetCouplingReadiness(value) => {
148                    entry.state.coupling_readiness = value.clamp(0.0, 1.0);
149                }
150            }
151        }
152    }
153
154    /// Restore presence from continuity record
155    pub async fn restore_presence(
156        &self,
157        resonator: &ResonatorId,
158        state: &PresenceState,
159    ) -> Result<(), String> {
160        let metadata = PresenceStateWithMetadata {
161            state: state.clone(),
162            last_update: Self::initial_last_update(self.config.min_signal_interval_ms),
163        };
164
165        self.states.insert(*resonator, metadata);
166
167        tracing::debug!("Restored presence for {}", resonator);
168        Ok(())
169    }
170
171    /// Remove presence (for cleanup)
172    pub fn remove_presence(&self, resonator: &ResonatorId) {
173        self.states.remove(resonator);
174    }
175
176    /// Get all present Resonators
177    pub fn get_all_present(&self) -> Vec<ResonatorId> {
178        self.states.iter().map(|entry| *entry.key()).collect()
179    }
180
181    /// Count of present Resonators
182    pub fn count(&self) -> usize {
183        self.states.len()
184    }
185}
186
187/// Adjustments to presence gradients
188pub enum PresenceAdjustment {
189    IncreaseResponsiveness(f64),
190    DecreaseResponsiveness(f64),
191    IncreaseStability(f64),
192    DecreaseStability(f64),
193    SetCouplingReadiness(f64),
194}