{"record":{"id":"eb920822ab10a191","repo":"affaan-m/ECC","slug":"cv-effect-event-get-effect-must-require-a-subject-anchor","errorCode":null,"errorMessage":"CV effect {event.get('effect')} must require a subject anchor","messagePattern":"CV effect (.+?) must require a subject anchor","errorType":"validation","errorClass":"ContractError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/contract.py","lineNumber":230,"sourceCode":"            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):\n        if all(intervals[index] == intervals[index % period] for index in range(len(intervals))):\n            raise ContractError(\"stochastic schedule is periodic; repeating interval cycle\")\n    if recipe.get(\"periodic\") is not False:\n        raise ContractError(\"effect recipe must explicitly declare periodic=false\")\n    for event in events:\n        cv_effect = str(event.get(\"effect\", \"\")).startswith(\"cv_\")\n        if cv_effect and event.get(\"requires_subject_anchor\") is not True:\n            raise ContractError(f\"CV effect {event.get('effect')} must require a subject anchor\")\n        if event.get(\"requires_subject_anchor\"):\n            anchor = event.get(\"subject_anchor\")\n            required = {\n                \"mode\", \"target\", \"source_ref_sha256\", \"evidence_time\",\n                \"source_duration\", \"lost_policy\",\n            }\n            if not isinstance(anchor, dict) or not required.issubset(anchor):\n                raise ContractError(f\"CV effect {event.get('effect')} lacks a valid subject anchor\")\n            if anchor.get(\"mode\") not in {\"object_track\", \"point_track\", \"segmentation_track\"}:\n                raise ContractError(f\"CV effect {event.get('effect')} has an invalid subject anchor\")\n            if anchor.get(\"lost_policy\") != \"disable_effect_until_track_recovers\":\n                raise ContractError(f\"CV effect {event.get('effect')} must fail closed on anchor loss\")\n            _validate_media_time(\n                anchor.get(\"evidence_time\"), anchor.get(\"source_duration\"),\n                label=\"anchor evidence time\",\n            )\n            if reference_durations is not None:\n                digest = anchor.get(\"source_ref_sha256\")","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/contract.py#L212-L248","documentation":"ContractError (f-string) raised when an event whose effect name starts with 'cv_' (a computer-vision effect) does not have requires_subject_anchor set to exactly True. CV effects modify content relative to a tracked subject, so the contract mandates an explicit subject-anchor requirement on the event.","triggerScenarios":"Calling validate_effect_recipe with an event like {\"effect\": \"cv_blur\", ...} missing requires_subject_anchor, or with requires_subject_anchor set to false, a truthy string, or 1 (anything not identically True).","commonSituations":"Renaming a generic effect to a cv_ prefix without updating anchor requirements; copying a non-CV event template; schema drift where requires_subject_anchor was made optional upstream.","solutions":["Set requires_subject_anchor: true on every cv_* event.","Rename the effect to a non-cv_ prefix if it genuinely does not track a subject.","Add an assertion in your recipe generator that cv_* events always include the flag.","Catch ContractError and surface the specific event effect name from the message for authoring fixes."],"exampleFix":"// before\n{\"time\": 1.2, \"effect\": \"cv_pixelate\"}\n// after\n{\"time\": 1.2, \"effect\": \"cv_pixelate\", \"requires_subject_anchor\": true}","handlingStrategy":"validation","validationCode":"for event in recipe[\"events\"]:\n    if str(event.get(\"effect\", \"\")).startswith(\"cv_\") and event.get(\"requires_subject_anchor\") is not True:\n        raise ValueError(f\"{event['effect']} must set requires_subject_anchor=true\")","typeGuard":"def cv_events_anchored(events) -> bool:\n    return all(\n        e.get(\"requires_subject_anchor\") is True\n        for e in events\n        if str(e.get(\"effect\", \"\")).startswith(\"cv_\")\n    )","tryCatchPattern":"try:\n    validate_effect_recipe(recipe, reference_durations)\nexcept ContractError as exc:\n    if \"must require a subject anchor\" in str(exc):\n        for e in recipe[\"events\"]:\n            if str(e.get(\"effect\", \"\")).startswith(\"cv_\"):\n                e[\"requires_subject_anchor\"] = True\n    else:\n        raise","preventionTips":["Enforce the cv_ -> anchor requirement in your recipe generator","Use a lint rule that flags cv_* events without requires_subject_anchor","Set the flag as a boolean literal true, not 1 or \"true\"","Keep effect naming and anchor policy in one schema definition"],"tags":["validation","contract","computer-vision"],"backgroundTag":"schema-validation-failed","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"}