Skip to main content

lean_ctx/core/quality_lab/
fidelity.rs

1/// Fidelity classification for compressed output.
2/// Determines what kind of quality guarantee the compression provides.
3#[derive(
4    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
5)]
6pub enum FidelityClassV1 {
7    /// Byte-exact reproduction of the original content.
8    Exact,
9    /// Structural elements preserved (functions, exports, imports, identifiers).
10    /// Content may be reformatted or abbreviated.
11    Structural,
12    /// Meaning preserved but representation changed (summaries, abstractions).
13    /// Requires explicit opt-in via policy.
14    Lossy,
15    /// Fidelity could not be assessed (unknown format, empty content).
16    Unknown,
17}
18
19/// Result of assessing the fidelity of a compression operation.
20#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
21pub struct FidelityAssessment {
22    pub class: FidelityClassV1,
23    pub preservation_score: f64,
24    pub identifier_score: f64,
25    pub density: f64,
26    pub passed_quality_gate: bool,
27}
28
29impl FidelityClassV1 {
30    pub fn is_lossless(&self) -> bool {
31        matches!(self, Self::Exact | Self::Structural)
32    }
33
34    pub fn savings_eligible(&self) -> bool {
35        matches!(self, Self::Exact | Self::Structural)
36    }
37
38    pub fn requires_policy_opt_in(&self) -> bool {
39        matches!(self, Self::Lossy)
40    }
41}
42
43impl FidelityAssessment {
44    pub fn token_savings_pct(&self, original_tokens: usize, compressed_tokens: usize) -> f64 {
45        if original_tokens == 0 {
46            return 0.0;
47        }
48
49        (1.0 - compressed_tokens as f64 / original_tokens as f64) * 100.0
50    }
51}
52
53pub fn assess_fidelity(original: &str, compressed: &str, ext: &str) -> FidelityAssessment {
54    if original == compressed {
55        return FidelityAssessment {
56            class: FidelityClassV1::Exact,
57            preservation_score: 1.0,
58            identifier_score: 1.0,
59            density: 1.0,
60            passed_quality_gate: true,
61        };
62    }
63
64    if original.is_empty() || compressed.is_empty() {
65        return unknown_assessment();
66    }
67
68    let quality_score = crate::core::quality::score(original, compressed, ext);
69    let preservation = crate::core::preservation::measure(original, compressed, ext);
70    let preservation_score = preservation.overall();
71
72    let class = classify(&quality_score, preservation_score);
73
74    FidelityAssessment {
75        class,
76        preservation_score,
77        identifier_score: quality_score.identifier_score,
78        density: quality_score.density,
79        passed_quality_gate: matches!(class, FidelityClassV1::Exact | FidelityClassV1::Structural),
80    }
81}
82
83fn classify(qs: &crate::core::quality::QualityScore, preservation: f64) -> FidelityClassV1 {
84    let has_ast = qs.ast_score > 0.0;
85
86    let structural = if has_ast {
87        qs.composite >= 0.75 && preservation >= 0.90
88    } else {
89        qs.identifier_score >= 0.6 && preservation >= 0.90
90    };
91
92    if structural {
93        return FidelityClassV1::Structural;
94    }
95
96    let signal = if has_ast {
97        qs.composite
98    } else {
99        qs.identifier_score
100    };
101
102    if signal >= 0.4 || preservation >= 0.7 {
103        return FidelityClassV1::Lossy;
104    }
105    if signal < 0.3 && preservation < 0.5 {
106        return FidelityClassV1::Unknown;
107    }
108    FidelityClassV1::Lossy
109}
110
111pub fn assess_fidelity_from_bytes(
112    original: &[u8],
113    compressed: &[u8],
114    ext: &str,
115) -> FidelityAssessment {
116    let Ok(original) = std::str::from_utf8(original) else {
117        return unknown_assessment();
118    };
119    let Ok(compressed) = std::str::from_utf8(compressed) else {
120        return unknown_assessment();
121    };
122
123    assess_fidelity(original, compressed, ext)
124}
125
126fn unknown_assessment() -> FidelityAssessment {
127    FidelityAssessment {
128        class: FidelityClassV1::Unknown,
129        preservation_score: 0.0,
130        identifier_score: 0.0,
131        density: 0.0,
132        passed_quality_gate: false,
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::{FidelityAssessment, FidelityClassV1, assess_fidelity, assess_fidelity_from_bytes};
139
140    #[test]
141    fn test_exact_fidelity_for_identical_content() {
142        let source = "pub fn calculate_total() -> f64 { 42.0 }";
143
144        let assessment = assess_fidelity(source, source, "rs");
145
146        assert_eq!(assessment.class, FidelityClassV1::Exact);
147        assert_eq!(assessment.preservation_score, 1.0);
148        assert!(assessment.passed_quality_gate);
149    }
150
151    #[test]
152    fn test_structural_fidelity_for_good_compression() {
153        let original = r#"
154pub fn calculate_total(items: &[Item]) -> f64 {
155    items.iter().map(|i| i.price * i.quantity as f64).sum()
156}
157
158pub fn format_receipt(total: f64) -> String {
159    format!("Total: ${:.2}", total)
160}
161"#;
162        let compressed = r#"pub fn calculate_total(items: &[Item]) -> f64 {
163    items.iter()
164        .map(|i| i.price * i.quantity as f64)
165        .sum()
166}
167pub fn format_receipt(total: f64) -> String {
168    format!("Total: ${total:.2}")
169}"#;
170
171        let assessment = assess_fidelity(original, compressed, "rs");
172
173        assert_eq!(assessment.class, FidelityClassV1::Structural);
174        assert!(assessment.preservation_score >= 0.95);
175        assert!(assessment.passed_quality_gate);
176    }
177
178    #[test]
179    fn test_lossy_fidelity_for_aggressive_compression() {
180        let original = r#"
181pub fn calculate_total(items: &[Item]) -> f64 {
182    items.iter().map(|i| i.price * i.quantity as f64).sum()
183}
184
185pub fn format_receipt(total: f64) -> String {
186    format!("Total: ${:.2}", total)
187}
188"#;
189        let compressed = "pub fn calculate_total(items: &[Item]) -> f64 { ... }";
190
191        let assessment = assess_fidelity(original, compressed, "rs");
192
193        assert_eq!(assessment.class, FidelityClassV1::Lossy);
194        assert!(!assessment.passed_quality_gate);
195    }
196
197    #[test]
198    fn test_unknown_fidelity_for_empty_input() {
199        let assessment = assess_fidelity("", "summary", "txt");
200
201        assert_eq!(assessment.class, FidelityClassV1::Unknown);
202        assert!(!assessment.passed_quality_gate);
203    }
204
205    #[test]
206    fn test_exact_is_lossless() {
207        assert!(FidelityClassV1::Exact.is_lossless());
208    }
209
210    #[test]
211    fn test_lossy_requires_policy() {
212        assert!(FidelityClassV1::Lossy.requires_policy_opt_in());
213        assert!(!FidelityClassV1::Structural.requires_policy_opt_in());
214    }
215
216    #[test]
217    fn test_savings_pct_calculation() {
218        let assessment = FidelityAssessment {
219            class: FidelityClassV1::Structural,
220            preservation_score: 1.0,
221            identifier_score: 1.0,
222            density: 1.0,
223            passed_quality_gate: true,
224        };
225
226        assert_eq!(assessment.token_savings_pct(100, 40), 60.0);
227        assert_eq!(assessment.token_savings_pct(0, 40), 0.0);
228    }
229
230    #[test]
231    fn test_bytes_assessment_with_valid_utf8() {
232        let source = b"pub fn total() -> usize { 42 }";
233
234        let assessment = assess_fidelity_from_bytes(source, source, "rs");
235
236        assert_eq!(assessment.class, FidelityClassV1::Exact);
237    }
238
239    #[test]
240    fn test_bytes_assessment_with_invalid_utf8() {
241        let assessment = assess_fidelity_from_bytes(&[0xff], b"summary", "txt");
242
243        assert_eq!(assessment.class, FidelityClassV1::Unknown);
244        assert!(!assessment.passed_quality_gate);
245    }
246
247    #[test]
248    fn test_fidelity_ordering() {
249        assert!(FidelityClassV1::Exact < FidelityClassV1::Structural);
250        assert!(FidelityClassV1::Structural < FidelityClassV1::Lossy);
251        assert!(FidelityClassV1::Lossy < FidelityClassV1::Unknown);
252    }
253
254    #[test]
255    fn test_savings_eligibility() {
256        assert!(FidelityClassV1::Exact.savings_eligible());
257        assert!(FidelityClassV1::Structural.savings_eligible());
258        assert!(!FidelityClassV1::Lossy.savings_eligible());
259        assert!(!FidelityClassV1::Unknown.savings_eligible());
260    }
261}