Files
linuxcncWebWASM/scripts/generate_wasm_simulation_release_gate_t7.py

136 lines
8.1 KiB
Python

#!/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())