feat(web): reuse verified batches in unattended flow
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled

This commit is contained in:
mes123456
2026-09-03 13:10:21 +08:00
parent 814b290c29
commit 646e580b94
14 changed files with 330 additions and 231 deletions

View File

@@ -299,11 +299,13 @@ def make_card(task: str, parent: str, status: str, batch: dict, entrypoint: str)
def select_batch(inventory: dict) -> dict:
units = {unit.get("identifier"): unit for unit in inventory.get("units", []) if isinstance(unit, dict) and unit.get("coverageStatus") != "VERIFIED"}
units = {unit.get("identifier"): unit for unit in inventory.get("units", []) if isinstance(unit, dict)}
for candidate in PREFERRED_BATCHES:
selected = [units.get(name) for name in candidate["identifiers"] + candidate["rna"]]
if all(selected):
return {**candidate, "units": selected}
unresolved = [unit for unit in selected if unit.get("coverageStatus") != "VERIFIED"]
if unresolved:
return {**candidate, "units": unresolved, "candidateUnits": selected}
raise SystemExit("no configured unresolved Blender source batch remains")
@@ -357,7 +359,7 @@ def main() -> int:
write_text(root_card_path, make_card(root_task, current, "done", batch, f"python3 my-tools/authorize_next_wbv2_task.py --write"))
write_text(module_card_path, make_card(module_task, root_task, "in_progress", batch, f"node tools/web/run-wbv2-module-task.mjs --task {module_task} --write"))
root_report_path, root_status_path, root_manifest_path = root_out / "successor-authorization-report.json", ROOT / "docs/status" / f"{root_task}.md", root_out / "manifest.json"
write_json(root_report_path, {"schemaVersion": 1, "task": root_task, "operation": "AUTHORIZE_SOURCE_BACKED_MODULE_BATCH", "taskClass": "QUEUE_ENABLEMENT", "status": "PASS", "parent": {"task": current, "manifest": rel(current_manifest_path), "manifestSha256": sha(current_manifest_path)}, "module": {"moduleId": batch["moduleId"], "task": module_task, "batchSize": len(batch["units"]), "sourceUnits": [unit["id"] for unit in batch["units"]], "sourceBacked": True}, "inventory": {"unitCount": inventory["summary"]["unitCount"], "verifiedUnits": inventory["coverage"]["verifiedUnits"], "unverifiedUnits": inventory["coverage"]["unverifiedUnits"], "sourceTreeSha256": inventory["authority"]["sourceTreeSha256"], "boundUnitDelta": 0}, "nextTask": module_task})
write_json(root_report_path, {"schemaVersion": 1, "task": root_task, "operation": "AUTHORIZE_SOURCE_BACKED_MODULE_BATCH", "taskClass": "QUEUE_ENABLEMENT", "status": "PASS", "parent": {"task": current, "manifest": rel(current_manifest_path), "manifestSha256": sha(current_manifest_path)}, "module": {"moduleId": batch["moduleId"], "task": module_task, "batchSize": len(batch["units"]), "candidateBatchSize": len(batch.get("candidateUnits", batch["units"])), "sourceUnits": [unit["id"] for unit in batch["units"]], "sourceBacked": True}, "inventory": {"unitCount": inventory["summary"]["unitCount"], "verifiedUnits": inventory["coverage"]["verifiedUnits"], "unverifiedUnits": inventory["coverage"]["unverifiedUnits"], "sourceTreeSha256": inventory["authority"]["sourceTreeSha256"], "boundUnitDelta": 0}, "efficiency": {"existingBindingsReused": len(batch.get("candidateUnits", batch["units"])) - len(batch["units"]), "selectionPolicy": "all-candidate-units-then-unresolved-only"}, "nextTask": module_task})
write_text(root_status_path, f"# {root_task} status\n\n- task: `{root_task}`; parent: `{current}`; status: `done`\n- taskClass: `QUEUE_ENABLEMENT`; no modifier-family parity completion claimed\n- source-backed batch authorized: `{batch['moduleId']}` ({len(batch['units'])} units)\n- dynamic inventory: `{inventory['summary']['unitCount']}` units; verified `{inventory['coverage']['verifiedUnits']}`; unverified `{inventory['coverage']['unverifiedUnits']}`; bound delta `0`\n- nextTask: `{module_task}`\n")
write_json(root_manifest_path, {"schemaVersion": 1, "task": root_task, "parentTask": current, "operation": "AUTHORIZE_SOURCE_BACKED_MODULE_BATCH", "status": "done", "enablingTask": True, "parityStateChange": False, "runtime": "MODULE_EXECUTION_QUEUE_BRIDGE", "artifacts": {"parentManifest": {"path": rel(current_manifest_path), "sha256": sha(current_manifest_path)}, "plan": {"path": rel(root_plan_path), "sha256": sha(root_plan_path)}, "report": {"path": rel(root_report_path), "sha256": sha(root_report_path)}, "status": {"path": rel(root_status_path), "sha256": sha(root_status_path)}}, "assertions": {"sourceBackedSuccessor": True, "moduleId": batch["moduleId"], "batchSize": len(batch["units"]), "inventoryTotal": 6900, "effectiveCompleted": 290, "effectivePending": 6609, "dynamicUnitCount": inventory["summary"]["unitCount"], "dynamicVerifiedUnits": inventory["coverage"]["verifiedUnits"], "dynamicUnverifiedUnits": inventory["coverage"]["unverifiedUnits"], "boundUnitDelta": 0, "fullHistoricalReproduction": False}, "nextTask": module_task})
@@ -377,7 +379,7 @@ def main() -> int:
# wastes budget and can fail solely because the parent manifest grew; only
# the active implementation batch needs a packet.
run_node("tools/web/check-task-context.mjs", "--task", module_task, "--write")
print(f"wbv2-successor-authorized root={root_task} next={module_task} module={batch['moduleId']} batchSize={len(batch['units'])} inventoryDelta=0")
print(f"wbv2-successor-authorized root={root_task} next={module_task} module={batch['moduleId']} batchSize={len(batch['units'])} reused={len(batch.get('candidateUnits', batch['units'])) - len(batch['units'])} inventoryDelta=0")
return 0

View File

@@ -8,6 +8,7 @@ SCHEDULER_POLL_MS="${WBV2_POLL_MS:-1000}"
SCHEDULER_PUSH_RETRIES="${WBV2_PUSH_RETRIES:-6}"
WATCHDOG_INTERVAL="${WBV2_WATCHDOG_INTERVAL:-10}"
WATCHDOG_RESTART_DELAY="${WBV2_WATCHDOG_RESTART_DELAY:-5}"
AUTO_AUTHORIZE="${WBV2_AUTO_AUTHORIZE:-1}"
# Keep repository evidence on disk while placing compiler/transient caches in
# RAM. Eight compile jobs is the repository's stability limit.
@@ -44,6 +45,10 @@ start_scheduler() {
start_watchdog() {
local pid_file="$CONTROL_DIR/my-tools-watchdog.pid"
local auto_authorize_args=()
if test "$AUTO_AUTHORIZE" != "0"; then
auto_authorize_args+=(--auto-authorize)
fi
if test -f "$pid_file"; then
local watchdog_pid
watchdog_pid="$(tr -d '[:space:]' < "$pid_file")"
@@ -58,6 +63,7 @@ start_watchdog() {
--restart-delay "$WATCHDOG_RESTART_DELAY" \
--max-iterations "$SCHEDULER_MAX_ITERATIONS" \
--push-retries "$SCHEDULER_PUSH_RETRIES" \
"${auto_authorize_args[@]}" \
--commit --push \
> "$CONTROL_DIR/my-tools-watchdog.stdout.log" 2>&1 < /dev/null &
printf 'watchdog launch pid: %s\n' "$!"

View File

@@ -1,8 +1,9 @@
#!/usr/bin/env python3
"""Watch and safely restart the Blender/WBV2 scheduler process.
"""Watch, authorize, and safely restart the Blender/WBV2 scheduler process.
The watchdog only restarts an unexpectedly missing scheduler. It never edits
the queue, claims tasks, retries blocked checkpoints, or deletes evidence.
The watchdog delegates terminal queue advancement to the source-backed
authorizer. It never edits the queue directly, retries blocked checkpoints, or
deletes evidence.
"""
from __future__ import annotations
@@ -22,6 +23,7 @@ from wbv2_runtime import ROOT, SCHEDULER_CHECKPOINT, SCHEDULER_LOCK, lock_owner,
WATCHDOG_LOCK = ROOT / "tests/golden/WBV2/my-tools-watchdog.lock"
WATCHDOG_PID = ROOT / "tests/golden/WBV2/my-tools-watchdog.pid"
WATCHDOG_STATUS = ROOT / "tests/golden/WBV2/my-tools-watchdog.json"
AUTHORIZATION_LOG = ROOT / "tests/golden/WBV2/my-tools-authorization.log"
def now() -> str:
@@ -81,6 +83,24 @@ def checkpoint_task(value: dict) -> str | None:
return None
def authorize_terminal_queue() -> tuple[bool, str]:
"""Ask the repository authorizer for one source-backed successor batch."""
result = subprocess.run(
["python3", str(ROOT / "my-tools/authorize_next_wbv2_task.py"), "--write"],
cwd=ROOT,
text=True,
capture_output=True,
check=False,
)
AUTHORIZATION_LOG.parent.mkdir(parents=True, exist_ok=True)
AUTHORIZATION_LOG.write_text(
f"$ python3 my-tools/authorize_next_wbv2_task.py --write\n{result.stdout}{result.stderr}",
encoding="utf-8",
)
output = f"{result.stdout}{result.stderr}".strip()
return result.returncode == 0, output
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--interval", type=float, default=10.0)
@@ -90,6 +110,8 @@ def main() -> int:
parser.add_argument("--commit", action="store_true", default=os.getenv("WBV2_AUTO_COMMIT", "1") != "0")
parser.add_argument("--push", action="store_true", default=os.getenv("WBV2_AUTO_PUSH", "1") != "0")
parser.add_argument("--push-retries", type=int, default=6)
parser.add_argument("--auto-authorize", dest="auto_authorize", action="store_true", default=os.getenv("WBV2_AUTO_AUTHORIZE", "1") != "0", help="authorize a source-backed successor when the queue reaches a terminal task")
parser.add_argument("--no-auto-authorize", dest="auto_authorize", action="store_false")
args = parser.parse_args()
if args.interval <= 0 or args.restart_delay < 0 or args.max_restarts < 0 or args.max_iterations < 0:
parser.error("interval/delay/limits must be non-negative; interval must be positive")
@@ -121,6 +143,38 @@ def main() -> int:
saved_checkpoint = checkpoint()
state = saved_checkpoint.get("state")
applies = checkpoint_task(saved_checkpoint) == pointer.get("task")
if terminal_pointer(pointer) and args.auto_authorize:
authorized, output = authorize_terminal_queue()
if authorized:
write_json(
WATCHDOG_STATUS,
{
"schemaVersion": 1,
"status": "authorized",
"reason": "source_backed_successor",
"task": pointer.get("task"),
"updatedAt": now(),
"restartCount": restarts,
"authorizationLog": str(AUTHORIZATION_LOG.relative_to(ROOT)),
},
)
child = None
time.sleep(0.2)
continue
reason = "no_configured_unresolved_source_batch" if "no configured unresolved Blender source batch remains" in output else "successor_authorization_failed"
write_json(
WATCHDOG_STATUS,
{
"schemaVersion": 1,
"status": "stopped" if reason == "no_configured_unresolved_source_batch" else "blocked",
"reason": reason,
"task": pointer.get("task"),
"updatedAt": now(),
"restartCount": restarts,
"authorizationLog": str(AUTHORIZATION_LOG.relative_to(ROOT)),
},
)
return 0 if reason == "no_configured_unresolved_source_batch" else 2
if terminal_pointer(pointer) or (applies and state in {"complete", "blocked", "stopped"}):
write_json(
WATCHDOG_STATUS,