diff --git a/CHANGELOG.md b/CHANGELOG.md index c83540fb..9d9f7824 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Full release notes with details on each version: [GitHub Releases](https://githu ## 0.9.44 (unreleased) +- Fix: `graphify affected` resolves an absolute-path seed against the repo root derived from the graph's own location instead of the current working directory, so a blast-radius query with an absolute seed run from anywhere (an editor, a script) no longer silently returns nothing; a seed outside the root still misses cleanly (#2706, thanks @ousamabenyounes). - Fix: a lazy CommonJS `require(...)` inside a function body (the idiom for breaking circular dependencies) now emits the same `imports_from`/`imports` dependency edges as a top-level require, attributed to the enclosing function, instead of being silently dropped; a dynamic `require(variable)` is still skipped (#2700, thanks @rajanpanth). - Fix: a JS/TS identifier bound by an import whose target resolves outside the scanned corpus (e.g. a `lucide-react` icon) is now shadowed, so using it as a value no longer fabricates an INFERRED `indirect_call` onto an unrelated same-named callable elsewhere in the corpus; a relative/in-corpus import still resolves to its real target (#2757, thanks @phudayyy). - Fix: an OCaml qualified call `M.f` to an external module (one not defined in the same file, e.g. Hardcaml's `Reg_spec.create`) no longer binds to a same-named local `let f` — which produced a false `calls` edge and, when the caller was that local `f`, a `f -> f` self-loop. External qualified calls are kept as a distinct target labelled by the full path; unqualified calls and calls into a locally-defined module still resolve locally, and cross-file `Geo.area` still collapses onto another file's `area`. diff --git a/tests/test_affected_cli.py b/tests/test_affected_cli.py index 9b853a8c..7c452907 100644 --- a/tests/test_affected_cli.py +++ b/tests/test_affected_cli.py @@ -371,3 +371,55 @@ def test_affected_absolute_seed_resolves_via_graph_root_off_cwd(tmp_path, monkey assert "Affected nodes for Foo" in out assert "X()" in out + +def test_affected_absolute_seed_outside_root_misses_cleanly(tmp_path, monkeypatch, capsys): + """An absolute seed that is NOT under the derived repo root must report a clean + no-match, not silently traverse from a wrong/guessed node (#2706).""" + from graphify.paths import GRAPHIFY_OUT_NAME + + repo_root = tmp_path / "repo" + out_dir = repo_root / GRAPHIFY_OUT_NAME + out_dir.mkdir(parents=True) + g = nx.DiGraph() + g.add_node("target", label="Foo", source_file="pkg/foo.py", source_location="L1") + g.add_node("caller", label="X()", source_file="app.py", source_location="L4") + g.add_edge("caller", "target", relation="calls") + gp = out_dir / "graph.json" + gp.write_text(json.dumps(json_graph.node_link_data(g, edges="links")), encoding="utf-8") + + monkeypatch.chdir(tmp_path) + outside_seed = str(tmp_path / "other-repo" / "pkg" / "foo.py") # same basename, different tree + monkeypatch.setattr(mainmod, "_check_skill_version", lambda _: None) + monkeypatch.setattr(mainmod.sys, "argv", + ["graphify", "affected", outside_seed, "--graph", str(gp)]) + mainmod.main() + + out = capsys.readouterr().out + assert "Affected nodes for Foo" not in out # must NOT resolve to the in-root Foo + + +def test_affected_absolute_seed_with_graph_not_under_out_dir(tmp_path, monkeypatch, capsys): + """Fallback layout: when --graph points at a graph.json NOT under the + graphify-out dir, the root is the graph's own parent (`else gp.parent`).""" + repo_root = tmp_path / "repo" + repo_root.mkdir(parents=True) + g = nx.DiGraph() + g.add_node("target", label="Foo", source_file="pkg/foo.py", source_location="L1") + g.add_node("caller", label="X()", source_file="app.py", source_location="L4") + g.add_edge("caller", "target", relation="calls") + gp = repo_root / "graph.json" # directly under repo_root, not graphify-out/ + gp.write_text(json.dumps(json_graph.node_link_data(g, edges="links")), encoding="utf-8") + + elsewhere = tmp_path / "elsewhere" + elsewhere.mkdir() + monkeypatch.chdir(elsewhere) + abs_seed = str(repo_root / "pkg" / "foo.py") + monkeypatch.setattr(mainmod, "_check_skill_version", lambda _: None) + monkeypatch.setattr(mainmod.sys, "argv", + ["graphify", "affected", abs_seed, "--graph", str(gp)]) + mainmod.main() + + out = capsys.readouterr().out + assert "Affected nodes for Foo" in out + assert "X()" in out +