first-commit
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
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
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Conditionally derive running-left by mirroring the approved running-right strip."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image, ImageOps
|
||||
|
||||
|
||||
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 job_list(manifest: dict[str, object]) -> list[dict[str, object]]:
|
||||
jobs = manifest.get("jobs")
|
||||
if not isinstance(jobs, list):
|
||||
raise SystemExit("invalid imagegen-jobs.json: jobs must be a list")
|
||||
return [job for job in jobs if isinstance(job, dict)]
|
||||
|
||||
|
||||
def find_job(manifest: dict[str, object], job_id: str) -> dict[str, object]:
|
||||
for job in job_list(manifest):
|
||||
if job.get("id") == job_id:
|
||||
return job
|
||||
raise SystemExit(f"unknown job id: {job_id}")
|
||||
|
||||
|
||||
def file_sha256(path: Path) -> str:
|
||||
digest = hashlib.sha256()
|
||||
with path.open("rb") as file:
|
||||
for chunk in iter(lambda: file.read(1024 * 1024), b""):
|
||||
digest.update(chunk)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def image_metadata(path: Path) -> dict[str, object]:
|
||||
with Image.open(path) as image:
|
||||
image.verify()
|
||||
with Image.open(path) as image:
|
||||
return {
|
||||
"width": image.width,
|
||||
"height": image.height,
|
||||
"mode": image.mode,
|
||||
"format": image.format,
|
||||
}
|
||||
|
||||
|
||||
def manifest_relative(path: Path, run_dir: Path) -> str:
|
||||
return str(path.resolve().relative_to(run_dir.resolve()))
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--run-dir", required=True)
|
||||
parser.add_argument(
|
||||
"--confirm-appropriate-mirror",
|
||||
action="store_true",
|
||||
help="Required after visually confirming the rightward strip can be mirrored without identity/prop issues.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decision-note",
|
||||
required=True,
|
||||
help="Short note explaining why mirroring is acceptable for this pet.",
|
||||
)
|
||||
parser.add_argument("--force", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.confirm_appropriate_mirror:
|
||||
raise SystemExit("refusing to mirror without --confirm-appropriate-mirror")
|
||||
if not args.decision_note.strip():
|
||||
raise SystemExit("--decision-note must explain why mirroring is appropriate")
|
||||
|
||||
run_dir = Path(args.run_dir).expanduser().resolve()
|
||||
manifest_path = run_dir / "imagegen-jobs.json"
|
||||
manifest = load_manifest(run_dir)
|
||||
right_job = find_job(manifest, "running-right")
|
||||
left_job = find_job(manifest, "running-left")
|
||||
|
||||
if right_job.get("status") != "complete":
|
||||
raise SystemExit("running-right must be complete before deriving running-left")
|
||||
mirror_policy = left_job.get("mirror_policy")
|
||||
if not isinstance(mirror_policy, dict) or mirror_policy.get("may_derive_from") != "running-right":
|
||||
raise SystemExit("running-left is not configured for conditional mirroring")
|
||||
|
||||
source = run_dir / "decoded" / "running-right.png"
|
||||
output = run_dir / "decoded" / "running-left.png"
|
||||
if not source.is_file():
|
||||
raise SystemExit(f"running-right decoded strip not found: {source}")
|
||||
if output.exists() and not args.force:
|
||||
raise SystemExit(f"{output} already exists; pass --force to replace it")
|
||||
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
with Image.open(source) as image:
|
||||
mirrored = ImageOps.mirror(image.convert("RGBA"))
|
||||
mirrored.save(output)
|
||||
|
||||
left_job["status"] = "complete"
|
||||
left_job["source_path"] = manifest_relative(source, run_dir)
|
||||
left_job["source_provenance"] = "deterministic-mirror"
|
||||
left_job["derived_from"] = "running-right"
|
||||
left_job["source_sha256"] = file_sha256(source)
|
||||
left_job["output_sha256"] = file_sha256(output)
|
||||
left_job["completed_at"] = datetime.now(timezone.utc).isoformat()
|
||||
left_job["metadata"] = image_metadata(output)
|
||||
left_job["mirror_decision"] = {
|
||||
"approved": True,
|
||||
"approved_at": left_job["completed_at"],
|
||||
"note": args.decision_note.strip(),
|
||||
}
|
||||
for key in [
|
||||
"last_error",
|
||||
"secondary_fallback",
|
||||
"synthetic_test_source",
|
||||
"repair_reason",
|
||||
"queued_at",
|
||||
]:
|
||||
left_job.pop(key, None)
|
||||
|
||||
manifest_path.write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"ok": True,
|
||||
"job_id": "running-left",
|
||||
"derived_from": "running-right",
|
||||
"output": str(output),
|
||||
"decision_note": args.decision_note.strip(),
|
||||
},
|
||||
indent=2,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user