{"record":{"id":"c2c0fb59997477bb","repo":"affaan-m/ECC","slug":"historical-evidence-does-not-bind-the-candidate-source-input","errorCode":null,"errorMessage":"historical evidence does not bind the candidate source/input/bytes","messagePattern":"historical evidence does not bind the candidate source/input/bytes","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/integration.py","lineNumber":305,"sourceCode":"        if name in result:\n            raise ValueError(\"candidate ids must be unique\")\n        _artifact(item[\"media\"])\n        _integer(item[\"media_frames\"], 1)\n        _rate(item[\"fps\"])\n        if item[\"origin\"] != \"provider_generated\" or item[\"relationship\"] != \"generated_variation\":\n            raise ValueError(\"a generated candidate cannot claim original-source identity\")\n        if item[\"source_sha256\"] != source_hash or item[\"compiled_input_sha256\"] != input_hash:\n            raise ValueError(\"candidate is bound to a different source or input\")\n        if item[\"review_status\"] not in (\"pending\", \"rejected\", \"approved\"):\n            raise ValueError(\"explicit candidate review state required\")\n        evidence = _artifact(item[\"generation_receipt\"], parse_json=True)\n        if not isinstance(evidence, dict) or not isinstance(evidence.get(\"request_id\"), str):\n            raise ValueError(\"historical request evidence required\")\n        _text(evidence[\"request_id\"])\n        expected = {\"source_sha256\": source_hash, \"compiled_input_sha256\": input_hash,\n                    \"candidate_sha256\": item[\"media\"][\"sha256\"], \"source_url\": source_url}\n        if any(evidence.get(key) != value for key, value in expected.items()):\n            raise ValueError(\"historical evidence does not bind the candidate source/input/bytes\")\n        result[name] = item\n    return result\n\n\ndef _inserts(items: list, candidates: dict, config: dict, input_hash: str, edit_hash: str) -> None:\n    occupied = []\n    for item in items:\n        _object(item, {\"candidate_id\", \"candidate_range\", \"timeline_range\", \"retime\", \"approval_file\"})\n        candidate = candidates.get(_text(item[\"candidate_id\"]))\n        if candidate is None or candidate[\"review_status\"] != \"approved\":\n            raise ValueError(\"insert requires an approved, resolved candidate\")\n        target = _range(item[\"timeline_range\"], config[\"baseline\"][\"timeline_range\"])\n        source = _range(item[\"candidate_range\"], [0, candidate[\"media_frames\"]])\n        if any(_overlap(target, p[\"range\"]) for p in config[\"protected_intervals\"]):\n            raise ValueError(\"insert overlaps protected original stack\")\n        if any(_overlap(target, span) for span in occupied):\n            raise ValueError(\"insert proposals overlap\")\n        if (item[\"retime\"] != \"none\" or target[1] - target[0] != source[1] - source[0]","sourceCodeStart":287,"sourceCodeEnd":323,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/integration.py#L287-L323","documentation":"The generation_receipt is not just present — it must bind this exact candidate: source_sha256, compiled_input_sha256, candidate_sha256 (of the media bytes), and source_url in the receipt must match the values TasteForge computes from the bundle context and the candidate's media. Any mismatch means the receipt belongs to a different request/asset, so the candidate's provenance chain is broken.","triggerScenarios":"Swapping the candidate's media bytes after generation without updating the receipt (candidate_sha256 mismatch); reusing a receipt from another candidate; editing the input or source after generation so the receipt's recorded hashes no longer match the current bundle hashes; a wrong source_url recorded.","commonSituations":"Manual media replacement (re-encode, trim, color-grade) post-generation; receipt/candidate files mixed up when moving between machines; parallel generation runs whose receipts got cross-assigned.","solutions":["Regenerate the candidate (or re-issue its receipt) so the receipt's source_sha256, compiled_input_sha256, candidate_sha256, and source_url all match the current values","Do not modify candidate media after generation; if post-processing is required, regenerate and obtain a new receipt","Match receipts to candidates programmatically by candidate_sha256 instead of by file order or name"],"exampleFix":"// before\nreceipt.candidate_sha256 = \"aaa111\"  # receipt from another candidate\n// after\nreceipt.candidate_sha256 = candidate.media.sha256  # e.g. \"bbb222\"","handlingStrategy":"validation","validationCode":"function assertReceiptBinding(c, ctx) {\n  const expected = {\n    source_sha256: ctx.sourceHash,\n    compiled_input_sha256: ctx.inputHash,\n    candidate_sha256: c.media.sha256,\n    source_url: ctx.sourceUrl,\n  };\n  for (const [k, v] of Object.entries(expected))\n    if (c.generation_receipt?.[k] !== v)\n      throw new Error(`receipt field ${k} does not bind candidate ${c.id}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  buildApplicationBundle(cfg);\n} catch (e) {\n  if (e.message.includes(\"does not bind the candidate\")) {\n    cfg.candidates = cfg.candidates.filter(c => receiptBinds(c, cfg)); // drop misbound entries\n    return buildApplicationBundle(cfg);\n  }\n  throw e;\n}","preventionTips":["Never mutate media bytes after generation; if you must, regenerate and get a new receipt","Match receipts to candidates by candidate_sha256, not by filename or order","Verify the full binding (source, input, bytes, url) as soon as receipts are loaded"],"tags":["integrity","audit","hash-mismatch"],"backgroundTag":"checksum-mismatch","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}