{"record":{"id":"b01a39da6b4fba5b","repo":"affaan-m/ECC","slug":"receipt-binds-missing-artifact-sorted-extra","errorCode":null,"errorMessage":"receipt binds missing artifact: {sorted(extra)}","messagePattern":"receipt binds missing artifact: (.+?)","errorType":"validation","errorClass":"ContractError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/contract.py","lineNumber":384,"sourceCode":"    emitted = {\n        path.relative_to(out_dir).as_posix()\n        for path in out_dir.rglob(\"*\")\n        if path.is_file() and path.name != \"receipt.json\"\n    }\n    bound_paths: list[str] = []\n    for entry in entries:\n        relative = entry.get(\"path\")\n        if not isinstance(relative, str) or not relative:\n            raise ContractError(\"artifact path must be a non-empty relative path\")\n        bound_paths.append(relative)\n    if len(bound_paths) != len(set(bound_paths)):\n        raise ContractError(\"receipt contains duplicate artifact paths\")\n    missing = emitted - set(bound_paths)\n    extra = set(bound_paths) - emitted\n    if missing:\n        raise ContractError(f\"unbound emitted artifact: {sorted(missing)}\")\n    if extra:\n        raise ContractError(f\"receipt binds missing artifact: {sorted(extra)}\")\n\n    for entry in entries:\n        relative = entry.get(\"path\")\n        assert isinstance(relative, str)\n        path = (out_dir / relative).resolve()\n        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:","sourceCodeStart":366,"sourceCodeEnd":402,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/contract.py#L366-L402","documentation":"This is the inverse of the unbound-artifact check: every path listed in receipt.json must exist as an actual file under the output directory. This ContractError is raised when the receipt references a path that is not present among the emitted files — the receipt claims an artifact that does not exist.","triggerScenarios":"Calling validate_artifact_receipt()/validate_bundle() after an artifact file was deleted or renamed post-generation, the receipt was hand-edited to add a path, or generation silently skipped an output the receipt still lists.","commonSituations":"A cleanup script removed output files but kept the receipt; a file was renamed without regenerating; partial transfer of the bundle (file lost in copy); receipt from a different/older run mixed with a new output directory.","solutions":["Regenerate the bundle and receipt together so the receipt only binds files that exist.","Restore the missing file from version control or backup.","Remove the stale entry from receipt.json if the artifact was intentionally dropped, then re-validate."],"exampleFix":"// before (receipt lists missing file)\n{\"artifacts\": [{\"path\": \"gone.png\", ...}]}\n// after (regenerate receipt after deletion)\ntasteforge generate ./bundle  # receipt now omits gone.png","handlingStrategy":"validation","validationCode":"bound = {e['path'] for e in receipt['artifacts']}\nmissing = bound - emitted\nif missing:\n    raise SystemExit(f'missing artifact files: {sorted(missing)}')","typeGuard":"def all_artifacts_exist(out_dir: Path, entries: list[dict]) -> bool:\n    return all((out_dir / e['path']).is_file() for e in entries)","tryCatchPattern":"try:\n    validate_artifact_receipt(out_dir, entries)\nexcept ContractError as e:\n    if 'binds missing artifact' in str(e):\n        restore_or_regenerate(out_dir)  # git restore files or rerun generation\n    else:\n        raise","preventionTips":["Regenerate the receipt whenever artifacts are added, removed, or renamed.","Transfer bundles as a single archive so files cannot be dropped in transit.","Never mix a receipt from one run with outputs from another."],"tags":["integrity","receipt","validation"],"backgroundTag":"resource-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"}