nexu-io/open-design · error · SystemExit

running-right must be complete before deriving running-left

Error message

running-right must be complete before deriving running-left

What it means

The mirror script refuses to derive running-left until the running-right imagegen job is fully done. It loads imagegen-jobs.json, finds the running-right job, and requires job["status"] == "complete". Mirroring an incomplete strip would bake an interim, blank, or failed image into the left sprite, which finalize_pet_run.py would later reject or, worse, ship.

Source

Thrown at skills/hatch-pet/scripts/derive_running_left_from_running_right.py:88

        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)

View on GitHub (pinned to 5be4028344)

Solutions

  1. Inspect imagegen-jobs.json (or run pet_job_status.py --run-dir <dir>) and confirm the running-right job status reads "complete".
  2. If running-right is still pending/running, let $imagegen finish (or re-run it) until the job completes.
  3. If the image already exists on disk but the manifest is stale, run record_imagegen_result.py to record completion.
  4. Re-run derive_running_left_from_running_right.py once status is complete.
Defensive patterns

Strategy: validation

Validate before calling

import json
from pathlib import Path

run_dir = Path("<run_dir>")
manifest = json.loads((run_dir / "imagegen-jobs.json").read_text())
right = next(j for j in manifest["jobs"] if j.get("id") == "running-right")
if right.get("status") != "complete":
    raise RuntimeError(f"running-right not complete: {right.get('status')!r}")

Type guard

def right_job_ready(manifest: dict) -> bool:
    job = next((j for j in manifest.get("jobs", []) if j.get("id") == "running-right"), None)
    return isinstance(job, dict) and job.get("status") == "complete"

Prevention

When it happens

Trigger: Running derive_running_left_from_running_right.py --run-dir <dir> while the running-right job's status is anything other than the literal "complete" (e.g. "pending", "running", "failed", "queued").

Common situations: Running the derive step out of order, before $imagegen finishes; a running-right job stuck in "running"; a job that errored but was never re-queued; reading a stale manifest copy.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/87daeb97d7d87644. Report an issue: GitHub.