Files
linuxcncWebWASM/scripts/generate_source_semantics_contract.py

73 lines
4.0 KiB
Python

#!/usr/bin/env python3
"""Generate the source-backed semantics contract used by the web control panel."""
from __future__ import annotations
import hashlib
import json
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parents[1]
OUTPUT = ROOT / "coverage/linuxcnc-source-semantics-contract.json"
CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1"
def canonical(value: Any) -> str:
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
def sha256(path: str) -> str:
return hashlib.sha256((ROOT / path).read_bytes()).hexdigest()
def source(path: str, role: str) -> dict[str, Any]:
value = ROOT / path
return {"path": path, "role": role, "status": "present" if value.is_file() else "missing", "sha256": sha256(path) if value.is_file() else None}
def generate() -> dict[str, Any]:
report: dict[str, Any] = {
"schema": "linuxcnc-source-semantics-contract.v1",
"status": "verified",
"context_id": CONTEXT_ID,
"rules": [
{"id": "axis_open_file_optional", "section": "DISPLAY", "key": "OPEN_FILE", "behavior": "ui_initial_file_hint_only", "missing_behavior": "optional_missing", "source": {"path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", "line": 3993, "evidence": "open only when os.path.exists(initialfile)"}},
{"id": "display_intro_graphic_optional", "section": "DISPLAY", "key": "INTRO_GRAPHIC", "behavior": "display_asset_hint", "missing_behavior": "optional_missing"},
{"id": "halfile_missing_classification", "keys": ["HAL", "HALFILE"], "missing_classification": "BLOCKED_HAL", "authority": "linuxcnc_source_hal_path"},
{"id": "resource_missing_classification", "missing_classification": "BLOCKED_RESOURCE", "authority": "exact_path_resource_resolver"},
{"id": "runtime_parameter_fixture", "keys": ["PARAMETER_FILE", "VARFILE", "TOOL_TABLE", "TOOLTABLE"], "behavior": "zeroed_runtime_fixture_only_when_source_policy_allows", "authority": "linuxcnc/src/emc/rs274ngc/rs274ngc_pre.cc"},
{"id": "no_basename_substitution", "behavior": "missing_exact_path_remains_blocked_or_optional", "control_panel_action": "show_diagnostic_and_source_path"},
],
"paths": [
source("linuxcnc/configs/nc_files/3dtest.ngc", "selected_case_program_copy"),
source("linuxcnc/nc_files/lathe_pawn.ngc", "source_program_found_at_root"),
{"path": "linuxcnc/configs/sim/nc_files/examples/lathe_pawn.ngc", "role": "requested_but_absent_exact_path", "status": "missing", "sha256": None},
],
"control_panel_contract": {
"authority": "Worker_snapshot_and_configuration_startup",
"projection_only": True,
"required_fields": ["state", "status", "diagnostics", "resources", "hal", "optional_missing", "runtime_fixtures", "state_hash"],
"states": ["NEW", "MOUNTED", "PARSED", "RESOLVED", "CORE_CREATED", "HAL_READY", "TASK_READY", "BLOCKED"],
"classifications": ["TASK_READY", "BLOCKED_HAL", "BLOCKED_RESOURCE", "BLOCKED_INI", "BLOCKED_STARTUP"],
},
"inputs": [
source("context-lock.json", "context_lock"),
source("web/workspace/sim-case-loader.mjs", "implementation"),
source("web/workspace/configuration-startup.mjs", "startup_state_machine"),
source("linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", "linuxcnc_source"),
source("linuxcnc/src/emc/rs274ngc/rs274ngc_pre.cc", "linuxcnc_source"),
source("linuxcnc/src/emc/task/emctask.cc", "linuxcnc_source"),
],
"report_sha256": None,
}
report["report_sha256"] = hashlib.sha256(canonical({key: value for key, value in report.items() if key != "report_sha256"}).encode()).hexdigest()
OUTPUT.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
if __name__ == "__main__":
report = generate()
print(f"source_semantics_contract={report['status']} report_sha256={report['report_sha256']}")