{"record":{"id":"46cfbbf612804ee8","repo":"affaan-m/ECC","slug":"receipt-evidence-artifacts-must-be-a-list","errorCode":null,"errorMessage":"receipt evidence_artifacts must be a list","messagePattern":"receipt evidence_artifacts must be a list","errorType":"validation","errorClass":"ContractError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/contract.py","lineNumber":312,"sourceCode":"                or payload.get(\"provider_calls\") != 0\n                or payload.get(\"provider_execution\") is not False):\n            raise ContractError(f\"{modality} manifest crosses the dry-run boundary\")\n        for request in payload[\"requests\"]:\n            if (request.get(\"dry_run\") is not True\n                    or request.get(\"submit\") is not False\n                    or type(request.get(\"provider_calls\")) is not int\n                    or request.get(\"provider_calls\") != 0\n                    or request.get(\"provider_execution\") is not False\n                    or request.get(\"provider_call_mode\") != \"disabled\"):\n                raise ContractError(f\"{modality} request crosses the dry-run boundary\")\n\n\ndef validate_artifact_receipt(out_dir: str | Path, receipt: dict[str, Any]) -> None:\n    \"\"\"Verify that the receipt binds every emitted artifact and its provenance.\"\"\"\n    out_dir = Path(out_dir).resolve()\n    entries = receipt.get(\"evidence_artifacts\")\n    if not isinstance(entries, list):\n        raise ContractError(\"receipt evidence_artifacts must be a list\")\n    if not all(isinstance(entry, dict) for entry in entries):\n        raise ContractError(\"receipt evidence_artifacts entries must be objects\")\n    known_sources: set[tuple[str, str]] = set()\n    source_durations: dict[tuple[str, str], float] = {}\n    for key in (\"references\", \"evidence_files\"):\n        sources = receipt.get(key, [])\n        if not isinstance(sources, list):\n            raise ContractError(f\"receipt {key} must be a list\")\n        for source in sources:\n            if not isinstance(source, dict):\n                raise ContractError(f\"receipt {key} contains an invalid source\")\n            source_path = source.get(\"path\")\n            expected_digest = source.get(\"sha256\")\n            if (not isinstance(source_path, str) or not source_path\n                    or not isinstance(expected_digest, str)\n                    or not re.fullmatch(r\"[0-9a-f]{64}\", expected_digest)):\n                raise ContractError(\"receipt has an invalid source identity\")\n            known_sources.add((source_path, expected_digest))","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/contract.py#L294-L330","documentation":"validate_artifact_receipt checks that a receipt's 'evidence_artifacts' key is a list before binding each emitted artifact to its provenance. A missing, None, or non-list value raises this ContractError, since artifact binding cannot proceed.","triggerScenarios":"Passing receipt = {} (no key), {'evidence_artifacts': None}, {'evidence_artifacts': {...}} (dict instead of list), or a string value to validate_artifact_receipt via validate_bundle.","commonSituations":"A receipt writer crashing before populating evidence_artifacts; hand-rolled receipt serialization emitting a dict keyed by artifact path instead of a list; schema drift between receipt producer and validator versions; loading the wrong JSON (a summary file instead of a receipt).","solutions":["Ensure 'evidence_artifacts' is a JSON array of artifact entry objects in the receipt.","Fix the receipt producer so it always emits the list, even when empty (use [] rather than omitting the key).","Check receipt file version/schema matches what this validator expects; regenerate the receipt with the current tool version.","Confirm the file loaded as 'receipt' is actually a Tasteforge receipt document."],"exampleFix":"// before\nreceipt = {\"evidence_artifacts\": {\"out.png\": {\"sha256\": \"...\"}}}\n// after\nreceipt = {\"evidence_artifacts\": [{\"path\": \"out.png\", \"sha256\": \"...\"}]}","handlingStrategy":"type-guard","validationCode":"def receipt_loadable(data):\n    return isinstance(data, dict) and isinstance(data.get(\"evidence_artifacts\"), list)","typeGuard":"def is_receipt(data: object) -> bool:\n    return isinstance(data, dict) and isinstance(data.get(\"evidence_artifacts\"), list)","tryCatchPattern":"try:\n    validate_artifact_receipt(out_dir, receipt)\nexcept ContractError as e:\n    if \"evidence_artifacts must be a list\" in str(e):\n        receipt = rebuild_receipt(out_dir)  # regenerate from emitted artifacts\n        validate_artifact_receipt(out_dir, receipt)\n    else:\n        raise","preventionTips":["Always emit evidence_artifacts as a list, using [] when there are no artifacts — never omit the key.","Validate the receipt against its schema right after writing it.","Version the receipt schema and check the loaded file matches the validator's expected version."],"tags":["receipt","artifacts","type-error"],"backgroundTag":"type-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"}