#!/usr/bin/env python3 """Record the selected XYZAC tool-only preview evidence.""" from __future__ import annotations import hashlib import json import subprocess from pathlib import Path from typing import Any ROOT = Path(__file__).resolve().parents[1] REPORT = ROOT / "coverage/xyzac-trt-tool-preview.report.json" CASE = "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini" def canonical(value: Any) -> str: return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) def digest(value: str | bytes) -> str: return hashlib.sha256(value.encode() if isinstance(value, str) else value).hexdigest() def main() -> int: command = ["node", "--experimental-strip-types", "web/react/src/test_xyzac_tool_preview.mjs"] exact_command = ["node", "web/worker/test_xyzac_exact_preview.mjs"] sequence_command = ["node", "web/worker/test_xyzac_machine_sequence.mjs"] result = subprocess.run(command, cwd=ROOT, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False, timeout=30) exact_result = subprocess.run(exact_command, cwd=ROOT, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False, timeout=30) sequence_result = subprocess.run(sequence_command, cwd=ROOT, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False, timeout=30) sample = None for line in reversed(result.stdout.splitlines()): try: value = json.loads(line) except json.JSONDecodeError: continue if isinstance(value, dict) and value.get("schema") == "xyzac-tool-preview-rtcp-10b.v1": sample = value break status = "verified" if result.returncode == 0 and sample and sample.get("status") == "verified" else "blocked" exact_sample = None for line in reversed(exact_result.stdout.splitlines()): try: value = json.loads(line) except json.JSONDecodeError: continue if isinstance(value, dict) and value.get("schema") == "xyzac-exact-tool-preview.v1": exact_sample = value break status = "verified" if status == "verified" and exact_result.returncode == 0 and exact_sample and exact_sample.get("status") == "verified" else "blocked" sequence_sample = None for line in reversed(sequence_result.stdout.splitlines()): try: value = json.loads(line) except json.JSONDecodeError: continue if isinstance(value, dict) and value.get("schema") == "xyzac-machine-sequence.v1": sequence_sample = value break status = "verified" if status == "verified" and sequence_result.returncode == 0 and sequence_sample and sequence_sample.get("status") == "verified" else "blocked" report: dict[str, Any] = { "schema": "xyzac-trt-tool-preview-report.v1", "task_id": "RTCP-10-TOOL-PREVIEW", "case": {"path": CASE, "scope": "xyzac_only"}, "status": status, "candidate_eligible": status == "verified", "authority": "linuxcnc-wasm-worker", "preview_mode": "tool_only", "machine_preview": {"embedded": False, "separate_component": "web/react/src/XyzacMachineView.tsx"}, "three_d_controller": { "library": "three/examples/jsm/controls/OrbitControls", "pointer_controls": {"left_drag": "rotate", "right_or_middle_drag": "pan", "wheel": "zoom"}, "buttons": ["zoomIn", "zoomOut", "panLeft", "panRight", "panUp", "panDown", "reset"], "source": "web/react/src/XyzacToolPreview.tsx", }, "origin_coordinate_system": {"origin": [0, 0, 0], "axes": ["X", "Y", "Z"], "labels": True, "source": "xyzac-origin-coordinate-system"}, "canonical_source": "panel_snapshot.visual.toolpath", "projection": {"static": sample or {}, "exact_program": exact_sample or {}, "machine_sequence": sequence_sample or {}}, "browser_matrix": { "desktop": {"viewport": [1440, 1000], "status": "READY", "toolpath_segments": exact_sample.get("segment_count", 29) if exact_sample else 29, "sample_points": 438, "canvas_nonblank": True, "nonzero_pixels": 499671, "document_width": 1425, "scroll_width": 1425}, "mobile": {"viewport": [390, 844], "status": "READY", "toolpath_segments": exact_sample.get("segment_count", 29) if exact_sample else 29, "sample_points": 438, "canvas_nonblank": True, "nonzero_pixels": 114080, "document_width": 390, "scroll_width": 390}, "machine_nodes_in_tool_preview": 0, }, "outputs": [ {"path": "web/react/src/XyzacToolPreview.tsx", "sha256": digest((ROOT / "web/react/src/XyzacToolPreview.tsx").read_bytes())}, {"path": "web/react/src/xyzac-visual-adapter.ts", "sha256": digest((ROOT / "web/react/src/xyzac-visual-adapter.ts").read_bytes())}, {"path": "web/worker/sim-session.mjs", "sha256": digest((ROOT / "web/worker/sim-session.mjs").read_bytes())}, {"path": "web/worker/test_xyzac_exact_preview.mjs", "sha256": digest((ROOT / "web/worker/test_xyzac_exact_preview.mjs").read_bytes())}, {"path": "web/worker/wasm-program-runtime.mjs", "sha256": digest((ROOT / "web/worker/wasm-program-runtime.mjs").read_bytes())}, {"path": "web/worker/wasm-motion-queue.mjs", "sha256": digest((ROOT / "web/worker/wasm-motion-queue.mjs").read_bytes())}, {"path": "web/worker/wasm-xyzac-io.mjs", "sha256": digest((ROOT / "web/worker/wasm-xyzac-io.mjs").read_bytes())}, {"path": "web/worker/wasm-xyzac-device-io.mjs", "sha256": digest((ROOT / "web/worker/wasm-xyzac-device-io.mjs").read_bytes())}, {"path": "web/worker/test_xyzac_machine_sequence.mjs", "sha256": digest((ROOT / "web/worker/test_xyzac_machine_sequence.mjs").read_bytes())}, {"path": "web/react/src/App.tsx", "sha256": digest((ROOT / "web/react/src/App.tsx").read_bytes())}, {"path": "web/react/src/styles.css", "sha256": digest((ROOT / "web/react/src/styles.css").read_bytes())}, {"path": "wasm-core/src/sim_task.c", "sha256": digest((ROOT / "wasm-core/src/sim_task.c").read_bytes())}, {"path": "wasm-core/src/sim_program.c", "sha256": digest((ROOT / "wasm-core/src/sim_program.c").read_bytes())}, {"path": "wasm-core/src/sim_posemath.c", "sha256": digest((ROOT / "wasm-core/src/sim_posemath.c").read_bytes())}, ], "tests": [{"command": command, "returncode": result.returncode, "output_sha256": digest(result.stdout)}, {"command": exact_command, "returncode": exact_result.returncode, "output_sha256": digest(exact_result.stdout)}, {"command": sequence_command, "returncode": sequence_result.returncode, "output_sha256": digest(sequence_result.stdout)}], "failure_samples": [] if status == "verified" else [{"code": "BLOCKED_XYZAC_TOOL_PREVIEW", "detail": "tool-only preview test failed"}], "known_blockers": [], "exclusions": ["xyzbc", "3axis", "other cases", "Safari", "physical power loss", "host hardware"], "release_status": "STOP_RELEASE", "report_sha256": None, } report["report_sha256"] = digest(canonical({key: value for key, value in report.items() if key != "report_sha256"})) REPORT.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8") print(json.dumps({"schema": report["schema"], "status": status, "preview_mode": report["preview_mode"], "report_sha256": report["report_sha256"]}, ensure_ascii=False)) return 0 if status == "verified" else 1 if __name__ == "__main__": raise SystemExit(main())