{"record":{"id":"66ebf30a2f214669","repo":"affaan-m/ECC","slug":"missing-receipt","errorCode":null,"errorMessage":"missing receipt","messagePattern":"missing receipt","errorType":"validation","errorClass":"ContractError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/contract.py","lineNumber":460,"sourceCode":"\n\ndef validate_bundle(out_dir: str | Path) -> None:\n    \"\"\"Validate required multimodal files and cross-artifact invariants.\"\"\"\n    out_dir = Path(out_dir)\n    _validate_output_tree(out_dir)\n    specs = [json.loads(path.read_text(encoding=\"utf-8\"))\n             for path in sorted((out_dir / \"genres\").glob(\"*.json\"))]\n    validate_genre_specs(specs)\n\n    validate_manifests(out_dir / \"manifests\")\n    provenance_path = out_dir / \"provenance.json\"\n    if not provenance_path.is_file():\n        raise ContractError(\"missing provenance\")\n    validate_provenance(json.loads(provenance_path.read_text(encoding=\"utf-8\")))\n\n    receipt_path = out_dir / \"receipt.json\"\n    if not receipt_path.is_file():\n        raise ContractError(\"missing receipt\")\n    receipt = json.loads(receipt_path.read_text(encoding=\"utf-8\"))\n    if (receipt.get(\"dry_run\") is not True\n            or receipt.get(\"provider_execution\") is not False\n            or type(receipt.get(\"provider_calls\")) is not int\n            or receipt.get(\"provider_calls\") != 0):\n        raise ContractError(\"receipt crosses the dry-run boundary\")\n    validate_artifact_receipt(out_dir, receipt)\n\n    reference_durations: dict[str, float] = {}\n    for reference in receipt.get(\"references\", []):\n        digest = reference.get(\"sha256\")\n        duration = reference.get(\"source_duration\")\n        if not isinstance(digest, str) or not _is_finite_real(duration):\n            raise ContractError(\"receipt reference cannot bind recipe evidence\")\n        duration = float(duration)\n        previous = reference_durations.get(digest)\n        if previous is not None and previous != duration:\n            raise ContractError(\"receipt reference digest has conflicting source durations\")","sourceCodeStart":442,"sourceCodeEnd":478,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/contract.py#L442-L478","documentation":"validate_bundle requires a receipt.json in every output bundle; this receipt records dry-run status, provider execution, and provider call counts. When the file does not exist, the validator raises this ContractError. The receipt is the audit artifact proving the run stayed offline/dry-run, so its absence invalidates the bundle.","triggerScenarios":"Calling validate_bundle (or _bundle) on an out_dir where receipt.json was never written or was deleted — e.g. interrupted run, wrong directory target, or a generator that omitted receipt emission.","commonSituations":"Copying partial bundles to another machine, running the validator against a stale checkout that predates receipt emission, or accidentally validating a subdirectory instead of the bundle root.","solutions":["Re-run the full pipeline so receipt.json is regenerated into out_dir","Verify validate_bundle receives the bundle root (which must contain receipt.json)","Restore receipt.json from a backup or archive if the bundle was copied incompletely","If implementing a custom generator, always write receipt.json before invoking the validator"],"exampleFix":"# before\nvalidate_bundle(out_dir / \"manifests\")\n# after\nassert (out_dir / \"receipt.json\").is_file()\nvalidate_bundle(out_dir)","handlingStrategy":"validation","validationCode":"from pathlib import Path\ndef ensure_receipt(out_dir: Path) -> None:\n    if not (out_dir / \"receipt.json\").is_file():\n        raise FileNotFoundError(f\"{out_dir} has no receipt.json\")","typeGuard":"def has_receipt(out_dir) -> bool:\n    return (out_dir / \"receipt.json\").is_file()","tryCatchPattern":"from tasteforge.contract import ContractError\ntry:\n    validate_bundle(out_dir)\nexcept ContractError as e:\n    if \"missing receipt\" in str(e):\n        regenerate_bundle(out_dir)","preventionTips":["Include receipt.json in archives/backups of bundles","Regenerate rather than reconstruct receipts manually","Confirm you pass the bundle root, not a subdirectory, to validate_bundle"],"tags":["contract","file-not-found","validation","receipt"],"backgroundTag":"file-not-found","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"}