Repository navigation
indirect_call edges from module-top-level code keep absolute-path-derived source id (id_remap skips raw_calls) #2231
Description
Summary
Running the full extraction pipeline on a ~420-file JS/TS project, graphify.diagnostics.diagnose_extraction reports 586 dangling-endpoint edges. I traced the indirect_call subset (58 edges, all with a missing source) to a concrete bug in the id-remap pass in extract.py.
Confirmed bug: id_remap is applied to all_nodes/all_edges but never to all_raw_calls
extractors/engine.py:2342creates a whole-file "module" node:file_nid = _make_id(str(path)), i.e. an id derived directly from the (often absolute) filesystem path.- That
file_nidis used asscope_nid/caller_nidforindirect_calledges emitted from module-top-level code (calls not inside any function), e.g. via_emit_indirect_ref(ident, file_nid, js_module_bound, "collection")aroundengine.py:4468-4502. These go throughraw_calls(all_raw_calls) for cross-file resolution. extract.py:4661-4753builds anid_remapdict that maps exactly this absolute-path-derived id (_make_id(str(path))/_make_id(str(path.resolve()))) to the canonical relative form_file_node_id(rel)— this is the fix for graph.json edge endpoints still leak absolute-path-derived file ids #502.extract.py:4754-4762appliesid_remaptoall_nodesandall_edgesonly:if id_remap: for n in all_nodes: if n.get("id") in id_remap: n["id"] = id_remap[n["id"]] for e in all_edges: if e.get("source") in id_remap: e["source"] = id_remap[e["source"]] if e.get("target") in id_remap: e["target"] = id_remap[e["target"]]
all_raw_callsis never touched by this loop.- The other remap (
sym_remap, built fromprefix_remap) does patchraw_calls[caller_nid](extract.py:4820-4823), butsym_remapis keyed off_file_node_id(path)(the relative-form prefix), which never matches the absolute-formfile_nid. So neither remap pass fixesraw_calls[caller_nid]for this id shape. - Net effect: when a
raw_callsentry withcaller_nid = file_nid(absolute-derived) is later resolved into a realindirect_calledge, itssourcekeeps the stale absolute-path id — which matches no node inall_nodes(those were remapped), so it dangles.
Example (Windows, absolute input paths)
indirect_call: c_users_x_documents_github_myproj_src_index_js -> src_obfuscators_nonvm_obfuscatewithoutvm_obfuscatewithoutvm
(source_file=src/index.js, source_location=L47)
Expected source id: src_index (matches the canonical file-node id used everywhere else).
Secondary observation (not root-caused, flagging for investigation)
Also saw ~50 dangling edges where an imports edge targets a barrel/re-export index.js file's re-exported symbol id instead of the file where the symbol is actually defined, e.g.:
imports: src_obfuscators_nonvm_nonvmbackenddefinition -> src_obfuscators_shared_backend_index_createbackendadapter
createBackendAdapter is defined in a sibling file under shared/backend/ and re-exported through index.js. There's existing re_exports/edge_alias_candidates repoint logic around extract.py:4824-4858 that looks like it's meant to handle exactly this shape, but I haven't traced why it doesn't catch this case — didn't want to guess, so flagging separately from the confirmed bug above.
Environment
- graphify (
graphifyy) installed viauv tool install - OS: Windows 11
- Corpus: ~422 JS/TS files, extracted via
graphify.extract.extract(code_files, cache_root=Path('.'))with absolutePathinputs fromgraphify.detect.detect() - Detected via:
graphify.diagnostics.diagnose_extraction(extraction, directed=False, root='.')
Activity
fix(extract): close the absolute-path node-id leak class (#2231, #2243)
chore: bump to 0.9.29; changelog for #2231/#2243/#2251/#2241/#2253/#2206
safishamsi commentedon Jul 28, 2026
Fixed in v0.9.29. id_remap is now applied to raw_calls caller_nid, so module-top-level indirect_call sources canonicalize to the real file node instead of an absolute-path-derived id; a general backstop also rewrites any remaining absolute-derived id/endpoint. https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.29
Merge upstream/v8 (0.9.29) into feat/clusters
fix(cache): re-anchor cached ids so a warm hit can't replay another r…
fix(extract): close the absolute-path node-id leak class (Graphify-La…
chore: bump to 0.9.29; changelog for Graphify-Labs#2231/Graphify-Labs…
fix(cache): re-anchor cached ids so a warm hit can't replay another r…