{"record":{"id":"a53b09cb00df2e1b","repo":"affaan-m/ECC","slug":"local-ingestion-must-have-zero-provider-calls","errorCode":null,"errorMessage":"Local ingestion must have zero provider calls","messagePattern":"Local ingestion must have zero provider calls","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/assets.py","lineNumber":225,"sourceCode":"        receipt['genre_spec'] = _fingerprint(_path(config['genre_spec'], base))\n    # All inputs already exist, so the output-exists gate also prevents collisions.\n    try:\n        with output.open('x', encoding='utf-8') as stream:\n            json.dump(receipt, stream, indent=2, sort_keys=True)\n            stream.write('\\n')\n    except OSError as exc:\n        raise ValueError(f'Cannot create exclusive receipt: {output}') from exc\n    return receipt\n\n\ndef validate_assets(receipt_path: str | Path) -> dict[str, Any]:\n    \"\"\"Re-hash every bound file and validate receipt semantics; no remote calls.\"\"\"\n    path = _path(receipt_path, Path.cwd())\n    receipt = _load(path)\n    if receipt.get('schema') != SCHEMA:\n        raise ValueError('Unsupported asset receipt schema')\n    if type(receipt.get('provider_calls')) is not int or receipt['provider_calls'] != 0:\n        raise ValueError('Local ingestion must have zero provider calls')\n    if receipt.get('provider_execution') is not False:\n        raise ValueError('Local ingestion cannot claim provider execution')\n    _, requests = (_bundle(receipt['bundle_receipt'], path.parent, True)\n                   if 'bundle_receipt' in receipt else (None, {}))\n    for asset in _assets(receipt.get('assets')):\n        _taste(asset, requests, True)\n        _verify_binding(asset, path.parent, asset['modality'])\n        provenance = _provenance(asset, path.parent, True)\n        if provenance is not None and provenance != asset['provider_provenance']:\n            raise ValueError('Invalid supplied provenance declaration')\n    inputs = receipt.get('input_artifacts')\n    if not isinstance(inputs, list):\n        raise ValueError('input_artifacts must be a list')\n    for artifact in inputs:\n        _verify_binding(artifact, path.parent)\n    if 'genre_spec' in receipt:\n        _verify_binding(receipt['genre_spec'], path.parent)\n    return receipt","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/assets.py#L207-L243","documentation":"validate_assets enforces that a receipt declares provider_calls as an integer equal to 0. This library only performs local hashing/binding; any claim of provider calls invalidates the receipt's local-only guarantee. Non-integer or nonzero values are rejected, including booleans (type check excludes bool via type(...) is not int).","triggerScenarios":"Calling validate_assets on a hand-edited receipt where provider_calls was changed or removed; a receipt assembled by another tool that records provider usage; provider_calls set to true/1.0/\"0\" or other non-int types.","commonSituations":"Merging receipts from a provider-executing pipeline with local ones; manual edits to make a receipt 'look complete'; custom tooling writing its own receipts that include provider calls.","solutions":["Use receipts produced by ingest_assets, which always sets provider_calls: 0.","Remove any provider-executing steps from the ingestion flow; this module forbids provider execution by design.","If a receipt legitimately used providers, it cannot be validated with validate_assets — keep it in a separate workflow.","Never hand-edit provider_calls; re-ingest to get a valid receipt."],"exampleFix":"# before (hand-edited receipt)\n{\"schema\": \"tasteforge.assets.v1\", \"provider_calls\": 2, ...}\n# after: regenerate via ingest_assets; it writes provider_calls: 0\nreceipt = ingest_assets('config.json', 'receipt.json')","handlingStrategy":"validation","validationCode":"r = json.load(open(p))\npc = r.get('provider_calls')\nassert type(pc) is int and pc == 0, 'receipt must record zero provider calls'","typeGuard":"def zero_provider_calls(obj) -> bool:\n    pc = obj.get('provider_calls')\n    return type(pc) is int and pc == 0","tryCatchPattern":null,"preventionTips":["Never merge provider-executing receipts into this local-only flow.","Treat receipts as immutable; regenerate instead of editing.","Note the type check rejects bool: keep the field a true JSON integer 0."],"tags":["validation","receipt","provider-policy","integrity"],"backgroundTag":"schema-validation-failed","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"}