93 lines
5.8 KiB
Python
93 lines
5.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Aggregate case-level AXIS parity evidence without hiding blocked families."""
|
|
|
|
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/axis-case-parity.report.json"
|
|
MATRIX = ROOT / "coverage/sim-case-matrix-s8-06.report.json"
|
|
|
|
|
|
def canonical(value: Any) -> str:
|
|
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
|
|
|
|
|
def file_hash(path: Path) -> str | None:
|
|
return hashlib.sha256(path.read_bytes()).hexdigest() if path.is_file() else None
|
|
|
|
|
|
def report(path: str) -> dict[str, Any]:
|
|
value = json.loads((ROOT / path).read_text(encoding="utf-8"))
|
|
return {"path": path, "schema": value.get("schema"), "task_id": value.get("task_id"), "status": value.get("status"), "sha256": file_hash(ROOT / path)}
|
|
|
|
|
|
def case_row(path: str, family: str, status: str, evidence: list[str], reason: str | None = None) -> dict[str, Any]:
|
|
result: dict[str, Any] = {"path": path, "family": family, "status": status, "source_sha256": file_hash(ROOT / path), "evidence": evidence}
|
|
if reason: result["reason"] = reason
|
|
return result
|
|
|
|
|
|
def generate() -> dict[str, Any]:
|
|
matrix = json.loads(MATRIX.read_text(encoding="utf-8"))
|
|
sample = matrix.get("execution", {}).get("sample", {}).get("cases", [])
|
|
by_path = {row.get("path"): row for row in sample if isinstance(row, dict)}
|
|
selected = [
|
|
case_row("linuxcnc/configs/sim/axis/minimal_xyz.ini", "cartesian/trivkins", "verified", ["coverage/minimal-xyz-vertical-s8-04.report.json", "AXIS-CTRL-01..08", "AXIS-VIEW-01..04"]),
|
|
case_row("linuxcnc/configs/sim/axis/corexy/corexy_by_kins.ini", "corexy/kins", "verified", ["coverage/corexy-dual-path-s8-05.report.json", "AXIS-VIEW-01..04"]),
|
|
case_row("linuxcnc/configs/sim/axis/corexy/corexy_by_hal.ini", "corexy/hal", "verified", ["coverage/corexy-dual-path-s8-05.report.json", "AXIS-VIEW-01..04"]),
|
|
case_row("linuxcnc/configs/sim/axis/ldelta.ini", "lineardelta", "verified", ["coverage/machine-families-s8-08.report.json", "AXIS-VIEW-01..04"]),
|
|
case_row("linuxcnc/configs/sim/axis/rdelta.ini", "rotarydelta", "verified", ["coverage/machine-families-s8-08.report.json", "AXIS-VIEW-01..04"]),
|
|
]
|
|
blocked_paths = [
|
|
"linuxcnc/configs/sim/axis/lathe.ini",
|
|
"linuxcnc/configs/sim/axis/lathe-fanucy/lathe-fanucy.ini",
|
|
"linuxcnc/configs/sim/axis/gantry/gantry.ini",
|
|
"linuxcnc/configs/sim/axis/gantry/gantry_mm.ini",
|
|
"linuxcnc/configs/sim/axis/vismach/5axis/bridgemill/5axis.ini",
|
|
"linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
|
]
|
|
blocked = []
|
|
for path in blocked_paths:
|
|
row = by_path.get(path, {})
|
|
blocked.append(case_row(path, "lathe/gantry/vismach-5axis", "blocked", ["coverage/sim-case-matrix-s8-06.report.json"], ", ".join(row.get("missing", [])) or "resource/startup matrix blocker"))
|
|
frontend = ["coverage/axis-control.report.json", "coverage/axis-view-edit.report.json", "coverage/gui-browser-visual-fe-m4.report.json"]
|
|
report_data: dict[str, Any] = {
|
|
"schema": "axis-case-parity-report.v1",
|
|
"task_id": "AXIS-CASE-01..04",
|
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
|
"status": "partial",
|
|
"implementation_status": "representative_cases_verified_remaining_families_blocked",
|
|
"cases": selected + blocked,
|
|
"family_summary": {
|
|
"cartesian_trivkins": {"status": "verified", "cases": 1},
|
|
"corexy_dual_path": {"status": "verified", "cases": 2},
|
|
"delta_representatives": {"status": "verified", "cases": 2},
|
|
"lathe_gantry_vismach_5axis": {"status": "blocked", "cases": len(blocked), "reason": "exact resource/startup blockers remain in S8-06; official kinematics oracle separately blocked"},
|
|
},
|
|
"evidence_reports": [report(path) for path in ["coverage/minimal-xyz-vertical-s8-04.report.json", "coverage/corexy-dual-path-s8-05.report.json", "coverage/machine-families-s8-08.report.json", "coverage/sim-case-matrix-s8-06.report.json", *frontend]],
|
|
"checks": {
|
|
"case_01_minimal_xyz": {"status": "verified", "endpoint_recovery": True, "controls": True, "frontend_authority": True},
|
|
"case_02_corexy_dual_path": {"status": "verified", "kins": True, "hal": True, "mode_parity": True, "recovery_parity": True},
|
|
"case_03_lathe_gantry": {"status": "blocked", "resource_matrix": "BLOCKED_RESOURCE", "oracle": "separate"},
|
|
"case_04_vismach_5axis": {"status": "blocked", "resource_matrix": "BLOCKED_RESOURCE", "oracle": "official_kinematics_oracle_blocked"},
|
|
"exact_paths": {"status": "verified", "basename_substitution": False, "matrix_case_count": matrix.get("counts", {})},
|
|
"frontend_mapping": {"status": "verified", "reports": frontend, "worker_snapshot_owner": True},
|
|
"blocked_visibility": {"status": "verified", "resource_and_oracle_blockers_retained": True},
|
|
},
|
|
"boundaries": ["verified cases are representative, not full 235-case acceptance", "lathe/gantry/VisMach/5-axis remain resource/oracle blocked", "no native oracle is inferred from Worker parity", "physical hardware and host GUI remain excluded"],
|
|
}
|
|
report_data["report_sha256"] = hashlib.sha256(canonical(report_data).encode()).hexdigest()
|
|
REPORT.parent.mkdir(parents=True, exist_ok=True)
|
|
REPORT.write_text(json.dumps(report_data, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
|
|
return report_data
|
|
|
|
|
|
if __name__ == "__main__":
|
|
result = generate()
|
|
print(f"axis_case_parity={result['status']} verified={sum(row['status']=='verified' for row in result['cases'])} blocked={sum(row['status']=='blocked' for row in result['cases'])} report_sha256={result['report_sha256']}")
|