#!/usr/bin/env python3 """Admit the five animation routes after native behavioral checks.""" import hashlib import json import pathlib import subprocess from wbv2_execution_phase import implementation_only ROOT = pathlib.Path(__file__).resolve().parents[1] JOB = "ANIMATION-FCURVE-DRIVER-COVERAGE" def main(): context = json.loads(subprocess.check_output( ["node", "tools/web/print-task-context.mjs"], cwd=ROOT, text=True)) if not implementation_only(ROOT): subprocess.run(["node", "tools/web/check-task-context.mjs"], cwd=ROOT, check=True, stdout=subprocess.DEVNULL) template = ROOT / "my-tools/local-workflows/ACTION-MARKER-AUTHORING.json" document = json.loads(template.read_text()) parent = ROOT / context["sourceDocuments"]["parentManifest"] document.update(task=context["task"], job=JOB, parentSha256=hashlib.sha256(parent.read_bytes()).hexdigest()) result = document["result"] result["summary"] = "Animation routes verified against evaluated desktop Main behavior" result["workflow"] = "Edit FCurve points, object drivers, layered Action groups and absolute Keying Sets, then bake evaluated object transforms with history and persistence" commands = ["setFCurveKeyframes", "setDriverExpression", "setActionGroups", "setKeyingSet", "bakeAnimation"] units = [f"web-command:{command}" for command in commands] result["sourceUnits"] = units result["routeIds"] = units result["sourceReferences"] = [ "blender-5.2.0/source/blender/blenkernel/intern/anim_sys.cc", "blender-5.2.0/source/blender/blenkernel/intern/fcurve_driver.cc", "blender-5.2.0/source/blender/animrig/intern/action.cc", "blender-5.2.0/source/blender/animrig/intern/evaluation.cc", "blender-5.2.0/source/blender/makesrna/intern/rna_access.cc", "blender-5.2.0/source/blender/makesrna/intern/rna_path.cc", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_rna_fallback.cpp", ] result["changedFiles"] = [ "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_rna_fallback.cpp", "web/app/src/app/App.tsx", "web/tests/e2e/animation-authoring-ui-production.spec.ts", "web/tests/fixtures/animation-authoring-main-snapshots.py", "my-tools/diagnose_local_animation_authoring.mjs", "my-tools/prepare_wbv2_animation_workflow.py", "tools/web/inspect-workflow-controls.mjs", ] replaced = { "my-tools/prepare_wbv2_authoring_batch.py": "my-tools/prepare_wbv2_animation_workflow.py", "web/tests/e2e/action-marker-ui-production.spec.ts": "web/tests/e2e/animation-authoring-ui-production.spec.ts", "web/tests/fixtures/action-main-snapshots.py": "web/tests/fixtures/animation-authoring-main-snapshots.py", } result["dependencies"] = sorted(set(replaced.get(value, value) for value in result["dependencies"]) | set(result["sourceReferences"]) | set(result["changedFiles"]) | { str(template.relative_to(ROOT)), "my-tools/wbv2_runtime_contract.py", "web/app/src/three-adapter/viewport.ts", }) for check in result["checks"]: check["argv"] = [value.replace("action-marker-ui-production.spec.ts", "animation-authoring-ui-production.spec.ts") .replace("WEB_TEST_PORT=5357", "WEB_TEST_PORT=5365") for value in check["argv"]] if check["id"] == "web-build": check["dependsOn"] = ["wasm-source-build", "native-animation"] result["checks"].insert(1, { "id": "native-animation", "argv": ["env", f"BLENDER_BIN={ROOT / 'build_blender_native/bin/blender'}", "node", "my-tools/diagnose_local_animation_authoring.mjs", "--indexed-path"], "cwd": ".", "kinds": ["unit"], "dependsOn": ["wasm-source-build"], "timeoutSeconds": 60, "continueOnFailure": True, }) for case in result["cases"]: case.update(id=f"animation-authoring-{case['kind']}", receipt=f"unallocated/{case['kind']}.json", unitIds=units) missing = [path for path in result["dependencies"] if not (ROOT / path).is_file()] if missing: raise RuntimeError("Missing local dependency: " + ", ".join(missing)) target = ROOT / "my-tools/local-workflows" / f"{JOB}.json" target.write_text(json.dumps(document, indent=2) + "\n") print(json.dumps({"job": JOB, "contract": str(target.relative_to(ROOT)), "state": "prepared", "routes": len(units)})) if __name__ == "__main__": main()