nexu-io/open-design · error · SystemExit

running-left is not configured for conditional mirroring

Error message

running-left is not configured for conditional mirroring

What it means

Mirroring is opt-in per pet. The running-left job in imagegen-jobs.json must carry a mirror_policy object whose may_derive_from equals the literal "running-right". Without this declaration the script treats mirroring as unapproved for this pet, because some pets are asymmetric (props, markings, handedness) and must be drawn independently.

Source

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

    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)

View on GitHub (pinned to 5be4028344)

Solutions

  1. Visually confirm the pet is actually symmetric enough that mirroring running-right produces a correct left strip.
  2. If symmetric, add "mirror_policy": {"may_derive_from": "running-right"} to the running-left job in imagegen-jobs.json.
  3. If asymmetric, abandon mirroring and generate running-left independently via $imagegen + record_imagegen_result.py.
  4. Re-run the derive script.

Example fix

// before (running-left job)
{
  "id": "running-left",
  "status": "pending"
}
// after
{
  "id": "running-left",
  "status": "pending",
  "mirror_policy": { "may_derive_from": "running-right" }
}
Defensive patterns

Strategy: type-guard

Validate before calling

import json
from pathlib import Path

run_dir = Path("<run_dir>")
manifest = json.loads((run_dir / "imagegen-jobs.json").read_text())
left = next(j for j in manifest["jobs"] if j.get("id") == "running-left")
policy = left.get("mirror_policy")
assert isinstance(policy, dict) and policy.get("may_derive_from") == "running-right", \
    "running-left mirror_policy.may_derive_from must be 'running-right'"

Type guard

def mirror_policy_ok(left_job: dict) -> bool:
    policy = left_job.get("mirror_policy")
    return isinstance(policy, dict) and policy.get("may_derive_from") == "running-right"

Prevention

When it happens

Trigger: The running-left job has no mirror_policy key, mirror_policy is not a dict, or mirror_policy["may_derive_from"] is set to a value other than "running-right".

Common situations: A pet run created before mirror_policy was introduced; a manifest hand-edited to strip the policy; an asymmetric pet that was never meant to mirror but someone is trying to shortcut it.

Related errors


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