affaan-m/ECC · error · ContractError

effect recipe must explicitly declare periodic=false

Error message

effect recipe must explicitly declare periodic=false

What it means

ContractError raised when an effect recipe does not explicitly set 'periodic' to False. The tasteforge contract requires recipes to affirmatively declare periodic=false, documenting that the author asserts a non-periodic schedule. Missing the key, setting it to True, or using a truthy non-boolean value all fail.

Solutions

  1. Add "periodic": false to the recipe dict (boolean, not string).
  2. Fix quoted values: change periodic: "false" to unquoted false in JSON/YAML.
  3. Update recipe generation templates to always emit the periodic field.
  4. Validate the recipe schema locally before submitting the bundle.

Example fix

// before
recipe = {"events": [...], "seed": 42}
// after
recipe = {"events": [...], "seed": 42, "periodic": False}
Defensive patterns

Strategy: validation

Validate before calling

if recipe.get("periodic") is not False:
    raise ValueError("recipe must explicitly declare periodic=false (boolean)")

Type guard

def declares_non_periodic(recipe) -> bool:
    return isinstance(recipe, dict) and recipe.get("periodic") is False

Try / catch

try:
    validate_effect_recipe(recipe, reference_durations)
except ContractError as exc:
    if "periodic=false" in str(exc):
        recipe["periodic"] = False
        validate_effect_recipe(recipe, reference_durations)
    else:
        raise

Prevention

When it happens

Trigger: Calling validate_effect_recipe with a recipe dict lacking the 'periodic' key; recipe contains periodic: true; recipe contains periodic: "false" (a string, which is not the boolean False since the check is `is not False`).

Common situations: Recipes written before the periodic field was introduced; hand-edited JSON omitting the declaration; YAML/JSON configs where 'false' was quoted as a string; copying a minimal recipe template that predates the requirement.

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


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

Appendix: source

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

        _validate_media_time(
            evidence.get("time"), evidence.get("source_duration"), label="effect evidence time"
        )
        if reference_durations is not None:
            digest = evidence.get("reference_sha256")
            expected_duration = reference_durations.get(digest) if isinstance(digest, str) else None
            if expected_duration is None or evidence.get("source_duration") != expected_duration:
                raise ContractError("effect evidence source duration is not bound to its receipt reference")
    times = [float(event["time"]) for event in events]
    if times != sorted(times) or len(times) != len(set(times)):
        raise ContractError("effect event times must be unique and increasing")
    intervals = [round(b - a, 6) for a, b in zip(times, times[1:])]  # noqa: RUF007
    if len(set(intervals)) <= 1:
        raise ContractError("stochastic schedule is periodic; intervals must vary")
    for period in range(1, len(intervals) // 2 + 1):
        if all(intervals[index] == intervals[index % period] for index in range(len(intervals))):
            raise ContractError("stochastic schedule is periodic; repeating interval cycle")
    if recipe.get("periodic") is not False:
        raise ContractError("effect recipe must explicitly declare periodic=false")
    for event in events:
        cv_effect = str(event.get("effect", "")).startswith("cv_")
        if cv_effect and event.get("requires_subject_anchor") is not True:
            raise ContractError(f"CV effect {event.get('effect')} must require a subject anchor")
        if event.get("requires_subject_anchor"):
            anchor = event.get("subject_anchor")
            required = {
                "mode", "target", "source_ref_sha256", "evidence_time",
                "source_duration", "lost_policy",
            }
            if not isinstance(anchor, dict) or not required.issubset(anchor):
                raise ContractError(f"CV effect {event.get('effect')} lacks a valid subject anchor")
            if anchor.get("mode") not in {"object_track", "point_track", "segmentation_track"}:
                raise ContractError(f"CV effect {event.get('effect')} has an invalid subject anchor")
            if anchor.get("lost_policy") != "disable_effect_until_track_recovers":
                raise ContractError(f"CV effect {event.get('effect')} must fail closed on anchor loss")
            _validate_media_time(
                anchor.get("evidence_time"), anchor.get("source_duration"),

View on GitHub (pinned to 8321021c54)