Repository navigation
Description
Version
codebase-memory-mcp dev
Platform
Linux (x64)
What happened?
trace_path returns the set of reachable nodes as rows containing only the qualified-name grouping plus name and minimum hop.
When the same node is reachable through multiple routes, those routes are merged. When it is reachable at multiple depths, only the shortest hop remains. The response does not expose predecessors, traversed edges, call-site lines, edge types, or path multiplicity, so the caller cannot reconstruct how a node was reached.
This is not a complaint that distinct nodes are merged: separate qualified nodes are preserved correctly. The missing information is the topology between the returned nodes.
Minimal reproduction
def sink() -> str:
return "sink"
def left() -> str:
return sink()
def right() -> str:
return sink()
def diamond() -> tuple[str, str]:
return left(), right()
def direct_and_indirect() -> tuple[str, str]:
return sink(), left()A fresh full index produced six expected CALLS edges and reported current coverage metadata with no recorded gaps.
Two equal-depth paths collapse to one node
{
"function_name": "cbm-trace-paths-repro.repro.diamond",
"direction": "outbound",
"depth": 2,
"edge_types": ["CALLS"],
"mode": "calls",
"format": "json"
}Actual result:
left hop=1
right hop=1
sink hop=2
The underlying graph contains two distinct paths:
diamond -> left -> sink
diamond -> right -> sink
An explicit graph query returns both rows and their call-site lines:
MATCH (root:Function)-[r1:CALLS]->(mid:Function)-[r2:CALLS]->(leaf:Function)
WHERE root.qualified_name = 'cbm-trace-paths-repro.repro.diamond'
AND leaf.qualified_name = 'cbm-trace-paths-repro.repro.sink'
RETURN root.qualified_name, r1.line, mid.qualified_name, r2.line, leaf.qualified_name
ORDER BY mid.qualified_nameDifferent-depth paths collapse to the shortest hop
Outbound tracing from direct_and_indirect returns:
sink hop=1
left hop=1
The graph contains both:
direct_and_indirect -> sink
direct_and_indirect -> left -> sink
The indirect route is not represented, and the response provides no indication that another path exists.
Inbound tracing from sink similarly lists diamond once at hop 2 even though diamond reaches it through both left and right.
Public production reproduction
Repository and commit:
- https://github.com/Enferlain/sd-scripts/tree/de2dd0ebae40174e8336157e8928e36cfd013284
- Root:
library/training/phases/training_loop.py::_run_epoch_steps
A depth-3 outbound trace from _run_epoch_steps returns each of these nodes once:
builtins.list.append hop=2
builtins.dict.get hop=1
But explicit graph queries show:
_run_epoch_steps -> RuntimeTrace.event -> list.append
_run_epoch_steps -> LossRecorder.add -> list.append
and both:
_run_epoch_steps -> dict.get
_run_epoch_steps -> _emit_step_tracking_logs -> dict.get
The flattened result cannot show either route multiplicity or the longer alternate route.
Distinct implementations are not lost by node deduplication
As a control, tracing inbound OVERRIDE from DiffusionTrainingStrategy.process_batch returns the SD, SD3, and SDXL methods as three separate production nodes (and the test implementation when include_tests=true). Deduplication is therefore keyed to node identity rather than short method name.
The fact that _run_epoch_steps currently reports only the SD3 process_batch implementation is a separate graph-construction/dispatch issue tracked in #1271. It is not caused by path flattening.
Expected behavior
Keep the compact unique-node view if desired, but provide enough optional topology to reconstruct reachability. A bounded representation such as this avoids eagerly enumerating every simple path:
{
"nodes": [
{"qualified_name": "...diamond", "hop": 0},
{"qualified_name": "...left", "hop": 1},
{"qualified_name": "...right", "hop": 1},
{"qualified_name": "...sink", "hop": 2}
],
"traversed_edges": [
{"source": "...diamond", "target": "...left", "type": "CALLS", "line": 14},
{"source": "...diamond", "target": "...right", "type": "CALLS", "line": 14},
{"source": "...left", "target": "...sink", "type": "CALLS", "line": 6},
{"source": "...right", "target": "...sink", "type": "CALLS", "line": 10}
]
}Possible API shapes:
- optional
topology=trueoroutput="graph"returning unique nodes plus traversed edges; - optional
output="paths"for bounded explicit path enumeration; - predecessor lists per node, provided non-tree/cross edges are also retained;
max_edges/max_pathsplus pagination and cycle protection.
Returning only one BFS-tree predecessor would still conceal alternate routes. The compact graph form should retain every traversed edge within the bounded result, or explicitly state that only shortest-path topology is included.
Impact
Without predecessor or edge information, an agent cannot reliably answer:
- which branch or implementation led to a dependency;
- whether two results share a common route;
- whether multiple independent routes increase impact/risk;
- which call-site and relationship type produced a hop;
- whether a shorter path merely hid a valid longer path.
callees_total / callers_total are counts of unique reachable nodes, not path counts, but the current response does not make that distinction obvious.
Related
- task: Graph queries & trace_path resolution #594 is the graph-query/
trace_pathcorrectness umbrella. - Python polymorphic call is collapsed to one low-confidence concrete target instead of override set #1271 covers polymorphic target construction and dispatch-aware traversal; this issue concerns the generic traversal output model after edges already exist.
Confirmations
- Searched open and closed issues; no issue specifically requests predecessor/edge topology or path-preserving trace output.
- Reproduced in a standalone five-function fixture.
- Confirmed independently in a public OSS repository.
Activity
DeusData commentedon Jul 28, 2026
Thanks for spelling out the lost topology rather than treating a node set as a path.
Current main implements trace_path as BFS reachability. bfs_union_same_name merges visited nodes by id and edges by source/target/type (src/mcp/mcp.c:5745-5801), then the response pages nodes in hop/id order (src/mcp/mcp.c:6258-6285). There is no predecessor map or path-instance representation, so converging paths and multiplicity cannot survive serialization.
That confirms a contract-level traversal bug, routed to 0.9.1-rc at priority/high. Thank you for making the required output semantics explicit.
DeusData commentedon Oct 19, 2026
Thank you again for distinguishing reachable nodes from the paths and edges connecting them. I have no new implementation result for preserving bounded topology or multiplicity. The existing diamond and alternate-depth examples already specify the missing output contract.
DeusData commentedon Oct 25, 2026
Thank you for the diamond and alternate-depth examples. Bounded topology and multiplicity remain a maintainer output-contract decision. Sorry for the delay; this is waiting on maintainer follow-up, and I’ll follow up soon.