Skip to main content

lsp_max/andon/
analysis.rs

1use 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}