Summary
When a Bash script calls a function that's defined in a sourced file, graphify never records the call edge. The function definitions and the source/import edge are all indexed fine, but the caller shows zero outgoing calls to the sourced function — so shell scripts look disconnected from the libraries they use.
This is separate from #2079. #2079 is about the source edge (source "${VAR}/lib.sh" pointing at nothing). This one is about the call edge, and it happens even with a plain source ./lib.sh where the source edge resolves fine.
Reproduction (mainline v8)
b.sh
#!/usr/bin/env bash
b_func() { echo ok; }
a.sh
#!/usr/bin/env bash
source ./b.sh
main() { b_func; }
In graph.json:
imports_from: a.sh -> b.sh ✅ (source edge resolves)
calls: [] ❌ (expected main -> b_func)
b_func has no incoming call edge, so graphify path main b_func and graphify callers b_func come up empty even though both functions are indexed.
Root cause
extract_bash (graphify/extractors/bash.py) only records a call edge when the callee is defined in the same file (name in defined_functions). A call to a function from a sourced file is silently dropped, and the extractor returns just {"nodes", "edges"} — it never emits the raw_calls / bash_sources that other languages use for cross-file resolution.
- There's already a resolver for exactly this —
resolve_bash_source_edges (graphify/symbol_resolution.py:404), with unit tests — but it has no call site anywhere in graphify/. It's defined and tested but not wired into the extract pipeline, and it has no raw_calls / bash_sources to work from anyway.
Suggested fix
Have extract_bash emit raw_calls for calls whose callee isn't defined in the current file (plus bash_sources for the source edges), then run them through resolve_bash_source_edges in the pipeline so cross-file calls to sourced functions become real calls edges.
Acceptance
Prior art
Aziron's atlas hit the same thing and split it into these two exact halves — source resolution and the intra-function call — in aziron-ai/atlas#13, which also references #2079.
Environment: graphify 0.9.25 (v8), macOS arm64.
Summary
When a Bash script calls a function that's defined in a sourced file, graphify never records the call edge. The function definitions and the source/import edge are all indexed fine, but the caller shows zero outgoing calls to the sourced function — so shell scripts look disconnected from the libraries they use.
This is separate from #2079. #2079 is about the source edge (
source "${VAR}/lib.sh"pointing at nothing). This one is about the call edge, and it happens even with a plainsource ./lib.shwhere the source edge resolves fine.Reproduction (mainline v8)
b.sha.shIn
graph.json:imports_from: a.sh -> b.sh✅ (source edge resolves)calls: []❌ (expectedmain -> b_func)b_funchas no incoming call edge, sographify path main b_funcandgraphify callers b_funccome up empty even though both functions are indexed.Root cause
extract_bash(graphify/extractors/bash.py) only records a call edge when the callee is defined in the same file (name in defined_functions). A call to a function from a sourced file is silently dropped, and the extractor returns just{"nodes", "edges"}— it never emits theraw_calls/bash_sourcesthat other languages use for cross-file resolution.resolve_bash_source_edges(graphify/symbol_resolution.py:404), with unit tests — but it has no call site anywhere ingraphify/. It's defined and tested but not wired into the extract pipeline, and it has noraw_calls/bash_sourcesto work from anyway.Suggested fix
Have
extract_bashemitraw_callsfor calls whose callee isn't defined in the current file (plusbash_sourcesfor the source edges), then run them throughresolve_bash_source_edgesin the pipeline so cross-file calls to sourced functions become realcallsedges.Acceptance
main -> b_funccall edge exists for the repro above.source ./b.shand thedirname "${BASH_SOURCE[0]}"/${VAR}idiom (the source-edge half is Bash: source "${VAR}/lib.sh" emits an unresolvable edge — sourced libraries appear orphaned #2079).graphify callers b_funcandgraphify path main b_funcresolve.Prior art
Aziron's atlas hit the same thing and split it into these two exact halves — source resolution and the intra-function call — in aziron-ai/atlas#13, which also references #2079.
Environment: graphify 0.9.25 (v8), macOS arm64.