affaan-m/ECC · error · ContractError

effect recipe must declare its seeded RNG algorithm

Error message

effect recipe must declare its seeded RNG algorithm

What it means

To keep schedules reproducible across runs and Python versions, the recipe must explicitly declare its RNG by setting rng_algorithm to the exact sentinel 'python.random.Random/v1'. Any other value (or a missing key) is rejected because the validator cannot guarantee the seed will be interpreted the same way.

Solutions

  1. Set recipe['rng_algorithm'] = 'python.random.Random/v1' exactly.
  2. Regenerate the recipe with a current tasteforge generator that emits the algorithm tag.
  3. Fix typos / version suffixes to match the accepted sentinel string.

Example fix

# before
recipe["rng_algorithm"] = "random.Random"
# after
recipe["rng_algorithm"] = "python.random.Random/v1"
Defensive patterns

Strategy: validation

Validate before calling

if recipe.get("rng_algorithm") != "python.random.Random/v1":
    raise ValueError("recipe must declare rng_algorithm 'python.random.Random/v1'")

Type guard

def declares_supported_rng(recipe: dict) -> bool:
    return recipe.get("rng_algorithm") == "python.random.Random/v1"

Try / catch

try:
    validate_effect_recipe(recipe)
except ContractError as exc:
    if "seeded RNG algorithm" in str(exc):
        recipe = {**recipe, "rng_algorithm": "python.random.Random/v1"}
        validate_effect_recipe(recipe)
    else:
        raise

Prevention

When it happens

Trigger: validate_effect_recipe called with a recipe missing 'rng_algorithm', or with values like 'random', 'secrets', 'numpy', or a different version tag such as 'python.random.Random/v2'.

Common situations: Recipes generated by older tool versions before the algorithm tag was introduced; switching RNG libraries; hand-edited recipes that omitted the field; typos in the algorithm string.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/6c8696520d8f95c3. Report an issue: GitHub.

Appendix: source

Thrown at skills/taste-application/scripts/tasteforge/contract.py:185

        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):
            raise ContractError("effect event start and duration must be finite positive timeline values")
        start = cast(float, start)
        duration = cast(float, duration)
        if float(start) + float(duration) > float(timeline) + 1e-9:

View on GitHub (pinned to 8321021c54)