Repository navigation
trace_path: expose direct self-recursion instead of an unqualified zero-callee result #2816
Description
Written with OpenAI Codex; the commands below were run against official Linux release binaries.
Summary
A direct recursive TypeScript call is recognized as self_recursive: true on its Function node but is absent from CALLS and from trace_path(direction="outbound"). The trace returns callees_total: 0 with relation eq, without indicating that a self-call was excluded.
This is not a v0.12.1 regression: it reproduces in both v0.11.0 and v0.12.1. I am reporting the observable trace/graph contract, not asking to restore the spurious self-loops from weak name matching described in #1085.
Environment
- Linux x86_64, Ubuntu glibc 2.39.
- Official
linux-amd64-portablerelease archives, checksums verified. codebase-memory-mcp 0.11.0andcodebase-memory-mcp 0.12.1.mode: full,persistence: false, separate cache and runtime directories for each version.- One source file. Ordinary call edges are positive controls.
Minimal source
export function countdown(n: number): number {
if (n <= 0) return 0;
return countdown(n - 1);
}
export function leaf(n: number): number {
return n;
}
export function wrapper(n: number): number {
return leaf(n);
}
export function walk(): void {
const visit = (n: number): void => {
if (n > 0) visit(n - 1);
};
visit(3);
}Reproduction
Save that source as /tmp/cbm-self-call/repo/recursion.ts. Use fresh, separate cache/runtime directories for each binary version:
mkdir -p /tmp/cbm-self-call/repo /tmp/cbm-self-call/cache /tmp/cbm-self-call/runtime
export CBM_CACHE_DIR=/tmp/cbm-self-call/cache
export CBM_RUNTIME_DIR=/tmp/cbm-self-call/runtimeSave these argument files outside repo/:
/tmp/cbm-self-call/index.json:
{"repo_path":"/tmp/cbm-self-call/repo","name":"self-only","mode":"full","persistence":false}/tmp/cbm-self-call/trace.json:
{"project":"self-only","function_name":"countdown","direction":"outbound","format":"json"}codebase-memory-mcp cli --json index_repository --args-file /tmp/cbm-self-call/index.json
codebase-memory-mcp cli --json trace_path --args-file /tmp/cbm-self-call/trace.jsonI also inspected the graph with these read-only Cypher queries:
MATCH (a)-[r:CALLS]->(b) RETURN a.name,b.name,r.callee,r.line,r.strategyMATCH (n:Function) RETURN n.name,n.self_recursive,n.recursiveActual result
Both versions:
countdownand nestedvisitexist as Function nodes, withself_recursive: true.- Positive controls are present:
wrapper -> leafandwalk -> visit. countdown -> countdownandvisit -> visitare absent fromCALLS.- The
countdownoutbound trace reportscallees_total: 0,callees_total_relation: "eq", and no callees.
Expected / requested contract
A direct, resolved self-call should either be represented in the graph/trace with traversal bounded against cycles, or the trace should explicitly expose its exclusion, for example by carrying the start node's recursion metadata or an excluded-self-call count. A plain exact zero is easy for a consumer to misread as “this function makes no calls.”
If omitting self-edges is an intentional graph policy, documenting it and exposing the already-known recursion signal in the trace would also address the reporting problem.
Source observation
The current main source at 8530c8cd1c1ac8e1e3d2962bc213a8c9025fea0a still rejects a target whose node ID equals the source:
This is source inspection only; I have not executed a build of that main commit.
Related issues and scope
- Parallel pipeline drops all JSX-via-alias CALLS edges (21% of call graph); repros on this repo's own source #1085 concerns phantom self-loops such as
Map.getbeing attached to the enclosing function. The calls above are genuine self-calls, and no weak member fallback is needed to identify them. - [bug]
self_recursive/unguarded_recursionmatch calls by bare name →super().save()and same-named wrappers are false positives #599 concerns false-positive recursion flags. Here the flag is correct; the graph/trace omits the call. - Enhancement: index inner declarations of factory/setup callbacks — Vue Pinia setup stores, composables & React hooks (the Vue/React twin of the tracked Zustand gap) #415 and Arrow functions assigned to consts emitted as label:Variable with no function signal #1220 concern missing inner/arrow definitions. Here the Function nodes exist and ordinary incoming calls resolve.
No proprietary source, private logs, credentials, or internal hostnames are included. I searched the existing recursion/self-loop/trace issues and did not find this specific trace-contract report.
Activity