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
- Visually confirm the pet is actually symmetric enough that mirroring running-right produces a correct left strip.
- If symmetric, add "mirror_policy": {"may_derive_from": "running-right"} to the running-left job in imagegen-jobs.json.
- If asymmetric, abandon mirroring and generate running-left independently via $imagegen + record_imagegen_result.py.
- 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
- Decide mirroring up front when creating the pet run and bake mirror_policy into the manifest template.
- Do not remove mirror_policy when editing the manifest.
- For asymmetric pets, never set may_derive_from; generate running-left via $imagegen instead.
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
- job manifest not found: {path}
- invalid imagegen-jobs.json: jobs must be a list
- unknown job id: {job_id}
- running-right must be complete before deriving running-left
- job {job_id} has no {field}
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/24c2d05ee326bd18.
Report an issue: GitHub.