diff --git a/code_review_graph/changes.py b/code_review_graph/changes.py index 2165fba1..0aa6f595 100644 --- a/code_review_graph/changes.py +++ b/code_review_graph/changes.py @@ -420,6 +420,19 @@ def analyze_changes( for key, ranges in parse_diff_ranges(repo_root, base).items() } + # The affected-flows lookup and the no-ranges fallback match + # changed_files against nodes.file_path, which stores absolute + # normalized paths. CLI callers pass repo-relative diff paths, so an + # exact IN (...) match finds no nodes and detect-changes reports + # "0 affected flow(s)" even when the MCP tool reports hundreds on the + # same input (#848). Remap the same way as changed_ranges keys; + # already-absolute inputs (MCP) pass through pathlib joining unchanged. + if repo_root is not None: + _root = Path(repo_root) + changed_files = [ + normalize_file_path(_root / fp) for fp in changed_files + ] + # Map changes to nodes. if changed_ranges: changed_nodes = map_changes_to_nodes(store, changed_ranges) diff --git a/tests/test_changes.py b/tests/test_changes.py index c0d4f75f..b1547371 100644 --- a/tests/test_changes.py +++ b/tests/test_changes.py @@ -403,6 +403,35 @@ def test_analyze_changes_with_flows(self): ) assert len(result["affected_flows"]) >= 1 + def test_analyze_changes_flows_with_relative_cli_paths(self): + """Relative changed_files still hit flows stored under absolute paths. + + Real builds store absolute node paths while the CLI passes + repo-relative diff paths, which made detect-changes report + "0 affected flow(s)" where the MCP tool reported them (#848). + """ + repo_root = "/repo" if not Path("C:/").exists() else "C:/repo" + routes_abs = f"{repo_root}/routes.py" + services_abs = f"{repo_root}/services.py" + self._add_func("handler", path=routes_abs, line_start=1, line_end=10) + self._add_func("service", path=services_abs, line_start=1, line_end=10) + self._add_call( + f"{routes_abs}::handler", + f"{services_abs}::service", + routes_abs, + ) + + flows = trace_flows(self.store) + store_flows(self.store, flows) + + result = analyze_changes( + self.store, + changed_files=["services.py"], + changed_ranges={services_abs: [(1, 10)]}, + repo_root=repo_root, + ) + assert len(result["affected_flows"]) >= 1 + def test_analyze_changes_review_priorities_ordered(self): """Review priorities are ordered by descending risk score.""" # Create several functions with varying risk levels.