diff --git a/code_review_graph/main.py b/code_review_graph/main.py index 5c2ed923..346d4f68 100644 --- a/code_review_graph/main.py +++ b/code_review_graph/main.py @@ -530,6 +530,8 @@ def get_affected_flows_tool( changed_files: Optional[list[str]] = None, base: str = "HEAD~1", repo_root: Optional[str] = None, + detail_level: str = "standard", + max_flows: int = 50, ) -> dict: """Find execution flows affected by changed files. @@ -541,10 +543,15 @@ def get_affected_flows_tool( changed_files: List of changed file paths (relative to repo root). Auto-detected if omitted. base: Git ref for auto-detecting changes. Default: HEAD~1. repo_root: Repository root path. Auto-detected if omitted. + detail_level: "standard" for full step details, "minimal" for per-flow + metadata only. Default: standard. + max_flows: Maximum flows to return; total reports the full count. + Default: 50. Pass 0 to disable the limit. """ root = _resolve_repo_root(repo_root) return with_provenance(get_affected_flows_func( changed_files=changed_files, base=base, repo_root=root, + detail_level=detail_level, max_flows=max_flows, ), root) diff --git a/code_review_graph/tools/review.py b/code_review_graph/tools/review.py index 9b877592..b211857d 100644 --- a/code_review_graph/tools/review.py +++ b/code_review_graph/tools/review.py @@ -293,6 +293,8 @@ def get_affected_flows_func( changed_files: list[str] | None = None, base: str = "HEAD~1", repo_root: str | None = None, + detail_level: str = "standard", + max_flows: int = 50, ) -> dict[str, Any]: """Find execution flows affected by changed files. @@ -305,9 +307,16 @@ def get_affected_flows_func( Auto-detected from git diff if omitted. base: Git ref for auto-detecting changes (default: HEAD~1). repo_root: Repository root path. Auto-detected if omitted. + detail_level: "standard" for full step details, "minimal" for + per-flow metadata only (name, criticality, depth, counts). + Every flow carries a full ``steps`` list in standard mode, so + large change sets can exceed 200k tokens without a bound (#849). + max_flows: Maximum flows to return (default: 50). ``total`` always + reports the untruncated count; 0 disables the limit. Returns: - Affected flows sorted by criticality, with step details. + Affected flows sorted by criticality; ``truncated`` is set when + ``max_flows`` cut the list. """ store, root = _get_store(repo_root) try: @@ -330,15 +339,33 @@ def get_affected_flows_func( result = _get_affected_flows(store, abs_files) total = result["total"] + flows = result["affected_flows"] + truncated = bool(max_flows) and max_flows > 0 and total > max_flows + if truncated: + flows = flows[:max_flows] + if detail_level == "minimal": + flows = [ + { + "id": f.get("id"), + "name": f.get("name"), + "criticality": f.get("criticality"), + "depth": f.get("depth"), + "node_count": f.get("node_count"), + "file_count": f.get("file_count"), + } + for f in flows + ] out = { "status": "ok", "summary": ( f"{total} flow(s) affected by changes " f"in {len(changed_files)} file(s)" + + (f", showing {len(flows)}" if truncated else "") ), "changed_files": changed_files, - "affected_flows": result["affected_flows"], + "affected_flows": flows, "total": total, + "truncated": truncated, } out["_hints"] = generate_hints( "get_affected_flows", out, get_session() diff --git a/tests/test_tools.py b/tests/test_tools.py index 16b56200..99781857 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -1266,6 +1266,44 @@ def test_get_affected_flows_summary(self): assert "flow(s) affected" in result["summary"] assert "changed_files" in result + def test_get_affected_flows_minimal_drops_steps(self): + """detail_level="minimal" strips per-flow step details (#849).""" + result = get_affected_flows_func( + changed_files=["auth.py"], + repo_root=str(self.root), + detail_level="minimal", + ) + assert result["status"] == "ok" + assert result["total"] >= 1 + for flow in result["affected_flows"]: + assert "steps" not in flow + assert "path" not in flow + assert "name" in flow + assert "criticality" in flow + + def test_get_affected_flows_max_flows_truncates(self): + """max_flows bounds the list while total keeps the full count (#849).""" + result = get_affected_flows_func( + changed_files=["auth.py"], + repo_root=str(self.root), + max_flows=1, + ) + assert result["status"] == "ok" + assert len(result["affected_flows"]) <= 1 + if result["total"] > 1: + assert result["truncated"] is True + assert "showing 1" in result["summary"] + + def test_get_affected_flows_max_flows_zero_disables_limit(self): + result = get_affected_flows_func( + changed_files=["auth.py"], + repo_root=str(self.root), + max_flows=0, + ) + assert result["status"] == "ok" + assert result["truncated"] is False + assert len(result["affected_flows"]) == result["total"] + class TestCommunityTools: """Tests for community-related MCP tool functions."""