{"record":{"id":"089518ccf9956340","repo":"affaan-m/ECC","slug":"candidate-fps-duration-retime-ambiguity","errorCode":null,"errorMessage":"candidate fps/duration/retime ambiguity","messagePattern":"candidate fps/duration/retime ambiguity","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/integration.py","lineNumber":325,"sourceCode":"    return result\n\n\ndef _inserts(items: list, candidates: dict, config: dict, input_hash: str, edit_hash: str) -> None:\n    occupied = []\n    for item in items:\n        _object(item, {\"candidate_id\", \"candidate_range\", \"timeline_range\", \"retime\", \"approval_file\"})\n        candidate = candidates.get(_text(item[\"candidate_id\"]))\n        if candidate is None or candidate[\"review_status\"] != \"approved\":\n            raise ValueError(\"insert requires an approved, resolved candidate\")\n        target = _range(item[\"timeline_range\"], config[\"baseline\"][\"timeline_range\"])\n        source = _range(item[\"candidate_range\"], [0, candidate[\"media_frames\"]])\n        if any(_overlap(target, p[\"range\"]) for p in config[\"protected_intervals\"]):\n            raise ValueError(\"insert overlaps protected original stack\")\n        if any(_overlap(target, span) for span in occupied):\n            raise ValueError(\"insert proposals overlap\")\n        if (item[\"retime\"] != \"none\" or target[1] - target[0] != source[1] - source[0]\n                or _rate(candidate[\"fps\"]) != _rate(config[\"baseline\"][\"fps\"])):\n            raise ValueError(\"candidate fps/duration/retime ambiguity\")\n        evidence = _artifact(item[\"approval_file\"], parse_json=True)\n        expected = {\"status\": \"approved\", \"candidate_sha256\": candidate[\"media\"][\"sha256\"],\n                    \"source_sha256\": config[\"source\"][\"media\"][\"sha256\"],\n                    \"compiled_input_sha256\": input_hash,\n                    \"edit_context_sha256\": edit_hash,\n                    \"candidate_range\": source, \"timeline_range\": target}\n        if not isinstance(evidence, dict) or any(\n                _canonical(evidence.get(key)) != _canonical(value) for key, value in expected.items()):\n            raise ValueError(\"approval evidence must bind exact source, candidate and placement\")\n        occupied.append(target)\n\n\ndef build_application_bundle(config: dict, compiled_input: dict | None, *, local_only: bool = False) -> dict:\n    \"\"\"Validate resident evidence and return a new deterministic, offline bundle.\"\"\"\n    if type(local_only) is not bool:\n        raise ValueError(\"local_only must be an exact boolean\")\n    _object(config, _REQUIRED, _OPTIONAL)\n    if len(_canonical(config)) > _MAX_JSON:","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/integration.py#L307-L343","documentation":"In tasteforge's _inserts, each approved insert proposal must be a straight passthrough of the candidate: same fps as the project baseline, identical duration in source and timeline ranges, and retime mode 'none'. If any of these differ, the tool cannot place the insert without transcoding or retiming, so it raises this error to force an explicit decision.","triggerScenarios":"Calling build_application_bundle with a config whose inserts[] item has retime != 'none', a timeline_range whose length differs from its candidate_range length, or a candidate whose fps differs from config['baseline']['fps'] (compared via _rate, so e.g. 30 vs 29.97 also fails).","commonSituations":"Hand-authored or regenerated candidate configs where fps metadata drifted from the baseline (mixed-frame-rate stock footage); edited ranges that were stretched or trimmed during timeline edits; leftover retime fields from a previous slow-motion proposal that was approved without being reverted.","solutions":["Set item['retime'] to 'none' in the insert entry, or drop the retime field entirely","Adjust the insert's timeline_range so its duration exactly equals the source candidate_range duration","Re-encode or re-export the candidate at the baseline project fps so _rate(candidate.fps) == _rate(baseline.fps)","Move the insert to a separate retime workflow step instead of bundling it as a passthrough insert"],"exampleFix":"// before\n{\"id\": \"ins1\", \"candidate\": \"c1\", \"range\": [0, 48], \"timeline_range\": [120, 121], \"retime\": \"linear\"}\n// after\n{\"id\": \"ins1\", \"candidate\": \"c1\", \"range\": [0, 48], \"timeline_range\": [120, 168], \"retime\": \"none\"}","handlingStrategy":"validation","validationCode":"def insert_is_passthrough(item, candidate, baseline):\n    return (item.get(\"retime\", \"none\") == \"none\"\n        and item[\"timeline_range\"][1] - item[\"timeline_range\"][0] == item[\"range\"][1] - item[\"range\"][0]\n        and float(candidate[\"fps\"]) == float(baseline[\"fps\"]))","typeGuard":"def is_valid_insert(item):\n    return isinstance(item, dict) and item.get(\"retime\") == \"none\" and isinstance(item.get(\"range\"), list) and isinstance(item.get(\"timeline_range\"), list)","tryCatchPattern":"try:\n    bundle = build_application_bundle(cfg, ci)\nexcept ValueError as e:\n    if \"fps/duration/retime\" in str(e):\n        fix_insert_to_passthrough(cfg)  # set retime='none' and equalize durations\n    else:\n        raise","preventionTips":["Always export candidates at the baseline project fps","Enforce retime='none' at config-authoring time with a lint/schema check","Assert range lengths match timeline_range lengths before building","Keep retimed edits in a separate workflow from passthrough inserts"],"tags":["validation","media","fps","retime"],"backgroundTag":"invalid-argument-value","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"}