Skip to main content

lean_ctx/core/
adaptive.rs

1#![allow(dead_code)]
2use crate::core::cache::SessionCache;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum TaskComplexity {
6    Mechanical,
7    Standard,
8    Architectural,
9}
10
11impl TaskComplexity {
12    pub fn instruction_suffix(&self) -> &'static str {
13        match self {
14            TaskComplexity::Mechanical => {
15                "TASK COMPLEXITY: mechanical\n\
16                 Minimal reasoning needed. Act immediately, report result in one line."
17            }
18            TaskComplexity::Standard => {
19                "TASK COMPLEXITY: standard\n\
20                 Brief reasoning allowed. Summarize approach in 1-2 lines, then act."
21            }
22            TaskComplexity::Architectural => {
23                "TASK COMPLEXITY: architectural\n\
24                 Full reasoning expected. Outline approach, consider edge cases, then act."
25            }
26        }
27    }
28
29    pub fn encoded_suffix(&self) -> String {
30        use crate::core::protocol::encode_instructions;
31        match self {
32            TaskComplexity::Mechanical => encode_instructions("mechanical"),
33            TaskComplexity::Standard => encode_instructions("standard"),
34            TaskComplexity::Architectural => encode_instructions("architectural"),
35        }
36    }
37
38    fn complexity_label(&self) -> &'static str {
39        match self {
40            TaskComplexity::Mechanical => "mechanical",
41            TaskComplexity::Standard => "standard",
42            TaskComplexity::Architectural => "architectural",
43        }
44    }
45}
46
47pub fn classify_from_context(cache: &SessionCache) -> TaskComplexity {
48    let stats = cache.get_stats();
49    let unique_files = cache.get_all_entries().len();
50    let total_reads = stats.total_reads;
51
52    if unique_files <= 1 && total_reads <= 3 {
53        return TaskComplexity::Mechanical;
54    }
55
56    if unique_files >= 5 || total_reads >= 15 {
57        return TaskComplexity::Architectural;
58    }
59
60    TaskComplexity::Standard
61}
62
63pub fn classify_from_signals(
64    file_count: usize,
65    has_tests: bool,
66    has_multi_lang: bool,
67) -> TaskComplexity {
68    if has_tests && file_count >= 5 {
69        return TaskComplexity::Architectural;
70    }
71
72    if has_multi_lang || file_count >= 3 {
73        return TaskComplexity::Standard;
74    }
75
76    TaskComplexity::Mechanical
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn test_mechanical_classification() {
85        let result = classify_from_signals(1, false, false);
86        assert_eq!(result, TaskComplexity::Mechanical);
87    }
88
89    #[test]
90    fn test_standard_classification() {
91        let result = classify_from_signals(3, false, false);
92        assert_eq!(result, TaskComplexity::Standard);
93    }
94
95    #[test]
96    fn test_architectural_classification() {
97        let result = classify_from_signals(5, true, false);
98        assert_eq!(result, TaskComplexity::Architectural);
99    }
100
101    #[test]
102    fn test_multi_lang_triggers_standard() {
103        let result = classify_from_signals(1, false, true);
104        assert_eq!(result, TaskComplexity::Standard);
105    }
106
107    #[test]
108    fn test_instruction_suffix_not_empty() {
109        assert!(!TaskComplexity::Mechanical.instruction_suffix().is_empty());
110        assert!(!TaskComplexity::Standard.instruction_suffix().is_empty());
111        assert!(!TaskComplexity::Architectural
112            .instruction_suffix()
113            .is_empty());
114    }
115
116    #[test]
117    fn test_context_based_mechanical() {
118        let cache = SessionCache::new();
119        let result = classify_from_context(&cache);
120        assert_eq!(result, TaskComplexity::Mechanical);
121    }
122}