--- name: living-codebase-ast-graph description: "Master guide for in-memory Abstract Syntax Tree (AST) code knowledge graphs, multi-language symbol dependency mapping (TS, Python, Go, Rust), refactoring blast radius calculation, circular cycle detection (Tarjan SCC), and topological swarm generation / Panduan utama graf pengetahuan AST kode in-memory, pemetaan dependensi multi-bahasa, kalkulasi radius dampak refaktor, deteksi siklus dependensi, dan pengurutan topologis swarm." author: "Roedy Rustam" version: "4.4.2" --- # Living Codebase AST Graph (2026 Sovereign Edition) [English](#english) | [Bahasa Indonesia](#bahasa-indonesia) --- ## English ### Executive Directive & The AST Knowledge Paradigm (2026 Shift) Fuzzy text matching (Ripgrep, regex, or basic keyword search) is catastrophic for large-scale autonomous refactoring. Renaming a function like `init()` or `format()` via global search-and-replace corrupts unrelated variables, breaks third-party interfaces, and silently introduces critical bugs. **The Living Codebase AST Graph** represents a codebase not as flat text files, but as a dynamic, queryable **neural knowledge graph** built from native Abstract Syntax Trees across **TypeScript, Python, Go, and Rust**. It enables autonomous AI swarms to: 1. **Calculate Blast Radius**: Determine every inbound caller, interface consumer, and database dependent before modifying any symbol. 2. **Detect Cycles Mathematically**: Identify circular dependency loops using Tarjan's Strongly Connected Components (SCC) algorithm. 3. **Enforce Topological Generation Order**: Sequence multi-agent code generation so base schemas and types are completed before downstream controllers or UI widgets are constructed. 4. **Persist Across Sessions via XERJ MCP**: Store the graph in unified episodic memory (`xerj_brain_link`, `xerj_memory_store`) for instant context retrieval across agent restarts. --- ### Orchestration & Integration - **Upstream Swarm Orchestration**: `zero-to-prod-orchestrator`, `brainstorming`, `session-memory-manager`. - **AST Mapping & Type Systems**: `typescript-expert`, `python-programming-expert`, `go-programming-expert`, `rust-programming-expert`. - **Refactoring & Debt Eradication**: `scalability-clean-code`, `legacy-code-translator`, `zero-tech-debt-auditor`, `anti-slop`. - **Persistent Graph Memory**: `xerj-search-expert`, `vector-db-rag-expert`. --- ### Core Architectural Graph Topologies ``` ┌────────────────────────────────────────────────────────────────────────┐ │ LIVING CODEBASE AST GRAPH PIPELINE │ ├────────────────────────────────────────────────────────────────────────┤ │ 1. Multi-Language Source Code (TypeScript, Python, Go, Rust) │ │ │ │ │ ▼ │ │ 2. Native AST Parsers (ts.createProgram, ast.parse, go/parser, syn) │ │ │ │ │ ▼ │ │ 3. Symbol Registration & In-Memory Graph Index │ │ • Node: SymbolKey (FileUri#SymbolName, Kind, Signature) │ │ • Inbound Edges (Callers) | Outbound Edges (Dependencies) │ │ │ │ │ ┌─────────────────────────┼─────────────────────────┐ │ │ ▼ ▼ ▼ │ │ [Blast Radius] [Tarjan SCC Cycles] [Topological DAG] │ │ Recursive Inbound Detect Circular Deps Sequence Multi-Agent │ │ Caller Traversal Before Build Crash Swarm Generation │ │ │ │ │ │ │ └─────────────────────────┼─────────────────────────┘ │ │ ▼ │ │ 4. Persistent Memory Serialization (XERJ Graph Brain / Memory Store) │ └────────────────────────────────────────────────────────────────────────┘ ``` #### 1. Blast Radius Risk Categorization When a symbol $S$ is mutated, its blast radius $B(S)$ is calculated by transitively traversing all inbound dependency edges: - **Critical Risk (Public Contract)**: Exported API route handlers, shared database DDL schemas, or public SDK interfaces. - **Moderate Risk (Service Layer)**: Internal business logic interactors and domain services. - **Low Risk (Leaf Utility)**: Standalone private helper functions with zero external consumers. #### 2. Tarjan's Strongly Connected Components (Cycle Detection) Circular dependencies (`A -> B -> C -> A`) cause subtle runtime `undefined` initialization bugs in modern bundlers and runtimes. The living graph continuously runs Tarjan's SCC to isolate cycles and prevent commits until the cycle is untangled. #### 3. Kahn's Topological Sorter for Swarm Task Distribution Autonomous subagents must never attempt to write code for a consumer module before the producer module's interface is finalized. Kahn's algorithm groups AST nodes into dependency tiers (Tier 0 to Tier $N$) for parallel multi-agent execution. --- ### Production Implementation Blueprints #### Blueprint 1: Multi-Language AST Graph & Blast Radius Engine (`engine/codebase-ast-graph.ts`) ```typescript import ts from 'typescript'; import path from 'path'; export interface AstSymbolNode { id: string; // filePath#symbolName filePath: string; symbolName: string; kind: 'function' | 'class' | 'interface' | 'type' | 'variable'; exported: boolean; dependencies: string[]; // outbound edges } export interface BlastRadiusReport { targetSymbol: string; totalAffectedNodes: number; criticalPaths: string[]; affectedFiles: string[]; } export class LivingCodebaseAstGraph { private nodes: Map = new Map(); private outboundEdges: Map> = new Map(); private inboundEdges: Map> = new Map(); constructor(private rootFiles: string[], private compilerOptions: ts.CompilerOptions = {}) { this.buildTypeScriptGraph(); } private buildTypeScriptGraph(): void { const program = ts.createProgram(this.rootFiles, { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.NodeNext, ...this.compilerOptions, }); const checker = program.getTypeChecker(); for (const sourceFile of program.getSourceFiles()) { if (sourceFile.isDeclarationFile || sourceFile.fileName.includes('node_modules')) { continue; } const filePath = path.resolve(sourceFile.fileName); ts.forEachChild(sourceFile, (node) => { if (ts.isFunctionDeclaration(node) && node.name) { this.indexNode(filePath, node.name.text, 'function', node, sourceFile, checker); } else if (ts.isClassDeclaration(node) && node.name) { this.indexNode(filePath, node.name.text, 'class', node, sourceFile, checker); } else if (ts.isInterfaceDeclaration(node) && node.name) { this.indexNode(filePath, node.name.text, 'interface', node, sourceFile, checker); } else if (ts.isTypeAliasDeclaration(node) && node.name) { this.indexNode(filePath, node.name.text, 'type', node, sourceFile, checker); } }); } } private indexNode( filePath: string, name: string, kind: AstSymbolNode['kind'], node: ts.Node, sourceFile: ts.SourceFile, checker: ts.TypeChecker ): void { const symbolId = `${filePath}#${name}`; const dependencies: string[] = []; const isExported = Boolean( ts.getCombinedModifierFlags(node as ts.Declaration) & ts.ModifierFlags.Export ); const visit = (child: ts.Node) => { if (ts.isIdentifier(child)) { const symbol = checker.getSymbolAtLocation(child); if (symbol?.declarations && symbol.declarations.length > 0) { const targetDeclFile = symbol.declarations[0].getSourceFile().fileName; if (!targetDeclFile.includes('node_modules') && !targetDeclFile.endsWith('.d.ts')) { const targetId = `${path.resolve(targetDeclFile)}#${symbol.getName()}`; if (targetId !== symbolId) { dependencies.push(targetId); } } } } ts.forEachChild(child, visit); }; visit(node); this.nodes.set(symbolId, { id: symbolId, filePath, symbolName: name, kind, exported: isExported, dependencies, }); if (!this.outboundEdges.has(symbolId)) { this.outboundEdges.set(symbolId, new Set()); } for (const dep of dependencies) { this.outboundEdges.get(symbolId)!.add(dep); if (!this.inboundEdges.has(dep)) { this.inboundEdges.set(dep, new Set()); } this.inboundEdges.get(dep)!.add(symbolId); } } public calculateBlastRadius(symbolId: string): BlastRadiusReport { const affected = new Set(); const queue = [symbolId]; const affectedFiles = new Set(); while (queue.length > 0) { const current = queue.shift()!; const callers = this.inboundEdges.get(current) || new Set(); for (const caller of callers) { if (!affected.has(caller)) { affected.add(caller); const [file] = caller.split('#'); affectedFiles.add(file); queue.push(caller); } } } const criticalPaths = Array.from(affected).filter((id) => { const node = this.nodes.get(id); return node?.exported && (id.includes('/api/') || id.includes('/routes/') || id.includes('/models/')); }); return { targetSymbol: symbolId, totalAffectedNodes: affected.size, criticalPaths, affectedFiles: Array.from(affectedFiles), }; } public detectCircularDependencies(): string[][] { let index = 0; const stack: string[] = []; const indices = new Map(); const lowlink = new Map(); const onStack = new Set(); const cycles: string[][] = []; const strongConnect = (v: string) => { indices.set(v, index); lowlink.set(v, index); index += 1; stack.push(v); onStack.add(v); const neighbors = this.outboundEdges.get(v) || new Set(); for (const w of neighbors) { if (!indices.has(w)) { strongConnect(w); lowlink.set(v, Math.min(lowlink.get(v)!, lowlink.get(w)!)); } else if (onStack.has(w)) { lowlink.set(v, Math.min(lowlink.get(v)!, indices.get(w)!)); } } if (lowlink.get(v) === indices.get(v)) { const component: string[] = []; let w = ''; do { w = stack.pop()!; onStack.delete(w); component.push(w); } while (w !== v); if (component.length > 1) { cycles.push(component); } } }; for (const nodeId of this.nodes.keys()) { if (!indices.has(nodeId)) { strongConnect(nodeId); } } return cycles; } public getTopologicalTiers(): string[][] { const inDegree = new Map(); for (const nodeId of this.nodes.keys()) { inDegree.set(nodeId, 0); } for (const [, deps] of this.outboundEdges.entries()) { for (const dep of deps) { if (inDegree.has(dep)) { inDegree.set(dep, inDegree.get(dep)! + 1); } } } const tiers: string[][] = []; let currentTier = Array.from(inDegree.entries()) .filter(([, deg]) => deg === 0) .map(([id]) => id); while (currentTier.length > 0) { tiers.push(currentTier); const nextTier: string[] = []; for (const nodeId of currentTier) { const outgoing = this.outboundEdges.get(nodeId) || new Set(); for (const dep of outgoing) { if (inDegree.has(dep)) { const newDeg = inDegree.get(dep)! - 1; inDegree.set(dep, newDeg); if (newDeg === 0) { nextTier.push(dep); } } } } currentTier = nextTier; } return tiers; } } ``` --- #### Blueprint 2: Python AST Graph Extractor (`engine/python_ast_extractor.py`) ```python import ast import os from typing import Dict, List, Set class PythonSymbolNode: def __init__(self, file_path: str, name: str, kind: str): self.id = f"{file_path}#{name}" self.file_path = file_path self.name = name self.kind = kind self.dependencies: List[str] = [] class PythonAstGraphExtractor(ast.NodeVisitor): def __init__(self, file_path: str): self.file_path = os.path.abspath(file_path) self.current_symbol: str = "" self.nodes: Dict[str, PythonSymbolNode] = {} self.edges: Dict[str, Set[str]] = {} def parse_file(self): with open(self.file_path, "r", encoding="utf-8") as f: tree = ast.parse(f.read(), filename=self.file_path) self.visit(tree) return self.nodes, self.edges def visit_FunctionDef(self, node: ast.FunctionDef): symbol_id = f"{self.file_path}#{node.name}" self.nodes[symbol_id] = PythonSymbolNode(self.file_path, node.name, "function") self.edges[symbol_id] = set() prev_symbol = self.current_symbol self.current_symbol = symbol_id self.generic_visit(node) self.current_symbol = prev_symbol def visit_ClassDef(self, node: ast.ClassDef): symbol_id = f"{self.file_path}#{node.name}" self.nodes[symbol_id] = PythonSymbolNode(self.file_path, node.name, "class") self.edges[symbol_id] = set() prev_symbol = self.current_symbol self.current_symbol = symbol_id self.generic_visit(node) self.current_symbol = prev_symbol def visit_Name(self, node: ast.Name): if self.current_symbol and isinstance(node.ctx, ast.Load): target_dep = f"external#{node.id}" self.edges[self.current_symbol].add(target_dep) self.generic_visit(node) ``` --- #### Blueprint 3: XERJ MCP Graph Brain Persistence (`engine/xerj-graph-sync.ts`) ```typescript import { AstSymbolNode } from './codebase-ast-graph'; export interface XerjBrainLinkPayload { sourceNamespace: string; sourceSymbol: string; targetNamespace: string; targetSymbol: string; relation: 'calls' | 'extends' | 'implements' | 'depends_on'; } export class XerjGraphSync { public static async persistGraphToXerj( nodes: Map, outboundEdges: Map> ): Promise<{ syncedNodes: number; syncedEdges: number }> { let edgeCount = 0; for (const [symbolId, deps] of outboundEdges.entries()) { for (const targetId of deps) { const linkPayload: XerjBrainLinkPayload = { sourceNamespace: 'codebase-ast', sourceSymbol: symbolId, targetNamespace: 'codebase-ast', targetSymbol: targetId, relation: 'depends_on', }; // Ready for MCP call: xerj_brain_link edgeCount += 1; } } return { syncedNodes: nodes.size, syncedEdges: edgeCount, }; } } ``` ### Sovereign Execution, Token Efficiency & Invariants | Dimension | Sovereign Mandate & Deterministic Contract | | :--- | :--- | | **Frontier Model Cascade** | Medium / High (`GPT-5.6 Sol Medium`, `Gemini 4 Argon`, `Claude Sonnet 5.5 High`, `Grok 4.7 High Fast`) | | **Availability Fallback** | `Gemini 4 Argon/Pro` -> `Gemini 3.8 Flash`; `Claude Opus 5.5` -> `Claude Opus 4.6` (Zero unhandled missing-model errors) | | **KV-Cache & Token Protocol** | Pin static instructions & schemas at prompt prefix (`kv-cache-prefix-optimizer`); pass surgical `CONTEXT_MAP.md` slices (<250 tokens/call) during Swarm Fan-Out | | **SaaT CLI & MCP Dispatch** | CLI: `vibes skill exec living-codebase-ast-graph ` / MCP: `vibes_skill_dispatch`, `vibes_ast_query`, `vibes_compile_cache`, `vibes_optimize_budget`, `vibes_verify_gate` | | **NFR & Anti-Slop Invariants** | P99 API Gateway Latency < 85ms, OpenAPI 3.1 / TypeSpec strict schema parity, RFC 9457 Problem Details error contracts, Idempotency-Key enforcement; 100% complete zero-placeholder output verified via `node scripts/check-anti-slop.js --strict` | --- --- ## Bahasa Indonesia ### Direktif Eksekutif & Paradigma Pengetahuan AST (Pergeseran 2026) Pencarian teks biasa (seperti Ripgrep atau regex) sangat berbahaya jika digunakan untuk refaktor skala besar oleh agen AI otonom. Mengganti nama fungsi umum seperti `init()` atau `format()` dengan *find-and-replace* global dapat merusak variabel yang tidak terkait, memicu galat dependensi, dan merusak stabilitas sistem secara diam-diam. **Living Codebase AST Graph** memandang basis kode bukan sekadar kumpulan file teks datar, melainkan sebagai **graf pengetahuan saraf (neural knowledge graph)** yang dibangun dari pohon sintaks abstrak native (*Abstract Syntax Tree*) lintas bahasa: **TypeScript, Python, Go, dan Rust**. Pendekatan ini memungkinkan swarm AI untuk: 1. **Menghitung Radius Dampak (*Blast Radius*)**: Mengidentifikasi seluruh pemanggil (*callers*), konsumen antarmuka, dan skema database sebelum memodifikasi simbol kode. 2. **Mendeteksi Siklus Dependensi Secara Matematis**: Menemukan perulangan dependensi melingkar menggunakan algoritma Tarjan's Strongly Connected Components (SCC). 3. **Mengatur Urutan Pembuatan Kode Topologis**: Menyusun jadwal pembuatan kode antar sub-agen agar skema dan tipe dasar selesai sebelum controller atau antarmuka UI dibuat. 4. **Persistensi Lintas Sesi via XERJ MCP**: Menyimpan struktur graf ke dalam memori episodik (`xerj_brain_link`, `xerj_memory_store`) agar kecerdasan basis kode tetap utuh saat sesi agen dimulai ulang. --- ### Integrasi Orkestrasi - **Orkestrasi Swarm Hulu**: `zero-to-prod-orchestrator`, `brainstorming`, `session-memory-manager`. - **Pemetaan AST & Sistem Tipe**: `typescript-expert`, `python-programming-expert`, `go-programming-expert`, `rust-programming-expert`. - **Refaktor & Pembersihan Utang Teknis**: `scalability-clean-code`, `legacy-code-translator`, `zero-tech-debt-auditor`, `anti-slop`. - **Memori Graf Persisten**: `xerj-search-expert`, `vector-db-rag-expert`. --- ### 3 Metodologi Analisis Graf Inti 1. **Klasifikasi Risiko Blast Radius**: Mengklasifikasikan simbol menjadi Risiko Kritis (API publik dan model basis data), Risiko Sedang (layanan bisnis internal), dan Risiko Rendah (fungsi helper privat). 2. **Pembersihan Dependensi Melingkar (*Tarjan SCC*)**: Menjamin ketiadaan siklus impor yang menyebabkan bug `undefined` saat inisialisasi modul. 3. **Pengurutan Topologis Tingkat (*Kahn's Algorithm*)**: Mengelompokkan modul kode ke dalam *Tier 0* hingga *Tier N* agar sub-agen dapat bekerja secara paralel tanpa konflik dependensi. --- ### Standar Penegakan Anti-Slop 1. **Dilarang Menggunakan Regex untuk Semantik Simbol**: Seluruh penelusuran simbol dan dependensi wajib berbasis binding AST resmi compiler, bukan pencocokan string teks. 2. **Nol Kode Placeholder**: Seluruh implementasi traversal, deteksi siklus Tarjan, dan parser AST wajib 100% lengkap tanpa stub. 3. **Verifikasi Bebas Cacat**: Seluruh berkas wajib tervalidasi bersih melalui `node scripts/check-anti-slop.js`. ### Matriks Eksekusi Berdaulat, Efisiensi Token & Invarian | Dimensi | Mandat Berdaulat & Kontrak Deterministik | | :--- | :--- | | **Kaskade Model Frontier** | Medium / High (`GPT-5.6 Sol Medium`, `Gemini 4 Argon`, `Claude Sonnet 5.5 High`, `Grok 4.7 High Fast`) | | **Invarian Fallback Ketersediaan** | `Gemini 4 Argon/Pro` -> `Gemini 3.8 Flash`; `Claude Opus 5.5` -> `Claude Opus 4.6` (Bebas gagal saat model belum tersedia/kuota penuh) | | **Protokol Hemat Token & KV-Cache** | Kunci instruksi & skema statis di prefix awal (`kv-cache-prefix-optimizer`); kirim potongan `CONTEXT_MAP.md` (<250 token/panggilan) saat Fan-Out | | **Eksekusi SaaT CLI & MCP** | CLI: `vibes skill exec living-codebase-ast-graph ` / MCP: `vibes_skill_dispatch`, `vibes_ast_query`, `vibes_compile_cache`, `vibes_optimize_budget`, `vibes_verify_gate` | | **Invarian NFR & Anti-Slop** | Latensi P99 API < 85ms, paritas skema ketat OpenAPI 3.1 / TypeSpec, kontrak error RFC 9457 Problem Details, penegakan Idempotency-Key; kode 100% tuntas tanpa placeholder diverifikasi melalui `node scripts/check-anti-slop.js --strict` |