From 62f4b9196bbcd7687810001e81129a5fcdc5037e Mon Sep 17 00:00:00 2001 From: toki Date: Mon, 10 Aug 2026 02:09:46 +0900 Subject: [PATCH] =?UTF-8?q?fix(agent-ops):=20Replan=20=EC=9D=B4=EB=A0=A5?= =?UTF-8?q?=EA=B3=BC=20=EC=99=84=EB=A3=8C=20=EB=A1=9C=EA=B7=B8=EB=A5=BC=20?= =?UTF-8?q?=EA=B5=AC=EB=B6=84=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scripts/run_epic_cycle.py | 16 +++++++++--- .../tests/test_run_epic_cycle.py | 26 +++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/agent-ops/skills/common/prepare-epic-work-items/scripts/run_epic_cycle.py b/agent-ops/skills/common/prepare-epic-work-items/scripts/run_epic_cycle.py index d7c7aeb7..2f1fe1e9 100755 --- a/agent-ops/skills/common/prepare-epic-work-items/scripts/run_epic_cycle.py +++ b/agent-ops/skills/common/prepare-epic-work-items/scripts/run_epic_cycle.py @@ -392,9 +392,10 @@ def active_task_finalization_artifacts(workspace: Path, task_group: str) -> list root = workspace / "agent-task" / task_group if not root.exists(): return [] - values: set[Path] = set() - for pattern in ("complete.log", "plan_*.log", "code_review_*.log"): - values.update(root.rglob(pattern)) + values: set[Path] = set(root.rglob("complete.log")) + for path in root.rglob("code_review_*.log"): + if "\n## Code Review Result\n" in path.read_text(encoding="utf-8"): + values.add(path) return sorted(values) @@ -789,8 +790,15 @@ def cycle(args: argparse.Namespace) -> int: ) recovery_details: list[str] = [] if args.retry and prior_cycle_status == "failed": - recovery_details.append(str(state.get("reason", "unknown parent failure"))) finalized = active_task_finalization_artifacts(workspace, task_group) + prior_reason = str(state.get("reason", "unknown parent failure")) + if not ( + prior_reason.startswith( + "preparation agent created forbidden finalization artifacts:" + ) + and not finalized + ): + recovery_details.append(prior_reason) if finalized: recovery_details.append( "Remove forbidden preparation-time finalization artifacts and restore valid active pairs: " diff --git a/agent-ops/skills/common/prepare-epic-work-items/tests/test_run_epic_cycle.py b/agent-ops/skills/common/prepare-epic-work-items/tests/test_run_epic_cycle.py index 551fa215..5676c706 100644 --- a/agent-ops/skills/common/prepare-epic-work-items/tests/test_run_epic_cycle.py +++ b/agent-ops/skills/common/prepare-epic-work-items/tests/test_run_epic_cycle.py @@ -345,6 +345,32 @@ class EpicCycleContractTest(unittest.TestCase): self.assertIn("explicit retry", prompt) self.assertIn("git diff --cached --check failed", prompt) + def test_finalization_artifacts_allow_replan_logs_but_reject_verdicts(self) -> None: + with tempfile.TemporaryDirectory() as raw: + workspace = Path(raw) + task = workspace / "agent-task" / "m-sample" / "01_work" + task.mkdir(parents=True) + replan_log = task / "code_review_local_G01_0.log" + replan_log.write_text("# Code Review Reference\n", encoding="utf-8") + + self.assertEqual( + MODULE.active_task_finalization_artifacts(workspace, "m-sample"), + [], + ) + + verdict_log = task / "code_review_local_G02_0.log" + verdict_log.write_text( + "# Code Review Reference\n\n## Code Review Result\n\nPASS\n", + encoding="utf-8", + ) + complete_log = task / "complete.log" + complete_log.write_text("done\n", encoding="utf-8") + + self.assertEqual( + MODULE.active_task_finalization_artifacts(workspace, "m-sample"), + [verdict_log, complete_log], + ) + def test_validate_pair_rejects_foreign_pair_outside_selected_batch(self) -> None: with tempfile.TemporaryDirectory() as raw: workspace = Path(raw)