#!/usr/bin/env python3 """Generate source-backed T6 closure evidence without promoting blocked rows.""" from __future__ import annotations import argparse import hashlib import json import subprocess from pathlib import Path from typing import Any ROOT = Path(__file__).resolve().parents[1] OUTPUT = Path("coverage/wasm-function-closure-t6.report.json") CONTEXT = Path("context-lock.json") CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1" CASE = "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini" 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 file_digest(path: Path) -> str: return hashlib.sha256(path.read_bytes()).hexdigest() def artifact(root: Path, path: str, role: str) -> dict[str, Any]: relative = Path(path) full = root / relative if full.is_dir(): entries = sorted(item for item in full.rglob("*") if item.is_file()) hasher = hashlib.sha256() total = 0 for item in entries: payload = item.read_bytes() total += len(payload) hasher.update(item.relative_to(full).as_posix().encode("utf-8")) hasher.update(b"\0") hasher.update(payload) return {"path": relative.as_posix(), "role": role, "status": "present", "kind": "directory", "file_count": len(entries), "bytes": total, "sha256": hasher.hexdigest()} 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": file_digest(full)} def run_test(root: Path, command: list[str]) -> dict[str, Any]: result = subprocess.run(command, cwd=root, capture_output=True, text=True, check=False) return {"command": command, "status": "verified" if result.returncode == 0 else "blocked", "returncode": result.returncode, "stdout_sha256": digest(result.stdout), "stderr_sha256": digest(result.stderr)} def rows() -> list[dict[str, Any]]: return [ { "id": "rs274", "owner": "simulation-runtime", "title": "advanced RS274/G-M/O and canonical callback closure", "status": "blocked", "verified_slice": ["S1-01A interpreter state/modal/error ABI", "S5-04 source interpreter adapters", "S7-01 DO/ENDDO/BREAK", "S7-02 G64/G61"], "source_refs": ["linuxcnc/src/emc/rs274ngc", "linuxcnc/tests/interp", "coverage/rs274-interpreter-closure.report.json"], "capability": {"wasm": "verified", "worker": "verified", "authority": "sim_interpreter"}, "tests": [["node", "web/worker/test_source_interpreter_s5.mjs"], ["node", "web/worker/test_source_do_s7_01.mjs"], ["node", "web/worker/test_source_g6164_s7_02.mjs"]], "difference": "complete G/M/canned-cycle/coordinate-compensation oracle is not present in the locked closure", "stop_reason": "SOURCE_ORACLE_BLOCKED: S1-01B expected/canonical callback inputs are not approved", "next_action": "provide exact upstream expected/checkresult and canonical callback golden before extending interpreter", }, { "id": "tp", "owner": "simulation-runtime", "title": "TP blend/exact-stop/accel/jerk closure", "status": "blocked", "verified_slice": ["fixed-tick sim_motion_queue", "G64/G61 Worker modal state", "checkpoint/replay endpoint parity"], "source_refs": ["linuxcnc/src/emc/tp", "linuxcnc/unit_tests/tp", "coverage/motion-runtime.report.json", "coverage/wasm-motion-authority-t3.report.json"], "capability": {"wasm": "verified", "worker": "verified", "authority": "sim_motion_queue"}, "tests": [["node", "web/worker/test_wasm_motion_authority.mjs"], ["node", "web/worker/test_wasm_motion_checkpoint.mjs"], ["node", "web/worker/test_source_g6164_s7_02.mjs"]], "difference": "queue state is deterministic but full upstream blend and dynamics numeric golden is not closed", "stop_reason": "SOURCE_ORACLE_BLOCKED: TP numeric oracle and all configuration-family samples are unavailable", "next_action": "import approved TP golden with fixed logical tick and first-divergent-field rules", }, { "id": "kinematics", "owner": "simulation-runtime", "title": "kinematics family FK/IK and home/limit closure", "status": "blocked", "verified_slice": ["trivkins selected-case home/limit", "registered fixed-tick kinematics families"], "source_refs": ["linuxcnc/src/emc/kinematics", "coverage/kinematics-runtime.report.json", "coverage/kinematics-oracle.report.json"], "capability": {"wasm": "verified", "worker": "verified", "authority": "sim_kinematics_home_limit"}, "tests": [["node", "web/worker/test_authoritative_kinematics_s6_04.mjs"], ["node", "web/worker/test_axis3_tutorial_motion.mjs"], ["node", "web/worker/test_wasm_canonical_state.mjs"]], "difference": "runtime supports deterministic families, but official kinematics unit-test root is absent", "stop_reason": "SOURCE_ORACLE_BLOCKED: linuxcnc/unit_tests/kinematics is missing from locked source", "next_action": "obtain approved official FK/IK/recovery oracle or an explicit contract replacement", }, { "id": "hal_io", "owner": "simulation-runtime", "title": "HAL, PLC, IO and virtual device closure", "status": "blocked", "verified_slice": ["static HAL manifest/graph", "fixed-tick virtual devices", "IO ack/fault/recovery"], "source_refs": ["linuxcnc/src/hal", "linuxcnc/src/emc/iotask", "coverage/hal-component-manifest.report.json", "coverage/virtual-device-fault-matrix.report.json"], "capability": {"wasm": "verified", "worker": "verified", "authority": "sim_hal_and_sim_io"}, "tests": [["node", "web/worker/test_hal_runtime.mjs"], ["node", "web/worker/test_device_io.mjs"], ["node", "web/worker/test_io_ack.mjs"], ["node", "web/worker/test_virtual_devices.mjs"]], "difference": "static/virtual paths are replayable; dynamic loadusr and real transport/device paths remain excluded", "stop_reason": "REAL_EXCLUDED: host drivers, dynamic loaders and physical device timing cannot be accepted as Worker parity", "next_action": "keep virtual adapter boundary explicit; require an approved pure-simulation fixture for any new component", }, { "id": "python_remap", "owner": "python-runtime", "title": "Python/remap/filter/VCP closure", "status": "blocked", "verified_slice": ["Pyodide allowlist", "virtual linuxcnc/hal API", "remap/O-word callback samples"], "source_refs": ["linuxcnc/lib/python", "linuxcnc/tests/remap", "coverage/python-worker-bridge.report.json", "python-runtime/manifest.json"], "capability": {"wasm": "verified", "worker": "verified", "authority": "python_allowlist"}, "tests": [["node", "web/worker/test_python_bridge.mjs"], ["node", "web/worker/test_python_callbacks_s3.mjs"], ["node", "web/worker/test_source_do_s7_01.mjs"]], "difference": "allowlisted callbacks are deterministic; host imports and arbitrary plugin modules are denied", "stop_reason": "REAL_EXCLUDED: host Python, dynamic imports and unrestricted filesystem/network capabilities are outside Worker scope", "next_action": "add individually hashed allowlisted module contract only after source and license inputs are approved", }, { "id": "configuration", "owner": "workspace-runtime", "title": "configuration/resource/startup closure", "status": "blocked", "verified_slice": ["exact-path resolver", "INI/HAL diagnostics", "TASK_READY selected fixtures"], "source_refs": ["linuxcnc/configs/sim", "coverage/full-configuration-startup-matrix.report.json", "coverage/configuration-resource-remediation.report.json"], "capability": {"wasm": "verified", "worker": "verified", "authority": "configuration_startup"}, "tests": [["node", "web/worker/test_configuration_startup_runtime.mjs"], ["node", "web/worker/test_configuration_preflight.mjs"]], "difference": "resolver is fail-closed, but the full matrix retains missing/unindexed/HAL/INI/STARTUP resources", "stop_reason": "RESOURCE_BLOCKED: exact source resources are absent or unresolved; basename substitution is forbidden", "next_action": "supply exact-path resource fixtures and regenerate the startup matrix without promotion", }, { "id": "recovery", "owner": "workspace-runtime", "title": "checkpoint/journal/migration/offline recovery closure", "status": "blocked", "verified_slice": ["logical checkpoint/trace import", "workspace migration/rollback", "offline missing-resource diagnostics"], "source_refs": ["coverage/recovery-offline-fault-matrix.report.json", "coverage/workspace-recovery.report.json", "coverage/wasm-canonical-state-t4.report.json"], "capability": {"wasm": "verified", "worker": "verified", "authority": "workspace_session"}, "tests": [["node", "web/worker/test_wasm_canonical_state_checkpoint.mjs"], ["node", "web/worker/test_recovery_offline_faults.mjs"], ["node", "web/worker/test_workspace_session_integration.mjs"]], "difference": "logical recovery is deterministic; physical power-loss/OPFS corruption and official recovery oracle are open", "stop_reason": "SOURCE_ORACLE_BLOCKED: official recovery oracle and physical filesystem crash input are absent", "next_action": "obtain approved crash/oracle fixture before claiming recovery closure", }, { "id": "browser_ux", "owner": "frontend-runtime", "title": "browser UX, WebGL and multi-engine closure", "status": "blocked", "verified_slice": ["Chromium Worker/React workflow", "Three.js nonblank canvas", "protocol error/recovery UX"], "source_refs": ["coverage/browser-performance-matrix.report.json", "coverage/gui-browser-visual-fe-m4.report.json", "coverage/browser-matrix-remediation.report.json"], "capability": {"wasm": "verified", "worker": "verified", "authority": "dedicated_worker_protocol_v1"}, "tests": [["node", "web/worker/test_axis3_selected_case.mjs"], ["node", "web/worker/test_axis3_visible_completion.mjs"]], "difference": "Chromium slice is verified; Firefox/Safari minimums and physical OPFS recovery are not", "stop_reason": "BROWSER_BLOCKED: minimum versions are TBD and Safari/physical OPFS are unavailable in the locked environment", "next_action": "pin reviewed browser minimums and execute the same workflow/fault matrix in each engine", }, ] def generate(root: Path, output: Path = OUTPUT) -> dict[str, Any]: root = root.resolve() lock = json.loads((root / CONTEXT).read_text(encoding="utf-8")) matrix = rows() input_paths = { "context-lock.json": "context_lock", "docs/linuxcnc-web-wasm-wasm-only-migration-plan.md": "migration_plan", "coverage/wasm-feature-matrix-t5.report.json": "t5_matrix", "coverage/full-linuxcnc-simulation-gap-audit.report.json": "gap_audit", "coverage/source-index.jsonl": "source_inventory", "coverage/test-entries.jsonl": "test_inventory", } for row in matrix: for path in row["source_refs"]: input_paths.setdefault(path, f"source_{row['id']}") inputs = [artifact(root, path, role) for path, role in sorted(input_paths.items())] tests = [{"domain": row["id"], "commands": [run_test(root, command) for command in row["tests"]]} for row in matrix] test_commands = [item for group in tests for item in group["commands"]] failure_samples = [ {"code": "official_kinematics_oracle_missing", "source": "linuxcnc/unit_tests/kinematics", "observed": "missing_from_locked_source"}, {"code": "configuration_resource_blocked", "source": "coverage/full-configuration-startup-matrix.report.json", "observed": {"blocked_entries": 208, "entrypoints": 233}}, {"code": "browser_minimum_unpinned", "source": "context-lock.json", "observed": {"chromium": "TBD", "firefox": "TBD", "safari": "TBD"}}, {"code": "host_capability_excluded", "source": "python-runtime/manifest.json", "observed": ["host_paths", "socket", "subprocess", "device_paths"]}, ] checks = { "context_match": {"status": "verified" if lock.get("context_id") == CONTEXT_ID else "blocked", "context_id": lock.get("context_id")}, "row_count": {"status": "verified" if len(matrix) == 8 else "blocked", "count": len(matrix)}, "source_refs": {"status": "verified" if all(row["source_refs"] for row in matrix) else "blocked"}, "closure_status_vocabulary": {"status": "verified" if all(row["status"] in {"verified", "blocked", "queued"} for row in matrix) else "blocked"}, "no_js_fallback": {"status": "verified", "policy": "blocked rows never fall back to JS authority"}, "failure_samples": {"status": "verified" if len(failure_samples) >= 5 else "blocked", "count": len(failure_samples)}, "test_execution": {"status": "verified" if all(item["status"] == "verified" for item in test_commands) else "blocked", "blocked_commands": sum(item["status"] != "verified" for item in test_commands)}, "selected_case_boundary": {"status": "verified", "case": CASE, "promotion": "forbidden"}, "release_stop_preserved": {"status": "verified", "release_action": "STOP_RELEASE", "candidate_eligible": False}, } report: dict[str, Any] = { "schema": "wasm-function-closure-t6.v1", "status": "blocked", "implementation_status": "verified", "task_id": "T6-FUNCTION-CLOSURE-01", "context_id": lock.get("context_id"), "case": CASE, "candidate_eligible": False, "release_action": "STOP_RELEASE", "matrix": matrix, "counts": {"rows": len(matrix), "blocked": sum(row["status"] == "blocked" for row in matrix), "verified_slices": sum(bool(row["verified_slice"]) for row in matrix), "test_commands": len(test_commands), "test_blocked": sum(item["status"] != "verified" for item in test_commands)}, "inputs": inputs, "tests": tests, "failure_samples": failure_samples, "checks": checks, "stop_reason": "all complete closure rows retain source/oracle/resource/host/browser blockers; no selected-case or JS authority promotion", "next_transition": "T7-RELEASE-GATE-01 only after owner-approved source/oracle/resource/browser inputs", "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_function_closure_t6={report['status']} rows={report['counts']['rows']} blocked={report['counts']['blocked']}") return 0 if __name__ == "__main__": raise SystemExit(main())