affaan-m/ECC · critical · ContractError
receipt crosses the dry-run boundary
Error message
receipt crosses the dry-run boundary
What it means
validate_bundle enforces that the receipt proves a pure dry run: dry_run must be exactly True, provider_execution exactly False, and provider_calls exactly the integer 0. If any of these invariants is broken, the receipt indicates a provider was invoked, which this package forbids, so the ContractError is raised. This is the fail-closed boundary that keeps offline packages from performing network calls.
Solutions
- Regenerate the bundle and receipt via a normal dry-run invocation of tasteforge
- Restore an original, unedited receipt.json — do not hand-edit dry_run/provider fields
- Ensure provider_calls is the JSON integer 0 (not "0" or 0.0) and dry_run/provider_execution are JSON booleans
- Verify the receipt belongs to this package's bundle, not another tool's output
Example fix
// before (edited receipt)
{"dry_run": "true", "provider_execution": false, "provider_calls": "0"}
// after
{"dry_run": true, "provider_execution": false, "provider_calls": 0} Defensive patterns
Strategy: validation
Validate before calling
import json, pathlib
def receipt_is_dry_run(out_dir) -> bool:
r = json.loads((pathlib.Path(out_dir) / "receipt.json").read_text())
return (r.get("dry_run") is True
and r.get("provider_execution") is False
and type(r.get("provider_calls")) is int
and r.get("provider_calls") == 0) Type guard
def is_dry_run_receipt(r) -> bool:
return isinstance(r, dict) and r.get("dry_run") is True and r.get("provider_execution") is False and type(r.get("provider_calls")) is int and r["provider_calls"] == 0 Try / catch
from tasteforge.contract import ContractError
try:
validate_bundle(out_dir)
except ContractError as e:
if "dry-run boundary" in str(e):
raise SystemExit("Receipt indicates provider execution; regenerate via dry-run only") Prevention
- Never hand-edit receipt fields like dry_run or provider_calls
- Use JSON integers and booleans (0 not "0"; true not "true") if tooling rewrites receipts
- Keep dry-run-only packages separate from any provider-executing tooling
When it happens
Trigger: Receipt JSON with dry_run != true, provider_execution != false, provider_calls missing/non-int, or provider_calls != 0 — e.g. a hand-edited receipt, a receipt from a future/pro variant with live providers, or tampering attempts like using 0.0, "0", or false.
Common situations: Hand-editing receipts to fake values, mixing receipts between packages that do execute providers and this dry-run-only package, or type confusion in JSON (string/float zero instead of int 0, which fails the strict `type(...) is not int` check).
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Asset taste lineage differs from its bundle request
- missing receipt
- receipt reference cannot bind recipe evidence
- receipt reference digest has conflicting source durations
- anchor evidence source duration is not bound to its receipt…
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/ee441ff1029e58c8.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/contract.py:466
specs = [json.loads(path.read_text(encoding="utf-8"))
for path in sorted((out_dir / "genres").glob("*.json"))]
validate_genre_specs(specs)
validate_manifests(out_dir / "manifests")
provenance_path = out_dir / "provenance.json"
if not provenance_path.is_file():
raise ContractError("missing provenance")
validate_provenance(json.loads(provenance_path.read_text(encoding="utf-8")))
receipt_path = out_dir / "receipt.json"
if not receipt_path.is_file():
raise ContractError("missing receipt")
receipt = json.loads(receipt_path.read_text(encoding="utf-8"))
if (receipt.get("dry_run") is not True
or receipt.get("provider_execution") is not False
or type(receipt.get("provider_calls")) is not int
or receipt.get("provider_calls") != 0):
raise ContractError("receipt crosses the dry-run boundary")
validate_artifact_receipt(out_dir, receipt)
reference_durations: dict[str, float] = {}
for reference in receipt.get("references", []):
digest = reference.get("sha256")
duration = reference.get("source_duration")
if not isinstance(digest, str) or not _is_finite_real(duration):
raise ContractError("receipt reference cannot bind recipe evidence")
duration = float(duration)
previous = reference_durations.get(digest)
if previous is not None and previous != duration:
raise ContractError("receipt reference digest has conflicting source durations")
reference_durations[digest] = duration
recipe_path = out_dir / "resolve" / "effect_recipe.json"
if not recipe_path.is_file():
raise ContractError("missing Resolve effect recipe")
validate_effect_recipe(View on GitHub (pinned to 8321021c54)