Complete 3axis tutorial native and Web/WASM parity tools
This commit is contained in:
114
scripts/generate_wasm_only_source_audit.py
Normal file
114
scripts/generate_wasm_only_source_audit.py
Normal file
@@ -0,0 +1,114 @@
|
||||
#!/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())
|
||||
Reference in New Issue
Block a user