#!/usr/bin/env python3 """Compare native AXIS and Web/WASM manual-tool evidence.""" from __future__ import annotations import argparse import hashlib import json from pathlib import Path from typing import Any ROOT = Path(__file__).resolve().parents[1] OUTPUT = Path("coverage/axis3-tutorial-native-web-tool-comparison.report.json") CASE = "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini" CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1" def canonical(value: Any) -> str: return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) def digest(value: Any) -> str: return hashlib.sha256(canonical(value).encode()).hexdigest() def load(root: Path, path: str) -> dict[str, Any]: return json.loads((root / path).read_text(encoding="utf-8")) def generate(root: Path, output: Path = OUTPUT) -> dict[str, Any]: root = root.resolve() native = load(root, "coverage/axis3-tutorial-manual-ui-verification.report.json") web = load(root, "coverage/axis3-tutorial-web-manual-verification.report.json") parity = load(root, "coverage/axis3-tutorial-web-wasm-complete-parity.report.json") native_ok = native.get("status") == "verified" and native.get("window", {}).get("title", "").find("3dtest.ngc") >= 0 and all(item.get("returncode") == 0 for item in native.get("screenshots", [])) web_ok = web.get("status") == "verified" and web.get("case") == CASE and all(item.get("bytes", 0) > 0 for item in web.get("screenshots", [])) shared = { "case": CASE, "context_id": CONTEXT_ID, "program": "linuxcnc/nc_files/3dtest.ngc", "native_source_files": "coverage/axis3-tutorial-native-run.report.json", "wasm_source_files": "coverage/axis3-tutorial-web-wasm-complete-parity.report.json", } checks = { "native_tool_verified": {"status": "verified" if native_ok else "blocked", "report": "coverage/axis3-tutorial-manual-ui-verification.report.json"}, "web_tool_verified": {"status": "verified" if web_ok else "blocked", "report": "coverage/axis3-tutorial-web-manual-verification.report.json"}, "same_case_and_context": {"status": "verified" if native.get("case") == "linuxcnc/configs/sim/axis/vismach/3axis-tutorial" and web.get("case") == CASE else "blocked", **shared}, "same_operation_intent": {"status": "verified" if [row.get("state") for row in native.get("transitions", [])] == ["WINDOW_VISIBLE", "ESTOP_RESET", "ON", "ALL_HOMED_REQUESTED", "AUTO_RUN_REQUESTED"] and [row.get("state") for row in web.get("states", []) if row.get("label") in {"ready", "estop", "power", "homed", "running", "complete"}] else "blocked", "native": [row.get("state") for row in native.get("transitions", [])], "web_labels": [row.get("label") for row in web.get("states", []) if row.get("label") in {"ready", "estop", "power", "homed", "running", "complete"}]}, "visual_evidence_nonempty": {"status": "verified" if native_ok and web_ok else "blocked", "native_screenshots": len(native.get("screenshots", [])), "web_screenshots": len(web.get("screenshots", []))}, "wasm_acceptance_regression": {"status": "verified" if parity.get("status") == "verified" and parity.get("passed_command_count") == parity.get("required_command_count") else "blocked", "passed": parity.get("passed_command_count"), "required": parity.get("required_command_count")}, } report: dict[str, Any] = { "schema": "axis3-tutorial-native-web-tool-comparison.v1", "task_id": "AXIS3-NATIVE-WEB-TOOL-COMPARISON-01", "context_id": CONTEXT_ID, "case": CASE, "status": "verified" if all(item["status"] == "verified" for item in checks.values()) else "blocked", "conclusion": "native_and_web_tools_match_selected_case_workflow" if all(item["status"] == "verified" for item in checks.values()) else "tool_comparison_blocked", "shared_inputs": shared, "tools": { "native": {"tool": "tool/linuxcnc-axis-manual-execution", "report": "coverage/axis3-tutorial-manual-ui-verification.report.json", "window": native.get("window"), "screenshots": native.get("screenshots"), "transitions": native.get("transitions")}, "web": {"tool": "tool/linuxcnc-web-wasm-manual-execution", "report": "coverage/axis3-tutorial-web-manual-verification.report.json", "browser": web.get("browser"), "screenshots": web.get("screenshots"), "states": [row for row in web.get("states", []) if row.get("label") in {"ready", "estop", "power", "homed", "running", "complete"}]}, }, "checks": checks, "boundaries": ["simulation-only", "native process uses POSIX non-realtime fallback", "Web visual uses Chromium SwiftShader", "real hardware and full LinuxCNC parity are outside selected-case tool comparison"], "report_sha256": None, } report["report_sha256"] = digest({key: value for key, value in report.items() if key != "report_sha256"}) destination = output if output.is_absolute() else root / output destination.parent.mkdir(parents=True, exist_ok=True) destination.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8") return report def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--repo-root", type=Path, default=ROOT) parser.add_argument("--output", type=Path, default=OUTPUT) args = parser.parse_args() report = generate(args.repo_root.resolve(), args.output) print(f"axis3_tool_comparison={report['status']} conclusion={report['conclusion']}") return 0 if report["status"] == "verified" else 1 if __name__ == "__main__": raise SystemExit(main())