Skip to main content

entrenar/citl/trainer/
correlation.rs

1//! Error correlation types for CITL trainer
2
3use super::{DecisionTrace, SourceSpan};
4use crate::citl::FixSuggestion;
5
6/// Result of error correlation analysis
7#[derive(Debug, Clone)]
8pub struct ErrorCorrelation {
9    /// The error code being analyzed
10    pub error_code: String,
11    /// Error span where the error occurred
12    pub error_span: SourceSpan,
13    /// Decisions that may have contributed to the error (sorted by suspiciousness)
14    pub suspicious_decisions: Vec<SuspiciousDecision>,
15    /// Suggested fixes from the pattern store
16    pub fix_suggestions: Vec<FixSuggestion>,
17}
18
19/// A decision suspected of contributing to an error
20#[derive(Debug, Clone)]
21pub struct SuspiciousDecision {
22    /// The decision trace
23    pub decision: DecisionTrace,
24    /// Suspiciousness score (0.0 to 1.0)
25    pub suspiciousness: f32,
26    /// Reason for suspicion
27    pub reason: String,
28}
29
30impl SuspiciousDecision {
31    /// Create a new suspicious decision
32    #[must_use]
33    pub fn new(decision: DecisionTrace, suspiciousness: f32, reason: impl Into<String>) -> Self {
34        Self { decision, suspiciousness: suspiciousness.clamp(0.0, 1.0), reason: reason.into() }
35    }
36}
37
38#[cfg(test)]
39mod tests {
40    use super::*;
41
42    #[test]
43    fn test_suspicious_decision_new() {
44        let trace = DecisionTrace::new("d1", "type", "desc");
45        let suspicious = SuspiciousDecision::new(trace, 0.8, "high suspicion");
46        assert_eq!(suspicious.suspiciousness, 0.8);
47    }
48
49    #[test]
50    fn test_suspicious_decision_clamped() {
51        let trace = DecisionTrace::new("d1", "type", "desc");
52        let suspicious = SuspiciousDecision::new(trace, 1.5, "over max");
53        assert_eq!(suspicious.suspiciousness, 1.0);
54    }
55}
56
57#[cfg(test)]
58mod prop_tests {
59    use super::*;
60    use proptest::prelude::*;
61
62    proptest! {
63        #[test]
64        fn prop_suspiciousness_clamped(score in -10.0f32..10.0) {
65            let trace = DecisionTrace::new("d", "type", "desc");
66            let suspicious = SuspiciousDecision::new(trace, score, "reason");
67            prop_assert!(suspicious.suspiciousness >= 0.0);
68            prop_assert!(suspicious.suspiciousness <= 1.0);
69        }
70    }
71}