Files
open-design/skills/hatch-pet/scripts/pet_job_status.py
T
Zakaria a46764fb1b
ci / Validate workspace (push) Has been cancelled
landing-page-ci / Validate landing page (push) Has been cancelled
landing-page-deploy / Deploy landing page (push) Has been cancelled
github-metrics / Generate repository metrics SVG (push) Has been cancelled
refresh-contributors-wall / Refresh contributors wall cache bust (push) Waiting to run
first-commit
2026-05-04 14:58:14 -04:00

118 lines
4.0 KiB
Python

#!/usr/bin/env python3
"""Show ready and pending $imagegen jobs for a Codex pet run."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
def load_manifest(run_dir: Path) -> dict[str, object]:
path = run_dir / "imagegen-jobs.json"
if not path.exists():
raise SystemExit(f"job manifest not found: {path}")
return json.loads(path.read_text(encoding="utf-8"))
def jobs(manifest: dict[str, object]) -> list[dict[str, object]]:
raw = manifest.get("jobs")
if not isinstance(raw, list):
raise SystemExit("invalid imagegen-jobs.json: jobs must be a list")
return [job for job in raw if isinstance(job, dict)]
def completed_ids(manifest: dict[str, object]) -> set[str]:
return {
str(job["id"])
for job in jobs(manifest)
if job.get("status") == "complete" and isinstance(job.get("id"), str)
}
def missing_deps(job: dict[str, object], completed: set[str]) -> list[str]:
deps = job.get("depends_on", [])
if not isinstance(deps, list):
return []
return [dep for dep in deps if isinstance(dep, str) and dep not in completed]
def job_view(
job: dict[str, object], run_dir: Path, completed: set[str]
) -> dict[str, object]:
prompt_file = job.get("prompt_file")
output_path = job.get("output_path")
inputs = (
job.get("input_images") if isinstance(job.get("input_images"), list) else []
)
input_images = []
for item in inputs:
path = (
run_dir / item["path"]
if isinstance(item, dict) and isinstance(item.get("path"), str)
else None
)
input_images.append(
{
"path": str(path) if path else None,
"role": item.get("role") if isinstance(item, dict) else None,
"exists": path.is_file() if path else False,
}
)
return {
"id": job.get("id"),
"kind": job.get("kind"),
"status": job.get("status", "pending"),
"prompt_file": str(run_dir / prompt_file)
if isinstance(prompt_file, str)
else None,
"input_images": input_images,
"output_path": str(run_dir / output_path)
if isinstance(output_path, str)
else None,
"missing_dependencies": missing_deps(job, completed),
"repair_attempt": job.get("repair_attempt", 0),
"generation_skill": job.get("generation_skill"),
"requires_grounded_generation": job.get("requires_grounded_generation", False),
"allow_prompt_only_generation": job.get("allow_prompt_only_generation", False),
"identity_reference_paths": job.get("identity_reference_paths", []),
"mirror_policy": job.get("mirror_policy", {}),
"derived_from": job.get("derived_from"),
"source_provenance": job.get("source_provenance"),
"mirror_decision": job.get("mirror_decision"),
"recording_owner": job.get("recording_owner", "parent"),
}
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--run-dir", required=True)
args = parser.parse_args()
run_dir = Path(args.run_dir).expanduser().resolve()
manifest = load_manifest(run_dir)
completed = completed_ids(manifest)
pending = [
job for job in jobs(manifest) if job.get("status", "pending") != "complete"
]
ready = [job for job in pending if not missing_deps(job, completed)]
blocked = [job for job in pending if missing_deps(job, completed)]
result = {
"ok": True,
"run_dir": str(run_dir),
"counts": {
"total": len(jobs(manifest)),
"complete": len(completed),
"ready": len(ready),
"blocked": len(blocked),
},
"ready_jobs": [job_view(job, run_dir, completed) for job in ready],
"blocked_jobs": [job_view(job, run_dir, completed) for job in blocked],
}
print(json.dumps(result, indent=2))
if __name__ == "__main__":
main()