{"record":{"id":"4d94f127ad1e38f6","repo":"affaan-m/ECC","slug":"receipt-reference-digest-has-conflicting-source-durations","errorCode":null,"errorMessage":"receipt reference digest has conflicting source durations","messagePattern":"receipt reference digest has conflicting source durations","errorType":"validation","errorClass":"ContractError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/contract.py","lineNumber":478,"sourceCode":"        raise ContractError(\"missing receipt\")\n    receipt = json.loads(receipt_path.read_text(encoding=\"utf-8\"))\n    if (receipt.get(\"dry_run\") is not True\n            or receipt.get(\"provider_execution\") is not False\n            or type(receipt.get(\"provider_calls\")) is not int\n            or receipt.get(\"provider_calls\") != 0):\n        raise ContractError(\"receipt crosses the dry-run boundary\")\n    validate_artifact_receipt(out_dir, receipt)\n\n    reference_durations: dict[str, float] = {}\n    for reference in receipt.get(\"references\", []):\n        digest = reference.get(\"sha256\")\n        duration = reference.get(\"source_duration\")\n        if not isinstance(digest, str) or not _is_finite_real(duration):\n            raise ContractError(\"receipt reference cannot bind recipe evidence\")\n        duration = float(duration)\n        previous = reference_durations.get(digest)\n        if previous is not None and previous != duration:\n            raise ContractError(\"receipt reference digest has conflicting source durations\")\n        reference_durations[digest] = duration\n\n    recipe_path = out_dir / \"resolve\" / \"effect_recipe.json\"\n    if not recipe_path.is_file():\n        raise ContractError(\"missing Resolve effect recipe\")\n    validate_effect_recipe(\n        json.loads(recipe_path.read_text(encoding=\"utf-8\")),\n        reference_durations=reference_durations,\n    )\n","sourceCodeStart":460,"sourceCodeEnd":488,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/contract.py#L460-L488","documentation":"When two receipt references cite the same artifact digest (same sha256) but declare different source_duration values, the receipt is self-contradictory: the same file cannot have two durations. validate_bundle builds a digest→duration map and raises this ContractError on the first conflict, protecting downstream recipe validation that relies on consistent durations.","triggerScenarios":"A receipt whose references list contains the same sha256 twice with differing source_duration floats — e.g. merging receipts from two runs, editing one entry's duration, or a generator emitting duplicate references with stale durations.","commonSituations":"Manually merging or patching receipts, copy-paste edits to fix durations for one reference but not its duplicates, or custom tooling that re-derives durations per-clip instead of per-source-file.","solutions":["Regenerate the receipt with the standard pipeline so duplicates share one consistent duration","Make all references with the same sha256 carry the identical source_duration value","Remove duplicate/stale reference entries for the same digest","Restore the receipt from a single coherent run rather than merging receipts"],"exampleFix":"// before: same digest, conflicting durations\n[{\"sha256\": \"abc\", \"source_duration\": 12.5}, {\"sha256\": \"abc\", \"source_duration\": 13.0}]\n// after\n[{\"sha256\": \"abc\", \"source_duration\": 12.5}, {\"sha256\": \"abc\", \"source_duration\": 12.5}]","handlingStrategy":"validation","validationCode":"import json\ndef durations_consistent(receipt: dict) -> bool:\n    seen = {}\n    for ref in receipt.get(\"references\", []):\n        digest, dur = ref.get(\"sha256\"), ref.get(\"source_duration\")\n        if digest in seen and seen[digest] != dur:\n            return False\n        seen[digest] = dur\n    return True","typeGuard":"def no_conflicting_durations(references: list) -> bool:\n    seen: dict = {}\n    for ref in references:\n        d = ref.get(\"sha256\"); dur = ref.get(\"source_duration\")\n        if d in seen and seen[d] != dur:\n            return False\n        seen[d] = dur\n    return True","tryCatchPattern":"from tasteforge.contract import ContractError\ntry:\n    validate_bundle(out_dir)\nexcept ContractError as e:\n    if \"conflicting source durations\" in str(e):\n        regenerate_bundle(out_dir)  # merged/edited receipts are unusable","preventionTips":["Never merge receipts from separate runs","Update all duplicate entries for a digest together if edits are unavoidable","Derive duration per source file once and reuse it for all its references"],"tags":["contract","receipt","consistency","integrity"],"backgroundTag":"internal-invariant-violation","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"}