diff --git a/my-tools/run_web_product_migration.py b/my-tools/run_web_product_migration.py index 227260dfc..ff3f89e58 100644 --- a/my-tools/run_web_product_migration.py +++ b/my-tools/run_web_product_migration.py @@ -199,6 +199,8 @@ Rules: - Preserve all existing dirty changes. Do not revert or rewrite unrelated work. - Read only the owned paths and their direct imported types/helpers. - For scoped searches, pass each owned path explicitly through the batch search runtime option; never pass a bare --runtime flag. +- Hard budget: at most 16 tool commands, including at most 8 read/search commands. After the read budget, implement immediately. +- Do not print or reread the complete diff; inspect only the edited regions once before writing the result. - Implement production behavior through the existing Blender Main/WASM command routes; do not simulate Blender behavior in JavaScript. - Do not run tests, builds, typecheck, lint, Chromium, full validators, or coverage binding. The user explicitly deferred verification to real usage. - Do not modify docs/EXECUTION_QUEUE.md, nextTask.md, source coverage ledgers, manifests, or this backlog. @@ -220,6 +222,8 @@ def run_task(task: dict, heartbeat_seconds: float) -> tuple[int, bool]: "--ephemeral", "--cd", str(ROOT), "--json", "-", ] completed_turn = False + result_seen_at: float | None = None + task_started_at = time.monotonic() with log_path.open("w", encoding="utf-8") as log: process = subprocess.Popen( command, @@ -238,6 +242,8 @@ def run_task(task: dict, heartbeat_seconds: float) -> tuple[int, bool]: 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(): + if result_seen_at is None: + result_seen_at = time.monotonic() try: with log_path.open("rb") as current_log: end = current_log.seek(0, os.SEEK_END) @@ -245,6 +251,8 @@ def run_task(task: dict, heartbeat_seconds: float) -> tuple[int, bool]: completed_turn = b'"type":"turn.completed"' in current_log.read() except OSError: completed_turn = False + if time.monotonic() - result_seen_at >= 5: + completed_turn = True if completed_turn: try: os.killpg(process.pid, signal.SIGTERM) @@ -255,6 +263,16 @@ def run_task(task: dict, heartbeat_seconds: float) -> tuple[int, bool]: os.killpg(process.pid, signal.SIGKILL) process.wait() break + if time.monotonic() - task_started_at >= 900: + 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 = 0 if completed_turn else (process.returncode or 0) ACTIVE_PROCESS = None diff --git a/tools/web/search-wbv2-batch-source.mjs b/tools/web/search-wbv2-batch-source.mjs index 57a698ca6..5ef59c0a3 100644 --- a/tools/web/search-wbv2-batch-source.mjs +++ b/tools/web/search-wbv2-batch-source.mjs @@ -11,6 +11,10 @@ const arg = (name, fallback = null) => { const index = process.argv.indexOf(name); return index >= 0 ? process.argv[index + 1] : fallback; }; +if (process.argv.includes("--help")) { + process.stdout.write("usage: node tools/web/search-wbv2-batch-source.mjs [--task ] --pattern [--runtime ...] or --list\n"); + process.exit(0); +} const task = arg("--task") ?? read("docs/EXECUTION_QUEUE.md").match(/\| 当前任务 \| `([^`]+)` \|/u)?.[1]; const pattern = arg("--pattern"); if (!task) throw new Error("unable to resolve current WBV2 task");