Skip to main content

agentic_codebase/semantic/
concept_nav.rs

1//! Concept Navigation — Invention 7.
2//!
3//! Navigate by CONCEPT, not by filename or keyword. "Where is authentication
4//! handled?" finds the answer without guessing filenames.
5
6use serde::{Deserialize, Serialize};
7
8use crate::graph::CodeGraph;
9use crate::types::CodeUnitType;
10
11// ── Types ────────────────────────────────────────────────────────────────────
12
13/// A semantic concept in the codebase.
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct CodeConcept {
16    /// Concept name.
17    pub name: String,
18    /// Description.
19    pub description: String,
20    /// Nodes that implement this concept.
21    pub implementations: Vec<ConceptImplementation>,
22    /// Related concepts.
23    pub related: Vec<String>,
24    /// Confidence this concept exists.
25    pub confidence: f64,
26}
27
28/// A node implementing a concept.
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct ConceptImplementation {
31    /// The implementing node.
32    pub node_id: u64,
33    /// Node name.
34    pub name: String,
35    /// File path.
36    pub file_path: String,
37    /// How strongly it implements the concept.
38    pub strength: f64,
39    /// What aspect it implements.
40    pub aspect: String,
41}
42
43/// Query for navigating to a concept.
44#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct ConceptQuery {
46    /// Natural language description.
47    pub description: String,
48    /// Optional constraints.
49    pub constraints: Vec<ConceptConstraint>,
50}
51
52/// Constraint on a concept query.
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub enum ConceptConstraint {
55    /// Must be in specific module.
56    InModule(String),
57    /// Must be specific type.
58    OfType(String),
59    /// Must have specific pattern.
60    HasPattern(String),
61}
62
63// ── Built-in concept definitions ─────────────────────────────────────────────
64
65struct ConceptDef {
66    name: &'static str,
67    description: &'static str,
68    keywords: &'static [&'static str],
69    related: &'static [&'static str],
70}
71
72const CONCEPTS: &[ConceptDef] = &[
73    ConceptDef {
74        name: "Authentication",
75        description: "User identity verification and access control",
76        keywords: &[
77            "auth",
78            "login",
79            "logout",
80            "token",
81            "jwt",
82            "oauth",
83            "password",
84            "session",
85            "credential",
86        ],
87        related: &["Authorization", "User Management", "Security"],
88    },
89    ConceptDef {
90        name: "Payment",
91        description: "Financial transaction processing",
92        keywords: &[
93            "payment", "charge", "stripe", "paypal", "billing", "checkout", "invoice", "refund",
94        ],
95        related: &["User Management", "Configuration"],
96    },
97    ConceptDef {
98        name: "User Management",
99        description: "User accounts, profiles, and registration",
100        keywords: &[
101            "user",
102            "account",
103            "profile",
104            "registration",
105            "signup",
106            "onboard",
107        ],
108        related: &["Authentication", "Database"],
109    },
110    ConceptDef {
111        name: "Database",
112        description: "Data persistence and querying",
113        keywords: &[
114            "database",
115            "db",
116            "query",
117            "sql",
118            "migration",
119            "schema",
120            "repository",
121            "orm",
122            "model",
123        ],
124        related: &["Caching", "Configuration"],
125    },
126    ConceptDef {
127        name: "API",
128        description: "External interface endpoints",
129        keywords: &[
130            "api",
131            "endpoint",
132            "route",
133            "handler",
134            "controller",
135            "rest",
136            "graphql",
137            "grpc",
138        ],
139        related: &["Authentication", "Error Handling"],
140    },
141    ConceptDef {
142        name: "Logging",
143        description: "Application logging and telemetry",
144        keywords: &[
145            "log",
146            "logger",
147            "trace",
148            "debug",
149            "warn",
150            "error",
151            "metric",
152            "telemetry",
153            "monitor",
154        ],
155        related: &["Error Handling", "Configuration"],
156    },
157    ConceptDef {
158        name: "Configuration",
159        description: "Application configuration and settings",
160        keywords: &[
161            "config",
162            "setting",
163            "env",
164            "environment",
165            "option",
166            "feature_flag",
167        ],
168        related: &["Logging"],
169    },
170    ConceptDef {
171        name: "Testing",
172        description: "Test infrastructure and utilities",
173        keywords: &[
174            "test",
175            "mock",
176            "stub",
177            "fixture",
178            "assert",
179            "spec",
180            "bench",
181            "integration_test",
182        ],
183        related: &["Error Handling"],
184    },
185    ConceptDef {
186        name: "Error Handling",
187        description: "Error management and recovery",
188        keywords: &[
189            "error",
190            "exception",
191            "retry",
192            "fallback",
193            "panic",
194            "throw",
195            "recover",
196            "result",
197        ],
198        related: &["Logging", "API"],
199    },
200    ConceptDef {
201        name: "Caching",
202        description: "Data caching for performance",
203        keywords: &[
204            "cache",
205            "memoize",
206            "lru",
207            "ttl",
208            "redis",
209            "memcached",
210            "invalidate",
211        ],
212        related: &["Database", "Configuration"],
213    },
214    ConceptDef {
215        name: "Rate Limiting",
216        description: "Request throttling and rate control",
217        keywords: &["rate_limit", "throttle", "backoff", "quota", "bucket"],
218        related: &["Authentication", "API"],
219    },
220    ConceptDef {
221        name: "Security",
222        description: "Security measures and vulnerability protection",
223        keywords: &[
224            "security", "encrypt", "decrypt", "hash", "sanitize", "xss", "csrf", "cors",
225        ],
226        related: &["Authentication", "API"],
227    },
228];
229
230// ── ConceptNavigator ─────────────────────────────────────────────────────────
231
232/// Navigate code by semantic concepts.
233pub struct ConceptNavigator<'g> {
234    graph: &'g CodeGraph,
235}
236
237impl<'g> ConceptNavigator<'g> {
238    pub fn new(graph: &'g CodeGraph) -> Self {
239        Self { graph }
240    }
241
242    /// Find code implementing a concept.
243    pub fn find_concept(&self, query: ConceptQuery) -> Vec<CodeConcept> {
244        let query_lower = query.description.to_lowercase();
245        let mut results = Vec::new();
246
247        for def in CONCEPTS {
248            let name_match = def.name.to_lowercase().contains(&query_lower)
249                || query_lower.contains(&def.name.to_lowercase());
250            let keyword_match = def.keywords.iter().any(|k| query_lower.contains(k));
251
252            if name_match || keyword_match {
253                let concept = self.build_concept(def, &query.constraints);
254                if !concept.implementations.is_empty() {
255                    results.push(concept);
256                }
257            }
258        }
259
260        // Sort by confidence descending
261        results.sort_by(|a, b| {
262            b.confidence
263                .partial_cmp(&a.confidence)
264                .unwrap_or(std::cmp::Ordering::Equal)
265        });
266        results
267    }
268
269    /// Map all concepts in the codebase.
270    pub fn map_all_concepts(&self) -> Vec<CodeConcept> {
271        let mut results = Vec::new();
272        for def in CONCEPTS {
273            let concept = self.build_concept(def, &[]);
274            if !concept.implementations.is_empty() {
275                results.push(concept);
276            }
277        }
278        results.sort_by(|a, b| {
279            b.confidence
280                .partial_cmp(&a.confidence)
281                .unwrap_or(std::cmp::Ordering::Equal)
282        });
283        results
284    }
285
286    /// Explain how a specific concept is implemented.
287    pub fn explain_concept(&self, name: &str) -> Option<CodeConcept> {
288        let name_lower = name.to_lowercase();
289        for def in CONCEPTS {
290            if def.name.to_lowercase() == name_lower {
291                let concept = self.build_concept(def, &[]);
292                return Some(concept);
293            }
294        }
295        None
296    }
297
298    // ── Internal ─────────────────────────────────────────────────────────
299
300    fn build_concept(&self, def: &ConceptDef, constraints: &[ConceptConstraint]) -> CodeConcept {
301        let mut implementations = Vec::new();
302
303        for unit in self.graph.units() {
304            let name_lower = unit.name.to_lowercase();
305            let qname_lower = unit.qualified_name.to_lowercase();
306
307            // Score how well this unit matches the concept
308            let mut score = 0.0f64;
309            for keyword in def.keywords {
310                if name_lower.contains(keyword) {
311                    score += 0.4;
312                }
313                if qname_lower.contains(keyword) {
314                    score += 0.2;
315                }
316                if let Some(ref doc) = unit.doc_summary {
317                    if doc.to_lowercase().contains(keyword) {
318                        score += 0.15;
319                    }
320                }
321            }
322
323            // Type bonus
324            if unit.unit_type == CodeUnitType::Function || unit.unit_type == CodeUnitType::Type {
325                score += 0.05;
326            }
327
328            if score < 0.3 {
329                continue;
330            }
331
332            // Apply constraints
333            let passes_constraints = constraints.iter().all(|c| match c {
334                ConceptConstraint::InModule(m) => qname_lower.contains(&m.to_lowercase()),
335                ConceptConstraint::OfType(t) => {
336                    unit.unit_type.label().to_lowercase() == t.to_lowercase()
337                }
338                ConceptConstraint::HasPattern(_) => true, // Simplified
339            });
340
341            if !passes_constraints {
342                continue;
343            }
344
345            let aspect = if unit.unit_type == CodeUnitType::Function {
346                "implementation".to_string()
347            } else if unit.unit_type == CodeUnitType::Type {
348                "definition".to_string()
349            } else if unit.unit_type == CodeUnitType::Test {
350                "test".to_string()
351            } else {
352                "usage".to_string()
353            };
354
355            implementations.push(ConceptImplementation {
356                node_id: unit.id,
357                name: unit.name.clone(),
358                file_path: unit.file_path.display().to_string(),
359                strength: score.min(1.0),
360                aspect,
361            });
362        }
363
364        // Sort by strength descending
365        implementations.sort_by(|a, b| {
366            b.strength
367                .partial_cmp(&a.strength)
368                .unwrap_or(std::cmp::Ordering::Equal)
369        });
370
371        let confidence = if implementations.is_empty() {
372            0.0
373        } else {
374            let top_strength = implementations[0].strength;
375            (top_strength * 0.6 + (implementations.len() as f64 * 0.1).min(0.4)).min(1.0)
376        };
377
378        CodeConcept {
379            name: def.name.to_string(),
380            description: def.description.to_string(),
381            implementations,
382            related: def.related.iter().map(|s| s.to_string()).collect(),
383            confidence,
384        }
385    }
386}
387
388// ── Tests ────────────────────────────────────────────────────────────────────
389
390#[cfg(test)]
391mod tests {
392    use super::*;
393    use crate::types::{CodeUnit, CodeUnitType, Language, Span};
394    use std::path::PathBuf;
395
396    fn test_graph() -> CodeGraph {
397        let mut graph = CodeGraph::with_default_dimension();
398        graph.add_unit(CodeUnit::new(
399            CodeUnitType::Function,
400            Language::Python,
401            "authenticate_user".to_string(),
402            "auth.authenticate_user".to_string(),
403            PathBuf::from("src/auth.py"),
404            Span::new(1, 0, 30, 0),
405        ));
406        graph.add_unit(CodeUnit::new(
407            CodeUnitType::Function,
408            Language::Python,
409            "process_payment".to_string(),
410            "payments.process_payment".to_string(),
411            PathBuf::from("src/payments.py"),
412            Span::new(1, 0, 20, 0),
413        ));
414        graph.add_unit(CodeUnit::new(
415            CodeUnitType::Function,
416            Language::Python,
417            "cache_result".to_string(),
418            "utils.cache_result".to_string(),
419            PathBuf::from("src/utils.py"),
420            Span::new(1, 0, 15, 0),
421        ));
422        graph
423    }
424
425    #[test]
426    fn find_authentication_concept() {
427        let graph = test_graph();
428        let nav = ConceptNavigator::new(&graph);
429        let results = nav.find_concept(ConceptQuery {
430            description: "authentication".to_string(),
431            constraints: Vec::new(),
432        });
433        assert!(!results.is_empty());
434        assert_eq!(results[0].name, "Authentication");
435        assert!(!results[0].implementations.is_empty());
436    }
437
438    #[test]
439    fn map_all_finds_concepts() {
440        let graph = test_graph();
441        let nav = ConceptNavigator::new(&graph);
442        let all = nav.map_all_concepts();
443        // Should find at least Authentication, Payment, Caching
444        assert!(all.len() >= 2);
445    }
446
447    #[test]
448    fn explain_concept_works() {
449        let graph = test_graph();
450        let nav = ConceptNavigator::new(&graph);
451        let explained = nav.explain_concept("Payment");
452        assert!(explained.is_some());
453        let c = explained.unwrap();
454        assert!(!c.implementations.is_empty());
455    }
456}