{"record":{"id":"b29e008e5ee191ea","repo":"affaan-m/ECC","slug":"effect-evidence-source-duration-is-not-bound-to-its-receipt","errorCode":null,"errorMessage":"effect evidence source duration is not bound to its receipt reference","messagePattern":"effect 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":215,"sourceCode":"        duration = event.get(\"duration\")\n        if (not _is_finite_real(start) or not _is_finite_real(duration)\n                or float(start) < 0 or float(duration) <= 0):\n            raise ContractError(\"effect event start and duration must be finite positive timeline values\")\n        start = cast(float, start)\n        duration = cast(float, duration)\n        if float(start) + float(duration) > float(timeline) + 1e-9:\n            raise ContractError(\"effect event end exceeds the declared timeline\")\n        evidence = event.get(\"evidence\")\n        if not isinstance(evidence, dict):\n            raise ContractError(f\"effect {event.get('effect')} lacks reference evidence\")\n        _validate_media_time(\n            evidence.get(\"time\"), evidence.get(\"source_duration\"), label=\"effect evidence time\"\n        )\n        if reference_durations is not None:\n            digest = evidence.get(\"reference_sha256\")\n            expected_duration = reference_durations.get(digest) if isinstance(digest, str) else None\n            if expected_duration is None or evidence.get(\"source_duration\") != expected_duration:\n                raise ContractError(\"effect evidence source duration is not bound to its receipt reference\")\n    times = [float(event[\"time\"]) for event in events]\n    if times != sorted(times) or len(times) != len(set(times)):\n        raise ContractError(\"effect event times must be unique and increasing\")\n    intervals = [round(b - a, 6) for a, b in zip(times, times[1:])]  # noqa: RUF007\n    if len(set(intervals)) <= 1:\n        raise ContractError(\"stochastic schedule is periodic; intervals must vary\")\n    for period in range(1, len(intervals) // 2 + 1):\n        if all(intervals[index] == intervals[index % period] for index in range(len(intervals))):\n            raise ContractError(\"stochastic schedule is periodic; repeating interval cycle\")\n    if recipe.get(\"periodic\") is not False:\n        raise ContractError(\"effect recipe must explicitly declare periodic=false\")\n    for event in events:\n        cv_effect = str(event.get(\"effect\", \"\")).startswith(\"cv_\")\n        if cv_effect and event.get(\"requires_subject_anchor\") is not True:\n            raise ContractError(f\"CV effect {event.get('effect')} must require a subject anchor\")\n        if event.get(\"requires_subject_anchor\"):\n            anchor = event.get(\"subject_anchor\")\n            required = {","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/contract.py#L197-L233","documentation":"When reference_durations is supplied, each event's evidence must be bound to a known reference: evidence['reference_sha256'] must exist in the reference_durations map, and evidence['source_duration'] must exactly equal the duration recorded for that digest. This proves the effect was applied to the exact source media named by its receipt.","triggerScenarios":"validate_effect_recipe called with reference_durations where an event's evidence has a reference_sha256 absent from the map, a missing/non-string digest, or a source_duration that differs (even slightly) from the recorded value.","commonSituations":"Stale evidence pointing at re-encoded media whose duration changed; digest computed over the wrong file; copying evidence between events referencing different sources; float precision changes from JSON round-trips of source_duration.","solutions":["Recompute evidence from the correct reference media so reference_sha256 and source_duration match the receipt map.","Update reference_durations (or pick the right receipt) so the digest is present and its duration matches.","Compare durations with exact equality against the receipt; avoid reformatting source_duration (e.g. 10 vs 10.0 keys are fine, but '10.0' string is not)."],"exampleFix":"# before\nevidence = {\"reference_sha256\": \"deadbeef\", \"source_duration\": 12.0}  # map says 10.0\n# after\nevidence = {\"reference_sha256\": digest_of_actual_source, \"source_duration\": reference_durations[digest_of_actual_source]}","handlingStrategy":"validation","validationCode":"digest = ev[\"evidence\"].get(\"reference_sha256\")\nexpected = reference_durations.get(digest) if isinstance(digest, str) else None\nif expected is None or ev[\"evidence\"].get(\"source_duration\") != expected:\n    raise ValueError(f\"evidence not bound to receipt for digest {digest}\")","typeGuard":"def evidence_bound(ev: dict, reference_durations: dict) -> bool:\n    digest = ev[\"evidence\"].get(\"reference_sha256\")\n    expected = reference_durations.get(digest) if isinstance(digest, str) else None\n    return expected is not None and ev[\"evidence\"].get(\"source_duration\") == expected","tryCatchPattern":"try:\n    validate_effect_recipe(recipe, reference_durations=reference_durations)\nexcept ContractError as exc:\n    if \"not bound to its receipt reference\" in str(exc):\n        recipe = rebind_evidence_to_receipts(recipe, reference_durations)\n        validate_effect_recipe(recipe, reference_durations=reference_durations)\n    else:\n        raise","preventionTips":["Recompute evidence digests whenever reference media is re-encoded.","Copy evidence only with its originating digest, never between events.","Keep source_duration as a float matching the receipt value exactly (no string formatting)."],"tags":["validation","evidence","integrity"],"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"}