{"record":{"id":"d0796bef9645b5ad","repo":"affaan-m/ECC","slug":"taste-claims-require-a-validated-bundle","errorCode":null,"errorMessage":"Taste claims require a validated bundle","messagePattern":"Taste claims require a validated bundle","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/assets.py","lineNumber":165,"sourceCode":"        binding = _fingerprint(root / 'receipt.json')\n    validate_bundle(root)\n    requests = {}\n    for modality in sorted(MODALITIES):\n        manifest = _load(root / 'manifests' / f'{modality}.json')\n        for request in manifest['requests']:\n            key = (modality, _text(request.get('request_id'), 'request_id'))\n            if key in requests:\n                raise ValueError('Ambiguous duplicate bundle request')\n            requests[key] = request\n    if binding != _fingerprint(root / 'receipt.json'):\n        raise ValueError('Bundle changed during validation')\n    return binding, requests\n\n\ndef _taste(asset: dict[str, Any], requests: dict[Any, Any], verify: bool) -> dict[str, Any]:\n    if not requests:\n        if any(key in asset for key in (*_TASTE_FIELDS, 'request_id')):\n            raise ValueError('Taste claims require a validated bundle')\n        return {}\n    request_id = _text(asset.get('request_id'), 'bundle request_id')\n    request = requests.get((asset['modality'], request_id))\n    if request is None:\n        raise ValueError('Asset request_id/modality does not match the bundle')\n    result = dict(request_id=request_id, **{key: request[key] for key in _TASTE_FIELDS})\n    if verify:\n        if any(asset.get(key) != value for key, value in result.items()):\n            raise ValueError('Asset taste lineage differs from its bundle request')\n    elif any(key in asset for key in _TASTE_FIELDS):\n        raise ValueError('Taste fields are derived from the bundle, not supplied')\n    return result\n\n\ndef ingest_assets(config_path: str | Path, out_receipt: str | Path) -> dict[str, Any]:\n    \"\"\"Bind local assets/provenance/lineage; write a new receipt, never overwrite.\"\"\"\n    config_file = _path(config_path, Path.cwd())\n    config = _load(config_file)","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/assets.py#L147-L183","documentation":"Taste metadata fields (genre_number, genre_slug, style_fingerprint, reference_sha256) and request_id are only valid when the asset resolves against a loaded, validated bundle. When no bundle requests exist, the library throws this if the asset carries any of those keys, preventing unverifiable taste claims.","triggerScenarios":"Calling ingest_assets/validate_assets without a bundle directory (no manifests/receipt), while an asset contains request_id or any _TASTE_FIELDS key.","commonSituations":"Validating loose assets outside a bundle but keeping taste fields from an earlier export; forgetting to point the tool at the bundle root; assets exported before the bundle was generated.","solutions":["Validate/ingest against the bundle root that contains manifests/ and receipt.json so the taste claims can resolve","Remove request_id and taste fields from assets that are intentionally outside a bundle","Regenerate the bundle first if it was never created, then re-run with it"],"exampleFix":"// before\nassets = [{\"id\": \"a1\", \"modality\": \"image\", \"origin\": \"internal\", \"request_id\": \"r1\"}]\nvalidate_assets(assets)  # no bundle\n// after\nassets = [{\"id\": \"a1\", \"modality\": \"image\", \"origin\": \"internal\"}]\nvalidate_assets(assets, bundle_root=\"path/to/bundle\")","handlingStrategy":"validation","validationCode":"TASTE_KEYS = {\"genre_number\", \"genre_slug\", \"style_fingerprint\", \"reference_sha256\", \"request_id\"}\nif bundle_root is None:\n    for a in assets:\n        bad = TASTE_KEYS & a.keys()\n        if bad:\n            raise ValueError(f\"asset {a['id']} has bundle-only keys {bad} but no bundle\")","typeGuard":null,"tryCatchPattern":"try:\n    ingest_assets(assets)\nexcept ValueError as e:\n    if str(e) == \"Taste claims require a validated bundle\":\n        strip_taste_keys(assets) or point_to_bundle(); retry()\n    else:\n        raise","preventionTips":["Only attach request_id/taste fields when validating against a real bundle","Keep bundle-less asset exports in a schema that excludes taste keys","Build bundles before exporting assets that reference them"],"tags":["bundle","taste-claims","validation"],"backgroundTag":"invalid-state-transition","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"}