1use crate::runtime::mesh_hooks::{IntakeClearHook, IntakeDiagnosticHook, OcelProcessHook};
2use crate::runtime::mesh_types::{
3 AutonomicMeshState, ConformanceDeltaEntry, Hook, HookEvent, InstanceId, LspInstance, LspPhase,
4 MaxDiagnostic, MeshAction, PolicyState,
5};
6use crate::runtime::sha256::sha256;
7use std::collections::{HashMap, VecDeque};
8use std::io;
9use std::path::Path;
10
11const MAX_EVENT_LOG: usize = 1000;
12const MAX_DISPATCH_DEPTH: usize = 16;
13const MAX_CONFORMANCE_DELTA_LOG: usize = 4096;
14
15pub struct AutonomicMesh {
16 pub instances: HashMap<String, LspInstance>,
17 pub hooks: Vec<Box<dyn Hook>>,
18 pub event_log: Vec<HookEvent>,
19 pub executed_bounded_actions: Vec<String>,
20 pub extra: HashMap<String, serde_json::Value>,
21 pub action_seq: u64,
22 pub conformance_delta_log: VecDeque<ConformanceDeltaEntry>,
23 dispatch_depth: usize,
24}
25
26impl Default for AutonomicMesh {
27 fn default() -> Self {
28 Self::new()
29 }
30}
31
32pub type MaxMesh = AutonomicMesh;
33
34pub fn build_conformance_vector(
35 diagnostics: &[MaxDiagnostic],
36) -> lsp_max_protocol::ConformanceVector {
37 let mut axis_map: HashMap<lsp_max_protocol::LawAxis, bool> = HashMap::new();
38 for diag in diagnostics {
39 let is_error = matches!(
40 diag.lsp.severity,
41 Some(lsp_types_max::DiagnosticSeverity::ERROR)
42 );
43 let entry = axis_map.entry(diag.law_axis.clone()).or_insert(false);
44 if is_error {
45 *entry = true;
46 }
47 }
48
49 let mut admitted = vec![];
50 let mut refused = vec![];
51 for (axis, has_error) in &axis_map {
52 if *has_error {
53 refused.push(axis.clone());
54 } else {
55 admitted.push(axis.clone());
56 }
57 }
58
59 let total = admitted.len() + refused.len();
60 let derived_score = if total == 0 {
61 None
62 } else {
63 Some(100.0 * admitted.len() as f64 / total as f64)
64 };
65
66 let witnessed: std::collections::HashSet<lsp_max_protocol::LawAxis> =
67 axis_map.keys().cloned().collect();
68 let unknown: Vec<lsp_max_protocol::LawAxis> = lsp_max_protocol::LawAxis::all_named()
69 .iter()
70 .filter(|ax| !witnessed.contains(ax))
71 .cloned()
72 .collect();
73
74 let mut cv = lsp_max_protocol::ConformanceVector {
75 admitted,
76 refused,
77 unknown,
78 score: derived_score,
79 strict_mode: true,
80 process_quality: None,
81 ..Default::default()
82 };
83 cv.sync_bits_from_vecs();
84 cv
85}
86
87impl AutonomicMesh {
88 pub fn new() -> Self {
89 Self {
90 instances: HashMap::new(),
91 hooks: Vec::new(),
92 event_log: Vec::new(),
93 executed_bounded_actions: Vec::new(),
94 extra: HashMap::new(),
95 action_seq: 0,
96 conformance_delta_log: VecDeque::new(),
97 dispatch_depth: 0,
98 }
99 }
100
101 pub fn to_state(&self) -> AutonomicMeshState {
102 AutonomicMeshState {
103 instances: self.instances.clone(),
104 event_log: self.event_log.clone(),
105 executed_bounded_actions: self.executed_bounded_actions.clone(),
106 extra: self.extra.clone(),
107 }
108 }
109
110 pub fn load_state(&mut self, state: AutonomicMeshState) {
111 self.instances = state.instances;
112 self.event_log = state.event_log;
113 self.executed_bounded_actions = state.executed_bounded_actions;
114 self.extra = state.extra;
115 }
116
117 pub fn load_from_file(path: &str) -> io::Result<Self> {
118 let mut mesh = Self::new();
119 if Path::new(path).exists() {
120 let data = std::fs::read_to_string(path)?;
121 if let Ok(state) = serde_json::from_str::<AutonomicMeshState>(&data) {
122 mesh.load_state(state);
123 }
124 } else {
125 let mut lsp1 = LspInstance::new("LSP_1");
126 lsp1.phase = LspPhase::Initialized;
127 let mut lsp2 = LspInstance::new("LSP_2");
128 lsp2.phase = LspPhase::Initialized;
129 lsp2.policy_state = Some(PolicyState::Operational);
130
131 mesh.add_instance(lsp1);
132 mesh.add_instance(lsp2);
133 mesh.save_to_file(path)?;
134 }
135 mesh.register_hook(Box::new(IntakeDiagnosticHook));
136 mesh.register_hook(Box::new(IntakeClearHook));
137 mesh.register_hook(Box::new(OcelProcessHook::new()));
138 Ok(mesh)
139 }
140
141 pub fn save_to_file(&self, path: &str) -> io::Result<()> {
142 let state = self.to_state();
143 let serialized =
144 serde_json::to_string_pretty(&state).map_err(|e| io::Error::other(e.to_string()))?;
145 std::fs::write(path, serialized)?;
146 Ok(())
147 }
148
149 pub fn add_instance(&mut self, instance: LspInstance) {
150 self.instances.insert(instance.id.clone(), instance);
151 }
152
153 pub fn register_instance(&mut self, id: String) {
154 self.add_instance(LspInstance::new(&id));
155 }
156
157 pub fn register_hook(&mut self, hook: Box<dyn Hook>) {
158 self.hooks.push(hook);
159 }
160
161 pub fn hook_descriptors(&self) -> Vec<crate::runtime::mesh_types::HookDescriptor> {
162 self.hooks.iter().map(|h| h.descriptor()).collect()
163 }
164
165 pub fn dispatch_event(&mut self, event: HookEvent) {
166 if self.dispatch_depth >= MAX_DISPATCH_DEPTH {
167 self.event_log.push(HookEvent::DiagnosticEmitted {
168 instance_id: InstanceId::from("mesh"),
169 diagnostic: Box::new(MaxDiagnostic {
170 lsp: lsp_types_max::Diagnostic {
171 range: lsp_types_max::Range::default(),
172 severity: Some(lsp_types_max::DiagnosticSeverity::ERROR),
173 code: None,
174 code_description: None,
175 source: Some("lsp-max".to_string()),
176 message: format!(
177 "Dispatch depth {} exceeds limit {MAX_DISPATCH_DEPTH}; recursive hook chain terminated",
178 self.dispatch_depth
179 ),
180 related_information: None,
181 tags: None,
182 data: None,
183 },
184 diagnostic_id: format!("dispatch-depth-exceeded-{}", self.dispatch_depth),
185 law_id: "MESH_DISPATCH_DEPTH".to_string(),
186 attempted_transition: None,
187 violated_axes: vec!["recursion-safety".to_string()],
188 doc_routes: vec![],
189 repair_actions: vec![],
190 verification_gates: vec![],
191 receipt_obligation: None,
192 law_axis: lsp_max_protocol::LawAxis::Security,
193 violated_invariant: String::new(),
194 observed_state: serde_json::Value::Null,
195 expected_state: serde_json::Value::Null,
196 repairability: lsp_max_protocol::Repairability::NotRepairable,
197 terminality: lsp_max_protocol::Terminality::Terminal,
198 }),
199 });
200 return;
201 }
202 self.dispatch_depth += 1;
203
204 self.event_log.push(event.clone());
205 if self.event_log.len() > MAX_EVENT_LOG {
206 self.event_log.drain(..self.event_log.len() - MAX_EVENT_LOG);
207 }
208
209 let mut actions = Vec::new();
210 for hook in &self.hooks {
211 let triggered = hook.trigger(&event);
212 actions.extend(triggered);
213 }
214
215 for action in actions {
216 self.execute_action(action);
217 }
218
219 self.dispatch_depth = self.dispatch_depth.saturating_sub(1);
220 }
221
222 pub fn execute_action(&mut self, action: MeshAction) {
223 self.action_seq = self.action_seq.saturating_add(1);
224 let seq = self.action_seq;
225
226 let maybe_instance_id: Option<String> = match &action {
227 MeshAction::AddDiagnostic { instance_id, .. }
228 | MeshAction::ClearDiagnostic { instance_id, .. }
229 | MeshAction::TransitionPolicyState { instance_id, .. }
230 | MeshAction::EmitReceipt { instance_id, .. }
231 | MeshAction::ExecuteBoundedAction { instance_id, .. }
232 | MeshAction::ResetInstance { instance_id } => Some(instance_id.0.clone()),
233 };
234 let old_score: Option<f64> = maybe_instance_id
235 .as_deref()
236 .and_then(|id| self.instances.get(id))
237 .map(|inst| inst.conformance_score());
238
239 match action {
240 MeshAction::TransitionPolicyState {
241 instance_id,
242 new_state,
243 } => {
244 if let Some(instance) = self.instances.get_mut(&instance_id.0) {
245 let old_state = instance
246 .policy_state
247 .clone()
248 .unwrap_or(PolicyState::Operational);
249 instance.policy_state = Some(new_state.clone());
250
251 let event = HookEvent::PolicyStateChanged {
252 instance_id,
253 from_state: old_state,
254 to_state: new_state,
255 };
256 self.dispatch_event(event);
257 }
258 }
259 MeshAction::ClearDiagnostic {
260 instance_id,
261 diagnostic_id,
262 } => {
263 if let Some(instance) = self.instances.get_mut(&instance_id.0) {
264 let old_len = instance.diagnostics.len();
265 instance
266 .diagnostics
267 .retain(|d| d.diagnostic_id != diagnostic_id);
268 if instance.diagnostics.len() < old_len {
269 instance.invalidate_score_cache();
270 let event = HookEvent::DiagnosticCleared {
271 instance_id,
272 diagnostic_id,
273 };
274 self.dispatch_event(event);
275 }
276 }
277 }
278 MeshAction::AddDiagnostic {
279 instance_id,
280 diagnostic,
281 } => {
282 if let Some(instance) = self.instances.get_mut(&instance_id.0) {
283 instance.diagnostics.push((*diagnostic).clone());
284 instance.invalidate_score_cache();
285 let event = HookEvent::DiagnosticEmitted {
286 instance_id,
287 diagnostic,
288 };
289 self.dispatch_event(event);
290 }
291 }
292 MeshAction::EmitReceipt {
293 instance_id,
294 mut receipt,
295 } => {
296 if let Some(instance) = self.instances.get_mut(&instance_id.0) {
297 receipt.hash = sha256(receipt.receipt_id.as_bytes());
298 instance.receipts.push(receipt.clone());
299 let event = HookEvent::ReceiptEmitted {
300 instance_id,
301 receipt,
302 };
303 self.dispatch_event(event);
304 }
305 }
306 MeshAction::ExecuteBoundedAction {
307 instance_id,
308 action_id,
309 description,
310 } => {
311 if action_id == "act-create-refund-receipt" {
312 let receipt_dir =
313 std::env::var("MESH_RECEIPT_DIR").unwrap_or_else(|_| ".".to_string());
314 let file_path = Path::new(&receipt_dir).join("refund_receipt.txt");
315 let content = format!(
316 "REFUND RECEIPT\nInstance: {}\nDescription: {}\nStatus: Executed\nTimestamp: {}\n",
317 instance_id,
318 description,
319 std::time::SystemTime::now()
320 .duration_since(std::time::UNIX_EPOCH)
321 .unwrap_or(std::time::Duration::ZERO)
322 .as_secs()
323 );
324 if let Err(e) = std::fs::write(&file_path, content) {
325 eprintln!(
326 "warn: failed to write receipt to {}: {}",
327 file_path.display(),
328 e
329 );
330 }
331 }
332 self.dispatch_event(HookEvent::BoundedActionExecuted {
333 instance_id,
334 action_id: action_id.clone(),
335 description: description.clone(),
336 });
337 self.executed_bounded_actions.push(action_id);
338 }
339 MeshAction::ResetInstance { instance_id } => {
340 if let Some(instance) = self.instances.get_mut(&instance_id.0) {
341 instance.diagnostics.clear();
342 instance.receipts.clear();
343 instance.policy_state = Some(PolicyState::Operational);
344 instance.invalidate_score_cache();
345 self.dispatch_event(HookEvent::InstanceReset { instance_id });
346 }
347 }
348 }
349
350 if let Some(iid) = maybe_instance_id {
351 if let Some(new_score) = self
352 .instances
353 .get(&iid)
354 .map(|inst| inst.conformance_score())
355 {
356 if let Some(old) = old_score {
357 if (new_score - old).abs() > f64::EPSILON {
358 let entry = ConformanceDeltaEntry {
359 seq,
360 instance_id: iid,
361 old_score: old,
362 new_score,
363 timestamp: String::new(),
364 };
365 self.conformance_delta_log.push_back(entry);
366 if self.conformance_delta_log.len() > MAX_CONFORMANCE_DELTA_LOG {
367 self.conformance_delta_log.pop_front();
368 }
369 }
370 }
371 }
372 }
373 }
374
375 pub fn run_command(&mut self, cmd: &str) -> Result<String, String> {
376 let parts: Vec<&str> = cmd.split_whitespace().collect();
377 if parts.is_empty() {
378 return Err("Empty command".to_string());
379 }
380
381 match parts[0] {
382 "diagnose" => {
383 if parts.len() < 6 {
384 return Err(
385 "Usage: diagnose <instance_id> <diag_id> <law_id> <severity> <msg...>"
386 .to_string(),
387 );
388 }
389 let instance_id = InstanceId::from(parts[1]);
390 let diag_id = parts[2].to_string();
391 let law_id = parts[3].to_string();
392 let severity_str = parts[4];
393 let msg = parts[5..].join(" ");
394
395 let severity = match severity_str {
396 "error" => Some(lsp_types_max::DiagnosticSeverity::ERROR),
397 "warning" => Some(lsp_types_max::DiagnosticSeverity::WARNING),
398 "info" => Some(lsp_types_max::DiagnosticSeverity::INFORMATION),
399 "hint" => Some(lsp_types_max::DiagnosticSeverity::HINT),
400 _ => return Err(format!("Unknown severity: {}", severity_str)),
401 };
402
403 let diagnostic = MaxDiagnostic {
404 lsp: lsp_types_max::Diagnostic {
405 range: lsp_types_max::Range::default(),
406 severity,
407 code: None,
408 code_description: None,
409 source: Some("autonomic-mesh".to_string()),
410 message: msg,
411 related_information: None,
412 tags: None,
413 data: None,
414 },
415 diagnostic_id: diag_id,
416 law_id,
417 attempted_transition: None,
418 violated_axes: vec!["semantic".to_string()],
419 doc_routes: vec![],
420 repair_actions: vec![],
421 verification_gates: vec![],
422 receipt_obligation: None,
423 law_axis: lsp_max_protocol::LawAxis::Domain,
424 violated_invariant: String::new(),
425 observed_state: serde_json::Value::Null,
426 expected_state: serde_json::Value::Null,
427 repairability: lsp_max_protocol::Repairability::Unknown,
428 terminality: lsp_max_protocol::Terminality::NonTerminal,
429 };
430
431 self.execute_action(MeshAction::AddDiagnostic {
432 instance_id: instance_id.clone(),
433 diagnostic: Box::new(diagnostic),
434 });
435
436 Ok(format!("Emitted diagnostic on {}", instance_id))
437 }
438 "clear" => {
439 if parts.len() < 3 {
440 return Err("Usage: clear <instance_id> <diag_id>".to_string());
441 }
442 let instance_id = InstanceId::from(parts[1]);
443 let diag_id = parts[2].to_string();
444
445 self.execute_action(MeshAction::ClearDiagnostic {
446 instance_id: instance_id.clone(),
447 diagnostic_id: diag_id,
448 });
449
450 Ok(format!("Cleared diagnostic on {}", instance_id))
451 }
452 "state" => {
453 if parts.len() < 2 {
454 return Err("Usage: state <instance_id>".to_string());
455 }
456 let instance_id = parts[1];
457 if let Some(inst) = self.instances.get(instance_id) {
458 let policy_str = match &inst.policy_state {
459 Some(p) => format!("{:?}", p),
460 None => "None".to_string(),
461 };
462 Ok(format!(
463 "Instance: {} | Phase: {} | Conformance: {} | PolicyState: {} | Diags: {} | Receipts: {}",
464 inst.id,
465 inst.phase,
466 inst.conformance_score(),
467 policy_str,
468 inst.diagnostics.len(),
469 inst.receipts.len()
470 ))
471 } else {
472 Err(format!("Instance not found: {}", instance_id))
473 }
474 }
475 "patch" => {
476 if parts.len() < 3 {
477 return Err("Usage: patch <instance_id> <policy_state>".to_string());
478 }
479 let instance_id = InstanceId::from(parts[1]);
480 let new_state = parts[2].parse::<PolicyState>()?;
481
482 self.execute_action(MeshAction::TransitionPolicyState {
483 instance_id: instance_id.clone(),
484 new_state,
485 });
486
487 Ok(format!("Patched state on {}", instance_id))
488 }
489 _ => Err(format!("Unknown command: {}", parts[0])),
490 }
491 }
492}