115 lines
5.3 KiB
Python
115 lines
5.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Audit the WASM-only source boundary without promoting partial parity."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
import re
|
|
import subprocess
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
REPORT = ROOT / "coverage/wasm-only-source-audit.report.json"
|
|
SCHEMA = "wasm-only-source-audit.v1"
|
|
|
|
WASM_MANIFEST = ROOT / "build-wasm/build.ninja"
|
|
JS_AUTHORITY = (
|
|
("web/worker/authoritative-runtime.mjs", "compatibility planner and pose mirror for non-selected cases", "compatibility_open"),
|
|
("web/worker/program-runner.mjs", "program scheduling and control flow mirror; canonical segments enter WASM queue", "open"),
|
|
("web/worker/motion-state.mjs", "home/jog/limit projection pending T4 canonical state", "open"),
|
|
("web/worker/sim-task.mjs", "task state mirror pending T4 canonical state", "open"),
|
|
)
|
|
BASELINES = (
|
|
("preview", ["python3", "scripts/test_axis3_tutorial_preview.py"]),
|
|
("selected_case_worker", ["node", "web/worker/test_axis3_selected_case.mjs"]),
|
|
("visible_completion_worker", ["node", "web/worker/test_axis3_visible_completion.mjs"]),
|
|
("wasm_motion_event", ["node", "web/worker/test_wasm_motion_event.mjs"]),
|
|
("wasm_motion_authority", ["node", "web/worker/test_wasm_motion_authority.mjs"]),
|
|
("wasm_motion_checkpoint", ["node", "web/worker/test_wasm_motion_checkpoint.mjs"]),
|
|
("wasm_canonical_state", ["node", "web/worker/test_wasm_canonical_state.mjs"]),
|
|
("wasm_canonical_state_checkpoint", ["node", "web/worker/test_wasm_canonical_state_checkpoint.mjs"]),
|
|
("react_typecheck", ["npm", "run", "typecheck"], ROOT / "web/react"),
|
|
)
|
|
|
|
|
|
def sha256(path: Path) -> str:
|
|
return hashlib.sha256(path.read_bytes()).hexdigest()
|
|
|
|
|
|
def canonical(value: Any) -> str:
|
|
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
|
|
|
|
|
def wasm_exports() -> dict[str, Any]:
|
|
text = WASM_MANIFEST.read_text(encoding="utf-8", errors="replace") if WASM_MANIFEST.is_file() else ""
|
|
names = sorted(set(re.findall(r"'_((?:sim|malloc|free)[A-Za-z0-9_]+)'", text)))
|
|
return {
|
|
"manifest": WASM_MANIFEST.as_posix(),
|
|
"manifest_sha256": sha256(WASM_MANIFEST) if WASM_MANIFEST.is_file() else None,
|
|
"count": len(names),
|
|
"names": names,
|
|
"domains": {
|
|
"interpreter": [name for name in names if "interpreter" in name],
|
|
"motion": [name for name in names if any(token in name for token in ("motion", "tp_", "kinematics_motion"))],
|
|
"task_scheduler": [name for name in names if any(token in name for token in ("task", "scheduler"))],
|
|
"hal_io": [name for name in names if any(token in name for token in ("hal", "device", "io_"))],
|
|
},
|
|
}
|
|
|
|
|
|
def js_authority() -> list[dict[str, Any]]:
|
|
rows = []
|
|
for path, reason, status in JS_AUTHORITY:
|
|
absolute = ROOT / path
|
|
rows.append({"path": path, "sha256": sha256(absolute) if absolute.is_file() else None, "reason": reason, "status": status})
|
|
return rows
|
|
|
|
|
|
def run_baselines() -> list[dict[str, Any]]:
|
|
rows = []
|
|
for name, command, *working_dir in BASELINES:
|
|
cwd = working_dir[0] if working_dir else ROOT
|
|
result = subprocess.run(command, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False)
|
|
rows.append({
|
|
"name": name,
|
|
"command": command,
|
|
"cwd": cwd.as_posix(),
|
|
"returncode": result.returncode,
|
|
"status": "verified" if result.returncode == 0 else "blocked",
|
|
"output_sha256": hashlib.sha256(result.stdout.encode()).hexdigest(),
|
|
"output_tail": result.stdout[-1200:],
|
|
})
|
|
return rows
|
|
|
|
|
|
def main() -> int:
|
|
report: dict[str, Any] = {
|
|
"schema": SCHEMA,
|
|
"status": "blocked",
|
|
"source_of_truth_target": "linuxcnc_source_to_wasm_core_only",
|
|
"selected_case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini",
|
|
"selected_case_motion_authority": "verified",
|
|
"selected_case_motion_source": "wasm_motion_queue",
|
|
"selected_case_checkpoint_restore": "verified",
|
|
"wasm_exports": wasm_exports(),
|
|
"js_authority_remaining": js_authority(),
|
|
"baseline": run_baselines(),
|
|
"open_tasks": ["T1_FULL_CANONICAL_CALLBACKS", "T2_FULL_WASM_PREVIEW", "T3_NON_SELECTED_MOTION_AUTHORITY", "T4_HAL_IO_TOOL_PROBE_CANONICAL_STATE", "T5_FULL_MATRIX"],
|
|
"completed_slices": ["T1_INTERPRETER_MOTION_EVENT", "T2_SELECTED_CASE_WASM_PREVIEW", "T3_WASM_MOTION_QUEUE_SNAPSHOT", "T3_SELECTED_MOTION_AUTHORITY", "T3_WASM_MOTION_CHECKPOINT", "T4_SELECTED_TASK_CANONICAL", "T4_SELECTED_HOME_LIMIT_CANONICAL"],
|
|
"selected_case_task_authority": "wasm_task_canonical",
|
|
"selected_case_home_limit_authority": "wasm_home_limit",
|
|
"stop_reason": "selected-case task/motion/home-limit canonical state and queue recovery are verified; HAL/IO/tool/probe, compatibility, non-selected motion authority, and full parity remain open",
|
|
}
|
|
report["report_sha256"] = hashlib.sha256(canonical(report).encode()).hexdigest()
|
|
REPORT.parent.mkdir(parents=True, exist_ok=True)
|
|
REPORT.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
|
|
print(json.dumps(report, ensure_ascii=False))
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|