Repository navigation
Description
Summary
The graph UI's dead-code classification (status: "dead" = "0 callers") flags a large number of live functions/methods as dead. The detector only counts direct CALLS/USAGE/CALL_REFERENCE edges and cannot see three common dispatch patterns, so it reports false positives for:
- Constructors —
ClassName(...)instantiation never creates aCALLSedge to__init__. - Dunder / framework-invoked methods —
__str__,__call__,_generate/_agenerate(called by a framework'sinvoke/ainvoke),_llm_type(property access). - Callback-by-reference — functions passed by name as arguments (
asyncio.to_thread(fn, ...),context.configure(include_object=fn),DecisionPoint(apply=fn)).
Repro
On a Python repo, index it and open the graph UI. In my case (a Python monorepo), 50 of 50 "dead" nodes were false positives. Concrete example:
ModeAwareModel.__init__ is flagged dead, but it is instantiated in three places:
# ai/langgraph/services/model.py
def get_model() -> BaseChatModel:
return ModeAwareModel(ChatOpenAI(...), role="supervisor")
def get_worker_model(session_id=None) -> BaseChatModel:
return ModeAwareModel(ChatOpenAI(...), role="worker")
# inside bind_tools()
return ModeAwareModel(cast(BaseChatModel, bound), self._role)The graph has no CALLS edge from ModeAwareModel(...) to ModeAwareModel.__init__, so the constructor is reported as having zero callers.
Other flagged-but-live examples:
_apply_updates— passed asapply=_apply_updatesto aDecisionPoint._include_object/_compare_type— passed asinclude_object=/compare_type=to Alembic'scontext.configure._run_file_search— passed toasyncio.to_thread.__str__,__call__,_generate,_agenerate,_llm_type— invoked by runtime/framework dispatch.
Root cause
The classification in src/ui/layout3d.c (cbm_layout_compute):
bool is_fn = label=="Function" || label=="Method"; // only these are candidates
int ic = in_calls[i];
int iu = in_usage[i] + in_call_reference[i];
...
else if (ic==0 && iu==0) status = "dead";dead is defined purely as zero incoming CALLS/USAGE/CALL_REFERENCE edges. Static analysis cannot resolve constructor dispatch, dunder/framework invocation, or callback-by-reference, so all three produce false "dead" results.
Suggested fix
In the dead-code classification, treat these as live (non-dead):
__init__— live when its owning class has any instantiation (Class(...)call) or is itself referenced/exported.- Dunder methods (
__str__,__call__,__repr__,__eq__, etc.) — either treat as live, or exclude from dead-code classification entirely. - Callback-referenced functions — a function referenced by name as an argument (e.g.
asyncio.to_thread(fn),configure(include_object=fn)) should count as having a caller.
At minimum, excluding __init__ and dunders from the "dead" bucket would eliminate the majority of false positives.
Environment
- codebase-memory-mcp v0.10.4 (single static binary,
--ui=true) - Python repo, ~13.4k graph nodes
Activity
DeusData commentedon Aug 18, 2026
Thank you for the concrete live examples. The current UI classifier does exactly what you found: after excluding tests, entries, routes, and exports, any Function or Method with zero inbound
CALLS,USAGE, andCALL_REFERENCEedges is labeleddead. That absence is not proof of dead code when implicit dispatch and callback references are outside the graph.This is routed as a high-priority correctness and UX bug. The first fix should remove the false certainty at the presentation boundary, for example by describing these as having no known inbound references, while constructor, dunder, framework, and callback extraction improvements remain separate graph-quality work. Thank you for showing that all 50 sampled results were false positives rather than reporting only one anecdote.
bhaveshpatelapple commentedon Sep 14, 2026
Confirming this is still present on v0.10.8 (
b4b403b1...336a6, Windows amd64), independent Python repo (~10k nodes / ~40k edges).Minimal repro:
Embedder.__init__is flaggedstatus: "dead"in the graph UI with 0 callers, despite the realEmbedder(model="y")call site inbuild(). Confirmed viatrace_path(function_name="Embedder.__init__", direction="inbound")→callers_total: 0, and via a direct (non-OPTIONAL) CypherMATCH (caller)-[:CALLS]->(f:Method {name:"__init__"})→ 0 rows, so it's not just a UI-layer miscount — the underlying CALLS edge forClassName(...)→__init__genuinely never gets created.On a real ~1,700-Function/136-Method project I indexed, this pattern accounted for 10 of 13 total flagged "dead" nodes — same shape as the
ModeAwareModelexample above.fix: describe graph reference gaps without declaring live code dead
DeusData commentedon Sep 25, 2026
Thank you, @adinballew, for sampling 50 results and showing that all 50 were live, and @bhaveshpatelapple for the minimal repro on v0.10.8!
I confirmed it on current main.
Embedder(model="y")does create a CALLS edge, but the edge points at the classEmbedder, not atEmbedder.__init__. So trace_path and the dead-code view both see zero callers for the constructor. That part is graph quality, and @mvanhorn has offered to take it. @mvanhorn, over to you, and thank you! Dunder methods and callbacks passed by reference are separate cases we'll look at afterwards.On the UI side, the graph view is being replaced. "No known inbound references" (rather than "dead") is already in the requirements for the new one. Thanks again for the concrete examples!
mvanhorn commentedon Sep 29, 2026
@DeusData the constructor half is up in #2424.
Embedder(model="y")now resolves toEmbedder.__init__, with the issue's repro as a test. One thing to confirm there: for a class that defines__init__, the Class node no longer gets the instantiation CALLS edge. Dunders and callbacks by reference are untouched.DeusData commentedon Oct 19, 2026
Thank you for the focused constructor work in #2424 and for calling out the Class-node edge change explicitly. That semantic choice needs to be settled in the PR review. Dunder dispatch and callbacks remain separate from the constructor slice; this follow-up does not mark the broader report resolved.
mvanhorn commentedon Oct 25, 2026
Agreed, the Class-node edge semantics belong in the constructor PR review. Dunder dispatch and callbacks remain separate, so the constructor slice alone doesn't resolve the broader report.
DeusData commentedon Oct 25, 2026
Thank you for confirming the constructor scope. The Class-node edge semantics remain a review decision in #2424, while dunder dispatch and callbacks stay separately open. Sorry for the delay; this is waiting on maintainer follow-up, and I’ll follow up soon.
fix(python): resolve ClassName() calls to __init__
test(pipeline): expect Layout.__init__ for the DeusData#1548 construc…
fix(python): bind ClassName() only to the class own __init__
DeusData commentedon Nov 3, 2026
Part of this landed on main in 8ae04f4 (#2424, thanks to @mvanhorn): a Python
ClassName()call now resolves to the class's own__init__when the class defines one, and keeps targeting the class node otherwise. Constructions reached only through module attributes or text resolvers still target the class, so this issue stays open for those.