{"record":{"id":"36329fb7fec046b8","repo":"affaan-m/ECC","slug":"artifact-relative-lacks-exact-reference-time-provenance","errorCode":null,"errorMessage":"artifact {relative} lacks exact reference/time provenance","messagePattern":"artifact (.+?) lacks exact reference/time provenance","errorType":"validation","errorClass":"ContractError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/contract.py","lineNumber":408,"sourceCode":"        try:\n            path.relative_to(out_dir)\n        except ValueError as error:\n            raise ContractError(f\"artifact path escapes output directory: {relative}\") from error\n        if entry.get(\"provider_execution\") is not False:\n            raise ContractError(f\"artifact {relative} permits provider execution\")\n        if not isinstance(entry.get(\"genre_numbers\"), list):\n            raise ContractError(f\"artifact {relative} lacks genre binding\")\n        modalities = entry.get(\"modalities\")\n        if (not isinstance(modalities, list)\n                or any(modality not in _REQUIRED_MODALITIES for modality in modalities)):\n            raise ContractError(f\"artifact {relative} has invalid modality binding\")\n        if entry.get(\"bytes\") != path.stat().st_size:\n            raise ContractError(f\"artifact {relative} byte size does not match receipt\")\n        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))","sourceCodeStart":390,"sourceCodeEnd":426,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/contract.py#L390-L426","documentation":"validate_artifact_receipt requires every artifact receipt entry to carry a non-empty provenance list describing the exact source references and times the artifact was derived from. This error is raised when the 'provenance' key is absent, not a list, or an empty list. The library refuses to bless artifacts with no traceable origin.","triggerScenarios":"Validating a bundle whose receipt entry lacks 'provenance', has provenance set to null/{} / string, or has provenance: []. Also hit when hand-written receipts omit provenance entirely for an artifact that in fact derives from a reference.","commonSituations":"Manually authoring a receipt and forgetting provenance; a generation step that skips reference capture; older receipts from before provenance became required; deleting provenance to silence an unknown-source error without replacing it with valid sources.","solutions":["Add a provenance list to the receipt entry with at least one object containing reference_path, reference_sha256, reference_times, and time_basis","Regenerate the artifact through tasteforge so provenance is captured automatically","If the artifact truly has no source, generate it as an original (unbound) rather than a reference-derived artifact, per the library's rules","Cross-check the provenance against known_sources assembled by validate_bundle"],"exampleFix":"# before\nreceipt['artifacts']['clip.mp4'] = {'bytes': 1024, 'sha256': '...', 'modalities': ['video']}\n# after\nreceipt['artifacts']['clip.mp4'] = {\n    'bytes': 1024, 'sha256': '...', 'modalities': ['video'],\n    'provenance': [{'reference_path': 'refs/intro.mp4',\n                    'reference_sha256': 'a'*64,\n                    'time_basis': 'whole_file', 'reference_times': []}]\n}","handlingStrategy":"validation","validationCode":"p = receipt_entry.get('provenance')\nassert isinstance(p, list) and len(p) > 0, 'receipt entry requires non-empty provenance list'","typeGuard":"def has_provenance(entry: dict) -> bool:\n    p = entry.get('provenance')\n    return isinstance(p, list) and len(p) > 0 and all(isinstance(s, dict) for s in p)","tryCatchPattern":"try:\n    validate_artifact_receipt(out_dir)\nexcept ContractError as e:\n    if 'lacks exact reference/time provenance' in str(e):\n        rebuild_provenance_for_entry(entry_name)\n    else:\n        raise","preventionTips":["Generate receipts via the pipeline, never by hand","Include a provenance template in receipt authoring tooling","Run receipt schema linting before validate_bundle","Keep provenance fields in one place to survive schema migrations"],"tags":["provenance","validation","receipt"],"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"}