{"record":{"id":"c2fb74ac5bace063","repo":"affaan-m/ECC","slug":"artifact-relative-lacks-media-reference-times","errorCode":null,"errorMessage":"artifact {relative} lacks media reference times","messagePattern":"artifact (.+?) lacks media reference times","errorType":"validation","errorClass":"ContractError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/contract.py","lineNumber":422,"sourceCode":"        if entry.get(\"sha256\") != _sha256(path):\n            raise ContractError(f\"artifact {relative} SHA-256 does not match receipt\")\n        provenance = entry.get(\"provenance\")\n        if not isinstance(provenance, list) or not provenance:\n            raise ContractError(f\"artifact {relative} lacks exact reference/time provenance\")\n        for source in provenance:\n            if not isinstance(source.get(\"reference_path\"), str) or not source[\"reference_path\"]:\n                raise ContractError(f\"artifact {relative} has invalid reference path\")\n            digest = source.get(\"reference_sha256\")\n            if not isinstance(digest, str) or len(digest) != 64:\n                raise ContractError(f\"artifact {relative} has invalid reference SHA-256\")\n            if (source[\"reference_path\"], digest) not in known_sources:\n                raise ContractError(f\"artifact {relative} cites an unknown provenance source\")\n            times = source.get(\"reference_times\")\n            basis = source.get(\"time_basis\")\n            if not isinstance(times, list) or basis not in {\"media_seconds\", \"whole_file\"}:\n                raise ContractError(f\"artifact {relative} has invalid reference/time provenance\")\n            if basis == \"media_seconds\" and not times:\n                raise ContractError(f\"artifact {relative} lacks media reference times\")\n            if basis == \"whole_file\" and times:\n                raise ContractError(f\"artifact {relative} whole-file provenance must not invent times\")\n            if basis == \"media_seconds\":\n                expected_duration = source_durations.get((source[\"reference_path\"], digest))\n                if expected_duration is None or source.get(\"source_duration\") != expected_duration:\n                    raise ContractError(f\"artifact {relative} has an unbound source duration\")\n                for time in times:\n                    _validate_media_time(\n                        time, expected_duration,\n                        label=f\"artifact {relative} media reference time\",\n                    )\n\n    digest_payload = dict(receipt)\n    claimed_digest = digest_payload.pop(\"receipt_sha256\", None)\n    actual_digest = hashlib.sha256(\n        json.dumps(digest_payload, sort_keys=True, separators=(\",\", \":\")).encode(\"utf-8\")\n    ).hexdigest()\n    if claimed_digest != actual_digest:","sourceCodeStart":404,"sourceCodeEnd":440,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/contract.py#L404-L440","documentation":"When a provenance source declares time_basis 'media_seconds', its reference_times list must be non-empty — citing specific media timestamps is the whole point of that basis. This error is raised when the times list is empty or missing for a media_seconds source.","triggerScenarios":"A source with time_basis='media_seconds' and reference_times=[] (or the key omitted); switching a whole_file source to media_seconds without adding timestamps.","commonSituations":"Template receipts copied from a whole_file example, generators that fail to capture timestamps for time-ranged edits, developers switching basis values without updating the times list.","solutions":["Populate reference_times with the actual media timestamps (in seconds) the artifact derives from","If the artifact uses the entire reference, change time_basis to 'whole_file' and keep times empty instead","Regenerate the artifact through the pipeline so times are captured automatically","Validate each timestamp is within the source duration to also avoid downstream media-time errors"],"exampleFix":"# before\n{'time_basis': 'media_seconds', 'reference_times': []}\n# after\n{'time_basis': 'media_seconds', 'reference_times': [12.5, 30.0]}","handlingStrategy":"validation","validationCode":"if src.get('time_basis') == 'media_seconds':\n    times = src.get('reference_times')\n    assert isinstance(times, list) and len(times) > 0, 'media_seconds requires non-empty reference_times'","typeGuard":"def has_media_times(src: dict) -> bool:\n    return src.get('time_basis') != 'media_seconds' or bool(src.get('reference_times'))","tryCatchPattern":"try:\n    validate_artifact_receipt(out_dir)\nexcept ContractError as e:\n    if 'lacks media reference times' in str(e):\n        capture_timestamps_or_switch_to_whole_file(src)\n    else:\n        raise","preventionTips":["Capture timestamps at generation time, not during hand-authoring","Default to whole_file when no time range is meaningful","Validate times are within the source duration before receipt signing","Never copy whole_file templates into media_seconds entries without adding times"],"tags":["provenance","media-times","validation"],"backgroundTag":"empty-required-field","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"}