You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
For a function defined in one module and referenced from other modules, the extractor produces two nodes: the resolved definition (with source_file/source_location) and an unresolved bare-name stub (source_file: ""). All cross-module references edges attach to the stub, never to the definition — so "who references this function" queries against the definition node return nothing.
Observed on graphifyy 0.8.46 (PyPI), Python extraction, on a large FastAPI codebase: a dependency-injection function referenced from 600+ files had zero incoming edges on its definition node, while its bare-name stub held all ~670 reference edges. Class/type symbols do not suffer from this — only functions/methods.
Extract → graph.json contains dependencies_get_db (definition, 0 in-refs) and a sourceless stub get_db holding the references edge from route().
Root cause (traced in 0.8.46 source)
extract.py → ensure_named_node (~L2464–2485): a referenced name not defined in the current file falls back to a bare-name node id + a sourceless stub node — deliberately deferring to a corpus-level rewire pass (per the code comment).
That pass, _rewire_unique_stub_nodes (~L7706–7744), gates merge candidates through _is_type_like_definition (~L7695–7703), which returns False for any label ending in ). But the extractor labels every function/method name() (~L3138) — so function definitions can never be rewire targets; only class/type-like stubs are repaired.
_node_label_key already strips non-alphanumerics, so the stub get_db and the definition label get_db() produce the same key — the only thing blocking the merge is the _is_type_like_definition filter.
Suggested fix directions
Resolve imports before minting reference targets: give ensure_named_node access to the file's from X import name [as alias] table (tree-sitter already parses these) and mint the file-qualified id when the referenced name is a known import — mirroring what the import-guided call resolution already does for calls edges.
Or widen the post-pass: in _rewire_unique_stub_nodes, allow function definitions (labels name() / .name()) as merge candidates when the label-key match is globally unique. Optionally restrict to stubs whose referencing files import that exact name, to keep same-name-different-module cases safe (the same evidence the Java shadow-stub repair uses).
Happy to provide more detail or test a patched build against our corpus. Thanks for the tool — the graph is genuinely useful for agent-driven code orientation; this is the one gap we had to work around client-side.
Summary
For a function defined in one module and referenced from other modules, the extractor produces two nodes: the resolved definition (with
source_file/source_location) and an unresolved bare-name stub (source_file: ""). All cross-modulereferencesedges attach to the stub, never to the definition — so "who references this function" queries against the definition node return nothing.Observed on
graphifyy 0.8.46(PyPI), Python extraction, on a large FastAPI codebase: a dependency-injection function referenced from 600+ files had zero incoming edges on its definition node, while its bare-name stub held all ~670 reference edges. Class/type symbols do not suffer from this — only functions/methods.Minimal repro sketch
Extract →
graph.jsoncontainsdependencies_get_db(definition, 0 in-refs) and a sourceless stubget_dbholding thereferencesedge fromroute().Root cause (traced in 0.8.46 source)
extract.py→ensure_named_node(~L2464–2485): a referenced name not defined in the current file falls back to a bare-name node id + a sourceless stub node — deliberately deferring to a corpus-level rewire pass (per the code comment)._rewire_unique_stub_nodes(~L7706–7744), gates merge candidates through_is_type_like_definition(~L7695–7703), which returnsFalsefor any label ending in). But the extractor labels every function/methodname()(~L3138) — so function definitions can never be rewire targets; only class/type-like stubs are repaired._resolve_cross_file_importsalso excludes()-labels (~L8832);dedup.pyintentionally never label-mergesfile_type == "code"nodes (--update / build_merge dedup merges distinct same-named code symbols #1205) nor sourceless groups (Bug Report —graphify --updateperforms a destructive, net-negative fuzzy node merge on an already-current code graph #1178)._node_label_keyalready strips non-alphanumerics, so the stubget_dband the definition labelget_db()produce the same key — the only thing blocking the merge is the_is_type_like_definitionfilter.Suggested fix directions
ensure_named_nodeaccess to the file'sfrom X import name [as alias]table (tree-sitter already parses these) and mint the file-qualified id when the referenced name is a known import — mirroring what the import-guided call resolution already does forcallsedges._rewire_unique_stub_nodes, allow function definitions (labelsname()/.name()) as merge candidates when the label-key match is globally unique. Optionally restrict to stubs whose referencing files import that exact name, to keep same-name-different-module cases safe (the same evidence the Java shadow-stub repair uses).Happy to provide more detail or test a patched build against our corpus. Thanks for the tool — the graph is genuinely useful for agent-driven code orientation; this is the one gap we had to work around client-side.