{"record":{"id":"1b44a75ed3ad1a05","repo":"affaan-m/ECC","slug":"anchor-evidence-source-duration-is-not-bound-to-its-receipt","errorCode":null,"errorMessage":"anchor evidence source duration is not bound to its receipt reference","messagePattern":"anchor evidence source duration is not bound to its receipt reference","errorType":"validation","errorClass":"ContractError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/contract.py","lineNumber":251,"sourceCode":"            required = {\n                \"mode\", \"target\", \"source_ref_sha256\", \"evidence_time\",\n                \"source_duration\", \"lost_policy\",\n            }\n            if not isinstance(anchor, dict) or not required.issubset(anchor):\n                raise ContractError(f\"CV effect {event.get('effect')} lacks a valid subject anchor\")\n            if anchor.get(\"mode\") not in {\"object_track\", \"point_track\", \"segmentation_track\"}:\n                raise ContractError(f\"CV effect {event.get('effect')} has an invalid subject anchor\")\n            if anchor.get(\"lost_policy\") != \"disable_effect_until_track_recovers\":\n                raise ContractError(f\"CV effect {event.get('effect')} must fail closed on anchor loss\")\n            _validate_media_time(\n                anchor.get(\"evidence_time\"), anchor.get(\"source_duration\"),\n                label=\"anchor evidence time\",\n            )\n            if reference_durations is not None:\n                digest = anchor.get(\"source_ref_sha256\")\n                expected_duration = reference_durations.get(digest) if isinstance(digest, str) else None\n                if expected_duration is None or anchor.get(\"source_duration\") != expected_duration:\n                    raise ContractError(\"anchor evidence source duration is not bound to its receipt reference\")\n    for event in events:\n        placement = event.get(\"placement\")\n        if not isinstance(placement, dict) or not {\"safe_area\", \"max_coverage\", \"occlusion_policy\"}.issubset(placement):\n            raise ContractError(f\"effect {event.get('effect')} lacks placement constraints\")\n\n\ndef validate_provenance(payload: dict[str, Any]) -> None:\n    \"\"\"Require every declared rule to cite immutable, timestamped evidence.\"\"\"\n    rules = payload.get(\"rules\")\n    if not isinstance(rules, list) or not rules:\n        raise ContractError(\"provenance must contain derived rules\")\n    for rule in rules:\n        evidence = rule.get(\"evidence\")\n        if not isinstance(evidence, list) or not evidence:\n            raise ContractError(f\"rule {rule.get('rule_id')} lacks reference evidence\")\n        for item in evidence:\n            digest = item.get(\"reference_sha256\")\n            if not isinstance(digest, str) or len(digest) != 64:","sourceCodeStart":233,"sourceCodeEnd":269,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/contract.py#L233-L269","documentation":"ContractError raised when the reference_durations lookup is active and the anchor's source_duration does not match the duration recorded for its source_ref_sha256 digest in the receipt references, or the digest is missing/unresolvable. This binds anchor evidence timing to the immutable receipt for the source media, preventing anchors that claim durations inconsistent with the actual reference.","triggerScenarios":"Calling validate_effect_recipe with reference_durations provided and an anchor whose source_ref_sha256 is absent or not a string, is not present in reference_durations, or whose source_duration differs from reference_durations[digest].","commonSituations":"Hand-editing source_duration after the receipt was generated; pointing the anchor at the wrong reference digest; regenerating source media (new duration) without refreshing the receipt and anchor; truncated digests or casing differences in the sha256 string.","solutions":["Regenerate the anchor's source_ref_sha256 and source_duration from the current receipt so they match reference_durations.","Verify the digest is a full lowercase hex sha256 string present in reference_durations.","Update source media references after any re-encode that changes duration.","Catch ContractError and diff anchor.source_duration against reference_durations.get(digest) to identify the mismatch."],"exampleFix":"// before\nanchor = {..., \"source_ref_sha256\": \"abc123\", \"source_duration\": 12.0}\n# receipt says reference_durations[\"abc123\"] == 10.0\n// after\nanchor = {..., \"source_ref_sha256\": \"abc123\", \"source_duration\": 10.0}  # matches receipt","handlingStrategy":"validation","validationCode":"digest = anchor.get(\"source_ref_sha256\")\nexpected = reference_durations.get(digest) if isinstance(digest, str) else None\nif expected is None or anchor.get(\"source_duration\") != expected:\n    raise ValueError(\"anchor source_duration does not match receipt reference\")","typeGuard":"def anchor_bound_to_reference(anchor, reference_durations) -> bool:\n    digest = anchor.get(\"source_ref_sha256\")\n    if not isinstance(digest, str):\n        return False\n    return reference_durations.get(digest) == anchor.get(\"source_duration\")","tryCatchPattern":"try:\n    validate_effect_recipe(recipe, reference_durations)\nexcept ContractError as exc:\n    if \"not bound to its receipt reference\" in str(exc):\n        refresh_anchor_from_receipt(anchor, receipts)\n        validate_effect_recipe(recipe, reference_durations)\n    else:\n        raise","preventionTips":["Always derive source_ref_sha256 and source_duration from the same receipt","Re-run receipt generation whenever source media changes","Compare durations with exact equality only after the receipt, not with tolerance","Fail fast with a diff of anchor duration vs reference duration in tooling"],"tags":["validation","contract","provenance","checksum"],"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"}