{"record":{"id":"4ad6dc43979b9399","repo":"affaan-m/ECC","slug":"effect-event-end-exceeds-the-declared-timeline","errorCode":null,"errorMessage":"effect event end exceeds the declared timeline","messagePattern":"effect event end exceeds the declared timeline","errorType":"validation","errorClass":"ContractError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/contract.py","lineNumber":204,"sourceCode":"    events = recipe.get(\"events\")\n    if not isinstance(events, list) or len(events) < 3:\n        raise ContractError(\"effect recipe needs at least three scheduled events\")\n    timeline = recipe.get(\"timeline_duration\")\n    if not _is_finite_real(timeline):\n        raise ContractError(\"effect recipe must declare a finite positive timeline duration\")\n    timeline = cast(float, timeline)\n    if float(timeline) <= 0:\n        raise ContractError(\"effect recipe must declare a finite positive timeline duration\")\n    for event in events:\n        start = event.get(\"time\")\n        duration = event.get(\"duration\")\n        if (not _is_finite_real(start) or not _is_finite_real(duration)\n                or float(start) < 0 or float(duration) <= 0):\n            raise ContractError(\"effect event start and duration must be finite positive timeline values\")\n        start = cast(float, start)\n        duration = cast(float, duration)\n        if float(start) + float(duration) > float(timeline) + 1e-9:\n            raise ContractError(\"effect event end exceeds the declared timeline\")\n        evidence = event.get(\"evidence\")\n        if not isinstance(evidence, dict):\n            raise ContractError(f\"effect {event.get('effect')} lacks reference evidence\")\n        _validate_media_time(\n            evidence.get(\"time\"), evidence.get(\"source_duration\"), label=\"effect evidence time\"\n        )\n        if reference_durations is not None:\n            digest = evidence.get(\"reference_sha256\")\n            expected_duration = reference_durations.get(digest) if isinstance(digest, str) else None\n            if expected_duration is None or evidence.get(\"source_duration\") != expected_duration:\n                raise ContractError(\"effect evidence source duration is not bound to its receipt reference\")\n    times = [float(event[\"time\"]) for event in events]\n    if times != sorted(times) or len(times) != len(set(times)):\n        raise ContractError(\"effect event times must be unique and increasing\")\n    intervals = [round(b - a, 6) for a, b in zip(times, times[1:])]  # noqa: RUF007\n    if len(set(intervals)) <= 1:\n        raise ContractError(\"stochastic schedule is periodic; intervals must vary\")\n    for period in range(1, len(intervals) // 2 + 1):","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/contract.py#L186-L222","documentation":"An event must fit inside the declared timeline: start + duration may not exceed timeline_duration (with a tiny 1e-9 tolerance for float rounding). An event that ends after the timeline is inconsistent with the recipe's declared bounds and is rejected.","triggerScenarios":"validate_effect_recipe called where any event satisfies time + duration > timeline_duration + 1e-9, e.g. start 28.0 with duration 5.0 on a 30.0 timeline.","commonSituations":"Editing event durations after fixing the timeline; generating events evenly spaced across a slightly-too-short timeline; unit mismatch between event times and timeline_duration; off-by-one when events are meant to end 'at' the boundary.","solutions":["Reduce the event's duration (or start) so time + duration <= timeline_duration.","Increase recipe['timeline_duration'] to cover all events.","Regenerate the schedule ensuring events are laid out within the timeline bound."],"exampleFix":"# before\n{\"time\": 28.0, \"duration\": 5.0}  # timeline 30.0\n# after\n{\"time\": 25.0, \"duration\": 5.0}  # ends at 30.0","handlingStrategy":"validation","validationCode":"for ev in recipe[\"events\"]:\n    if float(ev[\"time\"]) + float(ev[\"duration\"]) > float(recipe[\"timeline_duration\"]) + 1e-9:\n        raise ValueError(f\"event {ev} ends past timeline {recipe['timeline_duration']}\")","typeGuard":"def event_fits_timeline(ev: dict, timeline: float) -> bool:\n    return float(ev[\"time\"]) + float(ev[\"duration\"]) <= float(timeline) + 1e-9","tryCatchPattern":"try:\n    validate_effect_recipe(recipe)\nexcept ContractError as exc:\n    if \"exceeds the declared timeline\" in str(exc):\n        recipe = {**recipe, \"events\": [clip_event_to_timeline(ev, recipe[\"timeline_duration\"]) for ev in recipe[\"events\"]]}\n        validate_effect_recipe(recipe)\n    else:\n        raise","preventionTips":["Recompute timeline_duration after editing any event's start/duration.","Generate events by laying out intervals inside the timeline, not the reverse.","Keep event times and timeline_duration in the same unit."],"tags":["validation","timeline","events"],"backgroundTag":"value-out-of-range","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}