{
  "schemaVersion": 1,
  "slug": "first-last-frame-file-preflight",
  "locale": "en",
  "evidenceClass": "executed-local-fixture-not-provider-generation",
  "providerModelTest": false,
  "productionTaskRun": false,
  "runCommand": "node run-recipe.mjs --out ./my-run",
  "environment": {
    "nodeVersion": "v24.18.0",
    "ffmpeg": {
      "version": "ffmpeg version 6.0 Copyright (c) 2000-2023 the FFmpeg developers",
      "binarySha256": "a90e3db6a3fd35f6074b013f948b1aa45b31c6375489d39e572bea3f18336584"
    }
  },
  "title": "Preflight first and last frame files before binding a pair",
  "description": "Check two original PNGs for exact dimensions, 16:9 ratio, complete decode and role-specific SHA-256 binding; reject portrait, swapped and missing inputs.",
  "goal": "Prove that the files assigned to first and last are the intended readable images with matching geometry. This does not prove semantic continuity or a provider’s first/last-frame capability.",
  "preparation": [
    "Use Node.js and an installed FFmpeg. Set FFMPEG_BIN to its absolute path if it is not on PATH; the recorded fixture used FFmpeg 6.0.",
    "Inspect binding.json and the original 320×180 first.png/last.png practice images in executed/inputs. A separate 180×320 PNG deliberately violates the pair geometry.",
    "Keep the role name, basename and expected SHA-256 together. Run into a new output directory so an earlier manifest cannot silently refer to replaced input files."
  ],
  "steps": [
    {
      "title": "01. Freeze each role-to-file assignment",
      "body": "The script creates a pair of small original RGB illustrations and writes a binding manifest. First and last have distinct bytes and edge markers. Hashes are captured from these exact files, not inferred from an upload order or a filename containing “first”."
    },
    {
      "title": "02. Validate geometry and decode the image",
      "body": "For each bound file, read the PNG signature and IHDR width/height, then fully decode it through FFmpeg to one RGB frame. Both must be 320×180 and share the explicitly selected 16:9 canvas. Header dimensions alone would not establish that compressed pixels decode."
    },
    {
      "title": "03. Replay the four unsafe assignments",
      "body": "Run the portrait-last, swapped filename, missing last and parent-path cases. Each is expected to fail for its own reason: dimension mismatch, hash binding, missing input or unsafe basename. failure-receipt.json retains the actual exception messages."
    },
    {
      "title": "04. Retain the pair manifest with the files",
      "body": "Check seven passing assertions and the pre/post script hashes in execution-receipt.json. Keep both PNGs and binding.json together when adapting your own upload flow. Review camera, object and endpoint semantics separately; these file checks do not inspect them."
    }
  ],
  "acceptance": [
    "The correct first/last pair produces one completely decoded RGB frame per PNG.",
    "Both roles report exactly 320×180 pixels and the cross-multiplication confirms 16:9.",
    "First and last have different SHA-256 values, matching their bound original files.",
    "The 180×320 replacement is rejected for dimension mismatch.",
    "Swapping the last filename into the first role without changing its expected hash is rejected.",
    "A missing file and a parent-path basename are rejected, with all seven checks recorded."
  ],
  "troubleshooting": [
    {
      "symptom": "The tail image has the right subject but fails geometry.",
      "change": "Change only its actual pixel canvas to the pair’s selected dimensions, re-export, and capture a new hash. Renaming a portrait file cannot change its geometry."
    },
    {
      "symptom": "Two files have been uploaded in the wrong order.",
      "change": "Correct the role-to-basename mapping, then regenerate both expected hashes from the selected files and recheck the manifest."
    },
    {
      "symptom": "A header looks valid but decoding stops.",
      "change": "Replace only the damaged file with a complete local copy and rerun full decode. Do not accept IHDR dimensions as proof of readable pixels."
    }
  ],
  "ending": "This pair manifest is a file-readiness record. It does not demonstrate a generated transition, endpoint compatibility or visual preservation between the two states.",
  "assetCaption": "Original first/last PNG practice illustrations, a portrait negative fixture and a binding diagram. These are local inputs, not model results."
}
