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
- Add "periodic": false to the recipe dict (boolean, not string).
- Fix quoted values: change periodic: "false" to unquoted false in JSON/YAML.
- Update recipe generation templates to always emit the periodic field.
- 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
- Add "periodic": false to every recipe template
- Never serialize false as the string "false" in JSON/YAML
- Schema-validate recipes (e.g. with a JSON Schema requiring periodic: boolean const false) before validation
- Add a CI check that all checked-in recipes contain the field
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
- effect lacks placement constraints
- anchor evidence source duration is not bound to its receipt…
- Canonical session snapshot requires session.sourceTarget to…
- Canonical session snapshot requires session to be an object
- Canonical session snapshot requires workers to be an array
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)