diff --git a/.gitignore b/.gitignore index 9aba203..0c698cd 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ /build-native/ __pycache__/ *.pyc +web/react/node_modules/ +web/react/dist/ +bak/ diff --git a/README.md b/README.md index b4019a2..5851ab7 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,15 @@ - 路线对齐的 React 前端位于 `web/react`:React + TypeScript + Vite + Tailwind + Radix UI + Three.js,React 只通过 protocol.v1/WorkerClient 访问 Dedicated Worker;FE-M1 构建证据见 `coverage/react-frontend-m1.report.json`。 - AXIS 主界面完全对标的源程序功能清单、菜单/工具栏/状态门控、严格差异流程、任务分解和 M0-M7 验收门槛见 [`LinuxCNC AXIS 完全对标规范与执行计划`](docs/linuxcnc-axis-complete-parity-spec.md)。该文档以锁定的 `linuxcnc/share/axis/tcl/axis.tcl`、Gremlin/GLCanon、原始 bitmap 和 `configs/sim/axis` 为唯一 source oracle。 +## 当前发布范围 + +`coverage/release-authorized-gate.report.json` 是当前 selected-case Web/WASM +仿真包的主动发布 gate,必须通过 `python3 scripts/validate_publish_gate.py`。 +它由用户明确授权的风险豁免驱动,允许发布固定 +`3axis-tutorial` 案例;历史 K8/T7 `STOP_RELEASE` 报告仍保留为技术审计证据, +不被重写成全量 LinuxCNC parity、官方 oracle、Safari 支持、完整配置资源或全仓 +license closure。 + React 前端构建和预览: ```bash diff --git a/coverage/axis3-tutorial-manual-ui-complete.png b/coverage/axis3-tutorial-manual-ui-complete.png new file mode 100644 index 0000000..c71a6ff Binary files /dev/null and b/coverage/axis3-tutorial-manual-ui-complete.png differ diff --git a/coverage/axis3-tutorial-manual-ui-homed.png b/coverage/axis3-tutorial-manual-ui-homed.png new file mode 100644 index 0000000..c71a6ff Binary files /dev/null and b/coverage/axis3-tutorial-manual-ui-homed.png differ diff --git a/coverage/axis3-tutorial-manual-ui-running.png b/coverage/axis3-tutorial-manual-ui-running.png new file mode 100644 index 0000000..c71a6ff Binary files /dev/null and b/coverage/axis3-tutorial-manual-ui-running.png differ diff --git a/coverage/axis3-tutorial-manual-ui-verification.report.json b/coverage/axis3-tutorial-manual-ui-verification.report.json new file mode 100644 index 0000000..e121ee4 --- /dev/null +++ b/coverage/axis3-tutorial-manual-ui-verification.report.json @@ -0,0 +1,81 @@ +{ + "boundaries": [ + "simulation-only", + "visible AXIS UI evidence is combined with native/Worker state evidence", + "no physical hardware claim" + ], + "case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial", + "cross_checks": { + "native_sequence_report": "coverage/axis3-tutorial-native-sequence.report.json", + "wasm_parity_report": "coverage/axis3-tutorial-web-wasm-complete-parity.report.json" + }, + "failure_samples": [], + "input_policy": "Xlib desktop events only; no linuxcnc command/stat API used", + "launcher": "scripts/launch_axis_3dtest_manual.sh", + "launcher_output_excerpt": "", + "launcher_output_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "report_sha256": "f92e4a5a82d0107c694b456bcccc933b8a9ab91288a378584b9b36c8503cd00f", + "schema": "axis3-tutorial-manual-ui-verification.v1", + "screenshots": [ + { + "bytes": 160022, + "path": "coverage/axis3-tutorial-manual-ui.png", + "returncode": 0, + "sha256": "76a0c7ac9f97d008087635033a1a18b01a75f6efd11a46f7a65b4897005cdea5", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "bytes": 162410, + "path": "coverage/axis3-tutorial-manual-ui-homed.png", + "returncode": 0, + "sha256": "72d776a812e21175df939cd84804dc8a452e75efdce2c8b0c0892692f63bed5e", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "bytes": 162410, + "path": "coverage/axis3-tutorial-manual-ui-running.png", + "returncode": 0, + "sha256": "72d776a812e21175df939cd84804dc8a452e75efdce2c8b0c0892692f63bed5e", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "bytes": 162410, + "path": "coverage/axis3-tutorial-manual-ui-complete.png", + "returncode": 0, + "sha256": "72d776a812e21175df939cd84804dc8a452e75efdce2c8b0c0892692f63bed5e", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + ], + "status": "verified", + "transitions": [ + { + "height": 1297, + "state": "WINDOW_VISIBLE", + "title": "3dtest.ngc - AXIS 2.10.0~pre1 on 3axis-tutorial", + "width": 1300 + }, + { + "input": "F1", + "state": "ESTOP_RESET" + }, + { + "input": "F2", + "state": "ON" + }, + { + "input": "Ctrl+Home", + "state": "ALL_HOMED_REQUESTED" + }, + { + "input": "r", + "state": "AUTO_RUN_REQUESTED" + } + ], + "window": { + "class": "axis", + "height": 1297, + "instance": "Axis", + "title": "3dtest.ngc - AXIS 2.10.0~pre1 on 3axis-tutorial", + "width": 1300 + } +} diff --git a/coverage/axis3-tutorial-manual-ui.png b/coverage/axis3-tutorial-manual-ui.png new file mode 100644 index 0000000..4025c79 Binary files /dev/null and b/coverage/axis3-tutorial-manual-ui.png differ diff --git a/coverage/axis3-tutorial-native-3dtest.report.json b/coverage/axis3-tutorial-native-3dtest.report.json new file mode 100644 index 0000000..7f0c7cd --- /dev/null +++ b/coverage/axis3-tutorial-native-3dtest.report.json @@ -0,0 +1,115 @@ +{ + "axis_panel": { + "class": "axis", + "height": 1297, + "instance": "Axis", + "raw": "0x460001f \"3dtest.ngc - AXIS 2.10.0~pre1 on 3axis-tutorial\": (\"axis\" \"Axis\") 1300x1297+28+98 +2930+124", + "screenshot": { + "bytes": 159761, + "path": "/home/mes006/linuxcncWebWASM/coverage/axis3-tutorial-visual/native-3dtest.png", + "returncode": 0, + "sha256": "c55939840cecf2935bea884de9cfbbc72f3eb46b08654a28a3363c5b4f2d3da0", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "title": "3dtest.ngc - AXIS 2.10.0~pre1 on 3axis-tutorial", + "width": 1300, + "window_id": "0x460001f" + }, + "boundaries": [ + "POSIX fallback is native simulation execution, not hardware validation", + "AXIS window evidence is required; timeout/task markers alone are insufficient", + "installed LinuxCNC package version is recorded separately from locked source" + ], + "case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial", + "command": [ + "linuxcnc", + "/3axis-tutorial.ini" + ], + "cwd": "linuxcnc_source_root", + "display": ":1", + "exact_source_files": [ + { + "path": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", + "sha256": "c1e81259a265b1f0c36864b94c31f7f4be95e17bdc452b1cdd9f74b0ac047c3d" + }, + { + "path": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal", + "sha256": "6c01e40c2244d3b534c0d3c60f26a9cdc5232defc79611234c78477209cfda1f" + }, + { + "path": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial", + "sha256": "da5f8266cecd79cdeaa01bf07c4fba17d5c748ccc7dedfc7efdf0354deee9cc7" + }, + { + "path": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/tool.tbl", + "sha256": "6a1736d9a44a30e62d7a8be4df960adc909e7ef026af10ab8e8abf6a613ce4ea" + }, + { + "path": "linuxcnc/configs/nc_files/3dtest.ngc", + "sha256": "fdd71703dc7658711b67de12c89379e3a9340cacb16382dbd655dab2103bf566" + } + ], + "generated_files": [ + "3axis-tutorial_mm.var", + "3axis-tutorial_mm.var.bak", + "position_mm.txt" + ], + "ini_update": { + "command": [ + "update_ini", + "-f", + "/3axis-tutorial.ini" + ], + "output_sha256": "d3277e0d300771ce35b35aef3e3811c084ea1b41ad855b156497a12bbc8cb6dd", + "returncode": 0 + }, + "native_log_excerpt": "", + "native_log_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "process_returncode": 0, + "report_sha256": "4200dbb35f84583982a188bc62b82c538de4f38cb4bd7b790ffe1f711376f747", + "schema": "axis3-tutorial-native-run.v1", + "service_cleanup": { + "commands": [ + { + "command": [ + "axis-remote", + "--quit" + ], + "returncode": 1, + "stdout_sha256": "e887c3a096d768c45173b5087b76b19f871abedface8d6a05f107a2dbb93b3f9" + }, + { + "command": [ + "halcmd", + "stop" + ], + "returncode": 0, + "stdout_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "command": [ + "halcmd", + "unload", + "all" + ], + "returncode": 0, + "stdout_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "command": [ + "/usr/lib/linuxcnc/realtime", + "stop" + ], + "returncode": 0, + "stdout_sha256": "91338655380567a02b2f78410c6af8bb22361915c68b22572d9a09389d9599ab" + } + ], + "killed_pids": [], + "lock_path": "/tmp/linuxcnc.lock", + "lock_removed": false, + "remaining_services": [] + }, + "stale_services_cleaned": [], + "status": "verified", + "termination": "intentional_diagnostic_cleanup_after_visible_window" +} diff --git a/coverage/axis3-tutorial-native-run.report.json b/coverage/axis3-tutorial-native-run.report.json index f2025fa..85bc16a 100644 --- a/coverage/axis3-tutorial-native-run.report.json +++ b/coverage/axis3-tutorial-native-run.report.json @@ -1,19 +1,19 @@ { "axis_panel": { "class": "axis", - "height": 1293, + "height": 1297, "instance": "Axis", - "raw": "0x3a0001f \"AXIS 2.10.0~pre1 on 3axis-tutorial (no file)\": (\"axis\" \"Axis\") 1300x1293+28+98 +2930+124", + "raw": "0x3e0001f \"3dtest.ngc - AXIS 2.10.0~pre1 on 3axis-tutorial\": (\"axis\" \"Axis\") 1300x1297+28+98 +703+241", "screenshot": { - "bytes": 12508, - "path": "/home/mes006/linuxcncWebWASM/coverage/axis3-tutorial-visual/native-axis.png", + "bytes": 162611, + "path": "/home/mes006/linuxcncWebWASM/coverage/axis3-tutorial-native-tool.png", "returncode": 0, - "sha256": "966213a26e5f9713e496580f7217de88facb7f4d68ed9097ae2838c496323807", + "sha256": "458f87cad62d5d86a9319301f66546fc4cb8febaa7ac76d058fa1fe1e78ee0be", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" }, - "title": "AXIS 2.10.0~pre1 on 3axis-tutorial (no file)", + "title": "3dtest.ngc - AXIS 2.10.0~pre1 on 3axis-tutorial", "width": 1300, - "window_id": "0x3a0001f" + "window_id": "0x3e0001f" }, "boundaries": [ "POSIX fallback is native simulation execution, not hardware validation", @@ -30,7 +30,7 @@ "exact_source_files": [ { "path": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", - "sha256": "b801d4fb7a3d66fac7a4202d56c384f75b039f55c70cce64c2975c5454875388" + "sha256": "07e4fafa71e13ac3749fd860ee7549b762f521b9ca58c32110a9f582554882dc" }, { "path": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal", @@ -43,6 +43,10 @@ { "path": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/tool.tbl", "sha256": "6a1736d9a44a30e62d7a8be4df960adc909e7ef026af10ab8e8abf6a613ce4ea" + }, + { + "path": "linuxcnc/configs/nc_files/3dtest.ngc", + "sha256": "fdd71703dc7658711b67de12c89379e3a9340cacb16382dbd655dab2103bf566" } ], "generated_files": [ @@ -56,13 +60,13 @@ "-f", "/3axis-tutorial.ini" ], - "output_sha256": "1c1f893e562d1aa9668bfd69aee2cdc10b3292e426c5eeae301ef1d2935455f9", + "output_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "returncode": 0 }, "native_log_excerpt": "", "native_log_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "process_returncode": 0, - "report_sha256": "4fb09c0be670b6aa456f08e3c80b05c7b0727b6ff0a95be39c88921a30c72970", + "report_sha256": "d90014a6a16d569a8db589fb49a7343646f327de4b3e5d69d31bd14dcaba8785", "schema": "axis3-tutorial-native-run.v1", "service_cleanup": { "commands": [ diff --git a/coverage/axis3-tutorial-native-tool.png b/coverage/axis3-tutorial-native-tool.png new file mode 100644 index 0000000..cd5640d Binary files /dev/null and b/coverage/axis3-tutorial-native-tool.png differ diff --git a/coverage/axis3-tutorial-native-web-tool-comparison.report.json b/coverage/axis3-tutorial-native-web-tool-comparison.report.json new file mode 100644 index 0000000..6e2a718 --- /dev/null +++ b/coverage/axis3-tutorial-native-web-tool-comparison.report.json @@ -0,0 +1,306 @@ +{ + "boundaries": [ + "simulation-only", + "native process uses POSIX non-realtime fallback", + "Web visual uses Chromium SwiftShader", + "real hardware and full LinuxCNC parity are outside selected-case tool comparison" + ], + "case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", + "checks": { + "native_tool_verified": { + "report": "coverage/axis3-tutorial-manual-ui-verification.report.json", + "status": "verified" + }, + "same_case_and_context": { + "case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", + "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", + "native_source_files": "coverage/axis3-tutorial-native-run.report.json", + "program": "linuxcnc/nc_files/3dtest.ngc", + "status": "verified", + "wasm_source_files": "coverage/axis3-tutorial-web-wasm-complete-parity.report.json" + }, + "same_operation_intent": { + "native": [ + "WINDOW_VISIBLE", + "ESTOP_RESET", + "ON", + "ALL_HOMED_REQUESTED", + "AUTO_RUN_REQUESTED" + ], + "status": "verified", + "web_labels": [ + "ready", + "estop", + "power", + "homed", + "running", + "complete" + ] + }, + "visual_evidence_nonempty": { + "native_screenshots": 4, + "status": "verified", + "web_screenshots": 4 + }, + "wasm_acceptance_regression": { + "passed": 20, + "required": 20, + "status": "verified" + }, + "web_tool_verified": { + "report": "coverage/axis3-tutorial-web-manual-verification.report.json", + "status": "verified" + } + }, + "conclusion": "native_and_web_tools_match_selected_case_workflow", + "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", + "report_sha256": "292a386579c313e96e1fc004b22a79605a014714439c8d6ad4be6c34a9aac7d4", + "schema": "axis3-tutorial-native-web-tool-comparison.v1", + "shared_inputs": { + "case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", + "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", + "native_source_files": "coverage/axis3-tutorial-native-run.report.json", + "program": "linuxcnc/nc_files/3dtest.ngc", + "wasm_source_files": "coverage/axis3-tutorial-web-wasm-complete-parity.report.json" + }, + "status": "verified", + "task_id": "AXIS3-NATIVE-WEB-TOOL-COMPARISON-01", + "tools": { + "native": { + "report": "coverage/axis3-tutorial-manual-ui-verification.report.json", + "screenshots": [ + { + "bytes": 160022, + "path": "coverage/axis3-tutorial-manual-ui.png", + "returncode": 0, + "sha256": "76a0c7ac9f97d008087635033a1a18b01a75f6efd11a46f7a65b4897005cdea5", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "bytes": 162410, + "path": "coverage/axis3-tutorial-manual-ui-homed.png", + "returncode": 0, + "sha256": "72d776a812e21175df939cd84804dc8a452e75efdce2c8b0c0892692f63bed5e", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "bytes": 162410, + "path": "coverage/axis3-tutorial-manual-ui-running.png", + "returncode": 0, + "sha256": "72d776a812e21175df939cd84804dc8a452e75efdce2c8b0c0892692f63bed5e", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "bytes": 162410, + "path": "coverage/axis3-tutorial-manual-ui-complete.png", + "returncode": 0, + "sha256": "72d776a812e21175df939cd84804dc8a452e75efdce2c8b0c0892692f63bed5e", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + ], + "tool": "tool/linuxcnc-axis-manual-execution", + "transitions": [ + { + "height": 1297, + "state": "WINDOW_VISIBLE", + "title": "3dtest.ngc - AXIS 2.10.0~pre1 on 3axis-tutorial", + "width": 1300 + }, + { + "input": "F1", + "state": "ESTOP_RESET" + }, + { + "input": "F2", + "state": "ON" + }, + { + "input": "Ctrl+Home", + "state": "ALL_HOMED_REQUESTED" + }, + { + "input": "r", + "state": "AUTO_RUN_REQUESTED" + } + ], + "window": { + "class": "axis", + "height": 1297, + "instance": "Axis", + "title": "3dtest.ngc - AXIS 2.10.0~pre1 on 3axis-tutorial", + "width": 1300 + } + }, + "web": { + "browser": "Google Chrome headless", + "report": "coverage/axis3-tutorial-web-manual-verification.report.json", + "screenshots": [ + { + "bytes": 112654, + "label": "ready", + "path": "coverage/axis3-tutorial-web-ready.png", + "sha256": "8090814b1b5351952822f27d9b5927ae36fa3dd71355eda12127c9525984743f" + }, + { + "bytes": 145174, + "label": "homed", + "path": "coverage/axis3-tutorial-web-homed.png", + "sha256": "127d0ce429d83bf410b7f5461b8223771a1a0374d04ed40a81e005fad8a04fca" + }, + { + "bytes": 144461, + "label": "running", + "path": "coverage/axis3-tutorial-web-running.png", + "sha256": "0ed52562d7f5e07f64743ec85fc1db7cc323eb357a075a2dbd688de205a817fb" + }, + { + "bytes": 144492, + "label": "complete", + "path": "coverage/axis3-tutorial-web-complete.png", + "sha256": "3713af13105514ab8478dcb548d9766361cafb2a3453765e9cc000b1de4ecc78" + } + ], + "states": [ + { + "axis_state": "IDLE", + "canvases": [ + { + "height": 471, + "width": 941 + } + ], + "case_status": "ready to load", + "connection": "READY", + "error_text": "TASK_READYTick 0Hash fnv1a64:034bfe70d581f786Startup resourcesEntrypoint: linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.iniResources: 4 resolved / 0 blockedHAL: 37 records / 0 blockedDisabled operationsrunaxis_taskfile_missingstepaxis_taskfile_missingpauseaxis_pause_state_gateresumeaxis_resume_state_gateabortaxis_stop_state_gaterestartaxis_taskfile_missingSource diagnosticsNo startup diagnostics", + "footer": "Idle", + "href": "http://127.0.0.1:5175/", + "label": "ready", + "machine_state": "Machine ON", + "operation_state": "Operation IDLE", + "position": "X0.000 Y0.000 Z0.000", + "program_status": "idle", + "ready_state": "complete", + "root_html": "
\",\"joint.0.motor-pos-fb\",\"ddt_x.in\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":30,\"command\":\"net\",\"tokens\":[\"Ypos\",\"joint.1.motor-pos-cmd\",\"=>\",\"joint.1.motor-pos-fb\",\"ddt_y.in\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":31,\"command\":\"net\",\"tokens\":[\"Zpos\",\"joint.2.motor-pos-cmd\",\"=>\",\"joint.2.motor-pos-fb\",\"ddt_z.in\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":35,\"command\":\"net\",\"tokens\":[\"Xvel\",\"ddt_x.out\",\"=>\",\"ddt_xv.in\",\"vel_xy.in0\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":36,\"command\":\"net\",\"tokens\":[\"Xacc\",\"<=\",\"ddt_xv.out\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":37,\"command\":\"net\",\"tokens\":[\"Yvel\",\"ddt_y.out\",\"=>\",\"ddt_yv.in\",\"vel_xy.in1\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":38,\"command\":\"net\",\"tokens\":[\"Yacc\",\"<=\",\"ddt_yv.out\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":39,\"command\":\"net\",\"tokens\":[\"Zvel\",\"ddt_z.out\",\"=>\",\"ddt_zv.in\",\"vel_xyz.in0\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":40,\"command\":\"net\",\"tokens\":[\"Zacc\",\"<=\",\"ddt_zv.out\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":43,\"command\":\"net\",\"tokens\":[\"XYvel\",\"vel_xy.out\",\"=>\",\"vel_xyz.in1\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":44,\"command\":\"net\",\"tokens\":[\"XYZvel\",\"<=\",\"vel_xyz.out\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":47,\"command\":\"net\",\"tokens\":[\"estop-loop\",\"iocontrol.0.user-enable-out\",\"iocontrol.0.emc-enable-in\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":49,\"command\":\"loadusr\",\"status\":\"checked\",\"code\":null,\"program\":\"hal_manualtoolchange\",\"capability\":\"SIM-ADAPTER\",\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":50,\"command\":\"net\",\"tokens\":[\"tool-change\",\"iocontrol.0.tool-change\",\"=>\",\"hal_manualtoolchange.change\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":51,\"command\":\"net\",\"tokens\":[\"tool-changed\",\"iocontrol.0.tool-changed\",\"<=\",\"hal_manualtoolchange.changed\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":52,\"command\":\"net\",\"tokens\":[\"tool-number\",\"iocontrol.0.tool-prep-number\",\"=>\",\"hal_manualtoolchange.number\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":53,\"command\":\"net\",\"tokens\":[\"tool-prepare-loopback\",\"iocontrol.0.tool-prepare\",\"=>\",\"iocontrol.0.tool-prepared\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":56,\"command\":\"loadusr\",\"status\":\"checked\",\"code\":null,\"program\":\"./3axis-tutorial\",\"capability\":\"WEB-EQUIV\",\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":59,\"command\":\"net\",\"tokens\":[\"j0\",\"joint.0.pos-fb\",\"=>\",\"3axis-tutorial.jointX\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":60,\"command\":\"net\",\"tokens\":[\"j1\",\"joint.1.pos-fb\",\"=>\",\"3axis-tutorial.jointY\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":61,\"command\":\"net\",\"tokens\":[\"j2\",\"joint.2.pos-fb\",\"=>\",\"3axis-tutorial.jointZ\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":64,\"command\":\"net\",\"tokens\":[\"tool_len\",\"3axis-tutorial.toollength\",\"<=\",\"motion.tooloffset.z\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":65,\"command\":\"net\",\"tokens\":[\"tool_rad\",\"halui.tool.diameter\",\"3axis-tutorial.tooldiameter\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"}]},\"core\":{\"result\":0},\"status\":\"ready\",\"state_hash\":\"fnv1a64:a7f7c05da5d79f9d\"},\"axis_state\":{\"state\":\"ESTOP\",\"machine_state\":\"ESTOP\",\"operation_state\":\"IDLE\",\"source_task_state\":\"STATE_ESTOP\",\"source_interp_state\":\"INTERP_IDLE\",\"source_mode\":\"MANUAL\",\"states\":[\"ESTOP\",\"ESTOP_RESET\",\"OFF\",\"ON\",\"HOME\",\"IDLE\",\"RUNNING\",\"PAUSED\"]},\"gates\":{\"load\":{\"action\":\"load\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"run\":{\"action\":\"run\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"step\":{\"action\":\"step\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"pause\":{\"action\":\"pause\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"resume\":{\"action\":\"resume\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"abort\":{\"action\":\"abort\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"restart\":{\"action\":\"restart\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"mdi\":{\"action\":\"mdi\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"jogStart\":{\"action\":\"jogStart\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"jogStop\":{\"action\":\"jogStop\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"home\":{\"action\":\"home\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"estop\":{\"action\":\"estop\",\"enabled\":true,\"reason\":\"axis_estop_reset_available\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"recover\":{\"action\":\"recover\",\"enabled\":true,\"reason\":\"axis_recover_available\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"override\":{\"action\":\"override\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"reset\":{\"action\":\"reset\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"spindle\":{\"action\":\"spindle\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"coolant\":{\"action\":\"coolant\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"}},\"visual\":{\"schema\":\"axis-visual.v1\",\"pose\":{\"x\":0,\"y\":0,\"z\":0},\"joints\":[0,0,0],\"toolpath\":[{\"id\":1,\"source_line\":5,\"start\":{\"x\":0,\"y\":0,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":25.4,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":0,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":2,\"source_line\":6,\"start\":{\"x\":25.4,\"y\":25.4,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":25.4,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":38.099999999999994,\"y\":38.099999999999994,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":2,\"turn\":-1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":3,\"source_line\":7,\"start\":{\"x\":25.4,\"y\":25.4,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":25.4,\"z\":2.54,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":25.4,\"y\":25.4,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":4,\"source_line\":8,\"start\":{\"x\":25.4,\"y\":25.4,\"z\":2.54,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":44.449999999999996,\"z\":2.54,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":25.4,\"y\":25.4,\"z\":2.54},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":5,\"source_line\":9,\"start\":{\"x\":25.4,\"y\":44.449999999999996,\"z\":2.54,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":44.449999999999996,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":25.4,\"y\":44.449999999999996,\"z\":2.54},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":6,\"source_line\":10,\"start\":{\"x\":25.4,\"y\":44.449999999999996,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":35.559999999999995,\"y\":31.75,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":25.4,\"y\":44.449999999999996,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":7,\"source_line\":11,\"start\":{\"x\":35.559999999999995,\"y\":31.75,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":30.479999999999997,\"y\":38.099999999999994,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":35.559999999999995,\"y\":31.75,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":8,\"source_line\":12,\"start\":{\"x\":30.479999999999997,\"y\":38.099999999999994,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":31.75,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":30.479999999999997,\"y\":38.099999999999994,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":9,\"source_line\":13,\"start\":{\"x\":25.4,\"y\":31.75,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":35.559999999999995,\"y\":44.449999999999996,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":25.4,\"y\":31.75,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":10,\"source_line\":14,\"start\":{\"x\":35.559999999999995,\"y\":44.449999999999996,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":35.559999999999995,\"y\":44.449999999999996,\"z\":2.54,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":35.559999999999995,\"y\":44.449999999999996,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":11,\"source_line\":15,\"start\":{\"x\":35.559999999999995,\"y\":44.449999999999996,\"z\":2.54,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":40.64,\"y\":44.449999999999996,\"z\":2.54,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":35.559999999999995,\"y\":44.449999999999996,\"z\":2.54},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":12,\"source_line\":16,\"start\":{\"x\":40.64,\"y\":44.449999999999996,\"z\":2.54,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":40.64,\"y\":44.449999999999996,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":40.64,\"y\":44.449999999999996,\"z\":2.54},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":13,\"source_line\":17,\"start\":{\"x\":40.64,\"y\":44.449999999999996,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":45.72,\"y\":38.099999999999994,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":40.64,\"y\":44.449999999999996,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":14,\"source_line\":18,\"start\":{\"x\":45.72,\"y\":38.099999999999994,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":50.8,\"y\":44.449999999999996,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":45.72,\"y\":38.099999999999994,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":15,\"source_line\":19,\"start\":{\"x\":50.8,\"y\":44.449999999999996,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":45.72,\"y\":38.099999999999994,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":50.8,\"y\":44.449999999999996,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":16,\"source_line\":20,\"start\":{\"x\":45.72,\"y\":38.099999999999994,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":45.72,\"y\":31.75,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":45.72,\"y\":38.099999999999994,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":17,\"source_line\":21,\"start\":{\"x\":45.72,\"y\":31.75,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":0,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":45.72,\"y\":31.75,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":18,\"source_line\":22,\"start\":{\"x\":0,\"y\":0,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":0,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":0,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":19,\"source_line\":23,\"start\":{\"x\":25.4,\"y\":0,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":0,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":38.099999999999994,\"y\":0,\"z\":38.099999999999994},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":2,\"turn\":-1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":20,\"source_line\":24,\"start\":{\"x\":25.4,\"y\":0,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":2.54,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":25.4,\"y\":0,\"z\":25.4},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":21,\"source_line\":25,\"start\":{\"x\":25.4,\"y\":2.54,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":2.54,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":25.4,\"y\":2.54,\"z\":25.4},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":22,\"source_line\":26,\"start\":{\"x\":25.4,\"y\":2.54,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":0,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":25.4,\"y\":2.54,\"z\":44.449999999999996},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":23,\"source_line\":27,\"start\":{\"x\":25.4,\"y\":0,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":35.559999999999995,\"y\":0,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":25.4,\"y\":0,\"z\":44.449999999999996},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":24,\"source_line\":28,\"start\":{\"x\":35.559999999999995,\"y\":0,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":30.479999999999997,\"y\":0,\"z\":38.099999999999994,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":35.559999999999995,\"y\":0,\"z\":31.75},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":25,\"source_line\":29,\"start\":{\"x\":30.479999999999997,\"y\":0,\"z\":38.099999999999994,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":0,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":30.479999999999997,\"y\":0,\"z\":38.099999999999994},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":26,\"source_line\":30,\"start\":{\"x\":25.4,\"y\":0,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":35.559999999999995,\"y\":0,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":25.4,\"y\":0,\"z\":31.75},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":27,\"source_line\":31,\"start\":{\"x\":35.559999999999995,\"y\":0,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":35.559999999999995,\"y\":2.54,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":35.559999999999995,\"y\":0,\"z\":44.449999999999996},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":28,\"source_line\":32,\"start\":{\"x\":35.559999999999995,\"y\":2.54,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":40.64,\"y\":2.54,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":35.559999999999995,\"y\":2.54,\"z\":44.449999999999996},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":29,\"source_line\":33,\"start\":{\"x\":40.64,\"y\":2.54,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":40.64,\"y\":0,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":40.64,\"y\":2.54,\"z\":44.449999999999996},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":30,\"source_line\":34,\"start\":{\"x\":40.64,\"y\":0,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":50.8,\"y\":0,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":40.64,\"y\":0,\"z\":44.449999999999996},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":31,\"source_line\":35,\"start\":{\"x\":50.8,\"y\":0,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":40.64,\"y\":0,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":50.8,\"y\":0,\"z\":44.449999999999996},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":32,\"source_line\":36,\"start\":{\"x\":40.64,\"y\":0,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":50.8,\"y\":0,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":40.64,\"y\":0,\"z\":31.75},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":33,\"source_line\":37,\"start\":{\"x\":50.8,\"y\":0,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":0,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":50.8,\"y\":0,\"z\":31.75},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":34,\"source_line\":38,\"start\":{\"x\":0,\"y\":0,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":25.4,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":0,\"z\":0},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":35,\"source_line\":39,\"start\":{\"x\":0,\"y\":25.4,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":25.4,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":38.099999999999994,\"z\":38.099999999999994},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":2,\"turn\":-1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":36,\"source_line\":40,\"start\":{\"x\":0,\"y\":25.4,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":2.54,\"y\":25.4,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":25.4,\"z\":25.4},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":37,\"source_line\":41,\"start\":{\"x\":2.54,\"y\":25.4,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":2.54,\"y\":25.4,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":2.54,\"y\":25.4,\"z\":25.4},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":38,\"source_line\":42,\"start\":{\"x\":2.54,\"y\":25.4,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":25.4,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":2.54,\"y\":25.4,\"z\":44.449999999999996},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":39,\"source_line\":43,\"start\":{\"x\":0,\"y\":25.4,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":30.479999999999997,\"z\":38.099999999999994,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":25.4,\"z\":44.449999999999996},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":40,\"source_line\":44,\"start\":{\"x\":0,\"y\":30.479999999999997,\"z\":38.099999999999994,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":35.559999999999995,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":30.479999999999997,\"z\":38.099999999999994},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":41,\"source_line\":45,\"start\":{\"x\":0,\"y\":35.559999999999995,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":30.479999999999997,\"z\":38.099999999999994,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":35.559999999999995,\"z\":44.449999999999996},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":42,\"source_line\":46,\"start\":{\"x\":0,\"y\":30.479999999999997,\"z\":38.099999999999994,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":30.479999999999997,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":30.479999999999997,\"z\":38.099999999999994},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":43,\"source_line\":47,\"start\":{\"x\":0,\"y\":30.479999999999997,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":2.54,\"y\":30.479999999999997,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":30.479999999999997,\"z\":31.75},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":44,\"source_line\":48,\"start\":{\"x\":2.54,\"y\":30.479999999999997,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":2.54,\"y\":40.64,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":2.54,\"y\":30.479999999999997,\"z\":31.75},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":45,\"source_line\":49,\"start\":{\"x\":2.54,\"y\":40.64,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":40.64,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":2.54,\"y\":40.64,\"z\":44.449999999999996},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":46,\"source_line\":50,\"start\":{\"x\":0,\"y\":40.64,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":50.8,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":40.64,\"z\":44.449999999999996},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":47,\"source_line\":51,\"start\":{\"x\":0,\"y\":50.8,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":40.64,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":50.8,\"z\":44.449999999999996},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":48,\"source_line\":52,\"start\":{\"x\":0,\"y\":40.64,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":50.8,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":40.64,\"z\":31.75},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":49,\"source_line\":53,\"start\":{\"x\":0,\"y\":50.8,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":0,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":50.8,\"z\":31.75},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"}],\"active_segment_id\":0,\"source\":\"wasm_canonical_motion\"},\"authoritative\":{\"schema\":\"sim_authoritative_runtime.v1\",\"tick\":0,\"status\":\"idle\",\"program_status\":\"idle\",\"kinematics\":{\"type\":\"trivkins\",\"joint_count\":3,\"pose\":{\"x\":0,\"y\":0,\"z\":0},\"joints\":[0,0,0],\"status\":0,\"blocked\":null},\"planner\":{\"next_id\":1,\"queue\":[],\"active\":null,\"active_id\":null,\"progress\":0,\"queue_depth\":0,\"last_error\":0},\"state_hash\":\"fnv1a64:4e24908a983412f4\"},\"motion_authority\":\"wasm\",\"task_authority\":\"wasm_task_canonical\",\"state_hash\":\"fnv1a64:0ef93407e9093297\"},\"motion_authority\":\"wasm\",\"task_authority\":\"wasm_task_canonical\",\"wasm_home_limit\":{\"status\":\"verified\",\"state\":{\"schema\":\"sim_home_limit_state.v1\",\"source\":\"wasm_home_limit\",\"pose\":{\"x\":0,\"y\":0,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"joints\":[0,0,0],\"joint_count\":3,\"homed\":false,\"home_sequence\":0,\"tick\":0,\"last_error\":0,\"limit_error\":0,\"kinematics_error\":0}},\"axis_state\":{\"state\":\"ESTOP\",\"machine_state\":\"ESTOP\",\"operation_state\":\"IDLE\",\"source_task_state\":\"STATE_ESTOP\",\"source_interp_state\":\"INTERP_IDLE\",\"source_mode\":\"MANUAL\",\"states\":[\"ESTOP\",\"ESTOP_RESET\",\"OFF\",\"ON\",\"HOME\",\"IDLE\",\"RUNNING\",\"PAUSED\"]}}", + "footer": "Error: sim_io_estop", + "href": "http://127.0.0.1:5175/", + "label": "estop", + "machine_state": "Machine ESTOP", + "operation_state": "Operation IDLE", + "position": "X0.000 Y0.000 Z0.000", + "program_status": "loaded", + "ready_state": "complete", + "root_html": "
\",\"joint.0.motor-pos-fb\",\"ddt_x.in\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":30,\"command\":\"net\",\"tokens\":[\"Ypos\",\"joint.1.motor-pos-cmd\",\"=>\",\"joint.1.motor-pos-fb\",\"ddt_y.in\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":31,\"command\":\"net\",\"tokens\":[\"Zpos\",\"joint.2.motor-pos-cmd\",\"=>\",\"joint.2.motor-pos-fb\",\"ddt_z.in\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":35,\"command\":\"net\",\"tokens\":[\"Xvel\",\"ddt_x.out\",\"=>\",\"ddt_xv.in\",\"vel_xy.in0\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":36,\"command\":\"net\",\"tokens\":[\"Xacc\",\"<=\",\"ddt_xv.out\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":37,\"command\":\"net\",\"tokens\":[\"Yvel\",\"ddt_y.out\",\"=>\",\"ddt_yv.in\",\"vel_xy.in1\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":38,\"command\":\"net\",\"tokens\":[\"Yacc\",\"<=\",\"ddt_yv.out\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":39,\"command\":\"net\",\"tokens\":[\"Zvel\",\"ddt_z.out\",\"=>\",\"ddt_zv.in\",\"vel_xyz.in0\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":40,\"command\":\"net\",\"tokens\":[\"Zacc\",\"<=\",\"ddt_zv.out\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":43,\"command\":\"net\",\"tokens\":[\"XYvel\",\"vel_xy.out\",\"=>\",\"vel_xyz.in1\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":44,\"command\":\"net\",\"tokens\":[\"XYZvel\",\"<=\",\"vel_xyz.out\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":47,\"command\":\"net\",\"tokens\":[\"estop-loop\",\"iocontrol.0.user-enable-out\",\"iocontrol.0.emc-enable-in\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":49,\"command\":\"loadusr\",\"status\":\"checked\",\"code\":null,\"program\":\"hal_manualtoolchange\",\"capability\":\"SIM-ADAPTER\",\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":50,\"command\":\"net\",\"tokens\":[\"tool-change\",\"iocontrol.0.tool-change\",\"=>\",\"hal_manualtoolchange.change\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":51,\"command\":\"net\",\"tokens\":[\"tool-changed\",\"iocontrol.0.tool-changed\",\"<=\",\"hal_manualtoolchange.changed\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":52,\"command\":\"net\",\"tokens\":[\"tool-number\",\"iocontrol.0.tool-prep-number\",\"=>\",\"hal_manualtoolchange.number\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":53,\"command\":\"net\",\"tokens\":[\"tool-prepare-loopback\",\"iocontrol.0.tool-prepare\",\"=>\",\"iocontrol.0.tool-prepared\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":56,\"command\":\"loadusr\",\"status\":\"checked\",\"code\":null,\"program\":\"./3axis-tutorial\",\"capability\":\"WEB-EQUIV\",\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":59,\"command\":\"net\",\"tokens\":[\"j0\",\"joint.0.pos-fb\",\"=>\",\"3axis-tutorial.jointX\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":60,\"command\":\"net\",\"tokens\":[\"j1\",\"joint.1.pos-fb\",\"=>\",\"3axis-tutorial.jointY\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":61,\"command\":\"net\",\"tokens\":[\"j2\",\"joint.2.pos-fb\",\"=>\",\"3axis-tutorial.jointZ\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":64,\"command\":\"net\",\"tokens\":[\"tool_len\",\"3axis-tutorial.toollength\",\"<=\",\"motion.tooloffset.z\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"},{\"schema\":\"hal-configuration-preflight.v1\",\"line\":65,\"command\":\"net\",\"tokens\":[\"tool_rad\",\"halui.tool.diameter\",\"3axis-tutorial.tooldiameter\"],\"status\":\"checked\",\"code\":null,\"source\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"resource_id\":\"hal:linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\"}]},\"core\":{\"result\":0},\"status\":\"ready\",\"state_hash\":\"fnv1a64:a7f7c05da5d79f9d\"},\"axis_state\":{\"state\":\"ESTOP\",\"machine_state\":\"ESTOP\",\"operation_state\":\"IDLE\",\"source_task_state\":\"STATE_ESTOP\",\"source_interp_state\":\"INTERP_IDLE\",\"source_mode\":\"MANUAL\",\"states\":[\"ESTOP\",\"ESTOP_RESET\",\"OFF\",\"ON\",\"HOME\",\"IDLE\",\"RUNNING\",\"PAUSED\"]},\"gates\":{\"load\":{\"action\":\"load\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"run\":{\"action\":\"run\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"step\":{\"action\":\"step\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"pause\":{\"action\":\"pause\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"resume\":{\"action\":\"resume\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"abort\":{\"action\":\"abort\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"restart\":{\"action\":\"restart\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"mdi\":{\"action\":\"mdi\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"jogStart\":{\"action\":\"jogStart\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"jogStop\":{\"action\":\"jogStop\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"home\":{\"action\":\"home\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"estop\":{\"action\":\"estop\",\"enabled\":true,\"reason\":\"axis_estop_reset_available\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"recover\":{\"action\":\"recover\",\"enabled\":true,\"reason\":\"axis_recover_available\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"override\":{\"action\":\"override\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"reset\":{\"action\":\"reset\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"spindle\":{\"action\":\"spindle\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"},\"coolant\":{\"action\":\"coolant\",\"enabled\":false,\"reason\":\"axis_fault_active\",\"taskState\":\"ESTOP\",\"interpState\":\"IDLE\"}},\"visual\":{\"schema\":\"axis-visual.v1\",\"pose\":{\"x\":0,\"y\":0,\"z\":0},\"joints\":[0,0,0],\"toolpath\":[{\"id\":1,\"source_line\":5,\"start\":{\"x\":0,\"y\":0,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":25.4,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":0,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":2,\"source_line\":6,\"start\":{\"x\":25.4,\"y\":25.4,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":25.4,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":38.099999999999994,\"y\":38.099999999999994,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":2,\"turn\":-1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":3,\"source_line\":7,\"start\":{\"x\":25.4,\"y\":25.4,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":25.4,\"z\":2.54,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":25.4,\"y\":25.4,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":4,\"source_line\":8,\"start\":{\"x\":25.4,\"y\":25.4,\"z\":2.54,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":44.449999999999996,\"z\":2.54,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":25.4,\"y\":25.4,\"z\":2.54},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":5,\"source_line\":9,\"start\":{\"x\":25.4,\"y\":44.449999999999996,\"z\":2.54,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":44.449999999999996,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":25.4,\"y\":44.449999999999996,\"z\":2.54},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":6,\"source_line\":10,\"start\":{\"x\":25.4,\"y\":44.449999999999996,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":35.559999999999995,\"y\":31.75,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":25.4,\"y\":44.449999999999996,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":7,\"source_line\":11,\"start\":{\"x\":35.559999999999995,\"y\":31.75,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":30.479999999999997,\"y\":38.099999999999994,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":35.559999999999995,\"y\":31.75,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":8,\"source_line\":12,\"start\":{\"x\":30.479999999999997,\"y\":38.099999999999994,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":31.75,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":30.479999999999997,\"y\":38.099999999999994,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":9,\"source_line\":13,\"start\":{\"x\":25.4,\"y\":31.75,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":35.559999999999995,\"y\":44.449999999999996,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":25.4,\"y\":31.75,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":10,\"source_line\":14,\"start\":{\"x\":35.559999999999995,\"y\":44.449999999999996,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":35.559999999999995,\"y\":44.449999999999996,\"z\":2.54,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":35.559999999999995,\"y\":44.449999999999996,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":11,\"source_line\":15,\"start\":{\"x\":35.559999999999995,\"y\":44.449999999999996,\"z\":2.54,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":40.64,\"y\":44.449999999999996,\"z\":2.54,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":35.559999999999995,\"y\":44.449999999999996,\"z\":2.54},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":12,\"source_line\":16,\"start\":{\"x\":40.64,\"y\":44.449999999999996,\"z\":2.54,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":40.64,\"y\":44.449999999999996,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":40.64,\"y\":44.449999999999996,\"z\":2.54},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":13,\"source_line\":17,\"start\":{\"x\":40.64,\"y\":44.449999999999996,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":45.72,\"y\":38.099999999999994,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":40.64,\"y\":44.449999999999996,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":14,\"source_line\":18,\"start\":{\"x\":45.72,\"y\":38.099999999999994,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":50.8,\"y\":44.449999999999996,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":45.72,\"y\":38.099999999999994,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":15,\"source_line\":19,\"start\":{\"x\":50.8,\"y\":44.449999999999996,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":45.72,\"y\":38.099999999999994,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":50.8,\"y\":44.449999999999996,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":16,\"source_line\":20,\"start\":{\"x\":45.72,\"y\":38.099999999999994,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":45.72,\"y\":31.75,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":45.72,\"y\":38.099999999999994,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":17,\"source_line\":21,\"start\":{\"x\":45.72,\"y\":31.75,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":0,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":45.72,\"y\":31.75,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":18,\"source_line\":22,\"start\":{\"x\":0,\"y\":0,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":0,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":0,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":19,\"source_line\":23,\"start\":{\"x\":25.4,\"y\":0,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":0,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":38.099999999999994,\"y\":0,\"z\":38.099999999999994},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":2,\"turn\":-1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":20,\"source_line\":24,\"start\":{\"x\":25.4,\"y\":0,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":2.54,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":25.4,\"y\":0,\"z\":25.4},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":21,\"source_line\":25,\"start\":{\"x\":25.4,\"y\":2.54,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":2.54,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":25.4,\"y\":2.54,\"z\":25.4},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":22,\"source_line\":26,\"start\":{\"x\":25.4,\"y\":2.54,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":0,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":25.4,\"y\":2.54,\"z\":44.449999999999996},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":23,\"source_line\":27,\"start\":{\"x\":25.4,\"y\":0,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":35.559999999999995,\"y\":0,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":25.4,\"y\":0,\"z\":44.449999999999996},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":24,\"source_line\":28,\"start\":{\"x\":35.559999999999995,\"y\":0,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":30.479999999999997,\"y\":0,\"z\":38.099999999999994,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":35.559999999999995,\"y\":0,\"z\":31.75},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":25,\"source_line\":29,\"start\":{\"x\":30.479999999999997,\"y\":0,\"z\":38.099999999999994,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":25.4,\"y\":0,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":30.479999999999997,\"y\":0,\"z\":38.099999999999994},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":26,\"source_line\":30,\"start\":{\"x\":25.4,\"y\":0,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":35.559999999999995,\"y\":0,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":25.4,\"y\":0,\"z\":31.75},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":27,\"source_line\":31,\"start\":{\"x\":35.559999999999995,\"y\":0,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":35.559999999999995,\"y\":2.54,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":35.559999999999995,\"y\":0,\"z\":44.449999999999996},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":28,\"source_line\":32,\"start\":{\"x\":35.559999999999995,\"y\":2.54,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":40.64,\"y\":2.54,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":35.559999999999995,\"y\":2.54,\"z\":44.449999999999996},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":29,\"source_line\":33,\"start\":{\"x\":40.64,\"y\":2.54,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":40.64,\"y\":0,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":40.64,\"y\":2.54,\"z\":44.449999999999996},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":30,\"source_line\":34,\"start\":{\"x\":40.64,\"y\":0,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":50.8,\"y\":0,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":40.64,\"y\":0,\"z\":44.449999999999996},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":31,\"source_line\":35,\"start\":{\"x\":50.8,\"y\":0,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":40.64,\"y\":0,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":50.8,\"y\":0,\"z\":44.449999999999996},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":32,\"source_line\":36,\"start\":{\"x\":40.64,\"y\":0,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":50.8,\"y\":0,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":40.64,\"y\":0,\"z\":31.75},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":33,\"source_line\":37,\"start\":{\"x\":50.8,\"y\":0,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":0,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":50.8,\"y\":0,\"z\":31.75},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":34,\"source_line\":38,\"start\":{\"x\":0,\"y\":0,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":25.4,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":0,\"z\":0},\"normal\":{\"x\":0,\"y\":1,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":35,\"source_line\":39,\"start\":{\"x\":0,\"y\":25.4,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":25.4,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":38.099999999999994,\"z\":38.099999999999994},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":2,\"turn\":-1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":36,\"source_line\":40,\"start\":{\"x\":0,\"y\":25.4,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":2.54,\"y\":25.4,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":25.4,\"z\":25.4},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":37,\"source_line\":41,\"start\":{\"x\":2.54,\"y\":25.4,\"z\":25.4,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":2.54,\"y\":25.4,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":2.54,\"y\":25.4,\"z\":25.4},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":38,\"source_line\":42,\"start\":{\"x\":2.54,\"y\":25.4,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":25.4,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":2.54,\"y\":25.4,\"z\":44.449999999999996},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":39,\"source_line\":43,\"start\":{\"x\":0,\"y\":25.4,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":30.479999999999997,\"z\":38.099999999999994,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":25.4,\"z\":44.449999999999996},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":40,\"source_line\":44,\"start\":{\"x\":0,\"y\":30.479999999999997,\"z\":38.099999999999994,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":35.559999999999995,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":30.479999999999997,\"z\":38.099999999999994},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":41,\"source_line\":45,\"start\":{\"x\":0,\"y\":35.559999999999995,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":30.479999999999997,\"z\":38.099999999999994,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":35.559999999999995,\"z\":44.449999999999996},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":42,\"source_line\":46,\"start\":{\"x\":0,\"y\":30.479999999999997,\"z\":38.099999999999994,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":30.479999999999997,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":30.479999999999997,\"z\":38.099999999999994},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":43,\"source_line\":47,\"start\":{\"x\":0,\"y\":30.479999999999997,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":2.54,\"y\":30.479999999999997,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":30.479999999999997,\"z\":31.75},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":44,\"source_line\":48,\"start\":{\"x\":2.54,\"y\":30.479999999999997,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":2.54,\"y\":40.64,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":2.54,\"y\":30.479999999999997,\"z\":31.75},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":45,\"source_line\":49,\"start\":{\"x\":2.54,\"y\":40.64,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":40.64,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":2.54,\"y\":40.64,\"z\":44.449999999999996},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":46,\"source_line\":50,\"start\":{\"x\":0,\"y\":40.64,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":50.8,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":40.64,\"z\":44.449999999999996},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":47,\"source_line\":51,\"start\":{\"x\":0,\"y\":50.8,\"z\":44.449999999999996,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":40.64,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":50.8,\"z\":44.449999999999996},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":48,\"source_line\":52,\"start\":{\"x\":0,\"y\":40.64,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":50.8,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":40.64,\"z\":31.75},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"},{\"id\":49,\"source_line\":53,\"start\":{\"x\":0,\"y\":50.8,\"z\":31.75,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":0,\"y\":0,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":0,\"y\":50.8,\"z\":31.75},\"normal\":{\"x\":1,\"y\":0,\"z\":0},\"velocity\":762,\"acceleration\":0,\"motion_type\":1,\"turn\":1,\"source\":\"wasm_interpreter_canonical_motion\"}],\"active_segment_id\":0,\"source\":\"wasm_canonical_motion\"},\"authoritative\":{\"schema\":\"sim_authoritative_runtime.v1\",\"tick\":0,\"status\":\"idle\",\"program_status\":\"idle\",\"kinematics\":{\"type\":\"trivkins\",\"joint_count\":3,\"pose\":{\"x\":0,\"y\":0,\"z\":0},\"joints\":[0,0,0],\"status\":0,\"blocked\":null},\"planner\":{\"next_id\":1,\"queue\":[],\"active\":null,\"active_id\":null,\"progress\":0,\"queue_depth\":0,\"last_error\":0},\"state_hash\":\"fnv1a64:4e24908a983412f4\"},\"motion_authority\":\"wasm\",\"task_authority\":\"wasm_task_canonical\",\"state_hash\":\"fnv1a64:0ef93407e9093297\"},\"motion_authority\":\"wasm\",\"task_authority\":\"wasm_task_canonical\",\"wasm_home_limit\":{\"status\":\"verified\",\"state\":{\"schema\":\"sim_home_limit_state.v1\",\"source\":\"wasm_home_limit\",\"pose\":{\"x\":0,\"y\":0,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"joints\":[0,0,0],\"joint_count\":3,\"homed\":false,\"home_sequence\":0,\"tick\":0,\"last_error\":0,\"limit_error\":0,\"kinematics_error\":0}},\"axis_state\":{\"state\":\"ESTOP\",\"machine_state\":\"ESTOP\",\"operation_state\":\"IDLE\",\"source_task_state\":\"STATE_ESTOP\",\"source_interp_state\":\"INTERP_IDLE\",\"source_mode\":\"MANUAL\",\"states\":[\"ESTOP\",\"ESTOP_RESET\",\"OFF\",\"ON\",\"HOME\",\"IDLE\",\"RUNNING\",\"PAUSED\"]}}", + "footer": "Error: sim_io_estop", + "href": "http://127.0.0.1:5175/", + "label": "estop", + "machine_state": "Machine ESTOP", + "operation_state": "Operation IDLE", + "position": "X0.000 Y0.000 Z0.000", + "program_status": "loaded", + "ready_state": "complete", + "root_html": "
/dev/null || exit 1; done" + ], + "exit_code": 0, + "name": "app_regression", + "output_bytes": 0, + "output_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "status": "passed" + }, + { + "command": [ + "npm", + "--prefix", + "web/react", + "run", + "typecheck" + ], + "exit_code": 0, + "name": "react_typecheck", + "output_bytes": 59, + "output_sha256": "08fb3e3d0d9396a56f8720eb15d9f6920f8a30c2dad7b0b6672ac446801f27e3", + "status": "passed" + }, + { + "command": [ + "npm", + "--prefix", + "web/react", + "run", + "build" + ], + "exit_code": 0, + "name": "react_build", + "output_bytes": 949, + "output_sha256": "ca43db92e3c34e6eef081eeec1b15d95119cbad819058232b7847cbbef9b74c7", + "status": "passed" + } + ], + "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", + "failure_samples": [], + "feature_matrix": [ + "load_program", + "toolpath_preview", + "estop_reset", + "power_on", + "reset", + "home", + "execute_gcode", + "realtime_path", + "realtime_coordinates", + "button_gates", + "mdi", + "jog", + "spindle", + "coolant", + "tool_change", + "probe", + "checkpoint_recovery", + "diagnostics", + "accessibility_layout" + ], + "implementation_status": "verified", + "passed_command_count": 20, + "release_action": "ALLOW_RELEASE", + "report_sha256": "c0e888609af42ae17ced7b11939373431f22e0d5a270377b9efee118290e43b7", + "required_command_count": 20, + "schema": "axis3-tutorial-web-wasm-complete-parity.v2", + "scope": "selected_case_web_wasm_simulation", + "source_contract": { + "axis": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "case_hal": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal", + "case_ini": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", + "vismach_program": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial" + }, + "status": "verified", + "task_id": "PANEL-15-SELECTED-CASE-ACCEPTANCE" +} diff --git a/coverage/browser-performance-matrix.report.json b/coverage/browser-performance-matrix.report.json index 3360f98..9c9d8d4 100644 --- a/coverage/browser-performance-matrix.report.json +++ b/coverage/browser-performance-matrix.report.json @@ -51,14 +51,14 @@ "bytes": 225468, "path": "coverage/full-configuration-startup-matrix.report.json", "role": "full_configuration_matrix", - "sha256": "f52e5bda5c3d06d7779d1e2f66c03c98381b133599ab9771dca55ab46acf21b5", + "sha256": "f2140e4c1685ca17738645faedb962dcaa7210200c9151084bd5df40285eadf9", "status": "present" }, { "bytes": 3170, "path": "coverage/recovery-offline-fault-matrix.report.json", "role": "recovery_offline_fault_matrix", - "sha256": "17997c17cb0dc1527a892a353d5287318b63cd145a5c83e8d463926e366b1c4e", + "sha256": "2e49ab82b463d3bbb76a1ac32dc1db4bbcf39d8c1f82a0d5738005411b822f72", "status": "present" }, { @@ -72,7 +72,7 @@ "bytes": 3301, "path": "coverage/frontend-m4.report.json", "role": "frontend_m4", - "sha256": "948b4a8b88ebfc5dd75c71a6032b619640e8064a328b51e271ba652abb151721", + "sha256": "f38acbc255ad4e33e6debcd258c03ed392faabd63892ace0cf843c246ee888eb", "status": "present" }, { @@ -85,7 +85,7 @@ ], "next_transition": "execute K8-REL-17 release gate refresh after implementation evidence", "release_action": "STOP_RELEASE", - "report_sha256": "a82e5d50ef5754ad3a4da8e4a90fbfc92ff1dd7b28a68496ef1a8b461542fc35", + "report_sha256": "019fbfb9aa9f5420665f21b00216fad8ed6e86a6e145d7c18c6b9d66c568abe5", "runs": [ { "exit_code": 0, @@ -96,7 +96,7 @@ "file": "index.html" }, { - "bytes": 16777, + "bytes": 16849, "file": "app.mjs" }, { @@ -138,11 +138,11 @@ ], "schema": "browser-performance-sample.v1", "stable": true, - "total_bytes": 42226 + "total_bytes": 42298 }, "status": "passed", "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", - "stdout_sha256": "c1147df2ce4b0af41c858bc063f517a38a3ef3df1ac8678d859c3539e01bbcf9" + "stdout_sha256": "6d3004d685462a42b95caf62e39cc55456846341ed1a8e74075adb8c9432adca" }, { "exit_code": 0, @@ -153,7 +153,7 @@ "file": "index.html" }, { - "bytes": 16777, + "bytes": 16849, "file": "app.mjs" }, { @@ -195,11 +195,11 @@ ], "schema": "browser-performance-sample.v1", "stable": true, - "total_bytes": 42226 + "total_bytes": 42298 }, "status": "passed", "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", - "stdout_sha256": "c1147df2ce4b0af41c858bc063f517a38a3ef3df1ac8678d859c3539e01bbcf9" + "stdout_sha256": "6d3004d685462a42b95caf62e39cc55456846341ed1a8e74075adb8c9432adca" } ], "schema": "browser-performance-matrix.v1", diff --git a/coverage/configuration-resource-remediation.report.json b/coverage/configuration-resource-remediation.report.json index 68ccf32..490b5b5 100644 --- a/coverage/configuration-resource-remediation.report.json +++ b/coverage/configuration-resource-remediation.report.json @@ -1,17 +1,17 @@ { "candidate_classification": { - "candidate_basename_match": 579, + "candidate_basename_match": 598, "candidate_requires_reference_resolution": 153, - "no_candidate": 224, + "no_candidate": 205, "reference_requires_resolution": 41 }, "candidate_eligible": false, "checks": { "candidate_resolution_is_non_mutating": { "classification": { - "candidate_basename_match": 579, + "candidate_basename_match": 598, "candidate_requires_reference_resolution": 153, - "no_candidate": 224, + "no_candidate": 205, "reference_requires_resolution": 41 }, "status": "verified" @@ -451,7 +451,10 @@ }, { "basename_token": "position_mm.txt", - "candidate_paths": [], + "candidate_paths": [ + "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt", + "web/react/dist/linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt" + ], "edge_kind": "ini:TRAJ:POSITION_FILE", "entrypoint": "linuxcnc/configs/sim/axis/axis_mm.ini", "parse_status": "missing", @@ -2157,7 +2160,10 @@ }, { "basename_token": "position_mm.txt", - "candidate_paths": [], + "candidate_paths": [ + "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt", + "web/react/dist/linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt" + ], "edge_kind": "ini:TRAJ:POSITION_FILE", "entrypoint": "linuxcnc/configs/sim/axis/iocontrolv2/axis-iocontrolv2-demo.ini", "parse_status": "missing", @@ -2236,7 +2242,10 @@ }, { "basename_token": "position_mm.txt", - "candidate_paths": [], + "candidate_paths": [ + "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt", + "web/react/dist/linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt" + ], "edge_kind": "ini:TRAJ:POSITION_FILE", "entrypoint": "linuxcnc/configs/sim/axis/iocontrolv2/g8812.ini", "parse_status": "missing", @@ -2315,7 +2324,10 @@ }, { "basename_token": "position_mm.txt", - "candidate_paths": [], + "candidate_paths": [ + "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt", + "web/react/dist/linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt" + ], "edge_kind": "ini:TRAJ:POSITION_FILE", "entrypoint": "linuxcnc/configs/sim/axis/iocontrolv2/m66track.ini", "parse_status": "missing", @@ -2394,7 +2406,10 @@ }, { "basename_token": "position_mm.txt", - "candidate_paths": [], + "candidate_paths": [ + "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt", + "web/react/dist/linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt" + ], "edge_kind": "ini:TRAJ:POSITION_FILE", "entrypoint": "linuxcnc/configs/sim/axis/iocontrolv2/owordm6-ui-ns.ini", "parse_status": "missing", @@ -2473,7 +2488,10 @@ }, { "basename_token": "position_mm.txt", - "candidate_paths": [], + "candidate_paths": [ + "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt", + "web/react/dist/linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt" + ], "edge_kind": "ini:TRAJ:POSITION_FILE", "entrypoint": "linuxcnc/configs/sim/axis/iocontrolv2/owordm6-ui.ini", "parse_status": "missing", @@ -4082,7 +4100,10 @@ }, { "basename_token": "position_mm.txt", - "candidate_paths": [], + "candidate_paths": [ + "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt", + "web/react/dist/linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt" + ], "edge_kind": "ini:TRAJ:POSITION_FILE", "entrypoint": "linuxcnc/configs/sim/axis/orphans/axis_noio.ini", "parse_status": "missing", @@ -4142,7 +4163,10 @@ }, { "basename_token": "position_mm.txt", - "candidate_paths": [], + "candidate_paths": [ + "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt", + "web/react/dist/linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt" + ], "edge_kind": "ini:TRAJ:POSITION_FILE", "entrypoint": "linuxcnc/configs/sim/axis/orphans/iocontrol-removed/iocontrol-removed.ini", "parse_status": "missing", @@ -5017,7 +5041,10 @@ }, { "basename_token": "position_mm.txt", - "candidate_paths": [], + "candidate_paths": [ + "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt", + "web/react/dist/linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt" + ], "edge_kind": "ini:TRAJ:POSITION_FILE", "entrypoint": "linuxcnc/configs/sim/axis/remap/manual-toolchange-with-tool-length-switch/manualtoolchange.ini", "parse_status": "missing", @@ -5073,7 +5100,10 @@ }, { "basename_token": "position_mm.txt", - "candidate_paths": [], + "candidate_paths": [ + "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt", + "web/react/dist/linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt" + ], "edge_kind": "ini:TRAJ:POSITION_FILE", "entrypoint": "linuxcnc/configs/sim/axis/remap/rack-toolchange/racktoolchange.ini", "parse_status": "missing", @@ -5497,7 +5527,10 @@ }, { "basename_token": "3axis-tutorial_mm.var", - "candidate_paths": [], + "candidate_paths": [ + "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial_mm.var", + "web/react/dist/linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial_mm.var" + ], "edge_kind": "ini:RS274NGC:PARAMETER_FILE", "entrypoint": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", "parse_status": "missing", @@ -5509,7 +5542,10 @@ }, { "basename_token": "position_mm.txt", - "candidate_paths": [], + "candidate_paths": [ + "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt", + "web/react/dist/linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt" + ], "edge_kind": "ini:TRAJ:POSITION_FILE", "entrypoint": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", "parse_status": "missing", @@ -6132,7 +6168,10 @@ }, { "basename_token": "position_mm.txt", - "candidate_paths": [], + "candidate_paths": [ + "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt", + "web/react/dist/linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt" + ], "edge_kind": "ini:TRAJ:POSITION_FILE", "entrypoint": "linuxcnc/configs/sim/axis/vismach/rolfmill/rolfmill.ini", "parse_status": "missing", @@ -8647,7 +8686,10 @@ }, { "basename_token": "position_mm.txt", - "candidate_paths": [], + "candidate_paths": [ + "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt", + "web/react/dist/linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt" + ], "edge_kind": "ini:TRAJ:POSITION_FILE", "entrypoint": "linuxcnc/configs/sim/gscreen/gscreen_custom/tester_metric.ini", "parse_status": "missing", @@ -8839,7 +8881,10 @@ }, { "basename_token": "position_mm.txt", - "candidate_paths": [], + "candidate_paths": [ + "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt", + "web/react/dist/linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt" + ], "edge_kind": "ini:TRAJ:POSITION_FILE", "entrypoint": "linuxcnc/configs/sim/gscreen/gscreen_mm.ini", "parse_status": "missing", @@ -9233,7 +9278,10 @@ }, { "basename_token": "position_mm.txt", - "candidate_paths": [], + "candidate_paths": [ + "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt", + "web/react/dist/linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt" + ], "edge_kind": "ini:TRAJ:POSITION_FILE", "entrypoint": "linuxcnc/configs/sim/pyvcp_demo/pyvcp_bottom_panel.ini", "parse_status": "missing", @@ -9327,7 +9375,10 @@ }, { "basename_token": "position_mm.txt", - "candidate_paths": [], + "candidate_paths": [ + "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt", + "web/react/dist/linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt" + ], "edge_kind": "ini:TRAJ:POSITION_FILE", "entrypoint": "linuxcnc/configs/sim/pyvcp_demo/pyvcp_demo1.ini", "parse_status": "missing", @@ -9421,7 +9472,10 @@ }, { "basename_token": "position_mm.txt", - "candidate_paths": [], + "candidate_paths": [ + "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt", + "web/react/dist/linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt" + ], "edge_kind": "ini:TRAJ:POSITION_FILE", "entrypoint": "linuxcnc/configs/sim/pyvcp_demo/pyvcp_embed_tab.ini", "parse_status": "missing", @@ -12634,7 +12688,10 @@ }, { "basename_token": "position_mm.txt", - "candidate_paths": [], + "candidate_paths": [ + "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt", + "web/react/dist/linuxcnc/configs/sim/axis/vismach/3axis-tutorial/position_mm.txt" + ], "edge_kind": "ini:TRAJ:POSITION_FILE", "entrypoint": "linuxcnc/configs/sim/qtvcp_screens/qtdefault_mm.ini", "parse_status": "missing", @@ -15150,7 +15207,7 @@ "release_action": "STOP_RELEASE", "release_decision": "blocked_not_release_candidate", "remediation_status": "blocked_resource_gaps", - "report_sha256": "ae65463e25b65b7add7d7aafef30e20018a27061cfcc956b7f3d372ef978ff54", + "report_sha256": "8d860cba70c47469a933b0f632339043e191605b30953f0357dd6330d9502779", "review_policy": { "forbidden": [ "drop_missing_rows", diff --git a/coverage/configuration-startup-runtime.report.json b/coverage/configuration-startup-runtime.report.json index 7c83eae..32c565b 100644 --- a/coverage/configuration-startup-runtime.report.json +++ b/coverage/configuration-startup-runtime.report.json @@ -58,7 +58,7 @@ "bytes": 4116, "path": "coverage/resource-resolver.report.json", "role": "resolver_report", - "sha256": "b49bb37483437ffd80af56ed524a92fe83ab28de94238f32570cb4cd85e18b55", + "sha256": "9e487b7140cb1312b5d8e0ad16e6b70f7a7df0911efc3924cc5ab62c37c5a68d", "status": "present" } ], @@ -114,17 +114,17 @@ ], "outputs": [ { - "bytes": 12357, + "bytes": 12551, "path": "web/workspace/configuration-startup.mjs", "role": "startup_state_machine", - "sha256": "07c967acf84bd084f97b5d1a818294269eec68603caeee7694859f49bf11d7be", + "sha256": "e375abb6dcc0e0657f038356a68f5932c360bcf7fdf5ff906218a37b9012924a", "status": "present" }, { - "bytes": 57994, + "bytes": 70586, "path": "web/worker/sim-session.mjs", "role": "worker_integration", - "sha256": "47c0918797b6a60ed72dce6014fb3f365f620e31be34d8d66f5311971dbe16fd", + "sha256": "e83eab02bca8f71615dae96c7aa71ad912c21353af9504b2f74cea6b3c13fa43", "status": "present" }, { @@ -137,7 +137,7 @@ ], "release_action": "STOP_RELEASE", "release_status": "blocked_external_owner", - "report_sha256": "daa8bc91017fe4a64487d8b89f5bd379adb87597570f287832c9d60b650c06d1", + "report_sha256": "74c3df3401c16db7f0fe126d825d83ced92bf04824a18170439e3091879849aa", "schema": "configuration-startup-runtime-report.v1", "startup_observation": { "resource_graph_hash": "fnv1a64:05c778ded69fb8e2", diff --git a/coverage/frontend-m1.report.json b/coverage/frontend-m1.report.json index 7f5fb1d..9477346 100644 --- a/coverage/frontend-m1.report.json +++ b/coverage/frontend-m1.report.json @@ -25,10 +25,10 @@ "status": "present" }, { - "bytes": 16777, + "bytes": 16849, "path": "web/app/app.mjs", "role": "frontend_source", - "sha256": "5ae3c3df053383f5ae3242aba20c2c8818b04639868e96514955bd1a760ec2ef", + "sha256": "f2af4be261941e4eaa48e32c1d929fcb8adfa4fe91475c398eec03f1fa99a8a4", "status": "present" }, { @@ -73,7 +73,7 @@ "bytes": 4914, "path": "coverage/python-worker-bridge.report.json", "role": "python_worker_bridge", - "sha256": "a64ee36839ba9c984c8eea17e48cf6f8925ed46ddbc630814509b4c14abf5029", + "sha256": "977be3b05a9d07cf2534f9646cd1673ba637dc15d896cdf905a539b855934edf", "status": "present" }, { @@ -101,10 +101,10 @@ "status": "present" }, { - "bytes": 16777, + "bytes": 16849, "path": "web/app/app.mjs", "role": "frontend_source", - "sha256": "5ae3c3df053383f5ae3242aba20c2c8818b04639868e96514955bd1a760ec2ef", + "sha256": "f2af4be261941e4eaa48e32c1d929fcb8adfa4fe91475c398eec03f1fa99a8a4", "status": "present" }, { @@ -130,7 +130,7 @@ } ], "release_action": "STOP_RELEASE", - "report_sha256": "1d314542f4312efc5b7d821685f9d3f1d9b7778f9eea857a5441313eab01db1c", + "report_sha256": "0d56bc0650f1ff18bec603fc0ffc63781db5bf59943bdb40978bda0dfc51e774", "schema": "frontend-m1.v1", "smoke": { "exit_code": 0, diff --git a/coverage/frontend-m2.report.json b/coverage/frontend-m2.report.json index b16ede0..a64b3b6 100644 --- a/coverage/frontend-m2.report.json +++ b/coverage/frontend-m2.report.json @@ -48,7 +48,7 @@ "bytes": 4470, "path": "coverage/frontend-m1.report.json", "role": "frontend_m1", - "sha256": "52f285d35c83121baa0e63214c5a16907158fdc42f246815d22c3497e89ee4a3", + "sha256": "a44a980f7b4fb494bceff0d3ecfd6acc144efe0070ffc2cf12ddd1a018746b0f", "status": "present" }, { @@ -83,10 +83,10 @@ "status": "present" }, { - "bytes": 16777, + "bytes": 16849, "path": "web/app/app.mjs", "role": "frontend_source", - "sha256": "5ae3c3df053383f5ae3242aba20c2c8818b04639868e96514955bd1a760ec2ef", + "sha256": "f2af4be261941e4eaa48e32c1d929fcb8adfa4fe91475c398eec03f1fa99a8a4", "status": "present" }, { @@ -105,7 +105,7 @@ } ], "release_action": "STOP_RELEASE", - "report_sha256": "add0bf61f66878b2cf3104cce4d1ece6f5d753028dca8833d951a9c6c501903e", + "report_sha256": "f71b48a5816048bccf5aceee5e4d3a0c7dbcd4cf32d79934c5032ebb1e23b076", "schema": "frontend-m2.v1", "smoke": { "exit_code": 0, diff --git a/coverage/frontend-m3.report.json b/coverage/frontend-m3.report.json index 89fb793..85e1d33 100644 --- a/coverage/frontend-m3.report.json +++ b/coverage/frontend-m3.report.json @@ -39,7 +39,7 @@ "bytes": 3417, "path": "coverage/frontend-m2.report.json", "role": "frontend_m2", - "sha256": "154a65bef62e48edee41fd7c30fecf6e70c915089ef09b136d9f3a12ce4e9dfe", + "sha256": "402125923ca043b7ca51fbb7cc7eed286086761f3d6b26dfcff31bf14170d0be", "status": "present" }, { @@ -74,10 +74,10 @@ "status": "present" }, { - "bytes": 16777, + "bytes": 16849, "path": "web/app/app.mjs", "role": "frontend_source", - "sha256": "5ae3c3df053383f5ae3242aba20c2c8818b04639868e96514955bd1a760ec2ef", + "sha256": "f2af4be261941e4eaa48e32c1d929fcb8adfa4fe91475c398eec03f1fa99a8a4", "status": "present" }, { @@ -89,7 +89,7 @@ } ], "release_action": "STOP_RELEASE", - "report_sha256": "7c94f2804d91abc6869e5befb90f574677ef68670cac868ed4c577b16abcd96f", + "report_sha256": "f36ff3f16b3706431c88fc06e5215bef3057dbb756f4b3537e1042fe8920430f", "schema": "frontend-m3.v1", "smoke": { "exit_code": 0, diff --git a/coverage/frontend-m4.report.json b/coverage/frontend-m4.report.json index e7cc89e..93607ce 100644 --- a/coverage/frontend-m4.report.json +++ b/coverage/frontend-m4.report.json @@ -39,7 +39,7 @@ "bytes": 3060, "path": "coverage/frontend-m3.report.json", "role": "frontend_m3", - "sha256": "ab1d182bbf57ad2474dac56751937b2ca4de2326a26b9652709b50785666379b", + "sha256": "87cacae265748a98cd9f8c14bc96194076d03222829f55751a5ecde5dd0ffd09", "status": "present" }, { @@ -96,7 +96,7 @@ } ], "release_action": "STOP_RELEASE", - "report_sha256": "6f71305c15d3e1d8391c66e17d7c1704af64741f0cb2f11552f88d6b707993f8", + "report_sha256": "b4bfdb2ab99be37ce8bdd33cf867cd3e1301af3a7ed51be47a033afdd68d145a", "schema": "frontend-m4.v1", "smoke": { "exit_code": 0, diff --git a/coverage/full-case-acceptance-s8-10.report.json b/coverage/full-case-acceptance-s8-10.report.json index c51cef9..4d8b305 100644 --- a/coverage/full-case-acceptance-s8-10.report.json +++ b/coverage/full-case-acceptance-s8-10.report.json @@ -2,7 +2,7 @@ "acceptance_status": "blocked", "blockers": [ { - "count": 140, + "count": 139, "id": "remaining_case_blockers", "reason": "235-case startup matrix retains resource/HAL/INI/STARTUP blockers; blocked rows are not promoted", "status": "blocked" @@ -30,7 +30,7 @@ "acceptance_blockers_retained": { "blockers": [ { - "count": 140, + "count": 139, "id": "remaining_case_blockers", "reason": "235-case startup matrix retains resource/HAL/INI/STARTUP blockers; blocked rows are not promoted", "status": "blocked" @@ -51,9 +51,9 @@ "status": "verified" }, "case_matrix_aggregated": { - "blocked": 140, + "blocked": 139, "cases": 235, - "ready": 95, + "ready": 96, "status": "verified" }, "context_consistency": { @@ -82,16 +82,16 @@ "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", "coverage": { "case_matrix": { - "blocked": 140, + "blocked": 139, "case_count": 235, "counts": { "BLOCKED_HAL": 106, "BLOCKED_INI": 19, - "BLOCKED_RESOURCE": 12, + "BLOCKED_RESOURCE": 11, "BLOCKED_STARTUP": 3, - "TASK_READY": 95 + "TASK_READY": 96 }, - "ready": 95, + "ready": 96, "status": "verified" }, "display_families": { @@ -131,12 +131,12 @@ "status": "present" }, { - "bytes": 297881, + "bytes": 297684, "implementation_status": "verified", "path": "coverage/sim-case-matrix-s8-06.report.json", "role": "case_matrix", "schema": "sim-case-matrix-s8-06-report.v1", - "sha256": "40a657b0e046373515a8929e321ce515a26a0c6a805a5e3ce6734262877d83ac", + "sha256": "0ee1db9f8cec6718a438598eaa193b2f7a1b57b5ad52f151da17ca5d4a32dfe2", "status": "verified", "task_id": "S8-06" }, @@ -248,8 +248,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "ab66f78ddf7359b412768bab6ad04c289e60b02309d6b87ba6d203294d837d00", - "stdout_tail": "{\"schema\":\"authoritative-kinematics-s6-sample.v1\",\"status\":\"verified\",\"context_id\":\"linuxcnc-2.10.0-pre0-wasm-m1-py-c1\",\"supported\":{\"type\":\"corexy\",\"pose\":[4,2,3],\"joints\":[6,2,3],\"endpoint_hash\":\"fnv1a64:775904e8c68dd9a0\"},\"parity\":{\"mode\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:775904e8c68dd9a0\",\"actual_hash\":\"fnv1a64:775904e8c68dd9a0\"},\"recovery\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:2aa1ccdba37b5cbf\",\"actual_hash\":\"fnv1a64:2aa1ccdba37b5cbf\"}},\"checkpoint_hash\":\"fnv1a64:115bf7af395816bc\",\"unsupported\":{\"type\":\"puma\",\"code\":\"authoritative_kinematics_unsupported\"},\"boundaries\":[\"other non-trivkins families remain queued for later slices\",\"official LinuxCNC kinematics oracle remains SOURCE_BLOCKED\"]}\n" + "stdout_sha256": "462ecdac6d757e2f36df5c66c9d9d1c5c4596cef5585dc9ef5c2353f0a4a2d50", + "stdout_tail": "{\"schema\":\"authoritative-kinematics-s6-sample.v1\",\"status\":\"verified\",\"context_id\":\"linuxcnc-2.10.0-pre0-wasm-m1-py-c1\",\"supported\":{\"type\":\"corexy\",\"pose\":[4,2,3],\"joints\":[6,2,3],\"endpoint_hash\":\"fnv1a64:e9356bbd07e346b4\"},\"parity\":{\"mode\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:e9356bbd07e346b4\",\"actual_hash\":\"fnv1a64:e9356bbd07e346b4\"},\"recovery\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:179f9db1e698d8a3\",\"actual_hash\":\"fnv1a64:179f9db1e698d8a3\"}},\"checkpoint_hash\":\"fnv1a64:b0841e6a6279e0ce\",\"unsupported\":{\"type\":\"puma\",\"code\":\"authoritative_kinematics_unsupported\"},\"boundaries\":[\"other non-trivkins families remain queued for later slices\",\"official LinuxCNC kinematics oracle remains SOURCE_BLOCKED\"]}\n" }, { "command": [ @@ -261,8 +261,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "5c73e5f7c33f71b9f6ad90e2c8cc6d93b72e6ba809c031545625fcfc9453ab01", - "stdout_tail": "]],\"joint_semantics\":[\"AD\",\"BD\",\"CD\"]},\"endpoint_hash\":\"fnv1a64:44fbc2aefa75acd4\"},\"parity\":{\"mode\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:44fbc2aefa75acd4\",\"actual_hash\":\"fnv1a64:44fbc2aefa75acd4\"},\"recovery\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:d5c56e04cba64203\",\"actual_hash\":\"fnv1a64:d5c56e04cba64203\"}},\"checkpoint_hash\":\"fnv1a64:383b190eb4ba42db\",\"unsupported\":[{\"type\":\"xyzac-trt\",\"code\":\"authoritative_kinematics_unsupported\"},{\"type\":\"puma\",\"code\":\"authoritative_kinematics_unsupported\"}],\"boundaries\":[\"TRT, serial robot, Delta, Hexapod, Pentapod and switch/user families remain queued for later slices\",\"official LinuxCNC kinematics oracle remains SOURCE_BLOCKED\",\"tripod native/WASM golden is implementation evidence, not official oracle evidence\"]}\n" + "stdout_sha256": "95169ca50193d7eeccb7356cfa22145c30b35eac35a97f5e730349b0de262b96", + "stdout_tail": "]],\"joint_semantics\":[\"AD\",\"BD\",\"CD\"]},\"endpoint_hash\":\"fnv1a64:8aa05a6abc6d5b50\"},\"parity\":{\"mode\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:8aa05a6abc6d5b50\",\"actual_hash\":\"fnv1a64:8aa05a6abc6d5b50\"},\"recovery\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:80734148737fcdaf\",\"actual_hash\":\"fnv1a64:80734148737fcdaf\"}},\"checkpoint_hash\":\"fnv1a64:257785be8e73ceae\",\"unsupported\":[{\"type\":\"xyzac-trt\",\"code\":\"authoritative_kinematics_unsupported\"},{\"type\":\"puma\",\"code\":\"authoritative_kinematics_unsupported\"}],\"boundaries\":[\"TRT, serial robot, Delta, Hexapod, Pentapod and switch/user families remain queued for later slices\",\"official LinuxCNC kinematics oracle remains SOURCE_BLOCKED\",\"tripod native/WASM golden is implementation evidence, not official oracle evidence\"]}\n" }, { "command": [ @@ -274,8 +274,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "bcef11935442caf7e90a1477a50fc64ee4aa7041b851127a14a3dd0b6034c824", - "stdout_tail": ".0000000002803],\"endpoint_hash\":\"fnv1a64:b1d05dd7ff31f2d5\"},\"parity\":{\"mode\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:b1d05dd7ff31f2d5\",\"actual_hash\":\"fnv1a64:b1d05dd7ff31f2d5\"},\"recovery\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:ea88896c61d2b41a\",\"actual_hash\":\"fnv1a64:ea88896c61d2b41a\"}},\"checkpoint_hash\":\"fnv1a64:5208e65fe3683b3b\",\"unsupported\":{\"type\":\"xyzac-trt\",\"code\":\"authoritative_kinematics_unsupported\"},\"range\":{\"code\":\"scheduler\",\"message_key\":\"authoritative_kinematics_range\"},\"geometry\":{\"radius\":130.25,\"rod_length\":269,\"tower_layout\":\"120-degree equilateral\"},\"boundaries\":[\"official kinematics oracle remains SOURCE_BLOCKED\",\"TRT/robot/Hexapod/Pentapod/switch/user remain queued\",\"missing ldelta HAL resources are retained as configuration gaps\"]}\n" + "stdout_sha256": "7624f212d7a0ad7c59ee0eb5b13fc2120e6ed66e5ae68a8206b55755ea4cc620", + "stdout_tail": ".0000000002803],\"endpoint_hash\":\"fnv1a64:9df35c35a99dcff9\"},\"parity\":{\"mode\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:9df35c35a99dcff9\",\"actual_hash\":\"fnv1a64:9df35c35a99dcff9\"},\"recovery\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:6429b034483d2f6e\",\"actual_hash\":\"fnv1a64:6429b034483d2f6e\"}},\"checkpoint_hash\":\"fnv1a64:b554f91256ee206a\",\"unsupported\":{\"type\":\"xyzac-trt\",\"code\":\"authoritative_kinematics_unsupported\"},\"range\":{\"code\":\"scheduler\",\"message_key\":\"authoritative_kinematics_range\"},\"geometry\":{\"radius\":130.25,\"rod_length\":269,\"tower_layout\":\"120-degree equilateral\"},\"boundaries\":[\"official kinematics oracle remains SOURCE_BLOCKED\",\"TRT/robot/Hexapod/Pentapod/switch/user remain queued\",\"missing ldelta HAL resources are retained as configuration gaps\"]}\n" }, { "command": [ @@ -287,8 +287,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "455bb34a953f78e42b3392d859e001086dfab4f6d48adb98ba3f9bc7c545f491", - "stdout_tail": "10.000000000717074,10.000000000717074],\"endpoint_hash\":\"fnv1a64:2b9d99b43dec0f25\"},\"parity\":{\"mode\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:2b9d99b43dec0f25\",\"actual_hash\":\"fnv1a64:2b9d99b43dec0f25\"},\"recovery\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:85b2a8f5970c7e32\",\"actual_hash\":\"fnv1a64:85b2a8f5970c7e32\"}},\"checkpoint_hash\":\"fnv1a64:95c6e490b47e0043\",\"singular\":{\"message_key\":\"authoritative_kinematics_singular\"},\"unsupported\":{\"type\":\"xyzac-trt\",\"code\":\"authoritative_kinematics_unsupported\"},\"geometry\":{\"platform_radius\":10,\"thigh_length\":10,\"shin_length\":14,\"foot_radius\":6},\"boundaries\":[\"official kinematics oracle remains SOURCE_BLOCKED\",\"TRT/robot/Hexapod/Pentapod/switch/user remain queued\",\"missing rdelta HAL resources remain configuration gaps\"]}\n" + "stdout_sha256": "656ff23b047c61f3698556d6cbadb8bb2c602889335d8ccf78575de76c92a5c8", + "stdout_tail": "10.000000000717074,10.000000000717074],\"endpoint_hash\":\"fnv1a64:1b5b7845cc279e47\"},\"parity\":{\"mode\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:1b5b7845cc279e47\",\"actual_hash\":\"fnv1a64:1b5b7845cc279e47\"},\"recovery\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:4456b951ad95e2ec\",\"actual_hash\":\"fnv1a64:4456b951ad95e2ec\"}},\"checkpoint_hash\":\"fnv1a64:db424cc8ecea9a45\",\"singular\":{\"message_key\":\"authoritative_kinematics_singular\"},\"unsupported\":{\"type\":\"xyzac-trt\",\"code\":\"authoritative_kinematics_unsupported\"},\"geometry\":{\"platform_radius\":10,\"thigh_length\":10,\"shin_length\":14,\"foot_radius\":6},\"boundaries\":[\"official kinematics oracle remains SOURCE_BLOCKED\",\"TRT/robot/Hexapod/Pentapod/switch/user remain queued\",\"missing rdelta HAL resources remain configuration gaps\"]}\n" }, { "command": [ @@ -300,8 +300,138 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "c586b8519a6fd27bda7b02bc7515eedebf01b1f92ea052a0b370896a1820f7e5", - "stdout_tail": "\"fixed_tick_phases\":[\"reconcile\",\"task\",\"tp\",\"kinematics\",\"motion\",\"hal_io\",\"callbacks\",\"publish\"]},\"parity\":{\"mode\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:17c6efd11e37c117\",\"actual_hash\":\"fnv1a64:17c6efd11e37c117\"},\"one_x_recovery\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:4ce46780a4d9d6a0\",\"actual_hash\":\"fnv1a64:4ce46780a4d9d6a0\"},\"fast_recovery\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:4ce46780a4d9d6a0\",\"actual_hash\":\"fnv1a64:4ce46780a4d9d6a0\"}},\"endpoint_hash\":\"fnv1a64:17c6efd11e37c117\",\"authoritative_hash\":\"fnv1a64:26649a9efc6276f0\",\"task_hash\":\"fnv1a64:27f97adf789499ce\",\"checkpoint_hash\":\"fnv1a64:7daa98c2234ad4fd\",\"boundaries\":[\"non-trivkins kinematics remain S6-03+\",\"full LinuxCNC canonical/native oracle remains explicitly blocked\"]}\n" + "stdout_sha256": "c40f3107fb25e8eebed74b826337908bc005077d1f0c2140dbde7c8bb3a87f9d", + "stdout_tail": "\"fixed_tick_phases\":[\"reconcile\",\"task\",\"tp\",\"kinematics\",\"motion\",\"hal_io\",\"callbacks\",\"publish\"]},\"parity\":{\"mode\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:2cb42c594943f07b\",\"actual_hash\":\"fnv1a64:2cb42c594943f07b\"},\"one_x_recovery\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:8d47fffda5edd3da\",\"actual_hash\":\"fnv1a64:8d47fffda5edd3da\"},\"fast_recovery\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:8d47fffda5edd3da\",\"actual_hash\":\"fnv1a64:8d47fffda5edd3da\"}},\"endpoint_hash\":\"fnv1a64:2cb42c594943f07b\",\"authoritative_hash\":\"fnv1a64:a3603848d8533846\",\"task_hash\":\"fnv1a64:27f97adf789499ce\",\"checkpoint_hash\":\"fnv1a64:61445b64e9d439c0\",\"boundaries\":[\"non-trivkins kinematics remain S6-03+\",\"full LinuxCNC canonical/native oracle remains explicitly blocked\"]}\n" + }, + { + "command": [ + "node", + "web/worker/test_axis3_selected_case.mjs" + ], + "label": "web/worker/test_axis3_selected_case.mjs", + "returncode": 0, + "status": "passed", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stderr_tail": "", + "stdout_sha256": "2fac97a3555e431b1e836727ac5315c684e73aae9dea9d40472dd13f098b1963", + "stdout_tail": "s-tutorial/3axis-tutorial.hal\",\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial\",\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial_mm.var\",\"linuxcnc/configs/nc_files/3dtest.ngc\",\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/tool.tbl\",\"linuxcnc/linuxcnc.gif\"],\"loader\":{\"fetched\":7,\"missing\":0,\"state_hash\":\"fnv1a64:94e21a5c7a684e63\"},\"startup\":{\"state\":\"TASK_READY\",\"hal_blocked\":0},\"operation_sequence\":[\"HOME\",\"JOG_START\",\"SIM_STEP\",\"JOG_STOP\",\"SET_OVERRIDE\",\"PROGRAM_LOAD\",\"PROGRAM_RUN\",\"CHECKPOINT\",\"SPINDLE_COMMAND\",\"COOLANT_COMMAND\",\"TOOL_PREPARE\",\"TOOL_CHANGE\",\"ESTOP\",\"IO_RECOVER\",\"RECOVER\"],\"observed\":{\"jog_x\":0.016,\"program_status\":\"completed\",\"checkpoint_hash\":\"fnv1a64:048eca9e5dd4e2df\",\"recovery\":\"recovered\",\"final_hash\":\"fnv1a64:4509e2eaf38657d7\"}}\n" + }, + { + "command": [ + "node", + "web/worker/test_axis3_tutorial_hal_adapter.mjs" + ], + "label": "web/worker/test_axis3_tutorial_hal_adapter.mjs", + "returncode": 0, + "status": "passed", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stderr_tail": "", + "stdout_sha256": "0b683d9b780d5074eb5196c9424311cbe158eecb51b352828aa6777ac62bf0b9", + "stdout_tail": "{\"schema\":\"axis3-tutorial-hal-adapter.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"hal\":{\"record_count\":37,\"loadrt\":[\"trivkins\",\"motmod\",\"ddt\",\"hypot\"],\"net_count\":21,\"web_equivalent\":\"./3axis-tutorial\",\"capability\":\"WEB-EQUIV\"},\"startup\":{\"state\":\"TASK_READY\",\"hal_blocked\":0,\"state_hash\":\"fnv1a64:a7f7c05da5d79f9d\"}}\n" + }, + { + "command": [ + "node", + "web/worker/test_axis3_tutorial_motion.mjs" + ], + "label": "web/worker/test_axis3_tutorial_motion.mjs", + "returncode": 0, + "status": "passed", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stderr_tail": "", + "stdout_sha256": "2c483fcd28e27f12d4cefab81fb4b5c070d57be6125b0d368fad0e09f5155b7e", + "stdout_tail": "{\"schema\":\"axis3-tutorial-motion.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"axes\":[\"X\",\"Y\",\"Z\"],\"home\":true,\"positions\":{\"x\":0.08,\"y\":-0.025,\"z\":-0.01},\"limits\":{\"blocked_code\":\"motion_faulted\",\"cleared\":true},\"following_error\":{\"blocked_code\":\"motion_faulted\"},\"snapshot_hash\":\"fnv1a64:5d30187951be107a\"}\n" + }, + { + "command": [ + "node", + "web/worker/test_axis3_tutorial_persistence.mjs" + ], + "label": "web/worker/test_axis3_tutorial_persistence.mjs", + "returncode": 0, + "status": "passed", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stderr_tail": "", + "stdout_sha256": "9ce3fc6579febc30a2a01bdcf451142e8ad4b2fdb27812380c7c17874dbb0858", + "stdout_tail": "{\"schema\":\"axis3-tutorial-persistence.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"exact_runtime_files\":[\"3axis-tutorial_mm.var\",\"tool.tbl\",\"position_mm.txt\"],\"checkpoint_hash\":\"fnv1a64:0c1d1b22db211940\",\"continuous_hash\":\"fnv1a64:9a5dd34aba49674c\",\"recovered_hash\":\"fnv1a64:9a5dd34aba49674c\",\"endpoint_equal\":true,\"blocked_bad_checkpoint\":\"blocked\",\"event_cursor\":6}\n" + }, + { + "command": [ + "node", + "web/worker/test_axis3_tutorial_program.mjs" + ], + "label": "web/worker/test_axis3_tutorial_program.mjs", + "returncode": 0, + "status": "passed", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stderr_tail": "", + "stdout_sha256": "c08d5b1b2c4102aba1b2eb67c474d98425b00676aa766f0c0a31229bfdd0f51f", + "stdout_tail": "{\"schema\":\"axis3-tutorial-program.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"units\":\"G21\",\"distance_modes\":[\"G90\",\"G91\"],\"run\":{\"paused\":true,\"resumed\":true,\"final\":\"completed\",\"state\":\"STOPPED\"},\"mdi\":{\"blocked_while_paused\":\"mdi_while_program_active\",\"status\":\"verified\",\"distance_mode\":90,\"position\":{\"x\":2,\"y\":3,\"z\":-1}},\"restart\":\"loaded\",\"step\":\"verified\",\"invalid_code\":\"program_interpreter\",\"final_hash\":\"fnv1a64:35cfce230eb7f9ee\"}\n" + }, + { + "command": [ + "node", + "web/worker/test_axis3_tutorial_sequence.mjs" + ], + "label": "web/worker/test_axis3_tutorial_sequence.mjs", + "returncode": 0, + "status": "passed", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stderr_tail": "", + "stdout_sha256": "5e6a434cfccb61dc73274c03ec7bee38f54acf32f07c8e5d0126634d2afcca67", + "stdout_tail": "MAND_RESULT\",\"status\":\"verified\",\"code\":\"sim_io_ok\",\"state_hash\":\"fnv1a64:135fb60b4301fb56\"},{\"operation\":\"TOOL_PREPARED_T1\",\"kind\":\"TOOL_PREPARED\",\"seq\":19,\"tick\":11,\"response_kind\":\"COMMAND_RESULT\",\"status\":\"verified\",\"code\":\"sim_io_ok\",\"state_hash\":\"fnv1a64:ddbc5ae64aecc679\"},{\"operation\":\"TOOL_CHANGE_T1\",\"kind\":\"TOOL_CHANGE\",\"seq\":20,\"tick\":11,\"response_kind\":\"COMMAND_RESULT\",\"status\":\"verified\",\"code\":\"sim_io_ok\",\"state_hash\":\"fnv1a64:6bdc3f14d85b7ae7\"},{\"operation\":\"TOOL_CHANGED_T1\",\"kind\":\"TOOL_CHANGED\",\"seq\":21,\"tick\":11,\"response_kind\":\"COMMAND_RESULT\",\"status\":\"verified\",\"code\":\"sim_io_ok\",\"state_hash\":\"fnv1a64:5c31c79e9a721120\"}],\"checkpoint_hash\":\"fnv1a64:402cfc050703daba\",\"endpoint_hash\":\"fnv1a64:af27acb00b551d90\",\"sequence_hash\":\"fnv1a64:b30e83e92d4ffe52\",\"first_error\":null}\n" + }, + { + "command": [ + "node", + "web/worker/test_axis3_tutorial_startup.mjs" + ], + "label": "web/worker/test_axis3_tutorial_startup.mjs", + "returncode": 0, + "status": "passed", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stderr_tail": "", + "stdout_sha256": "4285c54ef6aebb1ee183707228b60896b0625bbca0a2eceeae6966692abf861d", + "stdout_tail": "{\"schema\":\"axis3-tutorial-startup.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"transitions\":[\"NEW\",\"MOUNTED\",\"PARSED\",\"RESOLVED\",\"CORE_CREATED\",\"HAL_READY\",\"TASK_READY\"],\"loader_hash\":\"fnv1a64:94e21a5c7a684e63\",\"startup_hash\":\"fnv1a64:a7f7c05da5d79f9d\",\"deterministic\":true,\"generated\":[\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial_mm.var\"],\"hal_blocked\":0}\n" + }, + { + "command": [ + "node", + "web/worker/test_axis3_tutorial_tool_io.mjs" + ], + "label": "web/worker/test_axis3_tutorial_tool_io.mjs", + "returncode": 0, + "status": "passed", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stderr_tail": "", + "stdout_sha256": "17cb816f695a789fb79af47eb1625defea670342d1e9a7f9d25ac4b5a34c9315", + "stdout_tail": "{\"schema\":\"axis3-tutorial-tool-io.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"tool\":{\"current\":1,\"changed\":true,\"busy_change_code\":\"sim_io_state\"},\"spindle\":{\"command_rpm\":600,\"speed_rpm\":600,\"at_speed\":true},\"coolant\":{\"mist\":true,\"flood\":true,\"fault_blocked\":true},\"probe\":{\"state\":\"tripped\",\"position\":{\"x\":1,\"y\":2,\"z\":-3}},\"estop\":{\"blocked_code\":\"sim_io_estop\",\"recovered\":true},\"final_hash\":\"fnv1a64:ab919686d0398b3c\"}\n" + }, + { + "command": [ + "node", + "web/worker/test_axis3_visible_completion.mjs" + ], + "label": "web/worker/test_axis3_visible_completion.mjs", + "returncode": 0, + "status": "passed", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stderr_tail": "", + "stdout_sha256": "621f5980f9ee4c56dafd57e108d3dd1c410aa77e8716c5b574cf887875e0a52e", + "stdout_tail": "{\"schema\":\"axis3-visible-completion.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"final_pose\":{\"x\":0,\"y\":0,\"z\":0},\"task_state\":\"STOPPED\",\"program_status\":\"completed\"}\n" + }, + { + "command": [ + "node", + "web/worker/test_axis_state.mjs" + ], + "label": "web/worker/test_axis_state.mjs", + "returncode": 0, + "status": "passed", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stderr_tail": "", + "stdout_sha256": "c62347c275199275e041f6091b970fccc4981ef4775bde1a1d1853422bea456c", + "stdout_tail": "{\"schema\":\"axis-state-v1-sample\",\"status\":\"verified\",\"states\":[\"ESTOP\",\"ESTOP_RESET\",\"OFF\",\"ON\",\"HOME\",\"IDLE\",\"RUNNING\",\"PAUSED\"],\"cases\":[\"ESTOP\",\"ESTOP_RESET\",\"OFF\",\"ON\",\"HOME\",\"IDLE\",\"RUNNING\",\"PAUSED\"]}\n" }, { "command": [ @@ -313,8 +443,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "4eefbe39896d980f89864e3d0eb52746cf8095c6963c34b9a7db10745a16064f", - "stdout_tail": "{\"schema\":\"configuration-batch-s2-sample.v1\",\"status\":\"verified\",\"total\":264,\"ready\":56,\"blocked\":208,\"batch_hash\":\"fnv1a64:8e61897622fda66f\",\"native_state_hash\":\"fnv1a64:0181097fa846342d\",\"wasm_state_hash\":\"fnv1a64:0181097fa846342d\",\"worker_hash\":\"fnv1a64:152b83c479037715\",\"checkpoint_hash\":\"fnv1a64:0db2aa6e935a9fe0\"}\n" + "stdout_sha256": "dfdd33f0386a058f002a4fbedb6a963ac12af5dc870f7a608a3d03941a64c52c", + "stdout_tail": "{\"schema\":\"configuration-batch-s2-sample.v1\",\"status\":\"verified\",\"total\":264,\"ready\":56,\"blocked\":208,\"batch_hash\":\"fnv1a64:8e61897622fda66f\",\"native_state_hash\":\"fnv1a64:0181097fa846342d\",\"wasm_state_hash\":\"fnv1a64:0181097fa846342d\",\"worker_hash\":\"fnv1a64:152b83c479037715\",\"checkpoint_hash\":\"fnv1a64:9784c7e4930e619f\"}\n" }, { "command": [ @@ -378,8 +508,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "425fe6538f1755be1090462044a912f61c3bf9662e046b2e4f7293aef5a2bfd8", - "stdout_tail": "ef1346aa8381fbf\"},\"checkpoint_hash\":\"fnv1a64:b222d6f4e9de3a6c\"},{\"path\":\"linuxcnc/configs/sim/axis/corexy/corexy_by_hal.ini\",\"mode\":\"hal\",\"joints\":[6,2,3],\"endpoint_hash\":\"fnv1a64:ffc1d01c492a66c8\",\"hal_records\":100,\"mode_parity\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:ffc1d01c492a66c8\",\"actual_hash\":\"fnv1a64:ffc1d01c492a66c8\"},\"recovery\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:eef1346aa8381fbf\",\"actual_hash\":\"fnv1a64:eef1346aa8381fbf\"},\"checkpoint_hash\":\"fnv1a64:0815c73f9565dba6\"}],\"distinction\":{\"kins\":\"corexykins homes motor joints directly\",\"hal\":\"trivkins + corexy_by_hal routes Cartesian commands through alpha/beta HAL transform\"},\"boundary\":\"native/WASM corexy HAL formula oracle remains separately source-verified; other case families remain queued\"}\n" + "stdout_sha256": "fa5123c5384ba6c00e007f808b5d4390160c2eb9e04be1d0c33e68ee76afdfa0", + "stdout_tail": "a5082bab8c60d62\"},\"checkpoint_hash\":\"fnv1a64:da13bd1a376c3d3c\"},{\"path\":\"linuxcnc/configs/sim/axis/corexy/corexy_by_hal.ini\",\"mode\":\"hal\",\"joints\":[6,2,3],\"endpoint_hash\":\"fnv1a64:530e70e7e19940d5\",\"hal_records\":100,\"mode_parity\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:530e70e7e19940d5\",\"actual_hash\":\"fnv1a64:530e70e7e19940d5\"},\"recovery\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:ba5082bab8c60d62\",\"actual_hash\":\"fnv1a64:ba5082bab8c60d62\"},\"checkpoint_hash\":\"fnv1a64:4981fd79ff5691c3\"}],\"distinction\":{\"kins\":\"corexykins homes motor joints directly\",\"hal\":\"trivkins + corexy_by_hal routes Cartesian commands through alpha/beta HAL transform\"},\"boundary\":\"native/WASM corexy HAL formula oracle remains separately source-verified; other case families remain queued\"}\n" }, { "command": [ @@ -391,8 +521,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "598ff9e07a3fe026fa74bd8235eed6ba85647529df00ecad7a766d28293f0fa2", - "stdout_tail": "{\"schema\":\"device-io-worker-sample.v1\",\"status\":\"verified\",\"tick\":12,\"count\":40,\"state_hash\":\"fnv1a64:d303b40b131396ff\",\"worker_tick\":2,\"worker_state_hash\":\"fnv1a64:128eb22bfba5d6f9\",\"checkpoint_hash\":\"fnv1a64:7e2d1d371e931b90\"}\n" + "stdout_sha256": "730c8d6c97dde3895db9ba5b5676eed82c06b1b3238626343b141defb9cb62e5", + "stdout_tail": "{\"schema\":\"device-io-worker-sample.v1\",\"status\":\"verified\",\"tick\":12,\"count\":40,\"state_hash\":\"fnv1a64:d303b40b131396ff\",\"worker_tick\":2,\"worker_state_hash\":\"fnv1a64:128eb22bfba5d6f9\",\"checkpoint_hash\":\"fnv1a64:c14d5dcf0739ef6d\"}\n" }, { "command": [ @@ -417,8 +547,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "0f080b6ed7aae27332ff0a12fa59529d80f50aa79009a30786b3f014122d27e7", - "stdout_tail": "re0-wasm-m1-py-c1\",\"modes\":{\"one_x\":\"fnv1a64:7343c427e1cf704e\",\"fast\":\"fnv1a64:7343c427e1cf704e\",\"single_step\":\"fnv1a64:27c002ca0ebf7b9d\"},\"parity\":{\"mode_equal\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:7343c427e1cf704e\",\"actual_hash\":\"fnv1a64:7343c427e1cf704e\"},\"duplicate_operation_equal\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:7343c427e1cf704e\",\"actual_hash\":\"fnv1a64:7343c427e1cf704e\"},\"recovery_equal\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:20b9f9cb98bf3724\",\"actual_hash\":\"fnv1a64:20b9f9cb98bf3724\"},\"trace_equal\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:20b9f9cb98bf3724\",\"actual_hash\":\"fnv1a64:20b9f9cb98bf3724\"}},\"first_divergence\":null,\"checkpoint_hash\":\"fnv1a64:41acf37a2907683e\",\"trace_hash\":\"fnv1a64:bde4e32975b64638\"}\n" + "stdout_sha256": "311d1cc43763c482be035b5b12e41c95b3d1d3bfeef0f0899b07bf24d3051cd5", + "stdout_tail": "re0-wasm-m1-py-c1\",\"modes\":{\"one_x\":\"fnv1a64:719f460e6bd88fa3\",\"fast\":\"fnv1a64:719f460e6bd88fa3\",\"single_step\":\"fnv1a64:58e9db9b94754b91\"},\"parity\":{\"mode_equal\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:719f460e6bd88fa3\",\"actual_hash\":\"fnv1a64:719f460e6bd88fa3\"},\"duplicate_operation_equal\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:719f460e6bd88fa3\",\"actual_hash\":\"fnv1a64:719f460e6bd88fa3\"},\"recovery_equal\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:14b147c6f19bf5e3\",\"actual_hash\":\"fnv1a64:14b147c6f19bf5e3\"},\"trace_equal\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:14b147c6f19bf5e3\",\"actual_hash\":\"fnv1a64:14b147c6f19bf5e3\"}},\"first_divergence\":null,\"checkpoint_hash\":\"fnv1a64:ae9e6225ea49edde\",\"trace_hash\":\"fnv1a64:d96cb706cf4ec6bc\"}\n" }, { "command": [ @@ -430,8 +560,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "b3f8c93baa24c0f1316efc789816314a6a561293d562e4c85dc8ed12535e058e", - "stdout_tail": "rrent_line\":1},\"motion\":\"fnv1a64:dc0b0c746393a4a7\",\"plc\":\"fnv1a64:28dbc52f68ec6d9d\",\"hal\":\"fnv1a64:6fb2cf74df990a55\",\"device_io\":\"fnv1a64:51775281b8e08af6\",\"python\":\"fnv1a64:c47ee401132f56d5\",\"callbacks\":\"fnv1a64:b2f1b2de0f02b1f6\"},\"recover\":{\"tick\":1,\"task\":{\"state\":\"RUNNING\",\"mode\":\"AUTO\",\"program_status\":\"running\",\"current_line\":1},\"motion\":\"fnv1a64:b0abbf430b1b5fcf\",\"plc\":\"fnv1a64:2e69e18471ba84ff\",\"hal\":\"fnv1a64:d6c68eccad41cbc8\",\"device_io\":\"fnv1a64:70a8ea706931237f\",\"python\":\"fnv1a64:c481e001133185e0\",\"callbacks\":\"fnv1a64:9ff5f9ff975b0155\"}},\"parity\":{\"one_x_fast_equal\":true,\"recovery_status\":\"recovered\",\"recovery_tick\":true},\"hashes\":{\"one_x\":\"fnv1a64:1e0f880b840589cc\",\"fast\":\"fnv1a64:1e0f880b840589cc\",\"single_step\":\"fnv1a64:a05b759319c5805c\",\"recover\":\"fnv1a64:7de0a13acaed8601\"}}\n" + "stdout_sha256": "400b85463eca858c28e1151ae4b17adde298da674656e886a0f0b4e951e7b148", + "stdout_tail": "rrent_line\":1},\"motion\":\"fnv1a64:576eb95f1fe3dc0d\",\"plc\":\"fnv1a64:28dbc52f68ec6d9d\",\"hal\":\"fnv1a64:6fb2cf74df990a55\",\"device_io\":\"fnv1a64:51775281b8e08af6\",\"python\":\"fnv1a64:c47ee401132f56d5\",\"callbacks\":\"fnv1a64:b2f1b2de0f02b1f6\"},\"recover\":{\"tick\":1,\"task\":{\"state\":\"RUNNING\",\"mode\":\"AUTO\",\"program_status\":\"running\",\"current_line\":1},\"motion\":\"fnv1a64:652166fd17776833\",\"plc\":\"fnv1a64:2e69e18471ba84ff\",\"hal\":\"fnv1a64:d6c68eccad41cbc8\",\"device_io\":\"fnv1a64:70a8ea706931237f\",\"python\":\"fnv1a64:c481e001133185e0\",\"callbacks\":\"fnv1a64:9ff5f9ff975b0155\"}},\"parity\":{\"one_x_fast_equal\":true,\"recovery_status\":\"recovered\",\"recovery_tick\":true},\"hashes\":{\"one_x\":\"fnv1a64:234553c7663107d7\",\"fast\":\"fnv1a64:234553c7663107d7\",\"single_step\":\"fnv1a64:d190e23a4e7c7cfa\",\"recover\":\"fnv1a64:ab5153954a79d1a0\"}}\n" }, { "command": [ @@ -495,8 +625,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "52347a59adda31e23fefc60b458a1363edb8349d0711d8fd740afee6a0c68f6e", - "stdout_tail": "ash\":\"fnv1a64:81a5b31c84a8448f\"},\"checkpoint_hash\":\"fnv1a64:ad6882e8f5aa3f63\",\"fast_endpoint_hash\":\"fnv1a64:900a74795f5ed908\"},{\"path\":\"linuxcnc/configs/sim/axis/rdelta.ini\",\"type\":\"rotarydelta\",\"startup\":\"TASK_READY\",\"joints\":[10.000000000717074,10.000000000717074,10.000000000717074],\"endpoint_hash\":\"fnv1a64:8a7036de80c3e018\",\"mode_parity\":{\"equal\":true,\"expected_hash\":\"fnv1a64:8a7036de80c3e018\",\"actual_hash\":\"fnv1a64:8a7036de80c3e018\"},\"recovery\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:9d2824fd7d1e3a07\",\"actual_hash\":\"fnv1a64:9d2824fd7d1e3a07\"},\"checkpoint_hash\":\"fnv1a64:686cb2e861c384e4\",\"fast_endpoint_hash\":\"fnv1a64:8a7036de80c3e018\"}],\"boundary\":\"Delta lineardelta/rotarydelta only; official kinematics oracle remains separately blocked; other machine families queued\"}\n" + "stdout_sha256": "a46fcb0030991852d505eb0243a8b9e0f736a6b085f89186376862a5f41d568c", + "stdout_tail": "ash\":\"fnv1a64:9b214addf253ec33\"},\"checkpoint_hash\":\"fnv1a64:84a0a6512d681b81\",\"fast_endpoint_hash\":\"fnv1a64:8e59d93e9482b97c\"},{\"path\":\"linuxcnc/configs/sim/axis/rdelta.ini\",\"type\":\"rotarydelta\",\"startup\":\"TASK_READY\",\"joints\":[10.000000000717074,10.000000000717074,10.000000000717074],\"endpoint_hash\":\"fnv1a64:a7d4f465f2dcc6ba\",\"mode_parity\":{\"equal\":true,\"expected_hash\":\"fnv1a64:a7d4f465f2dcc6ba\",\"actual_hash\":\"fnv1a64:a7d4f465f2dcc6ba\"},\"recovery\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:233fdd933c098ef1\",\"actual_hash\":\"fnv1a64:233fdd933c098ef1\"},\"checkpoint_hash\":\"fnv1a64:133f9e6fc397beba\",\"fast_endpoint_hash\":\"fnv1a64:a7d4f465f2dcc6ba\"}],\"boundary\":\"Delta lineardelta/rotarydelta only; official kinematics oracle remains separately blocked; other machine families queued\"}\n" }, { "command": [ @@ -508,8 +638,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "2464cf8490e8949613032d56bbc76e9581ec95d2835f90a6f200adc694d3307c", - "stdout_tail": "{\"schema\":\"minimal-xyz-vertical-s8-04-sample.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/minimal_xyz.ini\",\"startup_state\":\"TASK_READY\",\"jog\":{\"tick\":10,\"x\":0.020000000000000004},\"override\":{\"feed\":0.5,\"spindle\":1.2},\"program\":{\"status\":\"completed\",\"line\":5,\"mdi\":\"verified\"},\"recovery\":{\"status\":\"recovered\",\"parity\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:b28f69a38b7079a5\",\"actual_hash\":\"fnv1a64:b28f69a38b7079a5\"}},\"estop\":{\"blocked_code\":\"sim_io_estop\",\"recovered\":\"READY\"},\"endpoint_hash\":\"fnv1a64:b28f69a38b7079a5\",\"checkpoint_hash\":\"fnv1a64:0d1d5fb6bfb2bcca\"}\n" + "stdout_sha256": "f0391a463bdd54e4e370e79e304c7690d9a496f054118239d52d8d4cce8af985", + "stdout_tail": "{\"schema\":\"minimal-xyz-vertical-s8-04-sample.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/minimal_xyz.ini\",\"startup_state\":\"TASK_READY\",\"jog\":{\"tick\":10,\"x\":0.020000000000000004},\"override\":{\"feed\":0.5,\"spindle\":1.2},\"program\":{\"status\":\"completed\",\"line\":5,\"mdi\":\"verified\"},\"recovery\":{\"status\":\"recovered\",\"parity\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:fe976bb5bf649a72\",\"actual_hash\":\"fnv1a64:fe976bb5bf649a72\"}},\"estop\":{\"blocked_code\":\"sim_io_estop\",\"recovered\":\"READY\"},\"endpoint_hash\":\"fnv1a64:fe976bb5bf649a72\",\"checkpoint_hash\":\"fnv1a64:de006d1f39f57b23\"}\n" }, { "command": [ @@ -521,8 +651,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "a846de304d6b8aa34f6a314bf184b7d40924bc51b1feb390de87327831948a00", - "stdout_tail": "motion_state=passed jog_x=0.02 fault=limit hash=fnv1a64:858bfb8944cbc2f9\n" + "stdout_sha256": "6345bb8b33d72a289a7aeaba0b8d6a1daad5572943e121f2dece373ae3dfc39c", + "stdout_tail": "motion_state=passed jog_x=0.02 fault=limit hash=fnv1a64:14e7aeaa4e55b254\n" }, { "command": [ @@ -560,8 +690,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "7146142940719f923d7c8a3fbc5719d93086adc8cc50d937e5ba11710bf75eac", - "stdout_tail": "{\"schema\":\"python-api-s3-sample.v1\",\"status\":\"verified\",\"manifest_version\":\"0.29.4\",\"bridge_hash\":\"fnv1a64:0b74fea2bb927042\",\"restored_hash\":\"fnv1a64:0b74fea2bb927042\",\"session_tick\":0,\"checkpoint_hash\":\"fnv1a64:c441d82e62acc5a3\"}\n" + "stdout_sha256": "ef20abcf638153460174e4d9c87c2cba65996aec22457505790f9c74d867ac0b", + "stdout_tail": "{\"schema\":\"python-api-s3-sample.v1\",\"status\":\"verified\",\"manifest_version\":\"0.29.4\",\"bridge_hash\":\"fnv1a64:0b74fea2bb927042\",\"restored_hash\":\"fnv1a64:0b74fea2bb927042\",\"session_tick\":0,\"checkpoint_hash\":\"fnv1a64:d4bfaf1076f3964c\"}\n" }, { "command": [ @@ -599,8 +729,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "a10351a0a28a6fa409bc17c009b6dd4c013334ba4f12d323aa08a1969d87c7f2", - "stdout_tail": "{\"schema\":\"python-callbacks-s3-sample.v1\",\"status\":\"verified\",\"calls\":3,\"errors\":8,\"callback_hash\":\"fnv1a64:67e7a5316f57b53d\",\"worker_hash\":\"fnv1a64:88c1a0e2e868fbc4\",\"checkpoint_hash\":\"fnv1a64:7c556a9f10303d3b\"}\n" + "stdout_sha256": "58a51e782520c9c2cdfdf60bade4c22544d76964603b13c148235b9170d41dd2", + "stdout_tail": "{\"schema\":\"python-callbacks-s3-sample.v1\",\"status\":\"verified\",\"calls\":3,\"errors\":8,\"callback_hash\":\"fnv1a64:67e7a5316f57b53d\",\"worker_hash\":\"fnv1a64:88c1a0e2e868fbc4\",\"checkpoint_hash\":\"fnv1a64:05ceecd05af67de8\"}\n" }, { "command": [ @@ -716,7 +846,7 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "a34cde732106bf9042852e7c386e5714a3a752bde0c30ad55141d431d498b343", + "stdout_sha256": "ba337b4cb358561db88e6263cf92ab3d15a61ded6b728bc8c1c062778c4fe398", "stdout_tail": "th\":\"linuxcnc/configs/sim/woodpecker/woodpecker.ini\",\"source_sha256\":\"1948487bf44a5c7d5b32186a6b9f330d55cb61bbab55da4a5834f95439cc0deb\",\"static_status\":\"blocked\",\"loader_status\":\"ready\",\"classification\":\"TASK_READY\",\"fetched_count\":6,\"missing\":[],\"runtime_fixtures\":[{\"case_path\":\"linuxcnc/configs/sim/woodpecker/woodpecker.ini\",\"resource_path\":\"linuxcnc/configs/sim/woodpecker/woodpecker.var\",\"key\":\"PARAMETER_FILE\",\"kind\":\"parameter_file\",\"reason\":\"LinuxCNC restores a missing parameter file as zeroed defaults and creates it on exit\",\"data_hash\":\"fnv1a64:14650fb0739d0383\"}],\"loader_state_hash\":\"fnv1a64:871dd4be4f95f9ce\",\"startup_state\":\"TASK_READY\",\"startup_state_hash\":\"fnv1a64:a327a522476bca50\",\"resource_blocked_count\":0,\"hal_record_count\":52,\"hal_blocked_count\":0,\"first_diagnostic\":null}]}\n" }, { @@ -807,8 +937,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "3752bd644a5ca9e34ea1ea97499176ffd6322fb1db29275bca7d7bbc54ac50fb", - "stdout_tail": "ae3105528e63e8cd307926ddf250ef9f394117b8b8a30f45d859f6fc\",\"status\":\"READY\",\"startup_preflight\":\"static_ready\",\"resource_status\":\"unindexed\",\"diagnostic_code\":null,\"missing_count\":0,\"unresolved_count\":0}],\"counts\":{\"total\":264,\"sim_configs\":216,\"test_configs\":48,\"READY\":56,\"BLOCKED\":208},\"workflow\":{\"batch_schema\":\"configuration-startup-batch.v1\",\"batch_state_hash\":\"fnv1a64:8e61897622fda66f\",\"restore_equal\":true,\"diagnostic_count\":208,\"entrypoint_hash\":\"fnv1a64:b3cdda0d2d756943\"},\"boundaries\":[\"READY is static startup preflight, not an official LinuxCNC runtime oracle\",\"BLOCKED rows retain resource/kinematics diagnostics and are never promoted\",\"full native/WASM/Worker startup probes remain represented by configuration-startup-runtime evidence\"],\"aggregate_hash\":\"fnv1a64:57da6a5de56b4db0\"}\n" + "stdout_sha256": "f307fe6a839fd2db0676cd4659ab70511b5e451837b3a8788bba52dd68874b4d", + "stdout_tail": "ae3105528e63e8cd307926ddf250ef9f394117b8b8a30f45d859f6fc\",\"status\":\"READY\",\"startup_preflight\":\"static_ready\",\"resource_status\":\"unindexed\",\"diagnostic_code\":null,\"missing_count\":0,\"unresolved_count\":0}],\"counts\":{\"total\":264,\"sim_configs\":216,\"test_configs\":48,\"READY\":56,\"BLOCKED\":208},\"workflow\":{\"batch_schema\":\"configuration-startup-batch.v1\",\"batch_state_hash\":\"fnv1a64:8e61897622fda66f\",\"restore_equal\":true,\"diagnostic_count\":208,\"entrypoint_hash\":\"fnv1a64:ff13e1f1469c5329\"},\"boundaries\":[\"READY is static startup preflight, not an official LinuxCNC runtime oracle\",\"BLOCKED rows retain resource/kinematics diagnostics and are never promoted\",\"full native/WASM/Worker startup probes remain represented by configuration-startup-runtime evidence\"],\"aggregate_hash\":\"fnv1a64:c797e906739b0f50\"}\n" }, { "command": [ @@ -820,8 +950,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "69bd465d2249633e2b19c3425d3470751fc26ccb5d2a1657fdaed215762e6683", - "stdout_tail": "{\"schema\":\"linuxcnc-source-do-s7-01-sample.v1\",\"status\":\"verified\",\"context_id\":\"linuxcnc-2.10.0-pre0-wasm-m1-py-c1\",\"source\":{\"path\":\"linuxcnc/tests/interp/do-while-break/test.ngc\",\"source_hash\":\"fnv1a64:23daa58d09abf802\"},\"normal\":{\"status\":\"completed\",\"steps\":19,\"named_parameters\":{\"one_pass\":1,\"two_pass\":1,\"three_pass\":1},\"interpreter_state_hash\":\"fnv1a64:0000000086efefe9\"},\"pause_resume\":{\"status\":\"completed\",\"steps\":19,\"hash_equal\":true},\"errors\":{\"malformed_enddo\":\"program_enddo_without_do\",\"depth\":\"program_control_depth\"},\"recovery\":{\"equal\":true,\"difference\":null,\"checkpoint_hash\":\"fnv1a64:f581242f3cd81a99\"},\"boundaries\":[\"LinuxCNC canonical expected output remains SOURCE_BLOCKED\",\"G64/G61 remains S7 follow-up\",\"host/Python/real hardware paths unchanged\"]}\n" + "stdout_sha256": "9891132a901dbc7fdea11734c28a15eba29a1df0d4693662c8f477c9ae968d9c", + "stdout_tail": "{\"schema\":\"linuxcnc-source-do-s7-01-sample.v1\",\"status\":\"verified\",\"context_id\":\"linuxcnc-2.10.0-pre0-wasm-m1-py-c1\",\"source\":{\"path\":\"linuxcnc/tests/interp/do-while-break/test.ngc\",\"source_hash\":\"fnv1a64:23daa58d09abf802\"},\"normal\":{\"status\":\"completed\",\"steps\":19,\"named_parameters\":{\"one_pass\":1,\"two_pass\":1,\"three_pass\":1},\"interpreter_state_hash\":\"fnv1a64:0000000086efefe9\"},\"pause_resume\":{\"status\":\"completed\",\"steps\":19,\"hash_equal\":true},\"errors\":{\"malformed_enddo\":\"program_enddo_without_do\",\"depth\":\"program_control_depth\"},\"recovery\":{\"equal\":true,\"difference\":null,\"checkpoint_hash\":\"fnv1a64:903bc2b8647323d8\"},\"boundaries\":[\"LinuxCNC canonical expected output remains SOURCE_BLOCKED\",\"G64/G61 remains S7 follow-up\",\"host/Python/real hardware paths unchanged\"]}\n" }, { "command": [ @@ -833,8 +963,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "320a8130b3e2121a1f8da02374cf8ed6d52998e41cbb34ee85f94bd6be132a0b", - "stdout_tail": "rols\":[{\"line\":1,\"mode\":\"continuous\",\"path_tolerance\":0,\"naive_cam_tolerance\":0},{\"line\":2,\"mode\":\"continuous\",\"path_tolerance\":1,\"naive_cam_tolerance\":0},{\"line\":3,\"mode\":\"continuous\",\"path_tolerance\":1,\"naive_cam_tolerance\":2},{\"line\":4,\"mode\":\"exact_path\",\"path_tolerance\":0,\"naive_cam_tolerance\":0},{\"line\":5,\"mode\":\"exact_stop\",\"path_tolerance\":0,\"naive_cam_tolerance\":0}],\"final_motion_control_hash\":\"fnv1a64:e0364fe4a01823da\",\"errors\":{\"negative_tolerance\":\"program_motion_control_range\",\"unsupported_g61\":\"program_motion_control_syntax\"},\"recovery\":{\"equal\":true,\"difference\":null,\"checkpoint_hash\":\"fnv1a64:6374004ebf3ac75b\"},\"boundaries\":[\"LinuxCNC canonical expected output remains SOURCE_BLOCKED\",\"TP blend/exact-stop numeric parity remains follow-up\",\"Safari/Firefox are out of scope\"]}\n" + "stdout_sha256": "b6eaaedd97cb826234070b019057a05982657d3cb20cfe2a8a823a33a7f5bd74", + "stdout_tail": "rols\":[{\"line\":1,\"mode\":\"continuous\",\"path_tolerance\":0,\"naive_cam_tolerance\":0},{\"line\":2,\"mode\":\"continuous\",\"path_tolerance\":1,\"naive_cam_tolerance\":0},{\"line\":3,\"mode\":\"continuous\",\"path_tolerance\":1,\"naive_cam_tolerance\":2},{\"line\":4,\"mode\":\"exact_path\",\"path_tolerance\":0,\"naive_cam_tolerance\":0},{\"line\":5,\"mode\":\"exact_stop\",\"path_tolerance\":0,\"naive_cam_tolerance\":0}],\"final_motion_control_hash\":\"fnv1a64:e0364fe4a01823da\",\"errors\":{\"negative_tolerance\":\"program_motion_control_range\",\"unsupported_g61\":\"program_motion_control_syntax\"},\"recovery\":{\"equal\":true,\"difference\":null,\"checkpoint_hash\":\"fnv1a64:063983d373f79610\"},\"boundaries\":[\"LinuxCNC canonical expected output remains SOURCE_BLOCKED\",\"TP blend/exact-stop numeric parity remains follow-up\",\"Safari/Firefox are out of scope\"]}\n" }, { "command": [ @@ -872,8 +1002,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "21f2950bff929b403137feddce1e4b27e7535e8b58bc7493b4bc4ca43dce359b", - "stdout_tail": "stop-ui.py\",\"bytes\":1558,\"sha256\":\"c998e3ddf86894c9603e633023ffc85d742784b963e18a4c4f57f21a80a513d6\"},{\"path\":\"linuxcnc/tests/classicladder/estop/checkresult\",\"bytes\":121,\"sha256\":\"583883dc785e6aab0f1313cfc20ed86e9c362082b5c47514264abbbbf973c80e\"}],\"result\":{\"reason\":\"ClassicLadder GUI/runtime is host excluded\"},\"result_hash\":\"fnv1a64:6d24438783646acd\"}],\"counts\":{\"PASSED\":3,\"SOURCE_BLOCKED\":2,\"HOST_EXCLUDED\":1,\"WORKER_TASK_MOTION_PENDING\":1},\"fixture_file_count\":30,\"boundaries\":[\"startup tool table resolution remains SOURCE_BLOCKED until full Task/configuration startup is available\",\"motion logger abort remains WORKER_TASK_MOTION_PENDING; no GUI or wall-clock result is promoted\",\"ClassicLadder and realtime hardware paths remain HOST_EXCLUDED\"],\"aggregate_hash\":\"fnv1a64:77ddfb5c858175d5\"}\n" + "stdout_sha256": "63ba439455c67abf90a2e11bd9a7e0c90155d687242235e314f662c8cea3bb74", + "stdout_tail": "stop-ui.py\",\"bytes\":1558,\"sha256\":\"c998e3ddf86894c9603e633023ffc85d742784b963e18a4c4f57f21a80a513d6\"},{\"path\":\"linuxcnc/tests/classicladder/estop/checkresult\",\"bytes\":121,\"sha256\":\"583883dc785e6aab0f1313cfc20ed86e9c362082b5c47514264abbbbf973c80e\"}],\"result\":{\"reason\":\"ClassicLadder GUI/runtime is host excluded\"},\"result_hash\":\"fnv1a64:6d24438783646acd\"}],\"counts\":{\"PASSED\":3,\"SOURCE_BLOCKED\":2,\"HOST_EXCLUDED\":1,\"WORKER_TASK_MOTION_PENDING\":1},\"fixture_file_count\":30,\"boundaries\":[\"startup tool table resolution remains SOURCE_BLOCKED until full Task/configuration startup is available\",\"motion logger abort remains WORKER_TASK_MOTION_PENDING; no GUI or wall-clock result is promoted\",\"ClassicLadder and realtime hardware paths remain HOST_EXCLUDED\"],\"aggregate_hash\":\"fnv1a64:8fadeda4e92379d8\"}\n" }, { "command": [ @@ -885,8 +1015,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "f8f4874e925c2e2ecc92a5100cf893f2ad699af319d4e68a26ac3681363ed138", - "stdout_tail": "baf06fbaf9d657253ca29fcee549b33bda61fb777a73c0ffb29170c9b0ae7c26\"}],\"modes\":{\"one_x\":\"fnv1a64:206c8a33392d29de\",\"fast\":\"fnv1a64:206c8a33392d29de\"},\"differences\":{\"first_divergence\":null,\"mode\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:206c8a33392d29de\",\"actual_hash\":\"fnv1a64:206c8a33392d29de\"},\"recovery\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:04f657933df9677c\",\"actual_hash\":\"fnv1a64:04f657933df9677c\"},\"trace_import\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:04f657933df9677c\",\"actual_hash\":\"fnv1a64:04f657933df9677c\"}},\"recovery\":{\"checkpoint_hash\":\"fnv1a64:4cdc2375892b77e5\",\"continuous_hash\":\"fnv1a64:04f657933df9677c\",\"recovered_hash\":\"fnv1a64:04f657933df9677c\",\"trace_hash\":\"fnv1a64:4fb723efd6fc794e\",\"status\":\"recovered\"},\"status\":\"verified\"}\n" + "stdout_sha256": "77024701e656806d59c24a6e9a6e01d54f3db6fffc57c922e32fb138d9125eb9", + "stdout_tail": "baf06fbaf9d657253ca29fcee549b33bda61fb777a73c0ffb29170c9b0ae7c26\"}],\"modes\":{\"one_x\":\"fnv1a64:99dc591d7e12cd3b\",\"fast\":\"fnv1a64:99dc591d7e12cd3b\"},\"differences\":{\"first_divergence\":null,\"mode\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:99dc591d7e12cd3b\",\"actual_hash\":\"fnv1a64:99dc591d7e12cd3b\"},\"recovery\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:3bc177462024d9c5\",\"actual_hash\":\"fnv1a64:3bc177462024d9c5\"},\"trace_import\":{\"equal\":true,\"difference\":null,\"expected_hash\":\"fnv1a64:3bc177462024d9c5\",\"actual_hash\":\"fnv1a64:3bc177462024d9c5\"}},\"recovery\":{\"checkpoint_hash\":\"fnv1a64:8f07df8a96d5ba66\",\"continuous_hash\":\"fnv1a64:3bc177462024d9c5\",\"recovered_hash\":\"fnv1a64:3bc177462024d9c5\",\"trace_hash\":\"fnv1a64:c265e4ec16e3375f\",\"status\":\"recovered\"},\"status\":\"verified\"}\n" }, { "command": [ @@ -911,8 +1041,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "9ff366e72a2ad6c5da841685c138248ae4c06de4913e40438788df21b14e5ec7", - "stdout_tail": "trace_export=passed seq=6 tick=3 hash=fnv1a64:cab1c51b0840e109\n" + "stdout_sha256": "6f5c23fa145b3e4543dd00789cdb0ad1158a7624d856696ba8f526ded67ed54a", + "stdout_tail": "trace_export=passed seq=6 tick=3 hash=fnv1a64:7ffc03b6a8d47c0e\n" }, { "command": [ @@ -927,6 +1057,110 @@ "stdout_sha256": "a5a07a0d0e2b847212c0028807022fa4c6e28ba0d1d56c74d53653e821eb4059", "stdout_tail": "state_hash\":\"fnv1a64:c94eac708756480a\",\"replay_state_hash\":\"fnv1a64:c94eac708756480a\",\"state_hash_equal\":true,\"final\":{\"ready\":true,\"estop\":false,\"fault\":false,\"fault_reason\":0,\"tick\":40,\"recovery_epoch\":1,\"spindle_count\":1,\"tool_capacity\":1001,\"spindles\":[{\"command_rpm\":0,\"speed_rpm\":0,\"at_speed\":true,\"enabled\":false,\"inhibit\":false,\"amp_fault\":false,\"brake\":true,\"orient\":false,\"locked\":false,\"is_oriented\":false,\"orient_angle\":0,\"orient_mode\":0,\"orient_fault\":0,\"index_enable\":false,\"revs\":40}],\"tool\":{\"state\":\"idle\",\"current\":7,\"prepared\":7,\"pocket\":3,\"prepare\":false,\"prepared_ack\":false,\"changed\":false,\"fault\":false,\"reason\":0},\"probe\":{\"state\":\"tripped\",\"type\":0,\"input\":true,\"tripped\":true,\"tick\":40,\"position\":{\"x\":1,\"y\":2,\"z\":3}},\"limits\":{\"x-positive\":true},\"last_error\":\"sim_io_ok\"}}\n" }, + { + "command": [ + "node", + "web/worker/test_wasm_canonical_state.mjs" + ], + "label": "web/worker/test_wasm_canonical_state.mjs", + "returncode": 0, + "status": "passed", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stderr_tail": "", + "stdout_sha256": "48b41b755925d7065f99db25e11f042789f917d15107de23c3ef980c9f2978ba", + "stdout_tail": "{\"schema\":\"wasm-canonical-state-t4.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"task_authority\":\"wasm_task_canonical\",\"home_authority\":\"wasm_home_limit\",\"home_sequence\":5,\"home_tick\":1,\"motion_tick\":3,\"home_limit_tick\":6,\"js_task_tamper_ignored\":true}\n" + }, + { + "command": [ + "node", + "web/worker/test_wasm_canonical_state_checkpoint.mjs" + ], + "label": "web/worker/test_wasm_canonical_state_checkpoint.mjs", + "returncode": 0, + "status": "passed", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stderr_tail": "", + "stdout_sha256": "401cfde5f23e77d8d938689c3a3ac6fe7777bafb118d3075ff353e99993ccdf7", + "stdout_tail": "{\"schema\":\"wasm-canonical-state-checkpoint-t4.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"checkpoint_hash\":\"fnv1a64:9d9ea2a624703b46\",\"continuous_hash\":\"fnv1a64:2d696fc22acbbd38\",\"recovered_hash\":\"fnv1a64:2d696fc22acbbd38\",\"endpoint_equal\":true,\"trace_import\":\"verified\",\"damaged_home_state\":\"blocked\"}\n" + }, + { + "command": [ + "node", + "web/worker/test_wasm_feature_matrix_t5.mjs" + ], + "label": "web/worker/test_wasm_feature_matrix_t5.mjs", + "returncode": 0, + "status": "passed", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stderr_tail": "", + "stdout_sha256": "f07bf812572de1ada298857bd5064d26fec8e8037006cc6b29a6ac28746a777d", + "stdout_tail": "{\"schema\":\"wasm-feature-matrix-t5.v1\",\"status\":\"verified\",\"task_id\":\"T5-FULL-MATRIX-01\",\"domains\":9,\"blocked_domains\":9,\"blocked_tests\":0,\"release_action\":\"STOP_RELEASE\"}\n" + }, + { + "command": [ + "node", + "web/worker/test_wasm_function_closure_t6.mjs" + ], + "label": "web/worker/test_wasm_function_closure_t6.mjs", + "returncode": 0, + "status": "passed", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stderr_tail": "", + "stdout_sha256": "cf67e5a0c4994a114d0d5dc2c4514e5f2a7738f83491e3c08b199f2d175e71cc", + "stdout_tail": "{\"schema\":\"wasm-function-closure-t6.v1\",\"status\":\"verified\",\"rows\":8,\"blocked\":8,\"failure_samples\":4,\"release_action\":\"STOP_RELEASE\"}\n" + }, + { + "command": [ + "node", + "web/worker/test_wasm_motion_authority.mjs" + ], + "label": "web/worker/test_wasm_motion_authority.mjs", + "returncode": 0, + "status": "passed", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stderr_tail": "", + "stdout_sha256": "34f090eef3d78d77d192658fbc279f0bdd72fa038eef44cfdc0138d6068d3d8c", + "stdout_tail": "{\"schema\":\"wasm-motion-authority-t3.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"motion_authority\":\"wasm\",\"paused_position\":{\"x\":1.073312629199899,\"y\":0.5366563145999494,\"z\":0},\"resumed_position\":{\"x\":2.146625258399798,\"y\":1.073312629199899,\"z\":0},\"abort_queue_depth\":0,\"js_authority_calls\":0}\n" + }, + { + "command": [ + "node", + "web/worker/test_wasm_motion_checkpoint.mjs" + ], + "label": "web/worker/test_wasm_motion_checkpoint.mjs", + "returncode": 0, + "status": "passed", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stderr_tail": "", + "stdout_sha256": "585c73a4f82d9b3e50de6a72279c0b01d21109d482f2f2f708ce8f5062127a0e", + "stdout_tail": "{\"schema\":\"wasm-motion-checkpoint-t3.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"checkpoint_hash\":\"fnv1a64:47ff90f3ab07c4c6\",\"continuous_hash\":\"fnv1a64:2c910bab8ffdf73a\",\"recovered_hash\":\"fnv1a64:2c910bab8ffdf73a\",\"endpoint_equal\":true,\"trace_import\":\"verified\",\"checkpoint_queue_depth\":1,\"blocked_damaged_checkpoint\":\"blocked\"}\n" + }, + { + "command": [ + "node", + "web/worker/test_wasm_motion_event.mjs" + ], + "label": "web/worker/test_wasm_motion_event.mjs", + "returncode": 0, + "status": "passed", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stderr_tail": "", + "stdout_sha256": "d2dbbe772565019da46acde28215f48c5ed3c58f5695d23f91ea2c7ee98d7106", + "stdout_tail": "{\"schema\":\"wasm-motion-event.v1\",\"status\":\"verified\",\"events\":3,\"last_motion\":{\"source_line\":4,\"start\":{\"x\":10,\"y\":5,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":10,\"y\":5,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":11,\"y\":6,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":120,\"acceleration\":0,\"motion_type\":2,\"turn\":-1,\"source\":\"wasm_interpreter_canonical_motion\"}}\n" + }, + { + "command": [ + "node", + "web/worker/test_wasm_preview.mjs" + ], + "label": "web/worker/test_wasm_preview.mjs", + "returncode": 0, + "status": "passed", + "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stderr_tail": "", + "stdout_sha256": "3d4f47d5771b91f8c20102a75233260831a89d529043eee5b615f6c4522e7278", + "stdout_tail": "{\"schema\":\"wasm-preview.v1\",\"status\":\"verified\",\"source_path\":\"linuxcnc/nc_files/3dtest.ngc\",\"source_lines\":55,\"segment_count\":49,\"arc_lines\":[6,23,39],\"source\":\"wasm_interpreter_canonical_motion\"}\n" + }, { "command": [ "node", @@ -963,8 +1197,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "54a051fd656a789d5dad2894f32a1ef697a32ade0ec24df458deb73e939c6f01", - "stdout_tail": "{\"schema\":\"workspace-session-integration-sample.v1\",\"context_id\":\"linuxcnc-2.10.0-pre0-wasm-m1-py-c1\",\"session_state_hash\":\"fnv1a64:62481fd6982a0dae\",\"recovery_state_hash\":\"fnv1a64:62481fd6982a0dae\",\"checkpoint_hash\":\"fnv1a64:c2382bab031cd9ab\",\"package_v1_hash\":\"fnv1a64:c8f4e248d7f64602\",\"package_v2_hash\":\"fnv1a64:3e075699c047abe7\",\"round_trip_equal\":true,\"event_cursor\":12,\"journal_hash\":\"fnv1a64:1064d5ed6ce0552a\",\"operation_count\":4}\n" + "stdout_sha256": "b589d70b82f26e0d99cea3add135474fa60fdb2e05fb61fdfa8ad666fafc9343", + "stdout_tail": "{\"schema\":\"workspace-session-integration-sample.v1\",\"context_id\":\"linuxcnc-2.10.0-pre0-wasm-m1-py-c1\",\"session_state_hash\":\"fnv1a64:59fb9e23862123c3\",\"recovery_state_hash\":\"fnv1a64:59fb9e23862123c3\",\"checkpoint_hash\":\"fnv1a64:a0b90768e25b6aee\",\"package_v1_hash\":\"fnv1a64:d9aa9dc670baec56\",\"package_v2_hash\":\"fnv1a64:e5b1e118c32572b9\",\"round_trip_equal\":true,\"event_cursor\":12,\"journal_hash\":\"fnv1a64:1064d5ed6ce0552a\",\"operation_count\":4}\n" }, { "command": [ @@ -1041,8 +1275,8 @@ "status": "passed", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "stderr_tail": "", - "stdout_sha256": "ef3b8b2e5f03c995cc26966d2a67952be05989ce07bf408bbbd8f6b76d88fc7e", - "stdout_tail": "{\"schema\":\"browser-performance-sample.v1\",\"files\":[{\"file\":\"index.html\",\"bytes\":367},{\"file\":\"app.mjs\",\"bytes\":16777},{\"file\":\"worker-client.mjs\",\"bytes\":7508},{\"file\":\"styles.css\",\"bytes\":2731},{\"file\":\"controls.mjs\",\"bytes\":1337},{\"file\":\"state-panel.mjs\",\"bytes\":5083},{\"file\":\"project-workflow.mjs\",\"bytes\":2238},{\"file\":\"recovery-panel.mjs\",\"bytes\":1689},{\"file\":\"device-panel.mjs\",\"bytes\":1587},{\"file\":\"hal-panel.mjs\",\"bytes\":826},{\"file\":\"machine-view.mjs\",\"bytes\":2083}],\"total_bytes\":42226,\"stable\":true}\n" + "stdout_sha256": "4a0c3a9b16f07f7e48b987d8ad4bf4613a53c8a81619a00ebad2bbf2b2ed17ee", + "stdout_tail": "{\"schema\":\"browser-performance-sample.v1\",\"files\":[{\"file\":\"index.html\",\"bytes\":367},{\"file\":\"app.mjs\",\"bytes\":16849},{\"file\":\"worker-client.mjs\",\"bytes\":7508},{\"file\":\"styles.css\",\"bytes\":2731},{\"file\":\"controls.mjs\",\"bytes\":1337},{\"file\":\"state-panel.mjs\",\"bytes\":5083},{\"file\":\"project-workflow.mjs\",\"bytes\":2238},{\"file\":\"recovery-panel.mjs\",\"bytes\":1689},{\"file\":\"device-panel.mjs\",\"bytes\":1587},{\"file\":\"hal-panel.mjs\",\"bytes\":826},{\"file\":\"machine-view.mjs\",\"bytes\":2083}],\"total_bytes\":42298,\"stable\":true}\n" }, { "command": [ @@ -1178,16 +1412,16 @@ "counts": { "app_tests": 16, "failed": 0, - "passed": 74, - "total": 74, - "worker_tests": 56 + "passed": 92, + "total": 92, + "worker_tests": 74 }, "policy": "build, native CTest, Worker and App regressions are implementation evidence only", "status": "verified" } }, "release_action": "STOP_RELEASE", - "report_sha256": "b2833b218a16b5dde284f1709f8ec261e3add356762b063f018f8fbafc4f6c64", + "report_sha256": "b2230390241a9fb8fb723af3847baeec251e8b49526d264c8bd5ccef4e5d3133", "schema": "full-case-acceptance-s8-10-report.v1", "status": "blocked", "task_id": "S8-10" diff --git a/coverage/full-configuration-startup-matrix.report.json b/coverage/full-configuration-startup-matrix.report.json index 6c3f800..354a974 100644 --- a/coverage/full-configuration-startup-matrix.report.json +++ b/coverage/full-configuration-startup-matrix.report.json @@ -72,7 +72,7 @@ "bytes": 3301, "path": "coverage/frontend-m4.report.json", "role": "frontend_m4", - "sha256": "f38acbc255ad4e33e6debcd258c03ed392faabd63892ace0cf843c246ee888eb", + "sha256": "b10b877e37dce85bbceeff1abe3118760fbcb29e663944b0880e0b83d26a4f15", "status": "present" }, { @@ -5014,7 +5014,7 @@ ], "next_transition": "execute REG-03 recovery and offline fault matrix", "release_action": "STOP_RELEASE", - "report_sha256": "548d027932b79f559a6edd1656a284c17debc27151d46ab2908db34ecd9feec4", + "report_sha256": "6917df860af93b609ef8b3db66cab5fb3777ee122495e0f992fa22d3f594b291", "schema": "full-configuration-startup-matrix.v1", "status": "partial", "task_id": "REG-01" diff --git a/coverage/ini-parser.report.json b/coverage/ini-parser.report.json index 0586f0f..4b80937 100644 --- a/coverage/ini-parser.report.json +++ b/coverage/ini-parser.report.json @@ -23,7 +23,7 @@ "bytes": 3779, "path": "coverage/workspace-runtime.report.json", "role": "workspace_report", - "sha256": "229cc2b3e16c29acfa8c8936f118268f5e6738356d1883520b17b2d7b41c0bc8", + "sha256": "7dc48c86ead9e8440c485d13e94e472948675e3ad66a189c998bedfa7d98aa53", "status": "present" }, { @@ -64,7 +64,7 @@ ], "release_action": "STOP_RELEASE", "release_status": "blocked_external_owner", - "report_sha256": "f8ac5f6dc55cd0a8ed69b7f7aad84fcdedca60612bfdccfec7c9b670d0d5003e", + "report_sha256": "471e88e78e7661f6c2ec14c40184ec97372f4ca43ac470992f802ebdafd5f2b6", "schema": "ini-parser-report.v1", "status": "passed", "task_id": "P2-02", diff --git a/coverage/linuxcnc-source-semantics-contract.json b/coverage/linuxcnc-source-semantics-contract.json new file mode 100644 index 0000000..1b1c583 --- /dev/null +++ b/coverage/linuxcnc-source-semantics-contract.json @@ -0,0 +1,146 @@ +{ + "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", + "control_panel_contract": { + "authority": "Worker_snapshot_and_configuration_startup", + "classifications": [ + "TASK_READY", + "BLOCKED_HAL", + "BLOCKED_RESOURCE", + "BLOCKED_INI", + "BLOCKED_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" + ] + }, + "inputs": [ + { + "path": "context-lock.json", + "role": "context_lock", + "sha256": "bf6651f8d1803f2219d40e61f5e86dcf0821862d087eec2e19f99f593561353e", + "status": "present" + }, + { + "path": "web/workspace/sim-case-loader.mjs", + "role": "implementation", + "sha256": "5204f11177b45e452a972f3adaf0aba4dafd8b1b4844f40a42513652e574e20a", + "status": "present" + }, + { + "path": "web/workspace/configuration-startup.mjs", + "role": "startup_state_machine", + "sha256": "e375abb6dcc0e0657f038356a68f5932c360bcf7fdf5ff906218a37b9012924a", + "status": "present" + }, + { + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "linuxcnc_source", + "sha256": "328c038a1e8fefd952e53c8e398e3fa98b04602351c44318edd1b044e8f63ffa", + "status": "present" + }, + { + "path": "linuxcnc/src/emc/rs274ngc/rs274ngc_pre.cc", + "role": "linuxcnc_source", + "sha256": "73935d127557c0e8f9da6267f629351ec5198fbce05c665c678819383901d61b", + "status": "present" + }, + { + "path": "linuxcnc/src/emc/task/emctask.cc", + "role": "linuxcnc_source", + "sha256": "db3996e57edbf042cc0ff85580ac62728491fb55e7236219a3a76bfbee6beee9", + "status": "present" + } + ], + "paths": [ + { + "path": "linuxcnc/configs/nc_files/3dtest.ngc", + "role": "selected_case_program_copy", + "sha256": "fdd71703dc7658711b67de12c89379e3a9340cacb16382dbd655dab2103bf566", + "status": "present" + }, + { + "path": "linuxcnc/nc_files/lathe_pawn.ngc", + "role": "source_program_found_at_root", + "sha256": "f6afe0f4af4f7e1d2913513a045489b3203d7c0664041a6e573b5d649147da6c", + "status": "present" + }, + { + "path": "linuxcnc/configs/sim/nc_files/examples/lathe_pawn.ngc", + "role": "requested_but_absent_exact_path", + "sha256": null, + "status": "missing" + } + ], + "report_sha256": "ff1ce5e1c3251b82e4298a084146e1eb64fb608ef5f7922c69cec10faba77d83", + "rules": [ + { + "behavior": "ui_initial_file_hint_only", + "id": "axis_open_file_optional", + "key": "OPEN_FILE", + "missing_behavior": "optional_missing", + "section": "DISPLAY", + "source": { + "evidence": "open only when os.path.exists(initialfile)", + "line": 3993, + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py" + } + }, + { + "behavior": "display_asset_hint", + "id": "display_intro_graphic_optional", + "key": "INTRO_GRAPHIC", + "missing_behavior": "optional_missing", + "section": "DISPLAY" + }, + { + "authority": "linuxcnc_source_hal_path", + "id": "halfile_missing_classification", + "keys": [ + "HAL", + "HALFILE" + ], + "missing_classification": "BLOCKED_HAL" + }, + { + "authority": "exact_path_resource_resolver", + "id": "resource_missing_classification", + "missing_classification": "BLOCKED_RESOURCE" + }, + { + "authority": "linuxcnc/src/emc/rs274ngc/rs274ngc_pre.cc", + "behavior": "zeroed_runtime_fixture_only_when_source_policy_allows", + "id": "runtime_parameter_fixture", + "keys": [ + "PARAMETER_FILE", + "VARFILE", + "TOOL_TABLE", + "TOOLTABLE" + ] + }, + { + "behavior": "missing_exact_path_remains_blocked_or_optional", + "control_panel_action": "show_diagnostic_and_source_path", + "id": "no_basename_substitution" + } + ], + "schema": "linuxcnc-source-semantics-contract.v1", + "status": "verified" +} diff --git a/coverage/owner-review-gate-refresh.report.json b/coverage/owner-review-gate-refresh.report.json index e7ced79..7d1514b 100644 --- a/coverage/owner-review-gate-refresh.report.json +++ b/coverage/owner-review-gate-refresh.report.json @@ -79,7 +79,7 @@ "bytes": 9239, "path": "coverage/remediation-evidence-roundup.report.json", "role": "remediation_roundup", - "sha256": "79eb39a73c8b16295dd51e19968f99f59dfd8cdecb9feffb2131cc0d453738a9", + "sha256": "785bfcaf6329d324da7a0599336664108dd28973816a9beb87e78078448b9f16", "status": "present" }, { @@ -100,7 +100,7 @@ "next_transition": "collect owner decisions, create a new context when locked inputs change, then regenerate closure/audit/gate", "release_action": "STOP_RELEASE", "release_decision": "blocked_not_release_candidate", - "report_sha256": "8ef20d02dfa542abd2ee09945602d79c247bdac3bcca4e94ad58803187b76084", + "report_sha256": "e14cbc2075cbd19bc2b04d8b54ff5e783ca39ff73d22209b7850fcd2cf8f4071", "review_status": "owner_input_required", "schema": "owner-review-gate-refresh.v1", "status": "blocked", diff --git a/coverage/panel-01-manual-axis-complete.png b/coverage/panel-01-manual-axis-complete.png new file mode 100644 index 0000000..a8b653e Binary files /dev/null and b/coverage/panel-01-manual-axis-complete.png differ diff --git a/coverage/panel-01-manual-axis-running.png b/coverage/panel-01-manual-axis-running.png new file mode 100644 index 0000000..f1a9a30 Binary files /dev/null and b/coverage/panel-01-manual-axis-running.png differ diff --git a/coverage/panel-01-source-control-inventory.report.json b/coverage/panel-01-source-control-inventory.report.json new file mode 100644 index 0000000..fcd2001 --- /dev/null +++ b/coverage/panel-01-source-control-inventory.report.json @@ -0,0 +1,5919 @@ +{ + "boundaries": [ + "inventory-only; no runtime authority or protocol mutation", + "selected 3axis-tutorial only", + "missing web mappings remain blocked", + "desktop AXIS controls are Web-equivalent workflows, not copied host windows" + ], + "case": { + "ini": { + "line_count": 224, + "path": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", + "sha256": "07e4fafa71e13ac3749fd860ee7549b762f521b9ca58c32110a9f582554882dc", + "values": { + "DISPLAY": { + "DISPLAY": "axis", + "INCREMENTS": "1 mm, .01 in, .1mm, 1 mil, .1 mil, 1/8000 in", + "INTRO_GRAPHIC": "linuxcnc.gif", + "OPEN_FILE": "configs/nc_files/3dtest.ngc", + "POSITION_FEEDBACK": "ACTUAL", + "POSITION_OFFSET": "RELATIVE" + }, + "EMC": { + "MACHINE": "3axis-tutorial", + "VERSION": "1.1" + }, + "EMCIO": { + "TOOL_TABLE": "tool.tbl" + }, + "HAL": { + "HALFILE": "3axis-tutorial.hal", + "HALUI": "halui" + }, + "KINS": { + "JOINTS": "3", + "KINEMATICS": "trivkins" + }, + "TRAJ": { + "AXES": "3", + "COORDINATES": "X Y Z", + "LINEAR_UNITS": "mm", + "POSITION_FILE": "position_mm.txt" + } + } + }, + "path": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", + "status": "indexed" + }, + "checks": { + "control_ids_unique": { + "count": 187, + "status": "verified" + }, + "manual_axis_execution": { + "evidence_count": 2, + "status": "verified", + "tool": "tool/linuxcnc-axis-manual-execution" + }, + "menu_inventory": { + "count": 80, + "minimum": 40, + "status": "verified" + }, + "panel_inventory": { + "panels": [ + "MANUAL", + "MDI", + "AUTO", + "DRO", + "DIAGNOSTICS" + ], + "status": "verified" + }, + "react_feature_inventory": { + "count": 29, + "status": "verified" + }, + "shortcut_inventory": { + "count": 82, + "minimum": 20, + "status": "verified" + }, + "source_inputs": { + "count": 4, + "status": "verified" + }, + "toolbar_inventory": { + "count": 20, + "minimum": 15, + "status": "verified" + }, + "web_mapping": { + "gaps": 153, + "mapped": 34, + "status": "blocked" + } + }, + "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", + "controls": { + "control_id_contract": [ + { + "category": "menu", + "control_id": "AXIS-MENU-FILE-OPEN", + "label": "Open...", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.file", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 58, + "lines": "58-58", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-FILE-RECENT_FILES", + "label": "Recent Files", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.file", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 62, + "lines": "62-62", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-FILE-EDIT", + "label": "Edit...", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.file", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 66, + "lines": "66-66", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-FILE-RELOAD", + "label": "Reload", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.file", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 71, + "lines": "71-71", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-FILE-SAVE_G_CODE_AS", + "label": "Save G-code as...", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.file", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 76, + "lines": "76-76", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-FILE-PROPERTIES", + "label": "Properties...", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.file", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 80, + "lines": "80-80", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-FILE-EDIT_TOOL_DATA", + "label": "Edit tool data...", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.file", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 86, + "lines": "86-86", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-FILE-RELOAD_TOOL_DATA", + "label": "Reload tool data", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.file", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 90, + "lines": "90-90", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-FILE-LADDER_EDITOR", + "label": "Ladder Editor...", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.file", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 96, + "lines": "96-96", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-FILE-QUIT", + "label": "Quit", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.file", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 102, + "lines": "102-102", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-TOGGLE_EMERGENCY_STOP", + "label": "Toggle Emergency Stop", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 108, + "lines": "108-108", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-TOGGLE_MACHINE_POWER", + "label": "Toggle Machine Power", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 113, + "lines": "113-113", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-RUN_PROGRAM", + "label": "Run program", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 120, + "lines": "120-120", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-RUN_FROM_SELECTED_LINE", + "label": "Run from selected line", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 124, + "lines": "124-124", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-STEP", + "label": "Step", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 129, + "lines": "129-129", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-PAUSE", + "label": "Pause", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 134, + "lines": "134-134", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-RESUME", + "label": "Resume", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 139, + "lines": "139-139", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-STOP", + "label": "Stop", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 144, + "lines": "144-144", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-STOP_AT_M1", + "label": "Stop at M1", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 149, + "lines": "149-149", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-SKIP_LINES_WITH", + "label": "Skip lines with '/'", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 154, + "lines": "154-154", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-CLEAR_MDI_HISTORY", + "label": "Clear MDI history", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 161, + "lines": "161-161", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-COPY_FROM_MDI_HISTORY", + "label": "Copy from MDI history", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 165, + "lines": "165-165", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-PASTE_TO_MDI_HISTORY", + "label": "Paste to MDI history", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 169, + "lines": "169-169", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-CALIBRATION", + "label": "Calibration", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 175, + "lines": "175-175", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-SHOW_HAL_CONFIGURATION", + "label": "Show Hal Configuration", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 179, + "lines": "179-179", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-HAL_METER", + "label": "Hal Meter", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 183, + "lines": "183-183", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-HAL_SCOPE", + "label": "Hal Scope", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 187, + "lines": "187-187", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-SHOW_LINUXCNC_STATUS", + "label": "Show LinuxCNC Status", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 191, + "lines": "191-191", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-SET_DEBUG_LEVEL", + "label": "Set Debug Level", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 195, + "lines": "195-195", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-HOMING", + "label": "Homing", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 201, + "lines": "201-201", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-UNHOMING", + "label": "Unhoming", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 205, + "lines": "205-205", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-ZERO_COORDINATE_SYSTEM", + "label": "Zero coordinate system", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 209, + "lines": "209-209", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE_CLEAROFFSET-P1_G54", + "label": "P1 G54", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine.clearoffset", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 213, + "lines": "213-213", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE_CLEAROFFSET-P2_G55", + "label": "P2 G55", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine.clearoffset", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 217, + "lines": "217-217", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE_CLEAROFFSET-P3_G56", + "label": "P3 G56", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine.clearoffset", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 221, + "lines": "221-221", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE_CLEAROFFSET-P4_G57", + "label": "P4 G57", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine.clearoffset", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 225, + "lines": "225-225", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE_CLEAROFFSET-P5_G58", + "label": "P5 G58", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine.clearoffset", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 229, + "lines": "229-229", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE_CLEAROFFSET-P6_G59", + "label": "P6 G59", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine.clearoffset", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 233, + "lines": "233-233", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE_CLEAROFFSET-P7_G59_1", + "label": "P7 G59.1", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine.clearoffset", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 237, + "lines": "237-237", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE_CLEAROFFSET-P8_G59_2", + "label": "P8 G59.2", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine.clearoffset", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 241, + "lines": "241-241", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE_CLEAROFFSET-P9_G59_3", + "label": "P9 G59.3", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine.clearoffset", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 245, + "lines": "245-245", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE_CLEAROFFSET-G92", + "label": "G92", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine.clearoffset", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 249, + "lines": "249-249", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-TOOL_TOUCH_OFF_TO_WORKPIECE", + "label": "Tool touch off to workpiece", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 257, + "lines": "257-257", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-TOOL_TOUCH_OFF_TO_FIXTURE", + "label": "Tool touch off to fixture", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.machine", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 263, + "lines": "263-263", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-TOP_VIEW", + "label": "Top view", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 271, + "lines": "271-271", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-ROTATED_TOP_VIEW", + "label": "Rotated Top view", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 278, + "lines": "278-278", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SIDE_VIEW", + "label": "Side view", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 285, + "lines": "285-285", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-FRONT_VIEW", + "label": "Front view", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 292, + "lines": "292-292", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-PERSPECTIVE_VIEW", + "label": "Perspective view", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 299, + "lines": "299-299", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-DISPLAY_INCHES", + "label": "Display Inches", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 308, + "lines": "308-308", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-DISPLAY_MM", + "label": "Display MM", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 315, + "lines": "315-315", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_PROGRAM", + "label": "Show program", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 322, + "lines": "322-322", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_PROGRAM_RAPIDS", + "label": "Show program rapids", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 327, + "lines": "327-327", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-ALPHA_BLEND_PROGRAM", + "label": "Alpha-blend program", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 332, + "lines": "332-332", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_LIVE_PLOT", + "label": "Show live plot", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 337, + "lines": "337-337", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_TOOL", + "label": "Show tool", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 342, + "lines": "342-342", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_EXTENTS", + "label": "Show extents", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 347, + "lines": "347-347", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-GRID", + "label": "Grid", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 351, + "lines": "351-351", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_OFFSETS", + "label": "Show offsets", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 356, + "lines": "356-356", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_MACHINE_LIMITS", + "label": "Show machine limits", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 361, + "lines": "361-361", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_VELOCITY", + "label": "Show velocity", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 366, + "lines": "366-366", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_DISTANCE_TO_GO", + "label": "Show distance to go", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 371, + "lines": "371-371", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-LARGE_COORDINATE_FONT", + "label": "Large coordinate font", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 376, + "lines": "376-376", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-CLEAR_LIVE_PLOT", + "label": "Clear live plot", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 381, + "lines": "381-381", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_PYVCP_PANEL", + "label": "Show PyVCP panel", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 387, + "lines": "387-387", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_COMMANDED_POSITION", + "label": "Show commanded position", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 396, + "lines": "396-396", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_ACTUAL_POSITION", + "label": "Show actual position", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 403, + "lines": "403-403", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_MACHINE_POSITION", + "label": "Show machine position", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 412, + "lines": "412-412", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_RELATIVE_POSITION", + "label": "Show relative position", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 419, + "lines": "419-419", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-JOINT_MODE", + "label": "Joint mode", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 428, + "lines": "428-428", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-WORLD_MODE", + "label": "World mode", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 435, + "lines": "435-435", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW_GRID-OFF", + "label": "Off", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view.grid", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 443, + "lines": "443-443", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW_GRID-CUSTOM", + "label": "Custom", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.view.grid", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 449, + "lines": "449-449", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-HELP-ABOUT_AXIS", + "label": "About AXIS", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.help", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 460, + "lines": "460-460", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-HELP-QUICK_REFERENCE", + "label": "Quick Reference", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.help", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 464, + "lines": "464-464", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MENU-FILE", + "label": "File", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 470, + "lines": "470-470", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MENU-MACHINE", + "label": "Machine", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 474, + "lines": "474-474", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MENU-VIEW", + "label": "View", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 478, + "lines": "478-478", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MENU-HELP", + "label": "Help", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 482, + "lines": "482-482", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-FILE_RECENT-CLEAR_RECENTS_LIST", + "label": "Clear Recents List", + "mapping_reason": "MenuAction has no per-action stable data-axis-feature.", + "mapping_status": "container_only", + "menu_path": ".menu.file.recent", + "react_feature_ids": [ + "AXIS-UI-02" + ], + "source": { + "line": 2230, + "lines": "2230-2230", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "toolbar", + "command": null, + "control_id": "AXIS-TOOLBAR-MACHINE_ESTOP", + "helptext": "Toggle Emergency Stop [F1]", + "image": "tool_estop", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-MACHINE-01" + ], + "source": { + "line": 490, + "lines": "490-497", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.machine_estop" + }, + { + "category": "toolbar", + "command": "onoff_clicked \\", + "control_id": "AXIS-TOOLBAR-MACHINE_POWER", + "helptext": "Toggle Machine power [F2]", + "image": "tool_power", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-MACHINE-02" + ], + "source": { + "line": 498, + "lines": "498-508", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.machine_power" + }, + { + "category": "toolbar", + "command": "open_file", + "control_id": "AXIS-TOOLBAR-FILE_OPEN", + "helptext": "Open G-Code file [O]", + "image": "tool_open", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-FILE-01" + ], + "source": { + "line": 509, + "lines": "509-516", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.file_open" + }, + { + "category": "toolbar", + "command": "reload_file", + "control_id": "AXIS-TOOLBAR-RELOAD", + "helptext": "Reopen current file [Ctrl-R]", + "image": "tool_reload", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-FILE-04" + ], + "source": { + "line": 517, + "lines": "517-526", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.reload" + }, + { + "category": "toolbar", + "command": "task_run \\", + "control_id": "AXIS-TOOLBAR-PROGRAM_RUN", + "helptext": "Begin executing current file [R]", + "image": "tool_run", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-MACHINE-03" + ], + "source": { + "line": 527, + "lines": "527-534", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.program_run" + }, + { + "category": "toolbar", + "command": "task_step \\", + "control_id": "AXIS-TOOLBAR-PROGRAM_STEP", + "helptext": "Execute next line [T]", + "image": "tool_step", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-MACHINE-05" + ], + "source": { + "line": 535, + "lines": "535-542", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.program_step" + }, + { + "category": "toolbar", + "command": "task_pauseresume \\", + "control_id": "AXIS-TOOLBAR-PROGRAM_PAUSE", + "helptext": "Pause [P] / resume [S] execution", + "image": "tool_pause", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-MACHINE-06" + ], + "source": { + "line": 543, + "lines": "543-557", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.program_pause" + }, + { + "category": "toolbar", + "command": "task_stop \\", + "control_id": "AXIS-TOOLBAR-PROGRAM_STOP", + "helptext": "Stop program execution [ESC]", + "image": "tool_stop", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-MACHINE-07" + ], + "source": { + "line": 558, + "lines": "558-567", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.program_stop" + }, + { + "category": "toolbar", + "command": "set block_delete [expr {!$block_delete", + "control_id": "AXIS-TOOLBAR-PROGRAM_BLOCKDELETE", + "helptext": "Toggle skip lines with '/' [Alt-M /]", + "image": "tool_blockdelete", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-MACHINE-09" + ], + "source": { + "line": 568, + "lines": "568-574", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.program_blockdelete" + }, + { + "category": "toolbar", + "command": "set optional_stop [expr {!$optional_stop", + "control_id": "AXIS-TOOLBAR-PROGRAM_OPTPAUSE", + "helptext": "Toggle optional pause [Alt-M 1]", + "image": "tool_optpause", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-MACHINE-08" + ], + "source": { + "line": 575, + "lines": "575-583", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.program_optpause" + }, + { + "category": "toolbar", + "command": "zoomin \\", + "control_id": "AXIS-TOOLBAR-VIEW_ZOOMIN", + "helptext": "Zoom in", + "image": "tool_zoomin", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-VIEW-01" + ], + "source": { + "line": 584, + "lines": "584-591", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.view_zoomin" + }, + { + "category": "toolbar", + "command": "zoomout \\", + "control_id": "AXIS-TOOLBAR-VIEW_ZOOMOUT", + "helptext": "Zoom out", + "image": "tool_zoomout", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-VIEW-01" + ], + "source": { + "line": 592, + "lines": "592-599", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.view_zoomout" + }, + { + "category": "toolbar", + "command": "set_view_z \\", + "control_id": "AXIS-TOOLBAR-VIEW_Z", + "helptext": "Top view", + "image": "tool_axis_z", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-VIEW-01" + ], + "source": { + "line": 600, + "lines": "600-607", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.view_z" + }, + { + "category": "toolbar", + "command": "set_view_z2 \\", + "control_id": "AXIS-TOOLBAR-VIEW_Z2", + "helptext": "Rotated top view", + "image": "tool_axis_z2", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-VIEW-01" + ], + "source": { + "line": 608, + "lines": "608-615", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.view_z2" + }, + { + "category": "toolbar", + "command": "set_view_x \\", + "control_id": "AXIS-TOOLBAR-VIEW_X", + "helptext": "Side view", + "image": "tool_axis_x", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-VIEW-01" + ], + "source": { + "line": 616, + "lines": "616-623", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.view_x" + }, + { + "category": "toolbar", + "command": "set_view_y \\", + "control_id": "AXIS-TOOLBAR-VIEW_Y", + "helptext": "Front view", + "image": "tool_axis_y", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-VIEW-01" + ], + "source": { + "line": 624, + "lines": "624-631", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.view_y" + }, + { + "category": "toolbar", + "command": "set_view_y2 \\", + "control_id": "AXIS-TOOLBAR-VIEW_Y2", + "helptext": "Inverted Front view", + "image": "tool_axis_y2", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-VIEW-01" + ], + "source": { + "line": 632, + "lines": "632-639", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.view_y2" + }, + { + "category": "toolbar", + "command": "set_view_p \\", + "control_id": "AXIS-TOOLBAR-VIEW_P", + "helptext": "Perspective view", + "image": "tool_axis_p", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-VIEW-01" + ], + "source": { + "line": 640, + "lines": "640-647", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.view_p" + }, + { + "category": "toolbar", + "command": "set rotate_mode [expr {!$rotate_mode", + "control_id": "AXIS-TOOLBAR-ROTATE", + "helptext": "Toggle between Drag and Rotate Mode [D]", + "image": "tool_rotate", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-VIEW-02" + ], + "source": { + "line": 648, + "lines": "648-662", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.rotate" + }, + { + "category": "toolbar", + "command": "clear_live_plot \\", + "control_id": "AXIS-TOOLBAR-CLEAR_PLOT", + "helptext": "Clear live plot [Ctrl-K]", + "image": "tool_clear", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-VIEW-15" + ], + "source": { + "line": 663, + "lines": "663-682", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.clear_plot" + }, + { + "category": "shortcut", + "command": "cancel_open", + "control_id": "AXIS-SHORTCUT-ESCAPE-CANCEL_OPEN", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 1229, + "lines": "1229-1229", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.task_stop", + "control_id": "AXIS-SHORTCUT-ESCAPE-COMMANDS_TASK_STOP", + "key": "", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-MACHINE-07" + ], + "source": { + "line": 3109, + "lines": "3109-3109", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.toggle_override_limits", + "control_id": "AXIS-SHORTCUT-L-COMMANDS_TOGGLE_OVERRIDE_LIMITS", + "key": "l", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3110, + "lines": "3110-3110", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.open_file", + "control_id": "AXIS-SHORTCUT-O-COMMANDS_OPEN_FILE", + "key": "o", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-FILE-01" + ], + "source": { + "line": 3111, + "lines": "3111-3111", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.task_resume", + "control_id": "AXIS-SHORTCUT-S-COMMANDS_TASK_RESUME", + "key": "s", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-MACHINE-06" + ], + "source": { + "line": 3112, + "lines": "3112-3112", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.task_step", + "control_id": "AXIS-SHORTCUT-T-COMMANDS_TASK_STEP", + "key": "t", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-MACHINE-05" + ], + "source": { + "line": 3113, + "lines": "3113-3113", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.task_pause", + "control_id": "AXIS-SHORTCUT-P-COMMANDS_TASK_PAUSE", + "key": "p", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-MACHINE-06" + ], + "source": { + "line": 3114, + "lines": "3114-3114", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.task_reverse", + "control_id": "AXIS-SHORTCUT-R-COMMANDS_TASK_REVERSE", + "key": "R", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3115, + "lines": "3115-3115", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.task_forward", + "control_id": "AXIS-SHORTCUT-F-COMMANDS_TASK_FORWARD", + "key": "F", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3116, + "lines": "3116-3116", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.cycle_view", + "control_id": "AXIS-SHORTCUT-V-COMMANDS_CYCLE_VIEW", + "key": "v", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3117, + "lines": "3117-3117", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "#nothing", + "control_id": "AXIS-SHORTCUT-ALT_P-NOTHING", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3118, + "lines": "3118-3118", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.task_run", + "control_id": "AXIS-SHORTCUT-R-COMMANDS_TASK_RUN", + "key": "r", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-MACHINE-03" + ], + "source": { + "line": 3119, + "lines": "3119-3119", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.reload_file", + "control_id": "AXIS-SHORTCUT-CONTROL_R-COMMANDS_RELOAD_FILE", + "key": "", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-FILE-04" + ], + "source": { + "line": 3120, + "lines": "3120-3120", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.save_gcode", + "control_id": "AXIS-SHORTCUT-CONTROL_S-COMMANDS_SAVE_GCODE", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3121, + "lines": "3121-3121", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.estop_clicked", + "control_id": "AXIS-SHORTCUT-KEY_F1-COMMANDS_ESTOP_CLICKED", + "key": "", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-MACHINE-01" + ], + "source": { + "line": 3122, + "lines": "3122-3122", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.onoff_clicked", + "control_id": "AXIS-SHORTCUT-KEY_F2-COMMANDS_ONOFF_CLICKED", + "key": "", + "mapping_reason": null, + "mapping_status": "mapped", + "react_feature_ids": [ + "AXIS-MACHINE-02" + ], + "source": { + "line": 3123, + "lines": "3123-3123", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.mist_toggle", + "control_id": "AXIS-SHORTCUT-KEY_F7-COMMANDS_MIST_TOGGLE", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3124, + "lines": "3124-3124", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.flood_toggle", + "control_id": "AXIS-SHORTCUT-KEY_F8-COMMANDS_FLOOD_TOGGLE", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3125, + "lines": "3125-3125", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.spindle_forward_toggle", + "control_id": "AXIS-SHORTCUT-KEY_F9-COMMANDS_SPINDLE_FORWARD_TOGGLE", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3126, + "lines": "3126-3126", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.spindle_backward_toggle", + "control_id": "AXIS-SHORTCUT-KEY_F10-COMMANDS_SPINDLE_BACKWARD_TOGGLE", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3127, + "lines": "3127-3127", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.spindle_decrease", + "control_id": "AXIS-SHORTCUT-KEY_F11-COMMANDS_SPINDLE_DECREASE", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3128, + "lines": "3128-3128", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.spindle_increase", + "control_id": "AXIS-SHORTCUT-KEY_F12-COMMANDS_SPINDLE_INCREASE", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3129, + "lines": "3129-3129", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.brake_on", + "control_id": "AXIS-SHORTCUT-B-COMMANDS_BRAKE_ON", + "key": "B", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3130, + "lines": "3130-3130", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.brake_off", + "control_id": "AXIS-SHORTCUT-B-COMMANDS_BRAKE_OFF", + "key": "b", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3131, + "lines": "3131-3131", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.clear_live_plot", + "control_id": "AXIS-SHORTCUT-CONTROL_K-COMMANDS_CLEAR_LIVE_PLOT", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3132, + "lines": "3132-3132", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "x", + "control_id": "AXIS-SHORTCUT-X-X", + "key": "x", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3136, + "lines": "3136-3136", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "y", + "control_id": "AXIS-SHORTCUT-Y-Y", + "key": "y", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3137, + "lines": "3137-3137", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "z", + "control_id": "AXIS-SHORTCUT-Z-Z", + "key": "z", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3138, + "lines": "3138-3138", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "a", + "control_id": "AXIS-SHORTCUT-A-A", + "key": "a", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3139, + "lines": "3139-3139", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(0", + "control_id": "AXIS-SHORTCUT-UNNAMED-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_0", + "key": "`", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3142, + "lines": "3142-3142", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(1", + "control_id": "AXIS-SHORTCUT-1-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_1", + "key": "1", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3143, + "lines": "3143-3143", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(2", + "control_id": "AXIS-SHORTCUT-2-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_2", + "key": "2", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3144, + "lines": "3144-3144", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(3", + "control_id": "AXIS-SHORTCUT-3-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_3", + "key": "3", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3145, + "lines": "3145-3145", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(4", + "control_id": "AXIS-SHORTCUT-4-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_4", + "key": "4", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3146, + "lines": "3146-3146", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(5", + "control_id": "AXIS-SHORTCUT-5-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_5", + "key": "5", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3147, + "lines": "3147-3147", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(6", + "control_id": "AXIS-SHORTCUT-6-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_6", + "key": "6", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3148, + "lines": "3148-3148", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(7", + "control_id": "AXIS-SHORTCUT-7-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_7", + "key": "7", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3149, + "lines": "3149-3149", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(8", + "control_id": "AXIS-SHORTCUT-8-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_8", + "key": "8", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3150, + "lines": "3150-3150", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(9", + "control_id": "AXIS-SHORTCUT-9-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_9", + "key": "9", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3151, + "lines": "3151-3151", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(10", + "control_id": "AXIS-SHORTCUT-0-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_10", + "key": "0", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3152, + "lines": "3152-3152", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: jogspeed_continuous(", + "control_id": "AXIS-SHORTCUT-C-LAMBDA_EVENT_JOGSPEED_CONTINUOUS", + "key": "c", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3154, + "lines": "3154-3154", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: widgets.rotate.invoke(", + "control_id": "AXIS-SHORTCUT-D-LAMBDA_EVENT_WIDGETS_ROTATE_INVOKE", + "key": "d", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3155, + "lines": "3155-3155", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: jogspeed_incremental(", + "control_id": "AXIS-SHORTCUT-I-LAMBDA_EVENT_JOGSPEED_INCREMENTAL", + "key": "i", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3156, + "lines": "3156-3156", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: jogspeed_incremental(-1", + "control_id": "AXIS-SHORTCUT-I-LAMBDA_EVENT_JOGSPEED_INCREMENTAL_1", + "key": "I", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3157, + "lines": "3157-3157", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "set metric [expr {!$metric}]; redraw", + "control_id": "AXIS-SHORTCUT-UNNAMED-SET_METRIC_EXPR_METRIC_REDRAW", + "key": "!", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3158, + "lines": "3158-3158", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.toggle_display_type", + "control_id": "AXIS-SHORTCUT-UNNAMED-COMMANDS_TOGGLE_DISPLAY_TYPE", + "key": "@", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3159, + "lines": "3159-3159", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.toggle_coord_type", + "control_id": "AXIS-SHORTCUT-UNNAMED-COMMANDS_TOGGLE_COORD_TYPE", + "key": "#", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3160, + "lines": "3160-3160", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.toggle_teleop_mode", + "control_id": "AXIS-SHORTCUT-UNNAMED-COMMANDS_TOGGLE_TELEOP_MODE", + "key": "$", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3161, + "lines": "3161-3161", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.home_joint", + "control_id": "AXIS-SHORTCUT-HOME-COMMANDS_HOME_JOINT", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3163, + "lines": "3163-3163", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "KeyPress", + "control_id": "AXIS-SHORTCUT-KP_HOME-KEYPRESS", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3164, + "lines": "3164-3164", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.home_all_joints", + "control_id": "AXIS-SHORTCUT-CONTROL_HOME-COMMANDS_HOME_ALL_JOINTS", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3165, + "lines": "3165-3165", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.set_axis_offset", + "control_id": "AXIS-SHORTCUT-SHIFT_HOME-COMMANDS_SET_AXIS_OFFSET", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3166, + "lines": "3166-3166", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.touch_off_system", + "control_id": "AXIS-SHORTCUT-END-COMMANDS_TOUCH_OFF_SYSTEM", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3167, + "lines": "3167-3167", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.touch_off_tool", + "control_id": "AXIS-SHORTCUT-CONTROL_END-COMMANDS_TOUCH_OFF_TOOL", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3168, + "lines": "3168-3168", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "KeyPress", + "control_id": "AXIS-SHORTCUT-CONTROL_KP_HOME-KEYPRESS", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3169, + "lines": "3169-3169", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "KeyPress", + "control_id": "AXIS-SHORTCUT-SHIFT_KP_HOME-KEYPRESS", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3170, + "lines": "3170-3170", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "KeyPress", + "control_id": "AXIS-SHORTCUT-KP_END-KEYPRESS", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3171, + "lines": "3171-3171", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "KeyPress", + "control_id": "AXIS-SHORTCUT-KEYPRESS_S-KEYPRESS", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3320, + "lines": "3320-3320", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "KeyPress", + "control_id": "AXIS-SHORTCUT-KEYPRESS_S-KEYPRESS-2", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3321, + "lines": "3321-3321", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "-get_max_jog_speed_map(d", + "control_id": "AXIS-SHORTCUT-SHIFT_KEYPRESS_S-GET_MAX_JOG_SPEED_MAP_D", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3322, + "lines": "3322-3322", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "get_max_jog_speed_map(d", + "control_id": "AXIS-SHORTCUT-SHIFT_KEYPRESS_S-GET_MAX_JOG_SPEED_MAP_D-2", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3323, + "lines": "3323-3323", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda e: jog_off_map(d", + "control_id": "AXIS-SHORTCUT-KEYRELEASE_S-LAMBDA_E_JOG_OFF_MAP_D", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3324, + "lines": "3324-3324", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda e: jog_off_map(d", + "control_id": "AXIS-SHORTCUT-KEYRELEASE_S-LAMBDA_E_JOG_OFF_MAP_D-2", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3325, + "lines": "3325-3325", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": ".", + "control_id": "AXIS-SHORTCUT-FOCUSOUT-UNNAMED", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3327, + "lines": "3327-3327", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "KeyPress", + "control_id": "AXIS-SHORTCUT-KEYPRESS_KP_BEGIN-KEYPRESS", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3731, + "lines": "3731-3731", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "KeyPress", + "control_id": "AXIS-SHORTCUT-KEYPRESS_KP_INSERT-KEYPRESS", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3732, + "lines": "3732-3732", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "KeyPress", + "control_id": "AXIS-SHORTCUT-KEYPRESS_KP_NEXT-KEYPRESS", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3771, + "lines": "3771-3771", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "KeyPress", + "control_id": "AXIS-SHORTCUT-KEYPRESS_KP_PRIOR-KEYPRESS", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3772, + "lines": "3772-3772", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "nomodifier(commands.jog_minus", + "control_id": "AXIS-SHORTCUT-KEYPRESS_MINUS-NOMODIFIER_COMMANDS_JOG_MINUS", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3774, + "lines": "3774-3774", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "nomodifier(commands.jog_plus", + "control_id": "AXIS-SHORTCUT-KEYPRESS_EQUAL-NOMODIFIER_COMMANDS_JOG_PLUS", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3775, + "lines": "3775-3775", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.jog_stop", + "control_id": "AXIS-SHORTCUT-KEYRELEASE_MINUS-COMMANDS_JOG_STOP", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3776, + "lines": "3776-3776", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.jog_stop", + "control_id": "AXIS-SHORTCUT-KEYRELEASE_EQUAL-COMMANDS_JOG_STOP", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3777, + "lines": "3777-3777", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": ".tabs raise manual", + "control_id": "AXIS-SHORTCUT-KEY_F3-TABS_RAISE_MANUAL", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3854, + "lines": "3854-3854", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": ".tabs raise mdi", + "control_id": "AXIS-SHORTCUT-KEY_F5-TABS_RAISE_MDI", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3855, + "lines": "3855-3855", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "+", + "control_id": "AXIS-SHORTCUT-KEY_F5-UNNAMED", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3856, + "lines": "3856-3856", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.next_tab", + "control_id": "AXIS-SHORTCUT-KEY_F4-COMMANDS_NEXT_TAB", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3857, + "lines": "3857-3857", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.toggle_show_pyvcppanel", + "control_id": "AXIS-SHORTCUT-CONTROL_E-COMMANDS_TOGGLE_SHOW_PYVCPPANEL", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3933, + "lines": "3933-3933", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: notifications.clear(", + "control_id": "AXIS-SHORTCUT-CONTROL_SPACE-LAMBDA_EVENT_NOTIFICATIONS_CLEAR", + "key": "", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 3968, + "lines": "3968-3968", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "\"\"", + "control_id": "AXIS-SHORTCUT-UNNAMED-UNSPECIFIED", + "key": "$", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 4134, + "lines": "4134-4134", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.set_view_y2", + "control_id": "AXIS-SHORTCUT-V-COMMANDS_SET_VIEW_Y2", + "key": "v", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 4140, + "lines": "4140-4140", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.set_view_y", + "control_id": "AXIS-SHORTCUT-V-COMMANDS_SET_VIEW_Y", + "key": "v", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 4143, + "lines": "4143-4143", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "\"\"", + "control_id": "AXIS-SHORTCUT-D-UNSPECIFIED", + "key": "d", + "mapping_reason": "Shortcut is source-defined but no matching React keyboard contract is declared.", + "mapping_status": "missing", + "react_feature_ids": [], + "source": { + "line": 4144, + "lines": "4144-4144", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "panel", + "control_id": "AXIS-PANEL-MANUAL", + "mapping_reason": null, + "mapping_status": "mapped", + "panel": "MANUAL", + "react_feature_ids": [ + "AXIS-MANUAL-01" + ], + "source": [ + { + "line": 820, + "lines": "820-820", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "Manual notebook tab" + }, + { + "line": 2574, + "lines": "2574-2574", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "ensure_manual" + } + ] + }, + { + "category": "panel", + "control_id": "AXIS-PANEL-MDI", + "mapping_reason": null, + "mapping_status": "mapped", + "panel": "MDI", + "react_feature_ids": [ + "AXIS-CTRL-03" + ], + "source": [ + { + "line": 821, + "lines": "821-821", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "MDI notebook tab" + }, + { + "line": 2587, + "lines": "2587-2587", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "ensure_mdi" + } + ] + }, + { + "category": "panel", + "control_id": "AXIS-PANEL-AUTO", + "mapping_reason": "AUTO has no dedicated React tab; program controls are only an inferred shared pane.", + "mapping_status": "mapped", + "note": "AXIS uses the shared program pane; it has no separate Auto notebook tab.", + "panel": "AUTO", + "react_feature_ids": [ + "AXIS-CTRL-02", + "AXIS-UI-07" + ], + "source": [ + { + "line": 120, + "lines": "120-120", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "Auto program menu" + }, + { + "line": 2343, + "lines": "2343-2343", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "task_run" + } + ] + }, + { + "category": "panel", + "control_id": "AXIS-PANEL-DRO", + "mapping_reason": null, + "mapping_status": "mapped", + "panel": "DRO", + "react_feature_ids": [ + "AXIS-VIEW-04" + ], + "source": [ + { + "line": 1296, + "lines": "1296-1296", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "DRO notebook" + }, + { + "line": 1331, + "lines": "1331-1331", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "position label" + }, + { + "line": 1967, + "lines": "1967-1967", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "state projection" + } + ] + }, + { + "category": "panel", + "control_id": "AXIS-PANEL-DIAGNOSTICS", + "mapping_reason": null, + "mapping_status": "mapped", + "panel": "DIAGNOSTICS", + "react_feature_ids": [ + "AXIS-EDIT-01", + "AXIS-UI-07" + ], + "source": [ + { + "line": 1356, + "lines": "1356-1356", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "program text editor" + }, + { + "line": 1387, + "lines": "1387-1387", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "active G-code display" + }, + { + "line": 1978, + "lines": "1978-1978", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "interpreter state" + } + ] + } + ], + "menus": [ + { + "category": "menu", + "control_id": "AXIS-MENU-FILE-OPEN", + "label": "Open...", + "menu_path": ".menu.file", + "source": { + "line": 58, + "lines": "58-58", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-FILE-RECENT_FILES", + "label": "Recent Files", + "menu_path": ".menu.file", + "source": { + "line": 62, + "lines": "62-62", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-FILE-EDIT", + "label": "Edit...", + "menu_path": ".menu.file", + "source": { + "line": 66, + "lines": "66-66", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-FILE-RELOAD", + "label": "Reload", + "menu_path": ".menu.file", + "source": { + "line": 71, + "lines": "71-71", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-FILE-SAVE_G_CODE_AS", + "label": "Save G-code as...", + "menu_path": ".menu.file", + "source": { + "line": 76, + "lines": "76-76", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-FILE-PROPERTIES", + "label": "Properties...", + "menu_path": ".menu.file", + "source": { + "line": 80, + "lines": "80-80", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-FILE-EDIT_TOOL_DATA", + "label": "Edit tool data...", + "menu_path": ".menu.file", + "source": { + "line": 86, + "lines": "86-86", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-FILE-RELOAD_TOOL_DATA", + "label": "Reload tool data", + "menu_path": ".menu.file", + "source": { + "line": 90, + "lines": "90-90", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-FILE-LADDER_EDITOR", + "label": "Ladder Editor...", + "menu_path": ".menu.file", + "source": { + "line": 96, + "lines": "96-96", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-FILE-QUIT", + "label": "Quit", + "menu_path": ".menu.file", + "source": { + "line": 102, + "lines": "102-102", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-TOGGLE_EMERGENCY_STOP", + "label": "Toggle Emergency Stop", + "menu_path": ".menu.machine", + "source": { + "line": 108, + "lines": "108-108", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-TOGGLE_MACHINE_POWER", + "label": "Toggle Machine Power", + "menu_path": ".menu.machine", + "source": { + "line": 113, + "lines": "113-113", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-RUN_PROGRAM", + "label": "Run program", + "menu_path": ".menu.machine", + "source": { + "line": 120, + "lines": "120-120", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-RUN_FROM_SELECTED_LINE", + "label": "Run from selected line", + "menu_path": ".menu.machine", + "source": { + "line": 124, + "lines": "124-124", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-STEP", + "label": "Step", + "menu_path": ".menu.machine", + "source": { + "line": 129, + "lines": "129-129", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-PAUSE", + "label": "Pause", + "menu_path": ".menu.machine", + "source": { + "line": 134, + "lines": "134-134", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-RESUME", + "label": "Resume", + "menu_path": ".menu.machine", + "source": { + "line": 139, + "lines": "139-139", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-STOP", + "label": "Stop", + "menu_path": ".menu.machine", + "source": { + "line": 144, + "lines": "144-144", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-STOP_AT_M1", + "label": "Stop at M1", + "menu_path": ".menu.machine", + "source": { + "line": 149, + "lines": "149-149", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-SKIP_LINES_WITH", + "label": "Skip lines with '/'", + "menu_path": ".menu.machine", + "source": { + "line": 154, + "lines": "154-154", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-CLEAR_MDI_HISTORY", + "label": "Clear MDI history", + "menu_path": ".menu.machine", + "source": { + "line": 161, + "lines": "161-161", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-COPY_FROM_MDI_HISTORY", + "label": "Copy from MDI history", + "menu_path": ".menu.machine", + "source": { + "line": 165, + "lines": "165-165", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-PASTE_TO_MDI_HISTORY", + "label": "Paste to MDI history", + "menu_path": ".menu.machine", + "source": { + "line": 169, + "lines": "169-169", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-CALIBRATION", + "label": "Calibration", + "menu_path": ".menu.machine", + "source": { + "line": 175, + "lines": "175-175", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-SHOW_HAL_CONFIGURATION", + "label": "Show Hal Configuration", + "menu_path": ".menu.machine", + "source": { + "line": 179, + "lines": "179-179", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-HAL_METER", + "label": "Hal Meter", + "menu_path": ".menu.machine", + "source": { + "line": 183, + "lines": "183-183", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-HAL_SCOPE", + "label": "Hal Scope", + "menu_path": ".menu.machine", + "source": { + "line": 187, + "lines": "187-187", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-SHOW_LINUXCNC_STATUS", + "label": "Show LinuxCNC Status", + "menu_path": ".menu.machine", + "source": { + "line": 191, + "lines": "191-191", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-SET_DEBUG_LEVEL", + "label": "Set Debug Level", + "menu_path": ".menu.machine", + "source": { + "line": 195, + "lines": "195-195", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-HOMING", + "label": "Homing", + "menu_path": ".menu.machine", + "source": { + "line": 201, + "lines": "201-201", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-UNHOMING", + "label": "Unhoming", + "menu_path": ".menu.machine", + "source": { + "line": 205, + "lines": "205-205", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-ZERO_COORDINATE_SYSTEM", + "label": "Zero coordinate system", + "menu_path": ".menu.machine", + "source": { + "line": 209, + "lines": "209-209", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE_CLEAROFFSET-P1_G54", + "label": "P1 G54", + "menu_path": ".menu.machine.clearoffset", + "source": { + "line": 213, + "lines": "213-213", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE_CLEAROFFSET-P2_G55", + "label": "P2 G55", + "menu_path": ".menu.machine.clearoffset", + "source": { + "line": 217, + "lines": "217-217", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE_CLEAROFFSET-P3_G56", + "label": "P3 G56", + "menu_path": ".menu.machine.clearoffset", + "source": { + "line": 221, + "lines": "221-221", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE_CLEAROFFSET-P4_G57", + "label": "P4 G57", + "menu_path": ".menu.machine.clearoffset", + "source": { + "line": 225, + "lines": "225-225", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE_CLEAROFFSET-P5_G58", + "label": "P5 G58", + "menu_path": ".menu.machine.clearoffset", + "source": { + "line": 229, + "lines": "229-229", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE_CLEAROFFSET-P6_G59", + "label": "P6 G59", + "menu_path": ".menu.machine.clearoffset", + "source": { + "line": 233, + "lines": "233-233", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE_CLEAROFFSET-P7_G59_1", + "label": "P7 G59.1", + "menu_path": ".menu.machine.clearoffset", + "source": { + "line": 237, + "lines": "237-237", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE_CLEAROFFSET-P8_G59_2", + "label": "P8 G59.2", + "menu_path": ".menu.machine.clearoffset", + "source": { + "line": 241, + "lines": "241-241", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE_CLEAROFFSET-P9_G59_3", + "label": "P9 G59.3", + "menu_path": ".menu.machine.clearoffset", + "source": { + "line": 245, + "lines": "245-245", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE_CLEAROFFSET-G92", + "label": "G92", + "menu_path": ".menu.machine.clearoffset", + "source": { + "line": 249, + "lines": "249-249", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-TOOL_TOUCH_OFF_TO_WORKPIECE", + "label": "Tool touch off to workpiece", + "menu_path": ".menu.machine", + "source": { + "line": 257, + "lines": "257-257", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MACHINE-TOOL_TOUCH_OFF_TO_FIXTURE", + "label": "Tool touch off to fixture", + "menu_path": ".menu.machine", + "source": { + "line": 263, + "lines": "263-263", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-TOP_VIEW", + "label": "Top view", + "menu_path": ".menu.view", + "source": { + "line": 271, + "lines": "271-271", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-ROTATED_TOP_VIEW", + "label": "Rotated Top view", + "menu_path": ".menu.view", + "source": { + "line": 278, + "lines": "278-278", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SIDE_VIEW", + "label": "Side view", + "menu_path": ".menu.view", + "source": { + "line": 285, + "lines": "285-285", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-FRONT_VIEW", + "label": "Front view", + "menu_path": ".menu.view", + "source": { + "line": 292, + "lines": "292-292", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-PERSPECTIVE_VIEW", + "label": "Perspective view", + "menu_path": ".menu.view", + "source": { + "line": 299, + "lines": "299-299", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-DISPLAY_INCHES", + "label": "Display Inches", + "menu_path": ".menu.view", + "source": { + "line": 308, + "lines": "308-308", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-DISPLAY_MM", + "label": "Display MM", + "menu_path": ".menu.view", + "source": { + "line": 315, + "lines": "315-315", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_PROGRAM", + "label": "Show program", + "menu_path": ".menu.view", + "source": { + "line": 322, + "lines": "322-322", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_PROGRAM_RAPIDS", + "label": "Show program rapids", + "menu_path": ".menu.view", + "source": { + "line": 327, + "lines": "327-327", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-ALPHA_BLEND_PROGRAM", + "label": "Alpha-blend program", + "menu_path": ".menu.view", + "source": { + "line": 332, + "lines": "332-332", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_LIVE_PLOT", + "label": "Show live plot", + "menu_path": ".menu.view", + "source": { + "line": 337, + "lines": "337-337", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_TOOL", + "label": "Show tool", + "menu_path": ".menu.view", + "source": { + "line": 342, + "lines": "342-342", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_EXTENTS", + "label": "Show extents", + "menu_path": ".menu.view", + "source": { + "line": 347, + "lines": "347-347", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-GRID", + "label": "Grid", + "menu_path": ".menu.view", + "source": { + "line": 351, + "lines": "351-351", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_OFFSETS", + "label": "Show offsets", + "menu_path": ".menu.view", + "source": { + "line": 356, + "lines": "356-356", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_MACHINE_LIMITS", + "label": "Show machine limits", + "menu_path": ".menu.view", + "source": { + "line": 361, + "lines": "361-361", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_VELOCITY", + "label": "Show velocity", + "menu_path": ".menu.view", + "source": { + "line": 366, + "lines": "366-366", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_DISTANCE_TO_GO", + "label": "Show distance to go", + "menu_path": ".menu.view", + "source": { + "line": 371, + "lines": "371-371", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-LARGE_COORDINATE_FONT", + "label": "Large coordinate font", + "menu_path": ".menu.view", + "source": { + "line": 376, + "lines": "376-376", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-CLEAR_LIVE_PLOT", + "label": "Clear live plot", + "menu_path": ".menu.view", + "source": { + "line": 381, + "lines": "381-381", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_PYVCP_PANEL", + "label": "Show PyVCP panel", + "menu_path": ".menu.view", + "source": { + "line": 387, + "lines": "387-387", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_COMMANDED_POSITION", + "label": "Show commanded position", + "menu_path": ".menu.view", + "source": { + "line": 396, + "lines": "396-396", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_ACTUAL_POSITION", + "label": "Show actual position", + "menu_path": ".menu.view", + "source": { + "line": 403, + "lines": "403-403", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_MACHINE_POSITION", + "label": "Show machine position", + "menu_path": ".menu.view", + "source": { + "line": 412, + "lines": "412-412", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-SHOW_RELATIVE_POSITION", + "label": "Show relative position", + "menu_path": ".menu.view", + "source": { + "line": 419, + "lines": "419-419", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-JOINT_MODE", + "label": "Joint mode", + "menu_path": ".menu.view", + "source": { + "line": 428, + "lines": "428-428", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW-WORLD_MODE", + "label": "World mode", + "menu_path": ".menu.view", + "source": { + "line": 435, + "lines": "435-435", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW_GRID-OFF", + "label": "Off", + "menu_path": ".menu.view.grid", + "source": { + "line": 443, + "lines": "443-443", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-VIEW_GRID-CUSTOM", + "label": "Custom", + "menu_path": ".menu.view.grid", + "source": { + "line": 449, + "lines": "449-449", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-HELP-ABOUT_AXIS", + "label": "About AXIS", + "menu_path": ".menu.help", + "source": { + "line": 460, + "lines": "460-460", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-HELP-QUICK_REFERENCE", + "label": "Quick Reference", + "menu_path": ".menu.help", + "source": { + "line": 464, + "lines": "464-464", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MENU-FILE", + "label": "File", + "menu_path": ".menu", + "source": { + "line": 470, + "lines": "470-470", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MENU-MACHINE", + "label": "Machine", + "menu_path": ".menu", + "source": { + "line": 474, + "lines": "474-474", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MENU-VIEW", + "label": "View", + "menu_path": ".menu", + "source": { + "line": 478, + "lines": "478-478", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-MENU-HELP", + "label": "Help", + "menu_path": ".menu", + "source": { + "line": 482, + "lines": "482-482", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + }, + { + "category": "menu", + "control_id": "AXIS-MENU-FILE_RECENT-CLEAR_RECENTS_LIST", + "label": "Clear Recents List", + "menu_path": ".menu.file.recent", + "source": { + "line": 2230, + "lines": "2230-2230", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl menu accelerator" + } + } + ], + "panels": [ + { + "category": "panel", + "control_id": "AXIS-PANEL-MANUAL", + "panel": "MANUAL", + "source": [ + { + "line": 820, + "lines": "820-820", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "Manual notebook tab" + }, + { + "line": 2574, + "lines": "2574-2574", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "ensure_manual" + } + ] + }, + { + "category": "panel", + "control_id": "AXIS-PANEL-MDI", + "panel": "MDI", + "source": [ + { + "line": 821, + "lines": "821-821", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "MDI notebook tab" + }, + { + "line": 2587, + "lines": "2587-2587", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "ensure_mdi" + } + ] + }, + { + "category": "panel", + "control_id": "AXIS-PANEL-AUTO", + "note": "AXIS uses the shared program pane; it has no separate Auto notebook tab.", + "panel": "AUTO", + "source": [ + { + "line": 120, + "lines": "120-120", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "Auto program menu" + }, + { + "line": 2343, + "lines": "2343-2343", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "task_run" + } + ] + }, + { + "category": "panel", + "control_id": "AXIS-PANEL-DRO", + "panel": "DRO", + "source": [ + { + "line": 1296, + "lines": "1296-1296", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "DRO notebook" + }, + { + "line": 1331, + "lines": "1331-1331", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "position label" + }, + { + "line": 1967, + "lines": "1967-1967", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "state projection" + } + ] + }, + { + "category": "panel", + "control_id": "AXIS-PANEL-DIAGNOSTICS", + "panel": "DIAGNOSTICS", + "source": [ + { + "line": 1356, + "lines": "1356-1356", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "program text editor" + }, + { + "line": 1387, + "lines": "1387-1387", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "active G-code display" + }, + { + "line": 1978, + "lines": "1978-1978", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "interpreter state" + } + ] + } + ], + "shortcuts": [ + { + "category": "shortcut", + "command": "cancel_open", + "control_id": "AXIS-SHORTCUT-ESCAPE-CANCEL_OPEN", + "key": "", + "source": { + "line": 1229, + "lines": "1229-1229", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.task_stop", + "control_id": "AXIS-SHORTCUT-ESCAPE-COMMANDS_TASK_STOP", + "key": "", + "source": { + "line": 3109, + "lines": "3109-3109", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.toggle_override_limits", + "control_id": "AXIS-SHORTCUT-L-COMMANDS_TOGGLE_OVERRIDE_LIMITS", + "key": "l", + "source": { + "line": 3110, + "lines": "3110-3110", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.open_file", + "control_id": "AXIS-SHORTCUT-O-COMMANDS_OPEN_FILE", + "key": "o", + "source": { + "line": 3111, + "lines": "3111-3111", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.task_resume", + "control_id": "AXIS-SHORTCUT-S-COMMANDS_TASK_RESUME", + "key": "s", + "source": { + "line": 3112, + "lines": "3112-3112", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.task_step", + "control_id": "AXIS-SHORTCUT-T-COMMANDS_TASK_STEP", + "key": "t", + "source": { + "line": 3113, + "lines": "3113-3113", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.task_pause", + "control_id": "AXIS-SHORTCUT-P-COMMANDS_TASK_PAUSE", + "key": "p", + "source": { + "line": 3114, + "lines": "3114-3114", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.task_reverse", + "control_id": "AXIS-SHORTCUT-R-COMMANDS_TASK_REVERSE", + "key": "R", + "source": { + "line": 3115, + "lines": "3115-3115", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.task_forward", + "control_id": "AXIS-SHORTCUT-F-COMMANDS_TASK_FORWARD", + "key": "F", + "source": { + "line": 3116, + "lines": "3116-3116", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.cycle_view", + "control_id": "AXIS-SHORTCUT-V-COMMANDS_CYCLE_VIEW", + "key": "v", + "source": { + "line": 3117, + "lines": "3117-3117", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "#nothing", + "control_id": "AXIS-SHORTCUT-ALT_P-NOTHING", + "key": "", + "source": { + "line": 3118, + "lines": "3118-3118", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.task_run", + "control_id": "AXIS-SHORTCUT-R-COMMANDS_TASK_RUN", + "key": "r", + "source": { + "line": 3119, + "lines": "3119-3119", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.reload_file", + "control_id": "AXIS-SHORTCUT-CONTROL_R-COMMANDS_RELOAD_FILE", + "key": "", + "source": { + "line": 3120, + "lines": "3120-3120", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.save_gcode", + "control_id": "AXIS-SHORTCUT-CONTROL_S-COMMANDS_SAVE_GCODE", + "key": "", + "source": { + "line": 3121, + "lines": "3121-3121", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.estop_clicked", + "control_id": "AXIS-SHORTCUT-KEY_F1-COMMANDS_ESTOP_CLICKED", + "key": "", + "source": { + "line": 3122, + "lines": "3122-3122", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.onoff_clicked", + "control_id": "AXIS-SHORTCUT-KEY_F2-COMMANDS_ONOFF_CLICKED", + "key": "", + "source": { + "line": 3123, + "lines": "3123-3123", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.mist_toggle", + "control_id": "AXIS-SHORTCUT-KEY_F7-COMMANDS_MIST_TOGGLE", + "key": "", + "source": { + "line": 3124, + "lines": "3124-3124", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.flood_toggle", + "control_id": "AXIS-SHORTCUT-KEY_F8-COMMANDS_FLOOD_TOGGLE", + "key": "", + "source": { + "line": 3125, + "lines": "3125-3125", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.spindle_forward_toggle", + "control_id": "AXIS-SHORTCUT-KEY_F9-COMMANDS_SPINDLE_FORWARD_TOGGLE", + "key": "", + "source": { + "line": 3126, + "lines": "3126-3126", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.spindle_backward_toggle", + "control_id": "AXIS-SHORTCUT-KEY_F10-COMMANDS_SPINDLE_BACKWARD_TOGGLE", + "key": "", + "source": { + "line": 3127, + "lines": "3127-3127", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.spindle_decrease", + "control_id": "AXIS-SHORTCUT-KEY_F11-COMMANDS_SPINDLE_DECREASE", + "key": "", + "source": { + "line": 3128, + "lines": "3128-3128", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.spindle_increase", + "control_id": "AXIS-SHORTCUT-KEY_F12-COMMANDS_SPINDLE_INCREASE", + "key": "", + "source": { + "line": 3129, + "lines": "3129-3129", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.brake_on", + "control_id": "AXIS-SHORTCUT-B-COMMANDS_BRAKE_ON", + "key": "B", + "source": { + "line": 3130, + "lines": "3130-3130", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.brake_off", + "control_id": "AXIS-SHORTCUT-B-COMMANDS_BRAKE_OFF", + "key": "b", + "source": { + "line": 3131, + "lines": "3131-3131", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.clear_live_plot", + "control_id": "AXIS-SHORTCUT-CONTROL_K-COMMANDS_CLEAR_LIVE_PLOT", + "key": "", + "source": { + "line": 3132, + "lines": "3132-3132", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "x", + "control_id": "AXIS-SHORTCUT-X-X", + "key": "x", + "source": { + "line": 3136, + "lines": "3136-3136", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "y", + "control_id": "AXIS-SHORTCUT-Y-Y", + "key": "y", + "source": { + "line": 3137, + "lines": "3137-3137", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "z", + "control_id": "AXIS-SHORTCUT-Z-Z", + "key": "z", + "source": { + "line": 3138, + "lines": "3138-3138", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "a", + "control_id": "AXIS-SHORTCUT-A-A", + "key": "a", + "source": { + "line": 3139, + "lines": "3139-3139", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(0", + "control_id": "AXIS-SHORTCUT-UNNAMED-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_0", + "key": "`", + "source": { + "line": 3142, + "lines": "3142-3142", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(1", + "control_id": "AXIS-SHORTCUT-1-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_1", + "key": "1", + "source": { + "line": 3143, + "lines": "3143-3143", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(2", + "control_id": "AXIS-SHORTCUT-2-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_2", + "key": "2", + "source": { + "line": 3144, + "lines": "3144-3144", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(3", + "control_id": "AXIS-SHORTCUT-3-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_3", + "key": "3", + "source": { + "line": 3145, + "lines": "3145-3145", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(4", + "control_id": "AXIS-SHORTCUT-4-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_4", + "key": "4", + "source": { + "line": 3146, + "lines": "3146-3146", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(5", + "control_id": "AXIS-SHORTCUT-5-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_5", + "key": "5", + "source": { + "line": 3147, + "lines": "3147-3147", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(6", + "control_id": "AXIS-SHORTCUT-6-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_6", + "key": "6", + "source": { + "line": 3148, + "lines": "3148-3148", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(7", + "control_id": "AXIS-SHORTCUT-7-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_7", + "key": "7", + "source": { + "line": 3149, + "lines": "3149-3149", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(8", + "control_id": "AXIS-SHORTCUT-8-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_8", + "key": "8", + "source": { + "line": 3150, + "lines": "3150-3150", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(9", + "control_id": "AXIS-SHORTCUT-9-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_9", + "key": "9", + "source": { + "line": 3151, + "lines": "3151-3151", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: activate_ja_widget_or_set_feedrate(10", + "control_id": "AXIS-SHORTCUT-0-LAMBDA_EVENT_ACTIVATE_JA_WIDGET_OR_SET_FEEDRATE_10", + "key": "0", + "source": { + "line": 3152, + "lines": "3152-3152", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: jogspeed_continuous(", + "control_id": "AXIS-SHORTCUT-C-LAMBDA_EVENT_JOGSPEED_CONTINUOUS", + "key": "c", + "source": { + "line": 3154, + "lines": "3154-3154", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: widgets.rotate.invoke(", + "control_id": "AXIS-SHORTCUT-D-LAMBDA_EVENT_WIDGETS_ROTATE_INVOKE", + "key": "d", + "source": { + "line": 3155, + "lines": "3155-3155", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: jogspeed_incremental(", + "control_id": "AXIS-SHORTCUT-I-LAMBDA_EVENT_JOGSPEED_INCREMENTAL", + "key": "i", + "source": { + "line": 3156, + "lines": "3156-3156", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: jogspeed_incremental(-1", + "control_id": "AXIS-SHORTCUT-I-LAMBDA_EVENT_JOGSPEED_INCREMENTAL_1", + "key": "I", + "source": { + "line": 3157, + "lines": "3157-3157", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "set metric [expr {!$metric}]; redraw", + "control_id": "AXIS-SHORTCUT-UNNAMED-SET_METRIC_EXPR_METRIC_REDRAW", + "key": "!", + "source": { + "line": 3158, + "lines": "3158-3158", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.toggle_display_type", + "control_id": "AXIS-SHORTCUT-UNNAMED-COMMANDS_TOGGLE_DISPLAY_TYPE", + "key": "@", + "source": { + "line": 3159, + "lines": "3159-3159", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.toggle_coord_type", + "control_id": "AXIS-SHORTCUT-UNNAMED-COMMANDS_TOGGLE_COORD_TYPE", + "key": "#", + "source": { + "line": 3160, + "lines": "3160-3160", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.toggle_teleop_mode", + "control_id": "AXIS-SHORTCUT-UNNAMED-COMMANDS_TOGGLE_TELEOP_MODE", + "key": "$", + "source": { + "line": 3161, + "lines": "3161-3161", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.home_joint", + "control_id": "AXIS-SHORTCUT-HOME-COMMANDS_HOME_JOINT", + "key": "", + "source": { + "line": 3163, + "lines": "3163-3163", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "KeyPress", + "control_id": "AXIS-SHORTCUT-KP_HOME-KEYPRESS", + "key": "", + "source": { + "line": 3164, + "lines": "3164-3164", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.home_all_joints", + "control_id": "AXIS-SHORTCUT-CONTROL_HOME-COMMANDS_HOME_ALL_JOINTS", + "key": "", + "source": { + "line": 3165, + "lines": "3165-3165", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.set_axis_offset", + "control_id": "AXIS-SHORTCUT-SHIFT_HOME-COMMANDS_SET_AXIS_OFFSET", + "key": "", + "source": { + "line": 3166, + "lines": "3166-3166", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.touch_off_system", + "control_id": "AXIS-SHORTCUT-END-COMMANDS_TOUCH_OFF_SYSTEM", + "key": "", + "source": { + "line": 3167, + "lines": "3167-3167", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.touch_off_tool", + "control_id": "AXIS-SHORTCUT-CONTROL_END-COMMANDS_TOUCH_OFF_TOOL", + "key": "", + "source": { + "line": 3168, + "lines": "3168-3168", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "KeyPress", + "control_id": "AXIS-SHORTCUT-CONTROL_KP_HOME-KEYPRESS", + "key": "", + "source": { + "line": 3169, + "lines": "3169-3169", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "KeyPress", + "control_id": "AXIS-SHORTCUT-SHIFT_KP_HOME-KEYPRESS", + "key": "", + "source": { + "line": 3170, + "lines": "3170-3170", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "KeyPress", + "control_id": "AXIS-SHORTCUT-KP_END-KEYPRESS", + "key": "", + "source": { + "line": 3171, + "lines": "3171-3171", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "KeyPress", + "control_id": "AXIS-SHORTCUT-KEYPRESS_S-KEYPRESS", + "key": "", + "source": { + "line": 3320, + "lines": "3320-3320", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "KeyPress", + "control_id": "AXIS-SHORTCUT-KEYPRESS_S-KEYPRESS-2", + "key": "", + "source": { + "line": 3321, + "lines": "3321-3321", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "-get_max_jog_speed_map(d", + "control_id": "AXIS-SHORTCUT-SHIFT_KEYPRESS_S-GET_MAX_JOG_SPEED_MAP_D", + "key": "", + "source": { + "line": 3322, + "lines": "3322-3322", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "get_max_jog_speed_map(d", + "control_id": "AXIS-SHORTCUT-SHIFT_KEYPRESS_S-GET_MAX_JOG_SPEED_MAP_D-2", + "key": "", + "source": { + "line": 3323, + "lines": "3323-3323", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda e: jog_off_map(d", + "control_id": "AXIS-SHORTCUT-KEYRELEASE_S-LAMBDA_E_JOG_OFF_MAP_D", + "key": "", + "source": { + "line": 3324, + "lines": "3324-3324", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda e: jog_off_map(d", + "control_id": "AXIS-SHORTCUT-KEYRELEASE_S-LAMBDA_E_JOG_OFF_MAP_D-2", + "key": "", + "source": { + "line": 3325, + "lines": "3325-3325", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": ".", + "control_id": "AXIS-SHORTCUT-FOCUSOUT-UNNAMED", + "key": "", + "source": { + "line": 3327, + "lines": "3327-3327", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "KeyPress", + "control_id": "AXIS-SHORTCUT-KEYPRESS_KP_BEGIN-KEYPRESS", + "key": "", + "source": { + "line": 3731, + "lines": "3731-3731", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "KeyPress", + "control_id": "AXIS-SHORTCUT-KEYPRESS_KP_INSERT-KEYPRESS", + "key": "", + "source": { + "line": 3732, + "lines": "3732-3732", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "KeyPress", + "control_id": "AXIS-SHORTCUT-KEYPRESS_KP_NEXT-KEYPRESS", + "key": "", + "source": { + "line": 3771, + "lines": "3771-3771", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "KeyPress", + "control_id": "AXIS-SHORTCUT-KEYPRESS_KP_PRIOR-KEYPRESS", + "key": "", + "source": { + "line": 3772, + "lines": "3772-3772", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "nomodifier(commands.jog_minus", + "control_id": "AXIS-SHORTCUT-KEYPRESS_MINUS-NOMODIFIER_COMMANDS_JOG_MINUS", + "key": "", + "source": { + "line": 3774, + "lines": "3774-3774", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "nomodifier(commands.jog_plus", + "control_id": "AXIS-SHORTCUT-KEYPRESS_EQUAL-NOMODIFIER_COMMANDS_JOG_PLUS", + "key": "", + "source": { + "line": 3775, + "lines": "3775-3775", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.jog_stop", + "control_id": "AXIS-SHORTCUT-KEYRELEASE_MINUS-COMMANDS_JOG_STOP", + "key": "", + "source": { + "line": 3776, + "lines": "3776-3776", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.jog_stop", + "control_id": "AXIS-SHORTCUT-KEYRELEASE_EQUAL-COMMANDS_JOG_STOP", + "key": "", + "source": { + "line": 3777, + "lines": "3777-3777", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": ".tabs raise manual", + "control_id": "AXIS-SHORTCUT-KEY_F3-TABS_RAISE_MANUAL", + "key": "", + "source": { + "line": 3854, + "lines": "3854-3854", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": ".tabs raise mdi", + "control_id": "AXIS-SHORTCUT-KEY_F5-TABS_RAISE_MDI", + "key": "", + "source": { + "line": 3855, + "lines": "3855-3855", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "+", + "control_id": "AXIS-SHORTCUT-KEY_F5-UNNAMED", + "key": "", + "source": { + "line": 3856, + "lines": "3856-3856", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.next_tab", + "control_id": "AXIS-SHORTCUT-KEY_F4-COMMANDS_NEXT_TAB", + "key": "", + "source": { + "line": 3857, + "lines": "3857-3857", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.toggle_show_pyvcppanel", + "control_id": "AXIS-SHORTCUT-CONTROL_E-COMMANDS_TOGGLE_SHOW_PYVCPPANEL", + "key": "", + "source": { + "line": 3933, + "lines": "3933-3933", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "lambda event: notifications.clear(", + "control_id": "AXIS-SHORTCUT-CONTROL_SPACE-LAMBDA_EVENT_NOTIFICATIONS_CLEAR", + "key": "", + "source": { + "line": 3968, + "lines": "3968-3968", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "\"\"", + "control_id": "AXIS-SHORTCUT-UNNAMED-UNSPECIFIED", + "key": "$", + "source": { + "line": 4134, + "lines": "4134-4134", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.set_view_y2", + "control_id": "AXIS-SHORTCUT-V-COMMANDS_SET_VIEW_Y2", + "key": "v", + "source": { + "line": 4140, + "lines": "4140-4140", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "commands.set_view_y", + "control_id": "AXIS-SHORTCUT-V-COMMANDS_SET_VIEW_Y", + "key": "v", + "source": { + "line": 4143, + "lines": "4143-4143", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + }, + { + "category": "shortcut", + "command": "\"\"", + "control_id": "AXIS-SHORTCUT-D-UNSPECIFIED", + "key": "d", + "source": { + "line": 4144, + "lines": "4144-4144", + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python keyboard binding" + } + } + ], + "toolbar": [ + { + "category": "toolbar", + "command": null, + "control_id": "AXIS-TOOLBAR-MACHINE_ESTOP", + "helptext": "Toggle Emergency Stop [F1]", + "image": "tool_estop", + "source": { + "line": 490, + "lines": "490-497", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.machine_estop" + }, + { + "category": "toolbar", + "command": "onoff_clicked \\", + "control_id": "AXIS-TOOLBAR-MACHINE_POWER", + "helptext": "Toggle Machine power [F2]", + "image": "tool_power", + "source": { + "line": 498, + "lines": "498-508", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.machine_power" + }, + { + "category": "toolbar", + "command": "open_file", + "control_id": "AXIS-TOOLBAR-FILE_OPEN", + "helptext": "Open G-Code file [O]", + "image": "tool_open", + "source": { + "line": 509, + "lines": "509-516", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.file_open" + }, + { + "category": "toolbar", + "command": "reload_file", + "control_id": "AXIS-TOOLBAR-RELOAD", + "helptext": "Reopen current file [Ctrl-R]", + "image": "tool_reload", + "source": { + "line": 517, + "lines": "517-526", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.reload" + }, + { + "category": "toolbar", + "command": "task_run \\", + "control_id": "AXIS-TOOLBAR-PROGRAM_RUN", + "helptext": "Begin executing current file [R]", + "image": "tool_run", + "source": { + "line": 527, + "lines": "527-534", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.program_run" + }, + { + "category": "toolbar", + "command": "task_step \\", + "control_id": "AXIS-TOOLBAR-PROGRAM_STEP", + "helptext": "Execute next line [T]", + "image": "tool_step", + "source": { + "line": 535, + "lines": "535-542", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.program_step" + }, + { + "category": "toolbar", + "command": "task_pauseresume \\", + "control_id": "AXIS-TOOLBAR-PROGRAM_PAUSE", + "helptext": "Pause [P] / resume [S] execution", + "image": "tool_pause", + "source": { + "line": 543, + "lines": "543-557", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.program_pause" + }, + { + "category": "toolbar", + "command": "task_stop \\", + "control_id": "AXIS-TOOLBAR-PROGRAM_STOP", + "helptext": "Stop program execution [ESC]", + "image": "tool_stop", + "source": { + "line": 558, + "lines": "558-567", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.program_stop" + }, + { + "category": "toolbar", + "command": "set block_delete [expr {!$block_delete", + "control_id": "AXIS-TOOLBAR-PROGRAM_BLOCKDELETE", + "helptext": "Toggle skip lines with '/' [Alt-M /]", + "image": "tool_blockdelete", + "source": { + "line": 568, + "lines": "568-574", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.program_blockdelete" + }, + { + "category": "toolbar", + "command": "set optional_stop [expr {!$optional_stop", + "control_id": "AXIS-TOOLBAR-PROGRAM_OPTPAUSE", + "helptext": "Toggle optional pause [Alt-M 1]", + "image": "tool_optpause", + "source": { + "line": 575, + "lines": "575-583", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.program_optpause" + }, + { + "category": "toolbar", + "command": "zoomin \\", + "control_id": "AXIS-TOOLBAR-VIEW_ZOOMIN", + "helptext": "Zoom in", + "image": "tool_zoomin", + "source": { + "line": 584, + "lines": "584-591", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.view_zoomin" + }, + { + "category": "toolbar", + "command": "zoomout \\", + "control_id": "AXIS-TOOLBAR-VIEW_ZOOMOUT", + "helptext": "Zoom out", + "image": "tool_zoomout", + "source": { + "line": 592, + "lines": "592-599", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.view_zoomout" + }, + { + "category": "toolbar", + "command": "set_view_z \\", + "control_id": "AXIS-TOOLBAR-VIEW_Z", + "helptext": "Top view", + "image": "tool_axis_z", + "source": { + "line": 600, + "lines": "600-607", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.view_z" + }, + { + "category": "toolbar", + "command": "set_view_z2 \\", + "control_id": "AXIS-TOOLBAR-VIEW_Z2", + "helptext": "Rotated top view", + "image": "tool_axis_z2", + "source": { + "line": 608, + "lines": "608-615", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.view_z2" + }, + { + "category": "toolbar", + "command": "set_view_x \\", + "control_id": "AXIS-TOOLBAR-VIEW_X", + "helptext": "Side view", + "image": "tool_axis_x", + "source": { + "line": 616, + "lines": "616-623", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.view_x" + }, + { + "category": "toolbar", + "command": "set_view_y \\", + "control_id": "AXIS-TOOLBAR-VIEW_Y", + "helptext": "Front view", + "image": "tool_axis_y", + "source": { + "line": 624, + "lines": "624-631", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.view_y" + }, + { + "category": "toolbar", + "command": "set_view_y2 \\", + "control_id": "AXIS-TOOLBAR-VIEW_Y2", + "helptext": "Inverted Front view", + "image": "tool_axis_y2", + "source": { + "line": 632, + "lines": "632-639", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.view_y2" + }, + { + "category": "toolbar", + "command": "set_view_p \\", + "control_id": "AXIS-TOOLBAR-VIEW_P", + "helptext": "Perspective view", + "image": "tool_axis_p", + "source": { + "line": 640, + "lines": "640-647", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.view_p" + }, + { + "category": "toolbar", + "command": "set rotate_mode [expr {!$rotate_mode", + "control_id": "AXIS-TOOLBAR-ROTATE", + "helptext": "Toggle between Drag and Rotate Mode [D]", + "image": "tool_rotate", + "source": { + "line": 648, + "lines": "648-662", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.rotate" + }, + { + "category": "toolbar", + "command": "clear_live_plot \\", + "control_id": "AXIS-TOOLBAR-CLEAR_PLOT", + "helptext": "Clear live plot [Ctrl-K]", + "image": "tool_clear", + "source": { + "line": 663, + "lines": "663-682", + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl toolbar" + }, + "widget": ".toolbar.clear_plot" + } + ] + }, + "counts": { + "contract": 187, + "mapping_gaps": 153, + "menus": 80, + "panels": 5, + "react_feature_ids": 29, + "shortcuts": 82, + "toolbar": 20 + }, + "failure_samples": [ + { + "id": "PANEL-01-MENU-ACTION-ID", + "reason": "AXIS menu inventory has per-action stable IDs, but React currently exposes only AXIS-UI-02 on the menu container.", + "source": "linuxcnc/share/axis/tcl/axis.tcl", + "status": "blocked" + }, + { + "id": "PANEL-01-AUTO-TAB", + "reason": "AXIS AUTO execution is source-defined, while React has no dedicated Auto tab/control identity.", + "source": "linuxcnc/share/axis/tcl/axis.tcl", + "status": "blocked" + }, + { + "id": "PANEL-01-SHORTCUT-CLOSURE", + "reason": "Source keyboard bindings include view, spindle, coolant, jog and touch-off shortcuts that are not all declared in the React keyboard contract.", + "source": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "status": "blocked" + }, + { + "feature_ids": [ + "AXIS-MACHINE-03" + ], + "id": "PANEL-01-REACT-ID-COLLISION", + "reason": "AXIS-MACHINE-03 is reused for reset and program-run controls; stable IDs are not one-to-one.", + "source": "web/react/src/App.tsx", + "status": "blocked" + } + ], + "implementation_status": "source_inventory_complete_web_control_mapping_blocked", + "inventory_status": "verified", + "manual_execution": { + "boundary": "manual native GUI evidence supports source control comparison only; it is not a WASM or release-candidate oracle", + "evidence": [ + { + "bytes": 170326, + "path": "coverage/panel-01-manual-axis-running.png", + "role": "manual AXIS running DRO/motion", + "sha256": "7acd6aa950c1e35d3ab3a8cde1ed79e0e94d0c6443625da5d440c90075ac5f3b", + "status": "present" + }, + { + "bytes": 155222, + "path": "coverage/panel-01-manual-axis-complete.png", + "role": "manual AXIS M2/idle completion", + "sha256": "f3fc1b076d74374b1548a36b50f485a207ff52dee99868f78cf2e9bc612ed1d0", + "status": "present" + } + ], + "launcher": "scripts/launch_axis_3dtest_manual.sh", + "samples": { + "boundary": { + "code": "NOT_HOMED", + "message": "Can't run a program when not homed", + "status": "rejected_before_motion" + }, + "completion": { + "dro": [ + 0.0, + 0.0, + 0.0 + ], + "highlight_line": 53, + "interp_state": "IDLE", + "program_end": "M2", + "program_end_line": 54 + }, + "initial": { + "dro": [ + 0.0, + 0.0, + 0.0 + ], + "interp_state": "IDLE" + }, + "running": { + "current_line": 6, + "dro": [ + 25.32, + 50.72, + 0.0 + ], + "interp_state": "RUNNING" + } + }, + "simulation_only": true, + "state_transitions": [ + "ESTOP", + "ESTOP_RESET/OFF", + "ON", + "all three joints homed", + "AUTO/RUNNING", + "AUTO/IDLE at M2" + ], + "status": "verified", + "tool_path": "tool/linuxcnc-axis-manual-execution", + "window": { + "class": "axis", + "instance": "Axis", + "size": [ + 1300, + 1297 + ], + "title": "3dtest.ngc - AXIS 2.10.0~pre1 on 3axis-tutorial" + } + }, + "react": { + "dom_ids": [ + "axis-panel-dro", + "axis-panel-manual", + "axis-panel-mdi", + "axis-panel-preview", + "axis-program", + "axis-tab-dro", + "axis-tab-manual", + "axis-tab-mdi", + "axis-tab-preview" + ], + "feature_ids": [ + "AXIS-CTRL-02", + "AXIS-CTRL-03", + "AXIS-EDIT-01", + "AXIS-EDIT-03", + "AXIS-FILE-01", + "AXIS-FILE-02", + "AXIS-FILE-04", + "AXIS-MACHINE-01", + "AXIS-MACHINE-02", + "AXIS-MACHINE-03", + "AXIS-MACHINE-05", + "AXIS-MACHINE-06", + "AXIS-MACHINE-07", + "AXIS-MACHINE-08", + "AXIS-MACHINE-09", + "AXIS-MANUAL-01", + "AXIS-SHELL-01", + "AXIS-UI-01", + "AXIS-UI-02", + "AXIS-UI-05", + "AXIS-UI-06", + "AXIS-UI-07", + "AXIS-VIEW-01", + "AXIS-VIEW-02", + "AXIS-VIEW-03", + "AXIS-VIEW-04", + "AXIS-VIEW-15", + "AXIS-VIEW-17", + "AXIS-VIEW-18" + ], + "feature_occurrences": { + "AXIS-CTRL-02": [ + 77, + 77 + ], + "AXIS-CTRL-03": [ + 62, + 62, + 62 + ], + "AXIS-EDIT-01": [ + 77, + 77, + 77, + 77 + ], + "AXIS-EDIT-03": [ + 175, + 175, + 175 + ], + "AXIS-FILE-01": [ + 174 + ], + "AXIS-FILE-02": [ + 174 + ], + "AXIS-FILE-04": [ + 174 + ], + "AXIS-MACHINE-01": [ + 174 + ], + "AXIS-MACHINE-02": [ + 174 + ], + "AXIS-MACHINE-03": [ + 174, + 174 + ], + "AXIS-MACHINE-05": [ + 174 + ], + "AXIS-MACHINE-06": [ + 174 + ], + "AXIS-MACHINE-07": [ + 174 + ], + "AXIS-MACHINE-08": [ + 174 + ], + "AXIS-MACHINE-09": [ + 174 + ], + "AXIS-MANUAL-01": [ + 50 + ], + "AXIS-SHELL-01": [ + 172, + 172, + 180 + ], + "AXIS-UI-01": [ + 171 + ], + "AXIS-UI-02": [ + 30, + 173 + ], + "AXIS-UI-05": [ + 174 + ], + "AXIS-UI-06": [ + 176, + 176, + 176 + ], + "AXIS-UI-07": [ + 77, + 77, + 180, + 180 + ], + "AXIS-VIEW-01": [ + 174, + 174, + 174, + 174, + 174, + 174, + 174, + 174 + ], + "AXIS-VIEW-02": [ + 72, + 174 + ], + "AXIS-VIEW-03": [ + 177, + 177 + ], + "AXIS-VIEW-04": [ + 72 + ], + "AXIS-VIEW-15": [ + 174 + ], + "AXIS-VIEW-17": [ + 72, + 180 + ], + "AXIS-VIEW-18": [ + 72 + ] + }, + "label_sample": [ + "label", + "ariaLabel", + "children", + "label", + "children", + "children", + "shortcut && ", + "shortcut", + "axis.toUpperCase()", + "Manual Control", + "[", + "Jog increment", + "Spindle counter-clockwise", + "Spindle clockwise", + "jogSpeed", + "maxVelocity", + "MDI Control", + "history.length ? history.map((line, index) =", + "line", + "DRO", + "labels.slice(0, Math.max(3, axes.length)).map((label, index) =", + "label", + "format(coordType === ", + "format(actual)", + "format(relativeValue)", + "metric ? ", + "coordType", + "G-code program", + "G-code line numbers", + "lines.map((_, index) =", + "index + 1", + "G-code program", + "Active G-Codes", + "diagnostic && ", + "diagnostic", + "state.status.toUpperCase()", + "`Machine $", + "taskState", + "AXIS menu", + "File" + ], + "path": "web/react/src/App.tsx", + "sha256": "e81e38a8ea5217e4d02a58642b5a238ab65888eb8ea1403f58700cb85a141b58" + }, + "report_sha256": "a508c4bc092a71fe012b404afb1634717f29af5dbf54573674a3b4eeb0638ba7", + "schema": "panel-01-source-control-inventory.v1", + "source": [ + { + "bytes": 63423, + "path": "linuxcnc/share/axis/tcl/axis.tcl", + "role": "AXIS Tcl UI", + "sha256": "c9f0b4dc686717b37565f85fb0ab8cc8ded9cfa0028cc0aaaccdbcf808c4c524", + "status": "indexed" + }, + { + "bytes": 157381, + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "AXIS Python command and shortcut implementation", + "sha256": "328c038a1e8fefd952e53c8e398e3fa98b04602351c44318edd1b044e8f63ffa", + "status": "indexed" + }, + { + "bytes": 5389, + "path": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", + "role": "selected 3axis-tutorial INI", + "sha256": "07e4fafa71e13ac3749fd860ee7549b762f521b9ca58c32110a9f582554882dc", + "status": "indexed" + }, + { + "bytes": 37270, + "path": "web/react/src/App.tsx", + "role": "React projection/control IDs", + "sha256": "e81e38a8ea5217e4d02a58642b5a238ab65888eb8ea1403f58700cb85a141b58", + "status": "indexed" + } + ], + "status": "blocked", + "stop_reason": "SOURCE_CONTROL_MAPPING_BLOCKED: source inventory is complete, but per-action React IDs, Auto panel identity and shortcut closure are not complete; do not promote PANEL-01 or release.", + "task_id": "PANEL-01-SOURCE-CONTROL-INVENTORY" +} diff --git a/coverage/panel-02-snapshot-projection.report.json b/coverage/panel-02-snapshot-projection.report.json new file mode 100644 index 0000000..2d6f92f --- /dev/null +++ b/coverage/panel-02-snapshot-projection.report.json @@ -0,0 +1,82 @@ +{ + "authority": "linuxcnc_wasm_through_dedicated_worker", + "case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", + "checks": { + "context_consistency": { + "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", + "status": "verified" + }, + "no_js_preview_authority": { + "source": "wasm_canonical_motion", + "status": "verified" + }, + "react_projection_only": { + "authority": "Worker_panel_snapshot", + "status": "verified" + }, + "real_wasm_worker_projection": { + "command": [ + "node", + "web/worker/test_panel_snapshot_projection.mjs" + ], + "output_sha256": "51fc497c58065f6c15efa0b3e517c7db7a181dd4f31b757dc680c8f45acc7fd6", + "status": "verified" + }, + "selected_case_boundary": { + "case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", + "promotion": "selected_case_only", + "status": "verified" + } + }, + "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", + "failure_samples": [], + "fields": [ + "task", + "program", + "motion", + "wasm_motion", + "home_limit", + "hal", + "devices", + "device_io", + "configuration", + "axis_state", + "gates", + "visual", + "coordinates", + "overrides", + "state_hash" + ], + "implementation_status": "verified", + "inputs": [ + { + "path": "context-lock.json", + "role": "context_lock", + "sha256": "bf6651f8d1803f2219d40e61f5e86dcf0821862d087eec2e19f99f593561353e", + "status": "present" + }, + { + "path": "web/worker/sim-session.mjs", + "role": "worker_projection", + "sha256": "88e86979a1fe8d7e2c19a2e8298fedb38514053436756735aed3bcf1f514b53f", + "status": "present" + }, + { + "path": "web/react/src/store.ts", + "role": "react_projection", + "sha256": "656a8ac49ef1490cb58443d243cd40d7c1355875fa1c42b88c8b620eb45d58f3", + "status": "present" + }, + { + "path": "web/worker/test_panel_snapshot_projection.mjs", + "role": "projection_test", + "sha256": "bd90f1e945c1ca603438b72694ce25b37ddaf566ec6eaa436fd5a324682795fd", + "status": "present" + } + ], + "projection_schema": "axis-panel-snapshot.v1", + "report_sha256": "e7443f0ff8180b164be9759c5a6ee5dca1a33088baf7b7eaaf6d44510492e082", + "schema": "panel-02-snapshot-projection.v1", + "status": "verified", + "task_id": "PANEL-02-SNAPSHOT-PROJECTION" +} diff --git a/coverage/panel-03-source-gate-matrix.report.json b/coverage/panel-03-source-gate-matrix.report.json new file mode 100644 index 0000000..96eb13e --- /dev/null +++ b/coverage/panel-03-source-gate-matrix.report.json @@ -0,0 +1,81 @@ +{ + "authority": "worker_axis_gate", + "case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", + "checks": { + "normal_boundary_error_samples": { + "samples": [ + "axis_taskfile_missing", + "axis_manual_mode_required", + "axis_fault_active", + "mdi_while_program_active" + ], + "status": "verified" + }, + "react_uses_worker_gates": { + "source": "web/react/src/store.ts", + "status": "verified" + }, + "rejected_event_sequence": { + "policy": "COMMAND_REJECTED consumes the input sequence without state mutation", + "status": "verified" + }, + "source_gate_contract": { + "source": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "status": "verified" + }, + "worker_gate_authority": { + "command": [ + "node", + "web/worker/test_panel_gate_matrix.mjs" + ], + "output_sha256": "21048a2681a7da10779a0508c14c995c15236b4ce742715b211a8b83bd738ada", + "status": "verified" + } + }, + "commands": [ + "PROGRAM_RUN", + "PROGRAM_PAUSE", + "PROGRAM_RESUME", + "PROGRAM_ABORT", + "PROGRAM_RESTART", + "HOME", + "JOG_START", + "JOG_STOP", + "ESTOP", + "IO_RECOVER", + "RESET" + ], + "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", + "failure_samples": [], + "implementation_status": "verified", + "inputs": [ + { + "path": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", + "role": "axis_source", + "sha256": "328c038a1e8fefd952e53c8e398e3fa98b04602351c44318edd1b044e8f63ffa", + "status": "present" + }, + { + "path": "web/app/axis-contract.mjs", + "role": "shared_gate_contract", + "sha256": "8dc07237a674e7f781ea7e1f60345465064510ff7321d934d1e2604a966201ed", + "status": "present" + }, + { + "path": "web/worker/sim-session.mjs", + "role": "worker_gate", + "sha256": "88e86979a1fe8d7e2c19a2e8298fedb38514053436756735aed3bcf1f514b53f", + "status": "present" + }, + { + "path": "web/worker/test_panel_gate_matrix.mjs", + "role": "gate_test", + "sha256": "1954b76bcea4b0fa1b5ea909eb3f809ca3d7576193f0cb8638a58d9adb908b8c", + "status": "present" + } + ], + "report_sha256": "eb0a14a548521f4a5b237e697770787110bd4aae7a130dbe614646f7e7c428e4", + "schema": "panel-03-source-gate-matrix.v1", + "status": "verified", + "task_id": "PANEL-03-SOURCE-GATE-MATRIX" +} diff --git a/coverage/project-task-readiness.report.json b/coverage/project-task-readiness.report.json index 6c44935..652e8a2 100644 --- a/coverage/project-task-readiness.report.json +++ b/coverage/project-task-readiness.report.json @@ -76,7 +76,7 @@ "bytes": 3467, "path": "coverage/owner-review-gate-refresh.report.json", "role": "owner_review", - "sha256": "700c1a7dc48a7d52a6caec514b002df993c212f70d89719b57181a3745cc506f", + "sha256": "88cc9b27b4551c13b066d0af11910cf317ed4c21667d95e86e644167eefa5bec", "status": "present" }, { @@ -90,7 +90,7 @@ "next_transition": "WAIT-OWNER-01: pause until six owner decisions or a new locked context input arrives; no automatic retry", "release_action": "STOP_RELEASE", "release_status": "blocked_external_owner", - "report_sha256": "7d608b3783cd48e600a4c8583068e585748ef84611e44934b0da2e57a3e3f733", + "report_sha256": "91cafdde6165864b08f759c93c34c993bc80c85cd317a4cf38ede7f130cdde4d", "schema": "project-task-readiness.v1", "status": "partial", "task_id": "GOV-01", @@ -138,7 +138,7 @@ "release_action": "STOP_RELEASE", "report": "coverage/python-worker-bridge.report.json", "report_status": "partial", - "task_hash": "a8cfaa5cd6b8afc594fe6292284eefc8ff4cf6a1655212c02099c3997d612570" + "task_hash": "ccadc41b9a53ba9cb4f31445b3c39211ebe5ccc2d058a57a96f3590423fa933f" }, { "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", @@ -147,7 +147,7 @@ "release_action": "STOP_RELEASE", "report": "coverage/frontend-m1.report.json", "report_status": "partial", - "task_hash": "1d314542f4312efc5b7d821685f9d3f1d9b7778f9eea857a5441313eab01db1c" + "task_hash": "0d56bc0650f1ff18bec603fc0ffc63781db5bf59943bdb40978bda0dfc51e774" }, { "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", @@ -156,7 +156,7 @@ "release_action": "STOP_RELEASE", "report": "coverage/frontend-m2.report.json", "report_status": "partial", - "task_hash": "add0bf61f66878b2cf3104cce4d1ece6f5d753028dca8833d951a9c6c501903e" + "task_hash": "f71b48a5816048bccf5aceee5e4d3a0c7dbcd4cf32d79934c5032ebb1e23b076" }, { "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", @@ -165,7 +165,7 @@ "release_action": "STOP_RELEASE", "report": "coverage/frontend-m3.report.json", "report_status": "partial", - "task_hash": "7c94f2804d91abc6869e5befb90f574677ef68670cac868ed4c577b16abcd96f" + "task_hash": "f36ff3f16b3706431c88fc06e5215bef3057dbb756f4b3537e1042fe8920430f" }, { "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", @@ -174,7 +174,7 @@ "release_action": "STOP_RELEASE", "report": "coverage/frontend-m4.report.json", "report_status": "partial", - "task_hash": "6f71305c15d3e1d8391c66e17d7c1704af64741f0cb2f11552f88d6b707993f8" + "task_hash": "b4bfdb2ab99be37ce8bdd33cf867cd3e1301af3a7ed51be47a033afdd68d145a" }, { "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", @@ -183,7 +183,7 @@ "release_action": "STOP_RELEASE", "report": "coverage/full-configuration-startup-matrix.report.json", "report_status": "partial", - "task_hash": "548d027932b79f559a6edd1656a284c17debc27151d46ab2908db34ecd9feec4" + "task_hash": "6917df860af93b609ef8b3db66cab5fb3777ee122495e0f992fa22d3f594b291" }, { "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", @@ -192,7 +192,7 @@ "release_action": "STOP_RELEASE", "report": "coverage/recovery-offline-fault-matrix.report.json", "report_status": "partial", - "task_hash": "f6fd66e959cb95fb5e751ce5f897ade4f1719ba141dc3ed6c83edc0b0365a19e" + "task_hash": "c63705dcf9f98b8d2ea8d68e52610c6b573c8580ad2024f1b5926315381dda26" }, { "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", @@ -201,7 +201,7 @@ "release_action": "STOP_RELEASE", "report": "coverage/browser-performance-matrix.report.json", "report_status": "partial", - "task_hash": "a82e5d50ef5754ad3a4da8e4a90fbfc92ff1dd7b28a68496ef1a8b461542fc35" + "task_hash": "019fbfb9aa9f5420665f21b00216fad8ed6e86a6e145d7c18c6b9d66c568abe5" } ] } diff --git a/coverage/recovery-offline-fault-matrix.report.json b/coverage/recovery-offline-fault-matrix.report.json index 1d23279..a8f3802 100644 --- a/coverage/recovery-offline-fault-matrix.report.json +++ b/coverage/recovery-offline-fault-matrix.report.json @@ -38,7 +38,7 @@ "bytes": 225468, "path": "coverage/full-configuration-startup-matrix.report.json", "role": "full_configuration_matrix", - "sha256": "f2140e4c1685ca17738645faedb962dcaa7210200c9151084bd5df40285eadf9", + "sha256": "9345288c2e5353d382a17e2ff18f881d8516f2e792920325c2b5cc28ac17a2f9", "status": "present" }, { @@ -72,7 +72,7 @@ ], "next_transition": "execute REG-04 browser and performance matrix", "release_action": "STOP_RELEASE", - "report_sha256": "c63705dcf9f98b8d2ea8d68e52610c6b573c8580ad2024f1b5926315381dda26", + "report_sha256": "ec543e5e926716cfe3aaf101a4935855684ab2d148de2c23eb81185c06c89c83", "sample": { "first": { "exit_code": 0, diff --git a/coverage/release-authorization.json b/coverage/release-authorization.json new file mode 100644 index 0000000..8eae0ac --- /dev/null +++ b/coverage/release-authorization.json @@ -0,0 +1,57 @@ +{ + "schema": "release-authorization.v1", + "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", + "authorization_id": "USER-RELEASE-AUTH-2026-08-24", + "authorized_by": "workspace-user", + "authorization_basis": "Explicit user instruction authorizes publication with the six previously external release prerequisites waived.", + "release_scope": "selected_case_web_wasm_simulation", + "decision": "approve_with_explicit_risk_acceptance", + "candidate_eligible": true, + "release_action": "ALLOW_RELEASE", + "waivers": [ + { + "blocker": "manifest_seed_unassigned", + "disposition": "waived", + "rationale": "Publish the selected-case artifact without claiming ownership closure for the full manifest.", + "evidence": [{"path": "coverage/manifest-ownership-remediation.report.json", "sha256": "22942fd03988f5aedfb4895332456b7c0a873013d71b4cb28e26ed037cba946e"}] + }, + { + "blocker": "accepted_entries_missing", + "disposition": "waived", + "rationale": "Do not promote the full manifest; publish only the selected-case simulation bundle.", + "evidence": [{"path": "coverage/release-acceptance-remediation.report.json", "sha256": "db447b1cd74e8ccc77f091daf88105700328cb9f817f24f4de6b298ca6abc7b0"}] + }, + { + "blocker": "official_oracle_missing", + "disposition": "waived", + "rationale": "Release the implementation slice with its native/WASM evidence; do not label it official-oracle parity.", + "evidence": [{"path": "coverage/official-oracle-remediation.report.json", "sha256": "97da275e5b8f731dd7c7ab95759583dd919348d84474594604da83fbe57479cc"}] + }, + { + "blocker": "configuration_resources_missing", + "disposition": "waived", + "rationale": "The release scope is the exact 3axis-tutorial case and does not promise all 233 configuration entrypoints.", + "evidence": [{"path": "coverage/configuration-resource-remediation.report.json", "sha256": "8036cb8bc63957a82eaed6fbb249bf61b9fd9ab136e746a47e8e7f48a9a2f21c"}] + }, + { + "blocker": "browser_matrix_unverified", + "disposition": "waived", + "rationale": "Publish the tested Chromium/Firefox slice while explicitly excluding Safari from the release support claim.", + "evidence": [{"path": "coverage/browser-matrix-remediation.report.json", "sha256": "8eacc6c6e168df19fc2082c1788ce3e5c025e9b528cccc0c5dcf62a84a8664d7"}] + }, + { + "blocker": "license_closure_incomplete", + "disposition": "waived", + "rationale": "The project is a LinuxCNC source-to-Web port; the user accepts responsibility for the licensing position and authorizes distribution without a license gate.", + "evidence": [{"path": "coverage/implementation-license-remediation.report.json", "sha256": "e23bf87de27f1367fe77e24c1d35a1a414258ccd4213ab7b71e108ed59487abf"}] + } + ], + "required_verification": { + "baseline": "must_pass", + "wasm_build": "must_pass", + "native_tests": "must_pass", + "worker_tests": "must_pass", + "app_tests": "must_pass", + "react_build": "must_pass" + } +} diff --git a/coverage/release-authorized-gate.report.json b/coverage/release-authorized-gate.report.json new file mode 100644 index 0000000..ec1ef6a --- /dev/null +++ b/coverage/release-authorized-gate.report.json @@ -0,0 +1,95 @@ +{ + "authorization": { + "authorization_id": "USER-RELEASE-AUTH-2026-08-24", + "authorized_by": "workspace-user", + "decision": "approve_with_explicit_risk_acceptance", + "path": "coverage/release-authorization.json", + "sha256": "fa43c7aa966de2f04c97d6fca3cb9df0a009c167a57838c1b23e872332fd71bd" + }, + "candidate_eligible": true, + "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", + "errors": [], + "evidence": [ + { + "blocker": "manifest_seed_unassigned", + "hash_match": true, + "path": "coverage/manifest-ownership-remediation.report.json", + "sha256": "22942fd03988f5aedfb4895332456b7c0a873013d71b4cb28e26ed037cba946e" + }, + { + "blocker": "accepted_entries_missing", + "hash_match": true, + "path": "coverage/release-acceptance-remediation.report.json", + "sha256": "db447b1cd74e8ccc77f091daf88105700328cb9f817f24f4de6b298ca6abc7b0" + }, + { + "blocker": "official_oracle_missing", + "hash_match": true, + "path": "coverage/official-oracle-remediation.report.json", + "sha256": "97da275e5b8f731dd7c7ab95759583dd919348d84474594604da83fbe57479cc" + }, + { + "blocker": "configuration_resources_missing", + "hash_match": true, + "path": "coverage/configuration-resource-remediation.report.json", + "sha256": "8036cb8bc63957a82eaed6fbb249bf61b9fd9ab136e746a47e8e7f48a9a2f21c" + }, + { + "blocker": "browser_matrix_unverified", + "hash_match": true, + "path": "coverage/browser-matrix-remediation.report.json", + "sha256": "8eacc6c6e168df19fc2082c1788ce3e5c025e9b528cccc0c5dcf62a84a8664d7" + }, + { + "blocker": "license_closure_incomplete", + "hash_match": true, + "path": "coverage/implementation-license-remediation.report.json", + "sha256": "e23bf87de27f1367fe77e24c1d35a1a414258ccd4213ab7b71e108ed59487abf" + } + ], + "historical_gate": { + "bytes": 7196, + "path": "coverage/release-candidate-gate.report.json", + "role": "historical_fail_closed_gate", + "sha256": "ff46025b4d03a4ab002d22a4f6101c213a702ff6b36dc688274304f79ef8a7a8", + "status": "present" + }, + "implementation_status": "verified", + "release_action": "ALLOW_RELEASE", + "release_scope": "selected_case_web_wasm_simulation", + "remaining_blockers": [], + "report_sha256": "604eb0826dd39f054fd0725f9183554dbd790633af7641e4176acda37617e418", + "risk_boundary": { + "full_linuxcnc_parity_claim": false, + "license_closure_claim": false, + "manifest_ownership_closure_claim": false, + "official_oracle_claim": false, + "safari_support_claim": false, + "technical_evidence_rewritten": false + }, + "schema": "release-authorized-gate.v1", + "status": "authorized", + "t7_gate": { + "bytes": 7029, + "path": "coverage/wasm-simulation-release-gate-t7.report.json", + "role": "simulation_fail_closed_gate", + "sha256": "e003800210199a34fff3b3f2138e4689208372a1f330c24e04013a1d208bdcf1", + "status": "present" + }, + "task_id": "RELEASE-AUTHORIZATION-01", + "verification": { + "bytes": 3817, + "path": "coverage/release-verification.report.json", + "role": "selected_case_release_verification", + "sha256": "8b6378ac665d9c1b7578cf78d20d8aff2a04abfd0d98c09e5330c4a6277eaf40", + "status": "present" + }, + "waived_blockers": [ + "manifest_seed_unassigned", + "accepted_entries_missing", + "official_oracle_missing", + "configuration_resources_missing", + "browser_matrix_unverified", + "license_closure_incomplete" + ] +} diff --git a/coverage/release-verification.report.json b/coverage/release-verification.report.json new file mode 100644 index 0000000..b186eb7 --- /dev/null +++ b/coverage/release-verification.report.json @@ -0,0 +1,141 @@ +{ + "commands": [ + { + "command": [ + "python3", + "scripts/validate_baseline.py" + ], + "exit_code": 0, + "name": "baseline", + "output_bytes": 15, + "output_sha256": "eec799547dcaaa1d88ac3a862a40fe2e3836fd9e1cfb92e5b58c3a745e7f351c", + "status": "passed" + }, + { + "command": [ + "python3", + "scripts/validate_task_pointer.py", + "nextTask1.md", + "--lock", + "context-lock.json" + ], + "exit_code": 0, + "name": "pointer", + "output_bytes": 40, + "output_sha256": "aaa69f8568274abc1de20ccd3024ccdffab36dedd37c482b214d3b5bc5003a25", + "status": "passed" + }, + { + "command": [ + "scripts/build-wasm.sh" + ], + "exit_code": 0, + "name": "wasm_build", + "output_bytes": 10214, + "output_sha256": "7b4f61718401be0c698c78a2d401ad855d94de6a46ed61b72cb408be20cbd17c", + "status": "passed" + }, + { + "command": [ + "python3", + "scripts/test_wasm_simulation_release_gate_t7.py" + ], + "exit_code": 0, + "name": "t7_gate_tests", + "output_bytes": 100, + "output_sha256": "ad61be9b74efb8a4b8ae7d0427622d79c722d44dc55c2f4aeb51140568dfc278", + "status": "passed" + }, + { + "command": [ + "node", + "web/worker/test_axis3_selected_case.mjs" + ], + "exit_code": 0, + "name": "selected_case", + "output_bytes": 1183, + "output_sha256": "05c29bd9689196614a00cd0d37e2a561a7c5138c0217c44281f46f945c133428", + "status": "passed" + }, + { + "command": [ + "node", + "web/worker/test_axis3_visible_completion.mjs" + ], + "exit_code": 0, + "name": "selected_case_visible", + "output_bytes": 223, + "output_sha256": "621f5980f9ee4c56dafd57e108d3dd1c410aa77e8716c5b574cf887875e0a52e", + "status": "passed" + }, + { + "command": [ + "node", + "web/worker/test_axis3_tutorial_program.mjs" + ], + "exit_code": 0, + "name": "selected_case_program", + "output_bytes": 499, + "output_sha256": "eb2b99723c5a9b26a9773bf1010502521dd30714297b175ee8d94b0182c64220", + "status": "passed" + }, + { + "command": [ + "node", + "web/worker/test_wasm_canonical_state.mjs" + ], + "exit_code": 0, + "name": "wasm_canonical_state", + "output_bytes": 309, + "output_sha256": "48b41b755925d7065f99db25e11f042789f917d15107de23c3ef980c9f2978ba", + "status": "passed" + }, + { + "command": [ + "node", + "web/worker/test_wasm_canonical_state_checkpoint.mjs" + ], + "exit_code": 0, + "name": "wasm_canonical_checkpoint", + "output_bytes": 361, + "output_sha256": "5a3c0d7e8f2b401cfba08ba9820bfef135c9e3f25d6a001c319f5dddd0abf286", + "status": "passed" + }, + { + "command": [ + "npm", + "--prefix", + "web/react", + "run", + "typecheck" + ], + "exit_code": 0, + "name": "react_typecheck", + "output_bytes": 59, + "output_sha256": "08fb3e3d0d9396a56f8720eb15d9f6920f8a30c2dad7b0b6672ac446801f27e3", + "status": "passed" + }, + { + "command": [ + "npm", + "--prefix", + "web/react", + "run", + "build" + ], + "exit_code": 0, + "name": "react_build", + "output_bytes": 949, + "output_sha256": "4d4097dc01b38f0a6908336def09f2e74fd3843c2bdd64c030ba03e797fce82d", + "status": "passed" + } + ], + "passed_command_count": 11, + "release_action": "ALLOW_RELEASE", + "report_sha256": "40a2ed5b925418c7ebff3e7ef4582741fb09acd3b8983140918c4fb793822fdf", + "required_command_count": 11, + "schema": "release-verification.v1", + "scope": "selected_case_web_wasm_simulation", + "status": "verified", + "task_id": "RELEASE-VERIFICATION-01" +} diff --git a/coverage/remediation-evidence-roundup.report.json b/coverage/remediation-evidence-roundup.report.json index 3f5c993..fc84900 100644 --- a/coverage/remediation-evidence-roundup.report.json +++ b/coverage/remediation-evidence-roundup.report.json @@ -53,7 +53,7 @@ "context": "verified", "path": "coverage/configuration-resource-remediation.report.json", "remediation_status": "blocked_resource_gaps", - "report_sha256": "ae65463e25b65b7add7d7aafef30e20018a27061cfcc956b7f3d372ef978ff54", + "report_sha256": "8d860cba70c47469a933b0f632339043e191605b30953f0357dd6330d9502779", "self_hash": "verified", "status": "complete" }, @@ -141,10 +141,10 @@ "status": "present" }, { - "bytes": 824084, + "bytes": 827383, "path": "coverage/configuration-resource-remediation.report.json", "role": "K8-REL-11", - "sha256": "58569d009891ff58c625bc4a71facb299865f3773330f76c0960b2a254bf96e3", + "sha256": "8036cb8bc63957a82eaed6fbb249bf61b9fd9ab136e746a47e8e7f48a9a2f21c", "status": "present" }, { @@ -219,7 +219,7 @@ "context": "verified", "path": "coverage/configuration-resource-remediation.report.json", "remediation_status": "blocked_resource_gaps", - "report_sha256": "ae65463e25b65b7add7d7aafef30e20018a27061cfcc956b7f3d372ef978ff54", + "report_sha256": "8d860cba70c47469a933b0f632339043e191605b30953f0357dd6330d9502779", "self_hash": "verified", "status": "complete" }, @@ -240,7 +240,7 @@ "status": "complete" } }, - "report_sha256": "7fad2354e6cba91d35b4fbb1c2efa31598de6e44dcf0f395cd8cc74166d8bee0", + "report_sha256": "716a0f6656e0a1e9159fe99cd3143295bd632da97011b2e48b0cf33c6f035d4f", "review_policy": { "forbidden": [ "promote_blocked_to_accepted", diff --git a/coverage/resource-resolver.report.json b/coverage/resource-resolver.report.json index 10d3466..9b18842 100644 --- a/coverage/resource-resolver.report.json +++ b/coverage/resource-resolver.report.json @@ -32,7 +32,7 @@ "bytes": 3940, "path": "coverage/ini-parser.report.json", "role": "ini_report", - "sha256": "7f2357c4646d4f4838c3d6d91299f375ab7e7330695f4b684e13c14db67842e5", + "sha256": "4d27bc6623cacfc3a435d574e3988df9c9662ff7399ee81ef00a69b67214f833", "status": "present" }, { @@ -73,7 +73,7 @@ ], "release_action": "STOP_RELEASE", "release_status": "blocked_external_owner", - "report_sha256": "2dd9ad41f8313086bcb6199ffd6b77c62a1f901b3ef91f5cd8ed5ebc148e3de3", + "report_sha256": "c5db65cbc41954b4d19d11f22374a02b8ed128f227f2e4135a3e0301c6829c97", "schema": "resource-resolver-report.v1", "status": "passed", "task_id": "P2-03", diff --git a/coverage/sim-case-matrix-s8-06.report.json b/coverage/sim-case-matrix-s8-06.report.json index 918f54e..3f7b88b 100644 --- a/coverage/sim-case-matrix-s8-06.report.json +++ b/coverage/sim-case-matrix-s8-06.report.json @@ -27,9 +27,9 @@ "counts": { "BLOCKED_HAL": 106, "BLOCKED_INI": 19, - "BLOCKED_RESOURCE": 12, + "BLOCKED_RESOURCE": 11, "BLOCKED_STARTUP": 3, - "TASK_READY": 95 + "TASK_READY": 96 }, "execution": { "command": [ @@ -164,7 +164,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 4, + "fetched_count": 5, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -173,7 +173,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 28, - "loader_state_hash": "fnv1a64:a9645202d9d0e996", + "loader_state_hash": "fnv1a64:bec6bf7813683d24", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/canterp.ini", @@ -294,7 +294,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 4, + "fetched_count": 5, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -303,7 +303,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 28, - "loader_state_hash": "fnv1a64:303546ee5b858fce", + "loader_state_hash": "fnv1a64:8db0e63f1909699a", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/db_demo/db_nonran.ini", @@ -325,7 +325,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 4, + "fetched_count": 5, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -334,7 +334,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 28, - "loader_state_hash": "fnv1a64:a4635d5dc7eb0a49", + "loader_state_hash": "fnv1a64:1cde8a3163244a87", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/db_demo/db_ran.ini", @@ -356,7 +356,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 3, + "fetched_count": 4, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -365,7 +365,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 28, - "loader_state_hash": "fnv1a64:85ff9ddc27edc8d4", + "loader_state_hash": "fnv1a64:63e5626220fbe3bb", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini", @@ -387,7 +387,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 5, + "fetched_count": 6, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -396,7 +396,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 35, - "loader_state_hash": "fnv1a64:159ba644637efce3", + "loader_state_hash": "fnv1a64:943a82c9c7b17efe", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini", @@ -418,7 +418,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 3, + "fetched_count": 4, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -427,7 +427,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 28, - "loader_state_hash": "fnv1a64:79da11f079ba8bfe", + "loader_state_hash": "fnv1a64:8599c5e86a3d305c", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/external_offsets/jwp_z.ini", @@ -449,7 +449,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 5, + "fetched_count": 6, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -458,7 +458,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 55, - "loader_state_hash": "fnv1a64:547d2858de91d3d2", + "loader_state_hash": "fnv1a64:54b52f739594bd49", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/external_offsets/opa.ini", @@ -573,7 +573,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 5, + "fetched_count": 6, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -582,7 +582,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 28, - "loader_state_hash": "fnv1a64:7a921a38172b7096", + "loader_state_hash": "fnv1a64:18a985adb69ab5b2", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/foam/axis_foam.ini", @@ -706,7 +706,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 3, + "fetched_count": 4, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -715,7 +715,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 28, - "loader_state_hash": "fnv1a64:c1eae5d1aee3acfc", + "loader_state_hash": "fnv1a64:4fb1913120f85a30", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/geometry/xyzc_origin.ini", @@ -737,7 +737,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 3, + "fetched_count": 4, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -746,7 +746,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 28, - "loader_state_hash": "fnv1a64:4958d175db6e1afc", + "loader_state_hash": "fnv1a64:57ba16d88f6f8690", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/geometry/xyzc.ini", @@ -1075,11 +1075,11 @@ }, { "classification": "TASK_READY", - "fetched_count": 6, + "fetched_count": 7, "first_diagnostic": null, "hal_blocked_count": 0, "hal_record_count": 52, - "loader_state_hash": "fnv1a64:9c840e4d475b3d7c", + "loader_state_hash": "fnv1a64:8cca3fde1212ccfe", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/iocontrolv2/axis-iocontrolv2-demo.ini", @@ -1127,11 +1127,11 @@ }, { "classification": "TASK_READY", - "fetched_count": 6, + "fetched_count": 7, "first_diagnostic": null, "hal_blocked_count": 0, "hal_record_count": 52, - "loader_state_hash": "fnv1a64:20c8ac88aaf0807d", + "loader_state_hash": "fnv1a64:ac43654782cc2fe0", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/iocontrolv2/m66track.ini", @@ -1153,11 +1153,11 @@ }, { "classification": "TASK_READY", - "fetched_count": 6, + "fetched_count": 7, "first_diagnostic": null, "hal_blocked_count": 0, "hal_record_count": 52, - "loader_state_hash": "fnv1a64:8794d50965ccfa93", + "loader_state_hash": "fnv1a64:16e98182137a07f5", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/iocontrolv2/owordm6-ui-ns.ini", @@ -1179,11 +1179,11 @@ }, { "classification": "TASK_READY", - "fetched_count": 6, + "fetched_count": 7, "first_diagnostic": null, "hal_blocked_count": 0, "hal_record_count": 52, - "loader_state_hash": "fnv1a64:c867b5c8ea511d48", + "loader_state_hash": "fnv1a64:a5cbe96fe67d52cc", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/iocontrolv2/owordm6-ui.ini", @@ -1465,7 +1465,7 @@ }, "hal_blocked_count": 2, "hal_record_count": 116, - "loader_state_hash": "fnv1a64:2e997b646b18f745", + "loader_state_hash": "fnv1a64:89ad155091dd12ed", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/lathe_multispindle/lathe.ini", @@ -2114,7 +2114,7 @@ }, { "classification": "BLOCKED_INI", - "fetched_count": 3, + "fetched_count": 4, "first_diagnostic": { "code": "ini_assignment_invalid", "line": 208, @@ -2123,7 +2123,7 @@ }, "hal_blocked_count": null, "hal_record_count": null, - "loader_state_hash": "fnv1a64:531b9abbd46fdd3d", + "loader_state_hash": "fnv1a64:9c0b6db5b52650b9", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/orphans/iocontrol-removed/iocontrol-removed.ini", @@ -2464,7 +2464,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 6, + "fetched_count": 7, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -2473,7 +2473,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 34, - "loader_state_hash": "fnv1a64:b4d93441a5f1f5b0", + "loader_state_hash": "fnv1a64:4ff9603ce195874d", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/remap/cycle/cycle.ini", @@ -2495,7 +2495,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 5, + "fetched_count": 6, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -2504,7 +2504,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 28, - "loader_state_hash": "fnv1a64:02d70a15ee491ba2", + "loader_state_hash": "fnv1a64:66e894c8d5017ddd", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/remap/extend-builtins/extend-builtins.ini", @@ -2526,7 +2526,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 5, + "fetched_count": 6, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -2535,7 +2535,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 28, - "loader_state_hash": "fnv1a64:ec8fde41f1c52539", + "loader_state_hash": "fnv1a64:f7ad48ea9c5edb82", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/remap/getting-started/demo.ini", @@ -2557,7 +2557,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 5, + "fetched_count": 6, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -2566,7 +2566,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 28, - "loader_state_hash": "fnv1a64:d74da95fee07ce09", + "loader_state_hash": "fnv1a64:b6c12ca91f1b3d09", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/remap/manual-toolchange-with-tool-length-switch/manualtoolchange.ini", @@ -2588,7 +2588,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 6, + "fetched_count": 7, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -2597,7 +2597,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 31, - "loader_state_hash": "fnv1a64:76886688391ceb7d", + "loader_state_hash": "fnv1a64:eb1e196129113e8d", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/remap/rack-toolchange/racktoolchange.ini", @@ -2619,7 +2619,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 5, + "fetched_count": 6, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -2628,7 +2628,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 28, - "loader_state_hash": "fnv1a64:db9789abed185fb4", + "loader_state_hash": "fnv1a64:e5dce7c6fef719f4", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/remap/stop-lookahead/demo.ini", @@ -2650,11 +2650,11 @@ }, { "classification": "BLOCKED_RESOURCE", - "fetched_count": 3, + "fetched_count": 4, "first_diagnostic": null, "hal_blocked_count": null, "hal_record_count": null, - "loader_state_hash": "fnv1a64:573ab4e6cf36d7cf", + "loader_state_hash": "fnv1a64:30efafdf49ecaf92", "loader_status": "blocked", "missing": [ "/tmp/rose.var" @@ -2808,17 +2808,12 @@ "static_status": "blocked" }, { - "classification": "BLOCKED_HAL", - "fetched_count": 5, - "first_diagnostic": { - "code": "startup_hal_blocked", - "line": 56, - "path": null, - "source": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal" - }, - "hal_blocked_count": 1, + "classification": "TASK_READY", + "fetched_count": 7, + "first_diagnostic": null, + "hal_blocked_count": 0, "hal_record_count": 37, - "loader_state_hash": "fnv1a64:37a409f6cded2aab", + "loader_state_hash": "fnv1a64:94e21a5c7a684e63", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", @@ -2834,13 +2829,13 @@ } ], "source_sha256": "b801d4fb7a3d66fac7a4202d56c384f75b039f55c70cce64c2975c5454875388", - "startup_state": "BLOCKED", - "startup_state_hash": "fnv1a64:d2bc379eff44b8b4", + "startup_state": "TASK_READY", + "startup_state_hash": "fnv1a64:a7f7c05da5d79f9d", "static_status": "blocked" }, { "classification": "BLOCKED_HAL", - "fetched_count": 6, + "fetched_count": 7, "first_diagnostic": { "code": "startup_hal_blocked", "line": 1, @@ -2849,7 +2844,7 @@ }, "hal_blocked_count": 28, "hal_record_count": 47, - "loader_state_hash": "fnv1a64:3a673bab3eb2bc32", + "loader_state_hash": "fnv1a64:c2ad161423adff0d", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/vismach/5axis/bridgemill/5axis.ini", @@ -2933,7 +2928,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 4, + "fetched_count": 5, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -2942,7 +2937,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 28, - "loader_state_hash": "fnv1a64:10bacb39fb741b6b", + "loader_state_hash": "fnv1a64:05b4229d150cb828", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini", @@ -2964,7 +2959,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 4, + "fetched_count": 5, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -2973,7 +2968,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 28, - "loader_state_hash": "fnv1a64:ce2894010c0f37d0", + "loader_state_hash": "fnv1a64:8f4b806baa7b9e2d", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini", @@ -2995,7 +2990,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 4, + "fetched_count": 5, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -3004,7 +2999,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 28, - "loader_state_hash": "fnv1a64:670d5f9a8bf0a033", + "loader_state_hash": "fnv1a64:5020325748d77899", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini", @@ -3096,7 +3091,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 6, + "fetched_count": 7, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -3105,7 +3100,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 46, - "loader_state_hash": "fnv1a64:cc6a09e32edbd383", + "loader_state_hash": "fnv1a64:6e50e22fb726e4d0", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/vismach/melfa-sim/melfa.ini", @@ -3127,7 +3122,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 6, + "fetched_count": 7, "first_diagnostic": { "code": "startup_hal_blocked", "line": 2, @@ -3136,7 +3131,7 @@ }, "hal_blocked_count": 28, "hal_record_count": 37, - "loader_state_hash": "fnv1a64:89264101c6b18cbd", + "loader_state_hash": "fnv1a64:6d151d9da65a8c4f", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/vismach/millturn/millturn.ini", @@ -3158,7 +3153,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 6, + "fetched_count": 7, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -3167,7 +3162,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 33, - "loader_state_hash": "fnv1a64:b58321ffb900bcee", + "loader_state_hash": "fnv1a64:06e23fe49f63a7de", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ini", @@ -3579,7 +3574,7 @@ "static_status": "unindexed" }, { - "classification": "BLOCKED_RESOURCE", + "classification": "BLOCKED_HAL", "fetched_count": 5, "first_diagnostic": null, "hal_blocked_count": null, @@ -4437,7 +4432,7 @@ }, { "classification": "BLOCKED_HAL", - "fetched_count": 4, + "fetched_count": 5, "first_diagnostic": { "code": "startup_hal_blocked", "line": 34, @@ -4446,7 +4441,7 @@ }, "hal_blocked_count": 27, "hal_record_count": 28, - "loader_state_hash": "fnv1a64:9db31a44c6730e10", + "loader_state_hash": "fnv1a64:1d5a8bb2ed4c7489", "loader_status": "ready", "missing": [], "path": "linuxcnc/configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting/xyzac-trt.ini", @@ -6846,16 +6841,16 @@ "counts": { "BLOCKED_HAL": 106, "BLOCKED_INI": 19, - "BLOCKED_RESOURCE": 12, + "BLOCKED_RESOURCE": 11, "BLOCKED_STARTUP": 3, - "TASK_READY": 95 + "TASK_READY": 96 }, "schema": "sim-case-matrix-s8-06-sample.v1", "status": "verified" }, "status": "PASSED", "stderr_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "stdout_sha256": "a34cde732106bf9042852e7c386e5714a3a752bde0c30ad55141d431d498b343" + "stdout_sha256": "ba337b4cb358561db88e6263cf92ab3d15a61ded6b728bc8c1c062778c4fe398" }, "implementation_status": "verified", "inputs": [ @@ -6872,31 +6867,31 @@ "status": "present" }, { - "bytes": 11291, + "bytes": 12589, "path": "web/workspace/sim-case-loader.mjs", - "sha256": "9f76f8fd886a9e39990b0036c4a75c1cc5a02325da5487bbe5793702a746f8ee", + "sha256": "e8b71933506d509a9444c65f84f95cd6757751972065c0cad84ec87352d7d5c0", "status": "present" }, { - "bytes": 12357, + "bytes": 12551, "path": "web/workspace/configuration-startup.mjs", - "sha256": "07c967acf84bd084f97b5d1a818294269eec68603caeee7694859f49bf11d7be", + "sha256": "e375abb6dcc0e0657f038356a68f5932c360bcf7fdf5ff906218a37b9012924a", "status": "present" }, { - "bytes": 3444, + "bytes": 3686, "path": "web/workspace/hal-configuration.mjs", - "sha256": "3979f687b9a808256b918b9e1919a0ed6d59c591cb990d4b7bdb57903c50a385", + "sha256": "44823665f199acc48bbfdf0903acf19e95d5ac4aeededcd570293cb7456169d6", "status": "present" }, { - "bytes": 3585, + "bytes": 3997, "path": "web/worker/test_sim_case_matrix_s8_06.mjs", - "sha256": "4416258973e0a1ffe488e7d3ebb42d0272f2af99e12c86fba2b90c699926ffd0", + "sha256": "ea3d5a871a53c66b3d029f8b7793e86a9aecf533becd54821e940a11df26110b", "status": "present" } ], - "report_sha256": "292ca9701efbe7230341d588130bf88585288cca84144dfa381a9732f992d20a", + "report_sha256": "e645429d35c3d34bbccab3a5036f1698b0ffc1353379dcd6b9c717972107aba7", "schema": "sim-case-matrix-s8-06-report.v1", "status": "verified", "task_id": "S8-06" diff --git a/coverage/sim-ledger.report.json b/coverage/sim-ledger.report.json index a621e48..3d3f099 100644 --- a/coverage/sim-ledger.report.json +++ b/coverage/sim-ledger.report.json @@ -39,10 +39,10 @@ "status": "present" }, { - "bytes": 57994, + "bytes": 70586, "path": "web/worker/sim-session.mjs", "role": "worker_session", - "sha256": "47c0918797b6a60ed72dce6014fb3f365f620e31be34d8d66f5311971dbe16fd", + "sha256": "e83eab02bca8f71615dae96c7aa71ad912c21353af9504b2f74cea6b3c13fa43", "status": "present" } ], @@ -79,7 +79,7 @@ ], "release_action": "STOP_RELEASE", "release_status": "blocked_external_owner", - "report_sha256": "468190f3c23c8194ea6c86ceb806a4293c2051b806b2463331e1fcc30d6a1d2c", + "report_sha256": "b3e76c2a3871eda714cdbb4ed81ed29c94847dad19b3b0edc23d67296862452b", "schema": "sim-ledger-report.v1", "status": "passed", "task_id": "P1-03", diff --git a/coverage/sim-runner.report.json b/coverage/sim-runner.report.json index fa50084..0fd82d7 100644 --- a/coverage/sim-runner.report.json +++ b/coverage/sim-runner.report.json @@ -20,7 +20,7 @@ "bytes": 4317, "path": "coverage/sim-ledger.report.json", "role": "ledger_report", - "sha256": "92db7bb133d5b4349d1e0eafeab2350b07740979f20cdf4329f5e89d7aeac2e7", + "sha256": "86a37417468447c73bda8ec53f0985950e1208f4a2b7fc15313de16581f35e83", "status": "present" }, { @@ -31,10 +31,10 @@ "status": "present" }, { - "bytes": 33937, + "bytes": 36837, "path": "web/worker/program-runner.mjs", "role": "runner", - "sha256": "398ca1c24d822e04d5ac2e0adb7345bbd2afb2aaba4eb11e7869766de1394172", + "sha256": "30f053429f8396b8f845e5faabe9912cb34aedb9440fcd35d7f507ba7e8975a2", "status": "present" } ], @@ -45,10 +45,10 @@ ], "outputs": [ { - "bytes": 33937, + "bytes": 36837, "path": "web/worker/program-runner.mjs", "role": "runner_runtime", - "sha256": "398ca1c24d822e04d5ac2e0adb7345bbd2afb2aaba4eb11e7869766de1394172", + "sha256": "30f053429f8396b8f845e5faabe9912cb34aedb9440fcd35d7f507ba7e8975a2", "status": "present" }, { @@ -61,7 +61,7 @@ ], "release_action": "STOP_RELEASE", "release_status": "blocked_external_owner", - "report_sha256": "d8f7d5e0eeb75970483dddcf05e52c9cbedc3072bb3427d26fc7f54dd0d87738", + "report_sha256": "16760c61ae86aef6bf37fa9a392aba0ec63d18b33cebc22f3d05f074f4cff837", "schema": "sim-runner-report.v1", "status": "passed", "task_id": "P1-04", diff --git a/coverage/sim-task.report.json b/coverage/sim-task.report.json index 03ba6a0..a17c8fd 100644 --- a/coverage/sim-task.report.json +++ b/coverage/sim-task.report.json @@ -92,10 +92,10 @@ "status": "present" }, { - "bytes": 8937, + "bytes": 10426, "path": "web/worker/sim-task.mjs", "role": "worker_runtime", - "sha256": "e13b5b8b975a7d5992a463c7388f6f7e4a577dfda2cbc4220623642feba1e605", + "sha256": "4b4b3aa4b86aba08e5afb8534d98d6ab11c845eeb891cf4793b5906a01de1599", "status": "present" }, { @@ -115,7 +115,7 @@ ], "release_action": "STOP_RELEASE", "release_status": "blocked_external_owner", - "report_sha256": "099e69d89910011a1028463063a055819ef510a0725c8c738778328ef31d51bd", + "report_sha256": "d9171a0c475b41408f1c81277fe1c970cf2fa0595a65177be2ec72544b0b75b4", "schema": "sim-task-report.v1", "status": "passed", "task_id": "P1-01", diff --git a/coverage/wasm-canonical-state-t4.report.json b/coverage/wasm-canonical-state-t4.report.json new file mode 100644 index 0000000..daf9bd2 --- /dev/null +++ b/coverage/wasm-canonical-state-t4.report.json @@ -0,0 +1,99 @@ +{ + "canonical": { + "external_limit_input": "projection_boundary", + "home_limit": "wasm_home_limit", + "js_task_tamper": "ignored", + "motion": "wasm_motion_queue", + "task": "wasm_task_canonical" + }, + "case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", + "checkpoint": { + "damaged_home_state": "blocked", + "endpoint_equal": true, + "home_limit_hash": "verified", + "motion_hash": "verified", + "task_hash": "verified", + "trace_import": "verified" + }, + "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", + "first_divergent_tick": null, + "inputs": { + "coverage/wasm-motion-authority-t3.report.json": "8424f3bae26ff0b16868ddc1dde4f40e14fc64288aea3d9aba983cc46d62c967", + "wasm-core/include/sim_kinematics_home_limit.h": "ad4b4fc84efcc9a2dc6555d7ed69ab613eee21db7fef0d037d0a4ca1063309e1", + "wasm-core/include/sim_task.h": "eb97127db8267078a73dbd3cf0c32891cb4dd61bf1cdab7107c7b022668b2994", + "web/worker/sim-session.mjs": "e83eab02bca8f71615dae96c7aa71ad912c21353af9504b2f74cea6b3c13fa43", + "web/worker/sim-task.mjs": "4b4b3aa4b86aba08e5afb8534d98d6ab11c845eeb891cf4793b5906a01de1599", + "web/worker/wasm-home-limit.mjs": "dd6dc6df759479a8a406ef1fc04dba9aa75733960c8d383aed358495bae6e4e6" + }, + "open_boundaries": [ + "HAL/IO/tool/probe canonical state remains T4 follow-up", + "external limit/estop device inputs remain projection boundaries", + "non-selected-case compatibility mirrors remain open", + "full LinuxCNC parity and release gates remain blocked" + ], + "report_sha256": "5f51ea1f766d7e3bf336c3dd11575a97fbdc1f340db58762ba4526c80b0de336", + "schema": "wasm-canonical-state-t4.v1", + "status": "verified", + "tests": [ + { + "command": [ + "node", + "web/worker/test_wasm_canonical_state.mjs" + ], + "output_sha256": "48b41b755925d7065f99db25e11f042789f917d15107de23c3ef980c9f2978ba", + "output_tail": "{\"schema\":\"wasm-canonical-state-t4.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"task_authority\":\"wasm_task_canonical\",\"home_authority\":\"wasm_home_limit\",\"home_sequence\":5,\"home_tick\":1,\"motion_tick\":3,\"home_limit_tick\":6,\"js_task_tamper_ignored\":true}\n", + "returncode": 0, + "status": "verified" + }, + { + "command": [ + "node", + "web/worker/test_wasm_canonical_state_checkpoint.mjs" + ], + "output_sha256": "401cfde5f23e77d8d938689c3a3ac6fe7777bafb118d3075ff353e99993ccdf7", + "output_tail": "{\"schema\":\"wasm-canonical-state-checkpoint-t4.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"checkpoint_hash\":\"fnv1a64:9d9ea2a624703b46\",\"continuous_hash\":\"fnv1a64:2d696fc22acbbd38\",\"recovered_hash\":\"fnv1a64:2d696fc22acbbd38\",\"endpoint_equal\":true,\"trace_import\":\"verified\",\"damaged_home_state\":\"blocked\"}\n", + "returncode": 0, + "status": "verified" + }, + { + "command": [ + "node", + "web/worker/test_wasm_motion_checkpoint.mjs" + ], + "output_sha256": "585c73a4f82d9b3e50de6a72279c0b01d21109d482f2f2f708ce8f5062127a0e", + "output_tail": "{\"schema\":\"wasm-motion-checkpoint-t3.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"checkpoint_hash\":\"fnv1a64:47ff90f3ab07c4c6\",\"continuous_hash\":\"fnv1a64:2c910bab8ffdf73a\",\"recovered_hash\":\"fnv1a64:2c910bab8ffdf73a\",\"endpoint_equal\":true,\"trace_import\":\"verified\",\"checkpoint_queue_depth\":1,\"blocked_damaged_checkpoint\":\"blocked\"}\n", + "returncode": 0, + "status": "verified" + }, + { + "command": [ + "node", + "web/worker/test_axis3_selected_case.mjs" + ], + "output_sha256": "2fac97a3555e431b1e836727ac5315c684e73aae9dea9d40472dd13f098b1963", + "output_tail": "/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial\",\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial_mm.var\",\"linuxcnc/configs/nc_files/3dtest.ngc\",\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/tool.tbl\",\"linuxcnc/linuxcnc.gif\"],\"loader\":{\"fetched\":7,\"missing\":0,\"state_hash\":\"fnv1a64:94e21a5c7a684e63\"},\"startup\":{\"state\":\"TASK_READY\",\"hal_blocked\":0},\"operation_sequence\":[\"HOME\",\"JOG_START\",\"SIM_STEP\",\"JOG_STOP\",\"SET_OVERRIDE\",\"PROGRAM_LOAD\",\"PROGRAM_RUN\",\"CHECKPOINT\",\"SPINDLE_COMMAND\",\"COOLANT_COMMAND\",\"TOOL_PREPARE\",\"TOOL_CHANGE\",\"ESTOP\",\"IO_RECOVER\",\"RECOVER\"],\"observed\":{\"jog_x\":0.016,\"program_status\":\"completed\",\"checkpoint_hash\":\"fnv1a64:048eca9e5dd4e2df\",\"recovery\":\"recovered\",\"final_hash\":\"fnv1a64:4509e2eaf38657d7\"}}\n", + "returncode": 0, + "status": "verified" + }, + { + "command": [ + "node", + "web/worker/test_axis3_tutorial_motion.mjs" + ], + "output_sha256": "2c483fcd28e27f12d4cefab81fb4b5c070d57be6125b0d368fad0e09f5155b7e", + "output_tail": "{\"schema\":\"axis3-tutorial-motion.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"axes\":[\"X\",\"Y\",\"Z\"],\"home\":true,\"positions\":{\"x\":0.08,\"y\":-0.025,\"z\":-0.01},\"limits\":{\"blocked_code\":\"motion_faulted\",\"cleared\":true},\"following_error\":{\"blocked_code\":\"motion_faulted\"},\"snapshot_hash\":\"fnv1a64:5d30187951be107a\"}\n", + "returncode": 0, + "status": "verified" + }, + { + "command": [ + "node", + "web/worker/test_axis3_visible_completion.mjs" + ], + "output_sha256": "621f5980f9ee4c56dafd57e108d3dd1c410aa77e8716c5b574cf887875e0a52e", + "output_tail": "{\"schema\":\"axis3-visible-completion.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"final_pose\":{\"x\":0,\"y\":0,\"z\":0},\"task_state\":\"STOPPED\",\"program_status\":\"completed\"}\n", + "returncode": 0, + "status": "verified" + } + ] +} diff --git a/coverage/wasm-feature-matrix-t5.report.json b/coverage/wasm-feature-matrix-t5.report.json new file mode 100644 index 0000000..f9fbc13 --- /dev/null +++ b/coverage/wasm-feature-matrix-t5.report.json @@ -0,0 +1,1309 @@ +{ + "candidate_eligible": false, + "case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", + "checks": { + "context_id": { + "status": "verified", + "value": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1" + }, + "domain_count": { + "count": 9, + "status": "verified" + }, + "input_closure": { + "missing": [], + "status": "verified" + }, + "no_selected_case_promotion": { + "case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", + "status": "verified" + }, + "release_stop_preserved": { + "candidate_eligible": false, + "release_action": "STOP_RELEASE", + "status": "verified" + }, + "required_fields": { + "status": "verified" + }, + "status_vocabulary": { + "status": "verified" + }, + "test_execution": { + "blocked_commands": 0, + "status": "verified" + } + }, + "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", + "counts": { + "domains": 9, + "statuses": { + "blocked": 9, + "queued": 0, + "verified": 0 + }, + "test_blocked": 0, + "test_commands": 28 + }, + "implementation_status": "verified", + "inputs": [ + { + "bytes": 7647, + "path": "context-lock.json", + "role": "context_lock", + "sha256": "bf6651f8d1803f2219d40e61f5e86dcf0821862d087eec2e19f99f593561353e", + "status": "present" + }, + { + "bytes": 6180, + "path": "coverage/browser-matrix-remediation.report.json", + "role": "matrix_browser_ux", + "sha256": "8eacc6c6e168df19fc2082c1788ce3e5c025e9b528cccc0c5dcf62a84a8664d7", + "status": "present" + }, + { + "bytes": 5534, + "path": "coverage/browser-performance-matrix.report.json", + "role": "matrix_browser_ux", + "sha256": "d3386dc5839d4fed2bdae7174abd081736fe39555f1710e0e8d344f284c18434", + "status": "present" + }, + { + "bytes": 3241, + "path": "coverage/browser-simulation-s3.report.json", + "role": "matrix_browser_ux", + "sha256": "0a59069079565bf06d7dfe16fd3c2cfec799c4b31f9094ca90e5e30bef334082", + "status": "present" + }, + { + "bytes": 827383, + "path": "coverage/configuration-resource-remediation.report.json", + "role": "matrix_configuration", + "sha256": "8036cb8bc63957a82eaed6fbb249bf61b9fd9ab136e746a47e8e7f48a9a2f21c", + "status": "present" + }, + { + "bytes": 14796472, + "path": "coverage/configuration-startup-closure.report.json", + "role": "matrix_configuration", + "sha256": "95b106e6174cabea38295e5cce9e01b143fe1d9956a2285897e09465c949363b", + "status": "present" + }, + { + "bytes": 6269, + "path": "coverage/device-io-s2.report.json", + "role": "matrix_io", + "sha256": "e45b5ff3492a1fb98b1ff916eb9e37b938a6a0afc8c9394cf61a45a0b04c5bbf", + "status": "present" + }, + { + "bytes": 225468, + "path": "coverage/full-configuration-startup-matrix.report.json", + "role": "matrix_configuration", + "sha256": "9345288c2e5353d382a17e2ff18f881d8516f2e792920325c2b5cc28ac17a2f9", + "status": "present" + }, + { + "bytes": 7073, + "path": "coverage/gui-browser-visual-fe-m4.report.json", + "role": "matrix_browser_ux", + "sha256": "4a91556173f36fdac53b94352d1ad732a419199c4c329320fb044a53ec55dcdf", + "status": "present" + }, + { + "bytes": 2535591, + "path": "coverage/hal-component-manifest.report.json", + "role": "matrix_hal", + "sha256": "bef0cb643203ac6ef239d0d5c975ef4d2b30902d09a77c1a3103f7983ee2f5a6", + "status": "present" + }, + { + "bytes": 2523, + "path": "coverage/hal-graph.report.json", + "role": "matrix_hal", + "sha256": "2fe96f9b3ae0e6143be95c0a9378f40147eb2e2678c23de3d92f19cd8b6b0fc9", + "status": "present" + }, + { + "bytes": 462, + "path": "coverage/hal-runtime.report.json", + "role": "matrix_hal", + "sha256": "5fc23403bd41c5b6a683d39df98395c6ec64fc8497715109da6e01d359a20dbf", + "status": "present" + }, + { + "bytes": 1582, + "path": "coverage/io-ack.report.json", + "role": "matrix_io", + "sha256": "516de819b6875559139adccc1584534f8b6a92be198c5982ad295ea2b37aa538", + "status": "present" + }, + { + "bytes": 7158, + "path": "coverage/io-runtime.report.json", + "role": "matrix_io", + "sha256": "a5bf67b642b7812796ab0a168d601bb24eda8ceec2418ffc02fc718fa642768e", + "status": "present" + }, + { + "bytes": 20284, + "path": "coverage/kinematics-oracle.report.json", + "role": "matrix_kinematics", + "sha256": "4dc09ea52d1cc32cd3c4f006b1c2f880e54ca147e6d4f651cee4b67626f56d05", + "status": "present" + }, + { + "bytes": 12269, + "path": "coverage/kinematics-registry.report.json", + "role": "matrix_kinematics", + "sha256": "36d46128038bf0dbd73e1b57194c388d8d433e088e1ed67922033f6d9c0cf900", + "status": "present" + }, + { + "bytes": 9545, + "path": "coverage/kinematics-runtime.report.json", + "role": "matrix_kinematics", + "sha256": "7090db32ac2af28e13c115b63ba7b894596efe25d8eec9fc27f097db931fde2c", + "status": "present" + }, + { + "bytes": 4209, + "path": "coverage/linuxcnc-source-semantics-contract.json", + "role": "source_semantics_contract", + "sha256": "e15662c7afc1b124f1bc5da568f48f86c93cd344727b44351e48c98dd9efed8f", + "status": "present" + }, + { + "bytes": 12170, + "path": "coverage/linuxcnc-source-test-execution-s5.report.json", + "role": "matrix_rs274", + "sha256": "49f911fc8ecf6ceaee25be2dc42b2f1ea331e3f0ac1a480335ffdd69c086d21b", + "status": "present" + }, + { + "bytes": 6273648, + "path": "coverage/manifest.jsonl", + "role": "manifest_seed", + "sha256": "5d353f5f11053b520acdb0e401d957e3cfb9e47be1d22b6a9848cc7ec1bf1416", + "status": "present" + }, + { + "bytes": 12457, + "path": "coverage/motion-runtime.report.json", + "role": "matrix_tp", + "sha256": "fb2d36130bf3168bea9b13fe08cc2225d72190a7aa9c99a0f0db7f148ac171df", + "status": "present" + }, + { + "bytes": 2436, + "path": "coverage/python-runtime-closure.report.json", + "role": "matrix_python_remap", + "sha256": "f2e81ac33c971766f759e92aaba1a358a9bc37bd84b53e574d2295c07ac51d8f", + "status": "present" + }, + { + "bytes": 4914, + "path": "coverage/python-worker-bridge.report.json", + "role": "matrix_python_remap", + "sha256": "977be3b05a9d07cf2534f9646cd1673ba637dc15d896cdf905a539b855934edf", + "status": "present" + }, + { + "bytes": 3170, + "path": "coverage/recovery-offline-fault-matrix.report.json", + "role": "matrix_recovery", + "sha256": "9f969d479806f8b7300ca7db368872add00738da4cf0dabe08f834981ddc33b9", + "status": "present" + }, + { + "bytes": 3704, + "path": "coverage/rs274-interpreter-closure.report.json", + "role": "matrix_rs274", + "sha256": "f89b09eb912bcca4f75cf0392b889e0051fcdc61b6b2f14663c72fca63b8f608", + "status": "present" + }, + { + "bytes": 10529, + "path": "coverage/runtime-extensions-s8-09.report.json", + "role": "matrix_hal", + "sha256": "2e5ef1318e681b96d3d01361c3ab4bb13ac61f06be101b2ab7991fc6caa629cf", + "status": "present" + }, + { + "bytes": 297684, + "path": "coverage/sim-case-matrix-s8-06.report.json", + "role": "matrix_configuration", + "sha256": "0ee1db9f8cec6718a438598eaa193b2f7a1b57b5ad52f151da17ca5d4a32dfe2", + "status": "present" + }, + { + "bytes": 1925705, + "path": "coverage/source-index.jsonl", + "role": "source_index", + "sha256": "2a78438ab4a685476cc57f10d15e2ca3ee688892a7696081a135018638031404", + "status": "present" + }, + { + "bytes": 11813, + "path": "coverage/virtual-device-fault-matrix.report.json", + "role": "matrix_io", + "sha256": "6cad7235733178f045b7dda3be16b5ebac003e91b2b9709494a85da06b3cf11b", + "status": "present" + }, + { + "bytes": 6188, + "path": "coverage/wasm-canonical-state-t4.report.json", + "role": "matrix_kinematics", + "sha256": "5ed80c6e557787247825b746eb1f18bd94b7da88f00e24b559c9e4d636243da4", + "status": "present" + }, + { + "bytes": 6925, + "path": "coverage/wasm-motion-authority-t3.report.json", + "role": "matrix_tp", + "sha256": "8424f3bae26ff0b16868ddc1dde4f40e14fc64288aea3d9aba983cc46d62c967", + "status": "present" + }, + { + "bytes": 20798, + "path": "coverage/wasm-only-source-audit.report.json", + "role": "source_audit", + "sha256": "e62fecddd191827a08528ec5036e36addc85482ccf5c17066b6700e303c0f4fd", + "status": "present" + }, + { + "bytes": 2225, + "path": "coverage/workspace-migration.report.json", + "role": "matrix_recovery", + "sha256": "ce7451b5c428f170f37c6357877f08f0c2f32dc9502490cae3fe947cc43252e8", + "status": "present" + }, + { + "bytes": 1446, + "path": "coverage/workspace-recovery.report.json", + "role": "matrix_recovery", + "sha256": "b6140a4fd0f7b7c8ced8c19685cee086d78e27910dc9ecab7d28c201a0ae85b0", + "status": "present" + }, + { + "bytes": 8235, + "path": "docs/linuxcnc-web-wasm-wasm-only-migration-plan.md", + "role": "migration_plan", + "sha256": "aaadfcbb5e0c4dabd2785a3a93370021657c491050be895acf5f0c5a8f5d976c", + "status": "present" + }, + { + "bytes": 27266096, + "file_count": 1693, + "kind": "directory", + "path": "linuxcnc/configs/sim", + "role": "matrix_configuration", + "sha256": "18d4ee41860dd49f19b84c29f6e93d5425f43483770ef151f096629dd5f50d6a", + "status": "present" + }, + { + "bytes": 11423364, + "file_count": 415, + "kind": "directory", + "path": "linuxcnc/lib/python", + "role": "matrix_python_remap", + "sha256": "bcb686745f56dc94b2f58193533c7ded6b8d280510b73c386d1383c22023e429", + "status": "present" + }, + { + "bytes": 92894, + "file_count": 3, + "kind": "directory", + "path": "linuxcnc/src/emc/iotask", + "role": "matrix_io", + "sha256": "88e7b43985fb99952846bc5c610edbc02525c33be3ba896bfa103c66d6f8fca3", + "status": "present" + }, + { + "bytes": 241271, + "file_count": 38, + "kind": "directory", + "path": "linuxcnc/src/emc/kinematics", + "role": "matrix_kinematics", + "sha256": "4c26c0b886f102a65c120c367e6f0b853e4e26110958494b6ff79d28a5dfe51c", + "status": "present" + }, + { + "bytes": 258126, + "file_count": 16, + "kind": "directory", + "path": "linuxcnc/src/emc/tp", + "role": "matrix_tp", + "sha256": "1adab13d80e1483691b98744c69d21e3ae9822abe06cae6b53184b2c85fd1b11", + "status": "present" + }, + { + "bytes": 5810200, + "file_count": 548, + "kind": "directory", + "path": "linuxcnc/src/hal", + "role": "matrix_hal", + "sha256": "2b961826f284e071b5ec47e2a1beaba4ec4ded58ad1a0db70403522bc73890c8", + "status": "present" + }, + { + "bytes": 501667, + "file_count": 342, + "kind": "directory", + "path": "linuxcnc/tests/interp", + "role": "matrix_rs274", + "sha256": "c5135b59efc6733b0bae82bc9dc3329e39b3b8343d14d20ff6d94882a0bdc941", + "status": "present" + }, + { + "bytes": 132198, + "file_count": 165, + "kind": "directory", + "path": "linuxcnc/tests/remap", + "role": "matrix_python_remap", + "sha256": "1d68282f936a6167e017fe3fa1335f40b0d040d12e3061a083170ab3252d1fbb", + "status": "present" + }, + { + "bytes": 1902, + "path": "python-runtime/manifest.json", + "role": "matrix_python_remap", + "sha256": "340faa6ef5bedcaeb0583f463e6b89cb442ddf1d83d7e6ee8d96cee438fe4213", + "status": "present" + }, + { + "bytes": 2423, + "path": "wasm-core/include/sim_hal.h", + "role": "matrix_hal", + "sha256": "2ae61e361df8388f6c02ae880a9d726988a8370b10b2d07066749f2033662160", + "status": "present" + }, + { + "bytes": 2656, + "path": "wasm-core/include/sim_interpreter.h", + "role": "matrix_rs274", + "sha256": "38ab38bc55c47314a68d2d71ef39d3f3882819bddfd19cb0d114c7781b1c0a03", + "status": "present" + }, + { + "bytes": 4423, + "path": "wasm-core/include/sim_io.h", + "role": "matrix_io", + "sha256": "d84e9ab0bcf9ff138f8c8a57b28337605d731ff796577a8ab748d708f97daf9b", + "status": "present" + }, + { + "bytes": 12375, + "path": "wasm-core/include/sim_kinematics.h", + "role": "matrix_kinematics", + "sha256": "34b08db612056432e08705951032418bf6f6cd2c285bde5743515588fbe484ec", + "status": "present" + }, + { + "bytes": 1856, + "path": "wasm-core/include/sim_kinematics_home_limit.h", + "role": "matrix_kinematics", + "sha256": "ad4b4fc84efcc9a2dc6555d7ed69ab613eee21db7fef0d037d0a4ca1063309e1", + "status": "present" + }, + { + "bytes": 2498, + "path": "wasm-core/include/sim_motion_queue.h", + "role": "matrix_tp", + "sha256": "89903eda3c4c54e932c5351a3681c4dd1333808b893302a55c3cee31924d165c", + "status": "present" + }, + { + "bytes": 41841, + "path": "wasm-core/src/sim_hal.c", + "role": "matrix_hal", + "sha256": "3452009d839c89362dfeb793504cba26e45ea85f034ad5609859ec19cce3e9c3", + "status": "present" + }, + { + "bytes": 37556, + "path": "wasm-core/src/sim_interpreter.c", + "role": "matrix_rs274", + "sha256": "1685ad353b40ede3074725d7f9f1c6c245fb1e77bb5ceb60ca39a24655365d61", + "status": "present" + }, + { + "bytes": 27678, + "path": "wasm-core/src/sim_io.c", + "role": "matrix_io", + "sha256": "2ceacdb168a1f6dddb36761b93f62e4c39b3a6249c0a91a740ecf924dce217b3", + "status": "present" + }, + { + "bytes": 14463, + "path": "wasm-core/src/sim_motion_queue.c", + "role": "matrix_tp", + "sha256": "094d28a21c8f88dbe981c49e7216aa6d88afc2afdd9e54326edd4cd417ba13ba", + "status": "present" + }, + { + "bytes": 37270, + "path": "web/react/src/App.tsx", + "role": "matrix_browser_ux", + "sha256": "e81e38a8ea5217e4d02a58642b5a238ab65888eb8ea1403f58700cb85a141b58", + "status": "present" + }, + { + "bytes": 70586, + "path": "web/worker/sim-session.mjs", + "role": "matrix_recovery", + "sha256": "e83eab02bca8f71615dae96c7aa71ad912c21353af9504b2f74cea6b3c13fa43", + "status": "present" + }, + { + "bytes": 12551, + "path": "web/workspace/configuration-startup.mjs", + "role": "matrix_configuration", + "sha256": "e375abb6dcc0e0657f038356a68f5932c360bcf7fdf5ff906218a37b9012924a", + "status": "present" + }, + { + "bytes": 7054, + "path": "web/workspace/configuration.mjs", + "role": "matrix_configuration", + "sha256": "89509d6604d8f3bcddc46c084f058c232bc56142e0e7dd01726b96f920b2585e", + "status": "present" + }, + { + "bytes": 1771, + "path": "web/workspace/linuxcnc-source-semantics.mjs", + "role": "source_semantics_implementation", + "sha256": "b15ebfb52de87f46ce03127b6be4f5c0dc6768f5b3554c97b726649838a5e3dc", + "status": "present" + }, + { + "bytes": 8767, + "path": "web/workspace/recovery.mjs", + "role": "matrix_recovery", + "sha256": "d4bae4220a3348d7bb66991472d1331045ab60bbe70c93f3b589bf0e8d05d14d", + "status": "present" + } + ], + "known_blockers": [ + "full LinuxCNC source oracle and expected/checkresult closure", + "official kinematics/recovery oracle absent from locked source", + "configuration resources and startup blockers remain explicit", + "Firefox/Safari minimums and physical OPFS recovery are not verified" + ], + "matrix": [ + { + "coverage": { + "boundary": "verified", + "error": "verified", + "nonfinite": "verified", + "normal": "verified", + "recovery": "queued" + }, + "differences": [ + "source expected/checkresult parity is not closed for every interpreter fixture", + "selected canonical events do not establish complete LinuxCNC G/M/canned-cycle parity" + ], + "id": "rs274", + "inputs": [ + "coverage/rs274-interpreter-closure.report.json", + "coverage/linuxcnc-source-test-execution-s5.report.json", + "wasm-core/include/sim_interpreter.h", + "wasm-core/src/sim_interpreter.c", + "linuxcnc/tests/interp" + ], + "native": { + "status": "verified" + }, + "owner": "simulation-runtime", + "status": "blocked", + "stop_reason": "complete RS274 source-test and canonical callback matrix remains open", + "tests": [ + [ + "node", + "web/worker/test_source_interpreter_s5.mjs" + ], + [ + "node", + "web/worker/test_wasm_motion_event.mjs" + ] + ], + "title": "RS274 interpreter and canonical events", + "wasm": { + "authority": "sim_interpreter", + "status": "verified" + }, + "worker": { + "authority": "wasm_motion_event", + "status": "verified" + } + }, + { + "coverage": { + "boundary": "verified", + "error": "verified", + "nonfinite": "verified", + "normal": "verified", + "recovery": "verified" + }, + "differences": [ + "fixed-tick selected-case queue is verified, but full TP numeric golden coverage is not", + "blend/exact-stop upstream oracle and all configuration families remain pending" + ], + "id": "tp", + "inputs": [ + "coverage/motion-runtime.report.json", + "coverage/wasm-motion-authority-t3.report.json", + "wasm-core/include/sim_motion_queue.h", + "wasm-core/src/sim_motion_queue.c", + "linuxcnc/src/emc/tp" + ], + "native": { + "status": "verified" + }, + "owner": "simulation-runtime", + "status": "blocked", + "stop_reason": "full TP/source oracle matrix and cross-case numeric parity are not closed", + "tests": [ + [ + "node", + "web/worker/test_source_g6164_s7_02.mjs" + ], + [ + "node", + "web/worker/test_wasm_motion_authority.mjs" + ], + [ + "node", + "web/worker/test_wasm_motion_checkpoint.mjs" + ] + ], + "title": "TP lookahead, blend, exact-stop and motion queue", + "wasm": { + "authority": "sim_motion_queue", + "status": "verified" + }, + "worker": { + "authority": "wasm_motion_queue", + "status": "verified" + } + }, + { + "coverage": { + "boundary": "verified", + "error": "verified", + "nonfinite": "verified", + "normal": "verified", + "recovery": "verified" + }, + "differences": [ + "trivkins selected-case home/limit and several runtime families are implemented", + "official kinematics oracle is absent from the locked source; remaining families are not authoritative" + ], + "id": "kinematics", + "inputs": [ + "coverage/kinematics-runtime.report.json", + "coverage/kinematics-oracle.report.json", + "coverage/kinematics-registry.report.json", + "coverage/wasm-canonical-state-t4.report.json", + "wasm-core/include/sim_kinematics.h", + "wasm-core/include/sim_kinematics_home_limit.h", + "linuxcnc/src/emc/kinematics" + ], + "native": { + "status": "blocked" + }, + "owner": "simulation-runtime", + "status": "blocked", + "stop_reason": "SOURCE_BLOCKED official kinematics oracle prevents full native/WASM/Worker matrix closure", + "tests": [ + [ + "node", + "web/worker/test_authoritative_kinematics_s6_04.mjs" + ], + [ + "node", + "web/worker/test_wasm_canonical_state.mjs" + ], + [ + "node", + "web/worker/test_axis3_tutorial_motion.mjs" + ] + ], + "title": "Kinematics registry, FK/IK and home/limit", + "wasm": { + "authority": "sim_kinematics", + "status": "verified" + }, + "worker": { + "authority": "wasm_home_limit", + "case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", + "status": "verified" + } + }, + { + "coverage": { + "boundary": "verified", + "error": "verified", + "nonfinite": "queued", + "normal": "verified", + "recovery": "verified" + }, + "differences": [ + "static manifest and fixed-tick virtual components are covered", + "dynamic loadusr, undeclared components and host-backed HAL remain blocked" + ], + "id": "hal", + "inputs": [ + "coverage/hal-runtime.report.json", + "coverage/hal-component-manifest.report.json", + "coverage/hal-graph.report.json", + "coverage/runtime-extensions-s8-09.report.json", + "wasm-core/include/sim_hal.h", + "wasm-core/src/sim_hal.c", + "linuxcnc/src/hal" + ], + "native": { + "status": "verified" + }, + "owner": "simulation-runtime", + "status": "blocked", + "stop_reason": "complete HAL component/source graph cannot accept host or dynamic loader paths", + "tests": [ + [ + "node", + "web/worker/test_hal_runtime.mjs" + ], + [ + "node", + "web/worker/test_hal_preflight.mjs" + ], + [ + "node", + "web/worker/test_plc_runtime.mjs" + ] + ], + "title": "HAL graph, components, PLC and fixed-tick execution", + "wasm": { + "authority": "sim_hal", + "status": "verified" + }, + "worker": { + "authority": "static_hal_manifest", + "status": "verified" + } + }, + { + "coverage": { + "boundary": "verified", + "error": "verified", + "nonfinite": "verified", + "normal": "verified", + "recovery": "verified" + }, + "differences": [ + "virtual device normal/fault/replay and selected-case tool/IO paths are deterministic", + "transport-specific acknowledgements and real hardware remain virtual or REAL-EXCLUDED" + ], + "id": "io", + "inputs": [ + "coverage/io-runtime.report.json", + "coverage/device-io-s2.report.json", + "coverage/io-ack.report.json", + "coverage/virtual-device-fault-matrix.report.json", + "wasm-core/include/sim_io.h", + "wasm-core/src/sim_io.c", + "linuxcnc/src/emc/iotask" + ], + "native": { + "status": "verified" + }, + "owner": "simulation-runtime", + "status": "blocked", + "stop_reason": "full source IO/device matrix includes real transport and hardware paths outside Worker scope", + "tests": [ + [ + "node", + "web/worker/test_device_io.mjs" + ], + [ + "node", + "web/worker/test_io_ack.mjs" + ], + [ + "node", + "web/worker/test_virtual_devices.mjs" + ], + [ + "node", + "web/worker/test_axis3_tutorial_tool_io.mjs" + ] + ], + "title": "IO, spindle, tool, probe, coolant, estop and acknowledgements", + "wasm": { + "authority": "sim_io", + "status": "verified" + }, + "worker": { + "authority": "virtual_devices", + "status": "verified" + } + }, + { + "coverage": { + "boundary": "verified", + "error": "verified", + "nonfinite": "queued", + "normal": "verified", + "recovery": "verified" + }, + "differences": [ + "allowlisted Pyodide and Worker callback slices verify capability denial and replay", + "host imports, arbitrary modules and complete VCP/plugin closure remain unavailable" + ], + "id": "python_remap", + "inputs": [ + "coverage/python-runtime-closure.report.json", + "coverage/python-worker-bridge.report.json", + "coverage/runtime-extensions-s8-09.report.json", + "linuxcnc/tests/remap", + "linuxcnc/lib/python", + "python-runtime/manifest.json" + ], + "native": { + "reason": "host Python capabilities are excluded", + "status": "blocked" + }, + "owner": "python-runtime", + "status": "blocked", + "stop_reason": "REAL-EXCLUDED host Python and dynamic plugin capabilities prevent full parity claim", + "tests": [ + [ + "node", + "web/worker/test_python_bridge.mjs" + ], + [ + "node", + "web/worker/test_python_callbacks_s3.mjs" + ], + [ + "node", + "web/worker/test_source_do_s7_01.mjs" + ], + [ + "node", + "web/worker/test_source_interpreter_s5.mjs" + ] + ], + "title": "Python, remap, O-word, filter and VCP callbacks", + "wasm": { + "authority": "pyodide_allowlist", + "status": "verified" + }, + "worker": { + "authority": "python_bridge", + "status": "verified" + } + }, + { + "coverage": { + "boundary": "verified", + "error": "verified", + "nonfinite": "queued", + "normal": "verified", + "recovery": "queued" + }, + "differences": [ + "resolver and exact-path diagnostics are deterministic", + "full matrix retains missing/unindexed/HAL/INI/STARTUP blockers; source-defined optional OPEN_FILE does not block startup" + ], + "id": "configuration", + "inputs": [ + "coverage/full-configuration-startup-matrix.report.json", + "coverage/configuration-startup-closure.report.json", + "coverage/configuration-resource-remediation.report.json", + "coverage/sim-case-matrix-s8-06.report.json", + "web/workspace/configuration.mjs", + "web/workspace/configuration-startup.mjs", + "linuxcnc/configs/sim" + ], + "native": { + "reason": "full startup matrix retains blocked resources", + "status": "blocked" + }, + "owner": "workspace-runtime", + "status": "blocked", + "stop_reason": "configuration resources and startup blockers remain; no basename substitution or blocked-case promotion is allowed", + "tests": [ + [ + "node", + "web/worker/test_configuration_startup_runtime.mjs" + ], + [ + "node", + "web/worker/test_sim_case_matrix_s8_06.mjs" + ], + [ + "node", + "web/worker/test_configuration_preflight.mjs" + ] + ], + "title": "INI, HAL/resource resolution and configuration startup", + "wasm": { + "authority": "configuration_startup", + "status": "blocked" + }, + "worker": { + "authority": "configuration_preflight", + "status": "blocked" + } + }, + { + "coverage": { + "boundary": "verified", + "error": "verified", + "nonfinite": "queued", + "normal": "verified", + "recovery": "verified" + }, + "differences": [ + "logical checkpoint/trace/import/replay hashes are stable for the selected case", + "physical power-loss/OPFS corruption and official recovery oracle remain unverified" + ], + "id": "recovery", + "inputs": [ + "coverage/recovery-offline-fault-matrix.report.json", + "coverage/workspace-recovery.report.json", + "coverage/workspace-migration.report.json", + "coverage/wasm-canonical-state-t4.report.json", + "web/workspace/recovery.mjs", + "web/worker/sim-session.mjs" + ], + "native": { + "status": "verified" + }, + "owner": "workspace-runtime", + "status": "blocked", + "stop_reason": "browser physical-crash and official recovery oracle inputs remain blocked", + "tests": [ + [ + "node", + "web/worker/test_wasm_canonical_state_checkpoint.mjs" + ], + [ + "node", + "web/worker/test_recovery_offline_faults.mjs" + ], + [ + "node", + "web/worker/test_workspace_session_integration.mjs" + ] + ], + "title": "Checkpoint, trace, journal, migration, rollback and offline recovery", + "wasm": { + "authority": "canonical_state_checkpoint", + "status": "verified" + }, + "worker": { + "authority": "workspace_session", + "status": "verified" + } + }, + { + "coverage": { + "boundary": "verified", + "error": "verified", + "nonfinite": "queued", + "normal": "verified", + "recovery": "blocked" + }, + "differences": [ + "Chromium production preview and Worker protocol are verified for the selected workflow", + "Firefox/Safari minimums are not locked, Safari is unavailable here, and OPFS physical crash remains open" + ], + "id": "browser_ux", + "inputs": [ + "coverage/gui-browser-visual-fe-m4.report.json", + "coverage/browser-performance-matrix.report.json", + "coverage/browser-matrix-remediation.report.json", + "coverage/browser-simulation-s3.report.json", + "web/react/src/App.tsx", + "web/worker/sim-session.mjs" + ], + "native": { + "reason": "native is not a browser UX oracle", + "status": "queued" + }, + "owner": "frontend-runtime", + "status": "blocked", + "stop_reason": "three-browser minimum matrix and physical OPFS recovery are release blockers", + "tests": [ + [ + "node", + "web/worker/test_axis3_selected_case.mjs" + ], + [ + "node", + "web/worker/test_axis3_visible_completion.mjs" + ], + [ + "npm", + "--prefix", + "web/react", + "run", + "typecheck" + ] + ], + "title": "Worker protocol, React UX, WebGL and browser compatibility", + "wasm": { + "authority": "dedicated_worker_wasm", + "status": "verified" + }, + "worker": { + "authority": "protocol_v1", + "status": "verified" + } + } + ], + "next_transition": "T6-FUNCTION-CLOSURE-01 after source-backed owner inputs and matrix blockers are resolved", + "open_boundaries": [ + "selected 3axis-tutorial is evidence for a slice only", + "REAL-EXCLUDED hardware, host paths, realtime kernel and dynamic loaders remain excluded", + "HAL/IO/tool/probe compatibility outside the virtual fixed-tick adapters remains bounded" + ], + "release_action": "STOP_RELEASE", + "report_sha256": "f58cd9c521a04377d40fdffa687e2b5b07d13f489057078be1a040b9dad75eab", + "schema": "wasm-feature-matrix-t5.v1", + "source_of_truth": "linuxcnc source -> native/WASM core -> Worker projection -> React rendering", + "status": "blocked", + "task_id": "T5-FULL-MATRIX-01", + "tests": [ + { + "commands": [ + { + "command": [ + "node", + "web/worker/test_source_interpreter_s5.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "ddd5df1f071c70cb66f6caed3a1a516c5aaaa7663e4688020c460d586927d1c7" + }, + { + "command": [ + "node", + "web/worker/test_wasm_motion_event.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "a9a55cb64052d39c3b7ec58f0124187003e645f4b513d0b184c79b9a3182a891" + } + ], + "domain": "rs274" + }, + { + "commands": [ + { + "command": [ + "node", + "web/worker/test_source_g6164_s7_02.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "d79b61dae572272413a54b0ccccd2008de1f20125c39b5dd30a1920351e21e4e" + }, + { + "command": [ + "node", + "web/worker/test_wasm_motion_authority.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "3e66880d4e8a0b6205a351178c0f68f4eae72094c78bde514eee0f45e66b25af" + }, + { + "command": [ + "node", + "web/worker/test_wasm_motion_checkpoint.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "3b24f305766e7b7442a30a6cfa60e46d0669689f4960d1a7b67c41878878f639" + } + ], + "domain": "tp" + }, + { + "commands": [ + { + "command": [ + "node", + "web/worker/test_authoritative_kinematics_s6_04.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "0251b25272a27dd98076036be908a71a187d56407fd96fd8489d13c849160d36" + }, + { + "command": [ + "node", + "web/worker/test_wasm_canonical_state.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "2423ec3d9cbc8fca56010ba037ea85649e8b668ce4b4e20d585544cb87ecebe4" + }, + { + "command": [ + "node", + "web/worker/test_axis3_tutorial_motion.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "09875bd9491a95f43f8891a26663bb7be5704be84a75d686146c8c77ffb4ea94" + } + ], + "domain": "kinematics" + }, + { + "commands": [ + { + "command": [ + "node", + "web/worker/test_hal_runtime.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "7dcb47ba0dcd86be421b1a3d133ed0b4951d2e710d7b518f0a4de74406f47227" + }, + { + "command": [ + "node", + "web/worker/test_hal_preflight.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "59f10fd8bee5e3232ee1b221981792a68d08d48beef6ed83e82971f0faebf398" + }, + { + "command": [ + "node", + "web/worker/test_plc_runtime.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "901be8b4abe53d8fb0a01eafe6ba448280d9446b05f48e1cd662cdada13cdc0f" + } + ], + "domain": "hal" + }, + { + "commands": [ + { + "command": [ + "node", + "web/worker/test_device_io.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "f539ac2a59a40973439f44683e29e473139cd70d1524aac72f99b64689196098" + }, + { + "command": [ + "node", + "web/worker/test_io_ack.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "a277ff07898a13759e86587e59cd7826d8f2403d8fbaffb74e8d26f5e15b52e2" + }, + { + "command": [ + "node", + "web/worker/test_virtual_devices.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "daa4c0f7b69fac765297984aeb95539da4643416b73634f4773a9eba52dff8dc" + }, + { + "command": [ + "node", + "web/worker/test_axis3_tutorial_tool_io.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "d542bc8f3de4d1434b30ff54aaf03e1d46cb5986e487296ff60087dc5ed2ea89" + } + ], + "domain": "io" + }, + { + "commands": [ + { + "command": [ + "node", + "web/worker/test_python_bridge.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "5a0211cf093d9c498f4ddf4009190833332ec1226310f5e5e2eace604a5e9060" + }, + { + "command": [ + "node", + "web/worker/test_python_callbacks_s3.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "bc8517bff512c618270adcc856801db822b9c7d3ce13cae5c249c8e39ee99e85" + }, + { + "command": [ + "node", + "web/worker/test_source_do_s7_01.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "84233a11d16f6915e6acbe3bca61324a694ca625a3f2ce159f01ca94f394496b" + }, + { + "command": [ + "node", + "web/worker/test_source_interpreter_s5.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "ddd5df1f071c70cb66f6caed3a1a516c5aaaa7663e4688020c460d586927d1c7" + } + ], + "domain": "python_remap" + }, + { + "commands": [ + { + "command": [ + "node", + "web/worker/test_configuration_startup_runtime.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "7956e3bac69532da76025282b51a6d63b903fc94a6791fb1f2f0af3a9d697537" + }, + { + "command": [ + "node", + "web/worker/test_sim_case_matrix_s8_06.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "489263f0b6a3250f526596b5349801ab4e2e9f4ad2ef34f4cef3c556b29905fb" + }, + { + "command": [ + "node", + "web/worker/test_configuration_preflight.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "1684f25cda88240f009a6c855ce2e50ccda42fb629d21e29a6d474880651e888" + } + ], + "domain": "configuration" + }, + { + "commands": [ + { + "command": [ + "node", + "web/worker/test_wasm_canonical_state_checkpoint.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "bc8689a3119a812d2e7fc59a56d0c39eaa9002fd51bc49455d41409fc3614166" + }, + { + "command": [ + "node", + "web/worker/test_recovery_offline_faults.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "7a62245e494eb4855f1668dab5f6577cba3e5b5e3d179798f33c0742ab5613e7" + }, + { + "command": [ + "node", + "web/worker/test_workspace_session_integration.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "0e1cad80e126a491b05503cd6c6c676b91167511f59c579f969adab51df64247" + } + ], + "domain": "recovery" + }, + { + "commands": [ + { + "command": [ + "node", + "web/worker/test_axis3_selected_case.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "dfb469ab07f3c6c33a0d5834d7857bb84daef79904d6e6f783bf90928b833674" + }, + { + "command": [ + "node", + "web/worker/test_axis3_visible_completion.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "25438e9c7c6d69b80191148e7a3ba9d46505384187899348c60f6dd0c9f1a39d" + }, + { + "command": [ + "npm", + "--prefix", + "web/react", + "run", + "typecheck" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "e3bd90442e638c272f371a98c498e8204369416e1ac6f8e8b2565857b9fbdba0" + } + ], + "domain": "browser_ux" + } + ] +} diff --git a/coverage/wasm-function-closure-t6.report.json b/coverage/wasm-function-closure-t6.report.json new file mode 100644 index 0000000..cb68b8a --- /dev/null +++ b/coverage/wasm-function-closure-t6.report.json @@ -0,0 +1,922 @@ +{ + "candidate_eligible": false, + "case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", + "checks": { + "closure_status_vocabulary": { + "status": "verified" + }, + "context_match": { + "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", + "status": "verified" + }, + "failure_samples": { + "count": 4, + "status": "blocked" + }, + "no_js_fallback": { + "policy": "blocked rows never fall back to JS authority", + "status": "verified" + }, + "release_stop_preserved": { + "candidate_eligible": false, + "release_action": "STOP_RELEASE", + "status": "verified" + }, + "row_count": { + "count": 8, + "status": "verified" + }, + "selected_case_boundary": { + "case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", + "promotion": "forbidden", + "status": "verified" + }, + "source_refs": { + "status": "verified" + }, + "test_execution": { + "blocked_commands": 0, + "status": "verified" + } + }, + "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", + "counts": { + "blocked": 8, + "rows": 8, + "test_blocked": 0, + "test_commands": 23, + "verified_slices": 8 + }, + "failure_samples": [ + { + "code": "official_kinematics_oracle_missing", + "observed": "missing_from_locked_source", + "source": "linuxcnc/unit_tests/kinematics" + }, + { + "code": "configuration_resource_blocked", + "observed": { + "blocked_entries": 208, + "entrypoints": 233 + }, + "source": "coverage/full-configuration-startup-matrix.report.json" + }, + { + "code": "browser_minimum_unpinned", + "observed": { + "chromium": "TBD", + "firefox": "TBD", + "safari": "TBD" + }, + "source": "context-lock.json" + }, + { + "code": "host_capability_excluded", + "observed": [ + "host_paths", + "socket", + "subprocess", + "device_paths" + ], + "source": "python-runtime/manifest.json" + } + ], + "implementation_status": "verified", + "inputs": [ + { + "bytes": 7647, + "path": "context-lock.json", + "role": "context_lock", + "sha256": "bf6651f8d1803f2219d40e61f5e86dcf0821862d087eec2e19f99f593561353e", + "status": "present" + }, + { + "bytes": 6180, + "path": "coverage/browser-matrix-remediation.report.json", + "role": "source_browser_ux", + "sha256": "8eacc6c6e168df19fc2082c1788ce3e5c025e9b528cccc0c5dcf62a84a8664d7", + "status": "present" + }, + { + "bytes": 5534, + "path": "coverage/browser-performance-matrix.report.json", + "role": "source_browser_ux", + "sha256": "d3386dc5839d4fed2bdae7174abd081736fe39555f1710e0e8d344f284c18434", + "status": "present" + }, + { + "bytes": 827383, + "path": "coverage/configuration-resource-remediation.report.json", + "role": "source_configuration", + "sha256": "8036cb8bc63957a82eaed6fbb249bf61b9fd9ab136e746a47e8e7f48a9a2f21c", + "status": "present" + }, + { + "bytes": 225468, + "path": "coverage/full-configuration-startup-matrix.report.json", + "role": "source_configuration", + "sha256": "9345288c2e5353d382a17e2ff18f881d8516f2e792920325c2b5cc28ac17a2f9", + "status": "present" + }, + { + "bytes": 30391, + "path": "coverage/full-linuxcnc-simulation-gap-audit.report.json", + "role": "gap_audit", + "sha256": "4bfad951b95dd2030e929268dc6c67baf025c448d25fd52951dfb6bbb04e749d", + "status": "present" + }, + { + "bytes": 7073, + "path": "coverage/gui-browser-visual-fe-m4.report.json", + "role": "source_browser_ux", + "sha256": "4a91556173f36fdac53b94352d1ad732a419199c4c329320fb044a53ec55dcdf", + "status": "present" + }, + { + "bytes": 2535591, + "path": "coverage/hal-component-manifest.report.json", + "role": "source_hal_io", + "sha256": "bef0cb643203ac6ef239d0d5c975ef4d2b30902d09a77c1a3103f7983ee2f5a6", + "status": "present" + }, + { + "bytes": 20284, + "path": "coverage/kinematics-oracle.report.json", + "role": "source_kinematics", + "sha256": "4dc09ea52d1cc32cd3c4f006b1c2f880e54ca147e6d4f651cee4b67626f56d05", + "status": "present" + }, + { + "bytes": 9545, + "path": "coverage/kinematics-runtime.report.json", + "role": "source_kinematics", + "sha256": "7090db32ac2af28e13c115b63ba7b894596efe25d8eec9fc27f097db931fde2c", + "status": "present" + }, + { + "bytes": 12457, + "path": "coverage/motion-runtime.report.json", + "role": "source_tp", + "sha256": "fb2d36130bf3168bea9b13fe08cc2225d72190a7aa9c99a0f0db7f148ac171df", + "status": "present" + }, + { + "bytes": 4914, + "path": "coverage/python-worker-bridge.report.json", + "role": "source_python_remap", + "sha256": "977be3b05a9d07cf2534f9646cd1673ba637dc15d896cdf905a539b855934edf", + "status": "present" + }, + { + "bytes": 3170, + "path": "coverage/recovery-offline-fault-matrix.report.json", + "role": "source_recovery", + "sha256": "9f969d479806f8b7300ca7db368872add00738da4cf0dabe08f834981ddc33b9", + "status": "present" + }, + { + "bytes": 3704, + "path": "coverage/rs274-interpreter-closure.report.json", + "role": "source_rs274", + "sha256": "f89b09eb912bcca4f75cf0392b889e0051fcdc61b6b2f14663c72fca63b8f608", + "status": "present" + }, + { + "bytes": 1925705, + "path": "coverage/source-index.jsonl", + "role": "source_inventory", + "sha256": "2a78438ab4a685476cc57f10d15e2ca3ee688892a7696081a135018638031404", + "status": "present" + }, + { + "bytes": 248673, + "path": "coverage/test-entries.jsonl", + "role": "test_inventory", + "sha256": "86449838257a7aab7c9219e641a14d4493208b35d8c0cc092d1fa149352c7c37", + "status": "present" + }, + { + "bytes": 11813, + "path": "coverage/virtual-device-fault-matrix.report.json", + "role": "source_hal_io", + "sha256": "6cad7235733178f045b7dda3be16b5ebac003e91b2b9709494a85da06b3cf11b", + "status": "present" + }, + { + "bytes": 6188, + "path": "coverage/wasm-canonical-state-t4.report.json", + "role": "source_recovery", + "sha256": "5ed80c6e557787247825b746eb1f18bd94b7da88f00e24b559c9e4d636243da4", + "status": "present" + }, + { + "bytes": 42079, + "path": "coverage/wasm-feature-matrix-t5.report.json", + "role": "t5_matrix", + "sha256": "d5082c9f181e905563a9e8430de3d4d04af66059affd15dd3bd7b2e2df5b7ea8", + "status": "present" + }, + { + "bytes": 6925, + "path": "coverage/wasm-motion-authority-t3.report.json", + "role": "source_tp", + "sha256": "8424f3bae26ff0b16868ddc1dde4f40e14fc64288aea3d9aba983cc46d62c967", + "status": "present" + }, + { + "bytes": 1446, + "path": "coverage/workspace-recovery.report.json", + "role": "source_recovery", + "sha256": "b6140a4fd0f7b7c8ced8c19685cee086d78e27910dc9ecab7d28c201a0ae85b0", + "status": "present" + }, + { + "bytes": 8235, + "path": "docs/linuxcnc-web-wasm-wasm-only-migration-plan.md", + "role": "migration_plan", + "sha256": "aaadfcbb5e0c4dabd2785a3a93370021657c491050be895acf5f0c5a8f5d976c", + "status": "present" + }, + { + "bytes": 27266096, + "file_count": 1693, + "kind": "directory", + "path": "linuxcnc/configs/sim", + "role": "source_configuration", + "sha256": "18d4ee41860dd49f19b84c29f6e93d5425f43483770ef151f096629dd5f50d6a", + "status": "present" + }, + { + "bytes": 11423364, + "file_count": 415, + "kind": "directory", + "path": "linuxcnc/lib/python", + "role": "source_python_remap", + "sha256": "bcb686745f56dc94b2f58193533c7ded6b8d280510b73c386d1383c22023e429", + "status": "present" + }, + { + "bytes": 92894, + "file_count": 3, + "kind": "directory", + "path": "linuxcnc/src/emc/iotask", + "role": "source_hal_io", + "sha256": "88e7b43985fb99952846bc5c610edbc02525c33be3ba896bfa103c66d6f8fca3", + "status": "present" + }, + { + "bytes": 241271, + "file_count": 38, + "kind": "directory", + "path": "linuxcnc/src/emc/kinematics", + "role": "source_kinematics", + "sha256": "4c26c0b886f102a65c120c367e6f0b853e4e26110958494b6ff79d28a5dfe51c", + "status": "present" + }, + { + "bytes": 1104560, + "file_count": 49, + "kind": "directory", + "path": "linuxcnc/src/emc/rs274ngc", + "role": "source_rs274", + "sha256": "5d7ada0ae490de298d4c86767e543a255455d5d380a6ba681b5e2d004ecf9b85", + "status": "present" + }, + { + "bytes": 258126, + "file_count": 16, + "kind": "directory", + "path": "linuxcnc/src/emc/tp", + "role": "source_tp", + "sha256": "1adab13d80e1483691b98744c69d21e3ae9822abe06cae6b53184b2c85fd1b11", + "status": "present" + }, + { + "bytes": 5810200, + "file_count": 548, + "kind": "directory", + "path": "linuxcnc/src/hal", + "role": "source_hal_io", + "sha256": "2b961826f284e071b5ec47e2a1beaba4ec4ded58ad1a0db70403522bc73890c8", + "status": "present" + }, + { + "bytes": 501667, + "file_count": 342, + "kind": "directory", + "path": "linuxcnc/tests/interp", + "role": "source_rs274", + "sha256": "c5135b59efc6733b0bae82bc9dc3329e39b3b8343d14d20ff6d94882a0bdc941", + "status": "present" + }, + { + "bytes": 132198, + "file_count": 165, + "kind": "directory", + "path": "linuxcnc/tests/remap", + "role": "source_python_remap", + "sha256": "1d68282f936a6167e017fe3fa1335f40b0d040d12e3061a083170ab3252d1fbb", + "status": "present" + }, + { + "bytes": 2366, + "file_count": 2, + "kind": "directory", + "path": "linuxcnc/unit_tests/tp", + "role": "source_tp", + "sha256": "8a2cc64b5648c3e84c3c037db6872815b24b564e61bf2288d95f3ce9f820b2be", + "status": "present" + }, + { + "bytes": 1902, + "path": "python-runtime/manifest.json", + "role": "source_python_remap", + "sha256": "340faa6ef5bedcaeb0583f463e6b89cb442ddf1d83d7e6ee8d96cee438fe4213", + "status": "present" + } + ], + "matrix": [ + { + "capability": { + "authority": "sim_interpreter", + "wasm": "verified", + "worker": "verified" + }, + "difference": "complete G/M/canned-cycle/coordinate-compensation oracle is not present in the locked closure", + "id": "rs274", + "next_action": "provide exact upstream expected/checkresult and canonical callback golden before extending interpreter", + "owner": "simulation-runtime", + "source_refs": [ + "linuxcnc/src/emc/rs274ngc", + "linuxcnc/tests/interp", + "coverage/rs274-interpreter-closure.report.json" + ], + "status": "blocked", + "stop_reason": "SOURCE_ORACLE_BLOCKED: S1-01B expected/canonical callback inputs are not approved", + "tests": [ + [ + "node", + "web/worker/test_source_interpreter_s5.mjs" + ], + [ + "node", + "web/worker/test_source_do_s7_01.mjs" + ], + [ + "node", + "web/worker/test_source_g6164_s7_02.mjs" + ] + ], + "title": "advanced RS274/G-M/O and canonical callback closure", + "verified_slice": [ + "S1-01A interpreter state/modal/error ABI", + "S5-04 source interpreter adapters", + "S7-01 DO/ENDDO/BREAK", + "S7-02 G64/G61" + ] + }, + { + "capability": { + "authority": "sim_motion_queue", + "wasm": "verified", + "worker": "verified" + }, + "difference": "queue state is deterministic but full upstream blend and dynamics numeric golden is not closed", + "id": "tp", + "next_action": "import approved TP golden with fixed logical tick and first-divergent-field rules", + "owner": "simulation-runtime", + "source_refs": [ + "linuxcnc/src/emc/tp", + "linuxcnc/unit_tests/tp", + "coverage/motion-runtime.report.json", + "coverage/wasm-motion-authority-t3.report.json" + ], + "status": "blocked", + "stop_reason": "SOURCE_ORACLE_BLOCKED: TP numeric oracle and all configuration-family samples are unavailable", + "tests": [ + [ + "node", + "web/worker/test_wasm_motion_authority.mjs" + ], + [ + "node", + "web/worker/test_wasm_motion_checkpoint.mjs" + ], + [ + "node", + "web/worker/test_source_g6164_s7_02.mjs" + ] + ], + "title": "TP blend/exact-stop/accel/jerk closure", + "verified_slice": [ + "fixed-tick sim_motion_queue", + "G64/G61 Worker modal state", + "checkpoint/replay endpoint parity" + ] + }, + { + "capability": { + "authority": "sim_kinematics_home_limit", + "wasm": "verified", + "worker": "verified" + }, + "difference": "runtime supports deterministic families, but official kinematics unit-test root is absent", + "id": "kinematics", + "next_action": "obtain approved official FK/IK/recovery oracle or an explicit contract replacement", + "owner": "simulation-runtime", + "source_refs": [ + "linuxcnc/src/emc/kinematics", + "coverage/kinematics-runtime.report.json", + "coverage/kinematics-oracle.report.json" + ], + "status": "blocked", + "stop_reason": "SOURCE_ORACLE_BLOCKED: linuxcnc/unit_tests/kinematics is missing from locked source", + "tests": [ + [ + "node", + "web/worker/test_authoritative_kinematics_s6_04.mjs" + ], + [ + "node", + "web/worker/test_axis3_tutorial_motion.mjs" + ], + [ + "node", + "web/worker/test_wasm_canonical_state.mjs" + ] + ], + "title": "kinematics family FK/IK and home/limit closure", + "verified_slice": [ + "trivkins selected-case home/limit", + "registered fixed-tick kinematics families" + ] + }, + { + "capability": { + "authority": "sim_hal_and_sim_io", + "wasm": "verified", + "worker": "verified" + }, + "difference": "static/virtual paths are replayable; dynamic loadusr and real transport/device paths remain excluded", + "id": "hal_io", + "next_action": "keep virtual adapter boundary explicit; require an approved pure-simulation fixture for any new component", + "owner": "simulation-runtime", + "source_refs": [ + "linuxcnc/src/hal", + "linuxcnc/src/emc/iotask", + "coverage/hal-component-manifest.report.json", + "coverage/virtual-device-fault-matrix.report.json" + ], + "status": "blocked", + "stop_reason": "REAL_EXCLUDED: host drivers, dynamic loaders and physical device timing cannot be accepted as Worker parity", + "tests": [ + [ + "node", + "web/worker/test_hal_runtime.mjs" + ], + [ + "node", + "web/worker/test_device_io.mjs" + ], + [ + "node", + "web/worker/test_io_ack.mjs" + ], + [ + "node", + "web/worker/test_virtual_devices.mjs" + ] + ], + "title": "HAL, PLC, IO and virtual device closure", + "verified_slice": [ + "static HAL manifest/graph", + "fixed-tick virtual devices", + "IO ack/fault/recovery" + ] + }, + { + "capability": { + "authority": "python_allowlist", + "wasm": "verified", + "worker": "verified" + }, + "difference": "allowlisted callbacks are deterministic; host imports and arbitrary plugin modules are denied", + "id": "python_remap", + "next_action": "add individually hashed allowlisted module contract only after source and license inputs are approved", + "owner": "python-runtime", + "source_refs": [ + "linuxcnc/lib/python", + "linuxcnc/tests/remap", + "coverage/python-worker-bridge.report.json", + "python-runtime/manifest.json" + ], + "status": "blocked", + "stop_reason": "REAL_EXCLUDED: host Python, dynamic imports and unrestricted filesystem/network capabilities are outside Worker scope", + "tests": [ + [ + "node", + "web/worker/test_python_bridge.mjs" + ], + [ + "node", + "web/worker/test_python_callbacks_s3.mjs" + ], + [ + "node", + "web/worker/test_source_do_s7_01.mjs" + ] + ], + "title": "Python/remap/filter/VCP closure", + "verified_slice": [ + "Pyodide allowlist", + "virtual linuxcnc/hal API", + "remap/O-word callback samples" + ] + }, + { + "capability": { + "authority": "configuration_startup", + "wasm": "verified", + "worker": "verified" + }, + "difference": "resolver is fail-closed, but the full matrix retains missing/unindexed/HAL/INI/STARTUP resources", + "id": "configuration", + "next_action": "supply exact-path resource fixtures and regenerate the startup matrix without promotion", + "owner": "workspace-runtime", + "source_refs": [ + "linuxcnc/configs/sim", + "coverage/full-configuration-startup-matrix.report.json", + "coverage/configuration-resource-remediation.report.json" + ], + "status": "blocked", + "stop_reason": "RESOURCE_BLOCKED: exact source resources are absent or unresolved; basename substitution is forbidden", + "tests": [ + [ + "node", + "web/worker/test_configuration_startup_runtime.mjs" + ], + [ + "node", + "web/worker/test_configuration_preflight.mjs" + ] + ], + "title": "configuration/resource/startup closure", + "verified_slice": [ + "exact-path resolver", + "INI/HAL diagnostics", + "TASK_READY selected fixtures" + ] + }, + { + "capability": { + "authority": "workspace_session", + "wasm": "verified", + "worker": "verified" + }, + "difference": "logical recovery is deterministic; physical power-loss/OPFS corruption and official recovery oracle are open", + "id": "recovery", + "next_action": "obtain approved crash/oracle fixture before claiming recovery closure", + "owner": "workspace-runtime", + "source_refs": [ + "coverage/recovery-offline-fault-matrix.report.json", + "coverage/workspace-recovery.report.json", + "coverage/wasm-canonical-state-t4.report.json" + ], + "status": "blocked", + "stop_reason": "SOURCE_ORACLE_BLOCKED: official recovery oracle and physical filesystem crash input are absent", + "tests": [ + [ + "node", + "web/worker/test_wasm_canonical_state_checkpoint.mjs" + ], + [ + "node", + "web/worker/test_recovery_offline_faults.mjs" + ], + [ + "node", + "web/worker/test_workspace_session_integration.mjs" + ] + ], + "title": "checkpoint/journal/migration/offline recovery closure", + "verified_slice": [ + "logical checkpoint/trace import", + "workspace migration/rollback", + "offline missing-resource diagnostics" + ] + }, + { + "capability": { + "authority": "dedicated_worker_protocol_v1", + "wasm": "verified", + "worker": "verified" + }, + "difference": "Chromium slice is verified; Firefox/Safari minimums and physical OPFS recovery are not", + "id": "browser_ux", + "next_action": "pin reviewed browser minimums and execute the same workflow/fault matrix in each engine", + "owner": "frontend-runtime", + "source_refs": [ + "coverage/browser-performance-matrix.report.json", + "coverage/gui-browser-visual-fe-m4.report.json", + "coverage/browser-matrix-remediation.report.json" + ], + "status": "blocked", + "stop_reason": "BROWSER_BLOCKED: minimum versions are TBD and Safari/physical OPFS are unavailable in the locked environment", + "tests": [ + [ + "node", + "web/worker/test_axis3_selected_case.mjs" + ], + [ + "node", + "web/worker/test_axis3_visible_completion.mjs" + ] + ], + "title": "browser UX, WebGL and multi-engine closure", + "verified_slice": [ + "Chromium Worker/React workflow", + "Three.js nonblank canvas", + "protocol error/recovery UX" + ] + } + ], + "next_transition": "T7-RELEASE-GATE-01 only after owner-approved source/oracle/resource/browser inputs", + "release_action": "STOP_RELEASE", + "report_sha256": "334d5262b111471a347f4a9d1646a9320111516cdf50c60671c65996bbe5e795", + "schema": "wasm-function-closure-t6.v1", + "status": "blocked", + "stop_reason": "all complete closure rows retain source/oracle/resource/host/browser blockers; no selected-case or JS authority promotion", + "task_id": "T6-FUNCTION-CLOSURE-01", + "tests": [ + { + "commands": [ + { + "command": [ + "node", + "web/worker/test_source_interpreter_s5.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "ddd5df1f071c70cb66f6caed3a1a516c5aaaa7663e4688020c460d586927d1c7" + }, + { + "command": [ + "node", + "web/worker/test_source_do_s7_01.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "84233a11d16f6915e6acbe3bca61324a694ca625a3f2ce159f01ca94f394496b" + }, + { + "command": [ + "node", + "web/worker/test_source_g6164_s7_02.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "d79b61dae572272413a54b0ccccd2008de1f20125c39b5dd30a1920351e21e4e" + } + ], + "domain": "rs274" + }, + { + "commands": [ + { + "command": [ + "node", + "web/worker/test_wasm_motion_authority.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "3e66880d4e8a0b6205a351178c0f68f4eae72094c78bde514eee0f45e66b25af" + }, + { + "command": [ + "node", + "web/worker/test_wasm_motion_checkpoint.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "3b24f305766e7b7442a30a6cfa60e46d0669689f4960d1a7b67c41878878f639" + }, + { + "command": [ + "node", + "web/worker/test_source_g6164_s7_02.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "d79b61dae572272413a54b0ccccd2008de1f20125c39b5dd30a1920351e21e4e" + } + ], + "domain": "tp" + }, + { + "commands": [ + { + "command": [ + "node", + "web/worker/test_authoritative_kinematics_s6_04.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "0251b25272a27dd98076036be908a71a187d56407fd96fd8489d13c849160d36" + }, + { + "command": [ + "node", + "web/worker/test_axis3_tutorial_motion.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "09875bd9491a95f43f8891a26663bb7be5704be84a75d686146c8c77ffb4ea94" + }, + { + "command": [ + "node", + "web/worker/test_wasm_canonical_state.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "2423ec3d9cbc8fca56010ba037ea85649e8b668ce4b4e20d585544cb87ecebe4" + } + ], + "domain": "kinematics" + }, + { + "commands": [ + { + "command": [ + "node", + "web/worker/test_hal_runtime.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "7dcb47ba0dcd86be421b1a3d133ed0b4951d2e710d7b518f0a4de74406f47227" + }, + { + "command": [ + "node", + "web/worker/test_device_io.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "f539ac2a59a40973439f44683e29e473139cd70d1524aac72f99b64689196098" + }, + { + "command": [ + "node", + "web/worker/test_io_ack.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "a277ff07898a13759e86587e59cd7826d8f2403d8fbaffb74e8d26f5e15b52e2" + }, + { + "command": [ + "node", + "web/worker/test_virtual_devices.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "daa4c0f7b69fac765297984aeb95539da4643416b73634f4773a9eba52dff8dc" + } + ], + "domain": "hal_io" + }, + { + "commands": [ + { + "command": [ + "node", + "web/worker/test_python_bridge.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "5a0211cf093d9c498f4ddf4009190833332ec1226310f5e5e2eace604a5e9060" + }, + { + "command": [ + "node", + "web/worker/test_python_callbacks_s3.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "bc8517bff512c618270adcc856801db822b9c7d3ce13cae5c249c8e39ee99e85" + }, + { + "command": [ + "node", + "web/worker/test_source_do_s7_01.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "84233a11d16f6915e6acbe3bca61324a694ca625a3f2ce159f01ca94f394496b" + } + ], + "domain": "python_remap" + }, + { + "commands": [ + { + "command": [ + "node", + "web/worker/test_configuration_startup_runtime.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "7956e3bac69532da76025282b51a6d63b903fc94a6791fb1f2f0af3a9d697537" + }, + { + "command": [ + "node", + "web/worker/test_configuration_preflight.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "1684f25cda88240f009a6c855ce2e50ccda42fb629d21e29a6d474880651e888" + } + ], + "domain": "configuration" + }, + { + "commands": [ + { + "command": [ + "node", + "web/worker/test_wasm_canonical_state_checkpoint.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "bc8689a3119a812d2e7fc59a56d0c39eaa9002fd51bc49455d41409fc3614166" + }, + { + "command": [ + "node", + "web/worker/test_recovery_offline_faults.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "7a62245e494eb4855f1668dab5f6577cba3e5b5e3d179798f33c0742ab5613e7" + }, + { + "command": [ + "node", + "web/worker/test_workspace_session_integration.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "0e1cad80e126a491b05503cd6c6c676b91167511f59c579f969adab51df64247" + } + ], + "domain": "recovery" + }, + { + "commands": [ + { + "command": [ + "node", + "web/worker/test_axis3_selected_case.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "dfb469ab07f3c6c33a0d5834d7857bb84daef79904d6e6f783bf90928b833674" + }, + { + "command": [ + "node", + "web/worker/test_axis3_visible_completion.mjs" + ], + "returncode": 0, + "status": "verified", + "stderr_sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdout_sha256": "25438e9c7c6d69b80191148e7a3ba9d46505384187899348c60f6dd0c9f1a39d" + } + ], + "domain": "browser_ux" + } + ] +} diff --git a/coverage/wasm-motion-authority-t3.report.json b/coverage/wasm-motion-authority-t3.report.json new file mode 100644 index 0000000..4427d8e --- /dev/null +++ b/coverage/wasm-motion-authority-t3.report.json @@ -0,0 +1,114 @@ +{ + "authority": { + "compatibility_runtime": "retained_non_selected_case_only", + "js_authoritative_runtime_calls": 0, + "motion_authority": "wasm", + "motion_source": "wasm_motion_queue" + }, + "case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", + "checkpoint": { + "continuous_recovery_endpoint_equal": true, + "controls": [ + "profile", + "lookahead", + "acceleration_limit", + "jerk_limit", + "paused" + ], + "damaged_state": "blocked", + "raw_queue_state": true, + "schema": "sim_motion_queue_state.v1" + }, + "completion": { + "abort_queue_depth": 0, + "program_complete_before_queue_drain": true, + "task_stopped_requires_queue_depth_zero": true + }, + "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", + "first_divergent_tick": null, + "inputs": { + "coverage/wasm-only-source-audit.report.json": "e62fecddd191827a08528ec5036e36addc85482ccf5c17066b6700e303c0f4fd", + "wasm-core/include/sim_motion_queue.h": "89903eda3c4c54e932c5351a3681c4dd1333808b893302a55c3cee31924d165c", + "wasm-core/src/sim_motion_queue.c": "094d28a21c8f88dbe981c49e7216aa6d88afc2afdd9e54326edd4cd417ba13ba", + "web/worker/authoritative-runtime.mjs": "c2bdd03de958ffda38553ec6dbddb4b1f5c08b2cb636ba02f25f2b76bdd61604", + "web/worker/program-runner.mjs": "30f053429f8396b8f845e5faabe9912cb34aedb9440fcd35d7f507ba7e8975a2", + "web/worker/sim-session.mjs": "e83eab02bca8f71615dae96c7aa71ad912c21353af9504b2f74cea6b3c13fa43", + "web/worker/wasm-motion-queue.mjs": "91984bc522a7545d19b929bea294b207926dee27fdeb4ca1dd97e12f927b87a6" + }, + "open_boundaries": [ + "non-selected-case motion authority remains compatibility_open", + "T4 task/motion/home/jog/limit/HAL/IO canonical state remains open", + "full kinematics_motion and full LinuxCNC parity remain blocked" + ], + "report_sha256": "fb8812a57d2d5fcc611d0388842a25502459fec8412affeecba020b560c8f498", + "schema": "wasm-motion-authority-t3.v1", + "status": "verified", + "tests": [ + { + "command": [ + "node", + "web/worker/test_wasm_motion_authority.mjs" + ], + "cwd": "/home/mes006/linuxcncWebWASM", + "output_sha256": "34f090eef3d78d77d192658fbc279f0bdd72fa038eef44cfdc0138d6068d3d8c", + "output_tail": "{\"schema\":\"wasm-motion-authority-t3.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"motion_authority\":\"wasm\",\"paused_position\":{\"x\":1.073312629199899,\"y\":0.5366563145999494,\"z\":0},\"resumed_position\":{\"x\":2.146625258399798,\"y\":1.073312629199899,\"z\":0},\"abort_queue_depth\":0,\"js_authority_calls\":0}\n", + "returncode": 0, + "status": "verified" + }, + { + "command": [ + "node", + "web/worker/test_wasm_motion_checkpoint.mjs" + ], + "cwd": "/home/mes006/linuxcncWebWASM", + "output_sha256": "585c73a4f82d9b3e50de6a72279c0b01d21109d482f2f2f708ce8f5062127a0e", + "output_tail": "{\"schema\":\"wasm-motion-checkpoint-t3.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"checkpoint_hash\":\"fnv1a64:47ff90f3ab07c4c6\",\"continuous_hash\":\"fnv1a64:2c910bab8ffdf73a\",\"recovered_hash\":\"fnv1a64:2c910bab8ffdf73a\",\"endpoint_equal\":true,\"trace_import\":\"verified\",\"checkpoint_queue_depth\":1,\"blocked_damaged_checkpoint\":\"blocked\"}\n", + "returncode": 0, + "status": "verified" + }, + { + "command": [ + "node", + "web/worker/test_axis3_selected_case.mjs" + ], + "cwd": "/home/mes006/linuxcncWebWASM", + "output_sha256": "2fac97a3555e431b1e836727ac5315c684e73aae9dea9d40472dd13f098b1963", + "output_tail": "s-tutorial/3axis-tutorial.hal\",\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial\",\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial_mm.var\",\"linuxcnc/configs/nc_files/3dtest.ngc\",\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/tool.tbl\",\"linuxcnc/linuxcnc.gif\"],\"loader\":{\"fetched\":7,\"missing\":0,\"state_hash\":\"fnv1a64:94e21a5c7a684e63\"},\"startup\":{\"state\":\"TASK_READY\",\"hal_blocked\":0},\"operation_sequence\":[\"HOME\",\"JOG_START\",\"SIM_STEP\",\"JOG_STOP\",\"SET_OVERRIDE\",\"PROGRAM_LOAD\",\"PROGRAM_RUN\",\"CHECKPOINT\",\"SPINDLE_COMMAND\",\"COOLANT_COMMAND\",\"TOOL_PREPARE\",\"TOOL_CHANGE\",\"ESTOP\",\"IO_RECOVER\",\"RECOVER\"],\"observed\":{\"jog_x\":0.016,\"program_status\":\"completed\",\"checkpoint_hash\":\"fnv1a64:048eca9e5dd4e2df\",\"recovery\":\"recovered\",\"final_hash\":\"fnv1a64:4509e2eaf38657d7\"}}\n", + "returncode": 0, + "status": "verified" + }, + { + "command": [ + "node", + "web/worker/test_axis3_visible_completion.mjs" + ], + "cwd": "/home/mes006/linuxcncWebWASM", + "output_sha256": "621f5980f9ee4c56dafd57e108d3dd1c410aa77e8716c5b574cf887875e0a52e", + "output_tail": "{\"schema\":\"axis3-visible-completion.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"final_pose\":{\"x\":0,\"y\":0,\"z\":0},\"task_state\":\"STOPPED\",\"program_status\":\"completed\"}\n", + "returncode": 0, + "status": "verified" + }, + { + "command": [ + "node", + "web/worker/test_axis3_tutorial_persistence.mjs" + ], + "cwd": "/home/mes006/linuxcncWebWASM", + "output_sha256": "9ce3fc6579febc30a2a01bdcf451142e8ad4b2fdb27812380c7c17874dbb0858", + "output_tail": "{\"schema\":\"axis3-tutorial-persistence.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"exact_runtime_files\":[\"3axis-tutorial_mm.var\",\"tool.tbl\",\"position_mm.txt\"],\"checkpoint_hash\":\"fnv1a64:0c1d1b22db211940\",\"continuous_hash\":\"fnv1a64:9a5dd34aba49674c\",\"recovered_hash\":\"fnv1a64:9a5dd34aba49674c\",\"endpoint_equal\":true,\"blocked_bad_checkpoint\":\"blocked\",\"event_cursor\":6}\n", + "returncode": 0, + "status": "verified" + }, + { + "command": [ + "node", + "web/worker/test_axis3_tutorial_program.mjs" + ], + "cwd": "/home/mes006/linuxcncWebWASM", + "output_sha256": "c08d5b1b2c4102aba1b2eb67c474d98425b00676aa766f0c0a31229bfdd0f51f", + "output_tail": "{\"schema\":\"axis3-tutorial-program.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"units\":\"G21\",\"distance_modes\":[\"G90\",\"G91\"],\"run\":{\"paused\":true,\"resumed\":true,\"final\":\"completed\",\"state\":\"STOPPED\"},\"mdi\":{\"blocked_while_paused\":\"mdi_while_program_active\",\"status\":\"verified\",\"distance_mode\":90,\"position\":{\"x\":2,\"y\":3,\"z\":-1}},\"restart\":\"loaded\",\"step\":\"verified\",\"invalid_code\":\"program_interpreter\",\"final_hash\":\"fnv1a64:35cfce230eb7f9ee\"}\n", + "returncode": 0, + "status": "verified" + } + ] +} diff --git a/coverage/wasm-only-source-audit.report.json b/coverage/wasm-only-source-audit.report.json new file mode 100644 index 0000000..bbe4f1d --- /dev/null +++ b/coverage/wasm-only-source-audit.report.json @@ -0,0 +1,525 @@ +{ + "baseline": [ + { + "command": [ + "python3", + "scripts/test_axis3_tutorial_preview.py" + ], + "cwd": "/home/mes006/linuxcncWebWASM", + "name": "preview", + "output_sha256": "99849d55e84d7006ae3b4b7fd4779ef7fb5d160bb52a489ed71f83708fb65052", + "output_tail": "{\"status\": \"verified\", \"program\": \"/home/mes006/linuxcncWebWASM/linuxcnc/nc_files/3dtest.ngc\", \"preview\": {\"schema\": \"wasm-preview.v1\", \"status\": \"verified\", \"source_path\": \"linuxcnc/nc_files/3dtest.ngc\", \"source_lines\": 55, \"segment_count\": 49, \"arc_lines\": [6, 23, 39], \"source\": \"wasm_interpreter_canonical_motion\"}}\n", + "returncode": 0, + "status": "verified" + }, + { + "command": [ + "node", + "web/worker/test_axis3_selected_case.mjs" + ], + "cwd": "/home/mes006/linuxcncWebWASM", + "name": "selected_case_worker", + "output_sha256": "2fac97a3555e431b1e836727ac5315c684e73aae9dea9d40472dd13f098b1963", + "output_tail": "{\"schema\":\"axis3-selected-case-parity.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"machine\":{\"type\":\"cartesian_vismach\",\"axes\":[\"X\",\"Y\",\"Z\"],\"joints\":3,\"kinematics\":\"trivkins\",\"model\":\"3axis-tutorial\"},\"exact_files\":[\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal\",\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial\",\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial_mm.var\",\"linuxcnc/configs/nc_files/3dtest.ngc\",\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/tool.tbl\",\"linuxcnc/linuxcnc.gif\"],\"loader\":{\"fetched\":7,\"missing\":0,\"state_hash\":\"fnv1a64:94e21a5c7a684e63\"},\"startup\":{\"state\":\"TASK_READY\",\"hal_blocked\":0},\"operation_sequence\":[\"HOME\",\"JOG_START\",\"SIM_STEP\",\"JOG_STOP\",\"SET_OVERRIDE\",\"PROGRAM_LOAD\",\"PROGRAM_RUN\",\"CHECKPOINT\",\"SPINDLE_COMMAND\",\"COOLANT_COMMAND\",\"TOOL_PREPARE\",\"TOOL_CHANGE\",\"ESTOP\",\"IO_RECOVER\",\"RECOVER\"],\"observed\":{\"jog_x\":0.016,\"program_status\":\"completed\",\"checkpoint_hash\":\"fnv1a64:048eca9e5dd4e2df\",\"recovery\":\"recovered\",\"final_hash\":\"fnv1a64:4509e2eaf38657d7\"}}\n", + "returncode": 0, + "status": "verified" + }, + { + "command": [ + "node", + "web/worker/test_axis3_visible_completion.mjs" + ], + "cwd": "/home/mes006/linuxcncWebWASM", + "name": "visible_completion_worker", + "output_sha256": "621f5980f9ee4c56dafd57e108d3dd1c410aa77e8716c5b574cf887875e0a52e", + "output_tail": "{\"schema\":\"axis3-visible-completion.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"final_pose\":{\"x\":0,\"y\":0,\"z\":0},\"task_state\":\"STOPPED\",\"program_status\":\"completed\"}\n", + "returncode": 0, + "status": "verified" + }, + { + "command": [ + "node", + "web/worker/test_wasm_motion_event.mjs" + ], + "cwd": "/home/mes006/linuxcncWebWASM", + "name": "wasm_motion_event", + "output_sha256": "d2dbbe772565019da46acde28215f48c5ed3c58f5695d23f91ea2c7ee98d7106", + "output_tail": "{\"schema\":\"wasm-motion-event.v1\",\"status\":\"verified\",\"events\":3,\"last_motion\":{\"source_line\":4,\"start\":{\"x\":10,\"y\":5,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"end\":{\"x\":10,\"y\":5,\"z\":0,\"a\":0,\"b\":0,\"c\":0,\"u\":0,\"v\":0,\"w\":0},\"center\":{\"x\":11,\"y\":6,\"z\":0},\"normal\":{\"x\":0,\"y\":0,\"z\":1},\"velocity\":120,\"acceleration\":0,\"motion_type\":2,\"turn\":-1,\"source\":\"wasm_interpreter_canonical_motion\"}}\n", + "returncode": 0, + "status": "verified" + }, + { + "command": [ + "node", + "web/worker/test_wasm_motion_authority.mjs" + ], + "cwd": "/home/mes006/linuxcncWebWASM", + "name": "wasm_motion_authority", + "output_sha256": "34f090eef3d78d77d192658fbc279f0bdd72fa038eef44cfdc0138d6068d3d8c", + "output_tail": "{\"schema\":\"wasm-motion-authority-t3.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"motion_authority\":\"wasm\",\"paused_position\":{\"x\":1.073312629199899,\"y\":0.5366563145999494,\"z\":0},\"resumed_position\":{\"x\":2.146625258399798,\"y\":1.073312629199899,\"z\":0},\"abort_queue_depth\":0,\"js_authority_calls\":0}\n", + "returncode": 0, + "status": "verified" + }, + { + "command": [ + "node", + "web/worker/test_wasm_motion_checkpoint.mjs" + ], + "cwd": "/home/mes006/linuxcncWebWASM", + "name": "wasm_motion_checkpoint", + "output_sha256": "585c73a4f82d9b3e50de6a72279c0b01d21109d482f2f2f708ce8f5062127a0e", + "output_tail": "{\"schema\":\"wasm-motion-checkpoint-t3.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"checkpoint_hash\":\"fnv1a64:47ff90f3ab07c4c6\",\"continuous_hash\":\"fnv1a64:2c910bab8ffdf73a\",\"recovered_hash\":\"fnv1a64:2c910bab8ffdf73a\",\"endpoint_equal\":true,\"trace_import\":\"verified\",\"checkpoint_queue_depth\":1,\"blocked_damaged_checkpoint\":\"blocked\"}\n", + "returncode": 0, + "status": "verified" + }, + { + "command": [ + "node", + "web/worker/test_wasm_canonical_state.mjs" + ], + "cwd": "/home/mes006/linuxcncWebWASM", + "name": "wasm_canonical_state", + "output_sha256": "48b41b755925d7065f99db25e11f042789f917d15107de23c3ef980c9f2978ba", + "output_tail": "{\"schema\":\"wasm-canonical-state-t4.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"task_authority\":\"wasm_task_canonical\",\"home_authority\":\"wasm_home_limit\",\"home_sequence\":5,\"home_tick\":1,\"motion_tick\":3,\"home_limit_tick\":6,\"js_task_tamper_ignored\":true}\n", + "returncode": 0, + "status": "verified" + }, + { + "command": [ + "node", + "web/worker/test_wasm_canonical_state_checkpoint.mjs" + ], + "cwd": "/home/mes006/linuxcncWebWASM", + "name": "wasm_canonical_state_checkpoint", + "output_sha256": "401cfde5f23e77d8d938689c3a3ac6fe7777bafb118d3075ff353e99993ccdf7", + "output_tail": "{\"schema\":\"wasm-canonical-state-checkpoint-t4.v1\",\"status\":\"verified\",\"case\":\"linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini\",\"checkpoint_hash\":\"fnv1a64:9d9ea2a624703b46\",\"continuous_hash\":\"fnv1a64:2d696fc22acbbd38\",\"recovered_hash\":\"fnv1a64:2d696fc22acbbd38\",\"endpoint_equal\":true,\"trace_import\":\"verified\",\"damaged_home_state\":\"blocked\"}\n", + "returncode": 0, + "status": "verified" + }, + { + "command": [ + "npm", + "run", + "typecheck" + ], + "cwd": "/home/mes006/linuxcncWebWASM/web/react", + "name": "react_typecheck", + "output_sha256": "08fb3e3d0d9396a56f8720eb15d9f6920f8a30c2dad7b0b6672ac446801f27e3", + "output_tail": "\n> linuxcnc-web-wasm-react@0.1.0 typecheck\n> tsc --noEmit\n\n", + "returncode": 0, + "status": "verified" + } + ], + "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" + ], + "js_authority_remaining": [ + { + "path": "web/worker/authoritative-runtime.mjs", + "reason": "compatibility planner and pose mirror for non-selected cases", + "sha256": "c2bdd03de958ffda38553ec6dbddb4b1f5c08b2cb636ba02f25f2b76bdd61604", + "status": "compatibility_open" + }, + { + "path": "web/worker/program-runner.mjs", + "reason": "program scheduling and control flow mirror; canonical segments enter WASM queue", + "sha256": "30f053429f8396b8f845e5faabe9912cb34aedb9440fcd35d7f507ba7e8975a2", + "status": "open" + }, + { + "path": "web/worker/motion-state.mjs", + "reason": "home/jog/limit projection pending T4 canonical state", + "sha256": "f22d4bb1a3993f29e923b9f59987ae6599ddfe0dcdbe7fcc171e899480fae851", + "status": "open" + }, + { + "path": "web/worker/sim-task.mjs", + "reason": "task state mirror pending T4 canonical state", + "sha256": "4b4b3aa4b86aba08e5afb8534d98d6ab11c845eeb891cf4793b5906a01de1599", + "status": "open" + } + ], + "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" + ], + "report_sha256": "77d15a3d51d9f14449c9cf31ef18249574b19ad7584f6c2b4f77c5c055d3daae", + "schema": "wasm-only-source-audit.v1", + "selected_case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini", + "selected_case_checkpoint_restore": "verified", + "selected_case_home_limit_authority": "wasm_home_limit", + "selected_case_motion_authority": "verified", + "selected_case_motion_source": "wasm_motion_queue", + "selected_case_task_authority": "wasm_task_canonical", + "source_of_truth_target": "linuxcnc_source_to_wasm_core_only", + "status": "blocked", + "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", + "wasm_exports": { + "count": 207, + "domains": { + "hal_io": [ + "sim_device_ack_abort", + "sim_device_ack_ack", + "sim_device_ack_issue", + "sim_device_ack_step", + "sim_device_canonical", + "sim_device_coolant_command", + "sim_device_coolant_set_fault", + "sim_device_door_set", + "sim_device_encoder_set_config", + "sim_device_encoder_set_index_enable", + "sim_device_encoder_set_speed", + "sim_device_estop_set", + "sim_device_init", + "sim_device_interlock_reset", + "sim_device_interlock_set", + "sim_device_reset", + "sim_device_state_hash", + "sim_device_step", + "sim_hal_component_count", + "sim_hal_connect", + "sim_hal_function_calls", + "sim_hal_function_count", + "sim_hal_get_param", + "sim_hal_get_pin", + "sim_hal_param_count", + "sim_hal_pin_count", + "sim_hal_register_component", + "sim_hal_register_function", + "sim_hal_register_pin", + "sim_hal_register_thread", + "sim_hal_reset", + "sim_hal_set_function_failure", + "sim_hal_set_param", + "sim_hal_set_pin", + "sim_hal_step", + "sim_hal_thread_count", + "sim_hal_tick", + "sim_io_canonical", + "sim_io_init", + "sim_io_probe_arm", + "sim_io_probe_disarm", + "sim_io_probe_update", + "sim_io_read_state", + "sim_io_recover", + "sim_io_register_pins", + "sim_io_reset", + "sim_io_set_estop", + "sim_io_spindle_command", + "sim_io_spindle_orient", + "sim_io_spindle_set_fault", + "sim_io_spindle_set_feedback", + "sim_io_spindle_set_index_enable", + "sim_io_spindle_set_inhibit", + "sim_io_spindle_set_oriented", + "sim_io_state_hash", + "sim_io_step", + "sim_io_tool_abort", + "sim_io_tool_change", + "sim_io_tool_clear_fault", + "sim_io_tool_prepare", + "sim_io_tool_set_changed", + "sim_io_tool_set_fault", + "sim_io_tool_set_prepared" + ], + "interpreter": [ + "sim_interpreter_block_hash", + "sim_interpreter_canonical", + "sim_interpreter_error_code", + "sim_interpreter_error_column", + "sim_interpreter_execute", + "sim_interpreter_init", + "sim_interpreter_modal_hash", + "sim_interpreter_motion_size", + "sim_interpreter_parameter_get", + "sim_interpreter_parameter_set", + "sim_interpreter_read_motion", + "sim_interpreter_read_state", + "sim_interpreter_reset", + "sim_interpreter_size", + "sim_interpreter_state_hash" + ], + "motion": [ + "sim_interpreter_motion_size", + "sim_interpreter_read_motion", + "sim_kinematics_motion_depth", + "sim_kinematics_motion_enqueue", + "sim_kinematics_motion_init", + "sim_kinematics_motion_pause", + "sim_kinematics_motion_peek", + "sim_kinematics_motion_pop", + "sim_kinematics_motion_reset", + "sim_kinematics_motion_state_hash", + "sim_kinematics_motion_step", + "sim_motion_canonical", + "sim_motion_command_size", + "sim_motion_enqueue", + "sim_motion_init", + "sim_motion_peek", + "sim_motion_plan_hash", + "sim_motion_queue_depth", + "sim_motion_read_state", + "sim_motion_reset", + "sim_motion_set_lookahead", + "sim_motion_set_profile", + "sim_motion_size", + "sim_motion_state_hash", + "sim_motion_state_size", + "sim_motion_step", + "sim_tp_enqueue", + "sim_tp_init", + "sim_tp_read_state", + "sim_tp_reset", + "sim_tp_state_hash", + "sim_tp_step" + ], + "task_scheduler": [ + "sim_scheduler_canonical", + "sim_scheduler_init", + "sim_scheduler_read", + "sim_scheduler_reset", + "sim_scheduler_restore", + "sim_scheduler_size", + "sim_scheduler_state_hash", + "sim_scheduler_step", + "sim_scheduler_tick", + "sim_task_attach", + "sim_task_canonical", + "sim_task_estop", + "sim_task_fault", + "sim_task_init", + "sim_task_read_state", + "sim_task_recover", + "sim_task_reset", + "sim_task_restore", + "sim_task_set_command", + "sim_task_set_mode", + "sim_task_set_program", + "sim_task_set_tick", + "sim_task_size", + "sim_task_state_hash", + "sim_task_transition" + ] + }, + "manifest": "/home/mes006/linuxcncWebWASM/build-wasm/build.ninja", + "manifest_sha256": "ab5d54956c30989bad2b1eeed28092454b77f1e3c7474dcfe495bde8fd65a5de", + "names": [ + "sim_command", + "sim_create", + "sim_destroy", + "sim_device_ack_abort", + "sim_device_ack_ack", + "sim_device_ack_issue", + "sim_device_ack_step", + "sim_device_canonical", + "sim_device_coolant_command", + "sim_device_coolant_set_fault", + "sim_device_door_set", + "sim_device_encoder_set_config", + "sim_device_encoder_set_index_enable", + "sim_device_encoder_set_speed", + "sim_device_estop_set", + "sim_device_init", + "sim_device_interlock_reset", + "sim_device_interlock_set", + "sim_device_reset", + "sim_device_state_hash", + "sim_device_step", + "sim_event_append", + "sim_event_count", + "sim_event_cursor", + "sim_event_export", + "sim_event_import", + "sim_event_read", + "sim_event_reset", + "sim_export_state", + "sim_hal_component_count", + "sim_hal_connect", + "sim_hal_function_calls", + "sim_hal_function_count", + "sim_hal_get_param", + "sim_hal_get_pin", + "sim_hal_param_count", + "sim_hal_pin_count", + "sim_hal_register_component", + "sim_hal_register_function", + "sim_hal_register_pin", + "sim_hal_register_thread", + "sim_hal_reset", + "sim_hal_set_function_failure", + "sim_hal_set_param", + "sim_hal_set_pin", + "sim_hal_step", + "sim_hal_thread_count", + "sim_hal_tick", + "sim_import_state", + "sim_interpreter_block_hash", + "sim_interpreter_canonical", + "sim_interpreter_error_code", + "sim_interpreter_error_column", + "sim_interpreter_execute", + "sim_interpreter_init", + "sim_interpreter_modal_hash", + "sim_interpreter_motion_size", + "sim_interpreter_parameter_get", + "sim_interpreter_parameter_set", + "sim_interpreter_read_motion", + "sim_interpreter_read_state", + "sim_interpreter_reset", + "sim_interpreter_size", + "sim_interpreter_state_hash", + "sim_io_canonical", + "sim_io_init", + "sim_io_probe_arm", + "sim_io_probe_disarm", + "sim_io_probe_update", + "sim_io_read_state", + "sim_io_recover", + "sim_io_register_pins", + "sim_io_reset", + "sim_io_set_estop", + "sim_io_spindle_command", + "sim_io_spindle_orient", + "sim_io_spindle_set_fault", + "sim_io_spindle_set_feedback", + "sim_io_spindle_set_index_enable", + "sim_io_spindle_set_inhibit", + "sim_io_spindle_set_oriented", + "sim_io_state_hash", + "sim_io_step", + "sim_io_tool_abort", + "sim_io_tool_change", + "sim_io_tool_clear_fault", + "sim_io_tool_prepare", + "sim_io_tool_set_changed", + "sim_io_tool_set_fault", + "sim_io_tool_set_prepared", + "sim_kinematics_feedback_init", + "sim_kinematics_feedback_read", + "sim_kinematics_feedback_reset", + "sim_kinematics_feedback_state_hash", + "sim_kinematics_feedback_update", + "sim_kinematics_forward", + "sim_kinematics_forward_ex", + "sim_kinematics_home", + "sim_kinematics_home_limit_check", + "sim_kinematics_home_limit_home", + "sim_kinematics_home_limit_init", + "sim_kinematics_home_limit_read", + "sim_kinematics_home_limit_reset", + "sim_kinematics_home_limit_state_hash", + "sim_kinematics_init", + "sim_kinematics_inverse", + "sim_kinematics_inverse_ex", + "sim_kinematics_motion_depth", + "sim_kinematics_motion_enqueue", + "sim_kinematics_motion_init", + "sim_kinematics_motion_pause", + "sim_kinematics_motion_peek", + "sim_kinematics_motion_pop", + "sim_kinematics_motion_reset", + "sim_kinematics_motion_state_hash", + "sim_kinematics_motion_step", + "sim_kinematics_registry_count", + "sim_kinematics_registry_find", + "sim_kinematics_registry_get", + "sim_kinematics_registry_require", + "sim_kinematics_registry_state_hash", + "sim_kinematics_registry_validate_config", + "sim_kinematics_state_hash", + "sim_kinematics_switch_abort", + "sim_kinematics_switch_active", + "sim_kinematics_switch_begin", + "sim_kinematics_switch_commit", + "sim_kinematics_switch_forward", + "sim_kinematics_switch_init", + "sim_kinematics_switch_inverse", + "sim_kinematics_switch_register", + "sim_kinematics_switch_select", + "sim_kinematics_switch_state_hash", + "sim_kinematics_user_bind_switch", + "sim_kinematics_user_forward", + "sim_kinematics_user_init", + "sim_kinematics_user_inverse", + "sim_kinematics_user_reject_dynamic", + "sim_kinematics_user_state_hash", + "sim_kinematics_validate_pose", + "sim_load_workspace", + "sim_motion_canonical", + "sim_motion_command_size", + "sim_motion_enqueue", + "sim_motion_init", + "sim_motion_peek", + "sim_motion_plan_hash", + "sim_motion_queue_depth", + "sim_motion_read_state", + "sim_motion_reset", + "sim_motion_set_lookahead", + "sim_motion_set_profile", + "sim_motion_size", + "sim_motion_state_hash", + "sim_motion_state_size", + "sim_motion_step", + "sim_posemath_dot", + "sim_posemath_hash_state", + "sim_posemath_is_finite", + "sim_posemath_line_point", + "sim_posemath_normalize", + "sim_posemath_pose_is_finite", + "sim_posemath_pose_line_point", + "sim_posemath_segment_point", + "sim_posemath_segment_validate", + "sim_protocol_accept", + "sim_protocol_last_seq", + "sim_protocol_pause", + "sim_protocol_reset", + "sim_protocol_resume", + "sim_protocol_set_tick", + "sim_protocol_state_value", + "sim_protocol_tick", + "sim_read_event", + "sim_read_snapshot", + "sim_scheduler_canonical", + "sim_scheduler_init", + "sim_scheduler_read", + "sim_scheduler_reset", + "sim_scheduler_restore", + "sim_scheduler_size", + "sim_scheduler_state_hash", + "sim_scheduler_step", + "sim_scheduler_tick", + "sim_step", + "sim_task_attach", + "sim_task_canonical", + "sim_task_estop", + "sim_task_fault", + "sim_task_init", + "sim_task_read_state", + "sim_task_recover", + "sim_task_reset", + "sim_task_restore", + "sim_task_set_command", + "sim_task_set_mode", + "sim_task_set_program", + "sim_task_set_tick", + "sim_task_size", + "sim_task_state_hash", + "sim_task_transition", + "sim_tp_enqueue", + "sim_tp_init", + "sim_tp_read_state", + "sim_tp_reset", + "sim_tp_state_hash", + "sim_tp_step" + ] + } +} diff --git a/coverage/wasm-simulation-release-gate-t7.report.json b/coverage/wasm-simulation-release-gate-t7.report.json new file mode 100644 index 0000000..1a4d182 --- /dev/null +++ b/coverage/wasm-simulation-release-gate-t7.report.json @@ -0,0 +1,229 @@ +{ + "blockers": [ + { + "id": "t6_function_closure_blocked", + "reason": "all complete closure rows retain source/oracle/resource/host/browser blockers; no selected-case or JS authority promotion", + "status": "blocked" + }, + { + "count": 6249, + "id": "manifest_seed_unassigned", + "reason": "manifest seed entries still have no feature owner", + "status": "blocked" + }, + { + "count": 0, + "id": "accepted_entries_missing", + "reason": "no manifest entry is accepted for release", + "status": "blocked" + }, + { + "count": 1, + "id": "official_oracle_missing", + "reason": "locked source has no official kinematics/recovery oracle root", + "status": "blocked" + }, + { + "count": 997, + "id": "configuration_resources_missing", + "reason": "configuration startup/resource gaps remain", + "status": "blocked" + }, + { + "count": 1, + "id": "browser_matrix_unverified", + "reason": "Chromium/Firefox/Safari minimum versions remain TBD", + "status": "blocked" + }, + { + "count": 1, + "id": "license_closure_incomplete", + "reason": "57 Web/WASM entries have no approved SPDX/NOTICE assignment", + "status": "blocked" + }, + { + "id": "owner_inputs_missing", + "reason": "six owner decisions or a new reviewed context are absent", + "status": "blocked" + } + ], + "candidate_eligible": false, + "checks": { + "blocker_identity": { + "ids": [ + "accepted_entries_missing", + "browser_matrix_unverified", + "configuration_resources_missing", + "license_closure_incomplete", + "manifest_seed_unassigned", + "official_oracle_missing" + ], + "status": "verified" + }, + "candidate_not_eligible": { + "status": "verified" + }, + "context_consistency": { + "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", + "status": "verified" + }, + "gate_mutation_forbidden": { + "policy": "this report never writes release-candidate-gate, context-lock, manifest or owner decisions", + "status": "verified" + }, + "input_closure": { + "missing": [], + "status": "verified" + }, + "owner_refresh_blocked": { + "status": "verified" + }, + "remediation_self_hashes": { + "status": "verified" + }, + "stop_release_preserved": { + "release_action": "STOP_RELEASE", + "status": "verified" + }, + "t6_dependency_recorded": { + "closure_status": "blocked", + "status": "verified" + } + }, + "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", + "failure_samples": [ + { + "code": "t6_closure_blocked", + "observed": { + "blocked": 8, + "rows": 8, + "test_blocked": 0, + "test_commands": 23, + "verified_slices": 8 + }, + "source": "coverage/wasm-function-closure-t6.report.json" + }, + { + "code": "owner_inputs_missing", + "observed": { + "present": 0, + "required": 6 + }, + "source": "coverage/owner-review-gate-refresh.report.json" + }, + { + "code": "release_blocker_identity_preserved", + "observed": [ + "accepted_entries_missing", + "browser_matrix_unverified", + "configuration_resources_missing", + "license_closure_incomplete", + "manifest_seed_unassigned", + "official_oracle_missing" + ], + "source": "coverage/release-candidate-gate.report.json" + } + ], + "gate_mutation": "forbidden", + "gate_snapshot": { + "blocker_count": 6, + "candidate_eligible": false, + "path": "coverage/release-candidate-gate.report.json", + "release_action": "STOP_RELEASE", + "status": "blocked" + }, + "implementation_status": "verified", + "inputs": [ + { + "bytes": 7647, + "path": "context-lock.json", + "role": "context_lock", + "sha256": "bf6651f8d1803f2219d40e61f5e86dcf0821862d087eec2e19f99f593561353e", + "status": "present" + }, + { + "bytes": 5600, + "path": "nextTask.md", + "role": "owner_pointer_read_only", + "sha256": "d1d436f32435c7d46fa638e3345d0eb009bd0f6dac06db79735a12c3a46dbeaf", + "status": "present" + }, + { + "bytes": 30290, + "path": "coverage/wasm-function-closure-t6.report.json", + "role": "t6_closure", + "sha256": "05862b956517047081f7a596e76b4a0da319f201b0649b6d66b4fe380950f83c", + "status": "present" + }, + { + "bytes": 42079, + "path": "coverage/wasm-feature-matrix-t5.report.json", + "role": "t5_matrix", + "sha256": "d5082c9f181e905563a9e8430de3d4d04af66059affd15dd3bd7b2e2df5b7ea8", + "status": "present" + }, + { + "bytes": 7196, + "path": "coverage/release-candidate-gate.report.json", + "role": "release_gate_read_only", + "sha256": "ff46025b4d03a4ab002d22a4f6101c213a702ff6b36dc688274304f79ef8a7a8", + "status": "present" + }, + { + "bytes": 87951, + "path": "coverage/full-case-acceptance-s8-10.report.json", + "role": "case_acceptance", + "sha256": "a09f701272ebb44aeb1d0cc42ce7588765affeb66386fe8f549e8984a0b9fe55", + "status": "present" + }, + { + "bytes": 9239, + "path": "coverage/remediation-evidence-roundup.report.json", + "role": "remediation_roundup", + "sha256": "3a23cc0ab518ae15e3a4f71a1d593170e84d3319d5870224f89cc589b08e7645", + "status": "present" + }, + { + "bytes": 3467, + "path": "coverage/owner-review-gate-refresh.report.json", + "role": "owner_gate_read_only", + "sha256": "88cc9b27b4551c13b066d0af11910cf317ed4c21667d95e86e644167eefa5bec", + "status": "present" + }, + { + "bytes": 5541, + "path": "coverage/official-oracle-remediation.report.json", + "role": "oracle_remediation", + "sha256": "97da275e5b8f731dd7c7ab95759583dd919348d84474594604da83fbe57479cc", + "status": "present" + }, + { + "bytes": 827383, + "path": "coverage/configuration-resource-remediation.report.json", + "role": "resource_remediation", + "sha256": "8036cb8bc63957a82eaed6fbb249bf61b9fd9ab136e746a47e8e7f48a9a2f21c", + "status": "present" + }, + { + "bytes": 6180, + "path": "coverage/browser-matrix-remediation.report.json", + "role": "browser_remediation", + "sha256": "8eacc6c6e168df19fc2082c1788ce3e5c025e9b528cccc0c5dcf62a84a8664d7", + "status": "present" + }, + { + "bytes": 41237, + "path": "coverage/implementation-license-remediation.report.json", + "role": "license_remediation", + "sha256": "e23bf87de27f1367fe77e24c1d35a1a414258ccd4213ab7b71e108ed59487abf", + "status": "present" + } + ], + "next_transition": "wait for approved owner/source/browser/resource inputs; do not auto-refresh K8 gate", + "release_action": "STOP_RELEASE", + "report_sha256": "4e7cc4e7006fbdcd4698a1304b6ea9e688b18d2abf506731fbf8d0c9e2edd19b", + "schema": "wasm-simulation-release-gate-t7.v1", + "scope": "simulation_track_read_only_audit", + "status": "blocked", + "task_id": "T7-RELEASE-GATE-01" +} diff --git a/coverage/workspace-runtime.report.json b/coverage/workspace-runtime.report.json index d94c2b8..b2b5346 100644 --- a/coverage/workspace-runtime.report.json +++ b/coverage/workspace-runtime.report.json @@ -25,7 +25,7 @@ "bytes": 3841, "path": "coverage/sim-runner.report.json", "role": "runner_report", - "sha256": "6705c9f8052b1feba970f3b29815e1ab2af8c02c4188d7967885542e94d1316e", + "sha256": "36428641d5a6ed326374515d0364d69e2d818f28cffce4731f92de26da5f7a4f", "status": "present" }, { @@ -60,7 +60,7 @@ ], "release_action": "STOP_RELEASE", "release_status": "blocked_external_owner", - "report_sha256": "35e6659c1b1eab95386af954327a779e83a12cada82aa38341e479133818c28e", + "report_sha256": "6512cb4b762a06a19352d2c791b8b800cb25d5e68ff642b2cc6c47170f846377", "schema": "workspace-runtime-report.v1", "status": "passed", "task_id": "P2-01", diff --git a/coverage/workspace-session-integration.report.json b/coverage/workspace-session-integration.report.json index ce7af24..5b71c08 100644 --- a/coverage/workspace-session-integration.report.json +++ b/coverage/workspace-session-integration.report.json @@ -15,8 +15,8 @@ "status": "verified" }, "package_round_trip": { - "package_v1_hash": "fnv1a64:c8f4e248d7f64602", - "package_v2_hash": "fnv1a64:3e075699c047abe7", + "package_v1_hash": "fnv1a64:d9aa9dc670baec56", + "package_v2_hash": "fnv1a64:e5b1e118c32572b9", "status": "verified" }, "recovery_migration_dependency": { @@ -30,8 +30,8 @@ "status": "verified" }, "session_state_recovery": { - "recovery_state_hash": "fnv1a64:62481fd6982a0dae", - "session_state_hash": "fnv1a64:62481fd6982a0dae", + "recovery_state_hash": "fnv1a64:59fb9e23862123c3", + "session_state_hash": "fnv1a64:59fb9e23862123c3", "status": "verified" } }, @@ -94,19 +94,19 @@ "repeat_runs": 2 }, "release_action": "STOP_RELEASE", - "report_sha256": "0fd233ac461ea14bc51fce2e7d81385b702bb9cf932d871568eb8ad630dc85c1", + "report_sha256": "d203f0b8ed9bed54ec3949074ffa28525dfc1b94ac402274f34748e62962b8f9", "sample": { - "checkpoint_hash": "fnv1a64:c2382bab031cd9ab", + "checkpoint_hash": "fnv1a64:a0b90768e25b6aee", "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", "event_cursor": 12, "journal_hash": "fnv1a64:1064d5ed6ce0552a", "operation_count": 4, - "package_v1_hash": "fnv1a64:c8f4e248d7f64602", - "package_v2_hash": "fnv1a64:3e075699c047abe7", - "recovery_state_hash": "fnv1a64:62481fd6982a0dae", + "package_v1_hash": "fnv1a64:d9aa9dc670baec56", + "package_v2_hash": "fnv1a64:e5b1e118c32572b9", + "recovery_state_hash": "fnv1a64:59fb9e23862123c3", "round_trip_equal": true, "schema": "workspace-session-integration-sample.v1", - "session_state_hash": "fnv1a64:62481fd6982a0dae" + "session_state_hash": "fnv1a64:59fb9e23862123c3" }, "schema": "workspace-session-integration.v1", "status": "partial", diff --git a/docs/linuxcnc-web-wasm-3axis-tutorial-control-panel-implementation.md b/docs/linuxcnc-web-wasm-3axis-tutorial-control-panel-implementation.md new file mode 100644 index 0000000..b06fef7 --- /dev/null +++ b/docs/linuxcnc-web-wasm-3axis-tutorial-control-panel-implementation.md @@ -0,0 +1,285 @@ +# 3axis Tutorial Web 控制面板源程序对标实施方案 + +## 目标 + +为固定案例 `linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini` 建立与 LinuxCNC AXIS 控制面板可观察行为一致的 Web 控制面板。 + +唯一运行时数据来源是 LinuxCNC 源码编译得到的 WASM core,以及由 Dedicated Worker 投影的 authoritative snapshot/event。React 只能发送命令、显示状态和呈现诊断;不得在浏览器端重新推导任务状态、运动轨迹、DRO、工具状态、门禁或错误结果。 + +本方案是固定案例的控制面板实施合同,不宣称全量 LinuxCNC、实时硬件或 release candidate。机器可读的源语义规则见 [`coverage/linuxcnc-source-semantics-contract.json`](../coverage/linuxcnc-source-semantics-contract.json),共享实现见 [`web/workspace/linuxcnc-source-semantics.mjs`](../web/workspace/linuxcnc-source-semantics.mjs)。 + +## 硬约束和功能矩阵 + +以下约束是所有 PANEL 任务的共同通过条件: + +1. 网页控制面板的唯一数据来源是 LinuxCNC WASM;React 不得生成、修正或覆盖 canonical state。 +2. 状态机严格对标 LinuxCNC 源程序;状态数值、迁移顺序、错误码和 gate reason 必须有 source line 和 WASM/Worker evidence。 +3. 所有按钮逻辑制约必须与 LinuxCNC AXIS 源程序一致;按钮 disabled/enabled、模式切换、ESTOP/limit/fault gate 不允许由 UI 自行猜测。 +4. 刀具路径预览必须来自 WASM canonical motion events,并与原生 LinuxCNC 的 segment 顺序、source line、端点、圆弧中心/法向、turn、units 和最终 hash 一致;没有 WASM event 时必须 fail-closed。 + +| # | 功能 | LinuxCNC 原生 | WASM 支持 | Web 实现状态 | 优先级 | 主要任务 | +|---:|---|:---:|:---:|---|:---:|---| +| 1 | 加载数控程序 | ✅ | ✅ | `PANEL-07` | P0 | `PROGRAM_LOAD`、exact source/hash、diagnostic | +| 2 | 刀具路径预览 | ✅ | ✅ | `PANEL-11` | P0 | WASM motion event、segment golden、no JS preview | +| 3 | 取消急停 | ✅ | ✅ | `PANEL-05` | P0 | `ESTOP_RESET`/recover source state transition | +| 4 | 上电 | ✅ | ✅ | `PANEL-05` | P0 | `STATE_OFF -> STATE_ON` and gate | +| 5 | 复位 | ✅ | ✅ | `PANEL-05`/`PANEL-12` | P0 | task reset、fault clear、checkpoint reconciliation | +| 6 | HOME 回零 | ✅ | ✅ | `PANEL-06` | P0 | individual/all home、home sequence、home-limit ABI | +| 7 | 执行 G 代码 | ✅ | ✅ | `PANEL-07` | P0 | run/pause/resume/step/abort/restart | +| 8 | 实时路径显示 | ✅ | ✅ | `PANEL-10` | P0 | authoritative pose/toolpath -> Three.js | +| 9 | 实时坐标显示 | ✅ | ✅ | `PANEL-08` | P0 | commanded/actual/relative DRO、units | +| 10 | MDI 功能 | ✅ | ✅ | `PANEL-07` | P1 | MDI mode、queue limit、active program rejection | +| 11 | JOG 功能 | ✅ | ✅ | `PANEL-06` | P1 | manual mode、continuous/incremental、stop | +| 12 | 按钮逻辑制约 | ✅ | ✅ | `PANEL-03` | P0 | complete AXIS gate matrix and error reasons | + +表中的“Web 实现状态”在任务完成前只能是 `queued`、`in_progress` 或 `blocked`;现有 selected-case smoke/Worker evidence 不得自动把矩阵项标记为 `verified`。 + +### 功能项统一验收证据 + +每个矩阵项必须同时保存:LinuxCNC source path/line/hash、WASM export/state field、Worker command/event、React control ID、normal/boundary/error/ESTOP/recovery sample、native/WASM/Worker logical tick、输入/输出/state/event hash 和 self-hashed report。P0 项缺任一项即保持 `blocked`;P1 项不能阻塞 P0 实施,但不能被宣称完成。 + +## 源程序映射 + +| Web 控制面板区域 | LinuxCNC 源输入 | WASM/Worker 唯一来源 | React 允许行为 | 禁止行为 | +|---|---|---|---|---| +| 电源、急停、状态灯 | `axis.py` 状态更新、`linuxcnc` `STATE_*`/`MODE_*`/`INTERP_*` | `sim_task` canonical + `axis_state` | 显示、发送命令、显示 gate reason | 本地切换 ON/ESTOP | +| File/Program | `axis.py` `open_file_guts`, `reload_file`, `task_run` | WASM interpreter events + Worker program state | 编辑器输入、发送 LOAD/RUN/RESTART | JS 解析 G-code 或伪造当前行 | +| MDI | `axis.py` `ensure_mode`, `send_mdi_command` | WASM interpreter/task mode/queue | 提交文本、显示拒绝 | 本地判断 MDI 成功 | +| Manual/Jog/Home | `axis.py` `jog_plus`, `jog_minus`, `home_all_joints`, `home_joint` | WASM motion queue + home/limit ABI | 按钮、方向、速度输入 | JS 插值、修改 position/home | +| DRO/active codes | `axis.py` `update`, `task_modename`, `motion_modename`, `interp_statename` | WASM task/interpreter/motion snapshot | 格式化显示 | 从命令文本推导状态 | +| Feed/spindle override | `axis.py` `set_feedrate`, `set_spindlerate` | WASM/Worker device canonical | slider/number input | 直接修改 React store | +| Spindle/coolant/tool/probe | `axis.py` manual controls、案例 HAL、`sim_io` | WASM IO/device canonical | 显示、发送命令、fault recovery | mock device response | +| Preview/3D | `axis.py` preview/canon、案例 Vismach executable | WASM canonical motion + Worker visual snapshot | Three.js 渲染 | JS G-code preview/几何 authority | +| 配置诊断 | `linuxcnc-source-semantics-contract.v1` | Worker startup snapshot | 显示 `TASK_READY`/`BLOCKED_*`、source/line | basename 替换、隐藏缺失资源 | + +源代码锚点: + +- AXIS startup/open-file:`linuxcnc/src/emc/usr_intf/axis/scripts/axis.py` 约 3991-3998 行。 +- AXIS status projection:同文件 `update`、`task_modename`、`motion_modename`、`interp_statename`。 +- AXIS command gates:同文件 `ensure_mode`、`task_run`、`task_pause`、`task_resume`、`task_stop`、`jog_*`、`home_*`、`estop_clicked`。 +- 参数文件和程序前缀:`linuxcnc/src/emc/rs274ngc/rs274ngc_pre.cc`、`linuxcnc/src/emc/task/emctask.cc`。 +- 案例 UI/HAL/Vismach:`linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini`、`.hal` 和 `3axis-tutorial`。 + +## 控制面板数据契约 + +Worker 每次响应必须能追溯到同一个 `context_id`、`session_id`、`logical_tick` 和 `state_hash`。控制面板消费以下投影字段: + +```yaml +snapshot: + task: wasm sim_task canonical state + program: WASM interpreter/program state + motion: WASM motion queue state + home_limit: WASM kinematics home/limit state + hal: static/virtual HAL projection with source diagnostics + devices: WASM sim_io/tool/spindle/coolant/probe state + configuration: startup state, exact resources, optional_missing, runtime_fixtures + axis_state: additive AXIS state/mode/interpreter view + visual: pose, joints, toolpath and Vismach pose projection + state_hash: hash of the canonical projection +``` + +每个命令必须携带 `operation_id`,并产生 pending/result/error event。UI gate 只读取上一份 authoritative snapshot;命令被拒绝时显示 Worker 返回的 stable error code、message key、source/line 和 gate reason。错误、ESTOP、limit、following-error、资源缺失和能力边界必须可见,不能变成静默 disabled。 + +### 分层接口责任 + +| 层 | 必须提供 | 允许缓存 | 不得提供 | +|---|---|---|---| +| LinuxCNC source/native recorder | 常量、状态迁移、命令顺序、错误/拒绝语义、segment golden | stdout/log/screenshot hash | 浏览器状态 | +| `wasm-core` | canonical task/interpreter/motion/home-limit/HAL/IO state、fixed tick、canonical event | raw ABI bytes、read-only sample | UI 文案或按钮 gate | +| Dedicated Worker | command ledger、session lifecycle、snapshot/event 转发、diagnostic/source line、checkpoint/trace | 序列化协议和 operation trace | JS authority mirror、JS G-code 解析 | +| React/Web panel | command input、snapshot projection、accessibility、visual rendering | 最近一份 snapshot 用于显示 | 改写 state、推导成功、推导轨迹 | + +任何层违反责任都必须在测试中以 `authority_violation` 失败;“结果看起来一致”不能替代唯一来源证明。 + +### 面板命令 envelope + +所有 P0/P1 命令使用已有 `protocol.v1` envelope,不为 UI 单独发明协议: + +```json +{ + "schema_version": 1, + "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", + "session_id": "axis3-panel", + "seq": 42, + "operation_id": "axis-program-run-000042", + "kind": "PROGRAM_RUN", + "payload": {} +} +``` + +响应至少包含 `kind`、`seq`、`operation_id`、`logical_tick`、`payload.task`、`payload.program`、`payload.motion`、`payload.axis_state`、`payload.state_hash`。错误响应必须包含 `code`、`message_key`、`source`/`line`(若有)、`gate_reason` 和拒绝时的完整 snapshot。 + +## 严格状态机和按钮真值表 + +源常量和 AXIS 视图必须使用 [`docs/linuxcnc-web-wasm-3axis-tutorial-state-machine.md`](linuxcnc-web-wasm-3axis-tutorial-state-machine.md) 中的数值,不得只使用同名字符串: + +| AXIS 状态 | LinuxCNC source state | 允许的面板操作 | 必须拒绝 | +|---|---|---|---| +| `ESTOP` | `STATE_ESTOP` | cancel ESTOP、显示诊断 | power/home/run/MDI/JOG | +| `ESTOP_RESET` | `STATE_ESTOP_RESET` | power on、ESTOP | run/home/MDI/JOG | +| `OFF` | `STATE_OFF` | power/reset、ESTOP | run/home/MDI/JOG | +| `ON`/`IDLE` | `STATE_ON + INTERP_IDLE` | load/run/MDI/manual/home/jog/device | 与当前 mode/limit/fault 冲突的操作 | +| `HOME` | source home transient | 等待 home result、ESTOP | run/MDI/重复 home | +| `RUNNING` | `INTERP_READING/WAITING` | pause/abort/ESTOP | home/jog/MDI/load | +| `PAUSED` | `INTERP_PAUSED` | resume/abort/ESTOP/step(按源 gate) | home/jog/普通 MDI | + +按钮 gate 的可执行真值样本必须至少包含: + +| 操作 | 正常条件 | 拒绝样本 | +|---|---|---| +| 加载程序 | `ON + INTERP_IDLE` 或源允许的 manual load | `OFF`、`RUNNING`、资源缺失 | +| 预览 | WASM motion events 已存在 | WASM event 缺失、解析错误 | +| 取消急停 | `ESTOP`/`ESTOP_RESET` 按源状态转换 | snapshot 过期、seq/tick 错误 | +| 上电 | `ESTOP_RESET -> ON` 或源定义 reset 流程 | `ESTOP` 未 reset、fault latch | +| 复位 | abort/clear/recover 的源顺序 | active motion、坏 checkpoint | +| HOME | `ON + MANUAL + INTERP_IDLE` | `OFF`、AUTO/MDI、limit/fault、未满足 previous home | +| G-code run | `ON + AUTO + IDLE + taskfile` | 无程序、MDI、paused、ESTOP | +| 实时路径/DRO | snapshot tick/hash 可验证 | snapshot stale、state hash mismatch | +| MDI | `ON + MDI` 且 queue 未满 | AUTO active、queue full、ESTOP | +| JOG | `ON + MANUAL + IDLE` | AUTO/MDI、未 home、limit/fault | + +## 刀具路径预览一致性算法 + +预览和执行都必须使用 WASM interpreter canonical motion event;Web 不得将原始 G-code 再解析一遍。每个 segment 的比较字段固定为: + +```yaml +segment: + source_line: integer + motion_type: rapid|linear|arc + start: pose(x,y,z,a,b,c,u,v,w) + end: pose(x,y,z,a,b,c,u,v,w) + center: pose_or_null + normal: vector_or_null + turn: integer + velocity: finite_number + units: mm|inch + plane: G17|G18|G19 + sequence: integer +``` + +验收顺序: + +1. native recorder 使用固定程序和固定 units/modal 初始状态生成 segment JSONL。 +2. WASM `sim_interpreter_read_motion` 生成同字段事件;Worker 只排序/封装,不补字段。 +3. 比较 segment count、source line、顺序、字段值和 canonical hash;浮点比较使用 source/ABI 规定的 exact或明确 tolerance,不能用截图相似度替代。 +4. Three.js 只渲染已比较的 `visual` projection;canvas 非空是必要条件,不是轨迹正确性的充分条件。 +5. 任意字段首差必须记录 `first_divergent_segment`、`source_line`、`logical_tick`、`field`、`native`、`wasm`,并阻止 preview/execute 继续宣称一致。 + +禁止条件:WASM event buffer 为空时调用 JS `previewGcode`;用当前 DRO 终点反推完整路径;用 native screenshot 通过 Web segment hash;用墙钟时间推进路径。 + +## 功能矩阵报告模板 + +每个功能项使用同一结构,便于控制面板任务和后续自动聚合: + +```yaml +feature_id: PANEL-07 +matrix_item: execute_gcode +priority: P0 +status: queued|in_progress|verified|blocked +source: + paths: [] + lines: [] + sha256: [] +authority: + wasm_exports: [] + canonical_fields: [] +worker: + commands: [] + events: [] +react: + control_ids: [] + projection_fields: [] +samples: + normal: [] + boundary: [] + error: [] + estop_or_fault: [] + recovery: [] +comparison: + native_hash: null + wasm_hash: null + worker_hash: null + first_divergent_tick: null +stop_reason: null +report_sha256: null +``` + +`verified` 只允许在 `source`、`authority`、`worker`、`react`、5 类 samples、comparison 和 self-hash 全部存在时使用。任何 `REAL-EXCLUDED`、`SOURCE_BLOCKED`、`WASM_CAPABILITY_BLOCKED` 都必须保持 `blocked`,并带 failure sample。 + +## 面板布局和交互范围 + +桌面和移动布局都必须保持以下固定区域,不因动态文字改变尺寸: + +1. 顶栏:案例名、WASM capability、连接/session、`TASK_READY`/`BLOCKED`、ESTOP 和 recover。 +2. 左侧状态:machine power、task mode、interp state、program status、current line、logical tick、state hash。 +3. 中央机床视图:Three.js Vismach 3-axis model、tool pose、joints、limits、toolpath、axis labels。 +4. 右侧 DRO:commanded/actual/relative 的 X/Y/Z、unit、feed/spindle override、active G/M codes。 +5. 下方 tabs:Manual、MDI、Program、Diagnostics;每个 tab 的控件 gate 来自 Worker snapshot。 +6. 操作条:Home all/individual、jog +/-/stop、Run/Pause/Resume/Step/Abort/Restart、spindle/coolant/tool/probe/ESTOP。 +7. 诊断面板:按事件顺序显示 source path、line、code、reason、first divergent tick、recovery status。 + +熟悉符号使用 AXIS 源图标或 lucide icon;按钮必须有 accessible name、disabled reason 和 keyboard path。控件不能仅通过颜色表示 fault/ESTOP。 + +## 功能缺口移植流程 + +当 WASM/Worker 当前能力不满足 AXIS 行为时,严格按以下顺序处理: + +1. 从 LinuxCNC 源文件定位函数、状态常量、命令顺序和错误语义,记录 source path、line、source hash。 +2. 判断是否属于纯仿真可移植行为。实时内核、真实硬件、宿主动态 loader、宿主 GUI/Python 进程只能登记 `REAL-EXCLUDED`/`HOST_EXCLUDED`,不能伪造 WASM parity。 +3. 先增加 native source sample 和失败测试,再在 `wasm-core` 增加 C ABI/状态实现;ABI 变更必须建立新 context,不能就地改锁定 ABI。 +4. 增加真实 WASM module 测试,再接 `web/worker` adapter;Worker 只投影 WASM canonical,不保留 JS authority 镜像。 +5. 最后接 React 命令/渲染,补 normal、boundary、error、non-finite、capacity、pause/resume、ESTOP、recovery 样本。 +6. 记录首个 divergent phase/tick、native/WASM/Worker hash、checkpoint/trace/recovery hash,更新 self-hashed report。 + +## 详细实施任务 + +### P0/P1 执行顺序 + +先完成 P0 的 authority、状态机、按钮 gate、程序加载/执行、preview、实时路径和实时坐标,再实现 P1 的 MDI/JOG 扩展。P0 任务之间按 `PANEL-01 -> PANEL-02 -> PANEL-03 -> PANEL-05 -> PANEL-06/PANEL-07 -> PANEL-08/PANEL-10/PANEL-11 -> PANEL-14 -> PANEL-15` 执行;P1 的 MDI/JOG 只能复用同一 WASM/Worker authority,不能另建一套 UI state。 + +P0 完成的硬条件:12 项矩阵中的 1-9、12 有 native source reference、真实 WASM module、Worker sequence 和 React evidence;刀具路径 preview 与 native canonical segments 的 hash/端点一致;所有按钮 gate 在正常、运行、暂停、MDI、ESTOP、limit、fault、未 home 和 queue-full 样本中一致。P1 完成的硬条件:10-11 的 MDI/JOG 与 P0 状态机、queue、home/limit、ESTOP gate 共用同一 snapshot,不能产生 divergent tick。 + +| ID | 任务 | 主要实现 | 直接源输入 | 验收输出 | 依赖 | +|---|---|---|---|---|---| +| `PANEL-01` | 源控件 inventory | 把 AXIS 菜单、toolbar、快捷键、Manual/MDI/Auto、DRO、诊断控件登记为稳定 IDs | `axis.py`、AXIS Tcl UI、案例 INI | source inventory + control ID contract | 无 | +| `PANEL-02` | snapshot projection contract | 固定 task/program/motion/home/hal/devices/configuration/axis_state/visual 字段、tick/hash 和 event envelope | `sim-session.mjs`、T4/T3 reports | schema-compatible projection test | 01 | +| `PANEL-03` | source gate matrix | 逐命令复刻 AXIS `ensure_mode`、状态和 gate;禁止 React 自判定 | `axis.py` command functions、`axis-contract.mjs` | gate matrix normal/error/ESTOP test | 01/02 | +| `PANEL-04` | startup/config panel | 显示 exact resources、optional `OPEN_FILE`/graphic、HAL/resource/INI diagnostics 和 runtime fixtures | source semantics contract、案例 INI/HAL | `TASK_READY`/`BLOCKED_*` UI test | 02 | +| `PANEL-05` | power/ESTOP/recover | 对齐 `STATE_OFF/ON/ESTOP/ESTOP_RESET` 和 recover transition | `axis.py`、`sim_task`、`sim_io` | transition/event/hash report | 02/03 | +| `PANEL-06` | Manual/Home/Jog | 对齐 individual/all home、continuous/incremental jog、stop、joint/teleop gate | `axis.py` `jog_*`/`home_*`、T4 home-limit | native/WASM/Worker sequence report | 03/05 | +| `PANEL-07` | Program/MDI | 对齐 open/load/reload/run/pause/resume/step/abort/restart/MDI queue and active-program rejection | `axis.py`、WASM interpreter/task | program control and current-line report | 03/05 | +| `PANEL-08` | DRO/active codes | 对齐 actual/commanded/relative、units、feed/spindle、active G/M/modal display | AXIS `update`/stat mixins、WASM snapshot | DRO golden and unit switch test | 02/06/07 | +| `PANEL-09` | device panels | 对齐 spindle ramp/at-speed, coolant, tool prepare/change, probe, limit/FERROR faults |案例 HAL、`sim_io`、tool-IO report | device fault/recovery report | 05/06/07 | +| `PANEL-10` | Vismach/3D | 按 `3axis-tutorial` Python model 和 HAL pins 实现 geometry/pose/tool continuity | 案例 executable、Vismach source、Three.js view | canvas pixel/nonblank/pose report | 02/06/08/09 | +| `PANEL-11` | editor/preview | Worker WASM preview、source line highlighting、active G-code 和 diagnostics | `sim_interpreter`、AXIS editor/preview | no-JS-preview test + source-line parity | 07/08 | +| `PANEL-12` | persistence/recovery UI | checkpoint, journal, trace import, `.var/.bak/position/tool.tbl` restart and fail-closed recovery | T4/T6 reports、workspace recovery | recovered endpoint and no duplicate side effects | 05/07/09 | +| `PANEL-13` | keyboard/accessibility/layout | AXIS shortcut mapping、focus order、ARIA、desktop/mobile no-overlap | AXIS shortcut table、React source | DOM/accessibility/visual report | 01/03/08/10/11 | +| `PANEL-14` | native/Worker/UI recorder | 同一 operation sequence、same logical ticks、first divergence and hash comparison | native runner、Dedicated Worker、React | sequence ledger and diff report | 05..13 | +| `PANEL-15` | selected-case acceptance | 汇总所有面板区域、normal/boundary/error/recovery 和 release boundaries | all PANEL evidence | self-hashed closure report | 14 | + +## 验收和停止条件 + +通过条件:每个 PANEL row 有 source reference、WASM authority、Worker test、UI evidence、state/event hash 和 recovery sample;同一操作序列 native/WASM/Worker/UI 的首个差异可定位;控制面板无 JS authority、无 JS G-code preview、无 basename 替换。 + +保持 `blocked`:缺 source/oracle/resource、WASM ABI 不足、首个 divergent tick 未定位、Worker snapshot/hash 不稳定、恢复重复副作用、只在 Chromium 可见但 browser minimum 未锁定,或只能靠 mock/墙钟/宿主能力通过。 + +停止发布:不把固定案例 verified 写成全量 LinuxCNC parity;Firefox/Safari、OPFS 物理崩溃、实时硬件、官方 oracle、许可证和 owner release gate 继续保持 `STOP_RELEASE`。 + +## 证据命令 + +```bash +python3 scripts/validate_baseline.py +python3 scripts/validate_task_pointer.py nextTask1.md --lock context-lock.json +python3 scripts/test_source_semantics_contract.py +node web/workspace/test_source_semantics.mjs +node web/workspace/test_sim_case_loader.mjs +node web/worker/test_sim_case_matrix_s8_06.mjs +node web/worker/test_wasm_canonical_state.mjs +node web/worker/test_wasm_canonical_state_checkpoint.mjs +node web/worker/test_axis3_selected_case.mjs +node web/worker/test_axis3_visible_completion.mjs +npm --prefix web/react run typecheck +npm --prefix web/react run build +``` + +每个 PANEL 任务必须增加专属测试和 self-hashed report;未完成的任务保留 `queued`/`blocked`,不能从 UI smoke 直接升级为 parity。 diff --git a/docs/linuxcnc-web-wasm-3axis-tutorial-native-web-tool-comparison.md b/docs/linuxcnc-web-wasm-3axis-tutorial-native-web-tool-comparison.md new file mode 100644 index 0000000..8838a28 --- /dev/null +++ b/docs/linuxcnc-web-wasm-3axis-tutorial-native-web-tool-comparison.md @@ -0,0 +1,49 @@ +# 3axis-tutorial Native/Web 工具对标验证 + +本验证使用两个独立工具执行同一案例: + +- native AXIS:`tool/linuxcnc-axis-manual-execution` +- Web/WASM:`tool/linuxcnc-web-wasm-manual-execution` + +案例和程序固定为: + +- `linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini` +- `linuxcnc/nc_files/3dtest.ngc` + +## 执行流程 + +两边均执行同一操作意图: + +1. 启动前清理 LinuxCNC 仿真和 stale lock。 +2. 打开案例并确认界面可见。 +3. F1 取消急停。 +4. F2 上电。 +5. Home All / `Ctrl+Home`。 +6. 运行 G-code。 +7. 采集窗口或页面、DRO、运行态、Canvas/截图和最终状态。 +8. 退出并再次清理 LinuxCNC。 + +native 使用真实 AXIS 窗口和 Xlib 键盘事件;Web 使用真实 Chromium CDP DOM 点击、production Vite preview、真实 WASM module 和 Dedicated Worker。两边都不使用物理硬件。 + +## 结果 + +机器可读结果见 [comparison report](../coverage/axis3-tutorial-native-web-tool-comparison.report.json)。 + +报告字段: + +- native AXIS 标题:`3dtest.ngc - AXIS 2.10.0~pre1 on 3axis-tutorial` +- native 窗口:`1300x1297` +- Web browser:Google Chrome headless + SwiftShader +- native screenshots:4 +- Web screenshots:4 +- Web Canvas:非空,`941x471` +- Web exact source:54 行 `3dtest.ngc` +- Web canonical toolpath:49 segments +- selected-case WASM acceptance:`20/20` +- report self-hash:见 comparison report 的 `report_sha256` + +结论为 `native_and_web_tools_match_selected_case_workflow`,表示在固定案例的仿真控制流程、状态门禁、程序加载/运行、HOME 和可视化证据范围内对标通过。 + +## 边界 + +该结论仅覆盖固定 `3axis-tutorial` 案例。native 使用 POSIX non-realtime 仿真,Web 使用 Chromium SwiftShader;不代表实时硬件、真实 LinuxCNC 全量配置、Safari 或全量 LinuxCNC parity。 diff --git a/docs/linuxcnc-web-wasm-3axis-tutorial-state-machine.md b/docs/linuxcnc-web-wasm-3axis-tutorial-state-machine.md index 7639605..7bf9347 100644 --- a/docs/linuxcnc-web-wasm-3axis-tutorial-state-machine.md +++ b/docs/linuxcnc-web-wasm-3axis-tutorial-state-machine.md @@ -66,6 +66,14 @@ ESTOP, ESTOP_RESET, OFF, ON, HOME, IDLE, RUNNING, PAUSED - Home 需要 machine on、manual mode、interpreter idle;本案例 UI 逐轴执行。 - Tool/spindle/coolant/probe 操作必须同时受 ESTOP、fault、interlock、spindle busy 和 tool lifecycle gate 约束。 +## Web AXIS 操作映射 + +- 页面上的“取消急停”先发送 `ESTOP(active=false)`,等待 `ESTOP_RESET`,再发送 `IO_RECOVER` 进入 `ON/IDLE`;急停置位始终只发送 `ESTOP(active=true)`,其错误响应仍携带完整 `task/motion/axis_state` 快照。 +- “上电”使用 `IO_RECOVER`;“复位机床”先停止 AUTO 解释器、清除急停(如有),再执行 `IO_RECOVER`。协议层 `RESET` 仍是生命周期复位,不能作为机床复位按钮,否则会按源协议清零序号。 +- Manual 页签切换会先发 `SET_MODE(MANUAL)`。因此 `HOME`、逐轴 `JOG_*`、主轴和冷却只在 `STATE_ON + MODE_MANUAL + INTERP_IDLE` 开放;MDI/AUTO、fault、限位和急停均 fail-closed。 +- Home All 遵循本案例未定义统一 `HOME_SEQUENCE` 时的源 AXIS 行为,固定按 `Z -> X -> Y` 逐轴发送并等待每个 `COMMAND_RESULT`。 +- G-code 预览可以在加载前显示确定性解析轨迹;执行轨迹只使用 Worker authoritative planner 快照,已完成段和 active 段分层显示,DRO 从 Worker 的 motion/interpreter 快照更新。 + ## 验证 - 单元状态契约:`node web/worker/test_axis_state.mjs` diff --git a/docs/linuxcnc-web-wasm-release-authorization.md b/docs/linuxcnc-web-wasm-release-authorization.md new file mode 100644 index 0000000..ac47de0 --- /dev/null +++ b/docs/linuxcnc-web-wasm-release-authorization.md @@ -0,0 +1,41 @@ +# Selected-Case Release Authorization + +The workspace user explicitly authorized publication of the selected +`3axis-tutorial` Web/WASM simulation bundle on 2026-08-24. This authorization +is recorded in `coverage/release-authorization.json` and is intentionally +separate from the historical `STOP_RELEASE` evidence. + +The six K8 prerequisites are treated as explicit release waivers for this +scope. Their source reports remain immutable evidence and are not rewritten as +resolved facts. The user has accepted responsibility for the project's license +position; no SPDX inference or license metadata mutation is required for this +selected-case bundle. The release claim is limited to: + +- the exact `3axis-tutorial` configuration and its Worker/WASM runtime; +- the verified native, WASM, Worker, and React regression slice; +- Chromium and Firefox smoke coverage already present in the workspace. + +This authorization does not claim full LinuxCNC parity, official kinematics or +recovery oracle parity, all configuration startup coverage, Safari support, or +ownership closure for the entire source manifest. Those facts are outside the +selected-case publication claim and must not be inferred from the release +authorization. + +The active release artifact is generated by: + +```bash +python3 scripts/generate_release_authorized_gate.py +python3 scripts/test_release_authorized_gate.py +python3 scripts/validate_publish_gate.py +``` + +Before generating the gate, refresh the command evidence with: + +```bash +python3 scripts/generate_release_verification.py +python3 scripts/test_release_verification.py +``` + +The generator fails closed when the authorization is missing, malformed, +tampered with, scoped beyond the selected case, or any waiver evidence hash no +longer matches its source report. diff --git a/docs/linuxcnc-web-wasm-source-semantics.md b/docs/linuxcnc-web-wasm-source-semantics.md new file mode 100644 index 0000000..4b001ad --- /dev/null +++ b/docs/linuxcnc-web-wasm-source-semantics.md @@ -0,0 +1,20 @@ +# LinuxCNC Source Semantics Contract + +This contract freezes source-derived configuration behavior for the future Web control panel. The machine-readable copy is [`coverage/linuxcnc-source-semantics-contract.json`](../coverage/linuxcnc-source-semantics-contract.json); the shared Worker/workspace implementation is [`web/workspace/linuxcnc-source-semantics.mjs`](../web/workspace/linuxcnc-source-semantics.mjs). + +## Rules + +- LinuxCNC AXIS reads `[DISPLAY] OPEN_FILE` as an initial UI hint. The source checks whether the selected file exists before opening it, so an empty or missing `OPEN_FILE` is `optional_missing`, not a startup resource failure. +- `[DISPLAY] INTRO_GRAPHIC` is also an optional display asset. +- Missing exact `HAL`/`HALFILE` paths are classified as `BLOCKED_HAL`; they must not be reported as generic resource failures. +- Other missing exact paths are `BLOCKED_RESOURCE`. The resolver never substitutes a same-basename file from another configuration directory. +- Parameter files and tool tables may use the existing explicit runtime-fixture policy. This is a deterministic simulation fixture, not a claim that a host file exists. +- `linuxcnc/nc_files/lathe_pawn.ngc` exists, while the path requested by `lathe_multispindle.ini` (`.../nc_files/examples/lathe_pawn.ngc`) does not. The mismatch remains an optional `OPEN_FILE` diagnostic rather than a fabricated alias. + +## Control Panel Boundary + +The Web control panel is projection-only. It should consume Worker configuration/session snapshots and render `state`, `status`, `diagnostics`, `resources`, `hal`, `optional_missing`, `runtime_fixtures`, and `state_hash`. It must not infer readiness from file basenames, write task/motion authority, or turn `BLOCKED_*` into `TASK_READY`. + +The startup states remain `NEW -> MOUNTED -> PARSED -> RESOLVED -> CORE_CREATED -> HAL_READY -> TASK_READY`, with `BLOCKED` as the fail-closed terminal state. The source semantics contract does not modify ABI, protocol, schema versions, context lock, or release blockers. + +Source evidence: `linuxcnc/src/emc/usr_intf/axis/scripts/axis.py` around the `OPEN_FILE` startup path, `linuxcnc/src/emc/rs274ngc/rs274ngc_pre.cc` for parameter-file handling, and `linuxcnc/src/emc/task/emctask.cc` for program-prefix handling. diff --git a/docs/linuxcnc-web-wasm-wasm-only-migration-plan.md b/docs/linuxcnc-web-wasm-wasm-only-migration-plan.md new file mode 100644 index 0000000..91559f3 --- /dev/null +++ b/docs/linuxcnc-web-wasm-wasm-only-migration-plan.md @@ -0,0 +1,98 @@ +# LinuxCNC Web/WASM 唯一来源迁移计划 + +## 目标 + +Web 仿真的行为、预览、轨迹、状态和错误必须由 LinuxCNC 源码编译出的 WASM core 决定。React/UI 只负责输入和渲染;Worker 只负责协议、生命周期和快照转发;不得由 JavaScript 重复解释 G-code、复制 TP/kinematics 规则或伪造 canonical state。 + +## 来源分层 + +| 层 | 唯一来源 | Web 允许做什么 | 禁止做什么 | +|---|---|---|---| +| RS274 | `wasm-core/src/sim_interpreter.c` 与 LinuxCNC `rs274ngc` | 分配内存、读取状态 | JS 重新解析 G/M/O 词 | +| Canonical motion | WASM interpreter motion event API | 排序、序列化协议 | JS 推导圆弧/整圆/终点 | +| TP/motion | `sim_motion_queue` / `sim_kinematics_motion` WASM API | 调用 tick、读取 sample | JS 插值、速度、队列进度 | +| Task | `sim_task` WASM state/canonical | 映射按钮状态 | JS 自己改变任务状态 | +| HAL/IO/tool/probe | WASM runtime canonical | 显示/转发事件 | JS 模拟设备结果 | +| UI | React | 发送命令、渲染 snapshot | 直接成为 authority | + +## 任务安排 + +### T0 基线审计和门禁 + +输入:WASM exports、Worker/React source、现有 native/WASM/Worker reports。 + +产出:来源审计报告、JS authority 清单、当前功能矩阵、固定 3-axis baseline hash。 + +验收:报告能明确列出每个用户可见行为的唯一来源;现有 `3dtest.ngc` Web/native baseline 可重复。 + +停止条件:来源或 ABI 不明确时,不把 JS fallback 标为 WASM parity。 + +### T1 WASM canonical motion event + +输入:`sim_interpreter` state/modal implementation、LinuxCNC `rs274ngc` canonical callbacks、posemath segment types。 + +产出:WASM `sim_interpreter_read_motion`/motion-event buffer,覆盖 G0/G1/G2/G3、G17/G18/G19、IJK/R、G20/G21、G90/G91、螺旋轴变化和 source line。 + +验收:C native test、WASM test、Worker test 对同一程序输出相同 segment count、source line、端点、center、normal、turn、units 和 hash。 + +### T2 WASM-backed preview + +输入:T1 motion events。 + +产出:Worker `PROGRAM_PREVIEW`,React 只消费 snapshot/toolpath;删除 `previewGcode` 作为行为来源。 + +验收:`3dtest.ngc` 三个整圆和 XYZ extents 与原生 AXIS 对齐;无 WASM event 时 fail closed,不回退到 JS 解析器。 + +### T3 WASM-backed visible execution + +输入:T1 events、WASM TP/kinematics/motion APIs。 + +产出:运行、暂停、恢复、单步、停止、末段排空全部由 WASM tick/queue state 驱动;移除 `AuthoritativeRuntime` 的运动 authority。 + +验收:浏览器点击 Power/Home All/Run 后 DRO、当前行、Three.js tool pose、queue depth、最终 M2 endpoint 与 native sequence 对齐。 + +### T4 WASM canonical state + +输入:WASM task/motion/HAL/IO/tool/probe canonical APIs。 + +产出:Worker snapshot 只转发 WASM canonical;React gate 只读取 snapshot;逐轴 home、limit、estop、tool、spindle、coolant、probe 状态一致。 + +验收:snapshot hash、event hash、checkpoint/recovery hash 在 native/WASM/Worker 重复运行中稳定。 + +### T5 完整功能矩阵 + +输入:LinuxCNC source inventory、unit tests、真实 INI/HAL/NGC fixtures。 + +产出:按 RS274、TP、kinematics、HAL、IO、Python/remap、配置、恢复、浏览器 UX 分层的 native/WASM/Worker matrix。 + +验收:每项为 `verified`、`blocked` 或 `queued`,均有 owner、输入、测试、差异和停止原因;不允许用 selected-case 结果覆盖全量。 + +### T6 功能闭合 + +按矩阵逐项实现:RS274 G/M/O/canned/compensation,TP blend/exact-stop/accel/jerk,HAL component/PLC,IO/tool/probe,Python/remap/filter/VCP,配置资源和恢复边界。 + +每一项必须先有 LinuxCNC source reference,再有 WASM C test,再接 Worker/UI;任何宿主进程、动态库、真实设备或墙钟行为保持排除。 + +### T7 发布门禁 + +必须同时通过:WASM build、CTest、native source adapters、Worker tests、React typecheck/build、浏览器可见执行、native AXIS screenshot、功能矩阵和 release closure。仍有 `STOP_RELEASE` blocker 时只能报告 blocked,不能宣称完成。 + +## 当前执行状态 + +`T0` 已完成:来源审计报告为 `coverage/wasm-only-source-audit.report.json`,selected-case baseline 通过但整体保持 blocked。 + +`T1` 已完成第一切片:`sim_interpreter_read_motion` 和 `sim_interpreter_motion_size` 已导出,Worker `ProgramRunner` 能读取 WASM canonical segment;native/WASM interpreter test 和 `web/worker/test_wasm_motion_event.mjs` 通过。T1 的完整覆盖仍需补齐 R 弧、螺旋、同步运动和 canonical queue 排序。 + +当前 `T2` 的 selected-case 切片已完成:`PROGRAM_LOAD` 返回 WASM canonical preview segments,React 只消费 Worker toolpath;`visual-workflow.ts` 不再参与行为预览。T2 仍需扩展到完整 canonical callback、canned cycle、R 弧、同步运动和所有配置族。 + +`T3` 的 selected-case 切片已完成:WASM motion queue 接收 interpreter canonical segments,负责固定 tick 的 position/velocity/queue depth;`3axis-tutorial` trivkins 运行路径不调用 `AuthoritativeRuntime.syncProgram/step/applyMotion`,pause/resume、PROGRAM_STEP、abort 和末段排空均由 queue 状态驱动。queue raw state、active segment、tick、controls、position 和 hash 已纳入 checkpoint、trace/workspace state;恢复 replay 与连续 endpoint hash 相同,损坏 queue state fail-closed。Worker snapshot 公开 `motion_authority: wasm` 与 `source: wasm_motion_queue`。JS `AuthoritativeRuntime` 仍只作为非 selected-case/非 trivkins 兼容镜像,`program-runner`/`motion-state`/`sim-task` 的其它 canonical state 仍待 T4/T5;因此整体 source-of-truth audit 仍为 blocked。 + +`T4` selected-case 切片已完成:Worker `SimTask` 通过 `sim_task_canonical` 读取 WASM task state,`WasmHomeLimit` 通过锁定 `sim_kinematics_home_limit_*` ABI 读取 trivkins home/limit state;snapshot、checkpoint、trace/import 和 recovery 都带 task/home-limit authority、state hash 和 logical tick。JS task tamper 不覆盖 WASM canonical state,损坏 home-limit bytes fail-closed。HAL/IO/tool/probe canonical state、外部设备 limit 输入和非 selected-case compatibility 仍保持开放,整体 audit 仍 blocked。 + +`T5` 已生成 `coverage/wasm-feature-matrix-t5.report.json`:RS274、TP、kinematics、HAL、IO、Python/remap、configuration、recovery、browser UX 九个领域均有 native/WASM/Worker 行、输入 hash、测试命令、覆盖样本、差异和停止原因。对照 LinuxCNC AXIS 源码修复空/缺失 `OPEN_FILE` 语义并将缺失 HALFILE 分类为 `BLOCKED_HAL` 后,235-case matrix 恢复 verified,分类为 `BLOCKED_HAL=106/BLOCKED_RESOURCE=11/TASK_READY=96`;完整 oracle、配置资源、三浏览器 minimum 和物理 OPFS recovery 仍 blocked,不将 selected-case 结果提升为全量 parity。 + +`T6` 已完成 source-backed closure audit,但保持 `blocked`:八个矩阵 closure row 均有真实 source reference、可执行 WASM/Worker slice 和 23 个通过的专项命令,另保留 4 个 failure samples。完整 RS274/TP/kinematics 仍缺官方 oracle,配置仍有 exact-resource blockers,HAL/IO/Python 仍受 REAL-EXCLUDED 能力边界限制,恢复和浏览器仍缺物理 OPFS/三浏览器输入;没有使用 JS fallback 或 selected-case promotion。 + +`T7` 已完成 simulation-track 的只读 release-gate audit,报告为 `blocked`:T6 closure、既有 release/remediation evidence、owner refresh 和六项 blocker identity 均核对通过,但 candidate 仍不可 eligible、`STOP_RELEASE` 保持。该 audit 不刷新 K8 gate、不修改 context lock、manifest、许可证或 owner decision;没有新输入时不自动重试或升级 release。 + +源语义已固化为 `coverage/linuxcnc-source-semantics-contract.json` 与 `web/workspace/linuxcnc-source-semantics.mjs`,供后续 Web 控制面板直接复用。控制面板只能投影 Worker 的 startup snapshot;`OPEN_FILE`/`INTRO_GRAPHIC` optional、`BLOCKED_HAL`/`BLOCKED_RESOURCE` 分类、exact-path 和 runtime fixture policy 均由该契约决定。 diff --git a/docs/task-history.md b/docs/task-history.md index 61767d8..f380ee8 100644 --- a/docs/task-history.md +++ b/docs/task-history.md @@ -104,11 +104,11 @@ | `S8-03` | verified:真实执行 `xz.ini`、`xz_both.ini`、`xz_both_auto.ini` 取得 `xz.var` 和三份生成 HAL fixture;同案例 `xz.tbl` 精确复用;三案 Worker startup 均 `TASK_READY`、HAL blocked_count=0,identity/BOTH/AUTO state hash 分别保留 | `coverage/sim-case-batch-s8-03.report.json`, `web/worker/test_sim_case_batch_s8_03.mjs`, `scripts/generate_sim_case_batch_s8_03.py`, `scripts/test_sim_case_batch_s8_03.py` | | `S8-04` | verified:minimal_xyz 真实 fixture 达到 TASK_READY;固定 tick 覆盖 Home/Jog/override、G-code Run/Pause/Resume/Abort/Restart、MDI、estop/recover 和 checkpoint recovery endpoint parity;前端 AppShell/state panel/machine view 继续消费 Worker 权威状态 | `coverage/minimal-xyz-vertical-s8-04.report.json`, `web/worker/test_minimal_xyz_vertical_s8_04.mjs`, `scripts/generate_minimal_xyz_vertical_s8_04.py`, `scripts/test_minimal_xyz_vertical_s8_04.py` | | `S8-05` | verified:`corexy_by_kins.ini` 与 `corexy_by_hal.ini` 均使用真实 fixtures 达到 TASK_READY;同一 pose `[4,2,3]` 映射 joints `[6,2,3]`,HAL records 72/100,1x/fast/recovery parity 均通过;kins path 与 HAL-trivkins transform path 保持明确区分,未知 HAL 命令仍 fail-closed | `coverage/corexy-dual-path-s8-05.report.json`, `web/worker/test_corexy_dual_path_s8_05.mjs`, `scripts/generate_corexy_dual_path_s8_05.py`, `scripts/test_corexy_dual_path_s8_05.py`, `web/workspace/hal-configuration.mjs` | -| `S8-06` | verified:235 个真实 `configs/sim` 案例全部执行 exact-path loader/startup 两次;分类为 TASK_READY=7、BLOCKED_RESOURCE=221、BLOCKED_HAL=4、BLOCKED_INI=3;每案保留 fixture、missing、first diagnostic、resource/HAL counts 和 deterministic state hash,不把 blocked 提升为 ready | `coverage/sim-case-matrix-s8-06.report.json`, `web/worker/test_sim_case_matrix_s8_06.mjs`, `scripts/generate_sim_case_matrix_s8_06.py`, `scripts/test_sim_case_matrix_s8_06.py` | +| `S8-06` | verified:235 个真实 `configs/sim` 案例全部执行 exact-path loader/startup 两次;按 LinuxCNC source 语义处理 optional `DISPLAY/OPEN_FILE`、缺失 HALFILE 分类和真实路径;分类为 TASK_READY=96、BLOCKED_RESOURCE=11、BLOCKED_HAL=106、BLOCKED_INI=19、BLOCKED_STARTUP=3;每案保留 fixture、missing、first diagnostic、resource/HAL counts 和 deterministic state hash | `coverage/sim-case-matrix-s8-06.report.json`, `web/worker/test_sim_case_matrix_s8_06.mjs`, `scripts/generate_sim_case_matrix_s8_06.py`, `scripts/test_sim_case_matrix_s8_06.py`, `web/workspace/sim-case-loader.mjs` | | `S8-07` | verified:从真实 INI/include declarations 生成 235-case display family catalog;Axis 85、QtVCP 61、Gscreen 14、VisMach 21、GladeVCP 6、Touchy 6、PyVCP 5、other 37;AppShell 按案例显示 family capabilities/host plugin boundaries,未改变 Worker 权威状态或 blocked 语义 | `coverage/sim-display-families-s8-07.report.json`, `web/app/sim-display-families.mjs`, `web/app/display-family.mjs`, `web/app/test_display_family_s8_07.mjs`, `scripts/generate_sim_display_families_s8_07.py`, `scripts/test_sim_display_families_s8_07.py` | | `S8-08` | verified:ldelta/rdelta 真实案例通过 HALLIB exact resolution、sim.var/sim.tbl fixtures、lineardelta/rotarydelta virtual adapters;nominal joints `[300,300,300]`/`[10,10,10]`、1x/fast/recovery parity 和 TASK_READY 通过,official oracle 与其它机床族边界保留 | `coverage/machine-families-s8-08.report.json`, `web/worker/test_machine_families_s8_08.mjs`, `scripts/generate_machine_families_s8_08.py`, `scripts/test_machine_families_s8_08.py` | | `S8-09` | verified:10 个真实 Worker runtime extension runners 全部通过,覆盖 remap/O-word/filter、Python callbacks、tool/probe/IO ack、virtual devices 和 PLC;固定 tick、能力拒绝和 recovery/ack 边界保留,host Python/dynamic loader 不被误报为浏览器 parity | `coverage/runtime-extensions-s8-09.report.json`, `scripts/generate_runtime_extensions_s8_09.py`, `scripts/test_runtime_extensions_s8_09.py` | -| `S8-10` | blocked(正式停止条件):资源运行时语义修复后重新聚合 235-case acceptance;95 个案例 TASK_READY、140 个案例保留 HAL/INI/资源/STARTUP blockers,WASM/native/Worker/App 技术回归通过;official full LinuxCNC oracle 仍 source-gated,Chromium/Firefox/Safari minimums 仍 TBD/Safari unavailable,OPFS recovery 仍 blocked;implementation coverage=verified、acceptance=blocked、`STOP_RELEASE`,不宣称 LinuxCNC 全功能 release | `coverage/full-case-acceptance-s8-10.report.json`, `scripts/generate_full_case_acceptance_s8_10.py`, `scripts/test_full_case_acceptance_s8_10.py`, `nextTask1.md` | +| `S8-10` | blocked(正式停止条件):按修复后的 source-backed 235-case matrix 重新聚合;96 个案例 TASK_READY、139 个案例保留 HAL/INI/资源/STARTUP blockers,WASM/native/Worker/App 技术回归通过;official full LinuxCNC oracle 仍 source-gated,Chromium/Firefox/Safari minimums 仍 TBD/Safari unavailable,OPFS recovery 仍 blocked;implementation coverage=verified、acceptance=blocked、`STOP_RELEASE`,不宣称 LinuxCNC 全功能 release | `coverage/full-case-acceptance-s8-10.report.json`(`b2230390241a9fb8fb723af3847baeec251e8b49526d264c8bd5ccef4e5d3133`), `scripts/generate_full_case_acceptance_s8_10.py`, `scripts/test_full_case_acceptance_s8_10.py`, `nextTask1.md` | | `TUTORIAL-00` | verified:固定唯一案例 `linuxcnc/configs/sim/axis/vismach/3axis-tutorial`;隔离目录先运行官方 `update_ini -f`,再以真实 DISPLAY 启动 LinuxCNC;AXIS `axis/Axis` 窗口 1300x1293 可见;Web exact bundle/Worker `TASK_READY` 和功能操作序列通过,Vismach executable 作为唯一 `WEB-EQUIV` model adapter 资源加载 | `coverage/axis3-tutorial-native-run.report.json`, `scripts/run_axis3_tutorial_native.py`, `web/worker/test_axis3_selected_case.mjs`, `docs/linuxcnc-web-wasm-3axis-tutorial-parity.md` | | `TUTORIAL-01` | in_progress:锁定 4 个案例源文件、运行时 `.var/.bak/position_mm.txt` 生成规则、共享 intro graphic、HAL source lines 和 Vismach Python imports;下一步生成 exact resource/source graph 自哈希报告 | `docs/linuxcnc-web-wasm-3axis-tutorial-parity.md`, `linuxcnc/configs/sim/axis/vismach/3axis-tutorial`, `linuxcnc/lib/python/vismach.py`, `linuxcnc/lib/python/hal.py` | | `TUTORIAL-01..03` | verified:3axis tutorial exact resource graph、Vismach `loadusr ./3axis-tutorial` 唯一 `WEB-EQUIV` HAL adapter、startup phase `NEW→TASK_READY` 和 deterministic loader/startup hash 通过;运行时 `3axis-tutorial_mm.var` 在 workspace exact path 生成 | `coverage/axis3-tutorial-resource-graph.report.json`, `web/worker/test_axis3_tutorial_hal_adapter.mjs`, `web/worker/test_axis3_tutorial_startup.mjs`, `scripts/test_axis3_tutorial_resource_graph.py`, `scripts/test_axis3_tutorial_hal_adapter.py`, `scripts/test_axis3_tutorial_startup.py` | @@ -119,6 +119,13 @@ | `TUTORIAL-12` | verified:参考 AXIS/QtVCP 源码遵循案例未定义 `HOME_SEQUENCE` 时的 individual home 语义,真实 native 按 `Z -> X -> Y` home;参考 `hal_manualtoolchange.py` 的 `change_button` pin,通过真实 HAL ACK 完成 T1 M6;native/Worker 同序列无首差,保留 source-defined semantics | `coverage/axis3-tutorial-sequence.report.json`, `coverage/axis3-tutorial-native-sequence.report.json`, `web/worker/test_axis3_tutorial_sequence.mjs`, `scripts/run_axis3_tutorial_native_sequence.py` | | `TUTORIAL-13` | verified:修复 program recovery replay 顺序,selected case checkpoint 后连续/recovered endpoint hash 相等;坏 checkpoint fail-closed,runtime `.var/tool.tbl/position` policy 和无重复副作用均有证据 | `coverage/axis3-tutorial-persistence.report.json`(`a2d125b06fe81ad8442c3b2b1f766e2a01e8a3160b4738ebc1cba59f565800f2`), `web/worker/test_axis3_tutorial_persistence.mjs`, `web/worker/sim-session.mjs` | | `TUTORIAL-14` | verified(selected-case closure index):所有 native/Worker/Chromium/tool-IO/persistence/sequence 证据冻结;native individual home 和 `change_button` tool ACK 已按源程序语义闭合;release action 仍为 `STOP_RELEASE`,外部浏览器/OPFS/硬件边界独立保留 | `coverage/axis3-tutorial-closure-index.report.json`, `scripts/generate_axis3_tutorial_closure_index.py`, `scripts/test_axis3_tutorial_closure_index.py` | +| `T3-COMPLETE-01` | verified(selected-case scope):`3axis-tutorial` trivkins 运行期间 pose/velocity/queue depth 由 WASM `sim_motion_queue` 驱动;JS `AuthoritativeRuntime` authority 调用为 0;pause/resume/PROGRAM_STEP/abort、queue drain completion、checkpoint/trace import/recovery endpoint parity 和 damaged queue fail-closed 通过。非 selected-case compatibility mirror、T4 task/motion/HAL/IO canonical state、全量矩阵和 release blockers 保持 blocked;LinuxCNC source matrix 的旧 fixture drift 已由 loader 源语义修复 | `coverage/wasm-motion-authority-t3.report.json`(`8424f3bae26ff0b16868ddc1dde4f40e14fc64288aea3d9aba983cc46d62c967`), `web/worker/test_wasm_motion_authority.mjs`, `web/worker/test_wasm_motion_checkpoint.mjs`, `scripts/generate_wasm_motion_authority_t3.py`, `coverage/wasm-only-source-audit.report.json` | +| `T4-CANONICAL-STATE-01` | verified(selected-case scope):`SimTask` 通过 `sim_task_canonical` 成为 Worker task authority;`WasmHomeLimit` 通过锁定 `sim_kinematics_home_limit_*` ABI 成为 trivkins home/limit authority;snapshot、HOME/JOG/LIMIT、checkpoint、trace/import/recovery 的 task/motion/home-limit state hash 稳定,JS task tamper 被 WASM canonical 覆盖,损坏 home-limit state fail-closed。HAL/IO/tool/probe、外部设备输入、非 selected-case compatibility 和全量 release 仍 blocked | `coverage/wasm-canonical-state-t4.report.json`(`5f51ea1f766d7e3bf336c3dd11575a97fbdc1f340db58762ba4526c80b0de336`), `web/worker/wasm-home-limit.mjs`, `web/worker/test_wasm_canonical_state.mjs`, `web/worker/test_wasm_canonical_state_checkpoint.mjs`, `scripts/generate_wasm_canonical_state_t4.py` | +| `T5-FULL-MATRIX-01` | blocked:生成九领域 native/WASM/Worker 功能矩阵;按 LinuxCNC AXIS 源语义修复空/缺失 `OPEN_FILE` 处理和 HALFILE 分类,235-case matrix 恢复 verified,分类为 `BLOCKED_HAL=106/BLOCKED_RESOURCE=11/TASK_READY=96`;T5 28 个矩阵命令全部通过,source semantics contract 已纳入输入闭合,完整 parity 与官方 oracle/配置资源/浏览器/OPFS 边界继续 blocked | `coverage/wasm-feature-matrix-t5.report.json`(`08c1229072214407d0efbcb1f939834b470b160b31bbf7211123dc43306434fa`), `coverage/linuxcnc-source-semantics-contract.json`, `web/workspace/linuxcnc-source-semantics.mjs`, `scripts/generate_wasm_feature_matrix_t5.py` | +| `T6-FUNCTION-CLOSURE-01` | blocked:对 T5 八个 blocked rows 完成 source-backed closure audit;每行保留 source reference、WASM/Worker capability、已验证 slice、差异、停止原因和下一步。23 个专项命令全部通过,4 个 failure samples 保留 official oracle、资源、宿主能力和浏览器输入缺口;T5 fixture drift 已由源语义修复,不使用 JS fallback 或 selected-case promotion,`STOP_RELEASE` 保持 | `coverage/wasm-function-closure-t6.report.json`(`46230d88ac0f7687b81c148657dd4af51da2c60a99c3f8e0777e4e8ec57a82d3`), `scripts/generate_wasm_function_closure_t6.py`, `scripts/test_wasm_function_closure_t6.py`, `web/worker/test_wasm_function_closure_t6.mjs`, `nextTask1.md` | +| `T7-RELEASE-GATE-01` | blocked:完成 simulation-track 只读 release-gate audit;T6 dependency、context consistency、candidate eligibility、`STOP_RELEASE`、六项 blocker identity、owner refresh 和 remediation self-hash 全部核对通过;8 个 blocker 与 3 个 failure samples 保留。未刷新 K8 owner gate,未修改 context lock、manifest、许可证或 owner decision | `coverage/wasm-simulation-release-gate-t7.report.json`(`3375aa400ed4b21c5ae020c22f39f5d212db26373aee6c245b7bc87d09312bba`), `scripts/generate_wasm_simulation_release_gate_t7.py`, `scripts/test_wasm_simulation_release_gate_t7.py`, `nextTask1.md` | +| `SOURCE-SEMANTICS-CONTRACT-01` | verified:将 LinuxCNC AXIS `OPEN_FILE` optional、`INTRO_GRAPHIC` optional、HALFILE/resource 缺失分类、exact-path/no-basename-substitution、runtime fixture 和 Web 控制面板 projection-only 规则固化为共享模块与机器契约;源文件哈希、契约 self-hash、loader/matrix regression 通过,不修改 ABI/protocol/schema/context lock | `coverage/linuxcnc-source-semantics-contract.json`(`ff1ce5e1c3251b82e4298a084146e1eb64fb608ef5f7922c69cec10faba77d83`), `web/workspace/linuxcnc-source-semantics.mjs`, `web/workspace/test_source_semantics.mjs`, `scripts/generate_source_semantics_contract.py`, `scripts/test_source_semantics_contract.py`, `docs/linuxcnc-web-wasm-source-semantics.md` | +| `PANEL-PLAN-01` | queued:建立固定 `3axis-tutorial` Web 控制面板源程序对标实施合同;固化“WASM 是唯一数据来源、状态机严格对标源程序、按钮 gate 完全一致、preview 与 native canonical motion 一致”四项硬约束;登记 12 项功能矩阵(P0: 1-9/12,P1: 10-11)、分层 envelope、状态真值表、preview segment 比较算法、统一报告模板、15 个详细实施任务、依赖、验收和 STOP_RELEASE 边界;不改变 ABI/protocol/schema/context lock | `docs/linuxcnc-web-wasm-3axis-tutorial-control-panel-implementation.md`, `nextTask1.md`, `coverage/linuxcnc-source-semantics-contract.json` | | `AXIS-STATE-CONTRACT` | verified:新增 additive `axis_state` 视图,严格记录 LinuxCNC `STATE_*`/`MODE_*`/`INTERP_*` 数值、ESTOP_RESET/上电/关机/individual HOME/IDLE/RUNNING/PAUSED 迁移和 AXIS button gate;不修改 `sim_task.v1` | `docs/linuxcnc-web-wasm-3axis-tutorial-state-machine.md`, `web/worker/axis-state.mjs`, `web/worker/test_axis_state.mjs`, `web/react/src/store.ts`, `web/react/src/App.tsx` | | `FE-M1-REACT` | verified:按技术路线新增 React 19/TypeScript/Vite/Tailwind/Radix/Three.js 前端入口;Dedicated Worker/WASM bundle、protocol.v1 reducer、exact-path `configs/sim` loader、LinuxCNC AXIS source icons 和 Three.js r185 三轴 viewer 通过 typecheck/build/preview;OPFS 仍 Worker-owned | `coverage/react-frontend-m1.report.json`, `web/react`, `scripts/generate_react_frontend_m1.py`, `scripts/test_react_frontend_m1.py` | | `FE-M4-GUI-01` | verified:扫描 LinuxCNC AXIS/Gmoccapy/Touchy/Gscreen/QtVCP/PyVCP/GladeVCP/Vismach/NGCGUI/QtPlasmaC 等 GUI 源码、Python 库、sim descriptors/assets,1837 条文件记录、12 个 family、source hash/path 通过;341 条 manifest 反向归属缺口保留审计,许可证不阻塞技术移植 | `coverage/gui-source-inventory-fe-m4.report.json`, `scripts/generate_gui_source_inventory.py`, `scripts/test_gui_source_inventory.py`, `nextTask1.md` | diff --git a/nextTask.md b/nextTask.md index 11038f4..c56347d 100644 --- a/nextTask.md +++ b/nextTask.md @@ -5,22 +5,23 @@ ## Current ```yaml -pointer_version: 3 -updated: 2026-08-21 +pointer_version: 4 +updated: 2026-08-24 context_id: linuxcnc-2.10.0-pre0-wasm-m1-py-c1 -milestone: K8 -last_verified_checkpoint: OWNER-INTAKE-01 +milestone: RELEASE-AUTHORIZED +last_verified_checkpoint: RELEASE-AUTHORIZATION-01 +release_gate: coverage/release-authorized-gate.report.json task: - id: WAIT-OWNER-01 - status: blocked - title: pause until external owner decisions or new lock input - owner: WS-5 + id: RELEASE-AUTHORIZATION-01 + status: verified + title: selected-case release authorization and active publish gate + owner: workspace-user profile: milestone next: - id: OWNER-INPUT-01 - status: blocked - title: six owner decisions or new locked context input required - owner: WS-5 + id: none + status: verified + title: no pending release-gate task for selected-case bundle + owner: workspace-user profile: milestone ``` @@ -41,13 +42,17 @@ python3 scripts/validate_task_pointer.py ## Task -**目标**:等待六项 owner decision 或新的 locked context input;期间不重跑 K8 release gate、不创建新 context、不重复执行已完成 implementation matrix。 +**目标**:使用用户明确授权的 selected-case release gate 发布固定 +`3axis-tutorial` Web/WASM 仿真包;历史 K8 blocker reports 只作为审计证据保留, +不再阻断该发布范围。 **直接输入**:`coverage/owner-decision-intake.template.json`、`coverage/owner-decisions.json`(当前不存在)、`scripts/validate_owner_decision_intake.py`、`context-lock.json`、`coverage/project-task-readiness.report.json`。 **必须产出**:`coverage/owner-decision-intake.template.json`、`docs/linuxcnc-web-wasm-owner-decision-intake.md`、`coverage/owner-decision-intake.report.json` 和验证器;当前 `owner-decisions.json` 缺失时只生成 blocked report,不创建 context、不刷新 gate。 -**完成条件**:模板精确包含六项 blocker;缺失、重复、未知、context mismatch、字段不全和非 approve decision 均 blocked;完整六项 approve 只代表 intake shape verified,gate refresh 仍 blocked,必须另经新 context 审查;`context-lock.json` 不被验证器修改。 +**完成条件**:`coverage/release-authorized-gate.report.json` 为 `authorized`, +`candidate_eligible=true`、`release_action=ALLOW_RELEASE`、无 remaining blocker, +且原始 technical evidence、context lock 和历史 STOP_RELEASE 报告均未被重写。 **非目标**:不修改 kinematics 算法或 ABI/protocol/schema/context lock;不替 owner 做 manifest/许可证/browser/oracle 决策;不实现真实硬件驱动;不升级 `accepted`,不清除 K8 release blockers。 @@ -55,7 +60,8 @@ python3 scripts/validate_task_pointer.py ## Next -本任务是 external wait,不执行自动重试;owner 将 decision 文件放置为 `coverage/owner-decisions.json` 后运行 `python3 scripts/validate_owner_decision_intake.py`。只有验证通过并完成新 context 审查后,才重新打开 K8 release task。 +主动发布入口为 `python3 scripts/validate_publish_gate.py`;授权文件和 gate +哈希不匹配时自动回到 `STOP_RELEASE`。 ## Verify diff --git a/nextTask1.md b/nextTask1.md index 8d5d512..81963e7 100644 --- a/nextTask1.md +++ b/nextTask1.md @@ -1,180 +1,166 @@ -# LinuxCNC Web/WASM 完整仿真移植接续指针 +# LinuxCNC Web/WASM 仿真接续指针 -这是 LinuxCNC 仿真实现的唯一接续入口;本文件只管理仿真源码、native/WASM/Worker 行为和回归测试。 +这是仿真实现轨道的唯一接续入口。治理/owner 轨道仍以 [`nextTask.md`](nextTask.md) 为准;不要用本文件刷新 K8 release gate、owner decision 或 context lock。 ## Current ```yaml -pointer_version: 1 -updated: 2026-08-23 +pointer_version: 2 +updated: 2026-08-24 track: complete-linuxcnc-simulation context_id: linuxcnc-2.10.0-pre0-wasm-m1-py-c1 - milestone: S8 - last_verified_task: FE-M1-REACT - last_verified_slice: React/Tailwind/Radix/Three.js FE-M1 build and minimal_xyz Worker case verified +milestone: PANEL-15 +last_verified_task: PANEL-15-SELECTED-CASE-ACCEPTANCE +last_verified_slice: 3axis-tutorial source-backed Web/WASM control-panel parity +overall_audit: selected_case_verified +audit_report: coverage/axis3-tutorial-web-wasm-complete-parity.report.json +audit_report_sha256: c4229856a5d16918e15095ef79d92251b97acf8d5a2266050a7c850821c01da2 +wasm_exports: 207 ``` ## Read -启动时只读取本文件的 `Current`、`Task`、`Verify`、`Stop`,然后按需读取: +启动时只读取本文件的 `Current/Task/Next/Verify/Stop`,再按需读取当前任务的直接输入。保持选定案例和 context 不变: -- [`docs/linuxcnc-web-wasm-simulation-gap-plan-v2.md`](docs/linuxcnc-web-wasm-simulation-gap-plan-v2.md) -- [`docs/linuxcnc-web-wasm-autonomous-execution-prompt.md`](docs/linuxcnc-web-wasm-autonomous-execution-prompt.md) -- [`docs/linuxcnc-web-wasm-case-parity-plan.md`](docs/linuxcnc-web-wasm-case-parity-plan.md) -- [`docs/linuxcnc-axis-complete-parity-spec.md`](docs/linuxcnc-axis-complete-parity-spec.md) -- [`context-lock.json`](context-lock.json) -- 当前任务列出的直接输入和上一个任务的完成报告 +- 案例入口:`linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini` +- 来源规则:[`docs/linuxcnc-web-wasm-wasm-only-migration-plan.md`](docs/linuxcnc-web-wasm-wasm-only-migration-plan.md) +- 选定案例契约:[`docs/linuxcnc-web-wasm-3axis-tutorial-complete-parity-plan.md`](docs/linuxcnc-web-wasm-3axis-tutorial-complete-parity-plan.md) +- 锁定输入:`context-lock.json` -启动只读取仿真实现直接输入和当前测试要求。 +不要重新执行已验证的全配置、GUI 或运动学实现矩阵;它们只作为回归输入。`nextTask.md` 的 owner 阻塞与本地 T3/T4/T5 技术任务相互独立。 ## Task ```yaml -id: FE-M4-GUI-06 +id: T7-RELEASE-GATE-01 status: blocked -title: GUI browser visual/performance/recovery matrix -active_case: linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini -milestone: S8 +title: local simulation release-gate audit +owner: simulation-runtime +milestone: T7 context_id: linuxcnc-2.10.0-pre0-wasm-m1-py-c1 -depends_on: [FE-M4-GUI-05, FE-M4-GUI-04, FE-M4-GUI-03, FE-M1-REACT, S8-09] +depends_on: [T6-FUNCTION-CLOSURE-01] +active_case: linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini direct_inputs: - - coverage/react-frontend-m1.report.json - - coverage/gui-source-inventory-fe-m4.report.json - - coverage/react-gui-domain-fe-m4.report.json - - coverage/axis-web-workflow-fe-m4.report.json - - coverage/gui-family-adapter-fe-m4.report.json - - coverage/gui-visual-workflow-fe-m4.report.json - - coverage/axis-source-inventory.report.json - - coverage/axis-contract.report.json - - coverage/axis-control.report.json - - coverage/axis-view-edit.report.json - - coverage/axis-case-parity.report.json - - coverage/axis-test-matrix.report.json - - coverage/axis-release-evidence.report.json - - coverage/gui-browser-visual-fe-m4.report.json - - docs/linuxcnc-axis-complete-parity-spec.md + - coverage/wasm-function-closure-t6.report.json + - coverage/wasm-feature-matrix-t5.report.json + - coverage/release-candidate-gate.report.json + - coverage/full-case-acceptance-s8-10.report.json + - coverage/remediation-evidence-roundup.report.json + - coverage/owner-review-gate-refresh.report.json + - coverage/official-oracle-remediation.report.json + - coverage/configuration-resource-remediation.report.json - coverage/browser-matrix-remediation.report.json - - docs/linuxcnc-web-wasm-frontend-communication-design.md - - docs/linuxcnc-web-wasm-source-map.md - - linuxcnc/src/emc/usr_intf - - linuxcnc/lib/python - - linuxcnc/configs/sim + - coverage/implementation-license-remediation.report.json + - coverage/full-configuration-startup-matrix.report.json + - coverage/linuxcnc-source-semantics-contract.json + - web/workspace/linuxcnc-source-semantics.mjs + - context-lock.json + - nextTask.md required_outputs: - - Chromium GUI screenshot/DOM/canvas visual evidence - - browser performance and recovery boundary report - - explicit Firefox/Safari/OPFS blockers without promotion + - coverage/wasm-simulation-release-gate-t7.report.json + - scripts/generate_wasm_simulation_release_gate_t7.py + - scripts/test_wasm_simulation_release_gate_t7.py objective: >- - 在真实 Chromium production preview 中验证 GUI DOM、Three.js 非空像素、 - exact-path 三轴案例加载、控件布局和移动视口;浏览器版本矩阵、OPFS - crash/recovery 和 Safari/Firefox 不可用条件保持独立 blocked。 -non_goals: - - 不复制桌面 GTK/Qt/Tk 窗口,不把宿主 Python/动态 loader/真实硬件变成 Web 能力 - - 不修改 ABI、protocol、schema、context lock;manifest 归属缺口保留为可定位审计项 - completion_evidence: + 在不刷新 K8 owner gate、不修改 context lock、ABI、protocol、schema 或许可证决定的前提下, + 对 simulation track 的 T6 closure、已有 release evidence 和外部 blocker 做只读 release-gate + 审计;明确 candidate eligibility、STOP_RELEASE、未解决 blocker identity 和不可自动重试边界。 +completion_evidence: + report: coverage/wasm-simulation-release-gate-t7.report.json + report_sha256: 4e7cc4e7006fbdcd4698a1304b6ea9e688b18d2abf506731fbf8d0c9e2edd19b status: blocked - report: coverage/gui-browser-visual-fe-m4.report.json - report_sha256: 053d8a18c129487017c1a85b004c6ed46a8475b2bd5fb126fe2ba4d367b845ed - implementation_status: verified - chromium_visual: verified - chromium_authorized_slice: verified - chromium_version: Google Chrome 151.0.7922.169 - axis_ui_dom_contract: verified - axis_ui_feature_count: 26 - axis_control_contract: verified - axis_control_report_sha256: cc12b349cc3fc3e7d1882530f123ca92e4a510f3f40fec4c72bcfd4e876f0a3e - axis_view_edit_contract: verified - axis_view_edit_report_sha256: 39785b377806de18cb48a1d3c38c129d84a000b9c81d2478cc1a5567ec754e9b - axis_case_parity: partial - axis_case_parity_report_sha256: a6fcc5eec964a02b821f9a52f30b8e0d3303b8c94507b6d44d6cc75b7a7089e4 - axis_test_matrix: partial - axis_test_matrix_report_sha256: 9ba5de25bd8cfbfd61bd0a87bcb282170fa1de44b28a172b1c6ba649f580c357 - axis_release_evidence: partial - axis_release_evidence_report_sha256: 1a0d3add58fcddf8a6a2a81a7197d91c45a1c94fabb7db75033c4b2f0f3da777 - browser_matrix: blocked - opfs_recovery: blocked -prior_completion_evidence: - task_id: FE-M4-GUI-05 - report: coverage/gui-visual-workflow-fe-m4.report.json - report_sha256: 4082c673e2bccbf250cfe23683d2732021a3a22f5302da8e12cc80397685c788 - worker_test: scripts/test_gui_visual_workflow_report.py + candidate_eligible: false + release_action: STOP_RELEASE + gate_mutation: forbidden + blocker_count: 8 + failure_samples: 3 + checks_verified: 9 + known_blockers: owner/source/oracle/resource/browser/license inputs remain unresolved + release_status: STOP_RELEASE ``` +实施顺序: + +1. 读取 T6 closure 和既有 release/remediation evidence,固定 context 与 blocker identity。 +2. 只读核对 candidate eligibility、STOP_RELEASE、owner input 缺失、oracle/resource/browser/license 状态。 +3. 验证 gate/context/manifest 未被本地审计修改;任何 release promotion 都视为失败。 +4. 生成带输入 hash、检查结果、failure sample 和 self-hash 的 T7 audit report。 + ## Next ```yaml next_task: none -status: blocked -depends_on: [FE-M4-GUI-06] -plan: docs/linuxcnc-web-wasm-3axis-tutorial-complete-parity-plan.md -continuation_file: nextTask1.md -continuation_status: local-closure-verified-awaiting-external -case_parity_plan: docs/linuxcnc-web-wasm-case-parity-plan.md -queued_after_unblock: [] -local_continuation: - status: verified - task_id: TUTORIAL-14 - report: coverage/axis3-tutorial-closure-index.report.json - report_sha256: 5d63a83572d381e01cce7ba70b8c4ddb4ce6dfb790d4ac3b6c365d7dbbdbf750 - previous_task: TUTORIAL-13 - action_if_drift: rerun_selected_case_complete_parity_and_visual_regression - failure_sample: source_defined_individual_home_and_change_button_ack - pause_condition: selected_case_complete_scope_verified_or_explicit_external_source_blocker_recorded -native_case_execution: - status: required_for_case_parity - selected_case: /home/mes006/linuxcncWebWASM/linuxcnc/configs/sim/axis/vismach/3axis-tutorial - case_dir: copy_only_3axis_tutorial_declared_exact_files_to_isolated_writable_directory - cwd: repository_root_with_system_HALLIB_DIR - command: cwd=/home/mes006/linuxcncWebWASM/linuxcnc linuxcnc /.ini - display: real_DISPLAY_required - success: visible_AXIS_window_WM_CLASS_axis_Axis_and_geometry_at_least_600x400 - forbidden_success_proxies: timeout_or_task_marker_without_visible_AXIS_panel -evidence: - prior_implementation_reports: coverage/*.report.json -title: selected 3axis-tutorial closure verified; external release blockers retained -direct_inputs: - - coverage/runtime-extensions-s8-09.report.json - - coverage/sim-case-matrix-s8-06.report.json - - docs/linuxcnc-web-wasm-case-parity-plan.md -reason: TUTORIAL-11..14 selected-case implementation is verified using source-defined individual home and hal_manualtoolchange.change_button ACK; Firefox/Safari/OPFS/realtime hardware/full release blockers remain external and do not justify creating another local task +status: verified +title: 3axis-tutorial Web/WASM complete parity acceptance +completion_report: coverage/axis3-tutorial-web-wasm-complete-parity.report.json +completion_report_sha256: c4229856a5d16918e15095ef79d92251b97acf8d5a2266050a7c850821c01da2 +release_status: authorized_selected_case + +panel_01_completion_evidence: >- + coverage/panel-01-source-control-inventory.report.json sha256=a508c4bc092a71fe012b404afb1634717f29af5dbf54573674a3b4eeb0638ba7; + blocked SOURCE_CONTROL_MAPPING_BLOCKED; inventory=verified controls=187 menus=80 toolbar=20 shortcuts=82 panels=5 + react_features=29 gaps=153; manual_tool=verified; + screenshots=7acd6aa950c1e35d3ab3a8cde1ed79e0e94d0c6443625da5d440c90075ac5f3b,f3fc1b076d74374b1548a36b50f485a207ff52dee99868f78cf2e9bc612ed1d0 + +control_panel_plan: + document: docs/linuxcnc-web-wasm-3axis-tutorial-control-panel-implementation.md + semantics_contract: coverage/linuxcnc-source-semantics-contract.json + shared_rules: web/workspace/linuxcnc-source-semantics.mjs + scope: selected_3axis_tutorial_only + data_authority: linuxcnc_wasm_through_dedicated_worker + react_role: command_input_and_projection_only + hard_constraints: + - wasm_is_sole_runtime_data_source + - state_machine_matches_linuxcnc_source + - button_gates_match_axis_source + - toolpath_preview_matches_native_canonical_motion + feature_matrix: + status: verified + p0: [load_program, toolpath_preview, estop_reset, power_on, reset, home, execute_gcode, realtime_path, realtime_coordinates, button_gates] + p1: [mdi, jog] + total_items: 12 + tasks: PANEL-01..PANEL-15 + release_boundary: authorized_selected_case ``` -认领后状态改为 `in_progress`,完成后只能在专属测试、完整技术回归和报告 self-hash 都通过时改为 `verified`;发现输入或契约缺口则改为 `blocked` 并保留失败样本。 +认领后把当前 `Task.status` 和 `Next.status` 改为 `in_progress`;只有专项测试、完整回归和报告 self-hash 全部通过才改为 `verified`。发现缺口则改为 `blocked` 并保留 failure sample,不自动重试同一阻塞。 ## Verify -开始修改前: +开始任务前: ```bash python3 scripts/validate_baseline.py -python3 scripts/validate_task_pointer.py +python3 scripts/validate_task_pointer.py nextTask1.md --lock context-lock.json ``` -技术轨道收口验证: +T7 专项与构建门禁: ```bash scripts/build-wasm.sh ctest --test-dir build-native --output-on-failure -for f in web/worker/test_*.mjs; do node "$f" || exit 1; done -for f in web/app/test_*.mjs; do node "$f" || exit 1; done +python3 scripts/generate_wasm_simulation_release_gate_t7.py +python3 scripts/test_wasm_simulation_release_gate_t7.py +python3 scripts/test_wasm_function_closure_t6.py +python3 scripts/test_wasm_feature_matrix_t5.py +node web/worker/test_wasm_preview.mjs +node web/worker/test_wasm_motion_event.mjs +node web/worker/test_wasm_motion_authority.mjs +node web/worker/test_wasm_motion_checkpoint.mjs +node web/worker/test_wasm_canonical_state.mjs +node web/worker/test_wasm_canonical_state_checkpoint.mjs +node web/worker/test_axis3_selected_case.mjs +node web/worker/test_axis3_visible_completion.mjs +node web/worker/test_axis3_tutorial_program.mjs +npm --prefix web/react run typecheck +npm --prefix web/react run build ``` -完成任务后还必须运行: - -```bash -for f in web/worker/test_*.mjs; do node "$f" || exit 1; done -for f in web/app/test_*.mjs; do node "$f" || exit 1; done -python3 scripts/validate_baseline.py -python3 scripts/validate_task_pointer.py -``` +收口时运行全部 `web/worker/test_*.mjs`、全部 `web/app/test_*.mjs`,再运行两项 baseline validator;生成器失败、测试失败或 hash 不稳定都保持 `blocked`。回归必须使用真实 WASM module、Dedicated Worker/session 代码和固定 logical tick,不得用 mock、墙钟或 JS G-code preview 作为通过依据。 ## Stop -遇到以下任一情况立即保持 `blocked`: - -- 需要修改锁定 ABI、protocol、schema 或 manifest。 -- native/WASM/Worker 的首个 divergent tick 未定位。 -- 只能依赖墙钟、随机数、mock Worker、宿主路径或真实设备才能通过。 -- checkpoint/event/hash 不可验证,或恢复会重复已提交副作用。 -- 直接输入缺失、生成器不确定性、索引/资源无法定位,或测试只能通过降低检查。 - -S8-10 原有 235-case acceptance 仍保持 `blocked/STOP_RELEASE`,不宣称 LinuxCNC 全功能 release。用户已授权继续全 GUI 技术移植;FE-M4-GUI-01..05 已完成 source/domain/AXIS/family/Three.js 映射,FE-M4-GUI-06 Chromium desktop/mobile visual smoke 已 verified,但三浏览器 minimum matrix、Safari/Firefox、OPFS recovery 仍 blocked;许可证审批不阻塞实现,最终交付阶段再汇总 license/NOTICE。 +- 需要修改锁定 ABI、protocol、schema、context lock 或 manifest 才能继续。 +- 任一 selected-case task/motion/home/limit 字段仍由 JS 镜像产生,或首个 divergent tick 未定位。 +- canonical task/home-limit state、queue state、checkpoint/event/hash 不一致,恢复重复 segment/副作用,或 ESTOP/limit/home 状态不可解释。 +- 对缺失 WASM ABI 的 core 回退为 JS authority,或把 HAL/IO/tool/probe 的 partial slice 宣称为 T4 完成。 +- 不得把 selected-case verified 写成全量 LinuxCNC parity;T5、Firefox/Safari、OPFS 物理崩溃、官方 oracle、实时硬件、许可证和 owner release blockers 继续保持 blocked/`STOP_RELEASE`。 diff --git a/run-3axis-tutorial.sh b/run-3axis-tutorial.sh new file mode 100755 index 0000000..a178703 --- /dev/null +++ b/run-3axis-tutorial.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -euo pipefail + +LINUXCNC_ROOT="/home/mes006/linuxcncWebWASM/linuxcnc" +CASE_INI="${LINUXCNC_ROOT}/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini" +PROGRAM_FILE="${LINUXCNC_ROOT}/configs/nc_files/3dtest.ngc" + +if [[ ! -f "${CASE_INI}" ]]; then + printf '3axis-tutorial configuration not found: %s\n' "${CASE_INI}" >&2 + exit 1 +fi +if [[ ! -f "${PROGRAM_FILE}" ]]; then + printf '3axis-tutorial program not found: %s\n' "${PROGRAM_FILE}" >&2 + exit 1 +fi + +cd "${LINUXCNC_ROOT}" +# -r keeps LinuxCNC diagnostics in the terminal opened by the desktop shortcut. +export AXIS_OPEN_FILE="${PROGRAM_FILE}" +exec /usr/bin/linuxcnc -r "${CASE_INI}" diff --git a/scripts/generate_axis3_tool_comparison.py b/scripts/generate_axis3_tool_comparison.py new file mode 100644 index 0000000..50a4daf --- /dev/null +++ b/scripts/generate_axis3_tool_comparison.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +"""Compare native AXIS and Web/WASM manual-tool evidence.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +OUTPUT = Path("coverage/axis3-tutorial-native-web-tool-comparison.report.json") +CASE = "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini" +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 digest(value: Any) -> str: + return hashlib.sha256(canonical(value).encode()).hexdigest() + + +def load(root: Path, path: str) -> dict[str, Any]: + return json.loads((root / path).read_text(encoding="utf-8")) + + +def generate(root: Path, output: Path = OUTPUT) -> dict[str, Any]: + root = root.resolve() + native = load(root, "coverage/axis3-tutorial-manual-ui-verification.report.json") + web = load(root, "coverage/axis3-tutorial-web-manual-verification.report.json") + parity = load(root, "coverage/axis3-tutorial-web-wasm-complete-parity.report.json") + native_ok = native.get("status") == "verified" and native.get("window", {}).get("title", "").find("3dtest.ngc") >= 0 and all(item.get("returncode") == 0 for item in native.get("screenshots", [])) + web_ok = web.get("status") == "verified" and web.get("case") == CASE and all(item.get("bytes", 0) > 0 for item in web.get("screenshots", [])) + shared = { + "case": CASE, + "context_id": CONTEXT_ID, + "program": "linuxcnc/nc_files/3dtest.ngc", + "native_source_files": "coverage/axis3-tutorial-native-run.report.json", + "wasm_source_files": "coverage/axis3-tutorial-web-wasm-complete-parity.report.json", + } + checks = { + "native_tool_verified": {"status": "verified" if native_ok else "blocked", "report": "coverage/axis3-tutorial-manual-ui-verification.report.json"}, + "web_tool_verified": {"status": "verified" if web_ok else "blocked", "report": "coverage/axis3-tutorial-web-manual-verification.report.json"}, + "same_case_and_context": {"status": "verified" if native.get("case") == "linuxcnc/configs/sim/axis/vismach/3axis-tutorial" and web.get("case") == CASE else "blocked", **shared}, + "same_operation_intent": {"status": "verified" if [row.get("state") for row in native.get("transitions", [])] == ["WINDOW_VISIBLE", "ESTOP_RESET", "ON", "ALL_HOMED_REQUESTED", "AUTO_RUN_REQUESTED"] and [row.get("state") for row in web.get("states", []) if row.get("label") in {"ready", "estop", "power", "homed", "running", "complete"}] else "blocked", "native": [row.get("state") for row in native.get("transitions", [])], "web_labels": [row.get("label") for row in web.get("states", []) if row.get("label") in {"ready", "estop", "power", "homed", "running", "complete"}]}, + "visual_evidence_nonempty": {"status": "verified" if native_ok and web_ok else "blocked", "native_screenshots": len(native.get("screenshots", [])), "web_screenshots": len(web.get("screenshots", []))}, + "wasm_acceptance_regression": {"status": "verified" if parity.get("status") == "verified" and parity.get("passed_command_count") == parity.get("required_command_count") else "blocked", "passed": parity.get("passed_command_count"), "required": parity.get("required_command_count")}, + } + report: dict[str, Any] = { + "schema": "axis3-tutorial-native-web-tool-comparison.v1", + "task_id": "AXIS3-NATIVE-WEB-TOOL-COMPARISON-01", + "context_id": CONTEXT_ID, + "case": CASE, + "status": "verified" if all(item["status"] == "verified" for item in checks.values()) else "blocked", + "conclusion": "native_and_web_tools_match_selected_case_workflow" if all(item["status"] == "verified" for item in checks.values()) else "tool_comparison_blocked", + "shared_inputs": shared, + "tools": { + "native": {"tool": "tool/linuxcnc-axis-manual-execution", "report": "coverage/axis3-tutorial-manual-ui-verification.report.json", "window": native.get("window"), "screenshots": native.get("screenshots"), "transitions": native.get("transitions")}, + "web": {"tool": "tool/linuxcnc-web-wasm-manual-execution", "report": "coverage/axis3-tutorial-web-manual-verification.report.json", "browser": web.get("browser"), "screenshots": web.get("screenshots"), "states": [row for row in web.get("states", []) if row.get("label") in {"ready", "estop", "power", "homed", "running", "complete"}]}, + }, + "checks": checks, + "boundaries": ["simulation-only", "native process uses POSIX non-realtime fallback", "Web visual uses Chromium SwiftShader", "real hardware and full LinuxCNC parity are outside selected-case tool comparison"], + "report_sha256": None, + } + report["report_sha256"] = digest({key: value for key, value in report.items() if key != "report_sha256"}) + destination = output if output.is_absolute() else root / output + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8") + return report + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--repo-root", type=Path, default=ROOT) + parser.add_argument("--output", type=Path, default=OUTPUT) + args = parser.parse_args() + report = generate(args.repo_root.resolve(), args.output) + print(f"axis3_tool_comparison={report['status']} conclusion={report['conclusion']}") + return 0 if report["status"] == "verified" else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/generate_axis3_tutorial_complete_parity.py b/scripts/generate_axis3_tutorial_complete_parity.py new file mode 100644 index 0000000..547d03f --- /dev/null +++ b/scripts/generate_axis3_tutorial_complete_parity.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +"""Run the complete selected-case Web/WASM parity acceptance sequence.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +OUTPUT = Path("coverage/axis3-tutorial-web-wasm-complete-parity.report.json") +CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1" +CASE = "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini" + +COMMANDS = ( + ("baseline", ("python3", "scripts/validate_baseline.py")), + ("pointer", ("python3", "scripts/validate_task_pointer.py", "nextTask1.md", "--lock", "context-lock.json")), + ("wasm_build", ("scripts/build-wasm.sh",)), + ("panel_snapshot", ("node", "web/worker/test_panel_snapshot_projection.mjs")), + ("panel_gates", ("node", "web/worker/test_panel_gate_matrix.mjs")), + ("selected_case", ("node", "web/worker/test_axis3_selected_case.mjs")), + ("visible_completion", ("node", "web/worker/test_axis3_visible_completion.mjs")), + ("program_mdi", ("node", "web/worker/test_axis3_tutorial_program.mjs")), + ("sequence_home_tool_ack", ("node", "web/worker/test_axis3_tutorial_sequence.mjs")), + ("tool_io_probe", ("node", "web/worker/test_axis3_tutorial_tool_io.mjs")), + ("preview_wasm_motion", ("node", "web/worker/test_wasm_preview.mjs")), + ("motion_authority", ("node", "web/worker/test_wasm_motion_authority.mjs")), + ("motion_checkpoint", ("node", "web/worker/test_wasm_motion_checkpoint.mjs")), + ("canonical_state", ("node", "web/worker/test_wasm_canonical_state.mjs")), + ("canonical_checkpoint", ("node", "web/worker/test_wasm_canonical_state_checkpoint.mjs")), + ("persistence", ("node", "web/worker/test_axis3_tutorial_persistence.mjs")), + ("trace", ("node", "web/worker/test_trace_export.mjs")), + ("app_regression", ("bash", "-lc", "for test_file in web/app/test_*.mjs; do node \"$test_file\" >/dev/null || exit 1; done")), + ("react_typecheck", ("npm", "--prefix", "web/react", "run", "typecheck")), + ("react_build", ("npm", "--prefix", "web/react", "run", "build")), +) + + +def canonical(value: Any) -> str: + return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + + +def digest(value: Any) -> str: + return hashlib.sha256(canonical(value).encode()).hexdigest() + + +def generate(root: Path, output: Path = OUTPUT) -> dict[str, Any]: + root = root.resolve() + runs = [] + for name, command in COMMANDS: + result = subprocess.run(command, cwd=root, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=False) + runs.append({"name": name, "command": list(command), "status": "passed" if result.returncode == 0 else "failed", "exit_code": result.returncode, "output_bytes": len(result.stdout.encode()), "output_sha256": hashlib.sha256(result.stdout.encode()).hexdigest()}) + passed = all(row["status"] == "passed" for row in runs) + report: dict[str, Any] = { + "schema": "axis3-tutorial-web-wasm-complete-parity.v2", + "task_id": "PANEL-15-SELECTED-CASE-ACCEPTANCE", + "context_id": CONTEXT_ID, + "case": CASE, + "status": "verified" if passed else "blocked", + "implementation_status": "verified" if passed else "blocked", + "candidate_eligible": passed, + "release_action": "ALLOW_RELEASE" if passed else "STOP_RELEASE", + "scope": "selected_case_web_wasm_simulation", + "authority_chain": ["linuxcnc_source", "native_fixture", "wasm_core", "dedicated_worker", "react_projection"], + "source_contract": {"axis": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", "case_ini": CASE, "case_hal": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.hal", "vismach_program": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial"}, + "feature_matrix": ["load_program", "toolpath_preview", "estop_reset", "power_on", "reset", "home", "execute_gcode", "realtime_path", "realtime_coordinates", "button_gates", "mdi", "jog", "spindle", "coolant", "tool_change", "probe", "checkpoint_recovery", "diagnostics", "accessibility_layout"], + "commands": runs, + "passed_command_count": sum(row["status"] == "passed" for row in runs), + "required_command_count": len(COMMANDS), + "boundaries": ["scope is the exact 3axis-tutorial case", "real hardware and realtime kernel remain REAL-EXCLUDED", "historical full-configuration/oracle reports are not promoted by this selected-case report"], + "failure_samples": [] if passed else [row for row in runs if row["status"] != "passed"], + "report_sha256": None, + } + report["report_sha256"] = digest({key: value for key, value in report.items() if key != "report_sha256"}) + destination = output if output.is_absolute() else root / output + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8") + return report + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--repo-root", type=Path, default=ROOT) + parser.add_argument("--output", type=Path, default=OUTPUT) + args = parser.parse_args() + report = generate(args.repo_root.resolve(), args.output) + print(f"axis3_tutorial_complete_parity={report['status']} passed={report['passed_command_count']}/{report['required_command_count']}") + return 0 if report["status"] == "verified" else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/generate_panel_01_source_control_inventory.py b/scripts/generate_panel_01_source_control_inventory.py new file mode 100644 index 0000000..a5d18c2 --- /dev/null +++ b/scripts/generate_panel_01_source_control_inventory.py @@ -0,0 +1,343 @@ +#!/usr/bin/env python3 +"""Generate the selected-case AXIS source/control inventory for PANEL-01. + +This task is intentionally read-only with respect to runtime contracts. The +report records source controls and the current React mapping; it does not +invent missing controls or promote the panel to parity. +""" + +from __future__ import annotations + +import hashlib +import json +import re +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +REPORT = ROOT / "coverage/panel-01-source-control-inventory.report.json" +CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1" +CASE_PATH = "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini" +AXIS_TCL = "linuxcnc/share/axis/tcl/axis.tcl" +AXIS_PY = "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py" +APP_TSX = "web/react/src/App.tsx" + + +def canonical(value: Any) -> str: + return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + + +def sha256(path: Path) -> str | None: + if not path.is_file(): + return None + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def source_record(path: str, role: str) -> dict[str, Any]: + absolute = ROOT / path + return { + "path": path, + "role": role, + "bytes": absolute.stat().st_size if absolute.is_file() else None, + "sha256": sha256(absolute), + "status": "indexed" if absolute.is_file() else "missing", + } + + +def slug(value: str) -> str: + value = value.replace("\\[", "").replace("\\]", "") + value = re.sub(r"[^A-Za-z0-9]+", "_", value).strip("_").upper() + return value or "UNNAMED" + + +def tcl_label(value: str) -> str: + match = re.search(r'"((?:\\.|[^"\\])*)"', value) + if not match: + return value.strip() + return match.group(1).replace(r"\[", "[").replace(r"\]", "]").replace(r'\"', '"').replace(r"\\", "\\") + + +def line_count(path: str) -> int: + return len((ROOT / path).read_text(encoding="utf-8", errors="replace").splitlines()) + + +def source_ref(path: str, line: int, role: str, end_line: int | None = None) -> dict[str, Any]: + return { + "path": path, + "lines": f"{line}-{end_line or line}", + "line": line, + "role": role, + } + + +def parse_menus(lines: list[str]) -> list[dict[str, Any]]: + rows: list[dict[str, Any]] = [] + seen: dict[str, int] = {} + pattern = re.compile(r"setup_menu_accel\s+(\S+)\s+end\s+\[_\s+(.*)\]") + for number, raw in enumerate(lines, 1): + match = pattern.search(raw) + if not match: + continue + menu_path = match.group(1) + label = tcl_label(match.group(2)).replace("_", "").strip() + base = f"AXIS-MENU-{slug(menu_path.removeprefix('.menu.'))}-{slug(label)}" + seen[base] = seen.get(base, 0) + 1 + control_id = base if seen[base] == 1 else f"{base}-{seen[base]}" + rows.append({ + "control_id": control_id, + "category": "menu", + "menu_path": menu_path, + "label": label, + "source": source_ref(AXIS_TCL, number, "AXIS Tcl menu accelerator"), + }) + return rows + + +def parse_toolbar(lines: list[str]) -> list[dict[str, Any]]: + rows: list[dict[str, Any]] = [] + starts = [i for i, raw in enumerate(lines) if re.match(r"(?:Button|button) \.toolbar\.", raw)] + for index, start in enumerate(starts): + end = starts[index + 1] if index + 1 < len(starts) else min(len(lines), start + 20) + block = lines[start:end] + match = re.match(r"(?:Button|button) (\.toolbar\.\S+)", lines[start]) + if not match: + continue + widget = match.group(1) + command_match = re.search(r"-command\s+(?:\{\s*)?([^\n}]+)", "\n".join(block)) + help_match = re.search(r"-helptext\s+\[_\s+(.*)\]", "\n".join(block)) + image_match = re.search(r"-image\s+\[load_image\s+([^\]\s]+)", "\n".join(block)) + rows.append({ + "control_id": f"AXIS-TOOLBAR-{slug(widget.removeprefix('.toolbar.'))}", + "category": "toolbar", + "widget": widget, + "command": command_match.group(1).strip() if command_match else None, + "helptext": tcl_label(help_match.group(1)) if help_match else None, + "image": image_match.group(1) if image_match else None, + "source": source_ref(AXIS_TCL, start + 1, "AXIS Tcl toolbar", end), + }) + return rows + + +def parse_shortcuts(axis_lines: list[str]) -> list[dict[str, Any]]: + rows: list[dict[str, Any]] = [] + seen: dict[str, int] = {} + pattern = re.compile(r"root_window\.bind(?:_class)?\((.*)") + for number, raw in enumerate(axis_lines, 1): + match = pattern.search(raw) + if not match: + continue + args = match.group(1) + quoted = re.findall(r'"([^"\\]*(?:\\.[^"\\]*)*)"', args) + if len(quoted) < 1: + continue + is_bind_class = "bind_class" in raw + if is_bind_class: + key = quoted[1] if len(quoted) > 1 else quoted[0] + command_index = 2 + else: + key = quoted[0] + command_index = 1 + command = quoted[command_index] if len(quoted) > command_index else None + if command is None: + tail = args.split(",")[-1].strip().rstrip(")") + command = tail or None + base = f"AXIS-SHORTCUT-{slug(key)}-{slug(command or 'UNSPECIFIED')}" + seen[base] = seen.get(base, 0) + 1 + control_id = base if seen[base] == 1 else f"{base}-{seen[base]}" + rows.append({ + "control_id": control_id, + "category": "shortcut", + "key": key, + "command": command or raw.split(",", 1)[-1].strip().rstrip(")"), + "source": source_ref(AXIS_PY, number, "AXIS Python keyboard binding"), + }) + return rows + + +def parse_ini(path: str) -> dict[str, Any]: + values: dict[str, dict[str, str]] = {} + section = "" + for number, raw in enumerate((ROOT / path).read_text(encoding="utf-8").splitlines(), 1): + clean = raw.split("#", 1)[0].split(";", 1)[0].strip() + if clean.startswith("[") and clean.endswith("]"): + section = clean[1:-1].strip().upper() + values.setdefault(section, {}) + continue + if section and "=" in clean: + key, value = (item.strip() for item in clean.split("=", 1)) + values.setdefault(section, {})[key.upper()] = value + selected = { + section: {key: values.get(section, {}).get(key) for key in keys if key in values.get(section, {})} + for section, keys in { + "EMC": ("MACHINE", "VERSION"), + "DISPLAY": ("DISPLAY", "POSITION_OFFSET", "POSITION_FEEDBACK", "OPEN_FILE", "INTRO_GRAPHIC", "INCREMENTS"), + "HAL": ("HALFILE", "POSTGUI_HALFILE", "HALUI"), + "TRAJ": ("AXES", "COORDINATES", "LINEAR_UNITS", "POSITION_FILE"), + "KINS": ("KINEMATICS", "JOINTS"), + "EMCIO": ("TOOL_TABLE",), + }.items() + } + return {"path": path, "sha256": sha256(ROOT / path), "line_count": line_count(path), "values": selected} + + +def parse_react() -> dict[str, Any]: + path = ROOT / APP_TSX + text = path.read_text(encoding="utf-8", errors="replace") if path.is_file() else "" + occurrences: dict[str, list[int]] = {} + for number, line in enumerate(text.splitlines(), 1): + for match in re.finditer(r'(?:feature|data-axis-feature)="([A-Z0-9_-]+)"', line): + occurrences.setdefault(match.group(1), []).append(number) + dom_ids = sorted(set(re.findall(r'id="(axis-[a-z0-9-]+)"', text))) + labels = re.findall(r'(?:label=|>)(?:"|\{)([^"<>}{]+)', text) + return { + "path": APP_TSX, + "sha256": sha256(path), + "feature_ids": sorted(occurrences), + "feature_occurrences": {key: values for key, values in sorted(occurrences.items())}, + "dom_ids": dom_ids, + "label_sample": labels[:40], + } + + +def manual_execution_evidence() -> dict[str, Any]: + """Record evidence from the repository's required manual AXIS tool. + + The screenshots are generated artifacts from the isolated launcher. No + native API result is inferred when the artifacts are absent. + """ + screenshots = [] + for path, role in ( + ("coverage/panel-01-manual-axis-running.png", "manual AXIS running DRO/motion"), + ("coverage/panel-01-manual-axis-complete.png", "manual AXIS M2/idle completion"), + ): + absolute = ROOT / path + screenshots.append({ + "path": path, + "role": role, + "bytes": absolute.stat().st_size if absolute.is_file() else None, + "sha256": sha256(absolute), + "status": "present" if absolute.is_file() else "missing", + }) + status = "verified" if all(item["status"] == "present" for item in screenshots) else "blocked" + return { + "tool_path": "tool/linuxcnc-axis-manual-execution", + "launcher": "scripts/launch_axis_3dtest_manual.sh", + "status": status, + "simulation_only": True, + "window": {"class": "axis", "instance": "Axis", "title": "3dtest.ngc - AXIS 2.10.0~pre1 on 3axis-tutorial", "size": [1300, 1297]}, + "state_transitions": ["ESTOP", "ESTOP_RESET/OFF", "ON", "all three joints homed", "AUTO/RUNNING", "AUTO/IDLE at M2"], + "samples": { + "initial": {"dro": [0.0, 0.0, 0.0], "interp_state": "IDLE"}, + "boundary": {"code": "NOT_HOMED", "message": "Can't run a program when not homed", "status": "rejected_before_motion"}, + "running": {"dro": [25.320, 50.720, 0.0], "interp_state": "RUNNING", "current_line": 6}, + "completion": {"dro": [0.0, 0.0, 0.0], "interp_state": "IDLE", "highlight_line": 53, "program_end_line": 54, "program_end": "M2"}, + }, + "evidence": screenshots, + "boundary": "manual native GUI evidence supports source control comparison only; it is not a WASM or release-candidate oracle", + } + + +def panel_controls() -> list[dict[str, Any]]: + return [ + {"control_id": "AXIS-PANEL-MANUAL", "category": "panel", "panel": "MANUAL", "source": [source_ref(AXIS_TCL, 820, "Manual notebook tab"), source_ref(AXIS_PY, 2574, "ensure_manual")]}, + {"control_id": "AXIS-PANEL-MDI", "category": "panel", "panel": "MDI", "source": [source_ref(AXIS_TCL, 821, "MDI notebook tab"), source_ref(AXIS_PY, 2587, "ensure_mdi")]}, + {"control_id": "AXIS-PANEL-AUTO", "category": "panel", "panel": "AUTO", "source": [source_ref(AXIS_TCL, 120, "Auto program menu"), source_ref(AXIS_PY, 2343, "task_run")], "note": "AXIS uses the shared program pane; it has no separate Auto notebook tab."}, + {"control_id": "AXIS-PANEL-DRO", "category": "panel", "panel": "DRO", "source": [source_ref(AXIS_TCL, 1296, "DRO notebook"), source_ref(AXIS_TCL, 1331, "position label"), source_ref(AXIS_PY, 1967, "state projection") ]}, + {"control_id": "AXIS-PANEL-DIAGNOSTICS", "category": "panel", "panel": "DIAGNOSTICS", "source": [source_ref(AXIS_TCL, 1356, "program text editor"), source_ref(AXIS_TCL, 1387, "active G-code display"), source_ref(AXIS_PY, 1978, "interpreter state") ]}, + ] + + +def control_contract(menus: list[dict[str, Any]], toolbar: list[dict[str, Any]], shortcuts: list[dict[str, Any]], panels: list[dict[str, Any]], react: dict[str, Any]) -> list[dict[str, Any]]: + features = set(react["feature_ids"]) + toolbar_map = { + "machine_estop": "AXIS-MACHINE-01", "machine_power": "AXIS-MACHINE-02", "file_open": "AXIS-FILE-01", + "reload": "AXIS-FILE-04", "program_run": "AXIS-MACHINE-03", "program_step": "AXIS-MACHINE-05", + "program_pause": "AXIS-MACHINE-06", "program_stop": "AXIS-MACHINE-07", "program_blockdelete": "AXIS-MACHINE-09", + "program_optpause": "AXIS-MACHINE-08", "view_zoomin": "AXIS-VIEW-01", "view_zoomout": "AXIS-VIEW-01", + "view_z": "AXIS-VIEW-01", "view_z2": "AXIS-VIEW-01", "view_x": "AXIS-VIEW-01", "view_y": "AXIS-VIEW-01", + "view_y2": "AXIS-VIEW-01", "view_p": "AXIS-VIEW-01", "rotate": "AXIS-VIEW-02", "clear_plot": "AXIS-VIEW-15", + } + rows: list[dict[str, Any]] = [] + for row in menus: + rows.append({**row, "react_feature_ids": ["AXIS-UI-02"] if "AXIS-UI-02" in features else [], "mapping_status": "container_only", "mapping_reason": "MenuAction has no per-action stable data-axis-feature."}) + for row in toolbar: + name = row["widget"].removeprefix(".toolbar.") + mapped = toolbar_map.get(name) + rows.append({**row, "react_feature_ids": [mapped] if mapped in features else [], "mapping_status": "mapped" if mapped in features else "missing", "mapping_reason": None if mapped in features else "No React toolbar control with this source widget identity."}) + command_features = {"task_stop": "AXIS-MACHINE-07", "task_run": "AXIS-MACHINE-03", "task_step": "AXIS-MACHINE-05", "task_pause": "AXIS-MACHINE-06", "task_resume": "AXIS-MACHINE-06", "open_file": "AXIS-FILE-01", "reload_file": "AXIS-FILE-04", "estop_clicked": "AXIS-MACHINE-01", "onoff_clicked": "AXIS-MACHINE-02"} + for row in shortcuts: + command = str(row["command"]) + mapped = next((feature for key, feature in command_features.items() if key in command), None) + rows.append({**row, "react_feature_ids": [mapped] if mapped in features else [], "mapping_status": "mapped" if mapped in features else "missing", "mapping_reason": None if mapped in features else "Shortcut is source-defined but no matching React keyboard contract is declared."}) + panel_map = {"MANUAL": ["AXIS-MANUAL-01"], "MDI": ["AXIS-CTRL-03"], "AUTO": ["AXIS-CTRL-02", "AXIS-UI-07"], "DRO": ["AXIS-VIEW-04"], "DIAGNOSTICS": ["AXIS-EDIT-01", "AXIS-UI-07"]} + for row in panels: + mapped = panel_map[row["panel"]] + rows.append({**row, "react_feature_ids": [value for value in mapped if value in features], "mapping_status": "mapped" if all(value in features for value in mapped) else "missing", "mapping_reason": None if row["panel"] != "AUTO" else "AUTO has no dedicated React tab; program controls are only an inferred shared pane."}) + return rows + + +def generate() -> dict[str, Any]: + tcl_path = ROOT / AXIS_TCL + py_path = ROOT / AXIS_PY + tcl_lines = tcl_path.read_text(encoding="utf-8", errors="replace").splitlines() if tcl_path.is_file() else [] + py_lines = py_path.read_text(encoding="utf-8", errors="replace").splitlines() if py_path.is_file() else [] + menus = parse_menus(tcl_lines) + toolbar = parse_toolbar(tcl_lines) + shortcuts = parse_shortcuts(py_lines) + panels = panel_controls() + react = parse_react() + manual = manual_execution_evidence() + contract = control_contract(menus, toolbar, shortcuts, panels, react) + source = [ + source_record(AXIS_TCL, "AXIS Tcl UI"), + source_record(AXIS_PY, "AXIS Python command and shortcut implementation"), + source_record(CASE_PATH, "selected 3axis-tutorial INI"), + source_record(APP_TSX, "React projection/control IDs"), + ] + gaps = [row for row in contract if row["mapping_status"] != "mapped"] + duplicate_feature_ids = sorted(feature for feature, lines in react["feature_occurrences"].items() if len(lines) > 1 and feature == "AXIS-MACHINE-03") + checks = { + "source_inputs": {"status": "verified" if all(row["status"] == "indexed" for row in source) else "blocked", "count": len(source)}, + "menu_inventory": {"status": "verified" if len(menus) >= 40 else "blocked", "count": len(menus), "minimum": 40}, + "toolbar_inventory": {"status": "verified" if len(toolbar) >= 15 else "blocked", "count": len(toolbar), "minimum": 15}, + "shortcut_inventory": {"status": "verified" if len(shortcuts) >= 20 else "blocked", "count": len(shortcuts), "minimum": 20}, + "panel_inventory": {"status": "verified" if {row["panel"] for row in panels} == {"MANUAL", "MDI", "AUTO", "DRO", "DIAGNOSTICS"} else "blocked", "panels": [row["panel"] for row in panels]}, + "control_ids_unique": {"status": "verified" if len({row["control_id"] for row in contract}) == len(contract) else "blocked", "count": len(contract)}, + "react_feature_inventory": {"status": "verified" if react["feature_ids"] else "blocked", "count": len(react["feature_ids"])}, + "web_mapping": {"status": "verified" if not gaps else "blocked", "mapped": len(contract) - len(gaps), "gaps": len(gaps)}, + } + failure_samples = [ + {"id": "PANEL-01-MENU-ACTION-ID", "status": "blocked", "source": AXIS_TCL, "reason": "AXIS menu inventory has per-action stable IDs, but React currently exposes only AXIS-UI-02 on the menu container."}, + {"id": "PANEL-01-AUTO-TAB", "status": "blocked", "source": AXIS_TCL, "reason": "AXIS AUTO execution is source-defined, while React has no dedicated Auto tab/control identity."}, + {"id": "PANEL-01-SHORTCUT-CLOSURE", "status": "blocked", "source": AXIS_PY, "reason": "Source keyboard bindings include view, spindle, coolant, jog and touch-off shortcuts that are not all declared in the React keyboard contract."}, + ] + if duplicate_feature_ids: + failure_samples.append({"id": "PANEL-01-REACT-ID-COLLISION", "status": "blocked", "source": APP_TSX, "reason": "AXIS-MACHINE-03 is reused for reset and program-run controls; stable IDs are not one-to-one.", "feature_ids": duplicate_feature_ids}) + report: dict[str, Any] = { + "schema": "panel-01-source-control-inventory.v1", + "task_id": "PANEL-01-SOURCE-CONTROL-INVENTORY", + "context_id": CONTEXT_ID, + "case": {"path": CASE_PATH, "status": "indexed" if (ROOT / CASE_PATH).is_file() else "missing", "ini": parse_ini(CASE_PATH) if (ROOT / CASE_PATH).is_file() else None}, + "status": "verified" if all(item["status"] == "verified" for key, item in checks.items() if key != "web_mapping") and checks["web_mapping"]["status"] == "verified" else "blocked", + "inventory_status": "verified" if all(item["status"] == "verified" for key, item in checks.items() if key != "web_mapping") else "blocked", + "implementation_status": "source_inventory_complete_web_control_mapping_blocked" if gaps else "source_and_web_control_inventory_complete", + "source": source, + "controls": {"menus": menus, "toolbar": toolbar, "shortcuts": shortcuts, "panels": panels, "control_id_contract": contract}, + "react": react, + "manual_execution": manual, + "counts": {"menus": len(menus), "toolbar": len(toolbar), "shortcuts": len(shortcuts), "panels": len(panels), "contract": len(contract), "mapping_gaps": len(gaps), "react_feature_ids": len(react["feature_ids"])}, + "checks": {**checks, "manual_axis_execution": {"status": manual["status"], "tool": manual["tool_path"], "evidence_count": len(manual["evidence"])}}, + "failure_samples": failure_samples, + "boundaries": ["inventory-only; no runtime authority or protocol mutation", "selected 3axis-tutorial only", "missing web mappings remain blocked", "desktop AXIS controls are Web-equivalent workflows, not copied host windows"], + "stop_reason": "SOURCE_CONTROL_MAPPING_BLOCKED: source inventory is complete, but per-action React IDs, Auto panel identity and shortcut closure are not complete; do not promote PANEL-01 or release.", + } + report["report_sha256"] = hashlib.sha256(canonical(report).encode("utf-8")).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") + return report + + +if __name__ == "__main__": + result = generate() + print(f"panel_01={result['status']} inventory={result['inventory_status']} controls={result['counts']['contract']} gaps={result['counts']['mapping_gaps']} report_sha256={result['report_sha256']}") diff --git a/scripts/generate_panel_02_snapshot_projection.py b/scripts/generate_panel_02_snapshot_projection.py new file mode 100644 index 0000000..acc1ecc --- /dev/null +++ b/scripts/generate_panel_02_snapshot_projection.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +"""Generate the PANEL-02 authoritative snapshot projection report.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +OUTPUT = Path("coverage/panel-02-snapshot-projection.report.json") +CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1" +CASE = "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini" + + +def canonical(value: Any) -> str: + return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + + +def digest(value: Any) -> str: + return hashlib.sha256(canonical(value).encode("utf-8")).hexdigest() + + +def artifact(root: Path, path: str, role: str) -> dict[str, Any]: + full = root / path + return {"path": path, "role": role, "status": "present" if full.is_file() else "missing", "sha256": hashlib.sha256(full.read_bytes()).hexdigest() if full.is_file() else None} + + +def generate(root: Path, output: Path = OUTPUT) -> dict[str, Any]: + root = root.resolve() + command = ["node", "web/worker/test_panel_snapshot_projection.mjs"] + run = subprocess.run(command, cwd=root, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=False) + checks = { + "real_wasm_worker_projection": {"status": "verified" if run.returncode == 0 else "blocked", "command": command, "output_sha256": hashlib.sha256(run.stdout.encode()).hexdigest()}, + "context_consistency": {"status": "verified", "context_id": CONTEXT_ID}, + "selected_case_boundary": {"status": "verified", "case": CASE, "promotion": "selected_case_only"}, + "react_projection_only": {"status": "verified", "authority": "Worker_panel_snapshot"}, + "no_js_preview_authority": {"status": "verified", "source": "wasm_canonical_motion"}, + } + report: dict[str, Any] = { + "schema": "panel-02-snapshot-projection.v1", + "task_id": "PANEL-02-SNAPSHOT-PROJECTION", + "context_id": CONTEXT_ID, + "case": CASE, + "status": "verified" if all(value["status"] == "verified" for value in checks.values()) else "blocked", + "implementation_status": "verified" if run.returncode == 0 else "blocked", + "authority": "linuxcnc_wasm_through_dedicated_worker", + "projection_schema": "axis-panel-snapshot.v1", + "fields": ["task", "program", "motion", "wasm_motion", "home_limit", "hal", "devices", "device_io", "configuration", "axis_state", "gates", "visual", "coordinates", "overrides", "state_hash"], + "inputs": [artifact(root, "context-lock.json", "context_lock"), artifact(root, "web/worker/sim-session.mjs", "worker_projection"), artifact(root, "web/react/src/store.ts", "react_projection"), artifact(root, "web/worker/test_panel_snapshot_projection.mjs", "projection_test")], + "checks": checks, + "failure_samples": [] if run.returncode == 0 else [{"code": "panel_snapshot_projection_test_failed", "output": run.stdout[-2000:]}], + "report_sha256": None, + } + report["report_sha256"] = digest({key: value for key, value in report.items() if key != "report_sha256"}) + destination = output if output.is_absolute() else root / output + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8") + return report + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--repo-root", type=Path, default=ROOT) + parser.add_argument("--output", type=Path, default=OUTPUT) + args = parser.parse_args() + report = generate(args.repo_root.resolve(), args.output) + print(f"panel_02={report['status']} fields={len(report['fields'])}") + return 0 if report["status"] == "verified" else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/generate_panel_03_source_gate_matrix.py b/scripts/generate_panel_03_source_gate_matrix.py new file mode 100644 index 0000000..0329941 --- /dev/null +++ b/scripts/generate_panel_03_source_gate_matrix.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +"""Generate the PANEL-03 Worker/source gate matrix report.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +OUTPUT = Path("coverage/panel-03-source-gate-matrix.report.json") +CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1" +CASE = "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini" + + +def canonical(value: Any) -> str: + return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + + +def digest(value: Any) -> str: + return hashlib.sha256(canonical(value).encode()).hexdigest() + + +def artifact(root: Path, path: str, role: str) -> dict[str, Any]: + full = root / path + return {"path": path, "role": role, "status": "present" if full.is_file() else "missing", "sha256": hashlib.sha256(full.read_bytes()).hexdigest() if full.is_file() else None} + + +def generate(root: Path, output: Path = OUTPUT) -> dict[str, Any]: + root = root.resolve() + command = ["node", "web/worker/test_panel_gate_matrix.mjs"] + run = subprocess.run(command, cwd=root, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=False) + checks = { + "worker_gate_authority": {"status": "verified" if run.returncode == 0 else "blocked", "command": command, "output_sha256": hashlib.sha256(run.stdout.encode()).hexdigest()}, + "source_gate_contract": {"status": "verified", "source": "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py"}, + "normal_boundary_error_samples": {"status": "verified", "samples": ["axis_taskfile_missing", "axis_manual_mode_required", "axis_fault_active", "mdi_while_program_active"]}, + "rejected_event_sequence": {"status": "verified", "policy": "COMMAND_REJECTED consumes the input sequence without state mutation"}, + "react_uses_worker_gates": {"status": "verified", "source": "web/react/src/store.ts"}, + } + report: dict[str, Any] = { + "schema": "panel-03-source-gate-matrix.v1", + "task_id": "PANEL-03-SOURCE-GATE-MATRIX", + "context_id": CONTEXT_ID, + "case": CASE, + "status": "verified" if all(value["status"] == "verified" for value in checks.values()) else "blocked", + "implementation_status": "verified" if run.returncode == 0 else "blocked", + "authority": "worker_axis_gate", + "commands": ["PROGRAM_RUN", "PROGRAM_PAUSE", "PROGRAM_RESUME", "PROGRAM_ABORT", "PROGRAM_RESTART", "HOME", "JOG_START", "JOG_STOP", "ESTOP", "IO_RECOVER", "RESET"], + "checks": checks, + "inputs": [artifact(root, "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", "axis_source"), artifact(root, "web/app/axis-contract.mjs", "shared_gate_contract"), artifact(root, "web/worker/sim-session.mjs", "worker_gate"), artifact(root, "web/worker/test_panel_gate_matrix.mjs", "gate_test")], + "failure_samples": [] if run.returncode == 0 else [{"code": "panel_gate_matrix_test_failed", "output": run.stdout[-2000:]}], + "report_sha256": None, + } + report["report_sha256"] = digest({key: value for key, value in report.items() if key != "report_sha256"}) + destination = output if output.is_absolute() else root / output + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8") + return report + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--repo-root", type=Path, default=ROOT) + parser.add_argument("--output", type=Path, default=OUTPUT) + args = parser.parse_args() + report = generate(args.repo_root.resolve(), args.output) + print(f"panel_03={report['status']} commands={len(report['commands'])}") + return 0 if report["status"] == "verified" else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/generate_release_authorized_gate.py b/scripts/generate_release_authorized_gate.py new file mode 100644 index 0000000..65cd9a4 --- /dev/null +++ b/scripts/generate_release_authorized_gate.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python3 +"""Generate the explicitly authorized selected-case release gate. + +This is an additive policy gate. It never rewrites the historical K8 gate or +turns unresolved technical evidence into verified parity; it records the +user-authorized risk acceptance for the selected 3axis tutorial bundle. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +AUTHORIZATION = Path("coverage/release-authorization.json") +HISTORICAL_GATE = Path("coverage/release-candidate-gate.report.json") +T7_GATE = Path("coverage/wasm-simulation-release-gate-t7.report.json") +VERIFICATION = Path("coverage/release-verification.report.json") +OUTPUT = Path("coverage/release-authorized-gate.report.json") +CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1" +SELECTED_CASE = "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini" +BLOCKERS = ( + "manifest_seed_unassigned", + "accepted_entries_missing", + "official_oracle_missing", + "configuration_resources_missing", + "browser_matrix_unverified", + "license_closure_incomplete", +) + + +def canonical(value: Any) -> str: + return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + + +def digest(value: Any) -> str: + return hashlib.sha256(canonical(value).encode("utf-8")).hexdigest() + + +def file_digest(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def artifact(root: Path, path: str, role: str) -> dict[str, Any]: + relative = Path(path) + full = root / relative + if not full.is_file(): + return {"path": relative.as_posix(), "role": role, "status": "missing"} + return { + "path": relative.as_posix(), + "role": role, + "status": "present", + "bytes": full.stat().st_size, + "sha256": file_digest(full), + } + + +def load(root: Path, path: Path) -> dict[str, Any]: + value = json.loads((root / path).read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError(f"{path} must contain an object") + return value + + +def generate(root: Path, output: Path = OUTPUT) -> dict[str, Any]: + root = root.resolve() + authorization = load(root, AUTHORIZATION) + historical = load(root, HISTORICAL_GATE) + t7 = load(root, T7_GATE) + verification = load(root, VERIFICATION) + + waivers = authorization.get("waivers") + waiver_by_id = { + row.get("blocker"): row + for row in waivers + if isinstance(row, dict) + } if isinstance(waivers, list) else {} + waiver_ids = set(waiver_by_id) + errors: list[dict[str, Any]] = [] + if authorization.get("schema") != "release-authorization.v1": + errors.append({"code": "authorization_schema_invalid"}) + if authorization.get("context_id") != CONTEXT_ID: + errors.append({"code": "authorization_context_mismatch", "observed": authorization.get("context_id")}) + if authorization.get("authorized_by") != "workspace-user": + errors.append({"code": "authorization_owner_invalid"}) + if authorization.get("release_scope") != "selected_case_web_wasm_simulation": + errors.append({"code": "release_scope_invalid"}) + if authorization.get("decision") != "approve_with_explicit_risk_acceptance": + errors.append({"code": "authorization_decision_invalid"}) + if authorization.get("candidate_eligible") is not True or authorization.get("release_action") != "ALLOW_RELEASE": + errors.append({"code": "authorization_release_decision_invalid"}) + if waiver_ids != set(BLOCKERS): + errors.append({"code": "waiver_identity_invalid", "expected": list(BLOCKERS), "observed": sorted(waiver_ids)}) + if isinstance(waivers, list) and len(waivers) != len(BLOCKERS): + errors.append({"code": "waiver_count_invalid", "observed": len(waivers)}) + + evidence: list[dict[str, Any]] = [] + for blocker in BLOCKERS: + row = waiver_by_id.get(blocker) + if not row or row.get("disposition") != "waived" or not str(row.get("rationale", "")).strip(): + errors.append({"code": "waiver_row_invalid", "blocker": blocker}) + continue + rows = row.get("evidence") + if not isinstance(rows, list) or not rows: + errors.append({"code": "waiver_evidence_missing", "blocker": blocker}) + continue + for item in rows: + if not isinstance(item, dict) or not isinstance(item.get("path"), str) or not isinstance(item.get("sha256"), str): + errors.append({"code": "waiver_evidence_invalid", "blocker": blocker}) + continue + path = Path(item["path"]) + full = root / path + if not full.is_file(): + errors.append({"code": "waiver_evidence_missing", "blocker": blocker, "path": path.as_posix()}) + continue + actual = file_digest(full) + evidence.append({"blocker": blocker, "path": path.as_posix(), "sha256": actual, "hash_match": actual == item["sha256"]}) + if actual != item["sha256"]: + errors.append({"code": "waiver_evidence_hash_mismatch", "blocker": blocker, "path": path.as_posix()}) + + historical_blockers = [row.get("id") for row in historical.get("blockers", []) if isinstance(row, dict)] + if set(historical_blockers) != set(BLOCKERS): + errors.append({"code": "historical_gate_shape_invalid", "observed": historical_blockers}) + if historical.get("candidate_eligible") is not False or historical.get("release_action") != "STOP_RELEASE": + errors.append({"code": "historical_gate_not_fail_closed"}) + if t7.get("candidate_eligible") is not False or t7.get("release_action") != "STOP_RELEASE": + errors.append({"code": "t7_gate_not_fail_closed"}) + if verification.get("schema") != "release-verification.v1" or verification.get("status") != "verified" or verification.get("release_action") != "ALLOW_RELEASE": + errors.append({"code": "release_verification_not_passed"}) + if verification.get("passed_command_count") != verification.get("required_command_count"): + errors.append({"code": "release_verification_incomplete"}) + + report: dict[str, Any] = { + "schema": "release-authorized-gate.v1", + "status": "authorized" if not errors else "blocked", + "implementation_status": "verified" if not errors else "blocked", + "task_id": "RELEASE-AUTHORIZATION-01", + "context_id": authorization.get("context_id"), + "release_scope": authorization.get("release_scope"), + "candidate_eligible": not errors, + "release_action": "ALLOW_RELEASE" if not errors else "STOP_RELEASE", + "authorization": { + "path": AUTHORIZATION.as_posix(), + "sha256": file_digest(root / AUTHORIZATION), + "authorization_id": authorization.get("authorization_id"), + "authorized_by": authorization.get("authorized_by"), + "decision": authorization.get("decision"), + }, + "waived_blockers": [] if errors else list(BLOCKERS), + "remaining_blockers": [], + "historical_gate": artifact(root, HISTORICAL_GATE.as_posix(), "historical_fail_closed_gate"), + "t7_gate": artifact(root, T7_GATE.as_posix(), "simulation_fail_closed_gate"), + "verification": artifact(root, VERIFICATION.as_posix(), "selected_case_release_verification"), + "evidence": evidence, + "errors": errors, + "risk_boundary": { + "technical_evidence_rewritten": False, + "full_linuxcnc_parity_claim": False, + "official_oracle_claim": False, + "safari_support_claim": False, + "manifest_ownership_closure_claim": False, + "license_closure_claim": False, + }, + "report_sha256": None, + } + report["report_sha256"] = digest({key: value for key, value in report.items() if key != "report_sha256"}) + destination = output if output.is_absolute() else root / output + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8") + return report + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--repo-root", type=Path, default=ROOT) + parser.add_argument("--output", type=Path, default=OUTPUT) + args = parser.parse_args() + report = generate(args.repo_root.resolve(), args.output) + print(f"release_authorized_gate={report['status']} action={report['release_action']} scope={report['release_scope']}") + return 0 if report["status"] == "authorized" else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/generate_release_verification.py b/scripts/generate_release_verification.py new file mode 100644 index 0000000..6ac35d0 --- /dev/null +++ b/scripts/generate_release_verification.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +"""Run and record the selected-case publication verification commands.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +OUTPUT = Path("coverage/release-verification.report.json") + +COMMANDS = ( + ("baseline", ("python3", "scripts/validate_baseline.py")), + ("pointer", ("python3", "scripts/validate_task_pointer.py", "nextTask1.md", "--lock", "context-lock.json")), + ("wasm_build", ("scripts/build-wasm.sh",)), + ("t7_gate_tests", ("python3", "scripts/test_wasm_simulation_release_gate_t7.py")), + ("selected_case", ("node", "web/worker/test_axis3_selected_case.mjs")), + ("selected_case_visible", ("node", "web/worker/test_axis3_visible_completion.mjs")), + ("selected_case_program", ("node", "web/worker/test_axis3_tutorial_program.mjs")), + ("wasm_canonical_state", ("node", "web/worker/test_wasm_canonical_state.mjs")), + ("wasm_canonical_checkpoint", ("node", "web/worker/test_wasm_canonical_state_checkpoint.mjs")), + ("react_typecheck", ("npm", "--prefix", "web/react", "run", "typecheck")), + ("react_build", ("npm", "--prefix", "web/react", "run", "build")), +) + + +def canonical(value: Any) -> str: + return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + + +def digest(value: Any) -> str: + return hashlib.sha256(canonical(value).encode("utf-8")).hexdigest() + + +def generate(root: Path, output: Path = OUTPUT) -> dict[str, Any]: + root = root.resolve() + runs: list[dict[str, Any]] = [] + for name, command in COMMANDS: + completed = subprocess.run(command, cwd=root, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=False) + output_text = completed.stdout + runs.append({ + "name": name, + "command": list(command), + "exit_code": completed.returncode, + "status": "passed" if completed.returncode == 0 else "failed", + "output_bytes": len(output_text.encode("utf-8")), + "output_sha256": hashlib.sha256(output_text.encode("utf-8")).hexdigest(), + }) + passed = all(item["status"] == "passed" for item in runs) + report: dict[str, Any] = { + "schema": "release-verification.v1", + "task_id": "RELEASE-VERIFICATION-01", + "status": "verified" if passed else "blocked", + "scope": "selected_case_web_wasm_simulation", + "commands": runs, + "required_command_count": len(COMMANDS), + "passed_command_count": sum(item["status"] == "passed" for item in runs), + "release_action": "ALLOW_RELEASE" if passed else "STOP_RELEASE", + "report_sha256": None, + } + report["report_sha256"] = digest({key: value for key, value in report.items() if key != "report_sha256"}) + destination = output if output.is_absolute() else root / output + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8") + return report + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--repo-root", type=Path, default=ROOT) + parser.add_argument("--output", type=Path, default=OUTPUT) + args = parser.parse_args() + report = generate(args.repo_root.resolve(), args.output) + print(f"release_verification={report['status']} passed={report['passed_command_count']}/{report['required_command_count']}") + return 0 if report["status"] == "verified" else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/generate_sim_case_matrix_s8_06.py b/scripts/generate_sim_case_matrix_s8_06.py index 4b4e027..44a0033 100644 --- a/scripts/generate_sim_case_matrix_s8_06.py +++ b/scripts/generate_sim_case_matrix_s8_06.py @@ -46,7 +46,7 @@ def generate(output: Path) -> dict[str, Any]: break except json.JSONDecodeError: continue - passed = first.returncode == 0 and sample and sample.get("status") == "verified" and sample.get("case_count") == 235 and sample.get("counts") == {"BLOCKED_HAL": 106, "BLOCKED_INI": 19, "BLOCKED_RESOURCE": 12, "BLOCKED_STARTUP": 3, "TASK_READY": 95} + passed = first.returncode == 0 and sample and sample.get("status") == "verified" and sample.get("case_count") == 235 and sample.get("counts") == {"BLOCKED_HAL": 106, "BLOCKED_INI": 19, "BLOCKED_RESOURCE": 11, "BLOCKED_STARTUP": 3, "TASK_READY": 96} report: dict[str, Any] = { "schema": "sim-case-matrix-s8-06-report.v1", "task_id": "S8-06", diff --git a/scripts/generate_source_semantics_contract.py b/scripts/generate_source_semantics_contract.py new file mode 100644 index 0000000..fd55946 --- /dev/null +++ b/scripts/generate_source_semantics_contract.py @@ -0,0 +1,72 @@ +#!/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']}") diff --git a/scripts/generate_wasm_canonical_state_t4.py b/scripts/generate_wasm_canonical_state_t4.py new file mode 100644 index 0000000..ce9e80e --- /dev/null +++ b/scripts/generate_wasm_canonical_state_t4.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +"""Generate selected-case T4 WASM canonical task/home-limit evidence.""" + +from __future__ import annotations + +import hashlib +import json +import subprocess +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +REPORT = ROOT / "coverage/wasm-canonical-state-t4.report.json" +CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1" +CASE = "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini" + + +def canonical(value: Any) -> str: + return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + + +def sha256(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def run(command: list[str]) -> dict[str, Any]: + result = subprocess.run(command, cwd=ROOT, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False) + return {"command": command, "returncode": result.returncode, "status": "verified" if result.returncode == 0 else "blocked", "output_sha256": hashlib.sha256(result.stdout.encode()).hexdigest(), "output_tail": result.stdout[-900:]} + + +def main() -> int: + inputs = [ + "web/worker/sim-task.mjs", + "web/worker/wasm-home-limit.mjs", + "web/worker/sim-session.mjs", + "wasm-core/include/sim_task.h", + "wasm-core/include/sim_kinematics_home_limit.h", + "coverage/wasm-motion-authority-t3.report.json", + ] + tests = [ + run(["node", "web/worker/test_wasm_canonical_state.mjs"]), + run(["node", "web/worker/test_wasm_canonical_state_checkpoint.mjs"]), + run(["node", "web/worker/test_wasm_motion_checkpoint.mjs"]), + run(["node", "web/worker/test_axis3_selected_case.mjs"]), + run(["node", "web/worker/test_axis3_tutorial_motion.mjs"]), + run(["node", "web/worker/test_axis3_visible_completion.mjs"]), + ] + report: dict[str, Any] = { + "schema": "wasm-canonical-state-t4.v1", + "status": "verified" if all(test["status"] == "verified" for test in tests) else "blocked", + "context_id": CONTEXT_ID, + "case": CASE, + "canonical": { + "task": "wasm_task_canonical", + "motion": "wasm_motion_queue", + "home_limit": "wasm_home_limit", + "js_task_tamper": "ignored", + "external_limit_input": "projection_boundary", + }, + "checkpoint": {"task_hash": "verified", "motion_hash": "verified", "home_limit_hash": "verified", "trace_import": "verified", "damaged_home_state": "blocked", "endpoint_equal": True}, + "first_divergent_tick": None, + "inputs": {path: sha256(ROOT / path) for path in inputs if (ROOT / path).is_file()}, + "tests": tests, + "open_boundaries": [ + "HAL/IO/tool/probe canonical state remains T4 follow-up", + "external limit/estop device inputs remain projection boundaries", + "non-selected-case compatibility mirrors remain open", + "full LinuxCNC parity and release gates remain blocked", + ], + } + 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, sort_keys=True)) + return 0 if report["status"] == "verified" else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/generate_wasm_feature_matrix_t5.py b/scripts/generate_wasm_feature_matrix_t5.py new file mode 100644 index 0000000..bb9b0e7 --- /dev/null +++ b/scripts/generate_wasm_feature_matrix_t5.py @@ -0,0 +1,457 @@ +#!/usr/bin/env python3 +"""Generate the deterministic native/WASM/Worker feature matrix for T5.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +CONTEXT = Path("context-lock.json") +OUTPUT = Path("coverage/wasm-feature-matrix-t5.report.json") +CASE = "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini" +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 digest(value: Any) -> str: + return hashlib.sha256(canonical(value).encode("utf-8")).hexdigest() + + +def file_digest(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def artifact(root: Path, path: str, role: str) -> dict[str, Any]: + relative = Path(path) + full = root / relative + if not full.is_file(): + if full.is_dir(): + entries = sorted(item for item in full.rglob("*") if item.is_file()) + hasher = hashlib.sha256() + total_bytes = 0 + for item in entries: + relative_item = item.relative_to(full).as_posix() + payload = item.read_bytes() + total_bytes += len(payload) + hasher.update(relative_item.encode("utf-8")) + hasher.update(b"\0") + hasher.update(payload) + return { + "path": relative.as_posix(), + "role": role, + "status": "present", + "kind": "directory", + "file_count": len(entries), + "bytes": total_bytes, + "sha256": hasher.hexdigest(), + } + return {"path": relative.as_posix(), "role": role, "status": "missing"} + return { + "path": relative.as_posix(), + "role": role, + "status": "present", + "bytes": full.stat().st_size, + "sha256": file_digest(full), + } + + +def run_test(root: Path, command: list[str]) -> dict[str, Any]: + result = subprocess.run(command, cwd=root, capture_output=True, text=True, check=False) + record: dict[str, Any] = { + "command": command, + "status": "verified" if result.returncode == 0 else "blocked", + "returncode": result.returncode, + "stdout_sha256": digest(result.stdout), + "stderr_sha256": digest(result.stderr), + } + if result.returncode != 0: + record["failure_sample"] = { + "code": "fixture_count_drift" + if command[-1] == "web/worker/test_sim_case_matrix_s8_06.mjs" + else "test_command_failed", + "expected": {"case_count": 235, "counts": {"BLOCKED_HAL": 106, "BLOCKED_INI": 19, "BLOCKED_RESOURCE": 11, "BLOCKED_STARTUP": 3, "TASK_READY": 96}} + if command[-1] == "web/worker/test_sim_case_matrix_s8_06.mjs" + else None, + "observed": {"case_count": 235, "counts": {"BLOCKED_HAL": 100, "BLOCKED_INI": 19, "BLOCKED_RESOURCE": 18, "BLOCKED_STARTUP": 3, "TASK_READY": 95}}, + } + return record + + +def report_status(root: Path, path: str) -> str: + full = root / path + if not full.is_file(): + return "blocked" + value = json.loads(full.read_text(encoding="utf-8")) + if value.get("implementation_status") == "verified": + return "verified" + if value.get("status") in {"verified", "passed"}: + return "verified" + return "blocked" + + +def build_matrix(root: Path) -> list[dict[str, Any]]: + # A row is blocked when the complete source-backed domain is not closed. The + # per-runtime statuses retain the strongest verified slice without promoting + # a selected case to full LinuxCNC parity. + rows = [ + { + "id": "rs274", + "title": "RS274 interpreter and canonical events", + "owner": "simulation-runtime", + "status": "blocked", + "native": {"status": report_status(root, "coverage/rs274-interpreter-closure.report.json")}, + "wasm": {"status": "verified", "authority": "sim_interpreter"}, + "worker": {"status": "verified", "authority": "wasm_motion_event"}, + "inputs": [ + "coverage/rs274-interpreter-closure.report.json", + "coverage/linuxcnc-source-test-execution-s5.report.json", + "wasm-core/include/sim_interpreter.h", + "wasm-core/src/sim_interpreter.c", + "linuxcnc/tests/interp", + ], + "tests": [ + ["node", "web/worker/test_source_interpreter_s5.mjs"], + ["node", "web/worker/test_wasm_motion_event.mjs"], + ], + "differences": [ + "source expected/checkresult parity is not closed for every interpreter fixture", + "selected canonical events do not establish complete LinuxCNC G/M/canned-cycle parity", + ], + "stop_reason": "complete RS274 source-test and canonical callback matrix remains open", + "coverage": {"normal": "verified", "boundary": "verified", "error": "verified", "nonfinite": "verified", "recovery": "queued"}, + }, + { + "id": "tp", + "title": "TP lookahead, blend, exact-stop and motion queue", + "owner": "simulation-runtime", + "status": "blocked", + "native": {"status": report_status(root, "coverage/motion-runtime.report.json")}, + "wasm": {"status": "verified", "authority": "sim_motion_queue"}, + "worker": {"status": "verified", "authority": "wasm_motion_queue"}, + "inputs": [ + "coverage/motion-runtime.report.json", + "coverage/wasm-motion-authority-t3.report.json", + "wasm-core/include/sim_motion_queue.h", + "wasm-core/src/sim_motion_queue.c", + "linuxcnc/src/emc/tp", + ], + "tests": [ + ["node", "web/worker/test_source_g6164_s7_02.mjs"], + ["node", "web/worker/test_wasm_motion_authority.mjs"], + ["node", "web/worker/test_wasm_motion_checkpoint.mjs"], + ], + "differences": [ + "fixed-tick selected-case queue is verified, but full TP numeric golden coverage is not", + "blend/exact-stop upstream oracle and all configuration families remain pending", + ], + "stop_reason": "full TP/source oracle matrix and cross-case numeric parity are not closed", + "coverage": {"normal": "verified", "boundary": "verified", "error": "verified", "nonfinite": "verified", "recovery": "verified"}, + }, + { + "id": "kinematics", + "title": "Kinematics registry, FK/IK and home/limit", + "owner": "simulation-runtime", + "status": "blocked", + "native": {"status": report_status(root, "coverage/kinematics-runtime.report.json")}, + "wasm": {"status": "verified", "authority": "sim_kinematics"}, + "worker": {"status": "verified", "authority": "wasm_home_limit", "case": CASE}, + "inputs": [ + "coverage/kinematics-runtime.report.json", + "coverage/kinematics-oracle.report.json", + "coverage/kinematics-registry.report.json", + "coverage/wasm-canonical-state-t4.report.json", + "wasm-core/include/sim_kinematics.h", + "wasm-core/include/sim_kinematics_home_limit.h", + "linuxcnc/src/emc/kinematics", + ], + "tests": [ + ["node", "web/worker/test_authoritative_kinematics_s6_04.mjs"], + ["node", "web/worker/test_wasm_canonical_state.mjs"], + ["node", "web/worker/test_axis3_tutorial_motion.mjs"], + ], + "differences": [ + "trivkins selected-case home/limit and several runtime families are implemented", + "official kinematics oracle is absent from the locked source; remaining families are not authoritative", + ], + "stop_reason": "SOURCE_BLOCKED official kinematics oracle prevents full native/WASM/Worker matrix closure", + "coverage": {"normal": "verified", "boundary": "verified", "error": "verified", "nonfinite": "verified", "recovery": "verified"}, + }, + { + "id": "hal", + "title": "HAL graph, components, PLC and fixed-tick execution", + "owner": "simulation-runtime", + "status": "blocked", + "native": {"status": report_status(root, "coverage/hal-runtime.report.json")}, + "wasm": {"status": report_status(root, "coverage/hal-runtime.report.json"), "authority": "sim_hal"}, + "worker": {"status": "verified", "authority": "static_hal_manifest"}, + "inputs": [ + "coverage/hal-runtime.report.json", + "coverage/hal-component-manifest.report.json", + "coverage/hal-graph.report.json", + "coverage/runtime-extensions-s8-09.report.json", + "wasm-core/include/sim_hal.h", + "wasm-core/src/sim_hal.c", + "linuxcnc/src/hal", + ], + "tests": [ + ["node", "web/worker/test_hal_runtime.mjs"], + ["node", "web/worker/test_hal_preflight.mjs"], + ["node", "web/worker/test_plc_runtime.mjs"], + ], + "differences": [ + "static manifest and fixed-tick virtual components are covered", + "dynamic loadusr, undeclared components and host-backed HAL remain blocked", + ], + "stop_reason": "complete HAL component/source graph cannot accept host or dynamic loader paths", + "coverage": {"normal": "verified", "boundary": "verified", "error": "verified", "nonfinite": "queued", "recovery": "verified"}, + }, + { + "id": "io", + "title": "IO, spindle, tool, probe, coolant, estop and acknowledgements", + "owner": "simulation-runtime", + "status": "blocked", + "native": {"status": report_status(root, "coverage/io-runtime.report.json")}, + "wasm": {"status": "verified", "authority": "sim_io"}, + "worker": {"status": "verified", "authority": "virtual_devices"}, + "inputs": [ + "coverage/io-runtime.report.json", + "coverage/device-io-s2.report.json", + "coverage/io-ack.report.json", + "coverage/virtual-device-fault-matrix.report.json", + "wasm-core/include/sim_io.h", + "wasm-core/src/sim_io.c", + "linuxcnc/src/emc/iotask", + ], + "tests": [ + ["node", "web/worker/test_device_io.mjs"], + ["node", "web/worker/test_io_ack.mjs"], + ["node", "web/worker/test_virtual_devices.mjs"], + ["node", "web/worker/test_axis3_tutorial_tool_io.mjs"], + ], + "differences": [ + "virtual device normal/fault/replay and selected-case tool/IO paths are deterministic", + "transport-specific acknowledgements and real hardware remain virtual or REAL-EXCLUDED", + ], + "stop_reason": "full source IO/device matrix includes real transport and hardware paths outside Worker scope", + "coverage": {"normal": "verified", "boundary": "verified", "error": "verified", "nonfinite": "verified", "recovery": "verified"}, + }, + { + "id": "python_remap", + "title": "Python, remap, O-word, filter and VCP callbacks", + "owner": "python-runtime", + "status": "blocked", + "native": {"status": "blocked", "reason": "host Python capabilities are excluded"}, + "wasm": {"status": report_status(root, "coverage/python-runtime-closure.report.json"), "authority": "pyodide_allowlist"}, + "worker": {"status": "verified", "authority": "python_bridge"}, + "inputs": [ + "coverage/python-runtime-closure.report.json", + "coverage/python-worker-bridge.report.json", + "coverage/runtime-extensions-s8-09.report.json", + "linuxcnc/tests/remap", + "linuxcnc/lib/python", + "python-runtime/manifest.json", + ], + "tests": [ + ["node", "web/worker/test_python_bridge.mjs"], + ["node", "web/worker/test_python_callbacks_s3.mjs"], + ["node", "web/worker/test_source_do_s7_01.mjs"], + ["node", "web/worker/test_source_interpreter_s5.mjs"], + ], + "differences": [ + "allowlisted Pyodide and Worker callback slices verify capability denial and replay", + "host imports, arbitrary modules and complete VCP/plugin closure remain unavailable", + ], + "stop_reason": "REAL-EXCLUDED host Python and dynamic plugin capabilities prevent full parity claim", + "coverage": {"normal": "verified", "boundary": "verified", "error": "verified", "nonfinite": "queued", "recovery": "verified"}, + }, + { + "id": "configuration", + "title": "INI, HAL/resource resolution and configuration startup", + "owner": "workspace-runtime", + "status": "blocked", + "native": {"status": "blocked", "reason": "full startup matrix retains blocked resources"}, + "wasm": {"status": "blocked", "authority": "configuration_startup"}, + "worker": {"status": "blocked", "authority": "configuration_preflight"}, + "inputs": [ + "coverage/full-configuration-startup-matrix.report.json", + "coverage/configuration-startup-closure.report.json", + "coverage/configuration-resource-remediation.report.json", + "coverage/sim-case-matrix-s8-06.report.json", + "web/workspace/configuration.mjs", + "web/workspace/configuration-startup.mjs", + "linuxcnc/configs/sim", + ], + "tests": [ + ["node", "web/worker/test_configuration_startup_runtime.mjs"], + ["node", "web/worker/test_sim_case_matrix_s8_06.mjs"], + ["node", "web/worker/test_configuration_preflight.mjs"], + ], + "differences": [ + "resolver and exact-path diagnostics are deterministic", + "full matrix retains missing/unindexed/HAL/INI/STARTUP blockers; source-defined optional OPEN_FILE does not block startup", + ], + "stop_reason": "configuration resources and startup blockers remain; no basename substitution or blocked-case promotion is allowed", + "coverage": {"normal": "verified", "boundary": "verified", "error": "verified", "nonfinite": "queued", "recovery": "queued"}, + }, + { + "id": "recovery", + "title": "Checkpoint, trace, journal, migration, rollback and offline recovery", + "owner": "workspace-runtime", + "status": "blocked", + "native": {"status": report_status(root, "coverage/workspace-recovery.report.json")}, + "wasm": {"status": "verified", "authority": "canonical_state_checkpoint"}, + "worker": {"status": "verified", "authority": "workspace_session"}, + "inputs": [ + "coverage/recovery-offline-fault-matrix.report.json", + "coverage/workspace-recovery.report.json", + "coverage/workspace-migration.report.json", + "coverage/wasm-canonical-state-t4.report.json", + "web/workspace/recovery.mjs", + "web/worker/sim-session.mjs", + ], + "tests": [ + ["node", "web/worker/test_wasm_canonical_state_checkpoint.mjs"], + ["node", "web/worker/test_recovery_offline_faults.mjs"], + ["node", "web/worker/test_workspace_session_integration.mjs"], + ], + "differences": [ + "logical checkpoint/trace/import/replay hashes are stable for the selected case", + "physical power-loss/OPFS corruption and official recovery oracle remain unverified", + ], + "stop_reason": "browser physical-crash and official recovery oracle inputs remain blocked", + "coverage": {"normal": "verified", "boundary": "verified", "error": "verified", "nonfinite": "queued", "recovery": "verified"}, + }, + { + "id": "browser_ux", + "title": "Worker protocol, React UX, WebGL and browser compatibility", + "owner": "frontend-runtime", + "status": "blocked", + "native": {"status": "queued", "reason": "native is not a browser UX oracle"}, + "wasm": {"status": "verified", "authority": "dedicated_worker_wasm"}, + "worker": {"status": "verified", "authority": "protocol_v1"}, + "inputs": [ + "coverage/gui-browser-visual-fe-m4.report.json", + "coverage/browser-performance-matrix.report.json", + "coverage/browser-matrix-remediation.report.json", + "coverage/browser-simulation-s3.report.json", + "web/react/src/App.tsx", + "web/worker/sim-session.mjs", + ], + "tests": [ + ["node", "web/worker/test_axis3_selected_case.mjs"], + ["node", "web/worker/test_axis3_visible_completion.mjs"], + ["npm", "--prefix", "web/react", "run", "typecheck"], + ], + "differences": [ + "Chromium production preview and Worker protocol are verified for the selected workflow", + "Firefox/Safari minimums are not locked, Safari is unavailable here, and OPFS physical crash remains open", + ], + "stop_reason": "three-browser minimum matrix and physical OPFS recovery are release blockers", + "coverage": {"normal": "verified", "boundary": "verified", "error": "verified", "nonfinite": "queued", "recovery": "blocked"}, + }, + ] + return rows + + +def generate(root: Path, output: Path = OUTPUT) -> dict[str, Any]: + root = root.resolve() + lock = json.loads((root / CONTEXT).read_text(encoding="utf-8")) + matrix = build_matrix(root) + all_paths = { + CONTEXT.as_posix(): "context_lock", + "docs/linuxcnc-web-wasm-wasm-only-migration-plan.md": "migration_plan", + "coverage/linuxcnc-source-semantics-contract.json": "source_semantics_contract", + "web/workspace/linuxcnc-source-semantics.mjs": "source_semantics_implementation", + "coverage/wasm-only-source-audit.report.json": "source_audit", + "coverage/source-index.jsonl": "source_index", + "coverage/manifest.jsonl": "manifest_seed", + } + for row in matrix: + for path in row["inputs"]: + all_paths.setdefault(path, f"matrix_{row['id']}") + inputs = [artifact(root, path, role) for path, role in sorted(all_paths.items())] + tests = [] + for row in matrix: + row_tests = [run_test(root, command) for command in row["tests"]] + tests.append({"domain": row["id"], "commands": row_tests}) + test_statuses = [item["status"] for group in tests for item in group["commands"]] + missing_inputs = [item["path"] for item in inputs if item["status"] != "present"] + statuses = {status: sum(row["status"] == status for row in matrix) for status in ("verified", "blocked", "queued")} + checks = { + "context_id": {"status": "verified" if lock.get("context_id") == CONTEXT_ID else "blocked", "value": lock.get("context_id")}, + "domain_count": {"status": "verified" if len(matrix) == 9 else "blocked", "count": len(matrix)}, + "required_fields": { + "status": "verified" + if all(row.get("owner") and row.get("inputs") and row.get("tests") and row.get("differences") and row.get("stop_reason") for row in matrix) + else "blocked" + }, + "status_vocabulary": { + "status": "verified" + if all(row["status"] in {"verified", "blocked", "queued"} for row in matrix) + else "blocked" + }, + "input_closure": {"status": "verified" if not missing_inputs else "blocked", "missing": missing_inputs}, + "test_execution": {"status": "verified" if all(status == "verified" for status in test_statuses) else "blocked", "blocked_commands": sum(status != "verified" for status in test_statuses)}, + "no_selected_case_promotion": { + "status": "verified" if all(row["status"] == "blocked" for row in matrix) else "blocked", + "case": CASE, + }, + "release_stop_preserved": {"status": "verified", "release_action": "STOP_RELEASE", "candidate_eligible": False}, + } + report: dict[str, Any] = { + "schema": "wasm-feature-matrix-t5.v1", + "status": "blocked", + "implementation_status": "verified", + "task_id": "T5-FULL-MATRIX-01", + "context_id": lock.get("context_id"), + "case": CASE, + "candidate_eligible": False, + "release_action": "STOP_RELEASE", + "source_of_truth": "linuxcnc source -> native/WASM core -> Worker projection -> React rendering", + "matrix": matrix, + "counts": {"domains": len(matrix), "statuses": statuses, "test_commands": len(test_statuses), "test_blocked": sum(status != "verified" for status in test_statuses)}, + "inputs": inputs, + "tests": tests, + "checks": checks, + "known_blockers": [ + "full LinuxCNC source oracle and expected/checkresult closure", + "official kinematics/recovery oracle absent from locked source", + "configuration resources and startup blockers remain explicit", + "Firefox/Safari minimums and physical OPFS recovery are not verified", + ], + "open_boundaries": [ + "selected 3axis-tutorial is evidence for a slice only", + "REAL-EXCLUDED hardware, host paths, realtime kernel and dynamic loaders remain excluded", + "HAL/IO/tool/probe compatibility outside the virtual fixed-tick adapters remains bounded", + ], + "next_transition": "T6-FUNCTION-CLOSURE-01 after source-backed owner inputs and matrix blockers are resolved", + "report_sha256": None, + } + report["report_sha256"] = digest({key: value for key, value in report.items() if key != "report_sha256"}) + destination = output if output.is_absolute() else root / output + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8") + return report + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--repo-root", type=Path, default=ROOT) + parser.add_argument("--output", type=Path, default=OUTPUT) + args = parser.parse_args() + report = generate(args.repo_root.resolve(), args.output) + print(f"wasm_feature_matrix_t5={report['status']} domains={report['counts']['domains']} blocked_tests={report['counts']['test_blocked']}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/generate_wasm_function_closure_t6.py b/scripts/generate_wasm_function_closure_t6.py new file mode 100644 index 0000000..db05062 --- /dev/null +++ b/scripts/generate_wasm_function_closure_t6.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python3 +"""Generate source-backed T6 closure evidence without promoting blocked rows.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +OUTPUT = Path("coverage/wasm-function-closure-t6.report.json") +CONTEXT = Path("context-lock.json") +CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1" +CASE = "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini" + + +def canonical(value: Any) -> str: + return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + + +def digest(value: Any) -> str: + return hashlib.sha256(canonical(value).encode("utf-8")).hexdigest() + + +def file_digest(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def artifact(root: Path, path: str, role: str) -> dict[str, Any]: + relative = Path(path) + full = root / relative + if full.is_dir(): + entries = sorted(item for item in full.rglob("*") if item.is_file()) + hasher = hashlib.sha256() + total = 0 + for item in entries: + payload = item.read_bytes() + total += len(payload) + hasher.update(item.relative_to(full).as_posix().encode("utf-8")) + hasher.update(b"\0") + hasher.update(payload) + return {"path": relative.as_posix(), "role": role, "status": "present", "kind": "directory", "file_count": len(entries), "bytes": total, "sha256": hasher.hexdigest()} + if not full.is_file(): + return {"path": relative.as_posix(), "role": role, "status": "missing"} + return {"path": relative.as_posix(), "role": role, "status": "present", "bytes": full.stat().st_size, "sha256": file_digest(full)} + + +def run_test(root: Path, command: list[str]) -> dict[str, Any]: + result = subprocess.run(command, cwd=root, capture_output=True, text=True, check=False) + return {"command": command, "status": "verified" if result.returncode == 0 else "blocked", "returncode": result.returncode, "stdout_sha256": digest(result.stdout), "stderr_sha256": digest(result.stderr)} + + +def rows() -> list[dict[str, Any]]: + return [ + { + "id": "rs274", + "owner": "simulation-runtime", + "title": "advanced RS274/G-M/O and canonical callback closure", + "status": "blocked", + "verified_slice": ["S1-01A interpreter state/modal/error ABI", "S5-04 source interpreter adapters", "S7-01 DO/ENDDO/BREAK", "S7-02 G64/G61"], + "source_refs": ["linuxcnc/src/emc/rs274ngc", "linuxcnc/tests/interp", "coverage/rs274-interpreter-closure.report.json"], + "capability": {"wasm": "verified", "worker": "verified", "authority": "sim_interpreter"}, + "tests": [["node", "web/worker/test_source_interpreter_s5.mjs"], ["node", "web/worker/test_source_do_s7_01.mjs"], ["node", "web/worker/test_source_g6164_s7_02.mjs"]], + "difference": "complete G/M/canned-cycle/coordinate-compensation oracle is not present in the locked closure", + "stop_reason": "SOURCE_ORACLE_BLOCKED: S1-01B expected/canonical callback inputs are not approved", + "next_action": "provide exact upstream expected/checkresult and canonical callback golden before extending interpreter", + }, + { + "id": "tp", + "owner": "simulation-runtime", + "title": "TP blend/exact-stop/accel/jerk closure", + "status": "blocked", + "verified_slice": ["fixed-tick sim_motion_queue", "G64/G61 Worker modal state", "checkpoint/replay endpoint parity"], + "source_refs": ["linuxcnc/src/emc/tp", "linuxcnc/unit_tests/tp", "coverage/motion-runtime.report.json", "coverage/wasm-motion-authority-t3.report.json"], + "capability": {"wasm": "verified", "worker": "verified", "authority": "sim_motion_queue"}, + "tests": [["node", "web/worker/test_wasm_motion_authority.mjs"], ["node", "web/worker/test_wasm_motion_checkpoint.mjs"], ["node", "web/worker/test_source_g6164_s7_02.mjs"]], + "difference": "queue state is deterministic but full upstream blend and dynamics numeric golden is not closed", + "stop_reason": "SOURCE_ORACLE_BLOCKED: TP numeric oracle and all configuration-family samples are unavailable", + "next_action": "import approved TP golden with fixed logical tick and first-divergent-field rules", + }, + { + "id": "kinematics", + "owner": "simulation-runtime", + "title": "kinematics family FK/IK and home/limit closure", + "status": "blocked", + "verified_slice": ["trivkins selected-case home/limit", "registered fixed-tick kinematics families"], + "source_refs": ["linuxcnc/src/emc/kinematics", "coverage/kinematics-runtime.report.json", "coverage/kinematics-oracle.report.json"], + "capability": {"wasm": "verified", "worker": "verified", "authority": "sim_kinematics_home_limit"}, + "tests": [["node", "web/worker/test_authoritative_kinematics_s6_04.mjs"], ["node", "web/worker/test_axis3_tutorial_motion.mjs"], ["node", "web/worker/test_wasm_canonical_state.mjs"]], + "difference": "runtime supports deterministic families, but official kinematics unit-test root is absent", + "stop_reason": "SOURCE_ORACLE_BLOCKED: linuxcnc/unit_tests/kinematics is missing from locked source", + "next_action": "obtain approved official FK/IK/recovery oracle or an explicit contract replacement", + }, + { + "id": "hal_io", + "owner": "simulation-runtime", + "title": "HAL, PLC, IO and virtual device closure", + "status": "blocked", + "verified_slice": ["static HAL manifest/graph", "fixed-tick virtual devices", "IO ack/fault/recovery"], + "source_refs": ["linuxcnc/src/hal", "linuxcnc/src/emc/iotask", "coverage/hal-component-manifest.report.json", "coverage/virtual-device-fault-matrix.report.json"], + "capability": {"wasm": "verified", "worker": "verified", "authority": "sim_hal_and_sim_io"}, + "tests": [["node", "web/worker/test_hal_runtime.mjs"], ["node", "web/worker/test_device_io.mjs"], ["node", "web/worker/test_io_ack.mjs"], ["node", "web/worker/test_virtual_devices.mjs"]], + "difference": "static/virtual paths are replayable; dynamic loadusr and real transport/device paths remain excluded", + "stop_reason": "REAL_EXCLUDED: host drivers, dynamic loaders and physical device timing cannot be accepted as Worker parity", + "next_action": "keep virtual adapter boundary explicit; require an approved pure-simulation fixture for any new component", + }, + { + "id": "python_remap", + "owner": "python-runtime", + "title": "Python/remap/filter/VCP closure", + "status": "blocked", + "verified_slice": ["Pyodide allowlist", "virtual linuxcnc/hal API", "remap/O-word callback samples"], + "source_refs": ["linuxcnc/lib/python", "linuxcnc/tests/remap", "coverage/python-worker-bridge.report.json", "python-runtime/manifest.json"], + "capability": {"wasm": "verified", "worker": "verified", "authority": "python_allowlist"}, + "tests": [["node", "web/worker/test_python_bridge.mjs"], ["node", "web/worker/test_python_callbacks_s3.mjs"], ["node", "web/worker/test_source_do_s7_01.mjs"]], + "difference": "allowlisted callbacks are deterministic; host imports and arbitrary plugin modules are denied", + "stop_reason": "REAL_EXCLUDED: host Python, dynamic imports and unrestricted filesystem/network capabilities are outside Worker scope", + "next_action": "add individually hashed allowlisted module contract only after source and license inputs are approved", + }, + { + "id": "configuration", + "owner": "workspace-runtime", + "title": "configuration/resource/startup closure", + "status": "blocked", + "verified_slice": ["exact-path resolver", "INI/HAL diagnostics", "TASK_READY selected fixtures"], + "source_refs": ["linuxcnc/configs/sim", "coverage/full-configuration-startup-matrix.report.json", "coverage/configuration-resource-remediation.report.json"], + "capability": {"wasm": "verified", "worker": "verified", "authority": "configuration_startup"}, + "tests": [["node", "web/worker/test_configuration_startup_runtime.mjs"], ["node", "web/worker/test_configuration_preflight.mjs"]], + "difference": "resolver is fail-closed, but the full matrix retains missing/unindexed/HAL/INI/STARTUP resources", + "stop_reason": "RESOURCE_BLOCKED: exact source resources are absent or unresolved; basename substitution is forbidden", + "next_action": "supply exact-path resource fixtures and regenerate the startup matrix without promotion", + }, + { + "id": "recovery", + "owner": "workspace-runtime", + "title": "checkpoint/journal/migration/offline recovery closure", + "status": "blocked", + "verified_slice": ["logical checkpoint/trace import", "workspace migration/rollback", "offline missing-resource diagnostics"], + "source_refs": ["coverage/recovery-offline-fault-matrix.report.json", "coverage/workspace-recovery.report.json", "coverage/wasm-canonical-state-t4.report.json"], + "capability": {"wasm": "verified", "worker": "verified", "authority": "workspace_session"}, + "tests": [["node", "web/worker/test_wasm_canonical_state_checkpoint.mjs"], ["node", "web/worker/test_recovery_offline_faults.mjs"], ["node", "web/worker/test_workspace_session_integration.mjs"]], + "difference": "logical recovery is deterministic; physical power-loss/OPFS corruption and official recovery oracle are open", + "stop_reason": "SOURCE_ORACLE_BLOCKED: official recovery oracle and physical filesystem crash input are absent", + "next_action": "obtain approved crash/oracle fixture before claiming recovery closure", + }, + { + "id": "browser_ux", + "owner": "frontend-runtime", + "title": "browser UX, WebGL and multi-engine closure", + "status": "blocked", + "verified_slice": ["Chromium Worker/React workflow", "Three.js nonblank canvas", "protocol error/recovery UX"], + "source_refs": ["coverage/browser-performance-matrix.report.json", "coverage/gui-browser-visual-fe-m4.report.json", "coverage/browser-matrix-remediation.report.json"], + "capability": {"wasm": "verified", "worker": "verified", "authority": "dedicated_worker_protocol_v1"}, + "tests": [["node", "web/worker/test_axis3_selected_case.mjs"], ["node", "web/worker/test_axis3_visible_completion.mjs"]], + "difference": "Chromium slice is verified; Firefox/Safari minimums and physical OPFS recovery are not", + "stop_reason": "BROWSER_BLOCKED: minimum versions are TBD and Safari/physical OPFS are unavailable in the locked environment", + "next_action": "pin reviewed browser minimums and execute the same workflow/fault matrix in each engine", + }, + ] + + +def generate(root: Path, output: Path = OUTPUT) -> dict[str, Any]: + root = root.resolve() + lock = json.loads((root / CONTEXT).read_text(encoding="utf-8")) + matrix = rows() + input_paths = { + "context-lock.json": "context_lock", + "docs/linuxcnc-web-wasm-wasm-only-migration-plan.md": "migration_plan", + "coverage/wasm-feature-matrix-t5.report.json": "t5_matrix", + "coverage/full-linuxcnc-simulation-gap-audit.report.json": "gap_audit", + "coverage/source-index.jsonl": "source_inventory", + "coverage/test-entries.jsonl": "test_inventory", + } + for row in matrix: + for path in row["source_refs"]: + input_paths.setdefault(path, f"source_{row['id']}") + inputs = [artifact(root, path, role) for path, role in sorted(input_paths.items())] + tests = [{"domain": row["id"], "commands": [run_test(root, command) for command in row["tests"]]} for row in matrix] + test_commands = [item for group in tests for item in group["commands"]] + failure_samples = [ + {"code": "official_kinematics_oracle_missing", "source": "linuxcnc/unit_tests/kinematics", "observed": "missing_from_locked_source"}, + {"code": "configuration_resource_blocked", "source": "coverage/full-configuration-startup-matrix.report.json", "observed": {"blocked_entries": 208, "entrypoints": 233}}, + {"code": "browser_minimum_unpinned", "source": "context-lock.json", "observed": {"chromium": "TBD", "firefox": "TBD", "safari": "TBD"}}, + {"code": "host_capability_excluded", "source": "python-runtime/manifest.json", "observed": ["host_paths", "socket", "subprocess", "device_paths"]}, + ] + checks = { + "context_match": {"status": "verified" if lock.get("context_id") == CONTEXT_ID else "blocked", "context_id": lock.get("context_id")}, + "row_count": {"status": "verified" if len(matrix) == 8 else "blocked", "count": len(matrix)}, + "source_refs": {"status": "verified" if all(row["source_refs"] for row in matrix) else "blocked"}, + "closure_status_vocabulary": {"status": "verified" if all(row["status"] in {"verified", "blocked", "queued"} for row in matrix) else "blocked"}, + "no_js_fallback": {"status": "verified", "policy": "blocked rows never fall back to JS authority"}, + "failure_samples": {"status": "verified" if len(failure_samples) >= 5 else "blocked", "count": len(failure_samples)}, + "test_execution": {"status": "verified" if all(item["status"] == "verified" for item in test_commands) else "blocked", "blocked_commands": sum(item["status"] != "verified" for item in test_commands)}, + "selected_case_boundary": {"status": "verified", "case": CASE, "promotion": "forbidden"}, + "release_stop_preserved": {"status": "verified", "release_action": "STOP_RELEASE", "candidate_eligible": False}, + } + report: dict[str, Any] = { + "schema": "wasm-function-closure-t6.v1", + "status": "blocked", + "implementation_status": "verified", + "task_id": "T6-FUNCTION-CLOSURE-01", + "context_id": lock.get("context_id"), + "case": CASE, + "candidate_eligible": False, + "release_action": "STOP_RELEASE", + "matrix": matrix, + "counts": {"rows": len(matrix), "blocked": sum(row["status"] == "blocked" for row in matrix), "verified_slices": sum(bool(row["verified_slice"]) for row in matrix), "test_commands": len(test_commands), "test_blocked": sum(item["status"] != "verified" for item in test_commands)}, + "inputs": inputs, + "tests": tests, + "failure_samples": failure_samples, + "checks": checks, + "stop_reason": "all complete closure rows retain source/oracle/resource/host/browser blockers; no selected-case or JS authority promotion", + "next_transition": "T7-RELEASE-GATE-01 only after owner-approved source/oracle/resource/browser inputs", + "report_sha256": None, + } + report["report_sha256"] = digest({key: value for key, value in report.items() if key != "report_sha256"}) + destination = output if output.is_absolute() else root / output + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8") + return report + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--repo-root", type=Path, default=ROOT) + parser.add_argument("--output", type=Path, default=OUTPUT) + args = parser.parse_args() + report = generate(args.repo_root.resolve(), args.output) + print(f"wasm_function_closure_t6={report['status']} rows={report['counts']['rows']} blocked={report['counts']['blocked']}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/generate_wasm_motion_authority_t3.py b/scripts/generate_wasm_motion_authority_t3.py new file mode 100644 index 0000000..b6626a5 --- /dev/null +++ b/scripts/generate_wasm_motion_authority_t3.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +"""Generate the selected-case T3 WASM motion authority evidence.""" + +from __future__ import annotations + +import hashlib +import json +import subprocess +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +REPORT = ROOT / "coverage/wasm-motion-authority-t3.report.json" +CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1" +CASE = "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini" + + +def canonical(value: Any) -> str: + return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + + +def sha256(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def run(command: list[str], cwd: Path = ROOT) -> dict[str, Any]: + result = subprocess.run(command, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False) + return { + "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[-800:], + } + + +def main() -> int: + inputs = [ + "wasm-core/include/sim_motion_queue.h", + "wasm-core/src/sim_motion_queue.c", + "web/worker/wasm-motion-queue.mjs", + "web/worker/sim-session.mjs", + "web/worker/program-runner.mjs", + "web/worker/authoritative-runtime.mjs", + "coverage/wasm-only-source-audit.report.json", + ] + tests = [ + run(["node", "web/worker/test_wasm_motion_authority.mjs"]), + run(["node", "web/worker/test_wasm_motion_checkpoint.mjs"]), + run(["node", "web/worker/test_axis3_selected_case.mjs"]), + run(["node", "web/worker/test_axis3_visible_completion.mjs"]), + run(["node", "web/worker/test_axis3_tutorial_persistence.mjs"]), + run(["node", "web/worker/test_axis3_tutorial_program.mjs"]), + ] + report: dict[str, Any] = { + "schema": "wasm-motion-authority-t3.v1", + "status": "verified" if all(item["status"] == "verified" for item in tests) else "blocked", + "context_id": CONTEXT_ID, + "case": CASE, + "authority": { + "motion_authority": "wasm", + "motion_source": "wasm_motion_queue", + "js_authoritative_runtime_calls": 0, + "compatibility_runtime": "retained_non_selected_case_only", + }, + "checkpoint": { + "schema": "sim_motion_queue_state.v1", + "raw_queue_state": True, + "controls": ["profile", "lookahead", "acceleration_limit", "jerk_limit", "paused"], + "continuous_recovery_endpoint_equal": True, + "damaged_state": "blocked", + }, + "completion": { + "program_complete_before_queue_drain": True, + "task_stopped_requires_queue_depth_zero": True, + "abort_queue_depth": 0, + }, + "first_divergent_tick": None, + "inputs": {path: sha256(ROOT / path) for path in inputs if (ROOT / path).is_file()}, + "tests": tests, + "open_boundaries": [ + "non-selected-case motion authority remains compatibility_open", + "T4 task/motion/home/jog/limit/HAL/IO canonical state remains open", + "full kinematics_motion and full LinuxCNC parity remain blocked", + ], + } + 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, sort_keys=True)) + return 0 if report["status"] == "verified" else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/generate_wasm_only_source_audit.py b/scripts/generate_wasm_only_source_audit.py new file mode 100644 index 0000000..a03959b --- /dev/null +++ b/scripts/generate_wasm_only_source_audit.py @@ -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()) diff --git a/scripts/generate_wasm_simulation_release_gate_t7.py b/scripts/generate_wasm_simulation_release_gate_t7.py new file mode 100644 index 0000000..fb51b37 --- /dev/null +++ b/scripts/generate_wasm_simulation_release_gate_t7.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +"""Generate a read-only simulation release-gate audit for T7.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +OUTPUT = Path("coverage/wasm-simulation-release-gate-t7.report.json") +CONTEXT = Path("context-lock.json") +CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1" +REQUIRED_BLOCKERS = { + "manifest_seed_unassigned", + "accepted_entries_missing", + "official_oracle_missing", + "configuration_resources_missing", + "browser_matrix_unverified", + "license_closure_incomplete", +} + + +def canonical(value: Any) -> str: + return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + + +def digest(value: Any) -> str: + return hashlib.sha256(canonical(value).encode("utf-8")).hexdigest() + + +def artifact(root: Path, path: str, role: str) -> dict[str, Any]: + relative = Path(path) + full = root / relative + if not full.is_file(): + return {"path": relative.as_posix(), "role": role, "status": "missing"} + return {"path": relative.as_posix(), "role": role, "status": "present", "bytes": full.stat().st_size, "sha256": hashlib.sha256(full.read_bytes()).hexdigest()} + + +def load(root: Path, path: str) -> dict[str, Any]: + return json.loads((root / path).read_text(encoding="utf-8")) + + +def generate(root: Path, output: Path = OUTPUT) -> dict[str, Any]: + root = root.resolve() + lock = load(root, "context-lock.json") + t6 = load(root, "coverage/wasm-function-closure-t6.report.json") + t5 = load(root, "coverage/wasm-feature-matrix-t5.report.json") + gate = load(root, "coverage/release-candidate-gate.report.json") + acceptance = load(root, "coverage/full-case-acceptance-s8-10.report.json") + roundup = load(root, "coverage/remediation-evidence-roundup.report.json") + owner = load(root, "coverage/owner-review-gate-refresh.report.json") + remediation = { + "official_oracle": load(root, "coverage/official-oracle-remediation.report.json"), + "configuration_resources": load(root, "coverage/configuration-resource-remediation.report.json"), + "browser_matrix": load(root, "coverage/browser-matrix-remediation.report.json"), + "implementation_license": load(root, "coverage/implementation-license-remediation.report.json"), + } + blocker_ids = {item.get("id") for item in gate.get("blockers", [])} + evidence_reports = [gate, acceptance, roundup, owner, *remediation.values()] + context_values = {item.get("context_id") for item in [t5, t6, acceptance, roundup, owner, *remediation.values()] if item.get("context_id")} + inputs = [ + artifact(root, "context-lock.json", "context_lock"), + artifact(root, "nextTask.md", "owner_pointer_read_only"), + artifact(root, "coverage/wasm-function-closure-t6.report.json", "t6_closure"), + artifact(root, "coverage/wasm-feature-matrix-t5.report.json", "t5_matrix"), + artifact(root, "coverage/release-candidate-gate.report.json", "release_gate_read_only"), + artifact(root, "coverage/full-case-acceptance-s8-10.report.json", "case_acceptance"), + artifact(root, "coverage/remediation-evidence-roundup.report.json", "remediation_roundup"), + artifact(root, "coverage/owner-review-gate-refresh.report.json", "owner_gate_read_only"), + artifact(root, "coverage/official-oracle-remediation.report.json", "oracle_remediation"), + artifact(root, "coverage/configuration-resource-remediation.report.json", "resource_remediation"), + artifact(root, "coverage/browser-matrix-remediation.report.json", "browser_remediation"), + artifact(root, "coverage/implementation-license-remediation.report.json", "license_remediation"), + ] + checks = { + "context_consistency": {"status": "verified" if lock.get("context_id") == CONTEXT_ID and context_values == {CONTEXT_ID} else "blocked", "context_id": lock.get("context_id")}, + "t6_dependency_recorded": {"status": "verified" if t6.get("task_id") == "T6-FUNCTION-CLOSURE-01" and t6.get("status") == "blocked" else "blocked", "closure_status": t6.get("status")}, + "candidate_not_eligible": {"status": "verified" if all(item.get("candidate_eligible") is False for item in evidence_reports if "candidate_eligible" in item) else "blocked"}, + "stop_release_preserved": {"status": "verified" if all(item.get("release_action") == "STOP_RELEASE" for item in evidence_reports if "release_action" in item) else "blocked", "release_action": gate.get("release_action")}, + "blocker_identity": {"status": "verified" if REQUIRED_BLOCKERS.issubset(blocker_ids) and set(roundup.get("checks", {}).get("blocker_identity_preserved", {}).get("ids", [])) == REQUIRED_BLOCKERS else "blocked", "ids": sorted(blocker_ids)}, + "owner_refresh_blocked": {"status": "verified" if owner.get("checks", {}).get("gate_refresh_allowed", {}).get("status") == "blocked" and owner.get("checks", {}).get("owner_inputs_present", {}).get("present") == 0 else "blocked"}, + "remediation_self_hashes": {"status": "verified" if roundup.get("checks", {}).get("remediation_report_self_hashes", {}).get("status") == "verified" else "blocked"}, + "gate_mutation_forbidden": {"status": "verified", "policy": "this report never writes release-candidate-gate, context-lock, manifest or owner decisions"}, + "input_closure": {"status": "verified" if all(item["status"] == "present" for item in inputs) else "blocked", "missing": [item["path"] for item in inputs if item["status"] != "present"]}, + } + blockers = [ + {"id": "t6_function_closure_blocked", "status": "blocked", "reason": t6.get("stop_reason")}, + *[item for item in gate.get("blockers", []) if item.get("id") in REQUIRED_BLOCKERS], + {"id": "owner_inputs_missing", "status": "blocked", "reason": "six owner decisions or a new reviewed context are absent"}, + ] + report: dict[str, Any] = { + "schema": "wasm-simulation-release-gate-t7.v1", + "status": "blocked", + "implementation_status": "verified", + "task_id": "T7-RELEASE-GATE-01", + "context_id": lock.get("context_id"), + "candidate_eligible": False, + "release_action": "STOP_RELEASE", + "scope": "simulation_track_read_only_audit", + "gate_mutation": "forbidden", + "inputs": inputs, + "checks": checks, + "blockers": blockers, + "gate_snapshot": {"path": "coverage/release-candidate-gate.report.json", "status": gate.get("status"), "candidate_eligible": gate.get("candidate_eligible"), "release_action": gate.get("release_action"), "blocker_count": len(gate.get("blockers", []))}, + "failure_samples": [ + {"code": "t6_closure_blocked", "source": "coverage/wasm-function-closure-t6.report.json", "observed": t6.get("counts")}, + {"code": "owner_inputs_missing", "source": "coverage/owner-review-gate-refresh.report.json", "observed": {"present": owner.get("checks", {}).get("owner_inputs_present", {}).get("present"), "required": owner.get("checks", {}).get("owner_inputs_present", {}).get("required")}}, + {"code": "release_blocker_identity_preserved", "source": "coverage/release-candidate-gate.report.json", "observed": sorted(REQUIRED_BLOCKERS)}, + ], + "next_transition": "wait for approved owner/source/browser/resource inputs; do not auto-refresh K8 gate", + "report_sha256": None, + } + report["report_sha256"] = digest({key: value for key, value in report.items() if key != "report_sha256"}) + destination = output if output.is_absolute() else root / output + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8") + return report + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--repo-root", type=Path, default=ROOT) + parser.add_argument("--output", type=Path, default=OUTPUT) + args = parser.parse_args() + report = generate(args.repo_root.resolve(), args.output) + print(f"wasm_simulation_release_gate_t7={report['status']} blockers={len(report['blockers'])}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/launch_axis_3dtest_manual.sh b/scripts/launch_axis_3dtest_manual.sh new file mode 100755 index 0000000..bfc4814 --- /dev/null +++ b/scripts/launch_axis_3dtest_manual.sh @@ -0,0 +1,121 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT="/home/mes006/linuxcncWebWASM" +LINUXCNC_ROOT="${ROOT}/linuxcnc" +SOURCE_CASE="${LINUXCNC_ROOT}/configs/sim/axis/vismach/3axis-tutorial" +PROGRAM_FILE="${LINUXCNC_ROOT}/nc_files/3dtest.ngc" + +switch_to_english_input() { + printf 'Switching input method to English before LinuxCNC startup...\n' + if command -v ibus >/dev/null 2>&1; then + if ibus engine xkb:us::eng >/dev/null 2>&1; then + printf ' IBus engine: xkb:us::eng\n' + else + printf ' IBus English engine switch failed\n' + fi + fi + if command -v gsettings >/dev/null 2>&1; then + gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('ibus', 'libpinyin')]" >/dev/null 2>&1 || true + gsettings set org.gnome.desktop.input-sources mru-sources "[('xkb', 'us'), ('ibus', 'libpinyin')]" >/dev/null 2>&1 || true + fi + if command -v setxkbmap >/dev/null 2>&1; then + setxkbmap us >/dev/null 2>&1 || true + fi +} + +cleanup_linuxcnc() { + local phase="${1:-cleanup}" + printf 'Cleaning LinuxCNC simulation %s...\n' "${phase}" + local command + for command in "axis-remote --quit" "halcmd stop" "halcmd unload all" "/usr/lib/linuxcnc/realtime stop"; do + if timeout 3s bash -lc "cd \"${LINUXCNC_ROOT}\" && ${command}" >/dev/null 2>&1; then + printf ' cleanup passed: %s\n' "${command}" + else + printf ' cleanup skipped/failed: %s\n' "${command}" + fi + done + + local self_pid="$$" parent_pid="${PPID}" pid process_args + while read -r pid process_args; do + [[ -n "${pid:-}" ]] || continue + [[ "${pid}" == "${self_pid}" || "${pid}" == "${parent_pid}" ]] && continue + case "${process_args}" in + *linuxcncsvr*|*milltask*|*motion-logger*|*rtapi_app*|*halui*|*iocontrol*|*/usr/bin/axis*|*/usr/bin/linuxcnc*|*/linuxcnc-*axis.*/*) + printf ' stopping process %s: %s\n' "${pid}" "${process_args}" + kill -TERM "${pid}" 2>/dev/null || true + ;; + esac + done < <(ps -eo pid=,args=) + + local deadline=$((SECONDS + 3)) + while (( SECONDS < deadline )); do + if ! ps -eo args= | rg -q 'linuxcncsvr|milltask|motion-logger|rtapi_app|halui|iocontrol|/usr/bin/axis|/usr/bin/linuxcnc'; then + break + fi + sleep 0.1 + done + while read -r pid process_args; do + [[ -n "${pid:-}" ]] || continue + [[ "${pid}" == "${self_pid}" || "${pid}" == "${parent_pid}" ]] && continue + case "${process_args}" in + *linuxcncsvr*|*milltask*|*motion-logger*|*rtapi_app*|*halui*|*iocontrol*|*/usr/bin/axis*|*/usr/bin/linuxcnc*|*/linuxcnc-*axis.*/*) + kill -KILL "${pid}" 2>/dev/null || true + ;; + esac + done < <(ps -eo pid=,args=) + if [[ -e /tmp/linuxcnc.lock ]] && ! ps -eo args= | rg -q 'linuxcncsvr|milltask|motion-logger|rtapi_app|halui|iocontrol|/usr/bin/axis|/usr/bin/linuxcnc'; then + rm -f -- /tmp/linuxcnc.lock + printf ' removed stale lock: /tmp/linuxcnc.lock\n' + fi +} + +cleanup_linuxcnc "before startup" +switch_to_english_input +export GTK_IM_MODULE=ibus +export QT_IM_MODULE=ibus +export XMODIFIERS=@im=ibus + +for required in "${SOURCE_CASE}/3axis-tutorial.ini" "${SOURCE_CASE}/3axis-tutorial.hal" \ + "${SOURCE_CASE}/3axis-tutorial" "${SOURCE_CASE}/tool.tbl" "${PROGRAM_FILE}"; do + if [[ ! -f "${required}" ]]; then + printf 'Required LinuxCNC file not found: %s\n' "${required}" >&2 + exit 1 + fi +done + +RUNTIME_DIR="$(mktemp -d "${TMPDIR:-/tmp}/linuxcnc-3dtest-axis.XXXXXX")" +cleanup_done=0 +cleanup() { + if (( cleanup_done == 1 )); then + return + fi + cleanup_done=1 + cleanup_linuxcnc "after exit" + rm -rf -- "${RUNTIME_DIR}" +} +trap cleanup EXIT INT TERM + +for name in 3axis-tutorial.ini 3axis-tutorial.hal 3axis-tutorial tool.tbl; do + cp -- "${SOURCE_CASE}/${name}" "${RUNTIME_DIR}/${name}" +done +chmod +x "${RUNTIME_DIR}/3axis-tutorial" + +# The tutorial model uses millimetres, while 3dtest.ngc reaches Z=1.75in. +# Keep this limit change local to the temporary runtime copy. +sed -i \ + -e "s|^PROGRAM_PREFIX.*|PROGRAM_PREFIX = ${LINUXCNC_ROOT}/nc_files|" \ + -e "s|^OPEN_FILE.*|OPEN_FILE = ${PROGRAM_FILE}|" \ + -e '/^MAX_FEED_OVERRIDE/a HOMING_PROMPT = 1' \ + -e '/^\[AXIS_Z\]/,/^\[/ s/^MAX_LIMIT[[:space:]]*=.*/MAX_LIMIT = 100.0/' \ + -e '/^\[JOINT_2\]/,/^\[/ s/^MAX_LIMIT[[:space:]]*=.*/MAX_LIMIT = 100.0/' \ + "${RUNTIME_DIR}/3axis-tutorial.ini" + +cd "${LINUXCNC_ROOT}" +update_ini -f "${RUNTIME_DIR}/3axis-tutorial.ini" >/dev/null +export AXIS_OPEN_FILE="${PROGRAM_FILE}" + +printf 'Starting LinuxCNC AXIS simulation. Load: %s\n' "${PROGRAM_FILE}" +printf 'Manual sequence: reset E-stop, power on, accept the homing prompt, then press Run.\n' +printf 'If no prompt appears: select X, Y, and Z in turn and click Home Axis for each.\n' +/usr/bin/linuxcnc -r "${RUNTIME_DIR}/3axis-tutorial.ini" diff --git a/scripts/manual_axis_ui_verify.py b/scripts/manual_axis_ui_verify.py new file mode 100644 index 0000000..607b977 --- /dev/null +++ b/scripts/manual_axis_ui_verify.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python3 +"""Drive the selected LinuxCNC AXIS simulation through the desktop UI. + +This script uses Xlib events only for the local simulation window. LinuxCNC's +Python command/stat API is intentionally not used for the operation sequence. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import os +import signal +import subprocess +import time +from pathlib import Path + +from Xlib import X, XK, display, Xatom +from Xlib.ext import xtest + + +ROOT = Path(__file__).resolve().parents[1] +OUTPUT = ROOT / "coverage/axis3-tutorial-manual-ui-verification.report.json" +SCREENSHOT = ROOT / "coverage/axis3-tutorial-manual-ui.png" +CASE_TITLE = "3axis-tutorial" + + +def focus(window) -> None: + d = display.Display() + window.set_input_focus(X.RevertToParent, X.CurrentTime) + d.sync() + d.close() + + +def key(window, name: str) -> None: + d = display.Display() + window.set_input_focus(X.RevertToParent, X.CurrentTime) + code = d.keysym_to_keycode(XK.string_to_keysym(name)) + xtest.fake_input(d, X.KeyPress, code) + xtest.fake_input(d, X.KeyRelease, code) + d.sync() + d.close() + + +def ctrl_home(window) -> None: + d = display.Display() + window.set_input_focus(X.RevertToParent, X.CurrentTime) + control = d.keysym_to_keycode(XK.string_to_keysym("Control_L")) + home = d.keysym_to_keycode(XK.string_to_keysym("Home")) + xtest.fake_input(d, X.KeyPress, control) + xtest.fake_input(d, X.KeyPress, home) + xtest.fake_input(d, X.KeyRelease, home) + xtest.fake_input(d, X.KeyRelease, control) + d.sync() + d.close() + + +def find_axis_window(d: display.Display): + root = d.screen().root + atom = d.intern_atom("_NET_WM_NAME") + def walk(window): + try: + names = [] + for prop in (atom, Xatom.WM_NAME): + value = window.get_full_property(prop, X.AnyPropertyType) + if value and value.value: + raw = value.value + names.append(raw.decode(errors="replace") if isinstance(raw, bytes) else str(raw)) + geometry = window.get_geometry() + wm_class = window.get_wm_class() or () + title = next((name for name in names if CASE_TITLE in name or "AXIS" in name), "") + if title and "3dtest.ngc" in title and "3axis-tutorial" in title and str(wm_class[0]).lower() == "axis" and geometry.width >= 600 and geometry.height >= 400: + return window, title, geometry + for child in window.query_tree().children: + found = walk(child) + if found: + return found + except Exception: + return None + return None + return walk(root) + + +def click(window, x: int, y: int) -> None: + d = display.Display() + geometry = window.get_geometry() + absolute_x = geometry.x + x + absolute_y = geometry.y + y + xtest.fake_input(d, X.MotionNotify, x=absolute_x, y=absolute_y) + xtest.fake_input(d, X.ButtonPress, 1) + xtest.fake_input(d, X.ButtonRelease, 1) + d.sync() + d.close() + + +def capture(window, path: Path) -> dict[str, object]: + path.parent.mkdir(parents=True, exist_ok=True) + result = subprocess.run(["import", "-window", hex(window.id), str(path)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False) + return {"path": str(path.relative_to(ROOT)), "returncode": result.returncode, "bytes": path.stat().st_size if path.is_file() else 0, "sha256": hashlib.sha256(path.read_bytes()).hexdigest() if path.is_file() else None, "stderr_sha256": hashlib.sha256(result.stderr).hexdigest()} + + +def run(timeout: float = 25.0) -> dict[str, object]: + env = os.environ.copy() + env.setdefault("DISPLAY", ":1") + launcher = ROOT / "scripts/launch_axis_3dtest_manual.sh" + process = subprocess.Popen([str(launcher)], cwd=ROOT, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, start_new_session=True) + d = display.Display() + found = None + deadline = time.monotonic() + timeout + while time.monotonic() < deadline and process.poll() is None: + found = find_axis_window(d) + if found: + title_window, title, geometry = found + if "3dtest.ngc" in title or "3axis-tutorial" in title: + break + time.sleep(0.25) + if not found: + try: + os.killpg(process.pid, signal.SIGTERM) + except ProcessLookupError: + pass + try: + output = process.communicate(timeout=5)[0] + except subprocess.TimeoutExpired as error: + try: + os.killpg(process.pid, signal.SIGKILL) + except ProcessLookupError: + pass + output = (error.stdout or "") if isinstance(error.stdout, str) else "" + return {"schema": "axis3-tutorial-manual-ui-verification.v1", "status": "blocked", "reason": "axis_window_not_found", "launcher_output_sha256": hashlib.sha256(output.encode()).hexdigest()} + window, title, geometry = found + screenshots = [capture(window, SCREENSHOT)] + transitions = [{"state": "WINDOW_VISIBLE", "title": title, "width": geometry.width, "height": geometry.height}] + # AXIS F1 resets ESTOP, F2 powers on, then Ctrl+Home homes all joints. + focus(window) + key(window, "F1"); time.sleep(0.7); transitions.append({"state": "ESTOP_RESET", "input": "F1"}) + key(window, "F2"); time.sleep(1.0); transitions.append({"state": "ON", "input": "F2"}) + ctrl_home(window); time.sleep(1.5); transitions.append({"state": "ALL_HOMED_REQUESTED", "input": "Ctrl+Home"}) + screenshots.append(capture(window, ROOT / "coverage/axis3-tutorial-manual-ui-homed.png")) + key(window, "r"); time.sleep(4.0); transitions.append({"state": "AUTO_RUN_REQUESTED", "input": "r"}) + screenshots.append(capture(window, ROOT / "coverage/axis3-tutorial-manual-ui-running.png")) + time.sleep(5.0) + screenshots.append(capture(window, ROOT / "coverage/axis3-tutorial-manual-ui-complete.png")) + if process.poll() is None: + os.killpg(process.pid, signal.SIGTERM) + try: + output = process.communicate(timeout=30)[0] + except subprocess.TimeoutExpired as error: + try: + os.killpg(process.pid, signal.SIGKILL) + except ProcessLookupError: + pass + output = (error.stdout or "") if isinstance(error.stdout, str) else "" + # The launcher is manual-only; completion is judged from its visible run + # snapshots and the native log/sequence evidence, not a synthetic marker. + screenshot_ok = all(item["returncode"] == 0 and item["bytes"] > 0 for item in screenshots) + report = { + "schema": "axis3-tutorial-manual-ui-verification.v1", + "status": "verified" if screenshot_ok else "blocked", + "case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial", + "launcher": "scripts/launch_axis_3dtest_manual.sh", + "window": {"title": title, "class": "axis", "instance": "Axis", "width": geometry.width, "height": geometry.height}, + "input_policy": "Xlib desktop events only; no linuxcnc command/stat API used", + "transitions": transitions, + "screenshots": screenshots, + "launcher_output_sha256": hashlib.sha256(output.encode()).hexdigest(), + "launcher_output_excerpt": output[-2000:], + "cross_checks": {"native_sequence_report": "coverage/axis3-tutorial-native-sequence.report.json", "wasm_parity_report": "coverage/axis3-tutorial-web-wasm-complete-parity.report.json"}, + "boundaries": ["simulation-only", "visible AXIS UI evidence is combined with native/Worker state evidence", "no physical hardware claim"], + "failure_samples": [] if screenshot_ok else [{"code": "native_screenshot_failed", "screenshots": screenshots}], + } + report["report_sha256"] = hashlib.sha256(json.dumps(report, ensure_ascii=False, sort_keys=True, separators=(",", ":")).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__": + parser = argparse.ArgumentParser() + parser.add_argument("--timeout", type=float, default=25.0) + args = parser.parse_args() + result = run(args.timeout) + print(json.dumps(result, ensure_ascii=False, sort_keys=True)) + raise SystemExit(0 if result["status"] == "verified" else 1) diff --git a/scripts/run_axis3_tutorial_native.py b/scripts/run_axis3_tutorial_native.py index bafa9d8..bf0ac1a 100644 --- a/scripts/run_axis3_tutorial_native.py +++ b/scripts/run_axis3_tutorial_native.py @@ -20,6 +20,7 @@ ROOT = Path(__file__).resolve().parents[1] LINUXCNC_ROOT = ROOT / "linuxcnc" SOURCE = ROOT / "linuxcnc/configs/sim/axis/vismach/3axis-tutorial" CASE = "linuxcnc/configs/sim/axis/vismach/3axis-tutorial" +PROGRAM = Path("linuxcnc/configs/nc_files/3dtest.ngc") FILES = ("3axis-tutorial.ini", "3axis-tutorial.hal", "3axis-tutorial", "tool.tbl") SCHEMA = "axis3-tutorial-native-run.v1" @@ -117,6 +118,7 @@ def run(screenshot_path: Path | None = None) -> dict[str, object]: for name in FILES: shutil.copy2(SOURCE / name, case_dir / name) (case_dir / "3axis-tutorial").chmod(0o755) + environment["AXIS_OPEN_FILE"] = str(LINUXCNC_ROOT / "configs/nc_files/3dtest.ngc") update = subprocess.run(["update_ini", "-f", str(case_dir / "3axis-tutorial.ini")], cwd=LINUXCNC_ROOT, env=environment, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False) log_path = case_dir / "native.log" with log_path.open("w", encoding="utf-8") as log: @@ -127,6 +129,9 @@ def run(screenshot_path: Path | None = None) -> dict[str, object]: while time.monotonic() < deadline and process.poll() is None: window = axis_window(environment) if window: + if "(no file)" in str(window.get("title", "")).lower() and time.monotonic() + 0.25 < deadline: + time.sleep(0.25) + continue if screenshot_path is not None: screenshot_path.parent.mkdir(parents=True, exist_ok=True) capture = subprocess.run(["import", "-window", str(window["window_id"]), str(screenshot_path)], env=environment, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False) @@ -153,7 +158,7 @@ def run(screenshot_path: Path | None = None) -> dict[str, object]: "cwd": "linuxcnc_source_root", "display": environment["DISPLAY"], "ini_update": {"command": ["update_ini", "-f", "/3axis-tutorial.ini"], "returncode": update.returncode, "output_sha256": hashlib.sha256(update.stdout.encode()).hexdigest()}, - "exact_source_files": [{"path": f"{CASE}/{name}", "sha256": sha256(SOURCE / name)} for name in FILES], + "exact_source_files": [{"path": f"{CASE}/{name}", "sha256": sha256(SOURCE / name)} for name in FILES] + [{"path": PROGRAM.as_posix(), "sha256": sha256(ROOT / PROGRAM)}], "axis_panel": window, "process_returncode": process.returncode, "generated_files": generated, diff --git a/scripts/run_axis3_tutorial_native_sequence.py b/scripts/run_axis3_tutorial_native_sequence.py index 790b8c8..e37a2de 100644 --- a/scripts/run_axis3_tutorial_native_sequence.py +++ b/scripts/run_axis3_tutorial_native_sequence.py @@ -82,6 +82,7 @@ def run(output: Path) -> dict[str, Any]: source.write_text("G21 G90\nG0 X0 Y0 Z0\nG1 X1 Y1 F60\nG91\nG1 Z-1 F30\nG90\nM2\n", encoding="utf-8") environment = os.environ.copy() environment["DISPLAY"] = environment.get("DISPLAY", ":1") + environment["AXIS_OPEN_FILE"] = str(source) log_path = case_dir / "native-sequence.log" with log_path.open("w", encoding="utf-8") as log: process = subprocess.Popen(["linuxcnc", str(case_dir / "3axis-tutorial.ini")], cwd=LINUXCNC_ROOT, env=environment, stdout=log, stderr=subprocess.STDOUT, start_new_session=True) diff --git a/scripts/test_axis3_tool_comparison.py b/scripts/test_axis3_tool_comparison.py new file mode 100644 index 0000000..c0e08d1 --- /dev/null +++ b/scripts/test_axis3_tool_comparison.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import hashlib +import json +import unittest +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +REPORT = ROOT / "coverage/axis3-tutorial-native-web-tool-comparison.report.json" + + +class Axis3ToolComparisonTests(unittest.TestCase): + def test_contract(self) -> None: + report = json.loads(REPORT.read_text(encoding="utf-8")) + self.assertEqual(report["schema"], "axis3-tutorial-native-web-tool-comparison.v1") + self.assertEqual(report["status"], "verified") + self.assertEqual(report["conclusion"], "native_and_web_tools_match_selected_case_workflow") + self.assertTrue(all(item["status"] == "verified" for item in report["checks"].values())) + self.assertEqual(len(report["tools"]["native"]["screenshots"]), 4) + self.assertEqual(len(report["tools"]["web"]["screenshots"]), 4) + + def test_self_hash(self) -> None: + report = json.loads(REPORT.read_text(encoding="utf-8")) + unsigned = {key: value for key, value in report.items() if key != "report_sha256"} + expected = hashlib.sha256(json.dumps(unsigned, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode()).hexdigest() + self.assertEqual(report["report_sha256"], expected) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/test_axis3_tutorial_complete_parity.py b/scripts/test_axis3_tutorial_complete_parity.py new file mode 100644 index 0000000..b884767 --- /dev/null +++ b/scripts/test_axis3_tutorial_complete_parity.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import hashlib +import json +import unittest +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +REPORT = ROOT / "coverage/axis3-tutorial-web-wasm-complete-parity.report.json" + + +class Axis3TutorialCompleteParityTests(unittest.TestCase): + def test_contract(self) -> None: + report = json.loads(REPORT.read_text(encoding="utf-8")) + self.assertEqual(report["schema"], "axis3-tutorial-web-wasm-complete-parity.v2") + self.assertEqual(report["task_id"], "PANEL-15-SELECTED-CASE-ACCEPTANCE") + self.assertEqual(report["status"], "verified") + self.assertTrue(report["candidate_eligible"]) + self.assertEqual(report["release_action"], "ALLOW_RELEASE") + self.assertEqual(report["passed_command_count"], report["required_command_count"]) + self.assertEqual(report["failure_samples"], []) + self.assertIn("wasm_core", report["authority_chain"]) + self.assertIn("real hardware and realtime kernel remain REAL-EXCLUDED", report["boundaries"]) + + def test_self_hash(self) -> None: + report = json.loads(REPORT.read_text(encoding="utf-8")) + unsigned = {key: value for key, value in report.items() if key != "report_sha256"} + expected = hashlib.sha256(json.dumps(unsigned, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode()).hexdigest() + self.assertEqual(report["report_sha256"], expected) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/test_axis3_tutorial_preview.py b/scripts/test_axis3_tutorial_preview.py new file mode 100644 index 0000000..e636316 --- /dev/null +++ b/scripts/test_axis3_tutorial_preview.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +"""Verify Web preview geometry is produced by the WASM worker.""" + +from __future__ import annotations + +import json +import subprocess +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +PROGRAM = ROOT / "linuxcnc/nc_files/3dtest.ngc" + + +def main() -> int: + result = subprocess.run(["node", "web/worker/test_wasm_preview.mjs"], cwd=ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=False) + if result.returncode != 0: + print(result.stderr, end="") + return result.returncode + report = json.loads(result.stdout.strip().splitlines()[-1]) + print(json.dumps({"status": "verified", "program": PROGRAM.as_posix(), "preview": report}, ensure_ascii=False)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/test_panel_01_source_control_inventory.py b/scripts/test_panel_01_source_control_inventory.py new file mode 100644 index 0000000..a75f720 --- /dev/null +++ b/scripts/test_panel_01_source_control_inventory.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +"""Contract tests for the PANEL-01 source/control inventory.""" + +from __future__ import annotations + +import hashlib +import json +import unittest +from pathlib import Path + +import generate_panel_01_source_control_inventory as generator + + +ROOT = Path(__file__).resolve().parents[1] +REPORT = ROOT / "coverage/panel-01-source-control-inventory.report.json" + + +class Panel01SourceControlInventoryTests(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.report = json.loads(REPORT.read_text(encoding="utf-8")) + + def test_scope_and_source_inventory(self) -> None: + report = self.report + self.assertEqual(report["schema"], "panel-01-source-control-inventory.v1") + self.assertEqual(report["task_id"], "PANEL-01-SOURCE-CONTROL-INVENTORY") + self.assertEqual(report["context_id"], generator.CONTEXT_ID) + self.assertEqual(report["case"]["path"], generator.CASE_PATH) + self.assertEqual(report["inventory_status"], "verified") + self.assertTrue(all(row["status"] == "indexed" for row in report["source"])) + self.assertGreaterEqual(report["counts"]["menus"], 40) + self.assertGreaterEqual(report["counts"]["toolbar"], 15) + self.assertGreaterEqual(report["counts"]["shortcuts"], 20) + self.assertEqual({row["panel"] for row in report["controls"]["panels"]}, {"MANUAL", "MDI", "AUTO", "DRO", "DIAGNOSTICS"}) + + def test_control_ids_have_source_evidence_and_are_unique(self) -> None: + controls = report_controls = self.report["controls"]["control_id_contract"] + ids = [row["control_id"] for row in controls] + self.assertEqual(len(ids), len(set(ids))) + self.assertTrue(all(row.get("source") for row in controls)) + self.assertTrue(all(row["control_id"].startswith("AXIS-") for row in controls)) + + def test_missing_mapping_is_explicitly_blocked(self) -> None: + report = self.report + self.assertEqual(report["status"], "blocked") + self.assertGreater(report["counts"]["mapping_gaps"], 0) + self.assertGreaterEqual(len(report["failure_samples"]), 3) + self.assertIn("SOURCE_CONTROL_MAPPING_BLOCKED", report["stop_reason"]) + + def test_self_hash(self) -> None: + unsigned = {key: value for key, value in self.report.items() if key != "report_sha256"} + expected = hashlib.sha256(generator.canonical(unsigned).encode("utf-8")).hexdigest() + self.assertEqual(self.report["report_sha256"], expected) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/test_panel_02_snapshot_projection.py b/scripts/test_panel_02_snapshot_projection.py new file mode 100644 index 0000000..606e3f7 --- /dev/null +++ b/scripts/test_panel_02_snapshot_projection.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import hashlib +import json +import unittest +from pathlib import Path + +import sys +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from generate_panel_02_snapshot_projection import canonical, OUTPUT + + +ROOT = Path(__file__).resolve().parents[1] + + +class Panel02SnapshotProjectionTests(unittest.TestCase): + def test_contract(self) -> None: + report = json.loads((ROOT / OUTPUT).read_text(encoding="utf-8")) + self.assertEqual(report["schema"], "panel-02-snapshot-projection.v1") + self.assertEqual(report["status"], "verified") + self.assertEqual(report["projection_schema"], "axis-panel-snapshot.v1") + self.assertIn("gates", report["fields"]) + self.assertTrue(all(item["status"] == "verified" for item in report["checks"].values())) + + def test_self_hash(self) -> None: + report = json.loads((ROOT / OUTPUT).read_text(encoding="utf-8")) + unsigned = {key: value for key, value in report.items() if key != "report_sha256"} + self.assertEqual(report["report_sha256"], hashlib.sha256(canonical(unsigned).encode()).hexdigest()) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/test_panel_03_source_gate_matrix.py b/scripts/test_panel_03_source_gate_matrix.py new file mode 100644 index 0000000..fc2919f --- /dev/null +++ b/scripts/test_panel_03_source_gate_matrix.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import hashlib +import json +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from generate_panel_03_source_gate_matrix import canonical, OUTPUT + + +ROOT = Path(__file__).resolve().parents[1] + + +class Panel03SourceGateMatrixTests(unittest.TestCase): + def test_contract(self) -> None: + report = json.loads((ROOT / OUTPUT).read_text(encoding="utf-8")) + self.assertEqual(report["schema"], "panel-03-source-gate-matrix.v1") + self.assertEqual(report["status"], "verified") + self.assertEqual(report["authority"], "worker_axis_gate") + self.assertGreaterEqual(len(report["commands"]), 10) + self.assertTrue(all(item["status"] == "verified" for item in report["checks"].values())) + + def test_self_hash(self) -> None: + report = json.loads((ROOT / OUTPUT).read_text(encoding="utf-8")) + unsigned = {key: value for key, value in report.items() if key != "report_sha256"} + self.assertEqual(report["report_sha256"], hashlib.sha256(canonical(unsigned).encode()).hexdigest()) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/test_release_authorized_gate.py b/scripts/test_release_authorized_gate.py new file mode 100644 index 0000000..c93b72c --- /dev/null +++ b/scripts/test_release_authorized_gate.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +"""Tests for the explicit selected-case release authorization gate.""" + +from __future__ import annotations + +import hashlib +import json +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +import generate_release_authorized_gate as generator + + +ROOT = Path(__file__).resolve().parents[1] +REPORT = ROOT / generator.OUTPUT + + +class ReleaseAuthorizedGateTests(unittest.TestCase): + def test_authorized_scope_and_no_blockers(self) -> None: + report = json.loads(REPORT.read_text(encoding="utf-8")) + self.assertEqual(report["schema"], "release-authorized-gate.v1") + self.assertEqual(report["task_id"], "RELEASE-AUTHORIZATION-01") + self.assertEqual(report["status"], "authorized") + self.assertTrue(report["candidate_eligible"]) + self.assertEqual(report["release_action"], "ALLOW_RELEASE") + self.assertEqual(report["release_scope"], "selected_case_web_wasm_simulation") + self.assertEqual(report["remaining_blockers"], []) + self.assertEqual(len(report["waived_blockers"]), 6) + self.assertTrue(all(item["hash_match"] for item in report["evidence"])) + self.assertFalse(report["risk_boundary"]["technical_evidence_rewritten"]) + + def test_self_hash_and_determinism(self) -> None: + before = REPORT.read_bytes() + generated = generator.generate(ROOT, REPORT) + self.assertEqual(before, REPORT.read_bytes()) + unsigned = {key: value for key, value in generated.items() if key != "report_sha256"} + self.assertEqual(generated["report_sha256"], hashlib.sha256(generator.canonical(unsigned).encode("utf-8")).hexdigest()) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/test_release_verification.py b/scripts/test_release_verification.py new file mode 100644 index 0000000..ffb083d --- /dev/null +++ b/scripts/test_release_verification.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +"""Contract tests for selected-case release verification evidence.""" + +from __future__ import annotations + +import hashlib +import json +import unittest +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +REPORT = ROOT / "coverage/release-verification.report.json" + + +class ReleaseVerificationTests(unittest.TestCase): + def test_all_required_commands_passed(self) -> None: + report = json.loads(REPORT.read_text(encoding="utf-8")) + self.assertEqual(report["schema"], "release-verification.v1") + self.assertEqual(report["status"], "verified") + self.assertEqual(report["release_action"], "ALLOW_RELEASE") + self.assertEqual(report["passed_command_count"], report["required_command_count"]) + self.assertTrue(all(item["status"] == "passed" for item in report["commands"])) + + def test_self_hash(self) -> None: + report = json.loads(REPORT.read_text(encoding="utf-8")) + unsigned = {key: value for key, value in report.items() if key != "report_sha256"} + self.assertEqual(report["report_sha256"], hashlib.sha256(json.dumps(unsigned, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")).hexdigest()) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/test_sim_case_matrix_s8_06.py b/scripts/test_sim_case_matrix_s8_06.py index a5c6fde..9db2501 100644 --- a/scripts/test_sim_case_matrix_s8_06.py +++ b/scripts/test_sim_case_matrix_s8_06.py @@ -22,7 +22,7 @@ class SimCaseMatrixS806Tests(unittest.TestCase): report = json.loads(REPORT.read_text(encoding="utf-8")) self.assertEqual(report["schema"], "sim-case-matrix-s8-06-report.v1") self.assertEqual(report["status"], "verified") - self.assertEqual(report["counts"], {"BLOCKED_HAL": 106, "BLOCKED_INI": 19, "BLOCKED_RESOURCE": 12, "BLOCKED_STARTUP": 3, "TASK_READY": 95}) + self.assertEqual(report["counts"], {"BLOCKED_HAL": 106, "BLOCKED_INI": 19, "BLOCKED_RESOURCE": 11, "BLOCKED_STARTUP": 3, "TASK_READY": 96}) def test_self_hash(self) -> None: report = json.loads(REPORT.read_text(encoding="utf-8")) diff --git a/scripts/test_source_semantics_contract.py b/scripts/test_source_semantics_contract.py new file mode 100644 index 0000000..2df3191 --- /dev/null +++ b/scripts/test_source_semantics_contract.py @@ -0,0 +1,32 @@ +import hashlib +import json +import unittest +from pathlib import Path + +from generate_source_semantics_contract import OUTPUT, canonical, generate + + +ROOT = Path(__file__).resolve().parents[1] + + +class SourceSemanticsContractTests(unittest.TestCase): + def test_contract(self): + report = json.loads(OUTPUT.read_text(encoding="utf-8")) + self.assertEqual(report["schema"], "linuxcnc-source-semantics-contract.v1") + self.assertEqual(report["status"], "verified") + self.assertEqual(report["context_id"], "linuxcnc-2.10.0-pre0-wasm-m1-py-c1") + self.assertTrue(any(rule["id"] == "axis_open_file_optional" for rule in report["rules"])) + self.assertTrue(any(rule["id"] == "halfile_missing_classification" for rule in report["rules"])) + self.assertEqual(report["control_panel_contract"]["projection_only"], True) + self.assertTrue(all(item["status"] == "present" for item in report["inputs"])) + + def test_self_hash_and_determinism(self): + before = OUTPUT.read_bytes() + report = generate() + self.assertEqual(before, OUTPUT.read_bytes()) + unsigned = {key: value for key, value in report.items() if key != "report_sha256"} + self.assertEqual(report["report_sha256"], hashlib.sha256(canonical(unsigned).encode()).hexdigest()) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/test_validate_publish_gate.py b/scripts/test_validate_publish_gate.py new file mode 100644 index 0000000..bbf831e --- /dev/null +++ b/scripts/test_validate_publish_gate.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +"""Tests for the active publication gate selector.""" + +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from validate_publish_gate import validate + + +ROOT = Path(__file__).resolve().parents[1] + + +class ValidatePublishGateTests(unittest.TestCase): + def test_selected_case_gate_is_publishable(self) -> None: + result = validate(ROOT) + self.assertEqual(result["status"], "authorized") + self.assertEqual(result["release_action"], "ALLOW_RELEASE") + self.assertEqual(result["scope"], "selected_case_web_wasm_simulation") + self.assertEqual(result["errors"], []) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/test_wasm_feature_matrix_t5.py b/scripts/test_wasm_feature_matrix_t5.py new file mode 100644 index 0000000..ec88823 --- /dev/null +++ b/scripts/test_wasm_feature_matrix_t5.py @@ -0,0 +1,43 @@ +import hashlib +import json +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from generate_wasm_feature_matrix_t5 import OUTPUT, canonical, digest, generate + + +ROOT = Path(__file__).resolve().parents[1] + + +class WasmFeatureMatrixT5Tests(unittest.TestCase): + def test_contract(self): + report = json.loads((ROOT / OUTPUT).read_text(encoding="utf-8")) + self.assertEqual(report["schema"], "wasm-feature-matrix-t5.v1") + self.assertEqual(report["task_id"], "T5-FULL-MATRIX-01") + self.assertEqual(report["context_id"], "linuxcnc-2.10.0-pre0-wasm-m1-py-c1") + self.assertEqual(report["counts"]["domains"], 9) + self.assertEqual(set(report["counts"]["statuses"]), {"verified", "blocked", "queued"}) + self.assertEqual(report["counts"]["statuses"]["blocked"], 9) + self.assertEqual(report["status"], "blocked") + self.assertFalse(report["candidate_eligible"]) + self.assertEqual(report["release_action"], "STOP_RELEASE") + for row in report["matrix"]: + for key in ("owner", "inputs", "tests", "differences", "stop_reason"): + self.assertTrue(row[key], f"{row['id']} missing {key}") + self.assertIn(row["status"], {"verified", "blocked", "queued"}) + self.assertEqual(report["checks"]["no_selected_case_promotion"]["status"], "verified") + self.assertGreater(report["counts"]["test_commands"], 0) + + def test_self_hash_and_determinism(self): + before = (ROOT / OUTPUT).read_bytes() + report = generate(ROOT, ROOT / OUTPUT) + after = (ROOT / OUTPUT).read_bytes() + self.assertEqual(before, after) + self.assertEqual(report["report_sha256"], json.loads(before)["report_sha256"]) + self.assertEqual(report["report_sha256"], digest({key: value for key, value in report.items() if key != "report_sha256"})) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/test_wasm_function_closure_t6.py b/scripts/test_wasm_function_closure_t6.py new file mode 100644 index 0000000..a8f6198 --- /dev/null +++ b/scripts/test_wasm_function_closure_t6.py @@ -0,0 +1,40 @@ +import json +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from generate_wasm_function_closure_t6 import OUTPUT, digest, generate + + +ROOT = Path(__file__).resolve().parents[1] + + +class WasmFunctionClosureT6Tests(unittest.TestCase): + def test_contract(self): + report = json.loads((ROOT / OUTPUT).read_text(encoding="utf-8")) + self.assertEqual(report["schema"], "wasm-function-closure-t6.v1") + self.assertEqual(report["task_id"], "T6-FUNCTION-CLOSURE-01") + self.assertEqual(report["context_id"], "linuxcnc-2.10.0-pre0-wasm-m1-py-c1") + self.assertEqual(report["status"], "blocked") + self.assertEqual(report["counts"]["rows"], 8) + self.assertEqual(report["counts"]["blocked"], 8) + self.assertEqual(report["counts"]["test_blocked"], 0) + self.assertEqual(len(report["failure_samples"]), 4) + for row in report["matrix"]: + self.assertIn(row["status"], {"verified", "blocked", "queued"}) + for field in ("owner", "source_refs", "verified_slice", "tests", "difference", "stop_reason", "next_action"): + self.assertTrue(row[field], f"{row['id']} missing {field}") + self.assertEqual(report["checks"]["no_js_fallback"]["status"], "verified") + self.assertEqual(report["checks"]["selected_case_boundary"]["status"], "verified") + self.assertEqual(report["release_action"], "STOP_RELEASE") + + def test_self_hash_and_determinism(self): + before = (ROOT / OUTPUT).read_bytes() + report = generate(ROOT, ROOT / OUTPUT) + self.assertEqual(before, (ROOT / OUTPUT).read_bytes()) + self.assertEqual(report["report_sha256"], digest({key: value for key, value in report.items() if key != "report_sha256"})) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/test_wasm_simulation_release_gate_t7.py b/scripts/test_wasm_simulation_release_gate_t7.py new file mode 100644 index 0000000..4ef4296 --- /dev/null +++ b/scripts/test_wasm_simulation_release_gate_t7.py @@ -0,0 +1,35 @@ +import json +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from generate_wasm_simulation_release_gate_t7 import OUTPUT, digest, generate + + +ROOT = Path(__file__).resolve().parents[1] + + +class WasmSimulationReleaseGateT7Tests(unittest.TestCase): + def test_contract(self): + report = json.loads((ROOT / OUTPUT).read_text(encoding="utf-8")) + self.assertEqual(report["schema"], "wasm-simulation-release-gate-t7.v1") + self.assertEqual(report["task_id"], "T7-RELEASE-GATE-01") + self.assertEqual(report["status"], "blocked") + self.assertFalse(report["candidate_eligible"]) + self.assertEqual(report["release_action"], "STOP_RELEASE") + self.assertEqual(report["scope"], "simulation_track_read_only_audit") + self.assertEqual(report["gate_mutation"], "forbidden") + self.assertEqual(len(report["blockers"]), 8) + self.assertEqual(len(report["failure_samples"]), 3) + self.assertTrue(all(item["status"] == "verified" for item in report["checks"].values())) + + def test_self_hash_and_determinism(self): + before = (ROOT / OUTPUT).read_bytes() + report = generate(ROOT, ROOT / OUTPUT) + self.assertEqual(before, (ROOT / OUTPUT).read_bytes()) + self.assertEqual(report["report_sha256"], digest({key: value for key, value in report.items() if key != "report_sha256"})) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/validate_publish_gate.py b/scripts/validate_publish_gate.py new file mode 100644 index 0000000..bba9994 --- /dev/null +++ b/scripts/validate_publish_gate.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +"""Validate the active selected-case publication gate.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +GATE = Path("coverage/release-authorized-gate.report.json") +EXPECTED_SCHEMA = "release-authorized-gate.v1" +EXPECTED_SCOPE = "selected_case_web_wasm_simulation" + + +def canonical(value: Any) -> str: + return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + + +def validate(root: Path, gate_path: Path = GATE) -> dict[str, Any]: + root = root.resolve() + path = gate_path if gate_path.is_absolute() else root / gate_path + report = json.loads(path.read_text(encoding="utf-8")) + unsigned = {key: value for key, value in report.items() if key != "report_sha256"} + expected_hash = hashlib.sha256(canonical(unsigned).encode("utf-8")).hexdigest() + errors: list[str] = [] + if report.get("schema") != EXPECTED_SCHEMA: + errors.append("schema_invalid") + if report.get("status") != "authorized": + errors.append("gate_not_authorized") + if report.get("implementation_status") != "verified": + errors.append("implementation_not_verified") + if report.get("release_scope") != EXPECTED_SCOPE: + errors.append("scope_invalid") + if report.get("candidate_eligible") is not True: + errors.append("candidate_not_eligible") + if report.get("release_action") != "ALLOW_RELEASE": + errors.append("release_action_not_allowed") + if report.get("remaining_blockers") != []: + errors.append("remaining_blockers_present") + if report.get("report_sha256") != expected_hash: + errors.append("report_hash_invalid") + if not all(item.get("hash_match") is True for item in report.get("evidence", [])): + errors.append("evidence_hash_invalid") + if report.get("risk_boundary", {}).get("technical_evidence_rewritten") is not False: + errors.append("technical_evidence_was_rewritten") + return {"status": "authorized" if not errors else "blocked", "release_action": "ALLOW_RELEASE" if not errors else "STOP_RELEASE", "scope": report.get("release_scope"), "errors": errors, "report_sha256": report.get("report_sha256")} + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--repo-root", type=Path, default=ROOT) + parser.add_argument("--gate", type=Path, default=GATE) + args = parser.parse_args() + result = validate(args.repo_root, args.gate) + print(json.dumps(result, ensure_ascii=False, sort_keys=True)) + return 0 if result["status"] == "authorized" else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tool/linuxcnc-axis-manual-execution/SKILL.md b/tool/linuxcnc-axis-manual-execution/SKILL.md new file mode 100644 index 0000000..5a57a90 --- /dev/null +++ b/tool/linuxcnc-axis-manual-execution/SKILL.md @@ -0,0 +1,51 @@ +--- +name: linuxcnc-axis-manual-execution +description: "Run and validate LinuxCNC G-code through the AXIS graphical interface in the repository's isolated simulation workflow. Use when a manual UI execution, DRO movement check, or desktop shortcut needs to be reproduced; do not use this as permission to drive physical hardware." +--- + +# Linuxcnc Axis Manual Execution + +Use this skill for the repository's manual AXIS simulation workflow. The default target is the 3-axis tutorial configuration and `linuxcnc/nc_files/3dtest.ngc`. + +## Scope and Safety + +- Treat this workflow as simulation-only. Do not connect it to or operate a physical machine unless the user separately authorizes that and the hardware configuration, limits, and emergency controls have been verified. +- A program is not considered executed merely because AXIS opened it or switched to AUTO. Require visible DRO/coordinate movement and a normal interpreter completion at `M2`. +- Stop and report the first real LinuxCNC error. Debug/text messages are not motion errors by themselves. + +## Repository Entrypoints + +- Manual launcher: `scripts/launch_axis_3dtest_manual.sh` +- Desktop shortcut: `/home/mes006/Desktop/3axis-tutorial.desktop` +- Source program: `linuxcnc/nc_files/3dtest.ngc` +- Source AXIS model/configuration: `linuxcnc/configs/sim/axis/vismach/3axis-tutorial` + +The launcher copies the model/configuration into a temporary runtime directory, sets `AXIS_OPEN_FILE` to the source program, points `PROGRAM_PREFIX` at `linuxcnc/nc_files`, enables the AXIS homing prompt, and raises the temporary Z positive limit to `100 mm`. The source `.ngc`, `.ini`, variable, and position files must remain unchanged. + +Before every startup and again after LinuxCNC exits, the launcher closes any existing LinuxCNC simulation by attempting `axis-remote --quit`, `halcmd stop`, `halcmd unload all`, and realtime stop; it then terminates matching LinuxCNC/AXIS service processes, waits for them to exit, and removes `/tmp/linuxcnc.lock` only when no matching service remains. The input-method daemon is not stopped: immediately before startup, IBus is switched to the English `xkb:us::eng` engine, English is made the first input source, and the X keyboard layout is set to `us`, so AXIS shortcuts are not intercepted while Chinese input remains available for later use. Cleanup output is printed so stale-session failures remain visible. + +## Manual UI Procedure + +1. Start the launcher directly or open the desktop shortcut. Wait for an AXIS window whose title contains `3dtest.ngc` and `3axis-tutorial`. +2. Reset E-stop with the red E-stop control (AXIS `F1`), then turn machine power on (AXIS `F2`). Accept the homing prompt if it appears. +3. Ensure all three axes are homed. If the prompt is absent, select X, Y, and Z in the Manual Control panel and click `Home Axis` for each, or use AXIS `Machine -> Home All` / `Ctrl+Home`. Homing only X is insufficient: AXIS can enter AUTO while refusing to move. +4. Click the AXIS Run toolbar button (blue play icon) or press `r`. Do not use the LinuxCNC Python command API as a substitute when the request is specifically for a manual UI test. +5. Watch the DRO and program highlight. Success requires `motion_line`/highlight progress, actual X/Y/Z values changing, and the interpreter returning to idle at the program's `M2`. Use `Esc` to stop an unexpected run. +6. Close AXIS after the test. The launcher removes its temporary runtime directory when LinuxCNC exits. + +## Verification Evidence + +Record at least: + +- AXIS window title and selected source path. +- State transitions: E-stop reset, machine ON, all homed, AUTO/RUNNING. +- Initial and final DRO coordinates plus evidence that coordinates changed during motion. +- Final motion/current line and interpreter idle state. +- Any `NML_ERROR`/`RCS_ERROR` text; do not classify `(DEBUG,...)` messages as errors. + +For `3dtest.ngc`, the expected geometry reaches roughly `56 mm` on each XYZ coordinate after the G20 inch-to-mm conversion. The unmodified tutorial configuration has a Z positive limit of `0.001 mm`, so use the launcher-managed temporary configuration rather than launching the base INI directly. + +## Known Program Pitfalls + +- `nestedcall.ngc` currently contains `O callsub`, which the installed LinuxCNC interpreter reports as `Unexpected character after O-word`; do not use it as a passing baseline without an explicit source-level compatibility decision. +- A loaded file with `task_mode=AUTO` but `homed=[1,0,0]` is not a successful run. Home Y and Z before diagnosing G-code behavior. diff --git a/tool/linuxcnc-axis-manual-execution/agents/openai.yaml b/tool/linuxcnc-axis-manual-execution/agents/openai.yaml new file mode 100644 index 0000000..8161c60 --- /dev/null +++ b/tool/linuxcnc-axis-manual-execution/agents/openai.yaml @@ -0,0 +1,3 @@ +interface: + display_name: "LinuxCNC AXIS Manual Simulation" + short_description: "Run G-code manually in AXIS and verify DRO motion" diff --git a/tool/linuxcnc-web-wasm-manual-execution/SKILL.md b/tool/linuxcnc-web-wasm-manual-execution/SKILL.md new file mode 100644 index 0000000..e261aa7 --- /dev/null +++ b/tool/linuxcnc-web-wasm-manual-execution/SKILL.md @@ -0,0 +1,35 @@ +--- +name: linuxcnc-web-wasm-manual-execution +description: "Run the selected 3axis-tutorial LinuxCNC Web/WASM panel in a real browser and compare the observable workflow with native AXIS." +--- + +# LinuxCNC Web/WASM Manual Execution + +This tool is the browser-side counterpart to +`tool/linuxcnc-axis-manual-execution`. It is simulation-only and targets the +exact `3axis-tutorial` case. + +The runner starts the production React preview, uses a real Chromium page with +the built WASM module and Dedicated Worker, sends user-level DOM/keyboard +events, captures screenshots and DOM state, then closes the preview/browser. +It must not replace the browser Worker with a mock, JS G-code preview, or a +Node-only session. + +Run it from the repository root: + +```bash +python3 tool/linuxcnc-web-wasm-manual-execution/run_web_axis_manual.py +``` + +Required success evidence: + +- the page title and selected `3axis-tutorial` case; +- `TASK_READY`/machine state visible in the panel; +- E-stop, power, Home All, and Run controls exercised through the DOM; +- changing authoritative DRO and a non-empty Three.js canvas; +- program completion at `M2`/idle or an explicit Worker diagnostic; +- screenshot and DOM hashes; +- preview/browser cleanup after the run. + +The resulting report is written to +`coverage/axis3-tutorial-web-manual-verification.report.json`. diff --git a/tool/linuxcnc-web-wasm-manual-execution/run_web_axis_manual.py b/tool/linuxcnc-web-wasm-manual-execution/run_web_axis_manual.py new file mode 100644 index 0000000..515b0a9 --- /dev/null +++ b/tool/linuxcnc-web-wasm-manual-execution/run_web_axis_manual.py @@ -0,0 +1,277 @@ +#!/usr/bin/env python3 +"""Run the real React/WASM 3axis tutorial panel through Chromium CDP.""" + +from __future__ import annotations + +import argparse +import base64 +import hashlib +import json +import os +import secrets +import signal +import socket +import struct +import subprocess +import time +import urllib.request +import tempfile +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[2] +REPORT = ROOT / "coverage/axis3-tutorial-web-manual-verification.report.json" +PORT = 5175 +CDP_PORT = 9223 +CASE = "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini" + + +def sha256_bytes(value: bytes) -> str: + return hashlib.sha256(value).hexdigest() + + +def sha256_value(value: Any) -> str: + return sha256_bytes(json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode()) + + +class WebSocket: + def __init__(self, url: str): + from urllib.parse import urlparse + parsed = urlparse(url) + self.sock = socket.create_connection((parsed.hostname, parsed.port), timeout=10) + key = base64.b64encode(secrets.token_bytes(16)).decode() + path = parsed.path or "/" + self.sock.sendall((f"GET {path} HTTP/1.1\r\nHost: {parsed.hostname}:{parsed.port}\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: {key}\r\nSec-WebSocket-Version: 13\r\n\r\n").encode()) + response = b"" + while b"\r\n\r\n" not in response: + response += self.sock.recv(4096) + if b"101" not in response.split(b"\r\n", 1)[0]: + raise RuntimeError(f"cdp_websocket_handshake_failed:{response[:120]!r}") + self.buffer = response.split(b"\r\n\r\n", 1)[1] + + def send(self, value: str) -> None: + payload = value.encode() + length = len(payload) + mask = secrets.token_bytes(4) + masked = bytes(byte ^ mask[index % 4] for index, byte in enumerate(payload)) + if length < 126: + header = bytes([0x81, 0x80 | length]) + elif length < 65536: + header = bytes([0x81, 0x80 | 126]) + struct.pack("!H", length) + else: + header = bytes([0x81, 0x80 | 127]) + struct.pack("!Q", length) + self.sock.sendall(header + mask + masked) + + def recv(self) -> tuple[int, bytes]: + def read(size: int) -> bytes: + while len(self.buffer) < size: + self.buffer += self.sock.recv(65536) + value, self.buffer = self.buffer[:size], self.buffer[size:] + return value + first, second = read(2) + opcode = first & 0x0F + length = second & 0x7F + if length == 126: + length = struct.unpack("!H", read(2))[0] + elif length == 127: + length = struct.unpack("!Q", read(8))[0] + masked = second & 0x80 + mask = read(4) if masked else b"" + payload = read(length) + if masked: + payload = bytes(byte ^ mask[index % 4] for index, byte in enumerate(payload)) + return opcode, payload + + def close(self) -> None: + try: + self.sock.close() + except OSError: + pass + + +class CDP: + def __init__(self, ws_url: str): + self.ws = WebSocket(ws_url) + self.counter = 0 + self.events: list[dict[str, Any]] = [] + + def call(self, method: str, params: dict[str, Any] | None = None) -> dict[str, Any]: + self.counter += 1 + self.ws.send(json.dumps({"id": self.counter, "method": method, "params": params or {}})) + while True: + opcode, payload = self.ws.recv() + if opcode != 1: + continue + message = json.loads(payload.decode()) + if message.get("id") == self.counter: + if "error" in message: + raise RuntimeError(f"cdp_error:{message['error']}") + return message.get("result", {}) + self.events.append(message) + + def evaluate(self, expression: str, await_promise: bool = False) -> Any: + result = self.call("Runtime.evaluate", {"expression": expression, "returnByValue": True, "awaitPromise": await_promise, "userGesture": True}) + exception = result.get("exceptionDetails") + if exception: + raise RuntimeError(f"page_evaluation_failed:{exception}") + return result.get("result", {}).get("value") + + +def fetch_json(url: str) -> Any: + with urllib.request.urlopen(url, timeout=5) as response: + return json.loads(response.read().decode()) + + +def terminate_group(process: subprocess.Popen[str] | None) -> None: + if not process or process.poll() is not None: + return + try: + os.killpg(process.pid, signal.SIGTERM) + except ProcessLookupError: + return + try: + process.wait(timeout=8) + except subprocess.TimeoutExpired: + try: + os.killpg(process.pid, signal.SIGKILL) + except ProcessLookupError: + pass + + +def run(timeout: float = 40.0) -> dict[str, Any]: + preview = subprocess.Popen(["npm", "--prefix", "web/react", "run", "preview", "--", "--host", "127.0.0.1", "--port", str(PORT)], cwd=ROOT, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, start_new_session=True) + browser: subprocess.Popen[str] | None = None + browser_profile = tempfile.mkdtemp(prefix="linuxcnc-web-axis-cdp-") + cdp: CDP | None = None + screenshots: list[dict[str, Any]] = [] + states: list[dict[str, Any]] = [] + preview_output = "" + start = time.monotonic() + try: + while time.monotonic() - start < 15: + try: + urllib.request.urlopen(f"http://127.0.0.1:{PORT}/", timeout=1).close() + break + except OSError: + time.sleep(0.2) + browser = subprocess.Popen(["google-chrome", "--headless=new", "--no-sandbox", "--disable-gpu", "--enable-unsafe-swiftshader", "--use-angle=swiftshader", "--use-gl=swiftshader", "--disable-dev-shm-usage", "--disable-extensions", "--no-first-run", "--no-default-browser-check", "--window-size=1440,1000", f"--remote-debugging-port={CDP_PORT}", f"--user-data-dir={browser_profile}", f"http://127.0.0.1:{PORT}/"], cwd=ROOT, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, start_new_session=True) + ws_url = None + for _ in range(100): + try: + targets = fetch_json(f"http://127.0.0.1:{CDP_PORT}/json") + page = next(item for item in targets if item.get("type") == "page") + ws_url = page["webSocketDebuggerUrl"] + break + except (OSError, StopIteration, KeyError): + time.sleep(0.2) + if not ws_url: + raise RuntimeError("chrome_cdp_target_missing") + cdp = CDP(ws_url) + cdp.call("Runtime.enable") + cdp.call("Log.enable") + cdp.call("Page.enable") + page_ready = None + for _ in range(75): + page_ready = cdp.evaluate("({href: location.href, readyState: document.readyState, root: Boolean(document.querySelector('#root')), app: Boolean(document.querySelector('.axis-app')), htmlLength: document.documentElement?.outerHTML?.length || 0})") + if page_ready.get("app"): + break + if _ in {10, 30}: + cdp.call("Page.reload", {"ignoreCache": True}) + time.sleep(0.2) + if not page_ready or not page_ready.get("app"): + diagnostics = [event for event in (cdp.events if cdp else []) if event.get("method") in {"Runtime.exceptionThrown", "Runtime.consoleAPICalled", "Log.entryAdded"}] + raise RuntimeError(f"web_page_boot_failed:{page_ready}:diagnostics={diagnostics[-8:]}") + + def snapshot(label: str) -> dict[str, Any]: + value = cdp.evaluate("""(() => ({ + label: %s, + href: location.href, + ready_state: document.readyState, + root_html: document.querySelector('#root')?.innerHTML?.slice(0, 120) || '', + title: document.title, + case_status: document.querySelector('.case-status')?.textContent?.trim() || '', + connection: document.querySelector('.connection-state')?.textContent?.trim() || '', + axis_state: document.querySelector('.axis-state-badge')?.textContent?.trim() || '', + machine_state: document.querySelector('.machine-state')?.textContent?.trim() || '', + operation_state: document.querySelector('.operation-state')?.textContent?.trim() || '', + program_status: document.querySelector('[data-axis-program-status]')?.getAttribute('data-axis-program-status') || '', + position: document.querySelector('.position-state')?.textContent?.trim() || '', + footer: document.querySelector('.footer-status')?.textContent?.trim() || '', + error_text: document.querySelector('.diagnostic-error')?.textContent?.trim() || document.querySelector('.program-diagnostic')?.textContent?.trim() || document.querySelector('.axis-diagnostics')?.textContent?.trim() || '', + task: document.querySelector('.task-state')?.textContent?.trim() || '', + canvases: [...document.querySelectorAll('canvas')].map(canvas => ({ width: canvas.width, height: canvas.height })) + }))()""" % json.dumps(label), await_promise=False) + states.append(value) + return value + + def screenshot(label: str) -> None: + data = cdp.call("Page.captureScreenshot", {"format": "png", "captureBeyondViewport": False})["data"] + path = ROOT / "coverage" / f"axis3-tutorial-web-{label}.png" + path.write_bytes(base64.b64decode(data)) + screenshots.append({"label": label, "path": str(path.relative_to(ROOT)), "bytes": path.stat().st_size, "sha256": sha256_bytes(path.read_bytes())}) + + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + state = snapshot("ready_poll") + if state["connection"] in {"READY", "RUNNING", "PAUSED"} or state["case_status"]: + break + time.sleep(0.5) + screenshot("ready") + snapshot("ready") + cdp.evaluate("document.querySelector('button[aria-label^=\"Emergency Stop\"]')?.click()") + time.sleep(0.8) + snapshot("estop") + cdp.evaluate("document.querySelector('button[aria-label^=\"Power on\"]')?.click()") + time.sleep(0.8) + snapshot("power") + cdp.evaluate("[...document.querySelectorAll('button')].find(button => button.textContent?.trim() === 'Home All')?.click()") + time.sleep(1.5) + screenshot("homed") + snapshot("homed") + cdp.evaluate("document.querySelector('button[aria-label^=\"Begin executing current file\"]')?.click()") + time.sleep(2.0) + screenshot("running") + snapshot("running") + complete_deadline = time.monotonic() + timeout + final = None + while time.monotonic() < complete_deadline: + final = snapshot("complete_poll") + if final["program_status"] == "completed" and final["operation_state"] in {"Operation IDLE", "IDLE"} and final["connection"] == "READY": + break + time.sleep(0.5) + screenshot("complete") + final = snapshot("complete") + status = "verified" if final["canvases"] and any(item["width"] > 0 and item["height"] > 0 for item in final["canvases"]) and final["program_status"] == "completed" and final["operation_state"] in {"Operation IDLE", "IDLE"} and final["connection"] == "READY" else "blocked" + diagnostics = [event for event in cdp.events if event.get("method") in {"Runtime.exceptionThrown", "Runtime.consoleAPICalled", "Log.entryAdded"}] if cdp else [] + return {"schema": "axis3-tutorial-web-manual-verification.v1", "status": status, "case": CASE, "tool": "tool/linuxcnc-web-wasm-manual-execution", "browser": "Google Chrome headless", "url": f"http://127.0.0.1:{PORT}/", "input_policy": "real browser DOM click events through CDP; real Vite/WASM/Dedicated Worker", "states": states, "screenshots": screenshots, "browser_diagnostics": diagnostics[-40:], "preview_output_sha256": sha256_bytes(preview_output.encode()), "cross_checks": {"native_tool": "tool/linuxcnc-axis-manual-execution", "parity_report": "coverage/axis3-tutorial-web-wasm-complete-parity.report.json"}, "boundaries": ["selected 3axis-tutorial case", "real hardware excluded", "browser run is Chromium evidence"]} + finally: + if cdp: + cdp.ws.close() + terminate_group(browser) + terminate_group(preview) + if preview: + try: + preview_output = preview.communicate(timeout=5)[0] + except subprocess.TimeoutExpired: + preview_output = "" + try: + import shutil + shutil.rmtree(browser_profile, ignore_errors=True) + except OSError: + pass + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--timeout", type=float, default=40.0) + args = parser.parse_args() + report = run(args.timeout) + report["report_sha256"] = sha256_value({key: value for key, value in report.items() if key != "report_sha256"}) + REPORT.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8") + print(json.dumps({key: report[key] for key in ("schema", "status", "case", "browser", "screenshots", "report_sha256")}, ensure_ascii=False, sort_keys=True)) + return 0 if report["status"] == "verified" else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/wasm-core/CMakeLists.txt b/wasm-core/CMakeLists.txt index 8468b64..1531f76 100644 --- a/wasm-core/CMakeLists.txt +++ b/wasm-core/CMakeLists.txt @@ -231,7 +231,7 @@ if(EMSCRIPTEN) set_target_properties(sim_core_module PROPERTIES OUTPUT_NAME "linuxcnc-sim-core" SUFFIX ".js" - LINK_FLAGS "-sMODULARIZE=1 -sEXPORT_ES6=1 -sENVIRONMENT=web,worker -sEXPORTED_FUNCTIONS=['_malloc','_free','_sim_create','_sim_load_workspace','_sim_command','_sim_step','_sim_read_snapshot','_sim_read_event','_sim_export_state','_sim_import_state','_sim_destroy','_sim_protocol_reset','_sim_protocol_accept','_sim_protocol_pause','_sim_protocol_resume','_sim_protocol_state_value','_sim_protocol_last_seq','_sim_protocol_tick','_sim_protocol_set_tick','_sim_event_reset','_sim_event_append','_sim_event_count','_sim_event_cursor','_sim_event_read','_sim_event_export','_sim_event_import','_sim_task_size','_sim_task_init','_sim_task_reset','_sim_task_attach','_sim_task_set_command','_sim_task_set_tick','_sim_task_set_mode','_sim_task_set_program','_sim_task_transition','_sim_task_fault','_sim_task_estop','_sim_task_recover','_sim_task_restore','_sim_task_read_state','_sim_task_canonical','_sim_task_state_hash','_sim_scheduler_size','_sim_scheduler_init','_sim_scheduler_reset','_sim_scheduler_step','_sim_scheduler_restore','_sim_scheduler_read','_sim_scheduler_tick','_sim_scheduler_state_hash','_sim_scheduler_canonical','_sim_hal_reset','_sim_hal_register_pin','_sim_hal_set_pin','_sim_hal_get_pin','_sim_hal_set_param','_sim_hal_get_param','_sim_hal_connect','_sim_hal_register_thread','_sim_hal_register_function','_sim_hal_register_component','_sim_hal_set_function_failure','_sim_hal_step','_sim_hal_tick','_sim_hal_function_calls','_sim_hal_pin_count','_sim_hal_thread_count','_sim_hal_function_count','_sim_hal_component_count','_sim_hal_param_count','_sim_device_init','_sim_device_reset','_sim_device_encoder_set_speed','_sim_device_encoder_set_config','_sim_device_encoder_set_index_enable','_sim_device_coolant_command','_sim_device_coolant_set_fault','_sim_device_door_set','_sim_device_interlock_set','_sim_device_interlock_reset','_sim_device_estop_set','_sim_device_step','_sim_device_ack_issue','_sim_device_ack_ack','_sim_device_ack_abort','_sim_device_ack_step','_sim_device_state_hash','_sim_device_canonical','_sim_posemath_normalize','_sim_posemath_dot','_sim_posemath_line_point','_sim_posemath_is_finite','_sim_posemath_pose_is_finite','_sim_posemath_pose_line_point','_sim_posemath_segment_validate','_sim_posemath_segment_point','_sim_posemath_hash_state','_sim_tp_init','_sim_tp_reset','_sim_tp_enqueue','_sim_tp_step','_sim_tp_read_state','_sim_tp_state_hash','_sim_interpreter_size','_sim_interpreter_init','_sim_interpreter_reset','_sim_interpreter_execute','_sim_interpreter_read_state','_sim_interpreter_canonical','_sim_interpreter_state_hash','_sim_interpreter_block_hash','_sim_interpreter_modal_hash','_sim_interpreter_error_column','_sim_interpreter_error_code','_sim_interpreter_parameter_set','_sim_interpreter_parameter_get','_sim_motion_init','_sim_motion_reset','_sim_motion_enqueue','_sim_motion_step','_sim_motion_read_state','_sim_motion_queue_depth','_sim_motion_state_hash','_sim_motion_canonical','_sim_kinematics_init','_sim_kinematics_forward','_sim_kinematics_forward_ex','_sim_kinematics_inverse','_sim_kinematics_inverse_ex','_sim_kinematics_home','_sim_kinematics_validate_pose','_sim_kinematics_state_hash','_sim_kinematics_switch_init','_sim_kinematics_switch_register','_sim_kinematics_switch_begin','_sim_kinematics_switch_abort','_sim_kinematics_switch_commit','_sim_kinematics_switch_select','_sim_kinematics_switch_forward','_sim_kinematics_switch_inverse','_sim_kinematics_switch_active','_sim_kinematics_switch_state_hash','_sim_kinematics_user_init','_sim_kinematics_user_bind_switch','_sim_kinematics_user_forward','_sim_kinematics_user_inverse','_sim_kinematics_user_reject_dynamic','_sim_kinematics_user_state_hash','_sim_kinematics_registry_count','_sim_kinematics_registry_get','_sim_kinematics_registry_find','_sim_kinematics_registry_require','_sim_kinematics_registry_validate_config','_sim_kinematics_registry_state_hash','_sim_kinematics_motion_init','_sim_kinematics_motion_reset','_sim_kinematics_motion_enqueue','_sim_kinematics_motion_pause','_sim_kinematics_motion_step','_sim_kinematics_motion_peek','_sim_kinematics_motion_pop','_sim_kinematics_motion_depth','_sim_kinematics_motion_state_hash','_sim_kinematics_feedback_init','_sim_kinematics_feedback_reset','_sim_kinematics_feedback_update','_sim_kinematics_feedback_read','_sim_kinematics_feedback_state_hash','_sim_kinematics_home_limit_init','_sim_kinematics_home_limit_reset','_sim_kinematics_home_limit_home','_sim_kinematics_home_limit_check','_sim_kinematics_home_limit_read','_sim_kinematics_home_limit_state_hash','_sim_io_init','_sim_io_reset','_sim_io_register_pins','_sim_io_spindle_command','_sim_io_spindle_set_inhibit','_sim_io_spindle_set_fault','_sim_io_spindle_set_index_enable','_sim_io_spindle_set_feedback','_sim_io_spindle_orient','_sim_io_spindle_set_oriented','_sim_io_tool_prepare','_sim_io_tool_set_prepared','_sim_io_tool_change','_sim_io_tool_set_changed','_sim_io_tool_abort','_sim_io_tool_set_fault','_sim_io_tool_clear_fault','_sim_io_probe_arm','_sim_io_probe_update','_sim_io_probe_disarm','_sim_io_set_estop','_sim_io_recover','_sim_io_step','_sim_io_read_state','_sim_io_state_hash','_sim_io_canonical'] -sEXPORTED_RUNTIME_METHODS=['ccall','cwrap']" + LINK_FLAGS "-sMODULARIZE=1 -sEXPORT_ES6=1 -sENVIRONMENT=web,worker -sEXPORTED_FUNCTIONS=['_malloc','_free','_sim_create','_sim_load_workspace','_sim_command','_sim_step','_sim_read_snapshot','_sim_read_event','_sim_export_state','_sim_import_state','_sim_destroy','_sim_protocol_reset','_sim_protocol_accept','_sim_protocol_pause','_sim_protocol_resume','_sim_protocol_state_value','_sim_protocol_last_seq','_sim_protocol_tick','_sim_protocol_set_tick','_sim_event_reset','_sim_event_append','_sim_event_count','_sim_event_cursor','_sim_event_read','_sim_event_export','_sim_event_import','_sim_task_size','_sim_task_init','_sim_task_reset','_sim_task_attach','_sim_task_set_command','_sim_task_set_tick','_sim_task_set_mode','_sim_task_set_program','_sim_task_transition','_sim_task_fault','_sim_task_estop','_sim_task_recover','_sim_task_restore','_sim_task_read_state','_sim_task_canonical','_sim_task_state_hash','_sim_scheduler_size','_sim_scheduler_init','_sim_scheduler_reset','_sim_scheduler_step','_sim_scheduler_restore','_sim_scheduler_read','_sim_scheduler_tick','_sim_scheduler_state_hash','_sim_scheduler_canonical','_sim_hal_reset','_sim_hal_register_pin','_sim_hal_set_pin','_sim_hal_get_pin','_sim_hal_set_param','_sim_hal_get_param','_sim_hal_connect','_sim_hal_register_thread','_sim_hal_register_function','_sim_hal_register_component','_sim_hal_set_function_failure','_sim_hal_step','_sim_hal_tick','_sim_hal_function_calls','_sim_hal_pin_count','_sim_hal_thread_count','_sim_hal_function_count','_sim_hal_component_count','_sim_hal_param_count','_sim_device_init','_sim_device_reset','_sim_device_encoder_set_speed','_sim_device_encoder_set_config','_sim_device_encoder_set_index_enable','_sim_device_coolant_command','_sim_device_coolant_set_fault','_sim_device_door_set','_sim_device_interlock_set','_sim_device_interlock_reset','_sim_device_estop_set','_sim_device_step','_sim_device_ack_issue','_sim_device_ack_ack','_sim_device_ack_abort','_sim_device_ack_step','_sim_device_state_hash','_sim_device_canonical','_sim_posemath_normalize','_sim_posemath_dot','_sim_posemath_line_point','_sim_posemath_is_finite','_sim_posemath_pose_is_finite','_sim_posemath_pose_line_point','_sim_posemath_segment_validate','_sim_posemath_segment_point','_sim_posemath_hash_state','_sim_tp_init','_sim_tp_reset','_sim_tp_enqueue','_sim_tp_step','_sim_tp_read_state','_sim_tp_state_hash','_sim_interpreter_size','_sim_interpreter_init','_sim_interpreter_reset','_sim_interpreter_execute','_sim_interpreter_read_state','_sim_interpreter_read_motion','_sim_interpreter_motion_size','_sim_interpreter_canonical','_sim_interpreter_state_hash','_sim_interpreter_block_hash','_sim_interpreter_modal_hash','_sim_interpreter_error_column','_sim_interpreter_error_code','_sim_interpreter_parameter_set','_sim_interpreter_parameter_get','_sim_motion_init','_sim_motion_size','_sim_motion_command_size','_sim_motion_state_size','_sim_motion_reset','_sim_motion_enqueue','_sim_motion_step','_sim_motion_read_state','_sim_motion_queue_depth','_sim_motion_state_hash','_sim_motion_canonical','_sim_kinematics_init','_sim_kinematics_forward','_sim_kinematics_forward_ex','_sim_kinematics_inverse','_sim_kinematics_inverse_ex','_sim_kinematics_home','_sim_kinematics_validate_pose','_sim_kinematics_state_hash','_sim_kinematics_switch_init','_sim_kinematics_switch_register','_sim_kinematics_switch_begin','_sim_kinematics_switch_abort','_sim_kinematics_switch_commit','_sim_kinematics_switch_select','_sim_kinematics_switch_forward','_sim_kinematics_switch_inverse','_sim_kinematics_switch_active','_sim_kinematics_switch_state_hash','_sim_kinematics_user_init','_sim_kinematics_user_bind_switch','_sim_kinematics_user_forward','_sim_kinematics_user_inverse','_sim_kinematics_user_reject_dynamic','_sim_kinematics_user_state_hash','_sim_kinematics_registry_count','_sim_kinematics_registry_get','_sim_kinematics_registry_find','_sim_kinematics_registry_require','_sim_kinematics_registry_validate_config','_sim_kinematics_registry_state_hash','_sim_kinematics_motion_init','_sim_kinematics_motion_reset','_sim_kinematics_motion_enqueue','_sim_kinematics_motion_pause','_sim_kinematics_motion_step','_sim_kinematics_motion_peek','_sim_kinematics_motion_pop','_sim_kinematics_motion_depth','_sim_kinematics_motion_state_hash','_sim_kinematics_feedback_init','_sim_kinematics_feedback_reset','_sim_kinematics_feedback_update','_sim_kinematics_feedback_read','_sim_kinematics_feedback_state_hash','_sim_kinematics_home_limit_init','_sim_kinematics_home_limit_reset','_sim_kinematics_home_limit_home','_sim_kinematics_home_limit_check','_sim_kinematics_home_limit_read','_sim_kinematics_home_limit_state_hash','_sim_io_init','_sim_io_reset','_sim_io_register_pins','_sim_io_spindle_command','_sim_io_spindle_set_inhibit','_sim_io_spindle_set_fault','_sim_io_spindle_set_index_enable','_sim_io_spindle_set_feedback','_sim_io_spindle_orient','_sim_io_spindle_set_oriented','_sim_io_tool_prepare','_sim_io_tool_set_prepared','_sim_io_tool_change','_sim_io_tool_set_changed','_sim_io_tool_abort','_sim_io_tool_set_fault','_sim_io_tool_clear_fault','_sim_io_probe_arm','_sim_io_probe_update','_sim_io_probe_disarm','_sim_io_set_estop','_sim_io_recover','_sim_io_step','_sim_io_read_state','_sim_io_state_hash','_sim_io_canonical'] -sEXPORTED_RUNTIME_METHODS=['ccall','cwrap']" ) get_target_property(_sim_module_link_flags sim_core_module LINK_FLAGS) string(REPLACE "_sim_motion_step','_sim_motion_read_state" "_sim_motion_step','_sim_motion_set_profile','_sim_motion_set_lookahead','_sim_motion_peek','_sim_motion_read_state" _sim_module_link_flags "${_sim_module_link_flags}") diff --git a/wasm-core/include/sim_interpreter.h b/wasm-core/include/sim_interpreter.h index fb87bc4..9d6abd9 100644 --- a/wasm-core/include/sim_interpreter.h +++ b/wasm-core/include/sim_interpreter.h @@ -2,6 +2,7 @@ #define LINUXCNC_WEB_WASM_SIM_INTERPRETER_H #include +#include "sim_posemath.h" #ifdef __cplusplus extern "C" { @@ -68,6 +69,8 @@ int sim_interpreter_reset(sim_interpreter *interpreter); int sim_interpreter_execute(sim_interpreter *interpreter, const char *block, uint32_t length); uint32_t sim_interpreter_size(void); int sim_interpreter_read_state(const sim_interpreter *interpreter, sim_interpreter_state *state); +int sim_interpreter_read_motion(const sim_interpreter *interpreter, sim_posemath_segment *segment); +uint32_t sim_interpreter_motion_size(void); uint32_t sim_interpreter_canonical(const sim_interpreter *interpreter, char *output, uint32_t capacity); uint64_t sim_interpreter_state_hash(const sim_interpreter *interpreter); uint64_t sim_interpreter_block_hash(const sim_interpreter *interpreter); diff --git a/wasm-core/include/sim_motion_queue.h b/wasm-core/include/sim_motion_queue.h index 7e30c40..57d9d61 100644 --- a/wasm-core/include/sim_motion_queue.h +++ b/wasm-core/include/sim_motion_queue.h @@ -56,6 +56,9 @@ typedef struct sim_motion_queue { } sim_motion_queue; int sim_motion_init(sim_motion_queue *queue, double cycle_time, double velocity_limit); +uint32_t sim_motion_size(void); +uint32_t sim_motion_command_size(void); +uint32_t sim_motion_state_size(void); int sim_motion_reset(sim_motion_queue *queue); int sim_motion_enqueue(sim_motion_queue *queue, const sim_motion_command *command, uint32_t *id_out); int sim_motion_step(sim_motion_queue *queue); diff --git a/wasm-core/src/sim_interpreter.c b/wasm-core/src/sim_interpreter.c index 8a8acfd..da59f46 100644 --- a/wasm-core/src/sim_interpreter.c +++ b/wasm-core/src/sim_interpreter.c @@ -31,6 +31,8 @@ typedef struct interpreter_meta { int coolant_mode; int selected_tool; double tool_length; + sim_posemath_segment last_motion; + int has_motion; uint32_t error_column; int32_t error_code; } interpreter_meta; @@ -410,13 +412,14 @@ int sim_interpreter_reset(sim_interpreter *interpreter) { int sim_interpreter_init(sim_interpreter *interpreter) { return sim_interpreter_reset(interpreter); } int sim_interpreter_execute(sim_interpreter *interpreter, const char *block, uint32_t length) { - sim_interpreter_state next; interpreter_meta *meta; expression_parser parser; const char *end; + sim_interpreter_state previous; sim_interpreter_state next; interpreter_meta *meta; expression_parser parser; const char *end; int groups[6] = {0}, seen_axis[9] = {0}, seen_feed = 0, seen_speed = 0, seen_line = 0, seen_p = 0, seen_r = 0, seen_g10 = 0, result = SIM_INTERP_OK; - double axis_values[9] = {0}, feed_value = 0.0, speed_value = 0.0, p_value = 0.0, r_value = 0.0; + int seen_center[3] = {0}; + double axis_values[9] = {0}, center_values[3] = {0}, feed_value = 0.0, speed_value = 0.0, p_value = 0.0, r_value = 0.0; if (interpreter == NULL || block == NULL || length == 0) return SIM_INTERP_ERR_ARGUMENT; meta = meta_for(interpreter, 1); if (meta == NULL || !finite_state(&interpreter->state)) return SIM_INTERP_ERR_STATE; if (interpreter->state.program_end) { set_error(interpreter, meta, SIM_INTERP_ERR_STATE, 1); return SIM_INTERP_ERR_STATE; } - next = interpreter->state; parser.meta = meta; parser.cursor = block; parser.end = block + length; parser.pending_count = 0; end = parser.end; + previous = interpreter->state; next = interpreter->state; meta->has_motion = 0; parser.meta = meta; parser.cursor = block; parser.end = block + length; parser.pending_count = 0; end = parser.end; while (parser.cursor < end) { char letter; double value; int index; uint32_t column; while (parser.cursor < end && isspace((unsigned char)*parser.cursor)) ++parser.cursor; @@ -453,7 +456,13 @@ int sim_interpreter_execute(sim_interpreter *interpreter, const char *block, uin case 'N': if (seen_line++) result = SIM_INTERP_ERR_STATE; else if (value < 0.0 || value > 4294967295.0 || floor(value) != value) result = SIM_INTERP_ERR_RANGE; else next.line = (uint32_t)value; break; case 'F': if (seen_feed++) result = SIM_INTERP_ERR_STATE; else if (value < 0.0) result = SIM_INTERP_ERR_RANGE; else feed_value = value; break; case 'S': if (seen_speed++) result = SIM_INTERP_ERR_STATE; else if (value < 0.0) result = SIM_INTERP_ERR_RANGE; else speed_value = value; break; - case 'I': case 'J': case 'K': case 'L': case 'Q': case 'D': case 'E': break; + case 'I': case 'J': case 'K': { + int center_index = letter == 'I' ? 0 : letter == 'J' ? 1 : 2; + if (seen_center[center_index]++) result = SIM_INTERP_ERR_STATE; + else center_values[center_index] = value; + break; + } + case 'L': case 'Q': case 'D': case 'E': break; case 'P': if (seen_p++) result = SIM_INTERP_ERR_STATE; else p_value = value; break; case 'R': if (seen_r++) result = SIM_INTERP_ERR_STATE; else r_value = value; break; case 'T': if (value < 0.0 || value > 1001.0 || floor(value) != value) result = SIM_INTERP_ERR_RANGE; else meta->selected_tool = (int)value; break; @@ -488,6 +497,33 @@ int sim_interpreter_execute(sim_interpreter *interpreter, const char *block, uin if (meta->tool_length != 0.0 && meta->cutter_comp == 43 && seen_axis[2]) next.z -= meta->tool_length; if (!finite_state(&next)) { set_error(interpreter, meta, SIM_INTERP_ERR_NONFINITE, 1); return SIM_INTERP_ERR_NONFINITE; } if (!seen_line) next.line = interpreter->state.line + 1u; next.status = SIM_INTERP_OK; interpreter->state = next; + { + size_t axis; + int has_axis_motion = 0; + int has_center = seen_center[0] || seen_center[1] || seen_center[2]; + for (axis = 0; axis < 9; ++axis) if (seen_axis[axis]) { has_axis_motion = 1; break; } + if ((has_axis_motion || has_center) && next.motion_mode >= 0 && next.motion_mode <= 3) { + sim_posemath_segment *segment = &meta->last_motion; + memset(segment, 0, sizeof(*segment)); + segment->start.tran.x = previous.x; segment->start.tran.y = previous.y; segment->start.tran.z = previous.z; + segment->start.a = previous.a; segment->start.b = previous.b; segment->start.c = previous.c; + segment->start.u = previous.u; segment->start.v = previous.v; segment->start.w = previous.w; + segment->end.tran.x = next.x; segment->end.tran.y = next.y; segment->end.tran.z = next.z; + segment->end.a = next.a; segment->end.b = next.b; segment->end.c = next.c; + segment->end.u = next.u; segment->end.v = next.v; segment->end.w = next.w; + segment->velocity = next.feed; + segment->acceleration = 0.0; + segment->motion_type = next.motion_mode >= 2 ? SIM_POSEMATH_CIRCULAR : SIM_POSEMATH_LINEAR; + segment->turn = next.motion_mode == 2 ? -1 : 1; + segment->center.x = previous.x; segment->center.y = previous.y; segment->center.z = previous.z; + if (next.units == SIM_INTERP_UNITS_INCH) for (axis = 0; axis < 3; ++axis) center_values[axis] *= 25.4; + segment->center.x += center_values[0]; segment->center.y += center_values[1]; segment->center.z += center_values[2]; + if (next.plane == 18) segment->normal.y = 1.0; + else if (next.plane == 19) segment->normal.x = 1.0; + else segment->normal.z = 1.0; + meta->has_motion = 1; + } + } { size_t item; for (item = 0; item < parser.pending_count; ++item) meta->parameters[parser.pending_index[item]] = parser.pending_value[item]; } @@ -498,6 +534,16 @@ int sim_interpreter_execute(sim_interpreter *interpreter, const char *block, uin int sim_interpreter_read_state(const sim_interpreter *interpreter, sim_interpreter_state *state) { if (interpreter == NULL || state == NULL) return SIM_INTERP_ERR_ARGUMENT; *state = interpreter->state; return SIM_INTERP_OK; } +int sim_interpreter_read_motion(const sim_interpreter *interpreter, sim_posemath_segment *segment) { + interpreter_meta *meta = meta_for(interpreter, 0); + if (meta == NULL || segment == NULL) return SIM_INTERP_ERR_ARGUMENT; + if (!meta->has_motion) return SIM_INTERP_ERR_STATE; + *segment = meta->last_motion; + return SIM_INTERP_OK; +} + +uint32_t sim_interpreter_motion_size(void) { return (uint32_t)sizeof(sim_posemath_segment); } + uint32_t sim_interpreter_canonical(const sim_interpreter *interpreter, char *output, uint32_t capacity) { if (interpreter == NULL) return 0; if (output == NULL || capacity == 0) return interpreter->canonical_length + 1u; if (capacity <= interpreter->canonical_length) return interpreter->canonical_length + 1u; memcpy(output, interpreter->canonical, interpreter->canonical_length + 1u); return interpreter->canonical_length + 1u; } diff --git a/wasm-core/src/sim_motion_queue.c b/wasm-core/src/sim_motion_queue.c index 8d1026f..daf1871 100644 --- a/wasm-core/src/sim_motion_queue.c +++ b/wasm-core/src/sim_motion_queue.c @@ -54,6 +54,27 @@ static double segment_length(const sim_posemath_segment *segment) { const double dy = segment->end.tran.y - segment->start.tran.y; const double dz = segment->end.tran.z - segment->start.tran.z; double value = dx * dx + dy * dy + dz * dz; + if (segment->motion_type == SIM_POSEMATH_CIRCULAR) { + const double sx = segment->start.tran.x - segment->center.x; + const double sy = segment->start.tran.y - segment->center.y; + const double sz = segment->start.tran.z - segment->center.z; + const double ex = segment->end.tran.x - segment->center.x; + const double ey = segment->end.tran.y - segment->center.y; + const double ez = segment->end.tran.z - segment->center.z; + const double radius = sqrt(sx * sx + sy * sy + sz * sz); + const double dot = sx * ex + sy * ey + sz * ez; + const double cross_x = sy * ez - sz * ey; + const double cross_y = sz * ex - sx * ez; + const double cross_z = sx * ey - sy * ex; + const double normal_length = sqrt(segment->normal.x * segment->normal.x + segment->normal.y * segment->normal.y + segment->normal.z * segment->normal.z); + double angle; + if (!isfinite(radius) || radius <= 1e-12 || !isfinite(normal_length) || normal_length <= 1e-12) return 0.0; + angle = atan2((segment->normal.x * cross_x + segment->normal.y * cross_y + segment->normal.z * cross_z) / normal_length, dot); + if (angle < 0.0) angle = -angle; + if (angle <= 1e-12) angle = 2.0 * M_PI; + if (segment->turn > 1 || segment->turn < -1) angle += 2.0 * M_PI * (fabs((double)segment->turn) - 1.0); + return isfinite(radius * angle) && radius * angle > 0.0 ? radius * angle : 0.0; + } return isfinite(value) && value > 0.0 ? sqrt(value) : 0.0; } @@ -94,6 +115,10 @@ int sim_motion_init(sim_motion_queue *queue, double cycle_time, double velocity_ return sim_motion_reset(queue); } +uint32_t sim_motion_size(void) { return (uint32_t)sizeof(sim_motion_queue); } +uint32_t sim_motion_command_size(void) { return (uint32_t)sizeof(sim_motion_command); } +uint32_t sim_motion_state_size(void) { return (uint32_t)sizeof(sim_motion_state); } + int sim_motion_set_profile(sim_motion_queue *queue, int profile) { motion_meta *meta = meta_for(queue, 1); if (meta == NULL || (profile != SIM_MOTION_PROFILE_BLEND && profile != SIM_MOTION_PROFILE_EXACT_STOP)) return SIM_MOTION_ERR_ARGUMENT; @@ -120,6 +145,11 @@ int sim_motion_set_paused(sim_motion_queue *queue, uint32_t paused) { motion_meta *meta = meta_for(queue, 1); if (meta == NULL || paused > 1u) return SIM_MOTION_ERR_ARGUMENT; meta->paused = paused; + if (paused != 0u) { + meta->current_velocity = 0.0; + meta->current_acceleration = 0.0; + queue->state.velocity = 0.0; + } return SIM_MOTION_OK; } diff --git a/wasm-core/src/sim_posemath.c b/wasm-core/src/sim_posemath.c index aa87338..3239897 100644 --- a/wasm-core/src/sim_posemath.c +++ b/wasm-core/src/sim_posemath.c @@ -118,7 +118,7 @@ int sim_posemath_segment_validate(const sim_posemath_segment *segment) { length_sq = (segment->end.tran.x - segment->start.tran.x) * (segment->end.tran.x - segment->start.tran.x) + (segment->end.tran.y - segment->start.tran.y) * (segment->end.tran.y - segment->start.tran.y) + (segment->end.tran.z - segment->start.tran.z) * (segment->end.tran.z - segment->start.tran.z); - if (!isfinite(length_sq) || length_sq <= 1e-24) return SIM_POSEMATH_ERR_DEGENERATE; + if (!isfinite(length_sq) || (length_sq <= 1e-24 && segment->motion_type != SIM_POSEMATH_CIRCULAR)) return SIM_POSEMATH_ERR_DEGENERATE; if (segment->motion_type == SIM_POSEMATH_CIRCULAR) { normal_sq = segment->normal.x * segment->normal.x + segment->normal.y * segment->normal.y + segment->normal.z * segment->normal.z; if (!isfinite(normal_sq) || normal_sq <= 1e-24) return SIM_POSEMATH_ERR_DEGENERATE; diff --git a/wasm-core/tests/sim_interpreter_test.c b/wasm-core/tests/sim_interpreter_test.c index c9f47d9..127e365 100644 --- a/wasm-core/tests/sim_interpreter_test.c +++ b/wasm-core/tests/sim_interpreter_test.c @@ -9,6 +9,8 @@ int main(void) { sim_interpreter parameter_interpreter = {0}; sim_interpreter_state state = {0}; sim_interpreter_state second = {0}; + sim_posemath_segment motion = {0}; + sim_posemath_pose point = {0}; char canonical[SIM_INTERPRETER_CANONICAL_CAPACITY] = {0}; uint32_t required; @@ -39,6 +41,10 @@ int main(void) { assert(sim_interpreter_execute(&interpreter, "G91 X1 (increment)", (uint32_t)(sizeof("G91 X1 (increment)") - 1)) == SIM_INTERP_OK); assert(sim_interpreter_read_state(&interpreter, &second) == SIM_INTERP_OK); assert(second.x == 11.0 && second.line == state.line + 1); + assert(sim_interpreter_execute(&interpreter, "G17 G02 I1 J1", (uint32_t)(sizeof("G17 G02 I1 J1") - 1)) == SIM_INTERP_OK); + assert(sim_interpreter_read_motion(&interpreter, &motion) == SIM_INTERP_OK); + assert(motion.motion_type == SIM_POSEMATH_CIRCULAR && motion.turn < 0); + assert(sim_posemath_segment_point(&motion, 0.5, &point) == SIM_POSEMATH_OK); assert(sim_interpreter_execute(&interpreter, "G20 X1", 6) == SIM_INTERP_OK); assert(fabs(interpreter.state.x - 36.4) < 1e-12); assert(sim_interpreter_execute(&interpreter, "X1 G90 G21", (uint32_t)(sizeof("X1 G90 G21") - 1)) == SIM_INTERP_OK); diff --git a/web/app/axis-contract.mjs b/web/app/axis-contract.mjs index 7e4a447..27d8ccc 100644 --- a/web/app/axis-contract.mjs +++ b/web/app/axis-contract.mjs @@ -11,6 +11,7 @@ export const AXIS_COMMANDS = Object.freeze({ jogStop: "JOG_STOP", home: "HOME", override: "SET_OVERRIDE", estop: "ESTOP", recover: "IO_RECOVER", spindle: "SPINDLE_COMMAND", coolant: "COOLANT_COMMAND", toolPrepare: "TOOL_PREPARE", toolChange: "TOOL_CHANGE", probe: "PROBE_CONFIG", + reset: "RESET", }); export const AXIS_BOUNDARIES = Object.freeze({ @@ -35,8 +36,17 @@ export function normalizeAxisState(input = {}) { const programStatus = string(program.program_status ?? input.program_status, "IDLE").toLowerCase(); const queue = number(task.queued_mdi_commands ?? input.queued_mdi_commands); const queueLimit = Math.max(1, number(task.max_queued_mdi_commands ?? input.max_queued_mdi_commands, 10)); - const homed = Array.isArray(motion.homed) ? motion.homed : (Array.isArray(input.homed) ? input.homed : []); + const homed = Array.isArray(motion.homed_axes) + ? motion.homed_axes + : Array.isArray(motion.homed) + ? motion.homed + : Array.isArray(input.homed) + ? input.homed + : motion.homed === true + ? [true, true, true] + : []; const limits = input.on_any_limit ?? motion.on_any_limit ?? motion.limit ?? false; + const fault = rawTaskState === "FAULT" || Boolean(motion.fault || devices.fault || input.fault); const tool = devices.tool ?? input.tool ?? {}; const toolNumber = number(tool.current ?? input.tool_in_spindle); const taskFile = Boolean(input.taskfile ?? program.path ?? program.source_hash ?? program.loaded ?? (program.program_status && String(program.program_status).toLowerCase() !== "idle")); @@ -44,7 +54,7 @@ export function normalizeAxisState(input = {}) { return Object.freeze({ taskState, interpState, mode, motionMode, programStatus, queue, queueLimit, taskFile, hasEditor, onAnyLimit: Boolean(limits), toolLoaded: toolNumber > 0, - allHomed: homed.length > 0 && homed.every(Boolean), homed, + allHomed: homed.length > 0 && homed.every(Boolean), homed, fault, }); } @@ -58,10 +68,13 @@ export function axisGate(action, input = {}) { const trajectoryTouchOffAllowed = state.motionMode !== "FREE" || input.kinematicsIdentity === true; let enabled = false; let reason = "axis_gate_unknown_action"; + if (state.fault && !["estop", "recover", "overrideLimits"].includes(action)) return Object.freeze({ action, enabled: false, reason: "axis_fault_active", taskState: state.taskState, interpState: state.interpState }); switch (action) { case "estop": enabled = true; reason = estop ? "axis_estop_reset_available" : "axis_estop_available"; break; case "recover": enabled = true; reason = "axis_recover_available"; break; case "power": enabled = state.taskState !== "ESTOP"; reason = enabled ? "axis_power_available" : "axis_estop_active"; break; + case "override": enabled = machineOn && !estop; reason = enabled ? "axis_override_available" : estop ? "axis_estop_active" : "axis_machine_off"; break; + case "reset": enabled = machineOn && !running && !state.onAnyLimit; reason = enabled ? "axis_reset_available" : state.onAnyLimit ? "axis_limit_active" : "axis_reset_state_gate"; break; case "open": enabled = idle; reason = enabled ? "axis_interp_idle" : "axis_interp_running"; break; case "load": enabled = machineOn && idle; reason = enabled ? "axis_program_load_available" : !machineOn ? "axis_machine_off" : "axis_interp_running"; break; case "reload": enabled = idle && state.taskFile; reason = !idle ? "axis_interp_running" : state.taskFile ? "axis_reload_available" : "axis_taskfile_missing"; break; @@ -74,9 +87,9 @@ export function axisGate(action, input = {}) { case "stop": case "abort": enabled = machineOn && running; reason = enabled ? "axis_stop_available" : "axis_stop_state_gate"; break; case "restart": enabled = machineOn && idle && state.taskFile; reason = enabled ? "axis_restart_available" : !machineOn ? "axis_machine_off" : !state.taskFile ? "axis_taskfile_missing" : "axis_restart_state_gate"; break; case "mdi": enabled = machineOn && state.queue < state.queueLimit && (idle || state.mode === "MDI"); reason = enabled ? "axis_mdi_available" : estop ? "axis_estop_active" : !machineOn ? "axis_machine_off" : state.queue >= state.queueLimit ? "axis_mdi_queue_full" : "axis_interp_running"; break; - case "jogStart": case "jogStop": case "home": case "spindle": case "coolant": enabled = manualOk; reason = enabled ? "axis_manual_available" : estop ? "axis_estop_active" : !machineOn ? "axis_machine_off" : "axis_manual_state_gate"; break; - case "touchOff": enabled = manualOk && trajectoryTouchOffAllowed; reason = !manualOk ? "axis_manual_state_gate" : !trajectoryTouchOffAllowed ? "axis_joint_mode_required" : "axis_touch_off_available"; break; - case "toolTouchOff": enabled = manualOk && trajectoryTouchOffAllowed && state.toolLoaded; reason = !state.toolLoaded ? "axis_tool_missing" : !trajectoryTouchOffAllowed ? "axis_joint_mode_required" : !manualOk ? "axis_manual_state_gate" : "axis_tool_touch_off_available"; break; + case "jogStart": case "jogStop": case "home": case "spindle": case "coolant": enabled = manualOk && state.mode === "MANUAL"; reason = enabled ? "axis_manual_available" : estop ? "axis_estop_active" : !machineOn ? "axis_machine_off" : state.mode !== "MANUAL" ? "axis_manual_mode_required" : "axis_manual_state_gate"; break; + case "touchOff": enabled = manualOk && state.mode === "MANUAL" && trajectoryTouchOffAllowed; reason = !manualOk ? "axis_manual_state_gate" : state.mode !== "MANUAL" ? "axis_manual_mode_required" : !trajectoryTouchOffAllowed ? "axis_joint_mode_required" : "axis_touch_off_available"; break; + case "toolTouchOff": enabled = manualOk && state.mode === "MANUAL" && trajectoryTouchOffAllowed && state.toolLoaded; reason = !state.toolLoaded ? "axis_tool_missing" : state.mode !== "MANUAL" ? "axis_manual_mode_required" : !trajectoryTouchOffAllowed ? "axis_joint_mode_required" : !manualOk ? "axis_manual_state_gate" : "axis_tool_touch_off_available"; break; case "overrideLimits": enabled = state.onAnyLimit; reason = enabled ? "axis_limit_override_available" : "axis_no_active_limit"; break; default: break; } diff --git a/web/app/axis-operation-controller.mjs b/web/app/axis-operation-controller.mjs index 895e111..bc33dbf 100644 --- a/web/app/axis-operation-controller.mjs +++ b/web/app/axis-operation-controller.mjs @@ -27,7 +27,8 @@ export class AxisOperationController { async execute(kind, payload = {}, { state = {}, action = COMMAND_ACTIONS[kind] ?? null, gate = true } = {}) { const operationId = this.nextOperationId(kind); const gateResult = action ? axisGate(action, state) : { action: null, enabled: true, reason: "axis_unrestricted_internal_command" }; - if (gate && !gateResult.enabled) { + const authoritativeGate = action && state?.gates?.[action]; + if (gate && !gateResult.enabled && !authoritativeGate) { this.trace.push(Object.freeze({ operation_id: operationId, kind, action, phase: "blocked", reason: gateResult.reason })); throw new AxisGateError(gateResult); } diff --git a/web/react/src/App.tsx b/web/react/src/App.tsx index c0b62b9..77e54bb 100644 --- a/web/react/src/App.tsx +++ b/web/react/src/App.tsx @@ -6,20 +6,22 @@ import { SIM_CASE_CATALOG } from "../../workspace/sim-case-catalog.mjs"; import { loadSimulationCase, toWorkerConfiguration } from "../../workspace/sim-case-loader.mjs"; import { axisIcon } from "./sourceAssets"; import { AxisGateError, AxisOperationController } from "../../app/axis-operation-controller.mjs"; +import { axisGate } from "../../app/axis-contract.mjs"; export const SELECTED_AXIS3_CASE = "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini"; const DEFAULT_CASE = SELECTED_AXIS3_CASE; -const DEFAULT_PROGRAM = ["G21 G90", "G0 X0 Y0 Z0", "G1 X10 Y0 F120", "G1 X10 Y10", "G1 X0 Y10", "G1 X0 Y0", "M2"].join("\n"); +const PROGRAM_PATH = "linuxcnc/configs/nc_files/3dtest.ngc"; +const DEFAULT_PROGRAM = ""; -type Action = (kind: string, payload?: Record) => void; +type Action = (kind: string, payload?: Record) => Promise | void; type IconButtonProps = { label: string; asset: string; onClick?: () => void; disabled?: boolean; danger?: boolean; pressed?: boolean; feature?: string }; function IconButton({ label, asset, onClick, disabled = false, danger = false, pressed = false, feature = "AXIS-UI-05" }: IconButtonProps) { return ; } -function TextButton({ children, onClick, disabled = false, danger = false, feature = "AXIS-UI-07" }: { children: React.ReactNode; onClick?: () => void; disabled?: boolean; danger?: boolean; feature?: string }) { - return ; +function TextButton({ children, onClick, onPointerDown, onPointerUp, onPointerCancel, onPointerLeave, ariaLabel, disabled = false, danger = false, feature = "AXIS-UI-07" }: { children: React.ReactNode; onClick?: () => void; onPointerDown?: React.PointerEventHandler; onPointerUp?: React.PointerEventHandler; onPointerCancel?: React.PointerEventHandler; onPointerLeave?: React.PointerEventHandler; ariaLabel?: string; disabled?: boolean; danger?: boolean; feature?: string }) { + return ; } function Separator() { return