diff --git a/my-tools/run_web_product_migration.py b/my-tools/run_web_product_migration.py index a78f03d31..227260dfc 100644 --- a/my-tools/run_web_product_migration.py +++ b/my-tools/run_web_product_migration.py @@ -219,6 +219,7 @@ def run_task(task: dict, heartbeat_seconds: float) -> tuple[int, bool]: "codex", "exec", "--dangerously-bypass-approvals-and-sandbox", "--ephemeral", "--cd", str(ROOT), "--json", "-", ] + completed_turn = False with log_path.open("w", encoding="utf-8") as log: process = subprocess.Popen( command, @@ -235,8 +236,27 @@ def run_task(task: dict, heartbeat_seconds: float) -> tuple[int, bool]: process.stdin.close() while process.poll() is None: write_status(read_json(BACKLOG), "implementing", activeTask=task["id"], childPid=process.pid, log=str(log_path.relative_to(ROOT))) + log.flush() + if result_path.is_file(): + try: + with log_path.open("rb") as current_log: + end = current_log.seek(0, os.SEEK_END) + current_log.seek(max(0, end - 8192)) + completed_turn = b'"type":"turn.completed"' in current_log.read() + except OSError: + completed_turn = False + if completed_turn: + try: + os.killpg(process.pid, signal.SIGTERM) + process.wait(timeout=5) + except ProcessLookupError: + pass + except subprocess.TimeoutExpired: + os.killpg(process.pid, signal.SIGKILL) + process.wait() + break time.sleep(heartbeat_seconds) - code = process.returncode or 0 + code = 0 if completed_turn else (process.returncode or 0) ACTIVE_PROCESS = None after = {path: file_hash(path) for path in task["ownedPaths"]} changed = any(before[path] != after[path] for path in before) @@ -247,7 +267,7 @@ def run_task(task: dict, heartbeat_seconds: float) -> tuple[int, bool]: result_ok = result.get("task") == task["id"] and result.get("status") == "implemented" except json.JSONDecodeError: pass - return code, changed and result_ok + return code, result_ok and (changed or completed_turn) def main() -> int: