#!/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']}")