mur_common/skill/
env_class.rs1#[derive(Debug, Clone, PartialEq)]
11pub struct EnvClassification {
12 pub class: &'static str,
14 pub confidence: f64,
15}
16
17const ENV_MARKERS: &[&str] = &[
19 "connection refused",
20 "connection reset",
21 "timed out",
22 "timeout",
23 "could not resolve",
24 "temporary failure in name resolution",
25 "network is unreachable",
26 "tls",
27 "certificate",
28 "401",
29 "403",
30 "unauthorized",
31 "forbidden",
32 "credential",
33 "permission denied",
34 "no such file or directory",
35 "command not found",
36 "not found in path",
37 "rate limit",
38 "429",
39 "disk full",
40 "no space left",
41];
42
43pub fn classify_failure(stderr: &str) -> EnvClassification {
47 let lower = stderr.to_lowercase();
48 let hits = ENV_MARKERS.iter().filter(|m| lower.contains(*m)).count();
49 match hits {
50 0 => EnvClassification {
51 class: "workflow",
52 confidence: 0.6,
53 },
54 1 => EnvClassification {
55 class: "env",
56 confidence: 0.6,
57 },
58 _ => EnvClassification {
59 class: "env",
60 confidence: 0.9,
61 },
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn clean_assertion_failure_is_workflow() {
71 let c = classify_failure("assertion failed: expected 3 got 2");
72 assert_eq!(c.class, "workflow");
73 }
74
75 #[test]
76 fn network_error_is_env() {
77 let c = classify_failure("curl: (7) Connection refused");
78 assert_eq!(c.class, "env");
79 }
80
81 #[test]
82 fn multiple_markers_high_confidence_env() {
83 let c = classify_failure("error: timed out\ncurl: connection refused after retry");
84 assert_eq!(c.class, "env");
85 assert!(c.confidence > 0.8);
86 }
87}