104 lines
5.9 KiB
Python
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())
|