#!/usr/bin/env python3 """Generate a read-only simulation release-gate audit for T7.""" from __future__ import annotations import argparse import hashlib import json from pathlib import Path from typing import Any ROOT = Path(__file__).resolve().parents[1] OUTPUT = Path("coverage/wasm-simulation-release-gate-t7.report.json") CONTEXT = Path("context-lock.json") CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1" REQUIRED_BLOCKERS = { "manifest_seed_unassigned", "accepted_entries_missing", "official_oracle_missing", "configuration_resources_missing", "browser_matrix_unverified", "license_closure_incomplete", } 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("utf-8")).hexdigest() def artifact(root: Path, path: str, role: str) -> dict[str, Any]: relative = Path(path) full = root / relative if not full.is_file(): return {"path": relative.as_posix(), "role": role, "status": "missing"} return {"path": relative.as_posix(), "role": role, "status": "present", "bytes": full.stat().st_size, "sha256": hashlib.sha256(full.read_bytes()).hexdigest()} def load(root: Path, path: str) -> dict[str, Any]: return json.loads((root / path).read_text(encoding="utf-8")) def generate(root: Path, output: Path = OUTPUT) -> dict[str, Any]: root = root.resolve() lock = load(root, "context-lock.json") t6 = load(root, "coverage/wasm-function-closure-t6.report.json") t5 = load(root, "coverage/wasm-feature-matrix-t5.report.json") gate = load(root, "coverage/release-candidate-gate.report.json") acceptance = load(root, "coverage/full-case-acceptance-s8-10.report.json") roundup = load(root, "coverage/remediation-evidence-roundup.report.json") owner = load(root, "coverage/owner-review-gate-refresh.report.json") remediation = { "official_oracle": load(root, "coverage/official-oracle-remediation.report.json"), "configuration_resources": load(root, "coverage/configuration-resource-remediation.report.json"), "browser_matrix": load(root, "coverage/browser-matrix-remediation.report.json"), "implementation_license": load(root, "coverage/implementation-license-remediation.report.json"), } blocker_ids = {item.get("id") for item in gate.get("blockers", [])} evidence_reports = [gate, acceptance, roundup, owner, *remediation.values()] context_values = {item.get("context_id") for item in [t5, t6, acceptance, roundup, owner, *remediation.values()] if item.get("context_id")} inputs = [ artifact(root, "context-lock.json", "context_lock"), artifact(root, "nextTask.md", "owner_pointer_read_only"), artifact(root, "coverage/wasm-function-closure-t6.report.json", "t6_closure"), artifact(root, "coverage/wasm-feature-matrix-t5.report.json", "t5_matrix"), artifact(root, "coverage/release-candidate-gate.report.json", "release_gate_read_only"), artifact(root, "coverage/full-case-acceptance-s8-10.report.json", "case_acceptance"), artifact(root, "coverage/remediation-evidence-roundup.report.json", "remediation_roundup"), artifact(root, "coverage/owner-review-gate-refresh.report.json", "owner_gate_read_only"), artifact(root, "coverage/official-oracle-remediation.report.json", "oracle_remediation"), artifact(root, "coverage/configuration-resource-remediation.report.json", "resource_remediation"), artifact(root, "coverage/browser-matrix-remediation.report.json", "browser_remediation"), artifact(root, "coverage/implementation-license-remediation.report.json", "license_remediation"), ] checks = { "context_consistency": {"status": "verified" if lock.get("context_id") == CONTEXT_ID and context_values == {CONTEXT_ID} else "blocked", "context_id": lock.get("context_id")}, "t6_dependency_recorded": {"status": "verified" if t6.get("task_id") == "T6-FUNCTION-CLOSURE-01" and t6.get("status") == "blocked" else "blocked", "closure_status": t6.get("status")}, "candidate_not_eligible": {"status": "verified" if all(item.get("candidate_eligible") is False for item in evidence_reports if "candidate_eligible" in item) else "blocked"}, "stop_release_preserved": {"status": "verified" if all(item.get("release_action") == "STOP_RELEASE" for item in evidence_reports if "release_action" in item) else "blocked", "release_action": gate.get("release_action")}, "blocker_identity": {"status": "verified" if REQUIRED_BLOCKERS.issubset(blocker_ids) and set(roundup.get("checks", {}).get("blocker_identity_preserved", {}).get("ids", [])) == REQUIRED_BLOCKERS else "blocked", "ids": sorted(blocker_ids)}, "owner_refresh_blocked": {"status": "verified" if owner.get("checks", {}).get("gate_refresh_allowed", {}).get("status") == "blocked" and owner.get("checks", {}).get("owner_inputs_present", {}).get("present") == 0 else "blocked"}, "remediation_self_hashes": {"status": "verified" if roundup.get("checks", {}).get("remediation_report_self_hashes", {}).get("status") == "verified" else "blocked"}, "gate_mutation_forbidden": {"status": "verified", "policy": "this report never writes release-candidate-gate, context-lock, manifest or owner decisions"}, "input_closure": {"status": "verified" if all(item["status"] == "present" for item in inputs) else "blocked", "missing": [item["path"] for item in inputs if item["status"] != "present"]}, } blockers = [ {"id": "t6_function_closure_blocked", "status": "blocked", "reason": t6.get("stop_reason")}, *[item for item in gate.get("blockers", []) if item.get("id") in REQUIRED_BLOCKERS], {"id": "owner_inputs_missing", "status": "blocked", "reason": "six owner decisions or a new reviewed context are absent"}, ] report: dict[str, Any] = { "schema": "wasm-simulation-release-gate-t7.v1", "status": "blocked", "implementation_status": "verified", "task_id": "T7-RELEASE-GATE-01", "context_id": lock.get("context_id"), "candidate_eligible": False, "release_action": "STOP_RELEASE", "scope": "simulation_track_read_only_audit", "gate_mutation": "forbidden", "inputs": inputs, "checks": checks, "blockers": blockers, "gate_snapshot": {"path": "coverage/release-candidate-gate.report.json", "status": gate.get("status"), "candidate_eligible": gate.get("candidate_eligible"), "release_action": gate.get("release_action"), "blocker_count": len(gate.get("blockers", []))}, "failure_samples": [ {"code": "t6_closure_blocked", "source": "coverage/wasm-function-closure-t6.report.json", "observed": t6.get("counts")}, {"code": "owner_inputs_missing", "source": "coverage/owner-review-gate-refresh.report.json", "observed": {"present": owner.get("checks", {}).get("owner_inputs_present", {}).get("present"), "required": owner.get("checks", {}).get("owner_inputs_present", {}).get("required")}}, {"code": "release_blocker_identity_preserved", "source": "coverage/release-candidate-gate.report.json", "observed": sorted(REQUIRED_BLOCKERS)}, ], "next_transition": "wait for approved owner/source/browser/resource inputs; do not auto-refresh K8 gate", "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", encoding="utf-8") return report def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--repo-root", type=Path, default=ROOT) parser.add_argument("--output", type=Path, default=OUTPUT) args = parser.parse_args() report = generate(args.repo_root.resolve(), args.output) print(f"wasm_simulation_release_gate_t7={report['status']} blockers={len(report['blockers'])}") return 0 if __name__ == "__main__": raise SystemExit(main())