#!/usr/bin/env python3 """Run the full local Meta3 preparation chain. No network calls.""" from __future__ import annotations import argparse import json import subprocess import sys from datetime import datetime, timezone from pathlib import Path from typing import Any ROOT = Path(__file__).resolve().parents[1] def write_json(path: Path, data: dict[str, Any]) -> None: path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") def load_json(path: Path) -> dict[str, Any]: with path.open("r", encoding="utf-8") as handle: data = json.load(handle) if not isinstance(data, dict): raise ValueError(f"Expected JSON object: {path}") return data def run_step(name: str, args: list[str]) -> dict[str, Any]: started_at = datetime.now(timezone.utc).isoformat() completed = subprocess.run( [sys.executable, *args], cwd=ROOT, check=False, text=True, capture_output=True, ) return { "name": name, "argv": [sys.executable, *args], "started_at": started_at, "finished_at": datetime.now(timezone.utc).isoformat(), "returncode": completed.returncode, "stdout": completed.stdout, "stderr": completed.stderr, "status": "pass" if completed.returncode == 0 else "fail", } def parse_run_id(stdout: str) -> str: for line in stdout.splitlines(): if line.startswith("run_id="): return line.split("=", 1)[1].strip() raise ValueError("Could not parse run_id from meta3_prepare.py output") def markdown_report(report: dict[str, Any]) -> str: lines = [ "# Meta3 Local Orchestration Report", "", f"Run: `{report.get('run_id') or 'unknown'}`", f"Created: `{report['created_at']}`", f"Status: `{report['status']}`", "", "## Steps", "", ] for step in report["steps"]: lines.append(f"- `{step['name']}`: `{step['status']}` rc={step['returncode']}") lines.extend( [ "", "## Summary", "", f"- Network calls made: {report['network_calls_made']}", f"- Readiness status: {report.get('readiness_status')}", f"- Network preflight status: {report.get('network_preflight_status')}", f"- Morning status: {report.get('morning_status')}", f"- Runner status: {report.get('runner_status')}", ] ) if report["blockers"]: lines.extend(["", "## Blockers", ""]) lines.extend(f"- {blocker}" for blocker in report["blockers"]) return "\n".join(lines) + "\n" def maybe_status(path: Path) -> str | None: if not path.exists(): return None return str(load_json(path).get("status")) def main() -> int: parser = argparse.ArgumentParser(description="Run Meta3 local prep orchestration without network calls.") parser.add_argument("--run-name", default="orchestrated-local-ready") parser.add_argument("--accepted-call-budget", type=int, default=100) parser.add_argument("--calibration-sample-size", type=int, default=24) parser.add_argument("--claim-shard-size", type=int, default=24) parser.add_argument("--dry-run-limit", type=int, default=1) parser.add_argument("--with-mock-response", action="store_true") args = parser.parse_args() steps: list[dict[str, Any]] = [] blockers: list[str] = [] run_id: str | None = None smoke = run_step("smoke", ["scripts/meta3_smoke.py"]) steps.append(smoke) if smoke["returncode"] != 0: blockers.append("smoke failed") if not blockers: prepare = run_step("prepare", ["scripts/meta3_prepare.py", "--run-name", args.run_name]) steps.append(prepare) if prepare["returncode"] != 0: blockers.append("prepare failed") else: run_id = parse_run_id(str(prepare["stdout"])) if run_id and not blockers: planned_steps = [ ("plan_batches", ["scripts/meta3_plan_batches.py", "--run-id", run_id]), ("freeze_sources", ["scripts/meta3_freeze_sources.py", "--run-id", run_id]), ( "build_calibration_workpackets", [ "scripts/meta3_build_workpackets.py", "--run-id", run_id, "--mode", "calibration", "--sample-size", str(args.calibration_sample_size), ], ), ( "build_claim_ledger_workpackets", [ "scripts/meta3_build_workpackets.py", "--run-id", run_id, "--mode", "claim_ledger", "--shard-size", str(args.claim_shard_size), ], ), ("validate_run", ["scripts/meta3_validate_run.py", "--run-id", run_id]), ( "dry_run_calibration_runner", [ "scripts/meta3_run_packets.py", "--run-id", run_id, "--mode", "calibration", "--limit", str(args.dry_run_limit), ], ), ( "dry_run_claim_ledger_runner", [ "scripts/meta3_run_packets.py", "--run-id", run_id, "--mode", "claim_ledger", "--limit", str(args.dry_run_limit), ], ), ("runner_status", ["scripts/meta3_runner_status.py", "--run-id", run_id]), ( "network_preflight", [ "scripts/meta3_preflight.py", "--run-id", run_id, "--accepted-call-budget", str(args.accepted_call_budget), ], ), ] if args.with_mock_response: planned_steps.append( ( "create_mock_calibration_response", ["scripts/meta3_create_mock_response.py", "--run-id", run_id, "--mode", "calibration"], ) ) planned_steps.extend( [ ( "collect_calibration_responses", [ "scripts/meta3_collect_responses.py", "--run-id", run_id, "--mode", "calibration", "--allow-empty", ], ), ( "collect_claim_ledger_responses", [ "scripts/meta3_collect_responses.py", "--run-id", run_id, "--mode", "claim_ledger", "--allow-empty", ], ), ("morning_report", ["scripts/meta3_morning_report.py", "--run-id", run_id]), ] ) for name, argv in planned_steps: step = run_step(name, argv) steps.append(step) if step["returncode"] != 0: blockers.append(f"{name} failed") stderr_first_line = str(step.get("stderr") or "").strip().splitlines() if stderr_first_line: blockers.append(f"{name}: {stderr_first_line[0]}") break run_dir = ROOT / "runs" / run_id if run_id else None readiness_status = maybe_status(run_dir / "reports/run_readiness_report.json") if run_dir else None preflight_status = maybe_status(run_dir / "reports/network_preflight.json") if run_dir else None morning_status = maybe_status(run_dir / "reports/morning_report.json") if run_dir else None runner_status = maybe_status(run_dir / "reports/runner_status.json") if run_dir else None if preflight_status != "pass": blockers.append("network preflight is not pass") if morning_status == "prepared_not_executed": blockers.append("run is prepared but not analytically executed") status = "prepared_blocked" if blockers else "ready_for_network_start" report = { "schema": "meta3.local_orchestration_report.v1", "run_id": run_id, "created_at": datetime.now(timezone.utc).isoformat(), "status": status, "steps": steps, "readiness_status": readiness_status, "network_preflight_status": preflight_status, "morning_status": morning_status, "runner_status": runner_status, "blockers": sorted(set(blockers)), "network_calls_made": 0, } if run_dir: report_path = run_dir / "reports/local_orchestration_report.json" report_md_path = run_dir / "reports/LOCAL_ORCHESTRATION_REPORT.md" else: report_path = ROOT / "reports/local_orchestration_report.json" report_md_path = ROOT / "reports/LOCAL_ORCHESTRATION_REPORT.md" write_json(report_path, report) report_md_path.write_text(markdown_report(report), encoding="utf-8") print(f"local_orchestration_report={report_path}") print(f"local_orchestration_report_md={report_md_path}") print(f"run_id={run_id or 'none'}") print(f"status={status}") print(f"steps={len(steps)}") print(f"blockers={len(report['blockers'])}") print("network_calls_made=0") return 0 if all(step["returncode"] == 0 for step in steps) else 1 if __name__ == "__main__": raise SystemExit(main())