73 lines
4.0 KiB
Python
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']}")
|