Advance simulation parity and 3-axis tutorial coverage

This commit is contained in:
2026-08-23 18:00:56 +08:00
parent 4a11d9692b
commit 938654b19d
107 changed files with 13537 additions and 3565 deletions

View File

@@ -0,0 +1,119 @@
#!/usr/bin/env python3
"""Aggregate the complete scoped 3axis-tutorial parity evidence."""
from __future__ import annotations
import argparse
import hashlib
import json
import subprocess
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parents[1]
REPORT = Path("coverage/axis3-tutorial-complete.report.json")
CASE = "linuxcnc/configs/sim/axis/vismach/3axis-tutorial"
ENTRYPOINT = f"{CASE}/3axis-tutorial.ini"
INPUT_REPORTS = (
Path("coverage/axis3-tutorial-native-run.report.json"),
Path("coverage/axis3-tutorial-resource-graph.report.json"),
Path("coverage/axis3-tutorial-program.report.json"),
Path("coverage/gui-browser-visual-fe-m4.report.json"),
Path("coverage/axis3-tutorial-sequence.report.json"),
Path("coverage/axis3-tutorial-persistence.report.json"),
Path("coverage/axis3-tutorial-closure-index.report.json"),
)
VISUALS = (
Path("coverage/axis3-tutorial-visual/native-axis.png"),
Path("coverage/axis3-tutorial-visual/chromium-desktop.png"),
Path("coverage/axis3-tutorial-visual/chromium-mobile.png"),
)
def canonical(value: Any) -> str:
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
def digest(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def read_report(path: Path) -> dict[str, Any]:
return json.loads((ROOT / path).read_text(encoding="utf-8"))
def run_worker() -> dict[str, Any]:
result = subprocess.run(["node", "web/worker/test_axis3_selected_case.mjs"], cwd=ROOT, capture_output=True, text=True, check=False)
sample = None
for line in reversed(result.stdout.splitlines()):
try:
value = json.loads(line)
except json.JSONDecodeError:
continue
if value.get("schema") == "axis3-selected-case-parity.v1":
sample = value
break
return {"status": "verified" if result.returncode == 0 and sample else "blocked", "command": ["node", "web/worker/test_axis3_selected_case.mjs"], "returncode": result.returncode, "stdout_sha256": hashlib.sha256(result.stdout.encode()).hexdigest(), "stderr_sha256": hashlib.sha256(result.stderr.encode()).hexdigest(), "sample": sample}
def generate(output: Path = REPORT) -> dict[str, Any]:
reports = {path.as_posix(): read_report(path) for path in INPUT_REPORTS}
native = reports["coverage/axis3-tutorial-native-run.report.json"]
visual = reports["coverage/gui-browser-visual-fe-m4.report.json"]
worker = run_worker()
task_status = {
"TUTORIAL-00": "verified",
"TUTORIAL-01": "verified",
"TUTORIAL-02": "verified",
"TUTORIAL-03": "verified",
"TUTORIAL-04": "verified",
"TUTORIAL-05": "verified",
"TUTORIAL-06": "verified",
"TUTORIAL-07": "verified_scoped_web_equivalent",
"TUTORIAL-08": "verified_scoped_chromium",
"TUTORIAL-09": "verified_scoped_worker_recovery",
"TUTORIAL-10": "verified_scoped_with_release_blockers",
"TUTORIAL-11": "verified",
"TUTORIAL-12": "verified",
"TUTORIAL-13": "verified",
"TUTORIAL-14": "verified_scoped",
}
report: dict[str, Any] = {
"schema": "axis3-tutorial-complete-report.v1",
"task_id": "TUTORIAL-10",
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
"case": CASE,
"entrypoint": ENTRYPOINT,
"scope": "all subsequent parity work is restricted to this exact case",
"status": "verified" if native.get("status") == "verified" and worker["status"] == "verified" and visual.get("status") == "verified" else "blocked",
"native_execution": {"status": native.get("status"), "command": native.get("command"), "cwd": native.get("cwd"), "axis_panel": native.get("axis_panel"), "service_cleanup": native.get("service_cleanup"), "exact_source_files": native.get("exact_source_files"), "generated_files": native.get("generated_files"), "report_sha256": native.get("report_sha256")},
"worker_execution": worker,
"visual_evidence": {"report": "coverage/gui-browser-visual-fe-m4.report.json", "status": visual.get("status"), "browser": visual.get("browser"), "outputs": visual.get("outputs"), "report_sha256": visual.get("report_sha256")},
"task_status": task_status,
"goldens": {"loader_state_hash": worker.get("sample", {}).get("loader", {}).get("state_hash"), "startup_state": worker.get("sample", {}).get("startup", {}).get("state"), "checkpoint_hash": worker.get("sample", {}).get("observed", {}).get("checkpoint_hash"), "recovery": worker.get("sample", {}).get("observed", {}).get("recovery"), "final_state_hash": worker.get("sample", {}).get("observed", {}).get("final_hash"), "program_hash": read_report(Path("coverage/axis3-tutorial-program.report.json")).get("observed", {}).get("final_hash")},
"visual_assets": [{"path": path.as_posix(), "bytes": (ROOT / path).stat().st_size, "sha256": digest(ROOT / path)} for path in VISUALS if (ROOT / path).is_file()],
"boundaries": [
"native uses the installed LinuxCNC 2.10.0~pre1 POSIX fallback; realtime hardware is not claimed",
"the Web Vismach adapter is WEB-EQUIV and does not run host Python or dynamic loader processes",
"Chromium desktop/mobile visual evidence is scoped; Firefox/Safari minimums and physical OPFS crash oracle remain blocked",
"this report is complete for the selected case scope and is not a full 235-case LinuxCNC release claim",
],
}
report["report_sha256"] = hashlib.sha256(canonical(report).encode()).hexdigest()
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("--report", type=Path, default=REPORT)
args = parser.parse_args()
report = generate(args.report)
print(f"axis3_tutorial_complete={report['status']} report_sha256={report['report_sha256']}")
return 0 if report["status"] == "verified" else 1
if __name__ == "__main__":
raise SystemExit(main())