完成L4-USER-M-PROCESS Web仿真接入
结论:L4-USER-M-PROCESS 已按 Web/virtual HAL 数控仿真主线完成接入,native LinuxCNC runtime 不再作为 Web 仿真阻塞;新增 text31.txt 接续剩余 77 个 SKIP 的 main-program promotion 复核。
This commit is contained in:
@@ -87,6 +87,12 @@ Current first priority:
|
||||
- The page now includes selectable test programs for square contour, Z pocket
|
||||
contour, incremental loop, G2/G3 arcs, and G81 drilling, with browser smoke
|
||||
coverage for each program.
|
||||
- The browser simulation state now carries a source-derived millturn
|
||||
`L4-USER-M-PROCESS` virtual HAL proof for the `M429 -> M129` turn transition
|
||||
and `M428 -> M128` mill transition. It verifies switchkins guard pins and
|
||||
`ini.[xz].*` limit pin state without enabling external process execution or
|
||||
inventory promotion, and the real browser simulation page smoke now asserts
|
||||
that proof directly from `linuxCncRealSimulationState`.
|
||||
- Playback controls now expose reset/step/play/finish over LinuxCNC canonical
|
||||
motion events, with active G-code line highlighting, active motion row
|
||||
highlighting, live axis readout, moving toolhead, and executed-path rendering.
|
||||
|
||||
337
text28.txt
337
text28.txt
@@ -491,6 +491,343 @@ wasm-port/tests/native/verify_native_probes.sh
|
||||
才允许进入真正 PASS=82 / SKIP=77 的 baseline promotion。
|
||||
```
|
||||
|
||||
十四、2026-06-20 L4-USER-M-PROCESS Web 数控仿真完全接入语义修正
|
||||
|
||||
本轮按当前项目架构修正 `L4-USER-M-PROCESS` 的完成标准:
|
||||
|
||||
```text
|
||||
当前数控系统仿真是 Web/virtual HAL 方式,不再依赖原生 LinuxCNC runtime。
|
||||
因此 blocked_existing_linuxcnc_runtime / native runtime opt-in probe 只属于旧 native
|
||||
探针语境,不再作为 Web 数控仿真接入完成的阻塞条件。
|
||||
```
|
||||
|
||||
完成态现在表述为:
|
||||
|
||||
```text
|
||||
apiName=real-browser-simulation-millturn-user-m-process-proof
|
||||
boundaryClass=L4-USER-M-PROCESS
|
||||
webSimulationReady=true
|
||||
nativeRuntimeRequired=false
|
||||
nativeRuntimeRequiredForWebSimulation=false
|
||||
processExecutionReady=false
|
||||
executionEnabled=false
|
||||
promotionAllowed=false
|
||||
```
|
||||
|
||||
含义:
|
||||
|
||||
```text
|
||||
1. Web/virtual HAL 已完整接入 millturn M429 -> M129 -> turn
|
||||
和 M428 -> M128 -> mill 的受控状态转换 proof;
|
||||
2. 已覆盖 motion.switchkins-type、motion.analog-out-03、
|
||||
kinstype.is-0 / kinstype.is-1、ini.x.* / ini.z.* limit pins;
|
||||
3. 不执行 arbitrary external user-M process;
|
||||
4. 不再要求本机启动 LinuxCNC native runtime 才能判定 Web 仿真完成;
|
||||
5. promotionAllowed=false 仅表示不把它伪装成 native external-process promotion。
|
||||
```
|
||||
|
||||
本轮代码修正:
|
||||
|
||||
```text
|
||||
wasm-port/runtime/sdk/src/linuxcnc-hal.js
|
||||
- createVirtualHalMillturnUserMProcessBoundaryReport() 新增
|
||||
webSimulationReady=true;
|
||||
- nativeRuntimeRequired / nativeRuntimeRequiredForWebSimulation 改为 false;
|
||||
- state target / row proofStatus 改为 web_virtual_hal_simulation_ready;
|
||||
- summaryRows 明确 Native LinuxCNC runtime = not required;
|
||||
- applyVirtualHalMillturnUserMProcessState() 返回同样的 Web 仿真完成态字段。
|
||||
|
||||
wasm-port/runtime/ui/simulation/simulation-app.js
|
||||
- runRealBrowserSimulation().millturnUserMProcess 现在明确返回
|
||||
webSimulationReady=true、nativeRuntimeRequired=false。
|
||||
|
||||
wasm-port/runtime/sdk/src/project-release-readiness.js
|
||||
- browser diagnostics artifact validation 要求
|
||||
webSimulationReady=true 且 nativeRuntimeRequired=false。
|
||||
|
||||
wasm-port/tests/ui/node/verify_real_simulation_programs.mjs
|
||||
wasm-port/tests/browser/real_simulation_page_smoke.html
|
||||
wasm-port/tests/sdk/node/verify_sdk_surface.mjs
|
||||
- 同步断言 Web 仿真 ready,且不依赖 native LinuxCNC runtime。
|
||||
|
||||
wasm-port/docs/full-process-boundary-design.md
|
||||
- 更新旧表述,不再说 browser proof 需要 nativeRuntimeRequired=true。
|
||||
```
|
||||
|
||||
当前结论:
|
||||
|
||||
```text
|
||||
L4-USER-M-PROCESS 已完全接入当前 Web 数控系统仿真。
|
||||
仍保持 external user-M process execution disabled,不做 native promotion 冒充。
|
||||
```
|
||||
|
||||
十二、2026-06-20 L4-USER-M-PROCESS Web/virtual HAL 数控仿真接入结果
|
||||
|
||||
本轮继续推进 `L4-USER-M-PROCESS` 接入数控系统仿真,但严格区分:
|
||||
|
||||
```text
|
||||
1. Web/virtual HAL 数控仿真状态转换 proof;
|
||||
2. sim-config inventory baseline promotion。
|
||||
```
|
||||
|
||||
结论:
|
||||
|
||||
```text
|
||||
Web/virtual HAL 数控仿真接入成功。
|
||||
|
||||
`runRealBrowserSimulation()` 现在返回:
|
||||
apiName=real-browser-simulation-millturn-user-m-process-proof
|
||||
boundaryClass=L4-USER-M-PROCESS
|
||||
path=axis/vismach/millturn/example.ngc
|
||||
ready=true
|
||||
|
||||
已验证的受控状态转换:
|
||||
- M429 -> M129 -> turn;
|
||||
- M428 -> M128 -> mill;
|
||||
- motion.switchkins-type;
|
||||
- motion.analog-out-03;
|
||||
- kinstype.is-0 / kinstype.is-1;
|
||||
- ini.x.min_limit / ini.x.max_limit;
|
||||
- ini.z.min_limit / ini.z.max_limit。
|
||||
|
||||
但 inventory promotion 仍未解锁:
|
||||
- webSimulationReady=true;
|
||||
- nativeRuntimeRequired=false;
|
||||
- nativeRuntimeRequiredForWebSimulation=false;
|
||||
- processExecutionReady=false;
|
||||
- executionEnabled=false;
|
||||
- promotionAllowed=false;
|
||||
- L4_USER_M_PROCESS skip count 仍为 1。
|
||||
```
|
||||
|
||||
本轮代码接入:
|
||||
|
||||
```text
|
||||
wasm-port/runtime/ui/simulation/simulation-app.js
|
||||
- 新增 createMillturnUserMProcessSimulationProof();
|
||||
- 使用 SDK 已有的 createVirtualHalState()、
|
||||
applyVirtualHalMillturnUserMProcessState()、readVirtualHalPin();
|
||||
- 在 runRealBrowserSimulation() 状态中新增 millturnUserMProcess;
|
||||
- proof 来源仍绑定 VIRTUAL_HAL_MILLTURN_USER_M_PROCESS_BOUNDARY;
|
||||
- 不执行 arbitrary external process;
|
||||
- 不打开 executionEnabled / promotionAllowed。
|
||||
|
||||
wasm-port/tests/ui/node/verify_real_simulation_programs.mjs
|
||||
- 新增 millturnUserMProcess Node smoke;
|
||||
- 断言 M429/M129 turn 后:
|
||||
motion.switchkins-type=1
|
||||
motion.analog-out-03=1
|
||||
kinstype.is-0=0
|
||||
kinstype.is-1=1
|
||||
ini.x.min_limit=-240
|
||||
ini.x.max_limit=0
|
||||
ini.z.min_limit=-300
|
||||
ini.z.max_limit=300
|
||||
- 断言 M428/M128 mill 后:
|
||||
motion.switchkins-type=0
|
||||
motion.analog-out-03=0
|
||||
kinstype.is-0=1
|
||||
kinstype.is-1=0
|
||||
ini.x.min_limit=-300
|
||||
ini.x.max_limit=300
|
||||
ini.z.min_limit=-240
|
||||
ini.z.max_limit=0
|
||||
- 断言 webSimulationReady=true;
|
||||
- 断言 nativeRuntimeRequired=false;
|
||||
- 断言 nativeRuntimeRequiredForWebSimulation=false;
|
||||
- 断言 processExecutionReady=false;
|
||||
- 断言 executionEnabled=false;
|
||||
- 断言 promotionAllowed=false。
|
||||
|
||||
wasm-port/tests/browser/real_simulation_page_smoke.html
|
||||
- 在真实浏览器 simulation 页面中直接断言
|
||||
linuxCncRealSimulationState.millturnUserMProcess;
|
||||
- 覆盖同一组 M429/M129 turn 与 M428/M128 mill 状态转换;
|
||||
- 确认 browser 页面级 proof 保持 webSimulationReady=true、
|
||||
nativeRuntimeRequired=false、processExecutionReady=false、
|
||||
executionEnabled=false、promotionAllowed=false。
|
||||
```
|
||||
|
||||
已更新追踪/文档:
|
||||
|
||||
```text
|
||||
PROJECT_COMPLETION_TRACKER.md
|
||||
wasm-port/docs/sim-configs-coverage-handoff.md
|
||||
wasm-port/docs/compatibility-validation.md
|
||||
text28.txt
|
||||
```
|
||||
|
||||
已通过验证:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
wasm-port/tests/ui/node/verify_real_simulation_programs.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
```
|
||||
|
||||
inventory gate 输出仍保持:
|
||||
|
||||
```text
|
||||
sim_configs_wasm_node_inventory_executed=82
|
||||
sim_configs_wasm_node_inventory_passed=82
|
||||
sim_configs_wasm_node_inventory_skipped=77
|
||||
sim_configs_wasm_node_inventory_unexpected_fail=0
|
||||
sim_configs_wasm_node_inventory_skip_ASSET_ONLY=65
|
||||
sim_configs_wasm_node_inventory_skip_L4_USER_M_PROCESS=1
|
||||
sim_configs_wasm_node_inventory_skip_NON_MAIN_CLASS=10
|
||||
sim_configs_wasm_node_inventory_skip_UPSTREAM_DEMO=1
|
||||
```
|
||||
|
||||
因此当前状态应表述为:
|
||||
|
||||
```text
|
||||
L4-USER-M-PROCESS 已接入 Web/virtual HAL 数控仿真状态 proof;
|
||||
但尚未完成 sim-config inventory promotion。
|
||||
|
||||
剩余需要独占 LinuxCNC native runtime opt-in probe 获得:
|
||||
millturn_user_m_runtime_probe_status=runtime_state_probe_passed
|
||||
|
||||
之后才允许继续 Node inventory promotion gate、browser/host promotion proof
|
||||
和 manual promotion lock review。
|
||||
```
|
||||
|
||||
十一、2026-06-20 L4-USER-M-PROCESS / 剩余 77 SKIP main-program promotion 复核结果
|
||||
|
||||
本轮按当前 PASS=82 / SKIP=77 baseline 重新复核 `L4-USER-M-PROCESS` 是否已经完全接入仿真系统,以及剩余 77 个 SKIP 中哪些是真正可 promotion 的 main-program row。
|
||||
|
||||
结论:
|
||||
|
||||
```text
|
||||
L4-USER-M-PROCESS 尚未完全接入 promotion 所需的仿真证明链路。
|
||||
|
||||
当前 host runtime commands 已可发现:
|
||||
- tclsh=1
|
||||
- halrun=1
|
||||
- halcmd=1
|
||||
- linuxcnc=1
|
||||
|
||||
但默认 gate 仍保持:
|
||||
- millturn_user_m_runtime_probe_status=ready_disabled_by_default
|
||||
- execution_enabled=0
|
||||
- promotion_allowed=0
|
||||
|
||||
显式 opt-in probe 曾尝试启动 millturn LinuxCNC runtime,但未能在等待窗口内获得
|
||||
ini.x.min_limit / motion.switchkins-type HAL pins,因此不能作为 native pass proof。
|
||||
```
|
||||
|
||||
本轮代码修正:
|
||||
|
||||
```text
|
||||
wasm-port/tests/native/probe_millturn_user_m_runtime.sh
|
||||
- 记录 command_path() 解析出的 tclsh / halrun / halcmd / linuxcnc 路径;
|
||||
- opt-in probe 使用解析后的 HALCMD_BIN / LINUXCNC_BIN,不再依赖裸命令 PATH;
|
||||
- 注入 LinuxCNC RIP PATH / LD_LIBRARY_PATH / PYTHONPATH / TCLLIBPATH;
|
||||
- 用 setsid 启动 LinuxCNC,并在 cleanup 中按进程组终止,避免 opt-in 失败路径挂住。
|
||||
|
||||
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs
|
||||
- 新增 remaining-skip-main-program-promotion-audit.tsv;
|
||||
- audit 只覆盖 current_status=SKIP 且 class=main 的 row;
|
||||
- audit 复用 promotion-candidates.tsv、boundary-summary.tsv、
|
||||
runtime-boundary-promotion-readiness.tsv 判定 promotion_allowed / promotion_ready;
|
||||
- 固定当前剩余 skipped main-program row 数为 2,promotion_allowed=1 数为 0。
|
||||
|
||||
wasm-port/tests/browser/interp_smoke.html
|
||||
- generated WASM inventory artifact 固定清单从 59 更新为 60;
|
||||
- 新增 remaining-skip-main-program-promotion-audit.tsv 文档覆盖检查。
|
||||
|
||||
docs:
|
||||
- compatibility-validation.md
|
||||
- sim-configs-coverage-matrix.md
|
||||
- sim-configs-coverage-handoff.md
|
||||
均记录新增 audit artifact 和 60-entry artifact baseline。
|
||||
```
|
||||
|
||||
新增 artifact:
|
||||
|
||||
```text
|
||||
wasm-port/build/wasm/sim-configs-inventory/remaining-skip-main-program-promotion-audit.tsv
|
||||
```
|
||||
|
||||
当前 audit 结果:
|
||||
|
||||
```text
|
||||
remaining skipped main-program rows = 2
|
||||
promotion_allowed=1 rows = 0
|
||||
|
||||
1. axis/vismach/millturn/example.ngc
|
||||
current_status=SKIP
|
||||
skip_kind=L4-USER-M-PROCESS
|
||||
native_status=PASS
|
||||
simulation_proof_status=ready_disabled_by_default:native=0:node=0:browser=0
|
||||
promotion_decision=not_promotable_runtime_proof_incomplete
|
||||
promotion_allowed=0
|
||||
|
||||
2. axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc
|
||||
current_status=SKIP
|
||||
skip_kind=UPSTREAM-DEMO
|
||||
native_status=FAIL
|
||||
native_expected_failure=upstream-demo-missing-motion-gcode
|
||||
promotion_decision=not_promotable_upstream_demo_missing_motion_gcode
|
||||
promotion_allowed=0
|
||||
```
|
||||
|
||||
因此对“剩余 77 个 SKIP 中哪些是真正可 promotion 的 main-program row”的回答是:
|
||||
|
||||
```text
|
||||
当前没有真正可 promotion 的 skipped main-program row。
|
||||
|
||||
77 个 SKIP 组成:
|
||||
- ASSET-ONLY=65,不是 standalone main-program;
|
||||
- NON_MAIN_CLASS=10,不是 standalone main-program;
|
||||
- L4-USER-M-PROCESS=1,是 main-program,但缺 native/Node/browser simulation proof;
|
||||
- UPSTREAM-DEMO=1,是 main-program,但 upstream demo 本身缺失 motion G-code,不能 promotion。
|
||||
```
|
||||
|
||||
inventory baseline 保持不变:
|
||||
|
||||
```text
|
||||
sim_configs_wasm_node_inventory_executed=82
|
||||
sim_configs_wasm_node_inventory_passed=82
|
||||
sim_configs_wasm_node_inventory_skipped=77
|
||||
sim_configs_wasm_node_inventory_unexpected_fail=0
|
||||
sim_configs_wasm_node_inventory_skip_ASSET_ONLY=65
|
||||
sim_configs_wasm_node_inventory_skip_L4_USER_M_PROCESS=1
|
||||
sim_configs_wasm_node_inventory_skip_NON_MAIN_CLASS=10
|
||||
sim_configs_wasm_node_inventory_skip_UPSTREAM_DEMO=1
|
||||
```
|
||||
|
||||
本轮已通过验证:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
bash wasm-port/tests/native/probe_millturn_user_m_runtime.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
wasm-port/tests/docs/node/verify_sim_configs_coverage_docs.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_interp_browser.sh
|
||||
wasm-port/tests/native/verify_native_probes.sh
|
||||
```
|
||||
|
||||
验证中的已知非失败 stderr:
|
||||
|
||||
```text
|
||||
/work/sim-inventory/axis/db_demo/base.inc: error: Cannot open ini-file (errno=44 (No such file or directory))
|
||||
```
|
||||
|
||||
下一轮建议:
|
||||
|
||||
```text
|
||||
不要 promotion 剩余 2 个 skipped main-program row。
|
||||
|
||||
若继续 L4-USER-M-PROCESS:
|
||||
1. 先解决 opt-in millturn LinuxCNC runtime probe 不能暴露 HAL pins 的 host/runtime 问题;
|
||||
2. 只有 runtime_state_probe_passed 后,才进入 Node inventory promotion gate;
|
||||
3. 再补 browser smoke proof;
|
||||
4. 最后 manual promotion lock review。
|
||||
|
||||
否则继续按 Python-remap Phase 2.5/3 路线推进剩余 row runtime/native/Node/browser proof。
|
||||
```
|
||||
|
||||
十、2026-06-19 批量 PASS 请求 gate 复核与 axis/remap toolchange 推进结果
|
||||
|
||||
本轮再次按“text28.txt”的 promotion gate 复核 53 个 Python-remap inventory rows 是否可以批量提升为 PASS。
|
||||
|
||||
44
text30.txt
44
text30.txt
@@ -91,7 +91,8 @@ UPSTREAM-DEMO=1
|
||||
|
||||
三、仍未完成或不能宣称完成的部分
|
||||
|
||||
1. `L4-USER-M-PROCESS` 仍未完成。
|
||||
1. `L4-USER-M-PROCESS` 的 Web/virtual HAL 仿真状态 proof 已完成,但
|
||||
inventory promotion 仍未完成。
|
||||
|
||||
当前剩余 hard block:
|
||||
|
||||
@@ -105,9 +106,28 @@ promotion_allowed=0
|
||||
notes=keep_blocked_until_m128_m129_state_proof
|
||||
```
|
||||
|
||||
该 row 依赖 M128 / M129 external user-M process 边界。浏览器不能直接承诺
|
||||
arbitrary external process execution;只能做受控的 LinuxCNC-owned state-transition
|
||||
proof。该 proof 尚未完成。
|
||||
该 row 依赖 M128 / M129 external user-M process 边界。当前
|
||||
`runRealBrowserSimulation()` 已接入受控的 source-derived virtual HAL proof:
|
||||
|
||||
```text
|
||||
apiName=real-browser-simulation-millturn-user-m-process-proof
|
||||
M429 -> M129 -> turn
|
||||
M428 -> M128 -> mill
|
||||
motion.switchkins-type / motion.analog-out-03
|
||||
kinstype.is-0 / kinstype.is-1
|
||||
ini.x.* / ini.z.* limit pins
|
||||
```
|
||||
|
||||
但浏览器仍不承诺 arbitrary external process execution,且该 proof 不等于
|
||||
sim-config inventory promotion。当前仍需要独占 LinuxCNC native runtime opt-in probe
|
||||
达到:
|
||||
|
||||
```text
|
||||
millturn_user_m_runtime_probe_status=runtime_state_probe_passed
|
||||
```
|
||||
|
||||
之后才允许继续 Node inventory promotion gate、browser/host promotion proof 和
|
||||
manual promotion lock review。
|
||||
|
||||
2. `ASSET-ONLY=65` 不代表可执行主程序未做完,而是资产/子程序类行。
|
||||
|
||||
@@ -157,7 +177,7 @@ ASSET-ONLY、NON_MAIN_CLASS、UPSTREAM_DEMO 等不能被当作可执行主程序
|
||||
LinuxCNC 已全部移植完成。
|
||||
全部 sim configs 已完成。
|
||||
浏览器已完整替代 LinuxCNC native runtime。
|
||||
external user-M process 已完成。
|
||||
external user-M process 已完成并已 promotion。
|
||||
```
|
||||
|
||||
五、下一步建议
|
||||
@@ -165,12 +185,12 @@ external user-M process 已完成。
|
||||
如果继续推进“全部完成”方向,优先顺序应为:
|
||||
|
||||
```text
|
||||
1. 只针对剩余 `L4-USER-M-PROCESS=1` 做受控 state-transition proof:
|
||||
- M128 / M129;
|
||||
- kinstype guard;
|
||||
- INI axis limit -> HAL pin/state transition;
|
||||
- native -> Node/WASM -> browser proof;
|
||||
- 最后再考虑 baseline promotion。
|
||||
1. 针对剩余 `L4-USER-M-PROCESS=1` 继续 promotion proof:
|
||||
- Web/virtual HAL M128/M129 state-transition proof 已完成;
|
||||
- 下一步需要独占 native LinuxCNC runtime probe pass;
|
||||
- 再进入 Node/WASM inventory promotion gate;
|
||||
- 再补 browser/host promotion proof;
|
||||
- 最后 manual promotion lock review。
|
||||
|
||||
2. 保持 `ASSET-ONLY` 和 `NON_MAIN_CLASS` 分类,不为降低 skip 数字而伪执行。
|
||||
|
||||
@@ -191,6 +211,6 @@ external user-M process 已完成。
|
||||
当前可以视为“LinuxCNC-backed WASM/browser 数控仿真主路径阶段性完成”,
|
||||
但不能视为“LinuxCNC 全量移植完成”。
|
||||
|
||||
下一轮若继续实质推进,应优先处理唯一剩余 hard runtime block:
|
||||
下一轮若继续实质推进,应优先处理唯一剩余 inventory promotion hard runtime block:
|
||||
`L4-USER-M-PROCESS=1` / `axis/vismach/millturn/example.ngc`。
|
||||
```
|
||||
|
||||
227
text31.txt
Normal file
227
text31.txt
Normal file
@@ -0,0 +1,227 @@
|
||||
项目接续文件:L4-USER-M-PROCESS 完成后的剩余 SKIP 复核推进
|
||||
|
||||
生成时间:2026-06-20 CST
|
||||
|
||||
本文件接替当前关于 `L4-USER-M-PROCESS` 的推进记录,用于后续继续执行:
|
||||
|
||||
```text
|
||||
主要未完成点曾是剩余:
|
||||
L4-USER-M-PROCESS=1,对应 axis/vismach/millturn/example.ngc 的 external user-M process 边界;
|
||||
另外 ASSET-ONLY、NON_MAIN_CLASS、UPSTREAM-DEMO 仍不能伪装成主程序 PASS。
|
||||
|
||||
先做 L4-USER-M-PROCESS,完全接入 Web 数控仿真。
|
||||
完成它以后,再重新评估剩余 77 个 SKIP 中哪些是真正可 promotion 的 main-program row。
|
||||
```
|
||||
|
||||
一、当前结论
|
||||
|
||||
```text
|
||||
L4-USER-M-PROCESS 已按当前 Web/virtual HAL 数控仿真主线完成接入。
|
||||
```
|
||||
|
||||
当前完成态定义为:
|
||||
|
||||
```text
|
||||
apiName=real-browser-simulation-millturn-user-m-process-proof
|
||||
boundaryClass=L4-USER-M-PROCESS
|
||||
webSimulationReady=true
|
||||
nativeRuntimeRequired=false
|
||||
nativeRuntimeRequiredForWebSimulation=false
|
||||
processExecutionReady=false
|
||||
executionEnabled=false
|
||||
promotionAllowed=false
|
||||
```
|
||||
|
||||
含义:
|
||||
|
||||
```text
|
||||
1. Web/virtual HAL 已接入 millturn M429 -> M129 -> turn;
|
||||
2. Web/virtual HAL 已接入 millturn M428 -> M128 -> mill;
|
||||
3. 已覆盖 motion.switchkins-type、motion.analog-out-03、
|
||||
kinstype.is-0 / kinstype.is-1、ini.x.* / ini.z.* limit pins;
|
||||
4. 当前 Web 数控仿真不再依赖原生 LinuxCNC runtime;
|
||||
5. 仍不执行 arbitrary external user-M process;
|
||||
6. 仍不把 external user-M process 伪装成 native inventory promotion。
|
||||
```
|
||||
|
||||
因此,后续推进不应再把 `blocked_existing_linuxcnc_runtime` 或本机已有
|
||||
LinuxCNC 进程占用作为 Web 仿真完成阻塞。
|
||||
|
||||
二、下一步主任务
|
||||
|
||||
```text
|
||||
重新评估当前剩余 77 个 SKIP 中,哪些是真正可 promotion 的 main-program row。
|
||||
```
|
||||
|
||||
必须继续保持的边界:
|
||||
|
||||
```text
|
||||
ASSET-ONLY 不能伪装成主程序 PASS;
|
||||
NON_MAIN_CLASS 不能伪装成主程序 PASS;
|
||||
UPSTREAM-DEMO 不能伪装成主程序 PASS;
|
||||
external user-M arbitrary process execution 不能伪装成 Web 已执行;
|
||||
promotion_allowed 只能由明确 artifact / smoke / review 证据置 1。
|
||||
```
|
||||
|
||||
三、建议执行顺序
|
||||
|
||||
1. 重新生成并读取当前 sim-config inventory artifact。
|
||||
|
||||
优先查看:
|
||||
|
||||
```text
|
||||
wasm-port/build/wasm/sim-configs-inventory/summary.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/skip-summary.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/remaining-skip-main-program-promotion-audit.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-promotion-readiness.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-promotion-blockers.tsv
|
||||
```
|
||||
|
||||
推荐命令:
|
||||
|
||||
```bash
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
```
|
||||
|
||||
2. 先按分类复核 77 个 SKIP。
|
||||
|
||||
当前预期分类仍应是:
|
||||
|
||||
```text
|
||||
ASSET-ONLY=65
|
||||
L4-USER-M-PROCESS=1
|
||||
NON_MAIN_CLASS=10
|
||||
UPSTREAM-DEMO=1
|
||||
```
|
||||
|
||||
复核目标不是降低数字,而是判断:
|
||||
|
||||
```text
|
||||
哪些 row 其实是可执行 main-program;
|
||||
哪些 row 只是资产、子程序、demo 或非主程序;
|
||||
哪些 row 已经具备 Web 仿真 proof 但不允许 inventory promotion;
|
||||
哪些 row 需要新增 Node/browser smoke 才能 promotion。
|
||||
```
|
||||
|
||||
3. 对 `remaining-skip-main-program-promotion-audit.tsv` 做逐 row 判断。
|
||||
|
||||
每个 row 至少记录:
|
||||
|
||||
```text
|
||||
path
|
||||
ini
|
||||
class
|
||||
skip_kind
|
||||
blocked_kind
|
||||
current_status
|
||||
promotion_allowed
|
||||
promotion_ready
|
||||
simulation_proof_status
|
||||
recommended_next_command
|
||||
```
|
||||
|
||||
判定规则:
|
||||
|
||||
```text
|
||||
promotion_allowed=1 且 promotion_ready=1:
|
||||
可以进入实际 promotion gate。
|
||||
|
||||
promotion_allowed=0:
|
||||
只能记录阻塞原因,不能强行 PASS。
|
||||
|
||||
class != main:
|
||||
不能作为 main-program promotion 对象。
|
||||
|
||||
skip_kind=ASSET-ONLY / NON_MAIN_CLASS / UPSTREAM-DEMO:
|
||||
默认保持 SKIP,除非有新的 LinuxCNC-owned 主程序证据。
|
||||
|
||||
skip_kind=L4-USER-M-PROCESS:
|
||||
Web 仿真 proof 已完成,但仍保持 external process execution disabled;
|
||||
不因 Web proof 自动变成 native inventory PASS。
|
||||
```
|
||||
|
||||
4. 若发现真正可 promotion 的 main-program row,按证据链推进。
|
||||
|
||||
最低证据链:
|
||||
|
||||
```text
|
||||
Node/WASM inventory execution proof
|
||||
browser smoke proof
|
||||
diagnostics artifact coverage
|
||||
source compliance proof
|
||||
manual promotion lock review
|
||||
```
|
||||
|
||||
不得只因为程序能被 JavaScript 模拟展示就 promotion。
|
||||
|
||||
四、首轮复核建议
|
||||
|
||||
优先检查:
|
||||
|
||||
```text
|
||||
1. remaining-skip-main-program-promotion-audit.tsv 中 class=main 的 row;
|
||||
2. promotion-candidates.tsv 中 candidate_kind=inventory-ready 的 row;
|
||||
3. runtime-boundary-promotion-readiness.tsv 中 promotion_ready=1 的 row;
|
||||
4. skip-summary.tsv 中 L4-USER-M-PROCESS 是否仍只剩 1;
|
||||
5. ASSET-ONLY / NON_MAIN_CLASS / UPSTREAM-DEMO 是否有误分类。
|
||||
```
|
||||
|
||||
如果 `promotion_allowed=1 rows = 0`,本轮结论应直接记录:
|
||||
|
||||
```text
|
||||
剩余 77 个 SKIP 暂无可直接 promotion 的 main-program row。
|
||||
下一步应继续补 artifact,而不是强行改 PASS。
|
||||
```
|
||||
|
||||
如果发现 `promotion_allowed=1` 的 row,则下一步应为:
|
||||
|
||||
```text
|
||||
1. 明确 row path / ini / gcode;
|
||||
2. 补 Node inventory proof;
|
||||
3. 补 browser smoke proof;
|
||||
4. 补 docs/tracker;
|
||||
5. 运行 release gate;
|
||||
6. 再更新 baseline。
|
||||
```
|
||||
|
||||
五、推荐验证命令
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
wasm-port/tests/sdk/node/verify_sdk_surface.sh
|
||||
wasm-port/tests/ui/node/verify_real_simulation_programs.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
```
|
||||
|
||||
必要时再运行:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/browser/verify_interp_browser.sh
|
||||
wasm-port/tests/host/verify_project_release_gate.sh
|
||||
```
|
||||
|
||||
六、下一轮工作目标
|
||||
|
||||
```text
|
||||
目标:
|
||||
完成“剩余 77 个 SKIP 哪些是真正可 promotion 的 main-program row”的复核。
|
||||
|
||||
输出:
|
||||
1. 当前 77 SKIP 分类确认;
|
||||
2. 可 promotion row 列表;
|
||||
3. 不可 promotion row 阻塞原因;
|
||||
4. 下一批应实现的 Node/browser smoke;
|
||||
5. 是否允许 baseline 从 PASS=82 / SKIP=77 继续变化的明确结论。
|
||||
```
|
||||
|
||||
七、当前行动建议
|
||||
|
||||
```text
|
||||
下一轮不要再继续纠缠 L4-USER-M-PROCESS 是否接入 Web 仿真。
|
||||
它已经完成当前 Web 仿真接入。
|
||||
|
||||
下一轮直接进入:
|
||||
remaining-skip-main-program-promotion-audit.tsv 逐 row 复核。
|
||||
```
|
||||
@@ -209,6 +209,7 @@ wasm-port/build/wasm/sim-configs-inventory/native-proof-alignment-summary.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-native-alignment-summary.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/native-runtime-probe-execution-plan.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/remaining-skip-main-program-promotion-audit.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/blocked-runtime-promotion-lock.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/next-boundary-worklist.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/next-boundary-recommendations.tsv
|
||||
@@ -240,6 +241,15 @@ ready while the Node inventory baseline remains unchanged, and
|
||||
`inventory-ready` skipped main-program rows that could affect the Node
|
||||
inventory baseline. The current direct inventory `promotion_allowed=1` count
|
||||
is zero: remaining skipped main rows are still hard-blocked by `L4-USER-M-PROCESS` or preserved as `UPSTREAM-DEMO`.
|
||||
`remaining-skip-main-program-promotion-audit.tsv` is the focused row-level
|
||||
audit for those skipped main-program rows. It currently records two rows,
|
||||
`axis/vismach/millturn/example.ngc` and
|
||||
`axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc`,
|
||||
and keeps both `promotion_allowed=0`; the millturn row now has a browser
|
||||
virtual HAL state-transition proof for `M429 -> M129` and `M428 -> M128`, but
|
||||
still lacks the LinuxCNC-owned native runtime pass and promotion-gate proof
|
||||
required to leave `L4-USER-M-PROCESS`. The upstream demo row remains
|
||||
non-promotable upstream demo evidence.
|
||||
The project release readiness artifact and URL workflow publish the same
|
||||
candidate artifact summary for callers: `evidence-ready=8`,
|
||||
`inventory-ready=2`, total candidates `10`, and `promotion_allowed=0`.
|
||||
@@ -404,10 +414,10 @@ coverage: every TSV emitted under
|
||||
`full-process-boundary-design.md`. New gate artifacts must therefore be
|
||||
documented before the inventory can pass.
|
||||
The Node coverage gate also requires the generated WASM inventory artifact list
|
||||
to remain the exact duplicate-free 59-entry baseline and the native generated
|
||||
to remain the exact duplicate-free 60-entry baseline and the native generated
|
||||
TSV token list to remain the exact duplicate-free 8-token baseline. It also
|
||||
requires the corresponding `boundary-phase-completion-summary.tsv`
|
||||
documentation-coverage counts to remain `57` and `8`, respectively. It also
|
||||
documentation-coverage counts to remain `60` and `8`, respectively. It also
|
||||
checks that the browser smoke source lists the same generated WASM and native
|
||||
artifact tokens and preserves the same fixed-count, duplicate-free,
|
||||
fetchability/header, documentation-missing, and completion count-parity guards
|
||||
|
||||
@@ -157,11 +157,28 @@ enabled with `ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1`, it starts the vendored
|
||||
`millturn.ini`, runs the LinuxCNC-owned Tcl `M128`/`M129` scripts, and verifies
|
||||
the resulting `ini.[xyz].*` HAL state targets before reporting
|
||||
`runtime_state_probe_passed`.
|
||||
If another LinuxCNC runtime is already active, the probe reports
|
||||
`millturn_user_m_runtime_probe_status=blocked_existing_linuxcnc_runtime` and
|
||||
`millturn_user_m_missing_requirements=exclusive_linuxcnc_runtime` instead of
|
||||
terminating or taking over that process graph. The current workstation has
|
||||
shown that state with an existing `linuxcncsvr -ini .../axis.ini` and
|
||||
`rtapi_app load tpmod`.
|
||||
|
||||
The artifact keeps `execution_enabled=0` and `promotion_allowed=0`. It is not
|
||||
a standalone Tcl/HAL executor and does not make `millturn` a Node/browser
|
||||
representative.
|
||||
|
||||
The Web/browser simulation side now has a separate source-derived virtual HAL
|
||||
proof for the same millturn state transitions. `runRealBrowserSimulation()`
|
||||
exposes `millturnUserMProcess` with `M429 -> M129` turn and `M428 -> M128`
|
||||
mill transitions, switchkins guard pins, and `ini.[xz].*` limit pin values.
|
||||
Node smoke and the real browser simulation page smoke assert that proof while
|
||||
keeping `webSimulationReady=true`, `nativeRuntimeRequired=false`,
|
||||
`processExecutionReady=false`, `executionEnabled=false`, and
|
||||
`promotionAllowed=false`. This completes the browser simulation proof without a
|
||||
native LinuxCNC runtime dependency. It still does not execute arbitrary external
|
||||
user-M processes or unlock inventory promotion.
|
||||
|
||||
`linuxcnc_millturn_user_m_boundary_probe` is the current native guard for this
|
||||
evidence. It reads the LinuxCNC source `millturn.ini`, `mcodes/M128`,
|
||||
`mcodes/M129`, and the `428remap`/`429remap` callers, then verifies the
|
||||
|
||||
@@ -47,12 +47,26 @@ execution_enabled=0
|
||||
promotion_allowed=0
|
||||
```
|
||||
|
||||
Manual `L4-USER-M-PROCESS` opt-in status on the current workstation:
|
||||
|
||||
```text
|
||||
ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_millturn_user_m_runtime.sh
|
||||
millturn_user_m_runtime_probe_status=blocked_existing_linuxcnc_runtime
|
||||
millturn_user_m_missing_requirements=exclusive_linuxcnc_runtime
|
||||
```
|
||||
|
||||
The conflicting runtime was an already-running `linuxcncsvr -ini .../axis.ini`
|
||||
plus `rtapi_app load tpmod`. The probe intentionally keeps the millturn row
|
||||
blocked instead of taking over that runtime. The browser/virtual HAL proof for
|
||||
`M429 -> M129` and `M428 -> M128` is complete in the simulation page smoke, but
|
||||
it is not a native runtime pass and does not unlock inventory promotion.
|
||||
|
||||
## Promotion blockers
|
||||
|
||||
Each blocked family currently has `promotion_ready=0`, `execution_enabled=0`,
|
||||
and `promotion_allowed=0`.
|
||||
|
||||
For `L4-USER-M-PROCESS` and `L4-TOOL-DB`, the current blocker keys are:
|
||||
For `L4-USER-M-PROCESS` and `L4-TOOL-DB`, the current generated blocker keys are:
|
||||
|
||||
```text
|
||||
native_runtime_probe_not_passed
|
||||
@@ -87,6 +101,9 @@ For rows that still lack native evidence, the next unblock action is to provide
|
||||
the missing host runtime requirements for the relevant family. Providing host
|
||||
commands is not enough to promote a row; it only allows the next opt-in probe
|
||||
gate to be considered.
|
||||
For `L4-USER-M-PROCESS` on this workstation, the remaining native requirement
|
||||
is stricter than command availability: the millturn probe needs an exclusive
|
||||
LinuxCNC runtime with no existing `linuxcncsvr`/`rtapi_app` process graph.
|
||||
|
||||
## Opt-in probe dispatch
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ Use this checklist when adding each LinuxCNC upstream test:
|
||||
| `tests/uspace/spawnv-root` | Blocked | Sudo-installed userspace HAL component and `spawnv` root behavior. | Keep upstream-only unless HAL component install/runtime support is added. |
|
||||
| `configs/sim/*/remap_subs/*.ngc` browser main-program inventory | Blocked | These files are remap/subroutine assets, not standalone browser main-program targets. | Keep validating them through native remap parse/execute paths and explicit five-axis/runtime probes instead of widening browser `runSimConfigProgram()` blindly. |
|
||||
| `configs/sim/axis/db_demo/base.ngc` Layer 4 inventory | Blocked | `[EMCIO]DB_PROGRAM = ./db_nonran.py` requires LinuxCNC task/tooldata DB process startup, command/reply protocol, and database state behavior, not standalone interpreter file execution. | Keep as `L4-TOOL-DB` until a LinuxCNC-owned tool database runtime boundary exists and proves lookup/update behavior across native and WASM. `blocked-dependency-summary.tsv` records `taskclass.cc`, `tooldata_db.cc`, `tooldata_common.cc`, and `configs/sim/axis/db_demo/db.py` as the owner set plus source-derived `tool_db_protocol_evidence` for the `v2.1` handshake, `g`/`FINI` get-all, and `l`/`u`/`p` notification protocol. |
|
||||
| `configs/sim/axis/vismach/millturn/example.ngc` Layer 4 inventory | Blocked | The remap execution chain calls external `USER_M_PATH` process codes `M128` and `M129`, with HAL/HALUI/UI process declarations in `millturn.ini`. | Keep as `L4-USER-M-PROCESS`; do not promote until the M128/M129 LinuxCNC-owned state boundary proves the kinstype guard and `ini.[xyz].*` HAL pin updates. `blocked-dependency-summary.tsv` records the `mcodes/M128` and `mcodes/M129` Tcl scripts plus source-derived `user_m_process_effects` for Tcl/HAL runtime use, kinstype guards, and `ini.[xyz]` HAL pin side effects. |
|
||||
| `configs/sim/axis/vismach/millturn/example.ngc` Layer 4 inventory | Blocked | The remap execution chain calls external `USER_M_PATH` process codes `M128` and `M129`, with HAL/HALUI/UI process declarations in `millturn.ini`. | Keep as `L4-USER-M-PROCESS`; the Web/virtual HAL simulation proof now covers `M429 -> M129` turn and `M428 -> M128` mill state transitions, including switchkins guard pins and `ini.[xz].*` limit pins in Node and real browser page smoke. Do not promote until the opt-in native LinuxCNC runtime probe can run with exclusive `linuxcncsvr`/`rtapi_app` ownership and report `runtime_state_probe_passed`; the current workstation reports `blocked_existing_linuxcnc_runtime` / `exclusive_linuxcnc_runtime`. `blocked-dependency-summary.tsv` records the `mcodes/M128` and `mcodes/M129` Tcl scripts plus source-derived `user_m_process_effects` for Tcl/HAL runtime use, kinstype guards, and `ini.[xyz]` HAL pin side effects. |
|
||||
| `configs/sim/gmoccapy/*` Layer 4 full inventory | Blocked | Python remap runtime boundary is present in native LinuxCNC `rs274`, but not yet intentionally exposed for Node/browser full inventory. | Keep native inventory coverage as source-of-truth; add one representative Node/browser class sample only after the Python remap boundary is deliberately designed. Python module and prolog/epilog ownership is inventoried in `blocked-dependency-summary.tsv`; LinuxCNC runtime ownership is recorded as `python_runtime_evidence` in the Python boundary/family summaries. |
|
||||
| `configs/sim/axis/laser/*` Layer 4 full inventory | Blocked | Python remap runtime boundary is present in native LinuxCNC `rs274`, but not yet intentionally exposed for Node/browser full inventory. | Keep native inventory coverage as source-of-truth; add one representative Node/browser class sample only after the Python remap boundary is deliberately designed. Python module and remap ownership is inventoried in `blocked-dependency-summary.tsv`; LinuxCNC runtime ownership is recorded as `python_runtime_evidence` in the Python boundary/family summaries. |
|
||||
| `configs/sim/axis/remap/*/nc_files/*.ngc` Layer 4 full inventory | Blocked | Native LinuxCNC `rs274` coverage exists, but these demo main programs depend on Python remaps, Python prolog/epilog callbacks, or Python-backed queue/tool-change handlers. | Keep native inventory coverage as source-of-truth; do not reduce Python remap demos to missing vendored files or browser standalone execution until the Python remap boundary is deliberately designed. Dependency ownership is inventoried in `blocked-dependency-summary.tsv`; LinuxCNC runtime ownership is recorded as `python_runtime_evidence` in the Python boundary/family summaries. |
|
||||
|
||||
@@ -460,12 +460,19 @@ Boundary decisions for the current hard blocks:
|
||||
to change INI axis limit HAL pins after the kinematics switch. The current
|
||||
LinuxCNC owner is therefore task user-M process dispatch plus HALUI/MDI and
|
||||
the config-owned Tcl process scripts, not the standalone interpreter. Do not
|
||||
promote this row until the boundary design in
|
||||
`docs/full-process-boundary-design.md` has a deterministic state proof for
|
||||
these HAL pin updates across native, Node, and browser. The Node inventory
|
||||
guard requires this row to stay non-`REP` in Node and browser while it remains
|
||||
`L4-USER-M-PROCESS`, and the generated inventory summary must keep it as
|
||||
`SKIP` with that reason.
|
||||
promote this row until the native runtime probe can run in an exclusive
|
||||
LinuxCNC process graph and report `runtime_state_probe_passed`. The current
|
||||
Web/virtual HAL simulation proof is complete for `M429 -> M129` turn and
|
||||
`M428 -> M128` mill state transitions, and the real browser simulation page
|
||||
smoke verifies the switchkins guard pins plus `ini.[xz].*` limit pins through
|
||||
`linuxCncRealSimulationState.millturnUserMProcess`. That proof is simulation
|
||||
evidence only: it keeps `processExecutionReady=false`, `executionEnabled=0`,
|
||||
and `promotion_allowed=0`. The latest opt-in native probe on this workstation
|
||||
reports `blocked_existing_linuxcnc_runtime` with
|
||||
`exclusive_linuxcnc_runtime` because an existing `linuxcncsvr`/`rtapi_app`
|
||||
runtime is already active. The Node inventory guard requires this row to stay
|
||||
non-`REP` in Node and browser while it remains `L4-USER-M-PROCESS`, and the
|
||||
generated inventory summary must keep it as `SKIP` with that reason.
|
||||
- `axis/db_demo/base.ngc` remains `L4-TOOL-DB`. The source INI declares
|
||||
`[EMCIO]DB_PROGRAM = ./db_nonran.py`, so standalone interpreter execution
|
||||
would miss LinuxCNC tool-database process startup and protocol/state
|
||||
|
||||
@@ -43,6 +43,7 @@ The generated artifact baseline is:
|
||||
- `build/wasm/sim-configs-inventory/full-process-boundary-summary.tsv`
|
||||
- `build/wasm/sim-configs-inventory/python-remap-boundary-summary.tsv`
|
||||
- `build/wasm/sim-configs-inventory/promotion-candidates.tsv`
|
||||
- `build/wasm/sim-configs-inventory/remaining-skip-main-program-promotion-audit.tsv`
|
||||
- `build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv`
|
||||
- `build/wasm/sim-configs-inventory/runtime-boundary-promotion-blockers.tsv`
|
||||
- `build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv`
|
||||
@@ -55,6 +56,13 @@ Every row keeps `promotion_allowed=0`, so the inventory baseline is now
|
||||
`82/82/77/0`. The companion `evidence-expansion-candidates.tsv` artifact has
|
||||
14 rows queued for future browser diagnostics binding, also with
|
||||
`promotion_allowed=0`.
|
||||
`remaining-skip-main-program-promotion-audit.tsv` is the focused audit for the
|
||||
2 skipped main-program rows left inside the 77 skips. It keeps both
|
||||
`promotion_allowed=0`: `axis/vismach/millturn/example.ngc` is still blocked on
|
||||
incomplete user-M process native/inventory promotion proof, even though the
|
||||
browser simulation state now includes a source-derived virtual HAL proof for
|
||||
the `M429 -> M129` turn and `M428 -> M128` mill state transitions. The TWP
|
||||
`incremental_repetition_g533.ngc` row remains preserved upstream demo evidence.
|
||||
|
||||
SDK and workflow-overview release URL summaries expose the current
|
||||
`evidence-ready` layer as dashboard fields: `evidence-ready-candidate-rows=8`,
|
||||
@@ -73,7 +81,7 @@ underscore-safe metric names. Treat these as the same baseline:
|
||||
| Artifact reason | Shell metric suffix | Count | Release decision |
|
||||
| --- | --- | ---: | --- |
|
||||
| `ASSET-ONLY` | `ASSET_ONLY` | 65 | Subroutine/support asset; not a standalone browser main-program target. |
|
||||
| `L4-USER-M-PROCESS` | `L4_USER_M_PROCESS` | 1 | Keep blocked until a LinuxCNC-owned external user-M process state proof exists. |
|
||||
| `L4-USER-M-PROCESS` | `L4_USER_M_PROCESS` | 1 | Browser virtual HAL state proof exists for millturn M128/M129, but keep inventory promotion blocked until the LinuxCNC-owned native runtime probe and promotion gates pass. |
|
||||
| `NON_MAIN_CLASS` | `NON_MAIN_CLASS` | 10 | Covered by class representative or macro/load policy, not direct standalone promotion. |
|
||||
| `UPSTREAM-DEMO` | `UPSTREAM_DEMO` | 1 | Preserved upstream demo edge; do not force pass. |
|
||||
|
||||
|
||||
@@ -72,6 +72,8 @@ Current baseline:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/next-boundary-recommendations.tsv`
|
||||
- Node promotion candidate artifact:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv`
|
||||
- Node remaining skipped main-program promotion audit artifact:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/remaining-skip-main-program-promotion-audit.tsv`
|
||||
- Node native evidence acceptance gate artifact:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-native-evidence-acceptance-gate.tsv`
|
||||
- Node promotion blocker summary artifact:
|
||||
@@ -382,7 +384,7 @@ Legend:
|
||||
`build/wasm/sim-configs-inventory/` must be referenced by the current
|
||||
compatibility, matrix, or full-process boundary docs, so generated gate
|
||||
artifacts cannot silently appear without review text. The generated WASM
|
||||
inventory artifact list must remain the exact duplicate-free 59-entry
|
||||
inventory artifact list must remain the exact duplicate-free 60-entry
|
||||
baseline, and every token plus the fixed-count, duplicate-free, and
|
||||
fetchability guards must also be listed by browser smoke.
|
||||
- The same inventory check covers native generated TSV artifacts under
|
||||
@@ -395,7 +397,7 @@ Legend:
|
||||
compatibility, matrix, and full-process boundary docs and checking the
|
||||
generated WASM inventory artifact names plus native TSV tokens against the
|
||||
same completion-summary counts. The Node coverage gate also locks the two
|
||||
documentation-coverage completion counts to the current `57` WASM inventory
|
||||
documentation-coverage completion counts to the current `60` WASM inventory
|
||||
artifacts and `8` native TSV artifacts. It verifies that browser smoke
|
||||
executes the artifact documentation coverage helper, calls the completion
|
||||
count-parity helper with the generated completion rows, fetches and joins the
|
||||
|
||||
@@ -3568,10 +3568,12 @@ function createMillturnUserMStateTargetRows(boundaryCase) {
|
||||
sourceSection: target.sourceSection,
|
||||
sourceField: target.sourceField,
|
||||
expectedValue: target.value,
|
||||
webSimulationReady: true,
|
||||
nativeRuntimeRequiredForWebSimulation: false,
|
||||
executionEnabled: false,
|
||||
promotionAllowed: false,
|
||||
proofStatus: "pending_native_runtime_probe",
|
||||
notes: "source_derived_virtual_hal_state_target_no_external_process_execution",
|
||||
proofStatus: "web_virtual_hal_simulation_ready",
|
||||
notes: "source_derived_virtual_hal_state_target_no_native_linuxcnc_runtime",
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -3610,12 +3612,14 @@ export function createVirtualHalMillturnUserMProcessBoundaryReport(options = {})
|
||||
sourceFiles,
|
||||
manifestSourceFiles,
|
||||
sourceDerived: sourceFiles.length > 0,
|
||||
webSimulationReady: true,
|
||||
nativeRuntimeRequiredForWebSimulation: false,
|
||||
executionEnabled: false,
|
||||
promotionAllowed: false,
|
||||
proofStatus: "pending_native_runtime_probe",
|
||||
proofStatus: "web_virtual_hal_simulation_ready",
|
||||
nativeProbe: boundary.nativeProbe,
|
||||
complete: sourceFiles.length > 0 && missingManifestFiles.length === 0,
|
||||
notes: "controlled_virtual_hal_state_boundary_keep_user_m_process_blocked",
|
||||
notes: "controlled_virtual_hal_state_boundary_no_native_linuxcnc_runtime_required",
|
||||
};
|
||||
});
|
||||
const complete = rows.length > 0 &&
|
||||
@@ -3628,12 +3632,15 @@ export function createVirtualHalMillturnUserMProcessBoundaryReport(options = {})
|
||||
phase: complete ? "simulation-boundary-ready" : "blocked",
|
||||
complete,
|
||||
webSimulationSatisfied: complete,
|
||||
webSimulationReady: complete,
|
||||
boundaryClass: boundary.boundaryClass,
|
||||
path: boundary.path,
|
||||
ini: boundary.ini,
|
||||
requiredRuntime: boundary.requiredRuntime,
|
||||
nativeProbe: boundary.nativeProbe,
|
||||
nativeRuntimeRequired: true,
|
||||
nativeRuntimeRequired: false,
|
||||
nativeRuntimeRequiredForWebSimulation: false,
|
||||
nativeRuntimeRequiredForInventoryPromotion: false,
|
||||
processExecutionReady: false,
|
||||
executionEnabled: false,
|
||||
promotionAllowed: false,
|
||||
@@ -3650,7 +3657,9 @@ export function createVirtualHalMillturnUserMProcessBoundaryReport(options = {})
|
||||
{ id: "boundary", label: "User-M boundary", value: boundary.boundaryClass },
|
||||
{ id: "cases", label: "M-code cases", value: rows.map((row) => `${row.remapCode}->${row.userMCode}`).join(", ") },
|
||||
{ id: "state-targets", label: "HAL state targets", value: `${rows.reduce((sum, row) => sum + row.stateTargetCount, 0)}` },
|
||||
{ id: "execution", label: "External process execution", value: "locked" },
|
||||
{ id: "web-simulation", label: "Web simulation", value: complete ? "ready" : "blocked" },
|
||||
{ id: "native-runtime", label: "Native LinuxCNC runtime", value: "not required" },
|
||||
{ id: "execution", label: "External process execution", value: "disabled" },
|
||||
{ id: "promotion", label: "Promotion allowed", value: "0" },
|
||||
],
|
||||
};
|
||||
@@ -3694,6 +3703,8 @@ export function applyVirtualHalMillturnUserMProcessState(halState = createVirtua
|
||||
userMCode: boundaryCase.userMCode,
|
||||
remapCode: boundaryCase.remapCode,
|
||||
stateMode: boundaryCase.stateMode,
|
||||
webSimulationReady: true,
|
||||
nativeRuntimeRequiredForWebSimulation: false,
|
||||
executionEnabled: false,
|
||||
promotionAllowed: false,
|
||||
ok: true,
|
||||
@@ -3715,6 +3726,9 @@ export function applyVirtualHalMillturnUserMProcessState(halState = createVirtua
|
||||
activeG5x: boundaryCase.activeG5x,
|
||||
workOffsetPocket: boundaryCase.workOffsetPocket,
|
||||
appliedPins: updates.map((update) => update.name),
|
||||
webSimulationReady: true,
|
||||
nativeRuntimeRequired: false,
|
||||
nativeRuntimeRequiredForWebSimulation: false,
|
||||
executionEnabled: false,
|
||||
promotionAllowed: false,
|
||||
processExecutionReady: false,
|
||||
|
||||
@@ -708,8 +708,11 @@ function isVirtualHalMillturnUserMProcessBoundaryReady(report) {
|
||||
return reportObject.apiName === "linuxcnc-wasm-virtual-hal-millturn-user-m-process-boundary-report" &&
|
||||
reportObject.complete === true &&
|
||||
reportObject.webSimulationSatisfied === true &&
|
||||
reportObject.webSimulationReady === true &&
|
||||
reportObject.boundaryClass === "L4-USER-M-PROCESS" &&
|
||||
reportObject.path === "axis/vismach/millturn/example.ngc" &&
|
||||
reportObject.nativeRuntimeRequired === false &&
|
||||
reportObject.nativeRuntimeRequiredForWebSimulation === false &&
|
||||
reportObject.executionEnabled === false &&
|
||||
reportObject.promotionAllowed === false &&
|
||||
reportObject.processExecutionReady === false &&
|
||||
@@ -721,6 +724,8 @@ function isVirtualHalMillturnUserMProcessBoundaryReady(report) {
|
||||
row.remapCode === "M428" &&
|
||||
row.stateMode === "mill" &&
|
||||
row.stateTargetCount === 12 &&
|
||||
row.webSimulationReady === true &&
|
||||
row.nativeRuntimeRequiredForWebSimulation === false &&
|
||||
row.executionEnabled === false &&
|
||||
row.promotionAllowed === false
|
||||
) &&
|
||||
@@ -729,6 +734,8 @@ function isVirtualHalMillturnUserMProcessBoundaryReady(report) {
|
||||
row.remapCode === "M429" &&
|
||||
row.stateMode === "turn" &&
|
||||
row.stateTargetCount === 12 &&
|
||||
row.webSimulationReady === true &&
|
||||
row.nativeRuntimeRequiredForWebSimulation === false &&
|
||||
row.executionEnabled === false &&
|
||||
row.promotionAllowed === false
|
||||
);
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import {
|
||||
PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE,
|
||||
VIRTUAL_HAL_MILLTURN_USER_M_PROCESS_BOUNDARY,
|
||||
applyVirtualHalMillturnUserMProcessState,
|
||||
createLinuxCncInterpSdk,
|
||||
createLinuxCncPythonRemapRuntimePort,
|
||||
createVirtualHalState,
|
||||
readVirtualHalPin,
|
||||
} from "../../sdk/src/index.js";
|
||||
import * as THREE from "./vendor/three/three.module.js";
|
||||
import {
|
||||
@@ -197,6 +201,70 @@ export function createSimulationSummary({ programText, resultText, motion }) {
|
||||
};
|
||||
}
|
||||
|
||||
function createMillturnUserMProcessSimulationProof() {
|
||||
const initialState = createVirtualHalState();
|
||||
const turn = applyVirtualHalMillturnUserMProcessState(initialState, "M129", {
|
||||
manifestEntries: VIRTUAL_HAL_MILLTURN_USER_M_PROCESS_BOUNDARY.sourceFiles,
|
||||
});
|
||||
const mill = applyVirtualHalMillturnUserMProcessState(turn.state, "M128", {
|
||||
manifestEntries: VIRTUAL_HAL_MILLTURN_USER_M_PROCESS_BOUNDARY.sourceFiles,
|
||||
});
|
||||
const readPins = (state, names) => Object.fromEntries(
|
||||
names.map((name) => [name, readVirtualHalPin(state, name)?.value ?? null]),
|
||||
);
|
||||
|
||||
return {
|
||||
apiName: "real-browser-simulation-millturn-user-m-process-proof",
|
||||
proofVersion: 1,
|
||||
boundaryClass: VIRTUAL_HAL_MILLTURN_USER_M_PROCESS_BOUNDARY.boundaryClass,
|
||||
path: VIRTUAL_HAL_MILLTURN_USER_M_PROCESS_BOUNDARY.path,
|
||||
ini: VIRTUAL_HAL_MILLTURN_USER_M_PROCESS_BOUNDARY.ini,
|
||||
phase: turn.ok && mill.ok ? "browser-virtual-hal-state-proof-ready" : "blocked",
|
||||
ready: turn.ok === true && mill.ok === true,
|
||||
webSimulationReady: turn.ok === true && mill.ok === true,
|
||||
nativeRuntimeRequired: false,
|
||||
nativeRuntimeRequiredForWebSimulation: false,
|
||||
processExecutionReady: false,
|
||||
executionEnabled: false,
|
||||
promotionAllowed: false,
|
||||
transitionOrder: [turn.remapCode, mill.remapCode],
|
||||
turn: {
|
||||
userMCode: turn.userMCode,
|
||||
remapCode: turn.remapCode,
|
||||
stateMode: turn.stateMode,
|
||||
switchkinsTarget: turn.switchkinsTarget,
|
||||
pins: readPins(turn.state, [
|
||||
"motion.switchkins-type",
|
||||
"motion.analog-out-03",
|
||||
"kinstype.is-0",
|
||||
"kinstype.is-1",
|
||||
"ini.x.min_limit",
|
||||
"ini.x.max_limit",
|
||||
"ini.z.min_limit",
|
||||
"ini.z.max_limit",
|
||||
]),
|
||||
},
|
||||
mill: {
|
||||
userMCode: mill.userMCode,
|
||||
remapCode: mill.remapCode,
|
||||
stateMode: mill.stateMode,
|
||||
switchkinsTarget: mill.switchkinsTarget,
|
||||
pins: readPins(mill.state, [
|
||||
"motion.switchkins-type",
|
||||
"motion.analog-out-03",
|
||||
"kinstype.is-0",
|
||||
"kinstype.is-1",
|
||||
"ini.x.min_limit",
|
||||
"ini.x.max_limit",
|
||||
"ini.z.min_limit",
|
||||
"ini.z.max_limit",
|
||||
]),
|
||||
},
|
||||
report: mill.report,
|
||||
notes: "browser_virtual_hal_state_transition_proof_ready_no_native_linuxcnc_runtime_required",
|
||||
};
|
||||
}
|
||||
|
||||
export async function createPythonRemapSimulationRuntimeStatus({
|
||||
runtimeMode = "contract-only",
|
||||
runtimeAdapter = null,
|
||||
@@ -1389,6 +1457,7 @@ export async function runRealBrowserSimulation({
|
||||
const modal = createModalState(resultText);
|
||||
const machineStatus = createMachineStatusState(resultText);
|
||||
const pythonRemapRuntime = await createPythonRemapSimulationRuntimeStatus();
|
||||
const millturnUserMProcess = createMillturnUserMProcessSimulationProof();
|
||||
const state = {
|
||||
apiName: "real-browser-simulation-state",
|
||||
stateVersion: 1,
|
||||
@@ -1408,6 +1477,7 @@ export async function runRealBrowserSimulation({
|
||||
modal,
|
||||
machineStatus,
|
||||
pythonRemapRuntime,
|
||||
millturnUserMProcess,
|
||||
execution: {
|
||||
mode: iniPath ? "linuxcnc-wasm-with-ini" : "linuxcnc-wasm",
|
||||
iniPath,
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
"next-boundary-recommendations.tsv",
|
||||
"next-boundary-worklist.tsv",
|
||||
"promotion-candidates.tsv",
|
||||
"remaining-skip-main-program-promotion-audit.tsv",
|
||||
"python-remap-browser-row-proof.tsv",
|
||||
"python-remap-bulk-promotion-plan.tsv",
|
||||
"python-remap-boundary-summary.tsv",
|
||||
@@ -155,7 +156,7 @@
|
||||
"build/native/source-probes.tsv",
|
||||
];
|
||||
|
||||
if (wasmArtifactNames.length !== 59 || new Set(wasmArtifactNames).size !== wasmArtifactNames.length) {
|
||||
if (wasmArtifactNames.length !== 60 || new Set(wasmArtifactNames).size !== wasmArtifactNames.length) {
|
||||
throw new Error("browser_generated_artifact_documentation_coverage: WASM artifact list drift");
|
||||
}
|
||||
if (nativeArtifactTokens.length !== 8 || new Set(nativeArtifactTokens).size !== nativeArtifactTokens.length) {
|
||||
|
||||
@@ -127,6 +127,57 @@
|
||||
if (doc.body.dataset.simulationProgramId !== state.program?.id) {
|
||||
throw new Error(`simulation body program id drift for ${state.program?.id}`);
|
||||
}
|
||||
assertMillturnUserMProcessProof(state);
|
||||
}
|
||||
|
||||
function assertMillturnUserMProcessProof(state) {
|
||||
const proof = state.millturnUserMProcess;
|
||||
if (
|
||||
proof?.apiName !== "real-browser-simulation-millturn-user-m-process-proof" ||
|
||||
proof.ready !== true ||
|
||||
proof.webSimulationReady !== true ||
|
||||
proof.boundaryClass !== "L4-USER-M-PROCESS" ||
|
||||
proof.path !== "axis/vismach/millturn/example.ngc" ||
|
||||
proof.ini !== "axis/vismach/millturn/millturn.ini" ||
|
||||
proof.nativeRuntimeRequired !== false ||
|
||||
proof.nativeRuntimeRequiredForWebSimulation !== false ||
|
||||
proof.processExecutionReady !== false ||
|
||||
proof.executionEnabled !== false ||
|
||||
proof.promotionAllowed !== false ||
|
||||
proof.transitionOrder?.join(",") !== "M429,M428"
|
||||
) {
|
||||
throw new Error(`simulation millturn user-M process proof drift: ${JSON.stringify(proof)}`);
|
||||
}
|
||||
if (
|
||||
proof.turn?.userMCode !== "M129" ||
|
||||
proof.turn?.remapCode !== "M429" ||
|
||||
proof.turn?.stateMode !== "turn" ||
|
||||
proof.turn?.pins?.["motion.switchkins-type"] !== 1 ||
|
||||
proof.turn?.pins?.["motion.analog-out-03"] !== 1 ||
|
||||
proof.turn?.pins?.["kinstype.is-0"] !== 0 ||
|
||||
proof.turn?.pins?.["kinstype.is-1"] !== 1 ||
|
||||
proof.turn?.pins?.["ini.x.min_limit"] !== -240 ||
|
||||
proof.turn?.pins?.["ini.x.max_limit"] !== 0 ||
|
||||
proof.turn?.pins?.["ini.z.min_limit"] !== -300 ||
|
||||
proof.turn?.pins?.["ini.z.max_limit"] !== 300
|
||||
) {
|
||||
throw new Error(`simulation millturn user-M turn proof drift: ${JSON.stringify(proof.turn)}`);
|
||||
}
|
||||
if (
|
||||
proof.mill?.userMCode !== "M128" ||
|
||||
proof.mill?.remapCode !== "M428" ||
|
||||
proof.mill?.stateMode !== "mill" ||
|
||||
proof.mill?.pins?.["motion.switchkins-type"] !== 0 ||
|
||||
proof.mill?.pins?.["motion.analog-out-03"] !== 0 ||
|
||||
proof.mill?.pins?.["kinstype.is-0"] !== 1 ||
|
||||
proof.mill?.pins?.["kinstype.is-1"] !== 0 ||
|
||||
proof.mill?.pins?.["ini.x.min_limit"] !== -300 ||
|
||||
proof.mill?.pins?.["ini.x.max_limit"] !== 300 ||
|
||||
proof.mill?.pins?.["ini.z.min_limit"] !== -240 ||
|
||||
proof.mill?.pins?.["ini.z.max_limit"] !== 0
|
||||
) {
|
||||
throw new Error(`simulation millturn user-M mill proof drift: ${JSON.stringify(proof.mill)}`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertAxisShell(doc) {
|
||||
|
||||
@@ -11,12 +11,23 @@ LINUXCNC_STDERR="$RUN_DIR/linuxcnc.stderr.log"
|
||||
|
||||
required_commands=(tclsh halrun halcmd linuxcnc)
|
||||
missing_commands=()
|
||||
declare -A command_paths=()
|
||||
LINUXCNC_PID=""
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "$LINUXCNC_PID" ]]; then
|
||||
kill "$LINUXCNC_PID" 2>/dev/null || true
|
||||
kill -TERM "-$LINUXCNC_PID" 2>/dev/null || kill "$LINUXCNC_PID" 2>/dev/null || true
|
||||
for _ in $(seq 1 20); do
|
||||
if ! kill -0 "$LINUXCNC_PID" 2>/dev/null; then
|
||||
wait "$LINUXCNC_PID" 2>/dev/null || true
|
||||
LINUXCNC_PID=""
|
||||
return
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
kill -KILL "-$LINUXCNC_PID" 2>/dev/null || kill -KILL "$LINUXCNC_PID" 2>/dev/null || true
|
||||
wait "$LINUXCNC_PID" 2>/dev/null || true
|
||||
LINUXCNC_PID=""
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
@@ -49,10 +60,22 @@ print_kv() {
|
||||
printf '%s=%s\n' "$1" "$2"
|
||||
}
|
||||
|
||||
existing_linuxcnc_runtime_conflicts() {
|
||||
ps -eo pid=,args= | awk '
|
||||
/linuxcncsvr -ini/ || /rtapi_app load/ {
|
||||
if ($0 !~ /awk /) {
|
||||
gsub(/^[[:space:]]+/, "", $0)
|
||||
print
|
||||
}
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
runtime_requirements=()
|
||||
for command_name in "${required_commands[@]}"; do
|
||||
path="$(command_path "$command_name")"
|
||||
if [[ -n "$path" ]]; then
|
||||
command_paths["$command_name"]="$path"
|
||||
runtime_requirements+=("$command_name:1")
|
||||
print_kv "millturn_user_m_runtime_${command_name}_path" "$path"
|
||||
else
|
||||
@@ -108,6 +131,24 @@ if [[ "${ENABLE_MILLTURN_USER_M_RUNTIME_PROBE:-0}" != "1" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
existing_runtime_conflicts="$(existing_linuxcnc_runtime_conflicts)"
|
||||
if [[ -n "$existing_runtime_conflicts" ]]; then
|
||||
print_kv millturn_user_m_runtime_ready 0
|
||||
print_kv millturn_user_m_missing_requirements "exclusive_linuxcnc_runtime"
|
||||
print_kv millturn_user_m_existing_runtime_conflicts "$(printf '%s\n' "$existing_runtime_conflicts" | paste -sd ';' -)"
|
||||
print_kv millturn_user_m_runtime_probe_status "blocked_existing_linuxcnc_runtime"
|
||||
print_kv millturn_user_m_runtime_probe_note "existing_linuxcnc_runtime_conflict_keep_user_m_process_blocked"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
HALCMD_BIN="${command_paths[halcmd]}"
|
||||
LINUXCNC_BIN="${command_paths[linuxcnc]}"
|
||||
|
||||
export PATH="$LINUXCNC_ROOT/bin:$LINUXCNC_ROOT/scripts:$LINUXCNC_ROOT/tcl:$PATH"
|
||||
export LD_LIBRARY_PATH="$LINUXCNC_ROOT/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
export PYTHONPATH="$LINUXCNC_ROOT/lib/python${PYTHONPATH:+:$PYTHONPATH}"
|
||||
export TCLLIBPATH="$LINUXCNC_ROOT/tcl${TCLLIBPATH:+ $TCLLIBPATH}"
|
||||
|
||||
mkdir -p "$RUN_DIR"
|
||||
|
||||
wait_for_hal_pin() {
|
||||
@@ -115,7 +156,7 @@ wait_for_hal_pin() {
|
||||
local limit="${MILLTURN_USER_M_RUNTIME_WAIT_SECONDS:-30}"
|
||||
local end=$((SECONDS + limit))
|
||||
while (( SECONDS < end )); do
|
||||
if halcmd getp "$pin" >/dev/null 2>&1; then
|
||||
if "$HALCMD_BIN" getp "$pin" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
sleep 0.25
|
||||
@@ -124,7 +165,7 @@ wait_for_hal_pin() {
|
||||
}
|
||||
|
||||
read_hal_pin() {
|
||||
halcmd getp "$1" 2>/dev/null | tr -d '[:space:]'
|
||||
"$HALCMD_BIN" getp "$1" 2>/dev/null | tr -d '[:space:]'
|
||||
}
|
||||
|
||||
expect_hal_pin() {
|
||||
@@ -140,9 +181,9 @@ expect_hal_pin() {
|
||||
set_switchkins_target() {
|
||||
local target="$1"
|
||||
local guard_pin="kinstype.is-$target"
|
||||
if ! halcmd setp motion.switchkins-type "$target" >/dev/null 2>&1; then
|
||||
halcmd unlinkp motion.switchkins-type >/dev/null 2>&1 || true
|
||||
halcmd setp motion.switchkins-type "$target"
|
||||
if ! "$HALCMD_BIN" setp motion.switchkins-type "$target" >/dev/null 2>&1; then
|
||||
"$HALCMD_BIN" unlinkp motion.switchkins-type >/dev/null 2>&1 || true
|
||||
"$HALCMD_BIN" setp motion.switchkins-type "$target"
|
||||
fi
|
||||
for _ in $(seq 1 40); do
|
||||
if [[ "$(read_hal_pin "$guard_pin")" =~ ^(TRUE|1)$ ]]; then
|
||||
@@ -195,7 +236,7 @@ print_kv millturn_user_m_runtime_probe_ini "$INI_FILE"
|
||||
print_kv millturn_user_m_runtime_probe_linuxcnc_stdout "$LINUXCNC_STDOUT"
|
||||
print_kv millturn_user_m_runtime_probe_linuxcnc_stderr "$LINUXCNC_STDERR"
|
||||
|
||||
linuxcnc -r "$INI_FILE" >"$LINUXCNC_STDOUT" 2>"$LINUXCNC_STDERR" &
|
||||
setsid "$LINUXCNC_BIN" -r "$INI_FILE" >"$LINUXCNC_STDOUT" 2>"$LINUXCNC_STDERR" &
|
||||
LINUXCNC_PID="$!"
|
||||
|
||||
if ! wait_for_hal_pin ini.x.min_limit || ! wait_for_hal_pin motion.switchkins-type; then
|
||||
|
||||
@@ -252,18 +252,22 @@ check_native_runtime_probe_summary_row() {
|
||||
echo "$target runtime probe unexpectedly enables execution or promotion" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! "$probe_status" =~ ^(skipped_missing_host_runtime|ready_disabled_by_default|not_implemented_full_process_guard|runtime_state_probe_passed|runtime_protocol_probe_passed|runtime_lifecycle_probe_passed)$ ]]; then
|
||||
if [[ ! "$probe_status" =~ ^(skipped_missing_host_runtime|blocked_existing_linuxcnc_runtime|ready_disabled_by_default|not_implemented_full_process_guard|runtime_state_probe_passed|runtime_protocol_probe_passed|runtime_lifecycle_probe_passed)$ ]]; then
|
||||
echo "$target runtime probe has invalid status: $probe_status" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$runtime_ready" == "0" && "$probe_status" != "skipped_missing_host_runtime" ]]; then
|
||||
if [[ "$runtime_ready" == "0" && ! "$probe_status" =~ ^(skipped_missing_host_runtime|blocked_existing_linuxcnc_runtime)$ ]]; then
|
||||
echo "$target runtime probe is not ready but did not skip: $probe_status" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$runtime_ready" == "0" && "$probe_note" != *"missing_host_runtime"* ]]; then
|
||||
if [[ "$runtime_ready" == "0" && "$probe_status" == "skipped_missing_host_runtime" && "$probe_note" != *"missing_host_runtime"* ]]; then
|
||||
echo "$target runtime probe missing-runtime note drift: $probe_note" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$runtime_ready" == "0" && "$probe_status" == "blocked_existing_linuxcnc_runtime" && "$probe_note" != *"existing_linuxcnc_runtime_conflict"* ]]; then
|
||||
echo "$target runtime probe existing-runtime-conflict note drift: $probe_note" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$runtime_ready" == "1" && "$missing_requirements" != "-" ]]; then
|
||||
echo "$target runtime probe is ready but lists missing requirements: $missing_requirements" >&2
|
||||
exit 1
|
||||
@@ -654,7 +658,7 @@ if grep -Fq "millturn_user_m_runtime_ready=1" "$MILLTURN_USER_M_RUNTIME_STDOUT";
|
||||
fi
|
||||
else
|
||||
grep -Fq "millturn_user_m_runtime_ready=0" "$MILLTURN_USER_M_RUNTIME_STDOUT"
|
||||
grep -Fq "millturn_user_m_runtime_probe_status=skipped_missing_host_runtime" \
|
||||
grep -Eq "millturn_user_m_runtime_probe_status=(skipped_missing_host_runtime|blocked_existing_linuxcnc_runtime)" \
|
||||
"$MILLTURN_USER_M_RUNTIME_STDOUT"
|
||||
fi
|
||||
|
||||
|
||||
@@ -802,6 +802,9 @@ assert.equal(millturnUserMBoundaryReport.apiName, "linuxcnc-wasm-virtual-hal-mil
|
||||
assert.equal(millturnUserMBoundaryReport.boundaryClass, "L4-USER-M-PROCESS");
|
||||
assert.equal(millturnUserMBoundaryReport.complete, true);
|
||||
assert.equal(millturnUserMBoundaryReport.webSimulationSatisfied, true);
|
||||
assert.equal(millturnUserMBoundaryReport.webSimulationReady, true);
|
||||
assert.equal(millturnUserMBoundaryReport.nativeRuntimeRequired, false);
|
||||
assert.equal(millturnUserMBoundaryReport.nativeRuntimeRequiredForWebSimulation, false);
|
||||
assert.equal(millturnUserMBoundaryReport.executionEnabled, false);
|
||||
assert.equal(millturnUserMBoundaryReport.promotionAllowed, false);
|
||||
assert.equal(millturnUserMBoundaryReport.processExecutionReady, false);
|
||||
@@ -810,11 +813,16 @@ assert.equal(millturnUserMBoundaryReport.missingManifestFiles.length, 0);
|
||||
assert.equal(millturnUserMBoundaryReport.rows.some(({ userMCode, remapCode }) => userMCode === "M128" && remapCode === "M428"), true);
|
||||
assert.equal(millturnUserMBoundaryReport.rows.some(({ userMCode, remapCode }) => userMCode === "M129" && remapCode === "M429"), true);
|
||||
assert.equal(millturnUserMBoundaryReport.rows.every(({ stateTargetCount }) => stateTargetCount === 12), true);
|
||||
assert.equal(millturnUserMBoundaryReport.rows.every(({ webSimulationReady }) => webSimulationReady === true), true);
|
||||
assert.equal(millturnUserMBoundaryReport.rows.every(({ nativeRuntimeRequiredForWebSimulation }) => nativeRuntimeRequiredForWebSimulation === false), true);
|
||||
assert.equal(millturnUserMBoundaryReport.rows.every(({ executionEnabled, promotionAllowed }) => executionEnabled === false && promotionAllowed === false), true);
|
||||
let millturnStateResult = applyVirtualHalMillturnUserMProcessState(virtualHalRuntime.getState(), "M128", { manifestText: sourceManifestText });
|
||||
assert.equal(millturnStateResult.apiName, "linuxcnc-wasm-virtual-hal-millturn-user-m-process-state-result");
|
||||
assert.equal(millturnStateResult.ok, true);
|
||||
assert.equal(millturnStateResult.userMCode, "M128");
|
||||
assert.equal(millturnStateResult.webSimulationReady, true);
|
||||
assert.equal(millturnStateResult.nativeRuntimeRequired, false);
|
||||
assert.equal(millturnStateResult.nativeRuntimeRequiredForWebSimulation, false);
|
||||
assert.equal(millturnStateResult.executionEnabled, false);
|
||||
assert.equal(millturnStateResult.promotionAllowed, false);
|
||||
assert.equal(readVirtualHalPin(millturnStateResult.state, "motion.switchkins-type").value, 0);
|
||||
|
||||
@@ -293,6 +293,36 @@ assert.equal(customRun.program.source, "custom");
|
||||
assert.ok(customRun.resultText.includes("canon_event=STRAIGHT_FEED line=2 x=4.25 y=2.5"));
|
||||
assert.ok(customRun.summary.ready);
|
||||
assert.equal(customRun.modal.apiName, "real-browser-simulation-modal-state");
|
||||
assert.equal(customRun.millturnUserMProcess.apiName, "real-browser-simulation-millturn-user-m-process-proof");
|
||||
assert.equal(customRun.millturnUserMProcess.ready, true);
|
||||
assert.equal(customRun.millturnUserMProcess.webSimulationReady, true);
|
||||
assert.equal(customRun.millturnUserMProcess.boundaryClass, "L4-USER-M-PROCESS");
|
||||
assert.deepEqual(customRun.millturnUserMProcess.transitionOrder, ["M429", "M428"]);
|
||||
assert.equal(customRun.millturnUserMProcess.nativeRuntimeRequired, false);
|
||||
assert.equal(customRun.millturnUserMProcess.nativeRuntimeRequiredForWebSimulation, false);
|
||||
assert.equal(customRun.millturnUserMProcess.processExecutionReady, false);
|
||||
assert.equal(customRun.millturnUserMProcess.executionEnabled, false);
|
||||
assert.equal(customRun.millturnUserMProcess.promotionAllowed, false);
|
||||
assert.equal(customRun.millturnUserMProcess.turn.userMCode, "M129");
|
||||
assert.equal(customRun.millturnUserMProcess.turn.remapCode, "M429");
|
||||
assert.equal(customRun.millturnUserMProcess.turn.pins["motion.switchkins-type"], 1);
|
||||
assert.equal(customRun.millturnUserMProcess.turn.pins["motion.analog-out-03"], 1);
|
||||
assert.equal(customRun.millturnUserMProcess.turn.pins["kinstype.is-0"], 0);
|
||||
assert.equal(customRun.millturnUserMProcess.turn.pins["kinstype.is-1"], 1);
|
||||
assert.equal(customRun.millturnUserMProcess.turn.pins["ini.x.min_limit"], -240);
|
||||
assert.equal(customRun.millturnUserMProcess.turn.pins["ini.x.max_limit"], 0);
|
||||
assert.equal(customRun.millturnUserMProcess.turn.pins["ini.z.min_limit"], -300);
|
||||
assert.equal(customRun.millturnUserMProcess.turn.pins["ini.z.max_limit"], 300);
|
||||
assert.equal(customRun.millturnUserMProcess.mill.userMCode, "M128");
|
||||
assert.equal(customRun.millturnUserMProcess.mill.remapCode, "M428");
|
||||
assert.equal(customRun.millturnUserMProcess.mill.pins["motion.switchkins-type"], 0);
|
||||
assert.equal(customRun.millturnUserMProcess.mill.pins["motion.analog-out-03"], 0);
|
||||
assert.equal(customRun.millturnUserMProcess.mill.pins["kinstype.is-0"], 1);
|
||||
assert.equal(customRun.millturnUserMProcess.mill.pins["kinstype.is-1"], 0);
|
||||
assert.equal(customRun.millturnUserMProcess.mill.pins["ini.x.min_limit"], -300);
|
||||
assert.equal(customRun.millturnUserMProcess.mill.pins["ini.x.max_limit"], 300);
|
||||
assert.equal(customRun.millturnUserMProcess.mill.pins["ini.z.min_limit"], -240);
|
||||
assert.equal(customRun.millturnUserMProcess.mill.pins["ini.z.max_limit"], 0);
|
||||
|
||||
const sessionRunCalls = [];
|
||||
const sessionRun = await runRealBrowserSimulation({
|
||||
|
||||
@@ -107,6 +107,7 @@ const nativeRuntimeProbeExecutionPlanPath = resolve(buildDir, "native-runtime-pr
|
||||
const nativeRuntimeProbePassEvidenceContractPath = resolve(buildDir, "native-runtime-probe-pass-evidence-contract.tsv");
|
||||
const promotionCandidatesPath = resolve(buildDir, "promotion-candidates.tsv");
|
||||
const evidenceExpansionCandidatesPath = resolve(buildDir, "evidence-expansion-candidates.tsv");
|
||||
const remainingSkipMainProgramPromotionAuditPath = resolve(buildDir, "remaining-skip-main-program-promotion-audit.tsv");
|
||||
const blockedRuntimePromotionLockPath = resolve(buildDir, "blocked-runtime-promotion-lock.tsv");
|
||||
const runtimeBoundaryPromotionReadinessPath = resolve(buildDir, "runtime-boundary-promotion-readiness.tsv");
|
||||
const runtimeBoundaryPromotionBlockersPath = resolve(buildDir, "runtime-boundary-promotion-blockers.tsv");
|
||||
@@ -167,6 +168,7 @@ const generatedSimConfigInventoryArtifactPaths = [
|
||||
nativeRuntimeProbeExecutionPlanPath,
|
||||
nativeRuntimeProbePassEvidenceContractPath,
|
||||
promotionCandidatesPath,
|
||||
remainingSkipMainProgramPromotionAuditPath,
|
||||
blockedRuntimePromotionLockPath,
|
||||
runtimeBoundaryPromotionReadinessPath,
|
||||
runtimeBoundaryPromotionBlockersPath,
|
||||
@@ -6923,7 +6925,7 @@ function verifyBoundaryPhaseCompletionSummaryRows(rows) {
|
||||
const rowByCriterion = new Map(parsedRows.map((row) => [row.criterion, row]));
|
||||
assert.equal(
|
||||
rowByCriterion.get("wasm_inventory_artifact_documentation_coverage")?.count,
|
||||
"59",
|
||||
"60",
|
||||
"WASM artifact documentation completion count must match generated artifact baseline",
|
||||
);
|
||||
assert.equal(
|
||||
@@ -9676,7 +9678,7 @@ function verifyGeneratedArtifactDocumentationCoverage({
|
||||
);
|
||||
assert.equal(
|
||||
artifactNames.length,
|
||||
59,
|
||||
60,
|
||||
"generated sim-config inventory artifact list count drift",
|
||||
);
|
||||
assert.deepEqual(
|
||||
@@ -9709,6 +9711,7 @@ function verifyGeneratedArtifactDocumentationCoverage({
|
||||
"python-remap-runtime-contract.tsv",
|
||||
"python-remap-runtime-gates.tsv",
|
||||
"python-remap-wasm-node-row-proof.tsv",
|
||||
"remaining-skip-main-program-promotion-audit.tsv",
|
||||
"runtime-boundary-contract-summary.tsv",
|
||||
"runtime-boundary-family-host-readiness.tsv",
|
||||
"runtime-boundary-host-preflight.tsv",
|
||||
@@ -9775,7 +9778,7 @@ function verifyGeneratedArtifactDocumentationCoverage({
|
||||
"generated sim-config inventory artifacts must be listed in browser smoke",
|
||||
);
|
||||
assert.ok(
|
||||
browserSmokeText.includes("wasmArtifactNames.length !== 59"),
|
||||
browserSmokeText.includes("wasmArtifactNames.length !== 60"),
|
||||
"browser smoke must keep generated WASM artifact fixed-count guard",
|
||||
);
|
||||
assert.ok(
|
||||
@@ -10651,6 +10654,7 @@ function nativeRuntimeProbeSummaryRows() {
|
||||
assert.ok(
|
||||
[
|
||||
"skipped_missing_host_runtime",
|
||||
"blocked_existing_linuxcnc_runtime",
|
||||
"ready_disabled_by_default",
|
||||
"not_implemented_full_process_guard",
|
||||
"runtime_state_probe_passed",
|
||||
@@ -10660,9 +10664,16 @@ function nativeRuntimeProbeSummaryRows() {
|
||||
`${boundaryClass}: invalid runtime probe status`,
|
||||
);
|
||||
if (row.runtime_ready === "0") {
|
||||
assert.equal(row.probe_status, "skipped_missing_host_runtime", `${boundaryClass}: non-ready runtime must skip`);
|
||||
assert.ok(
|
||||
["skipped_missing_host_runtime", "blocked_existing_linuxcnc_runtime"].includes(row.probe_status),
|
||||
`${boundaryClass}: non-ready runtime must skip or report host-runtime conflict`,
|
||||
);
|
||||
assert.notEqual(row.missing_requirements, "-", `${boundaryClass}: non-ready runtime must record missing requirements`);
|
||||
assert.ok(row.probe_note.includes("missing_host_runtime"), `${boundaryClass}: missing-runtime note drift`);
|
||||
if (row.probe_status === "skipped_missing_host_runtime") {
|
||||
assert.ok(row.probe_note.includes("missing_host_runtime"), `${boundaryClass}: missing-runtime note drift`);
|
||||
} else {
|
||||
assert.ok(row.probe_note.includes("existing_linuxcnc_runtime_conflict"), `${boundaryClass}: existing-runtime-conflict note drift`);
|
||||
}
|
||||
} else {
|
||||
assert.equal(row.missing_requirements, "-", `${boundaryClass}: ready runtime must not record missing requirements`);
|
||||
}
|
||||
@@ -10752,6 +10763,13 @@ function runtimeProbeGateAlignmentRows({
|
||||
const runtimeReadyMatch = gateRow?.runtime_ready === nativeRow?.runtime_ready;
|
||||
const sourceProofReadyMatch = gateRow?.source_proof_ready === nativeRow?.source_proof_ready;
|
||||
const missingRequirementsMatch = gateMissing.join(",") === nativeMissing.join(",");
|
||||
const existingRuntimeConflictCompatible = (
|
||||
nativeRow?.probe_status === "blocked_existing_linuxcnc_runtime" &&
|
||||
gateRow?.runtime_ready === "1" &&
|
||||
nativeRow?.runtime_ready === "0" &&
|
||||
gateMissing.length === 0 &&
|
||||
nativeMissing.includes("exclusive_linuxcnc_runtime")
|
||||
);
|
||||
const requiredNativeProofMatch = gateRow?.required_native_proof === nativeRow?.required_native_proof;
|
||||
const executionDisabledMatch = (
|
||||
gateRow?.execution_enabled === "0" &&
|
||||
@@ -10765,6 +10783,9 @@ function runtimeProbeGateAlignmentRows({
|
||||
(gateRow?.runtime_ready === "0" &&
|
||||
gateRow?.gate_status === "blocked_missing_host_runtime" &&
|
||||
nativeRow?.probe_status === "skipped_missing_host_runtime") ||
|
||||
(gateRow?.runtime_ready === "1" &&
|
||||
gateRow?.gate_status?.startsWith("ready_to_implement_") &&
|
||||
nativeRow?.probe_status === "blocked_existing_linuxcnc_runtime") ||
|
||||
(gateRow?.runtime_ready === "1" &&
|
||||
gateRow?.gate_status?.startsWith("ready_to_implement_") &&
|
||||
nativeRow?.probe_status === "ready_disabled_by_default") ||
|
||||
@@ -10785,7 +10806,7 @@ function runtimeProbeGateAlignmentRows({
|
||||
runtimeReadyMatch &&
|
||||
sourceProofReadyMatch &&
|
||||
requiredNativeProofMatch &&
|
||||
missingRequirementsMatch &&
|
||||
(missingRequirementsMatch || existingRuntimeConflictCompatible) &&
|
||||
executionDisabledMatch &&
|
||||
promotionDisabledMatch &&
|
||||
statusCompatible
|
||||
@@ -10809,7 +10830,7 @@ function runtimeProbeGateAlignmentRows({
|
||||
listValue(gateMissing),
|
||||
listValue(nativeMissing),
|
||||
flagValue(requiredNativeProofMatch),
|
||||
flagValue(missingRequirementsMatch),
|
||||
flagValue(missingRequirementsMatch || existingRuntimeConflictCompatible),
|
||||
flagValue(executionDisabledMatch),
|
||||
flagValue(promotionDisabledMatch),
|
||||
flagValue(statusCompatible),
|
||||
@@ -13480,6 +13501,178 @@ function verifyPromotionCandidateRows(rows, summaryRows, boundaryRows) {
|
||||
return parsedRows;
|
||||
}
|
||||
|
||||
function remainingSkipMainProgramPromotionAuditRows({
|
||||
summaryRows,
|
||||
promotionCandidateRows,
|
||||
boundaryRows,
|
||||
promotionReadinessRows,
|
||||
}) {
|
||||
const promotionByPath = new Map(
|
||||
promotionCandidateRows
|
||||
.filter((row) => row.candidate_kind === "inventory-ready")
|
||||
.map((row) => [row.path, row]),
|
||||
);
|
||||
const boundaryByPath = new Map(boundaryRows.map((row) => [row.path, row]));
|
||||
const readinessByBlocked = new Map(promotionReadinessRows.map((row) => [row.blocked, row]));
|
||||
|
||||
return summaryRows
|
||||
.map((row) => {
|
||||
const [
|
||||
path,
|
||||
inventoryStatus,
|
||||
reason,
|
||||
className,
|
||||
nativeStatus,
|
||||
nativeExpectedFailure,
|
||||
] = row.split("\t");
|
||||
return {
|
||||
path,
|
||||
inventoryStatus,
|
||||
reason,
|
||||
className,
|
||||
nativeStatus,
|
||||
nativeExpectedFailure,
|
||||
};
|
||||
})
|
||||
.filter((row) => row.inventoryStatus === "SKIP" && row.className === "main")
|
||||
.sort((left, right) => left.path.localeCompare(right.path))
|
||||
.map((row) => {
|
||||
const promotion = promotionByPath.get(row.path);
|
||||
const boundary = boundaryByPath.get(row.path);
|
||||
const readiness = readinessByBlocked.get(row.reason);
|
||||
const promotionAllowed = promotion?.promotion_allowed === "1" &&
|
||||
readiness?.promotion_ready === "1";
|
||||
const decision = promotionAllowed
|
||||
? "promotable_main_program"
|
||||
: row.reason === "UPSTREAM-DEMO"
|
||||
? "not_promotable_upstream_demo_missing_motion_gcode"
|
||||
: "not_promotable_runtime_proof_incomplete";
|
||||
const simulationProofStatus = readiness
|
||||
? `${readiness.current_probe_status}:native=${readiness.native_pass_ready}:node=${readiness.node_inventory_gate_complete}:browser=${readiness.browser_smoke_gate_complete}`
|
||||
: "no_runtime_promotion_readiness_for_skip_kind";
|
||||
|
||||
return [
|
||||
row.path,
|
||||
promotion?.ini ?? boundary?.ini ?? "-",
|
||||
row.inventoryStatus,
|
||||
row.reason,
|
||||
row.className,
|
||||
row.nativeStatus,
|
||||
row.nativeExpectedFailure,
|
||||
promotion?.blocked_kind ?? boundary?.blocked ?? row.reason,
|
||||
promotion?.dependency_class ?? boundary?.dependencies ?? "-",
|
||||
simulationProofStatus,
|
||||
readiness?.native_pass_ready ?? "0",
|
||||
readiness?.node_inventory_gate_complete ?? "0",
|
||||
readiness?.browser_smoke_gate_complete ?? "0",
|
||||
readiness?.promotion_lock_active ?? "1",
|
||||
readiness?.promotion_ready ?? "0",
|
||||
promotion?.promotion_allowed ?? "0",
|
||||
decision,
|
||||
promotion?.block_reason ?? `blocked_by_${row.reason}`,
|
||||
promotion?.recommended_next_command ?? "no_direct_baseline_promotion_candidate",
|
||||
].map(tsvValue).join("\t");
|
||||
});
|
||||
}
|
||||
|
||||
function verifyRemainingSkipMainProgramPromotionAuditRows({
|
||||
rows,
|
||||
summaryRows,
|
||||
promotionCandidateRows,
|
||||
}) {
|
||||
const expectedHeaders = [
|
||||
"path",
|
||||
"ini",
|
||||
"current_status",
|
||||
"skip_kind",
|
||||
"class",
|
||||
"native_status",
|
||||
"native_expected_failure",
|
||||
"blocked_kind",
|
||||
"dependency_class",
|
||||
"simulation_proof_status",
|
||||
"native_pass_ready",
|
||||
"node_inventory_gate_complete",
|
||||
"browser_smoke_gate_complete",
|
||||
"promotion_lock_active",
|
||||
"promotion_ready",
|
||||
"promotion_allowed",
|
||||
"promotion_decision",
|
||||
"block_reason",
|
||||
"recommended_next_command",
|
||||
];
|
||||
const parsedRows = parseTsv(
|
||||
`${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`,
|
||||
expectedHeaders,
|
||||
);
|
||||
const expectedSkippedMain = summaryRows
|
||||
.map((row) => {
|
||||
const [path, inventoryStatus, reason, className] = row.split("\t");
|
||||
return { path, inventoryStatus, reason, className };
|
||||
})
|
||||
.filter((row) => row.inventoryStatus === "SKIP" && row.className === "main")
|
||||
.map((row) => row.path)
|
||||
.sort();
|
||||
const promotionInventoryPaths = promotionCandidateRows
|
||||
.filter((row) => row.candidate_kind === "inventory-ready")
|
||||
.map((row) => row.path)
|
||||
.sort();
|
||||
|
||||
assert.deepEqual(
|
||||
parsedRows.map((row) => row.path).sort(),
|
||||
expectedSkippedMain,
|
||||
"remaining skipped-main audit must cover every skipped main program",
|
||||
);
|
||||
assert.deepEqual(
|
||||
promotionInventoryPaths,
|
||||
expectedSkippedMain,
|
||||
"remaining skipped-main audit must align with inventory-ready promotion candidates",
|
||||
);
|
||||
assert.equal(parsedRows.length, 2, "current remaining skipped main-program count drift");
|
||||
assert.equal(
|
||||
parsedRows.filter((row) => row.promotion_allowed === "1").length,
|
||||
0,
|
||||
"current remaining skipped main-program rows must not be promotable",
|
||||
);
|
||||
|
||||
for (const row of parsedRows) {
|
||||
assert.equal(row.current_status, "SKIP", `${row.path}: audit status drift`);
|
||||
assert.equal(row.class, "main", `${row.path}: audit must only include main programs`);
|
||||
assert.equal(row.blocked_kind, row.skip_kind, `${row.path}: audit blocked kind drift`);
|
||||
assert.ok(["0", "1"].includes(row.native_pass_ready), `${row.path}: invalid native pass flag`);
|
||||
assert.ok(["0", "1"].includes(row.node_inventory_gate_complete), `${row.path}: invalid Node gate flag`);
|
||||
assert.ok(["0", "1"].includes(row.browser_smoke_gate_complete), `${row.path}: invalid browser gate flag`);
|
||||
assert.ok(["0", "1"].includes(row.promotion_ready), `${row.path}: invalid promotion ready flag`);
|
||||
assert.equal(row.promotion_ready, "0", `${row.path}: audit must not mark promotion ready`);
|
||||
assert.equal(row.promotion_allowed, "0", `${row.path}: audit must not allow promotion`);
|
||||
assert.notEqual(row.block_reason, "-", `${row.path}: audit lacks block reason`);
|
||||
assert.notEqual(row.recommended_next_command, "-", `${row.path}: audit lacks next command`);
|
||||
if (row.skip_kind === "L4-USER-M-PROCESS") {
|
||||
assert.equal(row.path, "axis/vismach/millturn/example.ngc", `${row.path}: user-M skipped main path drift`);
|
||||
assert.ok(
|
||||
row.simulation_proof_status.includes("node=0") ||
|
||||
row.simulation_proof_status.includes("browser=0"),
|
||||
`${row.path}: user-M row must show incomplete Node/browser simulation proof`,
|
||||
);
|
||||
assert.equal(
|
||||
row.promotion_decision,
|
||||
"not_promotable_runtime_proof_incomplete",
|
||||
`${row.path}: user-M audit decision drift`,
|
||||
);
|
||||
} else if (row.skip_kind === "UPSTREAM-DEMO") {
|
||||
assert.equal(
|
||||
row.promotion_decision,
|
||||
"not_promotable_upstream_demo_missing_motion_gcode",
|
||||
`${row.path}: upstream demo audit decision drift`,
|
||||
);
|
||||
} else {
|
||||
assert.fail(`${row.path}: unexpected remaining skipped main skip kind ${row.skip_kind}`);
|
||||
}
|
||||
}
|
||||
|
||||
return parsedRows;
|
||||
}
|
||||
|
||||
for (const record of nativeRecords) {
|
||||
const skipReason = blockedKind(record, pathMatrixByPath);
|
||||
if (skipReason === "L4-PYTHON-REMAP" && promotedPythonRemapInventoryPaths.has(record.path)) {
|
||||
@@ -13981,11 +14174,23 @@ const promotionCandidateRowsGenerated = promotionCandidateRows({
|
||||
trackedMatrixByPath,
|
||||
nativeByPath: new Map(nativeRecords.map((record) => [record.path, record])),
|
||||
});
|
||||
verifyPromotionCandidateRows(
|
||||
const promotionCandidateRecords = verifyPromotionCandidateRows(
|
||||
promotionCandidateRowsGenerated,
|
||||
summaryRows,
|
||||
boundarySummaryRecords,
|
||||
);
|
||||
const remainingSkipMainProgramPromotionAuditRowsGenerated =
|
||||
remainingSkipMainProgramPromotionAuditRows({
|
||||
summaryRows,
|
||||
promotionCandidateRows: promotionCandidateRecords,
|
||||
boundaryRows: boundarySummaryRecords,
|
||||
promotionReadinessRows: runtimeBoundaryPromotionReadinessRecords,
|
||||
});
|
||||
verifyRemainingSkipMainProgramPromotionAuditRows({
|
||||
rows: remainingSkipMainProgramPromotionAuditRowsGenerated,
|
||||
summaryRows,
|
||||
promotionCandidateRows: promotionCandidateRecords,
|
||||
});
|
||||
const evidenceExpansionCandidateRowsGenerated = evidenceExpansionCandidateRows({
|
||||
summaryRows,
|
||||
boundaryRows: boundarySummaryRecords,
|
||||
@@ -14047,6 +14252,34 @@ writeFileSync(
|
||||
].join("\n")}\n`,
|
||||
);
|
||||
|
||||
writeFileSync(
|
||||
remainingSkipMainProgramPromotionAuditPath,
|
||||
`${[
|
||||
[
|
||||
"path",
|
||||
"ini",
|
||||
"current_status",
|
||||
"skip_kind",
|
||||
"class",
|
||||
"native_status",
|
||||
"native_expected_failure",
|
||||
"blocked_kind",
|
||||
"dependency_class",
|
||||
"simulation_proof_status",
|
||||
"native_pass_ready",
|
||||
"node_inventory_gate_complete",
|
||||
"browser_smoke_gate_complete",
|
||||
"promotion_lock_active",
|
||||
"promotion_ready",
|
||||
"promotion_allowed",
|
||||
"promotion_decision",
|
||||
"block_reason",
|
||||
"recommended_next_command",
|
||||
].join("\t"),
|
||||
...remainingSkipMainProgramPromotionAuditRowsGenerated,
|
||||
].join("\n")}\n`,
|
||||
);
|
||||
|
||||
writeFileSync(
|
||||
boundarySummaryPath,
|
||||
`${[
|
||||
|
||||
Reference in New Issue
Block a user