affaan-m/ECC · error · ContractError
artifact whole-file provenance must not invent times
Error message
artifact {relative} whole-file provenance must not invent times What it means
A whole_file provenance source means the artifact derives from the entire reference, so no per-timestamp references may be invented. This error is raised when time_basis is 'whole_file' but reference_times is a non-empty list. It prevents receipts from claiming both full-file coverage and specific time ranges.
Solutions
- Set reference_times to [] for whole_file provenance
- If specific times are actually needed, change time_basis to 'media_seconds' and keep the times list
- Regenerate the receipt via tasteforge so basis and times stay consistent
- Remove auto-injected default timestamps from receipt post-processing scripts
Example fix
# before
{'time_basis': 'whole_file', 'reference_times': [0.0, 45.0]}
# after
{'time_basis': 'whole_file', 'reference_times': []} Defensive patterns
Strategy: validation
Validate before calling
if src.get('time_basis') == 'whole_file':
assert src.get('reference_times') == [], 'whole_file provenance must have empty reference_times' Type guard
def whole_file_times_clean(src: dict) -> bool:
return src.get('time_basis') != 'whole_file' or not src.get('reference_times') Try / catch
try:
validate_artifact_receipt(out_dir)
except ContractError as e:
if 'must not invent times' in str(e):
clear_times_for_whole_file_sources(receipt)
else:
raise Prevention
- Clear reference_times when switching a source to whole_file
- Strip default timestamps in receipt post-processing
- Use constructors that derive times emptiness from the basis automatically
- Review basis changes in receipt diffs
When it happens
Trigger: Setting time_basis='whole_file' while leaving reference_times populated from a previous media_seconds configuration, or generators that always emit default timestamps regardless of basis.
Common situations: Switching a receipt entry's basis without clearing times, copy-pasted template provenance, tools that auto-annotate times for all sources.
Related errors
- artifact has invalid reference/time provenance
- a generated candidate cannot claim original-source identity
- anchor evidence source duration is not bound to its receipt…
- artifact cites an unknown provenance source
- artifact has an unbound source duration
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/a14a9a7057aa396f.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/contract.py:424
provenance = entry.get("provenance")
if not isinstance(provenance, list) or not provenance:
raise ContractError(f"artifact {relative} lacks exact reference/time provenance")
for source in provenance:
if not isinstance(source.get("reference_path"), str) or not source["reference_path"]:
raise ContractError(f"artifact {relative} has invalid reference path")
digest = source.get("reference_sha256")
if not isinstance(digest, str) or len(digest) != 64:
raise ContractError(f"artifact {relative} has invalid reference SHA-256")
if (source["reference_path"], digest) not in known_sources:
raise ContractError(f"artifact {relative} cites an unknown provenance source")
times = source.get("reference_times")
basis = source.get("time_basis")
if not isinstance(times, list) or basis not in {"media_seconds", "whole_file"}:
raise ContractError(f"artifact {relative} has invalid reference/time provenance")
if basis == "media_seconds" and not times:
raise ContractError(f"artifact {relative} lacks media reference times")
if basis == "whole_file" and times:
raise ContractError(f"artifact {relative} whole-file provenance must not invent times")
if basis == "media_seconds":
expected_duration = source_durations.get((source["reference_path"], digest))
if expected_duration is None or source.get("source_duration") != expected_duration:
raise ContractError(f"artifact {relative} has an unbound source duration")
for time in times:
_validate_media_time(
time, expected_duration,
label=f"artifact {relative} media reference time",
)
digest_payload = dict(receipt)
claimed_digest = digest_payload.pop("receipt_sha256", None)
actual_digest = hashlib.sha256(
json.dumps(digest_payload, sort_keys=True, separators=(",", ":")).encode("utf-8")
).hexdigest()
if claimed_digest != actual_digest:
raise ContractError("receipt SHA-256 does not match its canonical content")
View on GitHub (pinned to 8321021c54)