fix: scope-aware caller selection for nested functions#129
Conversation
Add `codegraph path <from> <to>` — BFS shortest-path search on the call graph. Given two symbol names, finds the shortest call chain with hop count, intermediate nodes, edge kinds, and alternate path count. Supports --reverse, --max-depth, --kinds, --from-file/--to-file, -T, -j, -k flags. Exposed as symbol_path MCP tool. Impact: 4 functions changed, 3 affected
- Create docs/use-cases/titan-paradigm.md — maps Johannes R.'s multi-agent codebase cleanup architecture (RECON, GAUNTLET, GLOBAL SYNC, STATE MACHINE) to codegraph commands, roadmap items, and post-LLM-integration recommendations - Update roadmap/BACKLOG.md: mark #4 (node classification), #9 (git change coupling), #1 (dead code), #2 (shortest path), #12 (execution flow) as DONE; add 6 new Titan Paradigm-inspired items (#21-#26): composite audit, batch querying, triage priority queue, change validation predicates, graph snapshots, MCP orchestration tools - Update README.md: add roles + co-change to features table, differentiators, commands section, agent template, common flags, comparison table; update MCP tool count 18 → 19 - Update docs/recommended-practices.md: update MCP tool count and tool list, add roles/co-change/path to CLAUDE.md template and developer workflow, add "Understand architectural roles" and "Surface hidden coupling" sections, add co-change step to setup checklist - Add full examples with real output for roles, co-change, and path to docs/examples/CLI.md and docs/examples/MCP.md - Update GitHub repo description with new capabilities
- Restore Phase 3 (Architectural Refactoring) to ROADMAP - Renumber phases 4-8 and all cross-references - Fix MCP tool count per Greptile review
Address Greptile review comments on #121: - Update MCP tool counts from 18/19 to 21 (22 in multi-repo mode) across README, recommended-practices, dogfood skill, titan-paradigm - Add missing execution_flow and list_entry_points to tool enumeration - Renumber new backlog items 21-26 → 27-32 to avoid collision with existing items 21-22
Adds a benchmark suite that measures how much codegraph reduces token usage when AI agents navigate the Next.js codebase (~4k TS files). - scripts/token-benchmark-issues.js: 5 real Next.js PRs as test cases - scripts/token-benchmark.js: runner using Claude Agent SDK (baseline vs codegraph MCP), with --perf flag for build/query benchmarks - scripts/update-token-report.js: JSON → markdown report generator - docs/benchmarks/: methodology docs and placeholder report Impact: 21 functions changed, 7 affected
…verage benchmark.js now measures no-op rebuilds, 1-file rebuilds, and query latency (fn-deps, fn-impact, path, roles) alongside full builds. update-benchmark-report.js renders new Incremental Rebuilds and Query Latency sections in BUILD-BENCHMARKS.md and adds incremental/query rows to the README performance table. All new fields are additive for backward compatibility. Impact: 5 functions changed, 2 affected
Extract version from benchmark result JSON and include it in branch names, commit messages, PR titles, and PR bodies across all 4 benchmark jobs (build, embedding, query, incremental).
Resolve conflicts: - benchmark.yml: keep version-in-branch-name convention - README.md: take main's dual-engine performance table - BUILD-BENCHMARKS.md: main's dual-engine data + incremental/query sections - update-benchmark-report.js: take main's clearer regex comment Impact: 12 functions changed, 13 affected
Take main's latest benchmark numbers (PR #125) while preserving the new incremental rebuild and query latency fields from the branch.
Nested/closure functions (e.g. nodeId inside exportMermaid) were incorrectly classified as [dead] because the caller selection loop picked the last definition where line <= call.line, creating self-call edges that got filtered out. Now uses endLine to find the innermost enclosing scope, so calls within an outer function correctly attribute the outer function as caller rather than the nested function itself. Fixes false-positive [dead] for nodeId in branch-compare.js, export.js, and queries.js. Impact: 1 functions changed, 17 affected
|
Claude finished @carlos-alm's task —— View job Reviewing this PR for scope-aware caller selection fix Review Todo List
Phase 1: Root Cause Analysis ✅Problem Being Solved: The PR fixes false-positive Root Cause Identified: The caller selection loop used Fix Approach: The solution uses This is a legitimate bug fix addressing a real structural analysis issue, not hiding symptoms. Phase 2: Configuration Change Detection ✅No High-Risk Changes Detected:
The change is focused entirely on the core parsing logic with proper test coverage. Phase 3: Test Integrity ✅Excellent Test Coverage: The new integration test at lines 361-404 specifically validates the fix: test('enclosing function is the caller of a nested function, not a self-call', () => {
// Creates outer() { function inner() { return 42; } return inner(); }
// Verifies outer->inner edge exists (correct)
// Verifies inner->inner edge does NOT exist (the bug this fixes)
});Test Quality:
Phase 4: Code Quality Review ✅Implementation Quality (src/builder.js:788-811): let caller = null;
let callerSpan = Infinity; // NEW: Track narrowest enclosing scope
for (const def of symbols.definitions) {
if (def.line <= call.line) {
const end = def.endLine || Infinity;
if (call.line <= end) { // NEW: Call is within definition range
const span = end - def.line;
if (span < callerSpan) { // NEW: Pick narrowest scope
// ... assign caller
callerSpan = span;
}
} else if (!caller) { // NEW: Fallback only if no enclosing scope found
// ... fallback logic
}
}
}Strengths:
Architecture: The fix targets the exact root cause (caller selection) without side effects on other parsing logic. Edge Cases Handled:
Final Assessment |
Greptile SummaryThis PR fixes a bug where nested/closure functions were incorrectly classified as
Confidence Score: 5/5
Important Files Changed
Last reviewed commit: 0cb6324 |

Summary
[dead]classification for nested/closure functions (e.g.nodeIdinsideexportMermaid)line <= call.line, creating self-call edges that got filtered out →fan_in = 0→[dead]endLineto find the innermost enclosing scope, so calls correctly attribute the outer function as callernodeIdinstances inbranch-compare.js,export.js, andqueries.jsnow correctly show as[core]Test plan
outer→inneredge (notinner→innerself-call)node src/cli.js where nodeId -Tshows[core]for all 3 instancesnode src/cli.js fn-impact nodeId -Tshows correct caller chains