Skip to main content

lsp_max/runtime/
mesh_types.rs

1pub use lsp_max_protocol::{HookEvent, InstanceId, MaxDiagnostic, PolicyState, Receipt};
2
3#[derive(Debug, Clone)]
4pub enum MeshAction {
5    TransitionPolicyState {
6        instance_id: InstanceId,
7        new_state: PolicyState,
8    },
9    ClearDiagnostic {
10        instance_id: InstanceId,
11        diagnostic_id: String,
12    },
13    AddDiagnostic {
14        instance_id: InstanceId,
15        diagnostic: Box<MaxDiagnostic>,
16    },
17    EmitReceipt {
18        instance_id: InstanceId,
19        receipt: Receipt,
20    },
21    ExecuteBoundedAction {
22        instance_id: InstanceId,
23        action_id: String,
24        description: String,
25    },
26    ResetInstance {
27        instance_id: InstanceId,
28    },
29}
30
31impl std::fmt::Display for MeshAction {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        match self {
34            MeshAction::TransitionPolicyState {
35                instance_id,
36                new_state,
37            } => {
38                write!(f, "TransitionPolicyState({}, {:?})", instance_id, new_state)
39            }
40            MeshAction::ClearDiagnostic {
41                instance_id,
42                diagnostic_id,
43            } => {
44                write!(f, "ClearDiagnostic({}, {})", instance_id, diagnostic_id)
45            }
46            MeshAction::AddDiagnostic { instance_id, .. } => {
47                write!(f, "AddDiagnostic({})", instance_id)
48            }
49            MeshAction::EmitReceipt {
50                instance_id,
51                receipt,
52            } => {
53                write!(f, "EmitReceipt({}, {})", instance_id, receipt.receipt_id)
54            }
55            MeshAction::ExecuteBoundedAction {
56                instance_id,
57                action_id,
58                ..
59            } => {
60                write!(f, "ExecuteBoundedAction({}, {})", instance_id, action_id)
61            }
62            MeshAction::ResetInstance { instance_id } => {
63                write!(f, "ResetInstance({})", instance_id)
64            }
65        }
66    }
67}
68
69/// Failure mode for a hook — what happens when the hook cannot satisfy its contract.
70#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
71pub enum FailureMode {
72    RefuseEvent,
73    EmitDiagnostic,
74    Halt,
75}
76
77/// Static descriptor for a registered hook — name, type surface, trigger law, failure mode.
78#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
79pub struct HookDescriptor {
80    pub name: &'static str,
81    pub input_type: &'static str,
82    pub output_type: &'static str,
83    pub trigger_law: &'static str,
84    pub failure_mode: FailureMode,
85}
86
87pub trait Hook: Send + Sync {
88    fn name(&self) -> &str;
89    fn trigger(&self, event: &HookEvent) -> Vec<MeshAction>;
90    fn descriptor(&self) -> HookDescriptor;
91}
92
93/// Lifecycle phase of an LSP instance.
94#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
95pub enum LspPhase {
96    Uninitialized,
97    Initializing,
98    Initialized,
99    ShutDown,
100    Exited,
101}
102
103impl std::fmt::Display for LspPhase {
104    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105        match self {
106            LspPhase::Uninitialized => write!(f, "Uninitialized"),
107            LspPhase::Initializing => write!(f, "Initializing"),
108            LspPhase::Initialized => write!(f, "Initialized"),
109            LspPhase::ShutDown => write!(f, "ShutDown"),
110            LspPhase::Exited => write!(f, "Exited"),
111        }
112    }
113}
114
115#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
116pub struct LspInstance {
117    pub id: String,
118    pub phase: LspPhase,
119    pub diagnostics: Vec<MaxDiagnostic>,
120    pub receipts: Vec<Receipt>,
121    pub policy_state: Option<PolicyState>,
122    #[serde(skip)]
123    cached_score: std::cell::Cell<Option<f64>>,
124}
125
126impl LspInstance {
127    pub fn new(id: &str) -> Self {
128        Self {
129            id: id.to_string(),
130            phase: LspPhase::Uninitialized,
131            diagnostics: Vec::new(),
132            receipts: Vec::new(),
133            policy_state: None,
134            cached_score: std::cell::Cell::new(None),
135        }
136    }
137
138    #[inline]
139    pub fn invalidate_score_cache(&mut self) {
140        self.cached_score.set(None);
141    }
142
143    pub fn add_diagnostic(&mut self, diag: MaxDiagnostic) {
144        self.diagnostics.push(diag);
145        self.invalidate_score_cache();
146    }
147
148    pub fn remove_diagnostic(&mut self, diagnostic_id: &str) -> usize {
149        let before = self.diagnostics.len();
150        self.diagnostics
151            .retain(|d| d.diagnostic_id != diagnostic_id);
152        let removed = before - self.diagnostics.len();
153        if removed > 0 {
154            self.invalidate_score_cache();
155        }
156        removed
157    }
158
159    pub fn conformance_score(&self) -> f64 {
160        if let Some(score) = self.cached_score.get() {
161            return score;
162        }
163        let mut penalty: f64 = 0.0;
164        for diag in &self.diagnostics {
165            let p = match diag.lsp.severity {
166                Some(lsp_types_max::DiagnosticSeverity::ERROR) => 30.0,
167                Some(lsp_types_max::DiagnosticSeverity::WARNING) => 20.0,
168                Some(lsp_types_max::DiagnosticSeverity::INFORMATION) => 10.0,
169                Some(lsp_types_max::DiagnosticSeverity::HINT) => 5.0,
170                Some(_) => 30.0,
171                None => 30.0,
172            };
173            penalty += p;
174        }
175        let score = f64::max(100.0 - penalty, 0.0);
176        self.cached_score.set(Some(score));
177        score
178    }
179
180    pub fn conformance_grade(&self) -> ConformanceGrade {
181        ConformanceGrade::from_score(self.conformance_score())
182    }
183}
184
185impl Default for LspInstance {
186    fn default() -> Self {
187        Self {
188            id: String::new(),
189            phase: LspPhase::Uninitialized,
190            diagnostics: Vec::new(),
191            receipts: Vec::new(),
192            policy_state: None,
193            cached_score: std::cell::Cell::new(None),
194        }
195    }
196}
197
198/// Coarse quality bucket derived from a conformance score.
199#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
200pub enum ConformanceGrade {
201    Perfect,
202    Good,
203    Degraded,
204    Critical,
205}
206
207impl ConformanceGrade {
208    pub fn from_score(score: f64) -> Self {
209        if score >= 100.0 {
210            ConformanceGrade::Perfect
211        } else if score >= 75.0 {
212            ConformanceGrade::Good
213        } else if score >= 50.0 {
214            ConformanceGrade::Degraded
215        } else {
216            ConformanceGrade::Critical
217        }
218    }
219
220    pub fn as_str(&self) -> &'static str {
221        match self {
222            ConformanceGrade::Perfect => "perfect",
223            ConformanceGrade::Good => "good",
224            ConformanceGrade::Degraded => "degraded",
225            ConformanceGrade::Critical => "critical",
226        }
227    }
228}
229
230impl std::fmt::Display for ConformanceGrade {
231    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
232        write!(f, "{}", self.as_str())
233    }
234}
235
236#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
237pub struct AutonomicMeshState {
238    pub instances: std::collections::HashMap<String, LspInstance>,
239    pub event_log: Vec<HookEvent>,
240    pub executed_bounded_actions: Vec<String>,
241    #[serde(flatten)]
242    pub extra: std::collections::HashMap<String, serde_json::Value>,
243}
244
245impl std::fmt::Display for AutonomicMeshState {
246    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
247        write!(
248            f,
249            "AutonomicMeshState {{ instances: {}, event_log: {} }}",
250            self.instances.len(),
251            self.event_log.len()
252        )
253    }
254}
255
256#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
257pub struct ConformanceDeltaEntry {
258    pub seq: u64,
259    pub instance_id: String,
260    pub old_score: f64,
261    pub new_score: f64,
262    /// RFC-3339 UTC timestamp of the delta. Empty string for legacy entries.
263    pub timestamp: String,
264}
265
266#[derive(Debug, Clone, Copy, PartialEq, Eq)]
267pub enum MaxMethod {
268    Snapshot,
269    ConformanceVector,
270    ClearDiagnostic,
271    ExplainDiagnostic,
272    RepairPlan,
273    ApplyRepairTransaction,
274    ExportAnalysisBundle,
275    RunGate,
276    Receipt,
277    Hook,
278    HookGraph,
279    Chain,
280    Propagate,
281    AutonomicLoop,
282    ManifoldSnapshot,
283    LawfulTransition,
284    Admission,
285    Refusal,
286    Replay,
287    ReleaseActuation,
288    InstanceList,
289    DumpState,
290    RestoreState,
291    Reset,
292    ConformanceDelta,
293    VerifyLedger,
294    LedgerReport,
295}
296
297impl MaxMethod {
298    pub fn as_str(self) -> &'static str {
299        match self {
300            MaxMethod::Snapshot => "max/snapshot",
301            MaxMethod::ConformanceVector => "max/conformanceVector",
302            MaxMethod::ClearDiagnostic => "max/clearDiagnostic",
303            MaxMethod::ExplainDiagnostic => "max/explainDiagnostic",
304            MaxMethod::RepairPlan => "max/repairPlan",
305            MaxMethod::ApplyRepairTransaction => "max/applyRepairTransaction",
306            MaxMethod::ExportAnalysisBundle => "max/exportAnalysisBundle",
307            MaxMethod::RunGate => "max/runGate",
308            MaxMethod::Receipt => "max/receipt",
309            MaxMethod::Hook => "max/hook",
310            MaxMethod::HookGraph => "max/hookGraph",
311            MaxMethod::Chain => "max/chain",
312            MaxMethod::Propagate => "max/propagate",
313            MaxMethod::AutonomicLoop => "max/autonomicLoop",
314            MaxMethod::ManifoldSnapshot => "max/manifoldSnapshot",
315            MaxMethod::LawfulTransition => "max/lawfulTransition",
316            MaxMethod::Admission => "max/admission",
317            MaxMethod::Refusal => "max/refusal",
318            MaxMethod::Replay => "max/replay",
319            MaxMethod::ReleaseActuation => "max/releaseActuation",
320            MaxMethod::InstanceList => "max/instanceList",
321            MaxMethod::DumpState => "max/dumpState",
322            MaxMethod::RestoreState => "max/restoreState",
323            MaxMethod::Reset => "max/reset",
324            MaxMethod::ConformanceDelta => "max/conformanceDelta",
325            MaxMethod::VerifyLedger => "max/verifyLedger",
326            MaxMethod::LedgerReport => "max/ledgerReport",
327        }
328    }
329}
330
331impl<'a> TryFrom<&'a str> for MaxMethod {
332    type Error = ();
333
334    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
335        match s {
336            "max/snapshot" => Ok(MaxMethod::Snapshot),
337            "max/conformanceVector" => Ok(MaxMethod::ConformanceVector),
338            "max/clearDiagnostic" => Ok(MaxMethod::ClearDiagnostic),
339            "max/explainDiagnostic" => Ok(MaxMethod::ExplainDiagnostic),
340            "max/repairPlan" => Ok(MaxMethod::RepairPlan),
341            "max/applyRepairTransaction" => Ok(MaxMethod::ApplyRepairTransaction),
342            "max/exportAnalysisBundle" => Ok(MaxMethod::ExportAnalysisBundle),
343            "max/runGate" => Ok(MaxMethod::RunGate),
344            "max/receipt" => Ok(MaxMethod::Receipt),
345            "max/hook" => Ok(MaxMethod::Hook),
346            "max/hookGraph" => Ok(MaxMethod::HookGraph),
347            "max/chain" => Ok(MaxMethod::Chain),
348            "max/propagate" => Ok(MaxMethod::Propagate),
349            "max/autonomicLoop" => Ok(MaxMethod::AutonomicLoop),
350            "max/manifoldSnapshot" => Ok(MaxMethod::ManifoldSnapshot),
351            "max/lawfulTransition" => Ok(MaxMethod::LawfulTransition),
352            "max/admission" => Ok(MaxMethod::Admission),
353            "max/refusal" => Ok(MaxMethod::Refusal),
354            "max/replay" => Ok(MaxMethod::Replay),
355            "max/releaseActuation" => Ok(MaxMethod::ReleaseActuation),
356            "max/instanceList" => Ok(MaxMethod::InstanceList),
357            "max/dumpState" => Ok(MaxMethod::DumpState),
358            "max/restoreState" => Ok(MaxMethod::RestoreState),
359            "max/reset" => Ok(MaxMethod::Reset),
360            "max/conformanceDelta" => Ok(MaxMethod::ConformanceDelta),
361            "max/verifyLedger" => Ok(MaxMethod::VerifyLedger),
362            "max/ledgerReport" => Ok(MaxMethod::LedgerReport),
363            _ => Err(()),
364        }
365    }
366}