Skip to main content

lean_ctx/core/
adaptive.rs

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