affaan-m/ECC · error · ContractError
effect recipe crosses the dry-run provider boundary
Error message
effect recipe crosses the dry-run provider boundary
What it means
validate_effect_recipe enforces that every effect recipe operates in a strict dry-run mode: dry_run must be true, provider_calls must be exactly 0 (int), and provider_execution must be false. If any of these provider-boundary invariants is violated, the recipe is rejected because a 'dry-run' recipe that reaches the provider would produce nondeterministic, unverified outputs.
Solutions
- Set recipe['dry_run'] = True in the recipe dict.
- Ensure recipe['provider_calls'] is the exact int 0 (not 0.0, not True, not missing).
- Ensure recipe['provider_execution'] = False.
- Run the predicate (dry_run is True and type(provider_calls) is int and provider_calls == 0 and provider_execution is False) before calling the validator.
Example fix
# before
recipe = {"provider_calls": 0, "provider_execution": False}
# after
recipe = {"dry_run": True, "provider_calls": 0, "provider_execution": False} Defensive patterns
Strategy: validation
Validate before calling
def recipe_is_dry_run(recipe):
return (isinstance(recipe, dict)
and recipe.get("dry_run") is True
and type(recipe.get("provider_calls")) is int
and recipe.get("provider_calls") == 0
and recipe.get("provider_execution") is False) Type guard
def is_dry_run_recipe(recipe: object) -> bool:
return (isinstance(recipe, dict)
and recipe.get("dry_run") is True
and type(recipe.get("provider_calls")) is int
and recipe.get("provider_calls") == 0
and recipe.get("provider_execution") is False) Try / catch
try:
validate_effect_recipe(recipe)
except ContractError as exc:
if "dry-run provider boundary" in str(exc):
recipe = {**recipe, "dry_run": True, "provider_calls": 0, "provider_execution": False}
validate_effect_recipe(recipe)
else:
raise Prevention
- Always template new recipes from a known-valid dry-run example.
- Never flip provider_execution to true in recipes destined for the validator.
- Log the offending keys (dry_run/provider_calls/provider_execution) before validating.
- Add a unit test asserting generated recipes satisfy the dry-run predicate.
When it happens
Trigger: Calling validate_effect_recipe (directly or via validate_bundle) with a recipe dict where dry_run is missing/false, provider_calls is absent, a bool, or non-zero, or provider_execution is missing/true.
Common situations: Hand-written recipes that forgot the dry_run flag after copying a live provider config; toggling provider_execution to true to 'test for real'; a generator emitting provider_calls counts as floats or omitting the field; merging recipes from an older schema version before these keys existed.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- workflow requires dry_run=true; provider execution is…
- application config exceeds local size limit
- Asset request_id/modality does not match the bundle
- at least one genre spec is required
- bundle collections must be lists
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/f54799a7cf75fdbd.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/contract.py:181
)
signature = spec.get("signature")
if not isinstance(signature, dict) or not _SIGNATURE_AXES.issubset(signature):
raise ContractError(f"genre {spec.get('number')} has an incomplete signature")
if not all(isinstance(signature[axis], list) for axis in _SIGNATURE_AXES):
raise ContractError(f"genre {spec.get('number')} signature axes must be lists")
if not all(signature[axis] for axis in _SIGNATURE_AXES):
raise ContractError(f"genre {spec.get('number')} has an empty signature axis, including avoid")
def validate_effect_recipe(
recipe: dict[str, Any], *, reference_durations: dict[str, float] | None = None
) -> None:
"""Require a seeded aperiodic schedule and anchors on subject-aware effects."""
if (recipe.get("dry_run") is not True
or type(recipe.get("provider_calls")) is not int
or recipe.get("provider_calls") != 0
or recipe.get("provider_execution") is not False):
raise ContractError("effect recipe crosses the dry-run provider boundary")
if not isinstance(recipe.get("seed"), int) or isinstance(recipe.get("seed"), bool):
raise ContractError("effect recipe must have an integer seed")
if recipe.get("rng_algorithm") != "python.random.Random/v1":
raise ContractError("effect recipe must declare its seeded RNG algorithm")
events = recipe.get("events")
if not isinstance(events, list) or len(events) < 3:
raise ContractError("effect recipe needs at least three scheduled events")
timeline = recipe.get("timeline_duration")
if not _is_finite_real(timeline):
raise ContractError("effect recipe must declare a finite positive timeline duration")
timeline = cast(float, timeline)
if float(timeline) <= 0:
raise ContractError("effect recipe must declare a finite positive timeline duration")
for event in events:
start = event.get("time")
duration = event.get("duration")
if (not _is_finite_real(start) or not _is_finite_real(duration)
or float(start) < 0 or float(duration) <= 0):View on GitHub (pinned to 8321021c54)