Complete 3axis tutorial native and Web/WASM parity tools
3
.gitignore
vendored
@@ -2,3 +2,6 @@
|
||||
/build-native/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
web/react/node_modules/
|
||||
web/react/dist/
|
||||
bak/
|
||||
|
||||
@@ -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
|
||||
|
||||
BIN
coverage/axis3-tutorial-manual-ui-complete.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
coverage/axis3-tutorial-manual-ui-homed.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
coverage/axis3-tutorial-manual-ui-running.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
81
coverage/axis3-tutorial-manual-ui-verification.report.json
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
BIN
coverage/axis3-tutorial-manual-ui.png
Normal file
|
After Width: | Height: | Size: 156 KiB |
115
coverage/axis3-tutorial-native-3dtest.report.json
Normal file
@@ -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",
|
||||
"<case_dir>/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",
|
||||
"<case_dir>/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"
|
||||
}
|
||||
@@ -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",
|
||||
"<case_dir>/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": [
|
||||
|
||||
BIN
coverage/axis3-tutorial-native-tool.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
306
coverage/axis3-tutorial-native-web-tool-comparison.report.json
Normal file
BIN
coverage/axis3-tutorial-visual/chromium-3dtest-fit.png
Normal file
|
After Width: | Height: | Size: 158 KiB |
BIN
coverage/axis3-tutorial-visual/chromium-3dtest-loaded.png
Normal file
|
After Width: | Height: | Size: 163 KiB |
BIN
coverage/axis3-tutorial-visual/chromium-3dtest.png
Normal file
|
After Width: | Height: | Size: 158 KiB |
BIN
coverage/axis3-tutorial-visual/native-3dtest.png
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
coverage/axis3-tutorial-web-complete.png
Normal file
|
After Width: | Height: | Size: 141 KiB |
BIN
coverage/axis3-tutorial-web-homed.png
Normal file
|
After Width: | Height: | Size: 142 KiB |
571
coverage/axis3-tutorial-web-manual-verification.report.json
Normal file
BIN
coverage/axis3-tutorial-web-ready.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
coverage/axis3-tutorial-web-running.png
Normal file
|
After Width: | Height: | Size: 141 KiB |
285
coverage/axis3-tutorial-web-wasm-complete-parity.report.json
Normal file
@@ -0,0 +1,285 @@
|
||||
{
|
||||
"authority_chain": [
|
||||
"linuxcnc_source",
|
||||
"native_fixture",
|
||||
"wasm_core",
|
||||
"dedicated_worker",
|
||||
"react_projection"
|
||||
],
|
||||
"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"
|
||||
],
|
||||
"candidate_eligible": true,
|
||||
"case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini",
|
||||
"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": "b842fb78ac192f1856af805f44e9c8503324738b890cbb023449a0ea7ed651ab",
|
||||
"status": "passed"
|
||||
},
|
||||
{
|
||||
"command": [
|
||||
"node",
|
||||
"web/worker/test_panel_snapshot_projection.mjs"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"name": "panel_snapshot",
|
||||
"output_bytes": 404,
|
||||
"output_sha256": "51fc497c58065f6c15efa0b3e517c7db7a181dd4f31b757dc680c8f45acc7fd6",
|
||||
"status": "passed"
|
||||
},
|
||||
{
|
||||
"command": [
|
||||
"node",
|
||||
"web/worker/test_panel_gate_matrix.mjs"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"name": "panel_gates",
|
||||
"output_bytes": 285,
|
||||
"output_sha256": "21048a2681a7da10779a0508c14c995c15236b4ce742715b211a8b83bd738ada",
|
||||
"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": "visible_completion",
|
||||
"output_bytes": 223,
|
||||
"output_sha256": "621f5980f9ee4c56dafd57e108d3dd1c410aa77e8716c5b574cf887875e0a52e",
|
||||
"status": "passed"
|
||||
},
|
||||
{
|
||||
"command": [
|
||||
"node",
|
||||
"web/worker/test_axis3_tutorial_program.mjs"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"name": "program_mdi",
|
||||
"output_bytes": 499,
|
||||
"output_sha256": "eb2b99723c5a9b26a9773bf1010502521dd30714297b175ee8d94b0182c64220",
|
||||
"status": "passed"
|
||||
},
|
||||
{
|
||||
"command": [
|
||||
"node",
|
||||
"web/worker/test_axis3_tutorial_sequence.mjs"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"name": "sequence_home_tool_ack",
|
||||
"output_bytes": 3656,
|
||||
"output_sha256": "08d119d7d6fa26d7295a580ec28e2ac8261e00a11fa325a0ecc4427c4bb02338",
|
||||
"status": "passed"
|
||||
},
|
||||
{
|
||||
"command": [
|
||||
"node",
|
||||
"web/worker/test_axis3_tutorial_tool_io.mjs"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"name": "tool_io_probe",
|
||||
"output_bytes": 483,
|
||||
"output_sha256": "17cb816f695a789fb79af47eb1625defea670342d1e9a7f9d25ac4b5a34c9315",
|
||||
"status": "passed"
|
||||
},
|
||||
{
|
||||
"command": [
|
||||
"node",
|
||||
"web/worker/test_wasm_preview.mjs"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"name": "preview_wasm_motion",
|
||||
"output_bytes": 198,
|
||||
"output_sha256": "3d4f47d5771b91f8c20102a75233260831a89d529043eee5b615f6c4522e7278",
|
||||
"status": "passed"
|
||||
},
|
||||
{
|
||||
"command": [
|
||||
"node",
|
||||
"web/worker/test_wasm_motion_authority.mjs"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"name": "motion_authority",
|
||||
"output_bytes": 351,
|
||||
"output_sha256": "34f090eef3d78d77d192658fbc279f0bdd72fa038eef44cfdc0138d6068d3d8c",
|
||||
"status": "passed"
|
||||
},
|
||||
{
|
||||
"command": [
|
||||
"node",
|
||||
"web/worker/test_wasm_motion_checkpoint.mjs"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"name": "motion_checkpoint",
|
||||
"output_bytes": 387,
|
||||
"output_sha256": "0e66edea6f69ea7cd7206adf128b2b2ffb1039db6bd27dbfc5f8f89ddb0fa998",
|
||||
"status": "passed"
|
||||
},
|
||||
{
|
||||
"command": [
|
||||
"node",
|
||||
"web/worker/test_wasm_canonical_state.mjs"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"name": "canonical_state",
|
||||
"output_bytes": 309,
|
||||
"output_sha256": "48b41b755925d7065f99db25e11f042789f917d15107de23c3ef980c9f2978ba",
|
||||
"status": "passed"
|
||||
},
|
||||
{
|
||||
"command": [
|
||||
"node",
|
||||
"web/worker/test_wasm_canonical_state_checkpoint.mjs"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"name": "canonical_checkpoint",
|
||||
"output_bytes": 361,
|
||||
"output_sha256": "5a3c0d7e8f2b401cfba08ba9820bfef135c9e3f25d6a001c319f5dddd0abf286",
|
||||
"status": "passed"
|
||||
},
|
||||
{
|
||||
"command": [
|
||||
"node",
|
||||
"web/worker/test_axis3_tutorial_persistence.mjs"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"name": "persistence",
|
||||
"output_bytes": 425,
|
||||
"output_sha256": "350c5319372118604c99abc559b9c20505773aa24e956fbe0d6f4d7ce7c866d2",
|
||||
"status": "passed"
|
||||
},
|
||||
{
|
||||
"command": [
|
||||
"node",
|
||||
"web/worker/test_trace_export.mjs"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"name": "trace",
|
||||
"output_bytes": 63,
|
||||
"output_sha256": "963d8eb749515dcd05a72ac97bce61d29440f3b4ca2fff9fca8ac19c55be1e8d",
|
||||
"status": "passed"
|
||||
},
|
||||
{
|
||||
"command": [
|
||||
"bash",
|
||||
"-lc",
|
||||
"for test_file in web/app/test_*.mjs; do node \"$test_file\" >/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"
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
146
coverage/linuxcnc-source-semantics-contract.json
Normal file
@@ -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"
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
BIN
coverage/panel-01-manual-axis-complete.png
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
coverage/panel-01-manual-axis-running.png
Normal file
|
After Width: | Height: | Size: 166 KiB |
5919
coverage/panel-01-source-control-inventory.report.json
Normal file
82
coverage/panel-02-snapshot-projection.report.json
Normal file
@@ -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"
|
||||
}
|
||||
81
coverage/panel-03-source-gate-matrix.report.json
Normal file
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
57
coverage/release-authorization.json
Normal file
@@ -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"
|
||||
}
|
||||
}
|
||||
95
coverage/release-authorized-gate.report.json
Normal file
@@ -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"
|
||||
]
|
||||
}
|
||||
141
coverage/release-verification.report.json
Normal file
@@ -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"
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
99
coverage/wasm-canonical-state-t4.report.json
Normal file
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
1309
coverage/wasm-feature-matrix-t5.report.json
Normal file
922
coverage/wasm-function-closure-t6.report.json
Normal file
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
114
coverage/wasm-motion-authority-t3.report.json
Normal file
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
525
coverage/wasm-only-source-audit.report.json
Normal file
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
229
coverage/wasm-simulation-release-gate-t7.report.json
Normal file
@@ -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"
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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。
|
||||
@@ -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。
|
||||
@@ -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`
|
||||
|
||||
41
docs/linuxcnc-web-wasm-release-authorization.md
Normal file
@@ -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.
|
||||
20
docs/linuxcnc-web-wasm-source-semantics.md
Normal file
@@ -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.
|
||||
98
docs/linuxcnc-web-wasm-wasm-only-migration-plan.md
Normal file
@@ -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 均由该契约决定。
|
||||
@@ -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` |
|
||||
|
||||
36
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
|
||||
|
||||
|
||||
244
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 归属缺口保留为可定位审计项
|
||||
在不刷新 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 <case_dir>/<entrypoint>.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
|
||||
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`。
|
||||
|
||||
20
run-3axis-tutorial.sh
Executable file
@@ -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}"
|
||||
87
scripts/generate_axis3_tool_comparison.py
Normal file
@@ -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())
|
||||
96
scripts/generate_axis3_tutorial_complete_parity.py
Normal file
@@ -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())
|
||||
343
scripts/generate_panel_01_source_control_inventory.py
Normal file
@@ -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']}")
|
||||
77
scripts/generate_panel_02_snapshot_projection.py
Normal file
@@ -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())
|
||||
76
scripts/generate_panel_03_source_gate_matrix.py
Normal file
@@ -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())
|
||||
188
scripts/generate_release_authorized_gate.py
Normal file
@@ -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())
|
||||
84
scripts/generate_release_verification.py
Normal file
@@ -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())
|
||||
@@ -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",
|
||||
|
||||
72
scripts/generate_source_semantics_contract.py
Normal file
@@ -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']}")
|
||||
80
scripts/generate_wasm_canonical_state_t4.py
Normal file
@@ -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())
|
||||
457
scripts/generate_wasm_feature_matrix_t5.py
Normal file
@@ -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())
|
||||
238
scripts/generate_wasm_function_closure_t6.py
Normal file
@@ -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())
|
||||
97
scripts/generate_wasm_motion_authority_t3.py
Normal file
@@ -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())
|
||||
114
scripts/generate_wasm_only_source_audit.py
Normal file
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Audit the WASM-only source boundary without promoting partial parity."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
REPORT = ROOT / "coverage/wasm-only-source-audit.report.json"
|
||||
SCHEMA = "wasm-only-source-audit.v1"
|
||||
|
||||
WASM_MANIFEST = ROOT / "build-wasm/build.ninja"
|
||||
JS_AUTHORITY = (
|
||||
("web/worker/authoritative-runtime.mjs", "compatibility planner and pose mirror for non-selected cases", "compatibility_open"),
|
||||
("web/worker/program-runner.mjs", "program scheduling and control flow mirror; canonical segments enter WASM queue", "open"),
|
||||
("web/worker/motion-state.mjs", "home/jog/limit projection pending T4 canonical state", "open"),
|
||||
("web/worker/sim-task.mjs", "task state mirror pending T4 canonical state", "open"),
|
||||
)
|
||||
BASELINES = (
|
||||
("preview", ["python3", "scripts/test_axis3_tutorial_preview.py"]),
|
||||
("selected_case_worker", ["node", "web/worker/test_axis3_selected_case.mjs"]),
|
||||
("visible_completion_worker", ["node", "web/worker/test_axis3_visible_completion.mjs"]),
|
||||
("wasm_motion_event", ["node", "web/worker/test_wasm_motion_event.mjs"]),
|
||||
("wasm_motion_authority", ["node", "web/worker/test_wasm_motion_authority.mjs"]),
|
||||
("wasm_motion_checkpoint", ["node", "web/worker/test_wasm_motion_checkpoint.mjs"]),
|
||||
("wasm_canonical_state", ["node", "web/worker/test_wasm_canonical_state.mjs"]),
|
||||
("wasm_canonical_state_checkpoint", ["node", "web/worker/test_wasm_canonical_state_checkpoint.mjs"]),
|
||||
("react_typecheck", ["npm", "run", "typecheck"], ROOT / "web/react"),
|
||||
)
|
||||
|
||||
|
||||
def sha256(path: Path) -> str:
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def canonical(value: Any) -> str:
|
||||
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
||||
|
||||
|
||||
def wasm_exports() -> dict[str, Any]:
|
||||
text = WASM_MANIFEST.read_text(encoding="utf-8", errors="replace") if WASM_MANIFEST.is_file() else ""
|
||||
names = sorted(set(re.findall(r"'_((?:sim|malloc|free)[A-Za-z0-9_]+)'", text)))
|
||||
return {
|
||||
"manifest": WASM_MANIFEST.as_posix(),
|
||||
"manifest_sha256": sha256(WASM_MANIFEST) if WASM_MANIFEST.is_file() else None,
|
||||
"count": len(names),
|
||||
"names": names,
|
||||
"domains": {
|
||||
"interpreter": [name for name in names if "interpreter" in name],
|
||||
"motion": [name for name in names if any(token in name for token in ("motion", "tp_", "kinematics_motion"))],
|
||||
"task_scheduler": [name for name in names if any(token in name for token in ("task", "scheduler"))],
|
||||
"hal_io": [name for name in names if any(token in name for token in ("hal", "device", "io_"))],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def js_authority() -> list[dict[str, Any]]:
|
||||
rows = []
|
||||
for path, reason, status in JS_AUTHORITY:
|
||||
absolute = ROOT / path
|
||||
rows.append({"path": path, "sha256": sha256(absolute) if absolute.is_file() else None, "reason": reason, "status": status})
|
||||
return rows
|
||||
|
||||
|
||||
def run_baselines() -> list[dict[str, Any]]:
|
||||
rows = []
|
||||
for name, command, *working_dir in BASELINES:
|
||||
cwd = working_dir[0] if working_dir else ROOT
|
||||
result = subprocess.run(command, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False)
|
||||
rows.append({
|
||||
"name": name,
|
||||
"command": command,
|
||||
"cwd": cwd.as_posix(),
|
||||
"returncode": result.returncode,
|
||||
"status": "verified" if result.returncode == 0 else "blocked",
|
||||
"output_sha256": hashlib.sha256(result.stdout.encode()).hexdigest(),
|
||||
"output_tail": result.stdout[-1200:],
|
||||
})
|
||||
return rows
|
||||
|
||||
|
||||
def main() -> int:
|
||||
report: dict[str, Any] = {
|
||||
"schema": SCHEMA,
|
||||
"status": "blocked",
|
||||
"source_of_truth_target": "linuxcnc_source_to_wasm_core_only",
|
||||
"selected_case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini",
|
||||
"selected_case_motion_authority": "verified",
|
||||
"selected_case_motion_source": "wasm_motion_queue",
|
||||
"selected_case_checkpoint_restore": "verified",
|
||||
"wasm_exports": wasm_exports(),
|
||||
"js_authority_remaining": js_authority(),
|
||||
"baseline": run_baselines(),
|
||||
"open_tasks": ["T1_FULL_CANONICAL_CALLBACKS", "T2_FULL_WASM_PREVIEW", "T3_NON_SELECTED_MOTION_AUTHORITY", "T4_HAL_IO_TOOL_PROBE_CANONICAL_STATE", "T5_FULL_MATRIX"],
|
||||
"completed_slices": ["T1_INTERPRETER_MOTION_EVENT", "T2_SELECTED_CASE_WASM_PREVIEW", "T3_WASM_MOTION_QUEUE_SNAPSHOT", "T3_SELECTED_MOTION_AUTHORITY", "T3_WASM_MOTION_CHECKPOINT", "T4_SELECTED_TASK_CANONICAL", "T4_SELECTED_HOME_LIMIT_CANONICAL"],
|
||||
"selected_case_task_authority": "wasm_task_canonical",
|
||||
"selected_case_home_limit_authority": "wasm_home_limit",
|
||||
"stop_reason": "selected-case task/motion/home-limit canonical state and queue recovery are verified; HAL/IO/tool/probe, compatibility, non-selected motion authority, and full parity remain open",
|
||||
}
|
||||
report["report_sha256"] = hashlib.sha256(canonical(report).encode()).hexdigest()
|
||||
REPORT.parent.mkdir(parents=True, exist_ok=True)
|
||||
REPORT.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
|
||||
print(json.dumps(report, ensure_ascii=False))
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
135
scripts/generate_wasm_simulation_release_gate_t7.py
Normal file
@@ -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())
|
||||
121
scripts/launch_axis_3dtest_manual.sh
Executable file
@@ -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"
|
||||
184
scripts/manual_axis_ui_verify.py
Normal file
@@ -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)
|
||||
@@ -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", "<case_dir>/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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
32
scripts/test_axis3_tool_comparison.py
Normal file
@@ -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()
|
||||
35
scripts/test_axis3_tutorial_complete_parity.py
Normal file
@@ -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()
|
||||
26
scripts/test_axis3_tutorial_preview.py
Normal file
@@ -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())
|
||||
57
scripts/test_panel_01_source_control_inventory.py
Normal file
@@ -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()
|
||||
33
scripts/test_panel_02_snapshot_projection.py
Normal file
@@ -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()
|
||||
33
scripts/test_panel_03_source_gate_matrix.py
Normal file
@@ -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()
|
||||
44
scripts/test_release_authorized_gate.py
Normal file
@@ -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()
|
||||
32
scripts/test_release_verification.py
Normal file
@@ -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()
|
||||
@@ -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"))
|
||||
|
||||
32
scripts/test_source_semantics_contract.py
Normal file
@@ -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()
|
||||
28
scripts/test_validate_publish_gate.py
Normal file
@@ -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()
|
||||
43
scripts/test_wasm_feature_matrix_t5.py
Normal file
@@ -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()
|
||||
40
scripts/test_wasm_function_closure_t6.py
Normal file
@@ -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()
|
||||
35
scripts/test_wasm_simulation_release_gate_t7.py
Normal file
@@ -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()
|
||||