Files
linuxcncWebWASM/scripts/generate_axis3_tutorial_closure_index.py

62 lines
3.8 KiB
Python

#!/usr/bin/env python3
"""Freeze the selected-case evidence index without hiding blockers."""
from __future__ import annotations
import hashlib
import json
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parents[1]
REPORT = ROOT / "coverage/axis3-tutorial-closure-index.report.json"
INPUTS = (
Path("coverage/axis3-tutorial-complete.report.json"),
Path("coverage/axis3-tutorial-native-run.report.json"),
Path("coverage/axis3-tutorial-tool-io.report.json"),
Path("coverage/axis3-tutorial-sequence.report.json"),
Path("coverage/axis3-tutorial-persistence.report.json"),
Path("coverage/gui-browser-visual-fe-m4.report.json"),
)
def canonical(value: Any) -> str:
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
def generate() -> dict[str, Any]:
reports = {path.as_posix(): json.loads((ROOT / path).read_text(encoding="utf-8")) for path in INPUTS}
sequence = reports["coverage/axis3-tutorial-sequence.report.json"]
statuses = {path: report.get("status") for path, report in reports.items()}
report: dict[str, Any] = {
"schema": "axis3-tutorial-closure-index.v1",
"task_id": "TUTORIAL-14",
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
"case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial",
"entrypoint": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini",
"scope": "selected-case-only",
"status": "blocked" if sequence.get("status") == "blocked" else "verified",
"implementation_status": "verified_scoped",
"input_reports": [{"path": path.as_posix(), "status": reports[path.as_posix()].get("status"), "report_sha256": reports[path.as_posix()].get("report_sha256")} for path in INPUTS],
"task_status": {"TUTORIAL-00..11": "verified_scoped", "TUTORIAL-12": sequence.get("status"), "TUTORIAL-13": reports["coverage/axis3-tutorial-persistence.report.json"].get("status"), "TUTORIAL-14": "blocked_by_retained_divergence" if sequence.get("status") == "blocked" else "verified"},
"first_divergence": sequence.get("first_divergence"),
"goldens": {"loader": "fnv1a64:4a0e0041bdc491fe", "startup": "fnv1a64:c58e658b6cb3f213", "motion": "fnv1a64:83e51ec6ced82ba7", "program": "fnv1a64:79b6b470739e0e14", "checkpoint": "fnv1a64:10e366f530b2f5cb", "persistence": "fnv1a64:7984fdfabec7d093"},
"visual_assets": ["coverage/axis3-tutorial-visual/native-axis.png", "coverage/axis3-tutorial-visual/chromium-desktop.png", "coverage/axis3-tutorial-visual/chromium-mobile.png"],
"blockers": [
{"id": "native_home_all_semantics", "status": "SOURCE_DEFINED", "reason": "exact INI omits HOME_SEQUENCE; source AXIS semantics use individual Z/X/Y home and recorder follows that path"},
{"id": "manual_toolchange_dialog", "status": "SOURCE_DEFINED_ACK_PIN", "reason": "source hal_manualtoolchange exposes change_button; recorder completes the real HAL handshake through that pin"},
{"id": "release_matrix", "status": "blocked_external", "reason": "Firefox/Safari minimums, OPFS physical crash oracle and full release remain outside selected-case scope"},
],
"statuses": statuses,
"release_action": "STOP_RELEASE",
"boundaries": ["closure is complete only as selected-case implementation evidence", "no blocker is promoted to parity", "all future continuation remains this exact case"],
}
report["report_sha256"] = hashlib.sha256(canonical(report).encode()).hexdigest()
REPORT.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
print(f"axis3_tutorial_closure_index={report['status']} report_sha256={report['report_sha256']}")
return report
if __name__ == "__main__":
raise SystemExit(0 if generate()["status"] in {"verified", "blocked"} else 1)