Files
linuxcncWebWASM/scripts/generate_full_configuration_startup_matrix.py

104 lines
5.9 KiB
Python

#!/usr/bin/env python3
"""Aggregate the deterministic full sim configuration startup matrix."""
from __future__ import annotations
import argparse
import hashlib
import json
from collections import Counter
from pathlib import Path
from typing import Any
CONTEXT = Path("context-lock.json")
REGRESSION = Path("coverage/configuration-regression.report.json")
STARTUP = Path("coverage/configuration-startup-closure.report.json")
HAL = Path("coverage/hal-component-manifest.report.json")
FRONTEND = Path("coverage/frontend-m4.report.json")
GATE = Path("coverage/release-candidate-gate.report.json")
OUTPUT = Path("coverage/full-configuration-startup-matrix.report.json")
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 artifact(root: Path, path: Path, role: str) -> dict[str, Any]:
full = root / path
if not full.is_file(): return {"path": path.as_posix(), "role": role, "status": "missing"}
return {"path": path.as_posix(), "role": role, "status": "present", "bytes": full.stat().st_size, "sha256": hashlib.sha256(full.read_bytes()).hexdigest()}
def generate(root: Path, output: Path = OUTPUT) -> dict[str, Any]:
root = root.resolve()
lock = json.loads((root / CONTEXT).read_text())
regression = json.loads((root / REGRESSION).read_text())
startup = json.loads((root / STARTUP).read_text())
hal = json.loads((root / HAL).read_text())
frontend = json.loads((root / FRONTEND).read_text())
gate = json.loads((root / GATE).read_text())
rows = regression.get("configuration_entries", [])
startup_rows = startup.get("resources", [])
by_entry = Counter(str(row.get("entrypoint")) for row in startup_rows if row.get("entrypoint"))
matrix = []
for row in rows:
path = str(row.get("path", ""))
blocked = row.get("startup_preflight") != "static_ready"
matrix.append({
"config_id": row.get("config_id"),
"path": path,
"module": row.get("module"),
"family": row.get("family"),
"status": "blocked" if blocked else "ready",
"startup_preflight": row.get("startup_preflight"),
"resource_status": row.get("resource_status"),
"resource_rows": by_entry.get(path, row.get("resource_rows", 0)),
"missing_resources": row.get("missing_resources", []),
"unresolved_resources": row.get("unresolved_resources", []),
"kinematics_registered": row.get("kinematics_registered"),
"source_id": row.get("config_id"),
})
matrix.sort(key=lambda item: str(item.get("config_id", "")))
statuses = Counter(item["status"] for item in matrix)
checks = {
"entry_count": {"status": "verified" if len(matrix) == 264 else "blocked", "entries": len(matrix)},
"entrypoint_count": {"status": "verified" if regression.get("counts", {}).get("config_entrypoints") == 233 else "blocked", "entrypoints": regression.get("counts", {}).get("config_entrypoints")},
"resource_closure": {"status": "verified" if startup.get("implementation_status") == "verified" and startup.get("counts", {}).get("resource_rows") == 16093 else "blocked", "resources": startup.get("counts", {}).get("resource_rows")},
"hal_dependency": {"status": "verified" if hal.get("implementation_status") == "verified" else "blocked"},
"frontend_dependency": {"status": "verified" if frontend.get("implementation_status") == "verified" else "blocked"},
"stable_identity": {"status": "verified" if len({item["config_id"] for item in matrix}) == len(matrix) else "blocked"},
"release_stop_preserved": {"status": "verified" if gate.get("release_action") == "STOP_RELEASE" and gate.get("candidate_eligible") is False else "blocked", "release_action": gate.get("release_action")},
}
ok = all(value["status"] == "verified" for value in checks.values())
report: dict[str, Any] = {
"schema": "full-configuration-startup-matrix.v1",
"context_id": lock.get("context_id"),
"task_id": "REG-01",
"status": "partial" if ok else "blocked",
"implementation_status": "verified" if ok else "blocked",
"candidate_eligible": False,
"release_action": "STOP_RELEASE",
"inputs": [artifact(root, CONTEXT, "context_lock"), artifact(root, REGRESSION, "configuration_regression"), artifact(root, STARTUP, "configuration_startup_closure"), artifact(root, HAL, "hal_component_manifest"), artifact(root, FRONTEND, "frontend_m4"), artifact(root, GATE, "release_candidate_gate")],
"counts": {"registered_entries": len(matrix), "entrypoints": regression.get("counts", {}).get("config_entrypoints"), "statuses": dict(sorted(statuses.items())), "resource_rows": startup.get("counts", {}).get("resource_rows"), "blocked_resource_rows": startup.get("counts", {}).get("blocked")},
"checks": checks,
"matrix": matrix,
"next_transition": "execute REG-03 recovery and offline fault matrix",
"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")
return report
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--output", type=Path, default=OUTPUT)
args = parser.parse_args()
report = generate(args.repo_root.resolve(), args.output)
print(f"full_configuration_startup_matrix={report['status']} entries={report['counts']['registered_entries']} blocked={report['counts']['statuses'].get('blocked', 0)}")
return 0
if __name__ == "__main__": raise SystemExit(main())