lsp_max/andon/
analysis.rs1use crate::andon::andon::AndonEvent;
2use crate::andon::core::{InvariantRegistry, Severity};
3
4pub struct AnalysisPipeline;
5
6impl AnalysisPipeline {
7 pub fn evaluate_registry(registry: &InvariantRegistry) -> Vec<AndonEvent> {
8 let mut events = Vec::new();
9
10 if registry.is_empty() {
11 events.push(AndonEvent {
12 id: "LSPMAX-INVARIANT-EMPTY-REGISTRY".to_string(),
13 severity: Severity::Stop,
14 code: "LSPMAX-INVARIANT-EMPTY-REGISTRY".to_string(),
15 title: "Empty Invariant Registry".to_string(),
16 message: "InvariantRegistry.empty() implies ANDON. A project with no invariants is blind.".to_string(),
17 invariant_id: None,
18 observed_state: Some("invariants_count=0".to_string()),
19 expected_state: Some("invariants_count>0".to_string()),
20 blocking: true,
21 requires_ack: true,
22 admission_allowed: false,
23 next_lawful_step: Some("define_project_invariants".to_string()),
24 required_command: Some("bcinrPddl.openVirtualDocument".to_string()),
25 evidence_uri: None,
26 virtual_doc_uri: Some("lsp-max://truth/andon".to_string()),
27 receipt_required: false,
28 });
29 return events;
30 }
31
32 for inv in registry.get_all() {
33 if inv.true_probe.is_none() {
34 events.push(Self::build_missing_probe_event(
35 inv.id.clone(),
36 "LSPMAX-INVARIANT-TRUE-CASE-MISSING",
37 "TRUE",
38 "true_probe missing",
39 ));
40 }
41 if inv.false_probe.is_none() {
42 events.push(Self::build_missing_probe_event(
43 inv.id.clone(),
44 "LSPMAX-INVARIANT-FALSE-CASE-MISSING",
45 "FALSE",
46 "false_probe missing",
47 ));
48 }
49 if inv.counterfactual_probe.is_none() {
50 events.push(Self::build_missing_probe_event(
51 inv.id.clone(),
52 "LSPMAX-INVARIANT-COUNTERFACTUAL-MISSING",
53 "COUNTERFACTUAL",
54 "counterfactual_probe missing",
55 ));
56 }
57 if inv.witness_rule.is_none() {
58 events.push(Self::build_missing_probe_event(
59 inv.id.clone(),
60 "LSPMAX-WITNESS-MISSING",
61 "WITNESS",
62 "witness missing",
63 ));
64 }
65 if inv.blocks && inv.repair_rule.is_none() {
66 events.push(Self::build_missing_probe_event(
67 inv.id.clone(),
68 "LSPMAX-REPAIR-MISSING",
69 "REPAIR",
70 "repair missing on block",
71 ));
72 }
73 }
74
75 events
76 }
77
78 fn build_missing_probe_event(
79 invariant_id: String,
80 code: &str,
81 probe_type: &str,
82 message: &str,
83 ) -> AndonEvent {
84 AndonEvent {
85 id: format!("andon-{}-{}", invariant_id, code.to_lowercase()),
86 severity: Severity::Stop,
87 code: code.to_string(),
88 title: format!("Missing {} in {}", probe_type, invariant_id),
89 message: message.to_string(),
90 invariant_id: Some(invariant_id),
91 observed_state: Some(format!("{} missing", probe_type)),
92 expected_state: Some(format!("{} defined", probe_type)),
93 blocking: true,
94 requires_ack: true,
95 admission_allowed: false,
96 next_lawful_step: Some(format!("define_{}", probe_type.to_lowercase())),
97 required_command: None,
98 evidence_uri: None,
99 virtual_doc_uri: Some("lsp-max://truth/andon".to_string()),
100 receipt_required: false,
101 }
102 }
103}