{"record":{"id":"1eb998a370f22f02","repo":"affaan-m/ECC","slug":"cv-effect-event-get-effect-lacks-a-valid-subject-anchor","errorCode":null,"errorMessage":"CV effect {event.get('effect')} lacks a valid subject anchor","messagePattern":"CV effect (.+?) lacks a valid subject anchor","errorType":"validation","errorClass":"ContractError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/contract.py","lineNumber":238,"sourceCode":"    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\")\n                expected_duration = reference_durations.get(digest) if isinstance(digest, str) else None\n                if expected_duration is None or anchor.get(\"source_duration\") != expected_duration:\n                    raise ContractError(\"anchor evidence source duration is not bound to its receipt reference\")\n    for event in events:\n        placement = event.get(\"placement\")\n        if not isinstance(placement, dict) or not {\"safe_area\", \"max_coverage\", \"occlusion_policy\"}.issubset(placement):\n            raise ContractError(f\"effect {event.get('effect')} lacks placement constraints\")\n","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/contract.py#L220-L256","documentation":"ContractError (f-string) raised when a CV event declares requires_subject_anchor=true but its subject_anchor is not a dict containing all required keys: mode, target, source_ref_sha256, evidence_time, source_duration, lost_policy. The contract requires a structurally complete anchor before further semantic checks run.","triggerScenarios":"Calling validate_effect_recipe with subject_anchor set to null, a non-dict (e.g. string or list), or a dict missing one or more of the six required keys (commonly forgetting source_duration or lost_policy).","commonSituations":"Anchors partially filled in from a template; fields dropped during JSON round-tripping; upstream tooling that emits anchor as a flat structure instead of a nested dict; typos in key names such as sourceRefSha256.","solutions":["Ensure subject_anchor is a dict with all six keys: mode, target, source_ref_sha256, evidence_time, source_duration, lost_policy.","Use snake_case key names exactly as the contract specifies.","Emit the anchor from the receipt/reference pipeline so source_ref_sha256 and source_duration are populated from real data.","Add a pre-validation schema check that reports which required anchor keys are missing."],"exampleFix":"// before\n{\"effect\": \"cv_blur\", \"requires_subject_anchor\": true, \"subject_anchor\": {\"mode\": \"object_track\"}}\n// after\n{\"effect\": \"cv_blur\", \"requires_subject_anchor\": true, \"subject_anchor\": {\"mode\": \"object_track\", \"target\": \"subject_0\", \"source_ref_sha256\": \"<digest>\", \"evidence_time\": 1.2, \"source_duration\": 10.0, \"lost_policy\": \"disable_effect_until_track_recovers\"}}","handlingStrategy":"type-guard","validationCode":"REQUIRED_ANCHOR_KEYS = {\"mode\", \"target\", \"source_ref_sha256\", \"evidence_time\", \"source_duration\", \"lost_policy\"}\nfor event in recipe[\"events\"]:\n    anchor = event.get(\"subject_anchor\")\n    if not isinstance(anchor, dict) or not REQUIRED_ANCHOR_KEYS.issubset(anchor):\n        raise ValueError(f\"{event.get('effect')}: subject_anchor incomplete\")","typeGuard":"def is_valid_anchor_shape(anchor) -> bool:\n    required = {\"mode\", \"target\", \"source_ref_sha256\", \"evidence_time\", \"source_duration\", \"lost_policy\"}\n    return isinstance(anchor, dict) and required.issubset(anchor)","tryCatchPattern":"try:\n    validate_effect_recipe(recipe, reference_durations)\nexcept ContractError as exc:\n    if \"lacks a valid subject anchor\" in str(exc):\n        missing = REQUIRED_ANCHOR_KEYS - set(event[\"subject_anchor\"] or {})\n        raise ValueError(f\"anchor missing keys: {missing}\") from exc\n    raise","preventionTips":["Emit anchors from a single typed constructor that fills all six keys","Validate anchors against a JSON Schema before bundle submission","Never build anchors by hand; derive them from receipt data","Keep key names snake_case and centralized in one constants module"],"tags":["validation","contract","schema"],"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"}