feat(adr): implement ADR-0109, ADR-0110, and ADR-0111

- ADR-0109: Incremental Background AST Indexing & Differential Graph Updates via Tree-Sitter
- ADR-0110: Real-Time SSE Live Activity & Log Stream (/api/events/stream)
- ADR-0111: Automated Stale Symbol Pruning & Graph Tombstoning (sweep_graph_health auto_prune_stale_files)
This commit is contained in:
Riz Ashraf committed 2026-10-07 20:17:19 +01:00
1 parent d792b50343
commit 40cab6142b
6 files changed
+146 -7

No files matched your search

+24 -1
View File
@@ -1044,9 +1044,11 @@ impl McpTool for SweepGraphHealthHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: SweepGraphHealthTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let auto_prune = req.auto_prune_orphans.unwrap_or(false);
let auto_prune_stale = req.auto_prune_stale_files.unwrap_or(false);
let mut orphans = Vec::new();
let mut duplicates = Vec::new();
let mut stale_entities = Vec::new();
state.modify_graph(|g| {
// 1. Identify Orphans
@@ -1062,12 +1064,31 @@ impl McpTool for SweepGraphHealthHandler {
}
}
// ADR-0111: Automated Stale Symbol Pruning & Graph Tombstoning
for (name, entity) in g.entities.iter() {
if entity.entity_type == "File" || name.contains(".rs") || name.contains(".ts") || name.contains(".js") || name.contains(".py") {
let check_path = entity.file_path.as_deref().unwrap_or(name.as_str());
// Strip any symbol qualifiers like File::symbol
let base_file = check_path.split("::").next().unwrap_or(check_path);
if (base_file.contains('/') || base_file.contains('\\') || base_file.ends_with(".rs") || base_file.ends_with(".ts")) && !std::path::Path::new(base_file).exists() {
stale_entities.push(name.clone());
}
}
}
if auto_prune {
for orphan in &orphans {
g.entities.remove(orphan);
}
}
if auto_prune_stale {
for stale in &stale_entities {
g.entities.remove(stale);
g.relations.retain(|r| &r.from != stale && &r.to != stale);
}
}
// 2. Compute similarity pairs for duplicate detection using pre-computed lowercase names
let names: Vec<_> = g.entities.keys().cloned().collect();
let lower_names: Vec<String> = names.iter().map(|n| n.to_lowercase()).collect();
@@ -1092,8 +1113,10 @@ impl McpTool for SweepGraphHealthHandler {
let report = serde_json::json!({
"orphaned_entities": orphans,
"orphans_pruned": auto_prune,
"stale_entities": stale_entities,
"stale_pruned": auto_prune_stale,
"potential_duplicates": duplicates,
"health_score": if orphans.is_empty() && duplicates.is_empty() { "100%" } else { "Needs Maintenance" }
"health_score": if orphans.is_empty() && duplicates.is_empty() && stale_entities.is_empty() { "100%" } else { "Needs Maintenance" }
});
Ok(serde_json::to_string_pretty(&report)?)