From 9edb2ae00c873e9cb844e189c783da2064a6969d Mon Sep 17 00:00:00 2001 From: Jonathan Singer Date: Thu, 1 Oct 2026 18:27:51 -0400 Subject: [PATCH] fix: disable automatic fix agents for PR review scans --- strix/core/runner.py | 1 + tests/test_runner_teardown.py | 42 +++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/strix/core/runner.py b/strix/core/runner.py index 924091b8d..c892d1dce 100644 --- a/strix/core/runner.py +++ b/strix/core/runner.py @@ -512,6 +512,7 @@ async def run_strix_scan( ) if ( auto_fix_enabled + and scan_config.get("mode") != "pr_review" and report_state is not None and local_sources and (not interactive or local_fix_branches_enabled) diff --git a/tests/test_runner_teardown.py b/tests/test_runner_teardown.py index ccede4109..3aa9ad16f 100644 --- a/tests/test_runner_teardown.py +++ b/tests/test_runner_teardown.py @@ -198,8 +198,26 @@ async def test_assessment_publishes_before_fixes_end_and_sandbox_teardown( @pytest.mark.asyncio @pytest.mark.parametrize("interactive", [False, True]) +@pytest.mark.parametrize("is_resume", [False, True]) +@pytest.mark.parametrize( + "fix_config", + [ + {"one_click_fixes_enabled": False}, + {"mode": "pr_review"}, + { + "mode": "pr_review", + "one_click_fixes_enabled": True, + "auto_fix_enabled": True, + "local_fix_branches_enabled": True, + }, + ], +) async def test_disabled_one_click_fixes_skips_fix_runtime( - monkeypatch: pytest.MonkeyPatch, tmp_path: Path, interactive: bool + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + interactive: bool, + is_resume: bool, + fix_config: dict[str, Any], ) -> None: _wire_runner(monkeypatch, tmp_path) events: list[str] = [] @@ -213,6 +231,9 @@ async def test_disabled_one_click_fixes_skips_fix_runtime( def get_existing_vulnerabilities(self) -> list[Any]: return [] + def get_total_llm_cost(self) -> float: + return 0.0 + def save_run_data(self, **_: Any) -> None: events.append("save") @@ -227,6 +248,23 @@ async def test_disabled_one_click_fixes_skips_fix_runtime( assert kwargs["return_on_completion"] is False return types.SimpleNamespace(final_output={"scan_completed": True}) + if is_resume: + coordinator = AgentCoordinator() + await coordinator.register("root", "Root Agent", parent_id=None) + await coordinator.set_status("root", "completed") + await coordinator.register("repair", "Fix agent", parent_id="root", skills=["fix_task"]) + await coordinator.register( + "reviewer", "Independent fix verifier", parent_id="repair", skills=["fix_task"] + ) + (tmp_path / "agents.json").write_text(json.dumps(await coordinator.snapshot())) + (tmp_path / "agents.db").touch() + + async def unexpected_child(**_: Any) -> None: + events.append("fix agent resumed") + raise AssertionError("Disabled fixes must not respawn repair or reviewer agents") + + monkeypatch.setattr(execution, "spawn_child_agent", unexpected_child) + monkeypatch.setattr(runner, "get_global_report_state", State) monkeypatch.setattr(runner, "ScanFixes", Fixes) monkeypatch.setattr(runner, "run_agent_loop", root) @@ -234,7 +272,7 @@ async def test_disabled_one_click_fixes_skips_fix_runtime( scan_config={ "targets": [], "scan_mode": "deep", - "one_click_fixes_enabled": False, + **fix_config, }, scan_id="scan", image="image",