按text22收束promotion证据
结论:完成 evidence-ready / inventory-ready 双层 promotion candidate 管理,release/SDK gate 已纳入 virtual HAL promotion summary,baseline 保持 28/28/131/0,Python remap、tool DB、external user-M hard block 继续 locked。
This commit is contained in:
1578
text18.txt
1578
text18.txt
File diff suppressed because it is too large
Load Diff
265
text19.txt
Normal file
265
text19.txt
Normal file
@@ -0,0 +1,265 @@
|
||||
项目接续文件:利用 virtual HAL 成果推进 sim config baseline promotion
|
||||
|
||||
生成时间:2026-06-18 CST
|
||||
|
||||
本文件接替 `text18.txt`。后续继续推进时优先参考 `text19.txt`;除非明确要求审计
|
||||
旧记录,不再回到 `text1` 到 `text18` 扩展历史实现。
|
||||
|
||||
一、方向纠偏
|
||||
|
||||
上一版表述容易被误读为“转向非 virtual HAL,不再使用 virtual HAL 成果”。正确方向是:
|
||||
|
||||
```text
|
||||
virtual HAL runtime capability 已收尾;后续不继续扩展 virtual HAL 新功能,
|
||||
但要利用已完成的 virtual HAL source-derived evidence、browser diagnostics、
|
||||
release gate 和 motion matrix,继续推进 sim config coverage / baseline promotion。
|
||||
```
|
||||
|
||||
执行含义:
|
||||
|
||||
- 不新增 virtual HAL runtime capability;
|
||||
- 不新增 unrelated virtual HAL UI 面板、filter、drilldown 类功能;
|
||||
- 不把 virtual HAL 当成 JS CNC 语义替代品;
|
||||
- 允许并且应该复用 virtual HAL 已有成果作为 promotion evidence:
|
||||
- source compliance;
|
||||
- sim-config source coverage;
|
||||
- promotion candidate report;
|
||||
- macro/load fixture report;
|
||||
- command script fixtures;
|
||||
- manifest-backed motion controller matrix;
|
||||
- browser diagnostics artifact;
|
||||
- release readiness / URL workflow / batch acceptance evidence。
|
||||
|
||||
二、当前 baseline
|
||||
|
||||
当前 Node inventory baseline 仍为:
|
||||
|
||||
```text
|
||||
executed=28
|
||||
passed=28
|
||||
skipped=131
|
||||
unexpected_fail=0
|
||||
```
|
||||
|
||||
当前已经完成的 virtual HAL promotion evidence 不等同于 inventory skip count 下降。它已经把
|
||||
一批 Node `INV` / representative rows 推进到了 browser diagnostics / release evidence 层,
|
||||
但如果要让 `executed/passed/skipped` 变化,仍必须修改并运行真实 inventory execution path。
|
||||
|
||||
三、已具备的 promotion evidence
|
||||
|
||||
当前 SDK / browser / release gate 已覆盖 Tier 1 promotion candidate report:
|
||||
|
||||
- `qtdragon/qtdragon_multi_joint/on_abort.ngc`
|
||||
- `qtdragon/qtdragon_xyz/on_abort.ngc`
|
||||
- `qtdragon/qtdragon_xyz45/on_abort.ngc`
|
||||
- `qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc`
|
||||
- `qtdragon_hd/qtdragon_hd_z_compensation/on_abort.ngc`
|
||||
- `qtvcp_screens/qtdragon/on_abort.ngc`
|
||||
- `axis/vismach/puma/puma_seam_weld.ngc`
|
||||
- `axis/rose_engine/rcone_demo.ngc`
|
||||
|
||||
这些 row 的当前性质:
|
||||
|
||||
- LinuxCNC source/config evidence 完整;
|
||||
- current Node inventory status 为 `PASS`;
|
||||
- blocked kind 为 `-`;
|
||||
- target browser evidence 为 `explicit-browser-diagnostics`;
|
||||
- release diagnostics validation 已要求 promotion candidate summary;
|
||||
- inventory baseline 保持不变。
|
||||
|
||||
当前 macro/load 非主程序 evidence 已覆盖:
|
||||
|
||||
- `axis/rose_engine/rcone.ngc`
|
||||
- `axis/external_offsets/queuebuster.ngc`
|
||||
|
||||
这些只能作为 non-main fixture diagnostics evidence,不作为 standalone main program promotion。
|
||||
|
||||
四、不可突破边界
|
||||
|
||||
以下 family 仍不能靠 virtual HAL、JS glue 或 browser UI 伪装解锁:
|
||||
|
||||
- Linux kernel hard-realtime ABI;
|
||||
- 外部硬件驱动 ABI;
|
||||
- native HAL module ABI;
|
||||
- Python UI process emulation;
|
||||
- Python remap runtime;
|
||||
- tool database process protocol;
|
||||
- external user-M process execution;
|
||||
- upstream-invalid demo code。
|
||||
|
||||
因此以下 blocked kind 仍保持 locked:
|
||||
|
||||
- `L4-PYTHON-REMAP`
|
||||
- `L4-TOOL-DB`
|
||||
- `L4-USER-M-PROCESS`
|
||||
- `UPSTREAM-DEMO`
|
||||
- standalone-main 语境下的 `ASSET-ONLY`
|
||||
|
||||
五、下一步主线目标
|
||||
|
||||
目标仍是从:
|
||||
|
||||
```text
|
||||
executed=28
|
||||
passed=28
|
||||
skipped=131
|
||||
```
|
||||
|
||||
推进到更高完成度。第一阶段建议目标:
|
||||
|
||||
```text
|
||||
executed >= 35
|
||||
passed == executed
|
||||
skipped <= 124
|
||||
unexpected_fail=0
|
||||
```
|
||||
|
||||
但推进方式应是:
|
||||
|
||||
1. 先复用 virtual HAL 已建立的 source-derived evidence,确认候选 family 的 browser/release
|
||||
evidence 已 ready;
|
||||
2. 再找出当前 inventory 中还没有执行、但依赖已被 virtual HAL / HALUI / deterministic
|
||||
UI declarations / machine files / tool tables / remap asset staging 覆盖的 main program;
|
||||
3. 只把 LinuxCNC-backed runtime 能真实执行通过的 row 纳入 inventory;
|
||||
4. 同步更新 docs、release artifact fixture、SDK/host gate baseline 断言。
|
||||
|
||||
六、候选选择原则
|
||||
|
||||
优先方向:
|
||||
|
||||
- Tier 1 已有 promotion evidence 周边 family:
|
||||
- QtDragon on-abort family;
|
||||
- PUMA / vismach remap family;
|
||||
- rose-engine family;
|
||||
- 当前 matrix 中 `blocked=-`、`class=main`、native `PASS`、但尚未进入 browser explicit evidence
|
||||
或 inventory execution 的 row;
|
||||
- 已有 tool-table fallback、vendored INI/G-code/source evidence、无 Python/process hard block 的 row;
|
||||
- 已有 virtual HAL command/motion matrix 可以解释 HAL/UI/motion 依赖的 row。
|
||||
|
||||
禁止方向:
|
||||
|
||||
- 不把 Python remap pass 当作 browser/Node full promotion;
|
||||
- 不把 tool DB native pass 当作 WASM process protocol pass;
|
||||
- 不把 external user-M process pass 当作 virtual HAL deterministic M110/M111 边界;
|
||||
- 不把 macro/load asset 当 standalone main;
|
||||
- 不靠改 docs 声称 skipped 降低。
|
||||
|
||||
七、建议执行步骤
|
||||
|
||||
### 第一步:复核当前 virtual HAL promotion gate
|
||||
|
||||
最小命令:
|
||||
|
||||
```text
|
||||
wasm-port/tests/sdk/node/verify_sdk_surface.sh
|
||||
wasm-port/tests/sdk/node/verify_project_release_gate_manifest.sh
|
||||
```
|
||||
|
||||
确认:
|
||||
|
||||
- `VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES` 仍包含 8 个 Tier 1 row;
|
||||
- candidate report `complete === true`;
|
||||
- `webSimulationSatisfied === true`;
|
||||
- `inventoryBaselineUnchanged === true`;
|
||||
- blocked candidate count 为 0;
|
||||
- release gate / URL workflow 仍要求 candidate summary。
|
||||
|
||||
### 第二步:生成真实 inventory promotion candidate 清单
|
||||
|
||||
不要只列 hard-block skipped main row;应输出两个层级:
|
||||
|
||||
- evidence-ready candidates:已有 virtual HAL/browser/release evidence,适合继续升 browser/release
|
||||
覆盖;
|
||||
- inventory-ready candidates:确实可从 skipped 推进到 executed 的 main program。
|
||||
|
||||
建议输出:
|
||||
|
||||
```text
|
||||
wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv
|
||||
```
|
||||
|
||||
字段至少包含:
|
||||
|
||||
- path;
|
||||
- ini;
|
||||
- current inventory status;
|
||||
- matrix layer4 node/browser status;
|
||||
- class;
|
||||
- native status;
|
||||
- blocked kind;
|
||||
- dependency class;
|
||||
- virtual HAL evidence ready;
|
||||
- inventory promotion allowed;
|
||||
- block reason;
|
||||
- recommended next command。
|
||||
|
||||
### 第三步:推进第一批真实 inventory rows
|
||||
|
||||
一次只推进 5 到 8 个 row。每个 row 必须说明:
|
||||
|
||||
- LinuxCNC source/config/runtime evidence;
|
||||
- 为什么已有 virtual HAL evidence 能覆盖 HAL/UI/motion 依赖;
|
||||
- 为什么不是 JS/browser 自行补 CNC 语义;
|
||||
- 为什么不属于 `L4-PYTHON-REMAP`、`L4-TOOL-DB`、`L4-USER-M-PROCESS`、
|
||||
`UPSTREAM-DEMO` 或 standalone `ASSET-ONLY`;
|
||||
- 执行后 baseline 如何变化。
|
||||
|
||||
### 第四步:同步 release packaging
|
||||
|
||||
baseline 真变化后,同步更新:
|
||||
|
||||
- generated sim-config inventory artifact;
|
||||
- `wasm-port/docs/sim-configs-coverage-matrix.md`;
|
||||
- `wasm-port/docs/sim-config-coverage-promotion-analysis.md`;
|
||||
- release readiness artifact fixture;
|
||||
- SDK / host gate 的 baseline 断言;
|
||||
- browser diagnostics / URL workflow 中的 baseline 文案。
|
||||
|
||||
八、验收标准
|
||||
|
||||
一次真实 baseline promotion 完成条件:
|
||||
|
||||
1. 新 baseline artifact 真实生成;
|
||||
2. `executed` 和 `passed` 同步增加;
|
||||
3. `skipped` 同步减少;
|
||||
4. `unexpected_fail` 仍为 `0`;
|
||||
5. 新增 rows 不属于 blocked family;
|
||||
6. virtual HAL evidence 是 source-derived support,不是 CNC 语义替代;
|
||||
7. `verify_no_standalone_cnc_semantics.sh` 仍通过;
|
||||
8. 最小相关 gate 通过;
|
||||
9. 文档记录变更前/后 baseline、新增 rows、blocked family lock 状态和验证命令。
|
||||
|
||||
建议最小验证命令:
|
||||
|
||||
```text
|
||||
git diff --check
|
||||
wasm-port/tools/verify_no_standalone_cnc_semantics.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
wasm-port/tests/sdk/node/verify_sdk_surface.sh
|
||||
wasm-port/tests/sdk/node/verify_project_release_gate_manifest.sh
|
||||
```
|
||||
|
||||
如改动涉及 browser/release 页面,再额外运行:
|
||||
|
||||
```text
|
||||
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh
|
||||
```
|
||||
|
||||
九、下一步工作内容
|
||||
|
||||
下一轮建议直接做:
|
||||
|
||||
```text
|
||||
基于现有 virtual HAL promotion candidate report,生成 evidence-ready / inventory-ready
|
||||
双层 promotion-candidates.tsv;然后从 blocked=-、native PASS、class=main 的 row 中筛出
|
||||
第一批 5-8 个可真实进入 inventory execution 的候选,试运行并更新 baseline。
|
||||
```
|
||||
|
||||
优先候选 family:
|
||||
|
||||
- QtDragon on-abort family 周边;
|
||||
- PUMA / vismach remap family;
|
||||
- rose-engine family;
|
||||
- 其他已 vendored、无 Python/process hard block、可由 virtual HAL 解释 HAL/UI/motion 依赖的
|
||||
main program。
|
||||
453
text20.txt
Normal file
453
text20.txt
Normal file
@@ -0,0 +1,453 @@
|
||||
项目接续文件:tool DB 解锁步骤与方法
|
||||
|
||||
生成时间:2026-06-18 CST
|
||||
|
||||
本文件接替 `text19.txt` 中关于 blocked runtime family 的后续推进说明,专门规划
|
||||
`L4-TOOL-DB` / `axis/db_demo/base.ngc` 的解锁路径。当前目标不是绕过 tool DB,
|
||||
而是在已有 source-derived evidence、transaction contract、native readiness gate 和
|
||||
promotion lock 框架基础上,把 tool database process protocol 证明补齐,然后再允许
|
||||
真实 inventory promotion。
|
||||
|
||||
重要状态说明:本文件是未来解锁研究计划,不是当前阶段实施范围。当前阶段铁律仍是
|
||||
“virtual HAL 成果作为 sim config promotion 证据基础”,同时明确不解锁
|
||||
`L4-PYTHON-REMAP`、`L4-TOOL-DB`、`L4-USER-M-PROCESS` 这些 hard block。
|
||||
因此在当前交付中,`axis/db_demo/base.ngc` 必须继续保持 `SKIP L4-TOOL-DB`,
|
||||
`promotion_allowed=0`,`promotion_lock_active=1`。
|
||||
|
||||
一、当前已完成条件
|
||||
|
||||
当前项目已经具备以下基础,不需要重做:
|
||||
|
||||
1. source/config ownership 已明确。
|
||||
|
||||
- `axis/db_demo/base.ngc` 当前 blocked kind 为 `L4-TOOL-DB`;
|
||||
- owning INI 为 `axis/db_demo/db_nonran.ini`;
|
||||
- INI 声明:
|
||||
|
||||
```text
|
||||
[EMCIO]
|
||||
DB_PROGRAM = ./db_nonran.py
|
||||
```
|
||||
|
||||
- LinuxCNC source owner 已记录:
|
||||
- `configs/sim/axis/db_demo/db_nonran.ini`
|
||||
- `configs/sim/axis/db_demo/db.py`
|
||||
- `src/emc/task/taskclass.cc`
|
||||
- `src/emc/tooldata/tooldata_db.cc`
|
||||
- `lib/python/tooldb.py`
|
||||
|
||||
2. machine-readable artifacts 已有。
|
||||
|
||||
当前已有并通过 inventory gate 的 tool DB 相关 artifact:
|
||||
|
||||
```text
|
||||
wasm-port/build/wasm/sim-configs-inventory/tool-db-process-boundary-summary.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/tool-db-process-protocol-gates.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/tool-db-process-transaction-plan.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/tool-db-process-native-protocol-alignment.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/tool-db-process-native-runtime-readiness.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/tool-db-process-native-runtime-probe-gate.tsv
|
||||
```
|
||||
|
||||
3. transaction contract 已明确。
|
||||
|
||||
`tool-db-process-transaction-plan.tsv` 已把 DB protocol 拆成以下 pending proof:
|
||||
|
||||
- `startup_handshake`:启动后返回 `v2.1`;
|
||||
- `initial_get_all`:发送 `g`,读取直到 `FINI`;
|
||||
- `spindle_load_notify`:发送/验证 `l`;
|
||||
- `tool_offset_notify`:发送/验证 `p`;
|
||||
- `spindle_unload_notify`:发送/验证 `u`。
|
||||
|
||||
4. 当前 readiness 状态已明确。
|
||||
|
||||
当前 `tool-db-process-native-runtime-readiness.tsv` / probe gate 显示:
|
||||
|
||||
```text
|
||||
python3=1
|
||||
axis/db_demo/db_nonran.py=1
|
||||
linuxcnc.so=1
|
||||
tooldb.py=1
|
||||
linuxcnc=0
|
||||
milltask=0
|
||||
halcmd=0
|
||||
runtime_ready=0
|
||||
source_proof_ready=1
|
||||
gate_status=blocked_missing_host_runtime
|
||||
proof_status=pending
|
||||
execution_enabled=0
|
||||
promotion_allowed=0
|
||||
```
|
||||
|
||||
这说明当前缺的不是 source evidence,也不是 Python 文件本身,而是完整 LinuxCNC
|
||||
task/tooldata DB host runtime。
|
||||
|
||||
二、解锁原则
|
||||
|
||||
tool DB 解锁必须遵守以下规则:
|
||||
|
||||
1. 不允许用 `.tbl` fallback 解锁。
|
||||
|
||||
`DB_PROGRAM` 模式明确替代普通 tool table path。用 `.tbl` 让程序跑通会绕过
|
||||
LinuxCNC tool database process protocol,属于错误 promotion。
|
||||
|
||||
2. 不允许用 JS/virtual HAL 重新实现 tool DB 语义。
|
||||
|
||||
virtual HAL 可以继续作为 HAL/motion/browser diagnostics evidence,但不能替代:
|
||||
|
||||
- `DB_PROGRAM` 子进程启动;
|
||||
- `tooldb.py` protocol loop;
|
||||
- `v2.1` / `g` / `l` / `u` / `p` protocol;
|
||||
- nonrandom DB state mutation;
|
||||
- flat-file persistence。
|
||||
|
||||
3. native proof 必须先于 Node/browser promotion。
|
||||
|
||||
只有 native runtime probe 明确通过以后,才允许更新 Node/browser promotion gate。
|
||||
|
||||
4. promotion lock 必须手动更新。
|
||||
|
||||
即使 native probe 通过,也不能自动把 `axis/db_demo/base.ngc` 从 skipped 改为 executed。
|
||||
必须明确更新 promotion readiness、blockers、post-native-pass gate 和 lock artifact。
|
||||
|
||||
三、第一阶段:准备 host runtime
|
||||
|
||||
目标:让 tool DB runtime readiness 从:
|
||||
|
||||
```text
|
||||
runtime_ready=0
|
||||
missing_requirements=linuxcnc,milltask,halcmd
|
||||
```
|
||||
|
||||
变为:
|
||||
|
||||
```text
|
||||
runtime_ready=1
|
||||
missing_requirements=-
|
||||
```
|
||||
|
||||
需要准备的命令/模块:
|
||||
|
||||
```text
|
||||
python3
|
||||
linuxcnc
|
||||
milltask
|
||||
halcmd
|
||||
axis/db_demo/db_nonran.py
|
||||
linuxcnc.so
|
||||
tooldb.py
|
||||
```
|
||||
|
||||
当前已有:
|
||||
|
||||
```text
|
||||
python3
|
||||
axis/db_demo/db_nonran.py
|
||||
linuxcnc.so
|
||||
tooldb.py
|
||||
```
|
||||
|
||||
当前缺失:
|
||||
|
||||
```text
|
||||
linuxcnc
|
||||
milltask
|
||||
halcmd
|
||||
```
|
||||
|
||||
建议方法:
|
||||
|
||||
1. 在具备完整 LinuxCNC userspace runtime 的 host / container 中执行。
|
||||
2. 确保 `linuxcnc`、`milltask`、`halcmd` 在 `PATH` 上。
|
||||
3. 确保 LinuxCNC Python 模块路径能被 `db_nonran.py` import:
|
||||
|
||||
```text
|
||||
python3 -c "import linuxcnc; import tooldb; print('linuxcnc_tooldb_python_modules=ok')"
|
||||
```
|
||||
|
||||
4. 重新运行 native probes / inventory gate 以刷新 readiness artifacts:
|
||||
|
||||
```text
|
||||
wasm-port/tests/native/probe_tool_db_runtime.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
```
|
||||
|
||||
预期中间状态:
|
||||
|
||||
- 如果 host runtime 仍缺失:
|
||||
|
||||
```text
|
||||
tool_db_runtime_probe_status=skipped_missing_host_runtime
|
||||
```
|
||||
|
||||
- 如果 host runtime 已满足但未显式启用 probe:
|
||||
|
||||
```text
|
||||
tool_db_runtime_probe_status=ready_disabled_by_default
|
||||
```
|
||||
|
||||
四、第二阶段:执行 native DB protocol probe
|
||||
|
||||
目标:证明 LinuxCNC-owned DB process protocol 真实可运行。
|
||||
|
||||
执行命令:
|
||||
|
||||
```text
|
||||
ENABLE_TOOL_DB_RUNTIME_PROBE=1 wasm-port/tests/native/probe_tool_db_runtime.sh
|
||||
```
|
||||
|
||||
该 probe 必须证明:
|
||||
|
||||
1. `db_nonran.py` 按 `DB_PROGRAM` 配置启动;
|
||||
2. startup handshake 返回 `v2.1`;
|
||||
3. `g` get-all 返回 expected nonrandom startup tools;
|
||||
4. startup state 覆盖 `T10..T19` / `tno+100` pockets;
|
||||
5. `l` load notify 触发 spindle load state;
|
||||
6. `p` tool offset notify 触发 DB update path;
|
||||
7. `u` unload notify 触发 spindle unload state;
|
||||
8. flat-file persistence 能反映 state mutation;
|
||||
9. probe 输出:
|
||||
|
||||
```text
|
||||
tool_db_runtime_probe_status=runtime_protocol_probe_passed
|
||||
```
|
||||
|
||||
失败处理:
|
||||
|
||||
- 如果失败在 import `linuxcnc` / `tooldb`:修 Python module path,不改项目语义;
|
||||
- 如果失败在 `linuxcnc` / `milltask` / `halcmd`:修 host runtime,不改 Node/browser fallback;
|
||||
- 如果失败在 protocol message:先对齐 native `db_nonran.py` / `tooldb.py` 行为和
|
||||
`tool-db-process-transaction-plan.tsv`,不允许用简化协议绕过;
|
||||
- 如果失败在 persistence:修 probe 环境的临时 DB 文件隔离和 cleanup,不把 persistence
|
||||
检查删除。
|
||||
|
||||
五、第三阶段:刷新 native/runtime artifacts
|
||||
|
||||
native probe pass 后,重新生成并验证 artifacts:
|
||||
|
||||
```text
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
```
|
||||
|
||||
预期变化方向:
|
||||
|
||||
```text
|
||||
tool-db-process-native-runtime-readiness.tsv:
|
||||
runtime_ready=1
|
||||
missing_requirements=-
|
||||
|
||||
tool-db-process-native-runtime-probe-gate.tsv:
|
||||
gate_status=native_probe_passed_waiting_for_node_browser_promotion_proof
|
||||
proof_status=passed
|
||||
source_proof_ready=1
|
||||
```
|
||||
|
||||
注意:这个阶段仍不应直接让 `promotion_allowed=1`。native pass 只是打开下一步
|
||||
Node/browser proof 的条件。
|
||||
|
||||
六、第四阶段:更新 promotion readiness / blocker / lock
|
||||
|
||||
native pass 后,需要更新以下 gate 的逻辑和 fixtures:
|
||||
|
||||
```text
|
||||
runtime-boundary-promotion-readiness.tsv
|
||||
runtime-boundary-promotion-blockers.tsv
|
||||
runtime-boundary-post-native-pass-gates.tsv
|
||||
runtime-boundary-native-evidence-acceptance-gate.tsv
|
||||
blocked-runtime-promotion-lock.tsv
|
||||
next-boundary-recommendations.tsv
|
||||
promotion-candidates.tsv
|
||||
```
|
||||
|
||||
目标状态:
|
||||
|
||||
1. `native_evidence_ready=1`;
|
||||
2. `node_inventory_gate_complete=0`,直到 Node inventory row 真实执行;
|
||||
3. `browser_smoke_gate_complete=0`,直到 browser proof 真实通过;
|
||||
4. `promotion_lock_active=1`,直到人工明确解锁;
|
||||
5. blocker 从 `missing_host_runtime` 转为:
|
||||
|
||||
```text
|
||||
pending_node_inventory_promotion_gate
|
||||
pending_browser_smoke_gate
|
||||
promotion_lock_active
|
||||
```
|
||||
|
||||
这一步的重点是:native proof 已经完成,但还没有完成 Node/browser promotion,因此
|
||||
blocked family 不能直接消失。
|
||||
|
||||
七、第五阶段:设计 Node inventory execution path
|
||||
|
||||
只有 native DB protocol probe 通过后,才允许考虑让 `axis/db_demo/base.ngc` 进入
|
||||
Node inventory execution。
|
||||
|
||||
可接受方向:
|
||||
|
||||
1. 通过 LinuxCNC-owned tooldata DB protocol wrapper 执行;
|
||||
2. 保留 `DB_PROGRAM = ./db_nonran.py` 的行为边界;
|
||||
3. Node/WASM 侧只做 file staging、protocol bridge、result capture;
|
||||
4. 所有 tool DB state 来自 LinuxCNC protocol / db.py 行为,而不是 JS 自行解释。
|
||||
|
||||
不可接受方向:
|
||||
|
||||
1. 把 `axis/sim.tbl` 或任意 `.tbl` 当作 DB fallback;
|
||||
2. 在 JS 中手写 `T10..T19` tool DB state;
|
||||
3. 跳过 `v2.1` / `g` / `l` / `u` / `p` protocol;
|
||||
4. 只因为 native `bin/rs274` 能 parse `base.ngc` 就标记 Node inventory PASS;
|
||||
5. 把 virtual HAL readiness 当作 tool DB process readiness。
|
||||
|
||||
如果 Node/WASM 当前无法启动 Python DB process,则应保持:
|
||||
|
||||
```text
|
||||
node_inventory_gate_complete=0
|
||||
promotion_allowed=0
|
||||
```
|
||||
|
||||
并把下一步限定为“设计 tool DB protocol bridge”,而不是直接改 baseline。
|
||||
|
||||
八、第六阶段:Browser proof
|
||||
|
||||
Browser proof 只能在 Node inventory gate 真实完成后做。
|
||||
|
||||
Browser proof 至少需要:
|
||||
|
||||
- diagnostics artifact 记录 tool DB protocol proof status;
|
||||
- release URL workflow 显示 `axis/db_demo/base.ngc` 的 DB proof;
|
||||
- 明确 `tool_table_fallback_sufficient=0`;
|
||||
- 明确 `python3_sufficient=0`;
|
||||
- 明确 source owner 是 LinuxCNC DB protocol;
|
||||
- browser 只呈现 proof / workflow / artifact,不自行执行 tool DB semantics。
|
||||
|
||||
九、第七阶段:真实 inventory baseline promotion
|
||||
|
||||
只有满足以下条件后,才允许把 `axis/db_demo/base.ngc` 从 skipped 变为 executed:
|
||||
|
||||
1. native DB runtime probe passed;
|
||||
2. Node inventory execution path 通过 LinuxCNC-owned DB protocol;
|
||||
3. browser proof / release diagnostics gate 已通过;
|
||||
4. promotion lock 手动关闭;
|
||||
5. `promotion-candidates.tsv` 中对应行从:
|
||||
|
||||
```text
|
||||
candidate_kind=inventory-ready
|
||||
current_status=SKIP
|
||||
skip_kind=L4-TOOL-DB
|
||||
promotion_allowed=0
|
||||
```
|
||||
|
||||
变为真实可 promotion 状态,并有明确 proof chain;
|
||||
6. `summary.tsv` 中 `axis/db_demo/base.ngc` 变为:
|
||||
|
||||
```text
|
||||
inventory_status=PASS
|
||||
skip_or_fail_reason=-
|
||||
```
|
||||
|
||||
预期 baseline 变化:
|
||||
|
||||
```text
|
||||
executed: 28 -> 29
|
||||
passed: 28 -> 29
|
||||
skipped: 131 -> 130
|
||||
unexpected_fail: 0
|
||||
```
|
||||
|
||||
十、需要同步更新的文件/断言
|
||||
|
||||
baseline 真实变化后,至少同步:
|
||||
|
||||
```text
|
||||
wasm-port/docs/sim-configs-coverage-matrix.md
|
||||
wasm-port/docs/sim-config-coverage-promotion-analysis.md
|
||||
wasm-port/docs/compatibility-validation.md
|
||||
wasm-port/runtime/sdk/src/linuxcnc-hal.js
|
||||
wasm-port/runtime/sdk/src/project-release-readiness.js
|
||||
wasm-port/tests/fixtures/project-release-readiness-ready.json
|
||||
wasm-port/tests/sdk/node/verify_sdk_surface.mjs
|
||||
wasm-port/tests/sdk/node/verify_project_release_gate_manifest.mjs
|
||||
wasm-port/tests/host/verify_project_release_readiness_artifact.mjs
|
||||
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs
|
||||
```
|
||||
|
||||
需要同步更新的固定 baseline:
|
||||
|
||||
```text
|
||||
VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE
|
||||
project release readiness fixture simConfigInventory
|
||||
coverage matrix Current Node inventory
|
||||
compatibility validation current gate text
|
||||
promotion candidate expected status
|
||||
skip-summary expected count
|
||||
```
|
||||
|
||||
十一、最小验证命令
|
||||
|
||||
host runtime 准备阶段:
|
||||
|
||||
```text
|
||||
wasm-port/tests/native/probe_tool_db_runtime.sh
|
||||
```
|
||||
|
||||
native runtime proof 阶段:
|
||||
|
||||
```text
|
||||
ENABLE_TOOL_DB_RUNTIME_PROBE=1 wasm-port/tests/native/probe_tool_db_runtime.sh
|
||||
```
|
||||
|
||||
artifact / inventory 阶段:
|
||||
|
||||
```text
|
||||
git diff --check
|
||||
wasm-port/tools/verify_no_standalone_cnc_semantics.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
wasm-port/tests/sdk/node/verify_sdk_surface.sh
|
||||
wasm-port/tests/sdk/node/verify_project_release_gate_manifest.sh
|
||||
```
|
||||
|
||||
browser / release proof 阶段:
|
||||
|
||||
```text
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh
|
||||
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh
|
||||
wasm-port/tests/host/verify_project_release_readiness_artifact.sh
|
||||
```
|
||||
|
||||
十二、下一步工作内容
|
||||
|
||||
当前机器 artifact 显示缺少:
|
||||
|
||||
```text
|
||||
linuxcnc
|
||||
milltask
|
||||
halcmd
|
||||
```
|
||||
|
||||
因此下一步不是改 Node inventory baseline,而是:
|
||||
|
||||
1. 在具备完整 LinuxCNC host runtime 的环境中运行:
|
||||
|
||||
```text
|
||||
wasm-port/tests/native/probe_tool_db_runtime.sh
|
||||
```
|
||||
|
||||
2. 如果状态变为 `ready_disabled_by_default`,继续运行:
|
||||
|
||||
```text
|
||||
ENABLE_TOOL_DB_RUNTIME_PROBE=1 wasm-port/tests/native/probe_tool_db_runtime.sh
|
||||
```
|
||||
|
||||
3. 拿到 `runtime_protocol_probe_passed` 后,刷新 inventory artifacts:
|
||||
|
||||
```text
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
```
|
||||
|
||||
4. 再进入 promotion readiness / lock 更新,而不是直接把
|
||||
`axis/db_demo/base.ngc` 改成 PASS。
|
||||
|
||||
十三、简要结论
|
||||
|
||||
tool DB 的解锁关键不是 virtual HAL 能力不足,而是还缺 LinuxCNC task/tooldata DB
|
||||
process runtime proof。当前 source evidence、contract、readiness artifact、probe gate
|
||||
都已铺好;真正的第一步是补齐 host runtime 并跑通
|
||||
`ENABLE_TOOL_DB_RUNTIME_PROBE=1 wasm-port/tests/native/probe_tool_db_runtime.sh`。
|
||||
538
text21.txt
Normal file
538
text21.txt
Normal file
@@ -0,0 +1,538 @@
|
||||
项目接续文件:WASM + OPFS 环境下 tool DB 完全解锁可行性与实施步骤
|
||||
|
||||
生成时间:2026-06-18 CST
|
||||
|
||||
本文件接替 `text20.txt`,进一步回答:在以 WASM 和 OPFS 为技术基础的 Web 技术环境下,
|
||||
`L4-TOOL-DB` 是否可以完全解锁,以及怎样以可实现、可验证、不过度伪装的方式解锁。
|
||||
|
||||
重要状态说明:本文件是 future feasibility / architecture note,不是当前阶段执行计划。
|
||||
当前阶段必须按铁律收束:virtual HAL 成果只作为 sim config promotion 证据基础;
|
||||
`L4-PYTHON-REMAP`、`L4-TOOL-DB`、`L4-USER-M-PROCESS` 仍保持 hard block,不做解锁。
|
||||
本文件中的 WASM + OPFS tool DB 解锁路线只能作为后续专门 milestone 使用,不能用于
|
||||
当前 baseline promotion。
|
||||
|
||||
一、结论
|
||||
|
||||
可以实现“项目定义下的完全解锁”,但不能通过浏览器原生 spawn LinuxCNC native process
|
||||
的方式实现。
|
||||
|
||||
更准确地说:
|
||||
|
||||
```text
|
||||
如果“完全解锁”要求浏览器直接启动 native linuxcnc/milltask/halcmd/DB_PROGRAM 进程,
|
||||
答案是不可以。
|
||||
|
||||
如果“完全解锁”定义为:在 WASM + OPFS 中保留 LinuxCNC-owned tool DB protocol、
|
||||
执行配置声明的 DB_PROGRAM 行为、持久化 DB state、并通过 native/WASM/browser/release
|
||||
proof chain 证明行为等价,答案是可以。
|
||||
```
|
||||
|
||||
因此可行路线不是 `.tbl` fallback,也不是 JS 手写 tool semantics,而是:
|
||||
|
||||
```text
|
||||
WASM 内运行 LinuxCNC tooldata DB protocol owner + WASM/Worker 隔离的 DB_PROGRAM runtime +
|
||||
OPFS-backed flat-file persistence + source-derived proof gates。
|
||||
```
|
||||
|
||||
二、必须保持的语义边界
|
||||
|
||||
`axis/db_demo/base.ngc` 的 LinuxCNC-owned 行为来自:
|
||||
|
||||
- `db_nonran.ini` 中的 `DB_PROGRAM = ./db_nonran.py`;
|
||||
- LinuxCNC task/tooldata DB mode;
|
||||
- `tooldata_db.cc` 的 DB process protocol;
|
||||
- `tooldb.py` 的 protocol loop;
|
||||
- `db.py` / `db_nonran.py` 的 callback/state behavior;
|
||||
- flat-file database persistence。
|
||||
|
||||
WASM + OPFS 解锁时必须保持:
|
||||
|
||||
- `v2.1` startup handshake;
|
||||
- `g` get-all until `FINI`;
|
||||
- `l` spindle load notify;
|
||||
- `u` spindle unload notify;
|
||||
- `p` tool offset notify;
|
||||
- nonrandom `T10..T19` startup state;
|
||||
- DB file persistence;
|
||||
- `DB_PROGRAM` 是配置声明的 runtime dependency;
|
||||
- `.tbl` fallback 仍然标记为 insufficient。
|
||||
|
||||
三、浏览器环境限制
|
||||
|
||||
浏览器/WASM 环境天然缺少:
|
||||
|
||||
- native process spawn;
|
||||
- LinuxCNC host daemon/process model;
|
||||
- direct `milltask` process;
|
||||
- direct `halcmd` host process;
|
||||
- unrestricted POSIX filesystem。
|
||||
|
||||
但浏览器/WASM 环境具备:
|
||||
|
||||
- WASM module execution;
|
||||
- Web Worker 隔离执行单元;
|
||||
- MessageChannel / stream-style protocol;
|
||||
- OPFS 持久文件系统;
|
||||
- Emscripten FS 与 OPFS bridge;
|
||||
- 可移植 Python/WASM runtime 选项;
|
||||
- release/browser diagnostics artifact。
|
||||
|
||||
所以解锁方法必须把 native process boundary 改写为“协议等价 boundary”,而不是假装浏览器
|
||||
可以直接运行 native child process。
|
||||
|
||||
四、推荐总体架构
|
||||
|
||||
### 1. Tool DB protocol owner 仍在 LinuxCNC/WASM core
|
||||
|
||||
将 LinuxCNC tooldata DB protocol owner 保持在 C/C++ WASM 侧:
|
||||
|
||||
- 复用/移植 `tooldata_db.cc`;
|
||||
- 复用/移植 `tooldata_common.cc`;
|
||||
- 保留 protocol message 生成与解析;
|
||||
- 只把 process I/O edge 抽象为 host adapter。
|
||||
|
||||
建议新增抽象:
|
||||
|
||||
```text
|
||||
ToolDbProcessPort
|
||||
```
|
||||
|
||||
职责:
|
||||
|
||||
- start configured DB program;
|
||||
- write protocol line;
|
||||
- read reply line;
|
||||
- close / cleanup;
|
||||
- expose protocol transcript for diagnostics。
|
||||
|
||||
不允许职责:
|
||||
|
||||
- 自行解释 tool table;
|
||||
- 自行构造 `T10..T19`;
|
||||
- 跳过 `tooldb.py`;
|
||||
- 用 `.tbl` fallback。
|
||||
|
||||
### 2. DB_PROGRAM 在 Web Worker / WASM Python runtime 中运行
|
||||
|
||||
推荐把 `db_nonran.py` 放进独立 Web Worker,使用 WASM Python runtime 执行。
|
||||
|
||||
可选实现:
|
||||
|
||||
- CPython/WASM;
|
||||
- Pyodide;
|
||||
- 项目内裁剪 Python/WASM runtime;
|
||||
- 后续若项目已有 Python-remap runtime,可复用同一个 Python runtime substrate。
|
||||
|
||||
Worker 内需要挂载:
|
||||
|
||||
- `db_nonran.py`;
|
||||
- `db.py`;
|
||||
- `tooldb.py`;
|
||||
- minimal `linuxcnc` Python compatibility module;
|
||||
- OPFS-backed DB flat file。
|
||||
|
||||
这里的 minimal `linuxcnc` module 只能覆盖 DB demo 必需的 integration surface,例如:
|
||||
|
||||
- `linuxcnc.command().load_tool_table`
|
||||
|
||||
并且必须作为 process integration shim 记录,不允许承载 tool semantics。
|
||||
|
||||
### 3. OPFS 负责 DB persistence
|
||||
|
||||
将 DB demo 的 flat-file database 映射到 OPFS,例如:
|
||||
|
||||
```text
|
||||
/machines/<machine-id>/tool-db/db_nonran_file
|
||||
```
|
||||
|
||||
要求:
|
||||
|
||||
- startup 前可加载已有 OPFS DB file;
|
||||
- protocol mutation 后写回 OPFS;
|
||||
- reload session 后 DB state 保持;
|
||||
- diagnostics artifact 导出 DB file hash / state summary / transcript hash;
|
||||
- tests 能验证 persistence roundtrip。
|
||||
|
||||
### 4. Browser diagnostics 只证明,不伪装
|
||||
|
||||
browser UI / diagnostics 需要记录:
|
||||
|
||||
- DB runtime mode:`wasm-tool-db-protocol-worker`;
|
||||
- DB program path;
|
||||
- protocol transcript;
|
||||
- OPFS DB file path;
|
||||
- source files;
|
||||
- `.tbl fallback sufficient = false`;
|
||||
- Python-only sufficient = false;
|
||||
- native proof status;
|
||||
- WASM proof status;
|
||||
- browser proof status。
|
||||
|
||||
五、分阶段实施路线
|
||||
|
||||
### Phase 0:保持当前 lock,不改 baseline
|
||||
|
||||
当前状态仍是:
|
||||
|
||||
```text
|
||||
axis/db_demo/base.ngc SKIP L4-TOOL-DB
|
||||
executed=28
|
||||
passed=28
|
||||
skipped=131
|
||||
unexpected_fail=0
|
||||
```
|
||||
|
||||
先不要改成 PASS。
|
||||
|
||||
保留:
|
||||
|
||||
- `promotion_allowed=0`
|
||||
- `execution_enabled=0`
|
||||
- `promotion_lock_active=1`
|
||||
|
||||
### Phase 1:native proof 先闭环
|
||||
|
||||
目的:证明 source-derived protocol contract 与真实 LinuxCNC DB runtime 一致。
|
||||
|
||||
在完整 LinuxCNC host runtime 中执行:
|
||||
|
||||
```text
|
||||
wasm-port/tests/native/probe_tool_db_runtime.sh
|
||||
ENABLE_TOOL_DB_RUNTIME_PROBE=1 wasm-port/tests/native/probe_tool_db_runtime.sh
|
||||
```
|
||||
|
||||
目标输出:
|
||||
|
||||
```text
|
||||
tool_db_runtime_probe_status=runtime_protocol_probe_passed
|
||||
```
|
||||
|
||||
然后刷新:
|
||||
|
||||
```text
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
```
|
||||
|
||||
预期 artifact 转为:
|
||||
|
||||
```text
|
||||
source_proof_ready=1
|
||||
runtime_ready=1
|
||||
native_evidence_ready=1
|
||||
gate_status=native_probe_passed_waiting_for_node_browser_promotion_proof
|
||||
promotion_allowed=0
|
||||
```
|
||||
|
||||
### Phase 2:设计 WASM tool DB process port
|
||||
|
||||
新增 C/C++ 或 JS/WASM bridge 层,但协议 owner 必须保留 LinuxCNC source-derived 逻辑。
|
||||
|
||||
建议新增文件范围:
|
||||
|
||||
```text
|
||||
wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_tool_db_process_port.*
|
||||
wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_tool_db_protocol_harness.*
|
||||
wasm-port/runtime/sdk/src/linuxcnc-tool-db.js
|
||||
wasm-port/runtime/opfs/linuxcnc-tool-db-store.js
|
||||
```
|
||||
|
||||
核心 API 草案:
|
||||
|
||||
```text
|
||||
createLinuxCncToolDbRuntime({
|
||||
dbProgramPath,
|
||||
sourceFiles,
|
||||
opfsRoot,
|
||||
pythonRuntime,
|
||||
})
|
||||
|
||||
startToolDbProgram()
|
||||
sendToolDbProtocolMessage(line)
|
||||
readToolDbProtocolReply()
|
||||
runToolDbTransactionPlan(plan)
|
||||
exportToolDbDiagnostics()
|
||||
```
|
||||
|
||||
验证要求:
|
||||
|
||||
- API 返回 protocol transcript;
|
||||
- transcript 包含 `v2.1` / `g` / `FINI` / `l` / `p` / `u`;
|
||||
- DB state 不能来自 JS fixture hardcode;
|
||||
- source file hash 必须可追踪。
|
||||
|
||||
### Phase 3:WASM Python / DB_PROGRAM runtime
|
||||
|
||||
新增或接入 Python/WASM runtime。
|
||||
|
||||
最低目标不是完整 Python remap runtime,而是 tool DB demo runtime:
|
||||
|
||||
- 能 import `tooldb.py`;
|
||||
- 能 import `db.py` / `db_nonran.py`;
|
||||
- 能读写 OPFS-backed DB file;
|
||||
- 能通过 stdin/stdout 或 MessageChannel 模拟 process protocol;
|
||||
- 能提供 minimal `linuxcnc.command().load_tool_table` integration shim。
|
||||
|
||||
必须新增 negative tests:
|
||||
|
||||
- 没有 `db_nonran.py` 时 fail;
|
||||
- 没有 `tooldb.py` 时 fail;
|
||||
- 只有 `python3` / Python runtime 但没有 protocol transcript 时 fail;
|
||||
- 使用 `.tbl` fallback 时 fail;
|
||||
- JS 直接返回 `T10..T19` 而没有 DB_PROGRAM transcript 时 fail。
|
||||
|
||||
### Phase 4:WASM protocol transaction tests
|
||||
|
||||
新增 Node/WASM tests,先不改 full inventory baseline。
|
||||
|
||||
建议新增:
|
||||
|
||||
```text
|
||||
wasm-port/tests/wasm/node/verify_tool_db_process_wasm.sh
|
||||
wasm-port/tests/wasm/node/verify_tool_db_process_wasm.mjs
|
||||
```
|
||||
|
||||
测试内容:
|
||||
|
||||
1. stage `db_nonran.ini` / `db_nonran.py` / `db.py` / `tooldb.py`;
|
||||
2. start WASM DB program runtime;
|
||||
3. verify startup `v2.1`;
|
||||
4. send `g` and verify `T10..T19` plus `FINI`;
|
||||
5. send `l` load notify and verify pocket/spindle state;
|
||||
6. send `p` offset/update notify and verify DB mutation;
|
||||
7. send `u` unload notify and verify unload state;
|
||||
8. persist to OPFS-backed store;
|
||||
9. reload and verify state survives;
|
||||
10. export transcript artifact。
|
||||
|
||||
输出建议:
|
||||
|
||||
```text
|
||||
tool_db_process_wasm_protocol=ok
|
||||
tool_db_process_wasm_persistence=ok
|
||||
tool_db_process_wasm_no_tbl_fallback=ok
|
||||
```
|
||||
|
||||
### Phase 5:接入 inventory pre-promotion artifacts
|
||||
|
||||
在不改 baseline 的前提下,新增或扩展 artifact:
|
||||
|
||||
```text
|
||||
tool-db-process-wasm-protocol-proof.tsv
|
||||
tool-db-process-opfs-persistence-proof.tsv
|
||||
tool-db-process-browser-proof-gate.tsv
|
||||
```
|
||||
|
||||
字段建议:
|
||||
|
||||
- path;
|
||||
- ini;
|
||||
- db_program;
|
||||
- protocol_transcript_hash;
|
||||
- opfs_db_file;
|
||||
- startup_handshake_passed;
|
||||
- get_all_passed;
|
||||
- load_notify_passed;
|
||||
- offset_notify_passed;
|
||||
- unload_notify_passed;
|
||||
- persistence_passed;
|
||||
- tbl_fallback_sufficient;
|
||||
- python_only_sufficient;
|
||||
- source_derived;
|
||||
- execution_enabled;
|
||||
- promotion_allowed。
|
||||
|
||||
在这一阶段:
|
||||
|
||||
```text
|
||||
execution_enabled=0
|
||||
promotion_allowed=0
|
||||
```
|
||||
|
||||
仍保持 lock。
|
||||
|
||||
### Phase 6:Browser proof
|
||||
|
||||
新增 browser proof,但只证明 protocol runtime 和 OPFS persistence,不直接宣称 inventory pass。
|
||||
|
||||
建议新增或扩展:
|
||||
|
||||
```text
|
||||
wasm-port/tests/browser/verify_real_simulation_browser.sh
|
||||
wasm-port/tests/browser/real_simulation_page_smoke.html
|
||||
```
|
||||
|
||||
验证:
|
||||
|
||||
- browser 可以启动 tool DB Worker;
|
||||
- browser diagnostics 包含 DB protocol transcript summary;
|
||||
- OPFS persistence roundtrip 成功;
|
||||
- `.tbl fallback sufficient=false`;
|
||||
- `python_only_sufficient=false`;
|
||||
- release artifact URL workflow 能显示 tool DB proof;
|
||||
- missing DB proof negative fixture 会 fail。
|
||||
|
||||
### Phase 7:关闭 promotion lock 前的三方对齐
|
||||
|
||||
只有以下全部满足,才能准备关闭 lock:
|
||||
|
||||
```text
|
||||
native_db_process_protocol_probe_passed=1
|
||||
wasm_tool_db_protocol_passed=1
|
||||
browser_tool_db_protocol_proof_passed=1
|
||||
opfs_persistence_passed=1
|
||||
tbl_fallback_sufficient=0
|
||||
python_only_sufficient=0
|
||||
source_derived=1
|
||||
```
|
||||
|
||||
更新:
|
||||
|
||||
```text
|
||||
runtime-boundary-promotion-readiness.tsv
|
||||
runtime-boundary-promotion-blockers.tsv
|
||||
runtime-boundary-post-native-pass-gates.tsv
|
||||
runtime-boundary-native-evidence-acceptance-gate.tsv
|
||||
blocked-runtime-promotion-lock.tsv
|
||||
promotion-candidates.tsv
|
||||
```
|
||||
|
||||
目标状态:
|
||||
|
||||
```text
|
||||
native_evidence_ready=1
|
||||
node_inventory_gate_complete=1
|
||||
browser_smoke_gate_complete=1
|
||||
promotion_lock_active=0
|
||||
promotion_ready=1
|
||||
promotion_allowed=1
|
||||
```
|
||||
|
||||
### Phase 8:正式 inventory baseline promotion
|
||||
|
||||
更新 `verify_sim_configs_inventory_wasm.mjs`,允许 `axis/db_demo/base.ngc` 走 tool DB
|
||||
protocol execution path。
|
||||
|
||||
要求:
|
||||
|
||||
- 执行时必须通过 WASM tool DB protocol runtime;
|
||||
- 不能直接使用 `.tbl`;
|
||||
- 失败时不能降级为 SKIP;
|
||||
- protocol proof 缺失时必须 FAIL,而不是 silent fallback。
|
||||
|
||||
预期 `summary.tsv`:
|
||||
|
||||
```text
|
||||
axis/db_demo/base.ngc PASS - main PASS -
|
||||
```
|
||||
|
||||
预期 baseline:
|
||||
|
||||
```text
|
||||
executed=29
|
||||
passed=29
|
||||
skipped=130
|
||||
unexpected_fail=0
|
||||
```
|
||||
|
||||
同步更新:
|
||||
|
||||
```text
|
||||
VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE
|
||||
project release readiness fixture
|
||||
coverage matrix Current Node inventory
|
||||
compatibility validation current gate text
|
||||
skip-summary expected count
|
||||
promotion candidate expected status
|
||||
release readiness artifact
|
||||
batch acceptance artifact
|
||||
```
|
||||
|
||||
六、可实现性的关键判断
|
||||
|
||||
### 可以实现的部分
|
||||
|
||||
1. WASM 内保存 LinuxCNC tool DB protocol owner。
|
||||
2. Web Worker 作为 DB_PROGRAM process boundary 等价层。
|
||||
3. WASM Python runtime 执行 `db_nonran.py` / `tooldb.py`。
|
||||
4. OPFS 保存 DB flat-file。
|
||||
5. Node/WASM/browser 三层 proof。
|
||||
6. release artifact 记录 proof chain。
|
||||
7. inventory baseline 从 `28/28/131/0` 推进到 `29/29/130/0`。
|
||||
|
||||
### 不应承诺的部分
|
||||
|
||||
1. 浏览器直接运行 native `milltask`。
|
||||
2. 浏览器直接运行 host `halcmd`。
|
||||
3. 浏览器直接 spawn OS child process。
|
||||
4. 不经 Python DB program 而直接伪造 DB state。
|
||||
5. 用 `.tbl` 替代 `DB_PROGRAM`。
|
||||
|
||||
七、推荐实施顺序
|
||||
|
||||
最务实顺序:
|
||||
|
||||
```text
|
||||
1. 先在 native host 上跑通 ENABLE_TOOL_DB_RUNTIME_PROBE=1。
|
||||
2. 加 tool DB WASM protocol proof,不改 baseline。
|
||||
3. 加 OPFS persistence proof,不改 baseline。
|
||||
4. 加 browser diagnostics proof,不改 baseline。
|
||||
5. 更新 promotion readiness / lock。
|
||||
6. 最后才把 axis/db_demo/base.ngc 纳入 inventory PASS。
|
||||
```
|
||||
|
||||
八、最小验证命令
|
||||
|
||||
native:
|
||||
|
||||
```text
|
||||
wasm-port/tests/native/probe_tool_db_runtime.sh
|
||||
ENABLE_TOOL_DB_RUNTIME_PROBE=1 wasm-port/tests/native/probe_tool_db_runtime.sh
|
||||
```
|
||||
|
||||
WASM/Node:
|
||||
|
||||
```text
|
||||
wasm-port/tests/wasm/node/verify_tool_db_process_wasm.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
```
|
||||
|
||||
Browser:
|
||||
|
||||
```text
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh
|
||||
```
|
||||
|
||||
Release:
|
||||
|
||||
```text
|
||||
wasm-port/tests/sdk/node/verify_sdk_surface.sh
|
||||
wasm-port/tests/sdk/node/verify_project_release_gate_manifest.sh
|
||||
wasm-port/tests/host/verify_project_release_readiness_artifact.sh
|
||||
```
|
||||
|
||||
Safety:
|
||||
|
||||
```text
|
||||
git diff --check
|
||||
wasm-port/tools/verify_no_standalone_cnc_semantics.sh
|
||||
```
|
||||
|
||||
九、下一步工作内容
|
||||
|
||||
下一轮建议不要直接改 `axis/db_demo/base.ngc` 的 inventory status。先做最小可行
|
||||
WASM proof:
|
||||
|
||||
```text
|
||||
新增 verify_tool_db_process_wasm.mjs / .sh,证明 tool DB protocol transcript 和 OPFS
|
||||
persistence 可以在 WASM + OPFS 环境中闭环,同时继续保持 promotion_allowed=0。
|
||||
```
|
||||
|
||||
该 proof 的完成标准:
|
||||
|
||||
- `db_nonran.py` / `tooldb.py` 被真实加载;
|
||||
- protocol transcript 中出现 `v2.1`、`g`、`FINI`、`l`、`p`、`u`;
|
||||
- OPFS persistence roundtrip 通过;
|
||||
- `.tbl fallback` negative fixture 失败;
|
||||
- artifact 明确记录 `promotion_allowed=0`。
|
||||
|
||||
完成后再进入 browser proof 和 promotion lock 更新。
|
||||
202
text22.txt
Normal file
202
text22.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
项目接续文件:按铁律收束当前阶段 promotion 工作
|
||||
|
||||
生成时间:2026-06-18 CST
|
||||
|
||||
本文件接替 `text19.txt`,并对 `text20.txt` / `text21.txt` 做范围收束。当前阶段铁律为:
|
||||
|
||||
```text
|
||||
virtual HAL 成果作为 sim config promotion 证据基础;
|
||||
同时明确不解锁 Python remap / tool DB / external user-M 这些 hard block。
|
||||
```
|
||||
|
||||
一、当前阶段完成定义
|
||||
|
||||
当前阶段要完成的是 evidence-based promotion,不是 hard-block runtime 解锁。
|
||||
|
||||
允许做:
|
||||
|
||||
- 复用 virtual HAL source compliance;
|
||||
- 复用 virtual HAL sim-config source coverage;
|
||||
- 复用 virtual HAL promotion candidate report;
|
||||
- 复用 macro/load fixture report;
|
||||
- 复用 command script fixture report;
|
||||
- 复用 manifest-backed motion controller matrix;
|
||||
- 复用 browser diagnostics artifact;
|
||||
- 复用 release readiness / URL workflow / batch acceptance evidence;
|
||||
- 生成并维护 `promotion-candidates.tsv`,把候选分为:
|
||||
- `evidence-ready`;
|
||||
- `inventory-ready`。
|
||||
|
||||
禁止做:
|
||||
|
||||
- 不新增 virtual HAL runtime capability;
|
||||
- 不用 virtual HAL 替代 LinuxCNC CNC 语义;
|
||||
- 不用 JS/browser glue 解锁 Python remap;
|
||||
- 不用 JS/browser glue 解锁 tool DB;
|
||||
- 不用 JS/browser glue 解锁 external user-M process;
|
||||
- 不把 `.tbl` fallback 当作 tool DB pass;
|
||||
- 不把 macro/load asset 当 standalone main program;
|
||||
- 不靠改 docs 声称 skipped 下降。
|
||||
|
||||
二、当前 hard block 状态
|
||||
|
||||
以下 hard block 在当前阶段必须保持 locked:
|
||||
|
||||
```text
|
||||
L4-PYTHON-REMAP
|
||||
L4-TOOL-DB
|
||||
L4-USER-M-PROCESS
|
||||
UPSTREAM-DEMO
|
||||
standalone-main 语境下的 ASSET-ONLY
|
||||
```
|
||||
|
||||
当前 skip/block summary 仍保持:
|
||||
|
||||
```text
|
||||
ASSET-ONLY=65
|
||||
L4-PYTHON-REMAP=53
|
||||
L4-TOOL-DB=1
|
||||
L4-USER-M-PROCESS=1
|
||||
NON_MAIN_CLASS=10
|
||||
UPSTREAM-DEMO=1
|
||||
```
|
||||
|
||||
三、当前 baseline 必须保持
|
||||
|
||||
当前阶段完成后,Node inventory baseline 仍应为:
|
||||
|
||||
```text
|
||||
executed=28
|
||||
passed=28
|
||||
skipped=131
|
||||
unexpected_fail=0
|
||||
```
|
||||
|
||||
这不是失败,而是当前铁律下的正确结果:virtual HAL 已把一批可用 sim config row 推进到
|
||||
browser diagnostics / release evidence 层,但没有解锁 hard runtime family。
|
||||
|
||||
四、当前已完成的 evidence-ready promotion
|
||||
|
||||
`promotion-candidates.tsv` 中必须包含以下 `evidence-ready` rows:
|
||||
|
||||
- `qtdragon/qtdragon_multi_joint/on_abort.ngc`
|
||||
- `qtdragon/qtdragon_xyz/on_abort.ngc`
|
||||
- `qtdragon/qtdragon_xyz45/on_abort.ngc`
|
||||
- `qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc`
|
||||
- `qtdragon_hd/qtdragon_hd_z_compensation/on_abort.ngc`
|
||||
- `qtvcp_screens/qtdragon/on_abort.ngc`
|
||||
- `axis/vismach/puma/puma_seam_weld.ngc`
|
||||
- `axis/rose_engine/rcone_demo.ngc`
|
||||
|
||||
这些 rows 的语义:
|
||||
|
||||
```text
|
||||
current_status=PASS
|
||||
skip_kind=-
|
||||
matrix_layer4_node=INV
|
||||
matrix_layer4_browser=-
|
||||
blocked_kind=-
|
||||
virtual_hal_evidence_ready=1
|
||||
promotion_allowed=0
|
||||
block_reason=browser_release_evidence_ready_inventory_baseline_unchanged
|
||||
```
|
||||
|
||||
`promotion_allowed=0` 是有意的:这些 row 已有 browser/release evidence,但不代表
|
||||
inventory baseline 发生变化。
|
||||
|
||||
五、当前 inventory-ready hard block rows
|
||||
|
||||
`promotion-candidates.tsv` 中也会列出 `inventory-ready` 类型的 skipped main rows。
|
||||
这些行用于后续分析,不代表当前可解锁。
|
||||
|
||||
当前直接 inventory promotion 必须保持:
|
||||
|
||||
```text
|
||||
promotion_allowed=0
|
||||
```
|
||||
|
||||
原因:
|
||||
|
||||
- `L4-PYTHON-REMAP` 需要 LinuxCNC Python remap runtime;
|
||||
- `L4-TOOL-DB` 需要 tool database process protocol proof;
|
||||
- `L4-USER-M-PROCESS` 需要 external user-M process state proof;
|
||||
- `UPSTREAM-DEMO` 是 preserved upstream demo edge。
|
||||
|
||||
六、text20 / text21 的范围解释
|
||||
|
||||
`text20.txt` 和 `text21.txt` 只作为未来解锁研究记录:
|
||||
|
||||
- `text20.txt`:future tool DB native/runtime 解锁步骤;
|
||||
- `text21.txt`:future WASM + OPFS tool DB feasibility architecture。
|
||||
|
||||
它们不改变当前阶段铁律,不允许在当前阶段据此把 `L4-TOOL-DB` 解锁。
|
||||
|
||||
当前阶段若引用 `text20.txt` / `text21.txt`,只能作为:
|
||||
|
||||
- future milestone planning;
|
||||
- risk analysis;
|
||||
- architecture note;
|
||||
- blocked family proof requirement。
|
||||
|
||||
不能作为:
|
||||
|
||||
- 当前 baseline promotion 依据;
|
||||
- 当前 release ready 的解锁依据;
|
||||
- 当前 browser diagnostics positive pass 的替代证据。
|
||||
|
||||
七、当前阶段验收标准
|
||||
|
||||
当前阶段完成需要满足:
|
||||
|
||||
1. `promotion-candidates.tsv` 存在;
|
||||
2. `promotion-candidates.tsv` 同时包含 `evidence-ready` 和 `inventory-ready` 两层;
|
||||
3. `evidence-ready` rows 绑定 virtual HAL source-derived browser/release evidence;
|
||||
4. `inventory-ready` hard block rows 仍 `promotion_allowed=0`;
|
||||
5. baseline 保持 `28/28/131/0`;
|
||||
6. `L4-PYTHON-REMAP`、`L4-TOOL-DB`、`L4-USER-M-PROCESS` 仍 locked;
|
||||
7. docs 明确 artifact count 和 candidate report;
|
||||
8. release / SDK gate 仍要求 virtual HAL promotion candidate summary;
|
||||
9. 不新增 virtual HAL runtime capability;
|
||||
10. 不新增 JS-owned CNC semantics。
|
||||
|
||||
八、已验证命令
|
||||
|
||||
当前阶段最小验证命令:
|
||||
|
||||
```text
|
||||
git diff --check
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
wasm-port/tests/sdk/node/verify_sdk_surface.sh
|
||||
wasm-port/tests/sdk/node/verify_project_release_gate_manifest.sh
|
||||
```
|
||||
|
||||
预期 inventory 输出:
|
||||
|
||||
```text
|
||||
sim_configs_wasm_node_inventory_executed=28
|
||||
sim_configs_wasm_node_inventory_passed=28
|
||||
sim_configs_wasm_node_inventory_skipped=131
|
||||
sim_configs_wasm_node_inventory_unexpected_fail=0
|
||||
sim_configs_wasm_node_inventory=ok
|
||||
```
|
||||
|
||||
九、下一步建议
|
||||
|
||||
下一步不要直接解锁 hard block。按当前铁律,优先做:
|
||||
|
||||
1. 扩展 `evidence-ready` browser diagnostics 展示;
|
||||
2. 强化 release URL workflow 对 promotion candidate summary 的可见性;
|
||||
3. 对 `blocked=-`、native `PASS`、class `main` 的非 hard-block family 继续找可真实 promotion row;
|
||||
4. 对 Python remap / tool DB / external user-M 只维护 proof requirement 和 lock artifact;
|
||||
5. 如果要解锁 tool DB、Python remap 或 external user-M,必须另开 milestone,不能混入当前阶段。
|
||||
|
||||
十、简要结论
|
||||
|
||||
当前阶段已经按铁律完成:
|
||||
|
||||
```text
|
||||
virtual HAL 成果作为 sim config promotion 证据基础;
|
||||
Python remap / tool DB / external user-M hard block 不解锁;
|
||||
baseline 保持 28/28/131/0;
|
||||
promotion candidates 进入双层 evidence-ready / inventory-ready 管理。
|
||||
```
|
||||
@@ -204,6 +204,7 @@ wasm-port/build/wasm/sim-configs-inventory/boundary-phase-completion-summary.tsv
|
||||
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/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
|
||||
@@ -230,6 +231,13 @@ skip counts fail on blocked-policy drift.
|
||||
For eligible rows, missing vendored machine context is an inventory failure,
|
||||
not an expected skip; blocked rows must be classified by runtime dependency
|
||||
before the Node inventory filter runs.
|
||||
`promotion-candidates.tsv` records two promotion layers: `evidence-ready`
|
||||
rows whose existing virtual HAL source-derived browser/release evidence is
|
||||
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: all skipped main rows are still hard-blocked by `L4-PYTHON-REMAP`,
|
||||
`L4-TOOL-DB`, `L4-USER-M-PROCESS`, or preserved as `UPSTREAM-DEMO`.
|
||||
`boundary-summary.tsv` records one row per native inventory path with the
|
||||
matrix blocked kind, SDK classifier recommendation, declared HAL/UI/HALUI/Python
|
||||
process dependencies, `[EMCIO]DB_PROGRAM`, user-M execution codes, and the
|
||||
@@ -385,10 +393,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 54-entry baseline and the native generated
|
||||
to remain the exact duplicate-free 55-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 `54` and `8`, respectively. It also
|
||||
documentation-coverage counts to remain `55` 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
|
||||
|
||||
@@ -281,6 +281,336 @@ HAL from accidentally unlocking Python UI or Python remap families.
|
||||
The next practical slice is to add a machine-readable candidate audit for other
|
||||
UI-family macro/load rows before promoting any new positive fixture.
|
||||
|
||||
## 2026-06-18 UI-Family Macro/Load Candidate Audit
|
||||
|
||||
The macro/load report now carries audit rows for UI-family fixture candidates
|
||||
that are not ready for positive promotion:
|
||||
|
||||
- `gscreen/industrial_lathe_wear/toolchange.ngc`
|
||||
- `qtvcp_screens/industrial_lathe_wear/toolchange.ngc`
|
||||
|
||||
Both rows are recorded as `blocked-missing-owning-ini` audit candidates with
|
||||
`promotionAllowed === false`. They must remain excluded from positive
|
||||
macro/load fixtures until the owning INI, machine files, tool table, and
|
||||
UI/process declaration have LinuxCNC source evidence that does not require
|
||||
Python UI process emulation.
|
||||
|
||||
Release diagnostics validation now requires `auditPromotionViolations` to stay
|
||||
empty and requires both audit rows to be present as negative evidence. This is
|
||||
an audit gate, not a coverage promotion: inventory baseline remains unchanged.
|
||||
|
||||
## 2026-06-18 UI-Family Audit Boundary Evidence
|
||||
|
||||
The UI-family audit rows now carry machine-readable source evidence from the
|
||||
generated inventory boundary artifacts:
|
||||
|
||||
- `wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv`
|
||||
- `wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv`
|
||||
|
||||
For both industrial-lathe `toolchange.ngc` rows, the audit evidence requires
|
||||
`recommendedBlocked === "UNAVAILABLE"` and a `missing_vendored_ini:*`
|
||||
dependency for the owning INI. The INI-level row must also report
|
||||
`vendored === 0` and `reportAvailable === 0`.
|
||||
|
||||
The macro/load fixture report now exposes:
|
||||
|
||||
- `missingAuditRows`
|
||||
- `auditBoundaryEvidenceViolations`
|
||||
- `auditPromotionAllowedViolations`
|
||||
|
||||
Release diagnostics validation requires all of those lists to stay empty. This
|
||||
adds negative coverage for three failure modes: audit rows missing, audit rows
|
||||
accidentally promoted into positive fixtures, and `promotionAllowed === true`
|
||||
without generated boundary/source proof.
|
||||
|
||||
## 2026-06-18 SilverDragon Blocked Boundary Evidence
|
||||
|
||||
The blocked macro/load fixture rows now use the same generated boundary
|
||||
evidence contract. `gscreen/silverdragon/macros/tool_sensor.ngc` carries
|
||||
evidence from `boundary-summary.tsv` and `ini-boundary-summary.tsv` showing
|
||||
that the owning `gscreen/silverdragon/silverdragon.ini` is unavailable in the
|
||||
vendored inventory.
|
||||
|
||||
The report now exposes `blockedBoundaryEvidenceViolations`, and release
|
||||
diagnostics validation requires it to stay empty. This keeps the SilverDragon
|
||||
row as a blocked negative fixture with source-derived evidence, not a virtual
|
||||
HAL promotion path.
|
||||
|
||||
## 2026-06-18 Queuebuster Declaration Evidence
|
||||
|
||||
The declared-only `axis/external_offsets/queuebuster.ngc` fixture now carries
|
||||
declaration-level evidence from `eoffsets.ini`. The fixture report requires the
|
||||
owning INI to expose:
|
||||
|
||||
- section: `DISPLAY`
|
||||
- key: `NGCGUI_SUBFILE`
|
||||
- value: `queuebuster.ngc`
|
||||
- source line: `NGCGUI_SUBFILE = queuebuster.ngc`
|
||||
|
||||
This keeps `queuebuster.ngc` as a non-main fixture path declared by LinuxCNC
|
||||
configuration, not a standalone browser main program. Release diagnostics
|
||||
validation now requires `declarationEvidenceViolations` to stay empty.
|
||||
|
||||
## 2026-06-18 Rose Engine Declaration Evidence
|
||||
|
||||
The positive `axis/rose_engine/rcone.ngc` non-main fixture now carries the same
|
||||
declaration-level evidence contract as queuebuster. `rose_engine.ini` declares
|
||||
the fixture with `NGCGUI_SUBFILE = rcone.ngc`, and the macro/load report now
|
||||
requires every positive fixture with a `declarationSource` to carry matching
|
||||
source evidence.
|
||||
|
||||
This keeps `rcone.ngc` as a LinuxCNC-declared macro/load support asset paired
|
||||
with `rcone_demo.ngc`, not a standalone browser main program. The evidence is
|
||||
limited to the owning INI declaration and does not add browser-owned G-code,
|
||||
interpreter, planner, or remap behavior.
|
||||
|
||||
## 2026-06-18 Boundary Evidence Freshness Gate
|
||||
|
||||
Generated boundary evidence for blocked and audit-only macro/load rows now
|
||||
carries an inventory `baselineSummary`. The macro/load report requires that
|
||||
summary to match the current generated inventory baseline:
|
||||
|
||||
```text
|
||||
executed=28
|
||||
passed=28
|
||||
skipped=131
|
||||
unexpectedFail=0
|
||||
```
|
||||
|
||||
This makes `boundary-summary.tsv` and `ini-boundary-summary.tsv` evidence
|
||||
explicitly tied to the current inventory baseline. A stale baseline now fails
|
||||
the blocked/audit boundary evidence gate instead of silently passing release
|
||||
diagnostics.
|
||||
|
||||
## 2026-06-18 Promotion Family URL Workflow Summary
|
||||
|
||||
The release readiness validation and URL workflow summaries now surface the
|
||||
promotion candidate `familyRows` as a user-visible summary. Ready artifacts
|
||||
show:
|
||||
|
||||
```text
|
||||
qtdragon-on-abort: 6/6 ready; vismach-remap-sims: 1/1 ready; rose-engine-rcone-demo: 1/1 ready
|
||||
```
|
||||
|
||||
The ini-panel workflow overview URL summary reuses this value, so the
|
||||
QtDragon, PUMA, and rose-engine promotion families are visible without opening
|
||||
the raw diagnostics JSON. This is a presentation/reporting change only; it
|
||||
does not change inventory counts or CNC semantics.
|
||||
|
||||
## 2026-06-18 Structured Promotion Family Rows
|
||||
|
||||
The promotion family summary is now also exposed as structured rows. Each row
|
||||
records the family id, candidate count, complete count, source file count, and
|
||||
explicit browser diagnostics readiness. Current rows are:
|
||||
|
||||
```text
|
||||
qtdragon-on-abort: 6/6 ready; sources=12; diagnostics=ready
|
||||
vismach-remap-sims: 1/1 ready; sources=3; diagnostics=ready
|
||||
rose-engine-rcone-demo: 1/1 ready; sources=2; diagnostics=ready
|
||||
```
|
||||
|
||||
The release readiness artifact, validation summary, URL workflow summary, and
|
||||
ini-panel workflow overview all expose these rows. This keeps the summary
|
||||
machine-readable while preserving the existing single-line text summary.
|
||||
|
||||
## 2026-06-18 Boundary Evidence Artifact Hash Gate
|
||||
|
||||
Generated boundary evidence now includes SHA-256 hashes for the two source TSV
|
||||
artifacts used by blocked and audit macro/load evidence:
|
||||
|
||||
```text
|
||||
boundary-summary.tsv de6cf57b7c07182e3bcb32e22dbdf202b618cabc14620d6dff1ef815587950b9
|
||||
ini-boundary-summary.tsv b0afe27224e97a82fbecbbd75a7c86233c98fe957d9c1ba20f0656f8745a5eae
|
||||
```
|
||||
|
||||
The macro/load report and release validation require those hashes to match.
|
||||
This extends the freshness gate beyond inventory counts and makes stale or
|
||||
substituted boundary TSV evidence fail the release diagnostics gate.
|
||||
|
||||
## 2026-06-18 Browser Promotion Family DOM Rows
|
||||
|
||||
The real browser simulation page now renders the structured promotion family
|
||||
rows in the diagnostics panel. The visible browser DOM exposes:
|
||||
|
||||
```text
|
||||
qtdragon-on-abort: 6/6 ready; sources=12
|
||||
vismach-remap-sims: 1/1 ready; sources=3
|
||||
rose-engine-rcone-demo: 1/1 ready; sources=2
|
||||
```
|
||||
|
||||
The browser API also exposes `getVirtualHalPromotionFamilyRows()`, and the real
|
||||
simulation browser smoke asserts that the DOM rows, readiness dataset, and
|
||||
candidate report agree. This moves the existing source-derived promotion
|
||||
evidence from view-model/report data into a visible browser diagnostics surface.
|
||||
It does not change inventory counts or add CNC semantics.
|
||||
|
||||
## 2026-06-18 Fast Follow: Hash Tool And INI Panel Promotion DOM
|
||||
|
||||
The boundary TSV hash freshness gate now has an update/check helper:
|
||||
|
||||
```bash
|
||||
wasm-port/tools/update_sim_config_boundary_hashes.sh --check
|
||||
wasm-port/tools/update_sim_config_boundary_hashes.sh --write
|
||||
```
|
||||
|
||||
The script computes SHA-256 for `boundary-summary.tsv` and
|
||||
`ini-boundary-summary.tsv`, then checks or updates the SDK/release validation
|
||||
constants. This removes the manual hash-update step after generated inventory
|
||||
artifacts change.
|
||||
|
||||
The ini-panel workflow overview now also has a dedicated promotion family DOM
|
||||
surface. It can render and mount the three structured promotion rows from the
|
||||
release URL workflow summary into `[data-workflow-overview-promotion-family-rows]`.
|
||||
The UI shell smoke verifies the rows mount as ready with the expected QtDragon,
|
||||
PUMA, and rose-engine values.
|
||||
|
||||
## 2026-06-18 Promotion Family Source Drilldown
|
||||
|
||||
Promotion family rows now carry their LinuxCNC source file lists through the
|
||||
release readiness report, validation summary, URL workflow summary, and
|
||||
ini-panel workflow overview render state. The ini-panel promotion family DOM
|
||||
mount renders the three family rows plus per-family source-file drilldown rows.
|
||||
|
||||
Current drilldown count is 17 source files:
|
||||
|
||||
```text
|
||||
qtdragon-on-abort: 12 source files
|
||||
vismach-remap-sims: 3 source files
|
||||
rose-engine-rcone-demo: 2 source files
|
||||
```
|
||||
|
||||
This makes the browser/ini-panel evidence inspectable without opening the raw
|
||||
JSON artifact, while keeping the underlying behavior LinuxCNC-source-derived.
|
||||
|
||||
## 2026-06-18 Promotion Family Source Status
|
||||
|
||||
Each ini-panel promotion family source drilldown row now carries source status
|
||||
metadata:
|
||||
|
||||
- `ini` for LinuxCNC INI machine files;
|
||||
- `gcode` for `.ngc` main/on-abort programs;
|
||||
- `remap-subroutine` for files under `remap_subs` or `nc_subroutines`;
|
||||
- `manifest-backed` for rows already carried by the source-derived promotion
|
||||
evidence.
|
||||
|
||||
The DOM render adds dataset fields for source kind, manifest-backed state, and
|
||||
source status, so the UI can distinguish machine config, G-code, and remap
|
||||
support evidence without changing any CNC semantics.
|
||||
|
||||
## 2026-06-18 Promotion Family Source Kind Counts
|
||||
|
||||
The ini-panel promotion family render state now aggregates source-kind counts
|
||||
per family and exposes them in the family row DOM dataset. Current counts are:
|
||||
|
||||
```text
|
||||
qtdragon-on-abort: ini=6 gcode=6
|
||||
vismach-remap-sims: ini=1 gcode=1 remap-subroutine=1
|
||||
rose-engine-rcone-demo: ini=1 gcode=1
|
||||
```
|
||||
|
||||
The family row display includes this summary, while the per-source drilldown
|
||||
rows still expose individual source kind and manifest-backed status.
|
||||
|
||||
## 2026-06-18 Promotion Family Source Filter View Model
|
||||
|
||||
The ini-panel workflow overview now exposes a promotion family source filter
|
||||
view-model. It can filter the existing source drilldown by `all`, `ini`,
|
||||
`gcode`, `remap-subroutine`, or `source`, while preserving family grouping and
|
||||
source-kind counts.
|
||||
|
||||
Current filter totals are:
|
||||
|
||||
```text
|
||||
all=17
|
||||
ini=8
|
||||
gcode=8
|
||||
remap-subroutine=1
|
||||
```
|
||||
|
||||
The `remap-subroutine` filter currently resolves to the PUMA remap evidence
|
||||
row:
|
||||
|
||||
```text
|
||||
linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc
|
||||
```
|
||||
|
||||
This is a browser/UI evidence navigation helper only. It does not change the
|
||||
promotion candidate set, inventory baseline, or CNC runtime semantics.
|
||||
|
||||
## 2026-06-18 Promotion Family Source Family Filter
|
||||
|
||||
The source filter view-model now supports a second selector: `familyId`. This
|
||||
allows UI callers to scope source evidence by promotion family and then filter
|
||||
within that family by source kind.
|
||||
|
||||
Current family filter totals are:
|
||||
|
||||
```text
|
||||
all=17
|
||||
qtdragon-on-abort=12
|
||||
vismach-remap-sims=3
|
||||
rose-engine-rcone-demo=2
|
||||
```
|
||||
|
||||
For example, selecting `virtual-hal-promotion-family-qtdragon-on-abort` with
|
||||
`gcode` returns 6 QtDragon `.ngc` source rows from the 12-file family scope.
|
||||
The selector is a view-model feature only; it does not promote additional sim
|
||||
config rows or relax any blocked runtime family.
|
||||
|
||||
## 2026-06-18 Promotion Family Source Filter DOM Surface
|
||||
|
||||
The source filter view-model now has a dedicated DOM contract, readiness check,
|
||||
render helper, and mount helper. The DOM surface renders:
|
||||
|
||||
- family filter rows;
|
||||
- source-kind filter rows;
|
||||
- filtered source evidence rows;
|
||||
- selected family/source-kind dataset fields on the mount node.
|
||||
|
||||
The QtDragon + `gcode` combination currently renders 13 DOM rows: 7 filter
|
||||
rows and 6 filtered source rows. This moves the combined evidence filter from a
|
||||
view-model-only helper into a mountable ini-panel workflow overview surface.
|
||||
It still only displays existing LinuxCNC-source-derived evidence.
|
||||
|
||||
## 2026-06-18 Promotion Family Source Filter Selector Controls
|
||||
|
||||
The source filter DOM surface now renders two selector controls in addition to
|
||||
the diagnostic rows:
|
||||
|
||||
- a family selector backed by `familyFilters`;
|
||||
- a source-kind selector backed by `sourceKindFilters`.
|
||||
|
||||
For the QtDragon + `gcode` example, the controls expose the selected family
|
||||
`virtual-hal-promotion-family-qtdragon-on-abort` and selected source kind
|
||||
`gcode`, with the `gcode` option carrying count `6`. The selectors currently
|
||||
render selection state and option metadata for browser integration; they do not
|
||||
change the underlying promotion evidence or add CNC semantics.
|
||||
|
||||
## 2026-06-18 Promotion Family Source Filter Selection Workflow
|
||||
|
||||
The selector controls now have an action-plan and selection-change workflow.
|
||||
The workflow reads the current family/source-kind selector values, derives the
|
||||
next source filter view-model, and can optionally re-render the source filter
|
||||
DOM surface.
|
||||
|
||||
The current smoke path changes from QtDragon + `gcode` to
|
||||
`virtual-hal-promotion-family-rose-engine-rcone-demo` + `all`. That produces a
|
||||
new view-model with 2 filtered source files from a 2-file family scope and
|
||||
re-renders the DOM rows to include rose-engine source evidence. This is still a
|
||||
host/UI workflow over existing source-derived evidence only.
|
||||
|
||||
## 2026-06-18 Promotion Family Source Filter Event Binding
|
||||
|
||||
The selector controls now have a reusable event binding helper. It binds the
|
||||
family and source-kind `<select>` elements to the selection-change workflow,
|
||||
tracks the last change result, and can unbind both controls.
|
||||
|
||||
The current Node smoke binds two selector controls, changes the family selector
|
||||
to `virtual-hal-promotion-family-rose-engine-rcone-demo`, dispatches a `change`
|
||||
event, and verifies that the source filter DOM re-renders with 2 rose-engine
|
||||
source rows. This keeps the browser integration path explicit without changing
|
||||
promotion status or CNC semantics.
|
||||
|
||||
## Boundary Statement
|
||||
|
||||
This analysis relies on the improved virtual HAL only for Web simulation
|
||||
|
||||
@@ -62,6 +62,8 @@ Current baseline:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/next-boundary-worklist.tsv`
|
||||
- Node next-boundary recommendation artifact:
|
||||
`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 native evidence acceptance gate artifact:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-native-evidence-acceptance-gate.tsv`
|
||||
- Node promotion blocker summary artifact:
|
||||
@@ -349,7 +351,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 54-entry
|
||||
inventory artifact list must remain the exact duplicate-free 55-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
|
||||
@@ -362,7 +364,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 `54` WASM inventory
|
||||
documentation-coverage completion counts to the current `55` 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
|
||||
|
||||
@@ -70,6 +70,8 @@ import {
|
||||
VIRTUAL_HAL_COVERAGE_STATES,
|
||||
VIRTUAL_HAL_MOTION_CONTROLLER_MATRIX_FIXTURES,
|
||||
VIRTUAL_HAL_PROJECT_PIN_GROUPS,
|
||||
VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES,
|
||||
VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE,
|
||||
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
|
||||
VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES,
|
||||
VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS,
|
||||
@@ -327,8 +329,9 @@ private page state.
|
||||
files, current Node `PASS` evidence, blocked-family exclusions, target
|
||||
browser diagnostics evidence, and the required virtual HAL reports for
|
||||
QtDragon on-abort, PUMA seam-weld, and rose-engine promotion planning.
|
||||
- `VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES` and
|
||||
`VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES` plus
|
||||
- `VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES`,
|
||||
`VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES`, and
|
||||
`VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_AUDIT_CANDIDATES` plus
|
||||
`createVirtualHalSimConfigMacroLoadFixtureReport()` expose non-main
|
||||
macro/load fixture evidence for virtual HAL diagnostics. The report keeps
|
||||
`nonMainFixture === true`, separates fixture paths from manifest-backed
|
||||
@@ -336,7 +339,9 @@ private page state.
|
||||
`rcone.ngc` or external-offsets `queuebuster.ngc` from being promoted as
|
||||
standalone browser main programs. Blocked fixture rows such as SilverDragon
|
||||
`tool_sensor.ngc` remain excluded until their Python UI/process boundary is
|
||||
proven outside virtual HAL.
|
||||
proven outside virtual HAL. Audit candidates such as industrial-lathe
|
||||
`toolchange.ngc` rows stay machine-readable negative evidence until owning
|
||||
INI and UI/process source evidence is complete.
|
||||
- `createVirtualHalSourceComplianceReport()` is the machine-readable gate for
|
||||
the virtual HAL source rule. It maps replacement targets, runtime
|
||||
capabilities, and HAL pin families back to LinuxCNC source files such as
|
||||
|
||||
@@ -7,8 +7,11 @@ export {
|
||||
VIRTUAL_HAL_COVERAGE_STATES,
|
||||
VIRTUAL_HAL_MOTION_CONTROLLER_MATRIX_FIXTURES,
|
||||
VIRTUAL_HAL_PROJECT_PIN_GROUPS,
|
||||
VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_AUDIT_CANDIDATES,
|
||||
VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES,
|
||||
VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES,
|
||||
VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES,
|
||||
VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE,
|
||||
VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES,
|
||||
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
|
||||
VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES,
|
||||
@@ -186,6 +189,25 @@ export {
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState,
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilyRowsRenderState,
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterViewModel,
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomContract,
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomReadiness,
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan,
|
||||
handleIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionChange,
|
||||
bindIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionControls,
|
||||
renderIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState,
|
||||
mountIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState,
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilyRowsDomContract,
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilyRowsDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewPromotionFamilyRowsState,
|
||||
mountIniPanelShellWorkflowOverviewPromotionFamilyRowsState,
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownViewModel,
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownRenderState,
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomContract,
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState,
|
||||
mountIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
|
||||
|
||||
@@ -125,6 +125,20 @@ export const VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS = Object.freeze([
|
||||
}),
|
||||
]);
|
||||
|
||||
export const VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE = Object.freeze({
|
||||
executed: 28,
|
||||
passed: 28,
|
||||
skipped: 131,
|
||||
unexpectedFail: 0,
|
||||
});
|
||||
|
||||
export const VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES = Object.freeze({
|
||||
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv":
|
||||
"de6cf57b7c07182e3bcb32e22dbdf202b618cabc14620d6dff1ef815587950b9",
|
||||
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv":
|
||||
"b0afe27224e97a82fbecbbd75a7c86233c98fe957d9c1ba20f0656f8745a5eae",
|
||||
});
|
||||
|
||||
export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
|
||||
Object.freeze({
|
||||
id: "qtdragon-multi-joint-on-abort",
|
||||
@@ -137,7 +151,7 @@ export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
|
||||
iniPath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
|
||||
gcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
||||
currentNodeInventoryStatus: "PASS",
|
||||
currentMatrixBrowserStatus: "delegated-to-woodpecker",
|
||||
currentMatrixBrowserStatus: "explicit-browser-diagnostics",
|
||||
dependencyClass: "hal-ui-multi-joint",
|
||||
targetBrowserEvidence: "explicit-browser-diagnostics",
|
||||
requiredVirtualHalReports: Object.freeze([
|
||||
@@ -161,7 +175,7 @@ export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
|
||||
iniPath: "linuxcnc/configs/sim/qtdragon/qtdragon_xyz/qtdragon_inch.ini",
|
||||
gcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc",
|
||||
currentNodeInventoryStatus: "PASS",
|
||||
currentMatrixBrowserStatus: "delegated-to-woodpecker",
|
||||
currentMatrixBrowserStatus: "explicit-browser-diagnostics",
|
||||
dependencyClass: "hal-ui-on-abort",
|
||||
targetBrowserEvidence: "explicit-browser-diagnostics",
|
||||
requiredVirtualHalReports: Object.freeze([
|
||||
@@ -185,7 +199,7 @@ export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
|
||||
iniPath: "linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/qtdragon_xyza.ini",
|
||||
gcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/on_abort.ngc",
|
||||
currentNodeInventoryStatus: "PASS",
|
||||
currentMatrixBrowserStatus: "delegated-to-woodpecker",
|
||||
currentMatrixBrowserStatus: "explicit-browser-diagnostics",
|
||||
dependencyClass: "hal-ui-rotary-axis",
|
||||
targetBrowserEvidence: "explicit-browser-diagnostics",
|
||||
requiredVirtualHalReports: Object.freeze([
|
||||
@@ -209,7 +223,7 @@ export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
|
||||
iniPath: "linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/qtdragon_hd_vertical.ini",
|
||||
gcodePath: "linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc",
|
||||
currentNodeInventoryStatus: "PASS",
|
||||
currentMatrixBrowserStatus: "delegated-to-woodpecker",
|
||||
currentMatrixBrowserStatus: "explicit-browser-diagnostics",
|
||||
dependencyClass: "hal-ui-on-abort",
|
||||
targetBrowserEvidence: "explicit-browser-diagnostics",
|
||||
requiredVirtualHalReports: Object.freeze([
|
||||
@@ -233,7 +247,7 @@ export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
|
||||
iniPath: "linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/qtdragon_hd_z_compensation.ini",
|
||||
gcodePath: "linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/on_abort.ngc",
|
||||
currentNodeInventoryStatus: "PASS",
|
||||
currentMatrixBrowserStatus: "delegated-to-woodpecker",
|
||||
currentMatrixBrowserStatus: "explicit-browser-diagnostics",
|
||||
dependencyClass: "hal-ui-z-compensation",
|
||||
targetBrowserEvidence: "explicit-browser-diagnostics",
|
||||
requiredVirtualHalReports: Object.freeze([
|
||||
@@ -257,7 +271,7 @@ export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
|
||||
iniPath: "linuxcnc/configs/sim/qtvcp_screens/qtdragon/qtdragon_mpg.ini",
|
||||
gcodePath: "linuxcnc/configs/sim/qtvcp_screens/qtdragon/on_abort.ngc",
|
||||
currentNodeInventoryStatus: "PASS",
|
||||
currentMatrixBrowserStatus: "delegated-to-woodpecker",
|
||||
currentMatrixBrowserStatus: "explicit-browser-diagnostics",
|
||||
dependencyClass: "hal-ui-on-abort",
|
||||
targetBrowserEvidence: "explicit-browser-diagnostics",
|
||||
requiredVirtualHalReports: Object.freeze([
|
||||
@@ -282,7 +296,7 @@ export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
|
||||
iniPath: "linuxcnc/configs/sim/axis/vismach/puma/puma.ini",
|
||||
gcodePath: "linuxcnc/configs/sim/axis/vismach/puma/puma_seam_weld.ngc",
|
||||
currentNodeInventoryStatus: "PASS",
|
||||
currentMatrixBrowserStatus: "represented-by-puma-cube",
|
||||
currentMatrixBrowserStatus: "explicit-browser-diagnostics",
|
||||
dependencyClass: "halui-vismach-remap",
|
||||
targetBrowserEvidence: "explicit-browser-diagnostics",
|
||||
requiredVirtualHalReports: Object.freeze([
|
||||
@@ -306,7 +320,7 @@ export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
|
||||
iniPath: "linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
|
||||
gcodePath: "linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
|
||||
currentNodeInventoryStatus: "PASS",
|
||||
currentMatrixBrowserStatus: "not-promoted",
|
||||
currentMatrixBrowserStatus: "explicit-browser-diagnostics",
|
||||
dependencyClass: "hal-process-only",
|
||||
targetBrowserEvidence: "explicit-browser-diagnostics",
|
||||
requiredVirtualHalReports: Object.freeze([
|
||||
@@ -334,6 +348,14 @@ export const VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES = Object.freeze([
|
||||
owningIniPath: "linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
|
||||
pairedMainProgram: "linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
|
||||
declarationSource: "NGCGUI_SUBFILE",
|
||||
declarationEvidence: Object.freeze({
|
||||
sourceFile: "linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
|
||||
section: "DISPLAY",
|
||||
key: "NGCGUI_SUBFILE",
|
||||
value: "rcone.ngc",
|
||||
line: 34,
|
||||
sourceLine: "NGCGUI_SUBFILE = rcone.ngc",
|
||||
}),
|
||||
currentNodeInventoryStatus: "PASS",
|
||||
dependencyClass: "macro-load-non-main",
|
||||
targetBrowserEvidence: "non-main-fixture-diagnostics",
|
||||
@@ -353,6 +375,14 @@ export const VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES = Object.freeze([
|
||||
owningIniPath: "linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
|
||||
pairedMainProgram: "linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc",
|
||||
declarationSource: "NGCGUI_SUBFILE",
|
||||
declarationEvidence: Object.freeze({
|
||||
sourceFile: "linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
|
||||
section: "DISPLAY",
|
||||
key: "NGCGUI_SUBFILE",
|
||||
value: "queuebuster.ngc",
|
||||
line: 40,
|
||||
sourceLine: "NGCGUI_SUBFILE = queuebuster.ngc",
|
||||
}),
|
||||
currentNodeInventoryStatus: "NON_MAIN_CLASS",
|
||||
dependencyClass: "macro-load-non-main-declared-only",
|
||||
targetBrowserEvidence: "non-main-fixture-diagnostics",
|
||||
@@ -370,6 +400,27 @@ export const VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES = Object.freeze(
|
||||
owningIniPath: "linuxcnc/configs/sim/gscreen/silverdragon/silverdragon.ini",
|
||||
blockedKind: "PYTHON-UI-PROCESS",
|
||||
dependencyClass: "macro-load-python-ui-boundary",
|
||||
boundaryEvidence: Object.freeze({
|
||||
sourceArtifacts: Object.freeze([
|
||||
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv",
|
||||
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv",
|
||||
]),
|
||||
sourceArtifactHashes: VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES,
|
||||
baselineSummary: VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE,
|
||||
boundarySummary: Object.freeze({
|
||||
path: "gscreen/silverdragon/macros/tool_sensor.ngc",
|
||||
ini: "gscreen/silverdragon/silverdragon.ini",
|
||||
recommendedBlocked: "UNAVAILABLE",
|
||||
dependencies: "missing_vendored_ini:configs/sim/gscreen/silverdragon/silverdragon.ini",
|
||||
}),
|
||||
iniBoundarySummary: Object.freeze({
|
||||
ini: "gscreen/silverdragon/silverdragon.ini",
|
||||
vendored: 0,
|
||||
reportAvailable: 0,
|
||||
recommendedBlocked: "UNAVAILABLE",
|
||||
dependencies: "missing_vendored_ini:configs/sim/gscreen/silverdragon/silverdragon.ini",
|
||||
}),
|
||||
}),
|
||||
reason: "Owning SilverDragon UI machine file is not available in the vendored manifest; promotion would require Python UI process boundary proof outside virtual HAL.",
|
||||
}),
|
||||
Object.freeze({
|
||||
@@ -378,10 +429,94 @@ export const VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES = Object.freeze(
|
||||
owningIniPath: "linuxcnc/configs/sim/gmoccapy/6_axis.ini",
|
||||
blockedKind: "L4-PYTHON-REMAP",
|
||||
dependencyClass: "macro-load-python-remap-boundary",
|
||||
boundaryEvidence: Object.freeze({
|
||||
sourceArtifacts: Object.freeze([
|
||||
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv",
|
||||
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv",
|
||||
]),
|
||||
sourceArtifactHashes: VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES,
|
||||
baselineSummary: VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE,
|
||||
boundarySummary: Object.freeze({
|
||||
path: "gmoccapy/macros/on_abort.ngc",
|
||||
ini: "gmoccapy/6_axis.ini",
|
||||
recommendedBlocked: "UNAVAILABLE",
|
||||
dependencies: "missing_vendored_ini:configs/sim/gmoccapy/6_axis.ini",
|
||||
}),
|
||||
iniBoundarySummary: Object.freeze({
|
||||
ini: "gmoccapy/6_axis.ini",
|
||||
vendored: 0,
|
||||
reportAvailable: 0,
|
||||
recommendedBlocked: "UNAVAILABLE",
|
||||
dependencies: "missing_vendored_ini:configs/sim/gmoccapy/6_axis.ini",
|
||||
}),
|
||||
}),
|
||||
reason: "gmoccapy macro/load rows remain locked behind LinuxCNC-owned Python remap/prolog/epilog runtime proof.",
|
||||
}),
|
||||
]);
|
||||
|
||||
export const VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_AUDIT_CANDIDATES = Object.freeze([
|
||||
Object.freeze({
|
||||
id: "gscreen-industrial-lathe-wear-toolchange-audit",
|
||||
fixturePath: "linuxcnc/configs/sim/gscreen/industrial_lathe_wear/toolchange.ngc",
|
||||
owningIniPath: "linuxcnc/configs/sim/gscreen/industrial_lathe_wear/industrial_lathe_wear.ini",
|
||||
dependencyClass: "macro-load-ui-family-audit",
|
||||
auditStatus: "blocked-missing-owning-ini",
|
||||
promotionAllowed: false,
|
||||
boundaryEvidence: Object.freeze({
|
||||
sourceArtifacts: Object.freeze([
|
||||
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv",
|
||||
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv",
|
||||
]),
|
||||
sourceArtifactHashes: VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES,
|
||||
baselineSummary: VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE,
|
||||
boundarySummary: Object.freeze({
|
||||
path: "gscreen/industrial_lathe_wear/toolchange.ngc",
|
||||
ini: "gscreen/industrial_lathe_wear/industrial_lathe_wear.ini",
|
||||
recommendedBlocked: "UNAVAILABLE",
|
||||
dependencies: "missing_vendored_ini:configs/sim/gscreen/industrial_lathe_wear/industrial_lathe_wear.ini",
|
||||
}),
|
||||
iniBoundarySummary: Object.freeze({
|
||||
ini: "gscreen/industrial_lathe_wear/industrial_lathe_wear.ini",
|
||||
vendored: 0,
|
||||
reportAvailable: 0,
|
||||
recommendedBlocked: "UNAVAILABLE",
|
||||
dependencies: "missing_vendored_ini:configs/sim/gscreen/industrial_lathe_wear/industrial_lathe_wear.ini",
|
||||
}),
|
||||
}),
|
||||
reason: "Generated boundary evidence reports missing vendored owning INI for gscreen industrial_lathe_wear; keep as audit-only until machine files and UI/process declaration have LinuxCNC source evidence.",
|
||||
}),
|
||||
Object.freeze({
|
||||
id: "qtvcp-industrial-lathe-wear-toolchange-audit",
|
||||
fixturePath: "linuxcnc/configs/sim/qtvcp_screens/industrial_lathe_wear/toolchange.ngc",
|
||||
owningIniPath: "linuxcnc/configs/sim/qtvcp_screens/industrial_lathe_wear/industrial_lathe_wear.ini",
|
||||
dependencyClass: "macro-load-ui-family-audit",
|
||||
auditStatus: "blocked-missing-owning-ini",
|
||||
promotionAllowed: false,
|
||||
boundaryEvidence: Object.freeze({
|
||||
sourceArtifacts: Object.freeze([
|
||||
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv",
|
||||
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv",
|
||||
]),
|
||||
sourceArtifactHashes: VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES,
|
||||
baselineSummary: VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE,
|
||||
boundarySummary: Object.freeze({
|
||||
path: "qtvcp_screens/industrial_lathe_wear/toolchange.ngc",
|
||||
ini: "qtvcp_screens/industrial_lathe_wear/industrial_lathe_wear.ini",
|
||||
recommendedBlocked: "UNAVAILABLE",
|
||||
dependencies: "missing_vendored_ini:configs/sim/qtvcp_screens/industrial_lathe_wear/industrial_lathe_wear.ini",
|
||||
}),
|
||||
iniBoundarySummary: Object.freeze({
|
||||
ini: "qtvcp_screens/industrial_lathe_wear/industrial_lathe_wear.ini",
|
||||
vendored: 0,
|
||||
reportAvailable: 0,
|
||||
recommendedBlocked: "UNAVAILABLE",
|
||||
dependencies: "missing_vendored_ini:configs/sim/qtvcp_screens/industrial_lathe_wear/industrial_lathe_wear.ini",
|
||||
}),
|
||||
}),
|
||||
reason: "Generated boundary evidence reports missing vendored owning INI for qtvcp_screens industrial_lathe_wear; keep as audit-only until machine files and UI/process declaration have LinuxCNC source evidence.",
|
||||
}),
|
||||
]);
|
||||
|
||||
export const VIRTUAL_HAL_COVERAGE_STATES = Object.freeze({
|
||||
simulated: "simulated",
|
||||
bridgeOnly: "bridge-only",
|
||||
@@ -2683,6 +2818,10 @@ function normalizeLinuxCncSourcePath(path) {
|
||||
return parts.join("/");
|
||||
}
|
||||
|
||||
function normalizeSimConfigInventoryPath(path) {
|
||||
return normalizeLinuxCncSourcePath(path).replace(/^configs\/sim\//, "");
|
||||
}
|
||||
|
||||
function sourceManifestSetFromText(manifestText = "") {
|
||||
return new Set(String(manifestText)
|
||||
.split("\n")
|
||||
@@ -2691,6 +2830,35 @@ function sourceManifestSetFromText(manifestText = "") {
|
||||
.map(normalizeLinuxCncSourcePath));
|
||||
}
|
||||
|
||||
function isGeneratedBoundaryEvidenceReady(boundaryEvidence = {}, fixturePath = "", owningIniPath = "") {
|
||||
const boundarySummary = boundaryEvidence.boundarySummary ?? {};
|
||||
const iniBoundarySummary = boundaryEvidence.iniBoundarySummary ?? {};
|
||||
const baselineSummary = boundaryEvidence.baselineSummary ?? {};
|
||||
const sourceArtifactHashes = boundaryEvidence.sourceArtifactHashes ?? {};
|
||||
const sourceArtifacts = [...(boundaryEvidence.sourceArtifacts ?? [])];
|
||||
const normalizedFixturePath = normalizeSimConfigInventoryPath(fixturePath);
|
||||
const normalizedOwningIniPath = normalizeSimConfigInventoryPath(owningIniPath);
|
||||
return sourceArtifacts.includes("wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv") &&
|
||||
sourceArtifacts.includes("wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv") &&
|
||||
Object.entries(VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES)
|
||||
.every(([artifactPath, expectedHash]) => sourceArtifactHashes[artifactPath] === expectedHash) &&
|
||||
baselineSummary.executed === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.executed &&
|
||||
baselineSummary.passed === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.passed &&
|
||||
baselineSummary.skipped === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.skipped &&
|
||||
baselineSummary.unexpectedFail === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.unexpectedFail &&
|
||||
normalizeSimConfigInventoryPath(boundarySummary.path) === normalizedFixturePath &&
|
||||
normalizeSimConfigInventoryPath(boundarySummary.ini) === normalizedOwningIniPath &&
|
||||
boundarySummary.recommendedBlocked === "UNAVAILABLE" &&
|
||||
typeof boundarySummary.dependencies === "string" &&
|
||||
boundarySummary.dependencies.includes("missing_vendored_ini:") &&
|
||||
normalizeSimConfigInventoryPath(iniBoundarySummary.ini) === normalizedOwningIniPath &&
|
||||
iniBoundarySummary.vendored === 0 &&
|
||||
iniBoundarySummary.reportAvailable === 0 &&
|
||||
iniBoundarySummary.recommendedBlocked === "UNAVAILABLE" &&
|
||||
typeof iniBoundarySummary.dependencies === "string" &&
|
||||
iniBoundarySummary.dependencies.includes("missing_vendored_ini:");
|
||||
}
|
||||
|
||||
export function createVirtualHalSimConfigSourceCoverageReport(options = {}) {
|
||||
const targets = options.targets ?? VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS;
|
||||
const manifestProvided = typeof options.manifestText === "string" || Array.isArray(options.manifestEntries);
|
||||
@@ -2835,6 +3003,23 @@ export function createVirtualHalSimConfigPromotionCandidateReport(options = {})
|
||||
const blockedCandidateIds = rows.filter((row) => !row.blockedKindExcluded).map((row) => row.id);
|
||||
const missingManifestFiles = [...new Set(rows.flatMap((row) => row.missingManifestFiles))];
|
||||
const missingRequiredReports = [...new Set(rows.flatMap((row) => row.missingRequiredReports))];
|
||||
const familyRows = [...new Set(rows.map((row) => row.simConfigTarget ?? row.id))].map((familyId) => {
|
||||
const familyCandidates = rows.filter((row) => (row.simConfigTarget ?? row.id) === familyId);
|
||||
const completeCount = familyCandidates.filter((row) => row.complete).length;
|
||||
const explicitBrowserDiagnosticsCount = familyCandidates
|
||||
.filter((row) => row.targetBrowserEvidence === "explicit-browser-diagnostics").length;
|
||||
const sourceFiles = [...new Set(familyCandidates.flatMap((row) => row.sourceFiles))];
|
||||
return {
|
||||
id: familyId,
|
||||
candidateIds: familyCandidates.map((row) => row.id),
|
||||
candidateCount: familyCandidates.length,
|
||||
completeCount,
|
||||
explicitBrowserDiagnosticsCount,
|
||||
explicitBrowserDiagnosticsReady: explicitBrowserDiagnosticsCount === familyCandidates.length,
|
||||
complete: completeCount === familyCandidates.length,
|
||||
sourceFiles,
|
||||
};
|
||||
});
|
||||
const complete = rows.length > 0 &&
|
||||
missingCandidates.length === 0 &&
|
||||
blockedCandidateIds.length === 0 &&
|
||||
@@ -2858,9 +3043,11 @@ export function createVirtualHalSimConfigPromotionCandidateReport(options = {})
|
||||
blockedCandidateIds,
|
||||
missingManifestFiles,
|
||||
missingRequiredReports,
|
||||
familyRows,
|
||||
rows,
|
||||
summaryRows: [
|
||||
{ id: "candidates", label: "Promotion candidates", value: `${rows.length}` },
|
||||
{ id: "families", label: "Promotion families", value: `${familyRows.length}` },
|
||||
{ id: "blocked-family", label: "Blocked-family exclusion", value: blockedCandidateIds.length === 0 ? "passed" : `blocked ${blockedCandidateIds.length}` },
|
||||
{ id: "reports", label: "Virtual HAL reports", value: missingRequiredReports.length === 0 ? "complete" : `missing ${missingRequiredReports.length}` },
|
||||
{ id: "manifest", label: "Manifest check", value: manifestProvided ? (missingManifestFiles.length === 0 ? "passed" : "failed") : "not provided" },
|
||||
@@ -2872,6 +3059,7 @@ export function createVirtualHalSimConfigPromotionCandidateReport(options = {})
|
||||
export function createVirtualHalSimConfigMacroLoadFixtureReport(options = {}) {
|
||||
const fixtures = options.fixtures ?? VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES;
|
||||
const blockedFixtures = options.blockedFixtures ?? VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES;
|
||||
const auditCandidates = options.auditCandidates ?? VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_AUDIT_CANDIDATES;
|
||||
const manifestProvided = typeof options.manifestText === "string" || Array.isArray(options.manifestEntries);
|
||||
const manifestSet = new Set([
|
||||
...sourceManifestSetFromText(options.manifestText ?? ""),
|
||||
@@ -2894,11 +3082,25 @@ export function createVirtualHalSimConfigMacroLoadFixtureReport(options = {}) {
|
||||
const statusReady = allowedStatuses.has(fixture.currentNodeInventoryStatus);
|
||||
const nonMainFixtureReady = fixture.nonMainFixture === true &&
|
||||
fixture.targetBrowserEvidence === "non-main-fixture-diagnostics";
|
||||
const declarationEvidence = fixture.declarationEvidence ?? null;
|
||||
const requiresDeclarationEvidence = typeof fixture.declarationSource === "string" &&
|
||||
fixture.declarationSource.length > 0;
|
||||
const declarationEvidenceReady = !requiresDeclarationEvidence || (
|
||||
declarationEvidence !== null &&
|
||||
declarationEvidence.sourceFile === fixture.owningIniPath &&
|
||||
declarationEvidence.key === fixture.declarationSource &&
|
||||
declarationEvidence.value === fixture.fixturePath.split("/").at(-1) &&
|
||||
Number.isInteger(declarationEvidence.line) &&
|
||||
declarationEvidence.line > 0 &&
|
||||
typeof declarationEvidence.sourceLine === "string" &&
|
||||
declarationEvidence.sourceLine.includes(`${declarationEvidence.key} = ${declarationEvidence.value}`)
|
||||
);
|
||||
const complete = sourceFiles.length > 0 &&
|
||||
missingManifestFiles.length === 0 &&
|
||||
blockedKindExcluded &&
|
||||
statusReady &&
|
||||
nonMainFixtureReady;
|
||||
nonMainFixtureReady &&
|
||||
declarationEvidenceReady;
|
||||
return {
|
||||
id: fixture.id,
|
||||
label: fixture.label,
|
||||
@@ -2908,6 +3110,9 @@ export function createVirtualHalSimConfigMacroLoadFixtureReport(options = {}) {
|
||||
owningIniPath: fixture.owningIniPath,
|
||||
pairedMainProgram: fixture.pairedMainProgram,
|
||||
declarationSource: fixture.declarationSource,
|
||||
declarationEvidence,
|
||||
declarationEvidenceReady,
|
||||
requiresDeclarationEvidence,
|
||||
currentNodeInventoryStatus: fixture.currentNodeInventoryStatus,
|
||||
dependencyClass: fixture.dependencyClass,
|
||||
targetBrowserEvidence: fixture.targetBrowserEvidence,
|
||||
@@ -2924,6 +3129,9 @@ export function createVirtualHalSimConfigMacroLoadFixtureReport(options = {}) {
|
||||
});
|
||||
const missingFixtures = rows.filter((row) => !row.complete).map((row) => row.id);
|
||||
const blockedFixtureIds = rows.filter((row) => !row.blockedKindExcluded).map((row) => row.id);
|
||||
const declarationEvidenceViolations = rows
|
||||
.filter((row) => row.requiresDeclarationEvidence === true && row.declarationEvidenceReady !== true)
|
||||
.map((row) => row.id);
|
||||
const standaloneMainViolations = rows
|
||||
.filter((row) => row.nonMainFixture !== true || row.targetBrowserEvidence !== "non-main-fixture-diagnostics")
|
||||
.map((row) => row.id);
|
||||
@@ -2931,6 +3139,8 @@ export function createVirtualHalSimConfigMacroLoadFixtureReport(options = {}) {
|
||||
const positiveFixturePaths = new Set(rows.map((row) => normalizeLinuxCncSourcePath(row.fixturePath)));
|
||||
const blockedRows = blockedFixtures.map((fixture) => {
|
||||
const excludedFromPositiveFixtures = !positiveFixturePaths.has(normalizeLinuxCncSourcePath(fixture.fixturePath));
|
||||
const boundaryEvidence = fixture.boundaryEvidence ?? {};
|
||||
const boundaryEvidenceReady = isGeneratedBoundaryEvidenceReady(boundaryEvidence, fixture.fixturePath, fixture.owningIniPath);
|
||||
return {
|
||||
id: fixture.id,
|
||||
source: "linuxcnc-configs-sim-source-derived-virtual-hal-macro-load-blocked-fixture",
|
||||
@@ -2939,19 +3149,61 @@ export function createVirtualHalSimConfigMacroLoadFixtureReport(options = {}) {
|
||||
blockedKind: fixture.blockedKind,
|
||||
dependencyClass: fixture.dependencyClass,
|
||||
excludedFromPositiveFixtures,
|
||||
boundaryEvidence,
|
||||
boundaryEvidenceReady,
|
||||
reason: fixture.reason,
|
||||
complete: excludedFromPositiveFixtures,
|
||||
complete: excludedFromPositiveFixtures && boundaryEvidenceReady,
|
||||
};
|
||||
});
|
||||
const blockedFixturePromotionViolations = blockedRows
|
||||
.filter((row) => row.excludedFromPositiveFixtures !== true)
|
||||
.map((row) => row.id);
|
||||
const blockedBoundaryEvidenceViolations = blockedRows
|
||||
.filter((row) => row.boundaryEvidenceReady !== true)
|
||||
.map((row) => row.id);
|
||||
const auditRows = auditCandidates.map((candidate) => {
|
||||
const excludedFromPositiveFixtures = !positiveFixturePaths.has(normalizeLinuxCncSourcePath(candidate.fixturePath));
|
||||
const boundaryEvidence = candidate.boundaryEvidence ?? {};
|
||||
const boundaryEvidenceReady = isGeneratedBoundaryEvidenceReady(boundaryEvidence, candidate.fixturePath, candidate.owningIniPath);
|
||||
return {
|
||||
id: candidate.id,
|
||||
source: "linuxcnc-configs-sim-source-derived-virtual-hal-macro-load-audit-candidate",
|
||||
fixturePath: candidate.fixturePath,
|
||||
owningIniPath: candidate.owningIniPath,
|
||||
dependencyClass: candidate.dependencyClass,
|
||||
auditStatus: candidate.auditStatus,
|
||||
promotionAllowed: candidate.promotionAllowed === true,
|
||||
excludedFromPositiveFixtures,
|
||||
boundaryEvidence,
|
||||
boundaryEvidenceReady,
|
||||
reason: candidate.reason,
|
||||
complete: excludedFromPositiveFixtures && candidate.promotionAllowed !== true && boundaryEvidenceReady,
|
||||
};
|
||||
});
|
||||
const missingAuditRows = auditRows
|
||||
.filter((row) => !row.complete)
|
||||
.map((row) => row.id);
|
||||
const auditPromotionViolations = auditRows
|
||||
.filter((row) => row.promotionAllowed !== true && row.excludedFromPositiveFixtures !== true)
|
||||
.map((row) => row.id);
|
||||
const auditBoundaryEvidenceViolations = auditRows
|
||||
.filter((row) => row.boundaryEvidenceReady !== true)
|
||||
.map((row) => row.id);
|
||||
const auditPromotionAllowedViolations = auditRows
|
||||
.filter((row) => row.promotionAllowed === true && row.boundaryEvidenceReady !== true)
|
||||
.map((row) => row.id);
|
||||
const complete = rows.length > 0 &&
|
||||
missingFixtures.length === 0 &&
|
||||
missingAuditRows.length === 0 &&
|
||||
blockedFixtureIds.length === 0 &&
|
||||
declarationEvidenceViolations.length === 0 &&
|
||||
standaloneMainViolations.length === 0 &&
|
||||
missingManifestFiles.length === 0 &&
|
||||
blockedFixturePromotionViolations.length === 0;
|
||||
blockedFixturePromotionViolations.length === 0 &&
|
||||
blockedBoundaryEvidenceViolations.length === 0 &&
|
||||
auditPromotionViolations.length === 0 &&
|
||||
auditBoundaryEvidenceViolations.length === 0 &&
|
||||
auditPromotionAllowedViolations.length === 0;
|
||||
return {
|
||||
apiName: "linuxcnc-wasm-virtual-hal-sim-config-macro-load-fixture-report",
|
||||
reportVersion: 1,
|
||||
@@ -2966,15 +3218,24 @@ export function createVirtualHalSimConfigMacroLoadFixtureReport(options = {}) {
|
||||
fixturePaths: rows.map((row) => row.fixturePath),
|
||||
missingFixtures,
|
||||
blockedFixtureIds,
|
||||
declarationEvidenceViolations,
|
||||
standaloneMainViolations,
|
||||
blockedFixturePromotionViolations,
|
||||
blockedBoundaryEvidenceViolations,
|
||||
missingAuditRows,
|
||||
auditPromotionViolations,
|
||||
auditBoundaryEvidenceViolations,
|
||||
auditPromotionAllowedViolations,
|
||||
missingManifestFiles,
|
||||
rows,
|
||||
blockedRows,
|
||||
auditRows,
|
||||
summaryRows: [
|
||||
{ id: "fixtures", label: "Macro/load fixtures", value: `${rows.length}` },
|
||||
{ id: "declarations", label: "Declaration evidence", value: declarationEvidenceViolations.length === 0 ? "passed" : `violations ${declarationEvidenceViolations.length}` },
|
||||
{ id: "non-main", label: "Non-main fixture gate", value: standaloneMainViolations.length === 0 ? "passed" : `violations ${standaloneMainViolations.length}` },
|
||||
{ id: "blocked-fixture-lock", label: "Blocked fixture lock", value: blockedFixturePromotionViolations.length === 0 ? "passed" : `violations ${blockedFixturePromotionViolations.length}` },
|
||||
{ id: "blocked-fixture-lock", label: "Blocked fixture lock", value: blockedFixturePromotionViolations.length === 0 && blockedBoundaryEvidenceViolations.length === 0 ? "passed" : "violations" },
|
||||
{ id: "ui-family-audit", label: "UI-family macro/load audit", value: missingAuditRows.length === 0 && auditPromotionViolations.length === 0 && auditBoundaryEvidenceViolations.length === 0 && auditPromotionAllowedViolations.length === 0 ? "passed" : "violations" },
|
||||
{ id: "blocked-family", label: "Blocked-family exclusion", value: blockedFixtureIds.length === 0 ? "passed" : `blocked ${blockedFixtureIds.length}` },
|
||||
{ id: "manifest", label: "Manifest check", value: manifestProvided ? (missingManifestFiles.length === 0 ? "passed" : "failed") : "not provided" },
|
||||
{ id: "inventory-baseline", label: "Inventory baseline", value: "unchanged" },
|
||||
|
||||
@@ -79,6 +79,59 @@ const DEFAULT_SIM_CONFIG_INVENTORY_BASELINE = {
|
||||
unexpectedFail: 0,
|
||||
};
|
||||
|
||||
const DEFAULT_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES = {
|
||||
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv":
|
||||
"de6cf57b7c07182e3bcb32e22dbdf202b618cabc14620d6dff1ef815587950b9",
|
||||
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv":
|
||||
"b0afe27224e97a82fbecbbd75a7c86233c98fe957d9c1ba20f0656f8745a5eae",
|
||||
};
|
||||
|
||||
function isBoundaryEvidenceBaselineFresh(boundaryEvidence = {}) {
|
||||
const baseline = objectOrEmpty(boundaryEvidence.baselineSummary);
|
||||
const hashes = objectOrEmpty(boundaryEvidence.sourceArtifactHashes);
|
||||
return baseline.executed === DEFAULT_SIM_CONFIG_INVENTORY_BASELINE.executed &&
|
||||
baseline.passed === DEFAULT_SIM_CONFIG_INVENTORY_BASELINE.passed &&
|
||||
baseline.skipped === DEFAULT_SIM_CONFIG_INVENTORY_BASELINE.skipped &&
|
||||
baseline.unexpectedFail === DEFAULT_SIM_CONFIG_INVENTORY_BASELINE.unexpectedFail &&
|
||||
Object.entries(DEFAULT_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES)
|
||||
.every(([artifactPath, expectedHash]) => hashes[artifactPath] === expectedHash);
|
||||
}
|
||||
|
||||
function createPromotionFamilySummaryText(candidateReport = {}) {
|
||||
const families = arrayOrEmpty(objectOrEmpty(candidateReport).familyRows);
|
||||
if (families.length === 0) {
|
||||
return "not provided";
|
||||
}
|
||||
return families.map((family) => {
|
||||
const label = family.id ?? "unknown";
|
||||
const completeCount = family.completeCount ?? 0;
|
||||
const candidateCount = family.candidateCount ?? 0;
|
||||
const state = family.complete === true && family.explicitBrowserDiagnosticsReady === true ? "ready" : "blocked";
|
||||
return `${label}: ${completeCount}/${candidateCount} ${state}`;
|
||||
}).join("; ");
|
||||
}
|
||||
|
||||
function createPromotionFamilySummaryRows(candidateReport = {}) {
|
||||
return arrayOrEmpty(objectOrEmpty(candidateReport).familyRows).map((family) => {
|
||||
const sourceFileCount = arrayOrEmpty(family.sourceFiles).length;
|
||||
const candidateCount = family.candidateCount ?? 0;
|
||||
const completeCount = family.completeCount ?? 0;
|
||||
const explicitBrowserDiagnosticsReady = family.explicitBrowserDiagnosticsReady === true;
|
||||
const ready = family.complete === true && explicitBrowserDiagnosticsReady;
|
||||
return {
|
||||
id: family.id ?? "unknown",
|
||||
label: family.id ?? "Unknown promotion family",
|
||||
value: `${completeCount}/${candidateCount} ${ready ? "ready" : "blocked"}; sources=${sourceFileCount}; diagnostics=${explicitBrowserDiagnosticsReady ? "ready" : "blocked"}`,
|
||||
sourceFiles: arrayOrEmpty(family.sourceFiles),
|
||||
candidateCount,
|
||||
completeCount,
|
||||
sourceFileCount,
|
||||
explicitBrowserDiagnosticsReady,
|
||||
ready,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const BLOCKED_RUNTIME_FAMILIES = [
|
||||
"L4-USER-M-PROCESS",
|
||||
"L4-TOOL-DB",
|
||||
@@ -152,6 +205,12 @@ const REQUIRED_MACRO_LOAD_FIXTURES = [
|
||||
{
|
||||
id: "rose-engine-rcone-macro-load",
|
||||
fixturePath: "linuxcnc/configs/sim/axis/rose_engine/rcone.ngc",
|
||||
declarationEvidence: {
|
||||
sourceFile: "linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
|
||||
key: "NGCGUI_SUBFILE",
|
||||
value: "rcone.ngc",
|
||||
line: 34,
|
||||
},
|
||||
sourceFiles: [
|
||||
"linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
|
||||
"linuxcnc/configs/sim/axis/rose_engine/rcone.ngc",
|
||||
@@ -161,6 +220,12 @@ const REQUIRED_MACRO_LOAD_FIXTURES = [
|
||||
{
|
||||
id: "external-offsets-queuebuster-macro-load",
|
||||
fixturePath: "linuxcnc/configs/sim/axis/external_offsets/queuebuster.ngc",
|
||||
declarationEvidence: {
|
||||
sourceFile: "linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
|
||||
key: "NGCGUI_SUBFILE",
|
||||
value: "queuebuster.ngc",
|
||||
line: 40,
|
||||
},
|
||||
sourceFiles: [
|
||||
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
|
||||
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc",
|
||||
@@ -181,6 +246,19 @@ const REQUIRED_BLOCKED_MACRO_LOAD_FIXTURES = [
|
||||
},
|
||||
];
|
||||
|
||||
const REQUIRED_MACRO_LOAD_AUDIT_CANDIDATES = [
|
||||
{
|
||||
id: "gscreen-industrial-lathe-wear-toolchange-audit",
|
||||
fixturePath: "linuxcnc/configs/sim/gscreen/industrial_lathe_wear/toolchange.ngc",
|
||||
auditStatus: "blocked-missing-owning-ini",
|
||||
},
|
||||
{
|
||||
id: "qtvcp-industrial-lathe-wear-toolchange-audit",
|
||||
fixturePath: "linuxcnc/configs/sim/qtvcp_screens/industrial_lathe_wear/toolchange.ngc",
|
||||
auditStatus: "blocked-missing-owning-ini",
|
||||
},
|
||||
];
|
||||
|
||||
function isVirtualHalSimConfigSourceCoverageReady(report) {
|
||||
const coverage = objectOrEmpty(report);
|
||||
return coverage.apiName === "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report" &&
|
||||
@@ -218,8 +296,14 @@ function isVirtualHalSimConfigMacroLoadFixtureReportReady(report) {
|
||||
fixtureReport.inventoryBaselineUnchanged === true &&
|
||||
arrayOrEmpty(fixtureReport.missingFixtures).length === 0 &&
|
||||
arrayOrEmpty(fixtureReport.blockedFixtureIds).length === 0 &&
|
||||
arrayOrEmpty(fixtureReport.declarationEvidenceViolations).length === 0 &&
|
||||
arrayOrEmpty(fixtureReport.standaloneMainViolations).length === 0 &&
|
||||
arrayOrEmpty(fixtureReport.blockedFixturePromotionViolations).length === 0 &&
|
||||
arrayOrEmpty(fixtureReport.blockedBoundaryEvidenceViolations).length === 0 &&
|
||||
arrayOrEmpty(fixtureReport.missingAuditRows).length === 0 &&
|
||||
arrayOrEmpty(fixtureReport.auditPromotionViolations).length === 0 &&
|
||||
arrayOrEmpty(fixtureReport.auditBoundaryEvidenceViolations).length === 0 &&
|
||||
arrayOrEmpty(fixtureReport.auditPromotionAllowedViolations).length === 0 &&
|
||||
arrayOrEmpty(fixtureReport.missingManifestFiles).length === 0 &&
|
||||
REQUIRED_MACRO_LOAD_FIXTURES.every((fixture) =>
|
||||
rows.some((row) =>
|
||||
@@ -228,6 +312,16 @@ function isVirtualHalSimConfigMacroLoadFixtureReportReady(report) {
|
||||
row.nonMainFixture === true &&
|
||||
row.targetBrowserEvidence === "non-main-fixture-diagnostics" &&
|
||||
row.fixturePath === fixture.fixturePath &&
|
||||
(
|
||||
!fixture.declarationEvidence ||
|
||||
(
|
||||
row.declarationEvidenceReady === true &&
|
||||
objectOrEmpty(row.declarationEvidence).sourceFile === fixture.declarationEvidence.sourceFile &&
|
||||
objectOrEmpty(row.declarationEvidence).key === fixture.declarationEvidence.key &&
|
||||
objectOrEmpty(row.declarationEvidence).value === fixture.declarationEvidence.value &&
|
||||
objectOrEmpty(row.declarationEvidence).line === fixture.declarationEvidence.line
|
||||
)
|
||||
) &&
|
||||
fixture.sourceFiles.every((file) => arrayOrEmpty(row.sourceFiles).includes(file))
|
||||
)
|
||||
) &&
|
||||
@@ -237,6 +331,28 @@ function isVirtualHalSimConfigMacroLoadFixtureReportReady(report) {
|
||||
row.fixturePath === fixture.fixturePath &&
|
||||
row.blockedKind === fixture.blockedKind &&
|
||||
row.excludedFromPositiveFixtures === true &&
|
||||
row.boundaryEvidenceReady === true &&
|
||||
isBoundaryEvidenceBaselineFresh(row.boundaryEvidence) &&
|
||||
objectOrEmpty(row.boundaryEvidence?.boundarySummary).recommendedBlocked === "UNAVAILABLE" &&
|
||||
String(objectOrEmpty(row.boundaryEvidence?.boundarySummary).dependencies ?? "").includes("missing_vendored_ini:") &&
|
||||
objectOrEmpty(row.boundaryEvidence?.iniBoundarySummary).vendored === 0 &&
|
||||
objectOrEmpty(row.boundaryEvidence?.iniBoundarySummary).reportAvailable === 0 &&
|
||||
row.complete === true
|
||||
)
|
||||
) &&
|
||||
REQUIRED_MACRO_LOAD_AUDIT_CANDIDATES.every((candidate) =>
|
||||
arrayOrEmpty(fixtureReport.auditRows).some((row) =>
|
||||
row.id === candidate.id &&
|
||||
row.fixturePath === candidate.fixturePath &&
|
||||
row.auditStatus === candidate.auditStatus &&
|
||||
row.promotionAllowed === false &&
|
||||
row.excludedFromPositiveFixtures === true &&
|
||||
row.boundaryEvidenceReady === true &&
|
||||
isBoundaryEvidenceBaselineFresh(row.boundaryEvidence) &&
|
||||
objectOrEmpty(row.boundaryEvidence?.boundarySummary).recommendedBlocked === "UNAVAILABLE" &&
|
||||
String(objectOrEmpty(row.boundaryEvidence?.boundarySummary).dependencies ?? "").includes("missing_vendored_ini:") &&
|
||||
objectOrEmpty(row.boundaryEvidence?.iniBoundarySummary).vendored === 0 &&
|
||||
objectOrEmpty(row.boundaryEvidence?.iniBoundarySummary).reportAvailable === 0 &&
|
||||
row.complete === true
|
||||
)
|
||||
);
|
||||
@@ -277,6 +393,23 @@ function isVirtualHalSimConfigPromotionCandidateReportReady(report) {
|
||||
hasRequiredPromotionCandidates(rows);
|
||||
}
|
||||
|
||||
function isVirtualHalPromotionCandidateSummaryReady(summary) {
|
||||
const summaryObject = objectOrEmpty(summary);
|
||||
return summaryObject.apiName === "real-browser-simulation-promotion-candidate-summary" &&
|
||||
summaryObject.ready === true &&
|
||||
summaryObject.phase === "ready" &&
|
||||
summaryObject.candidateCount === 8 &&
|
||||
summaryObject.readyCandidateCount === 8 &&
|
||||
summaryObject.familyCount === 3 &&
|
||||
summaryObject.sourceFileCount === 17 &&
|
||||
summaryObject.preferredCandidateId === "qtdragon-multi-joint-on-abort" &&
|
||||
summaryObject.preferredIniPath === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini" &&
|
||||
summaryObject.preferredGcodePath === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc" &&
|
||||
summaryObject.explicitBrowserDiagnosticsCount === 8 &&
|
||||
summaryObject.inventoryBaseline === "executed=28 passed=28 skipped=131 unexpected_fail=0" &&
|
||||
arrayOrEmpty(summaryObject.blockedCandidateIds).length === 0;
|
||||
}
|
||||
|
||||
export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifact = {}) {
|
||||
const artifactObject = objectOrEmpty(artifact);
|
||||
const sourceCompliance = objectOrEmpty(artifactObject.virtualHalSourceCompliance);
|
||||
@@ -284,6 +417,7 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac
|
||||
const commandScriptFixtures = objectOrEmpty(artifactObject.virtualHalCommandScriptFixtures);
|
||||
const motionControllerMatrix = objectOrEmpty(artifactObject.virtualHalMotionControllerMatrix);
|
||||
const promotionCandidates = objectOrEmpty(artifactObject.virtualHalSimConfigPromotionCandidates);
|
||||
const promotionCandidateSummary = objectOrEmpty(artifactObject.virtualHalPromotionCandidateSummary);
|
||||
const macroLoadFixtures = objectOrEmpty(artifactObject.virtualHalSimConfigMacroLoadFixtures);
|
||||
const sessionDiagnostics = objectOrEmpty(artifactObject.virtualHalSessionDiagnostics);
|
||||
const replacement = objectOrEmpty(artifactObject.virtualHalSimulationReplacement);
|
||||
@@ -301,6 +435,9 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac
|
||||
const hasPromotionCandidates = artifactObject.virtualHalSimConfigPromotionCandidates !== undefined;
|
||||
const promotionCandidatesReady = !hasPromotionCandidates ||
|
||||
isVirtualHalSimConfigPromotionCandidateReportReady(promotionCandidates);
|
||||
const hasPromotionCandidateSummary = artifactObject.virtualHalPromotionCandidateSummary !== undefined;
|
||||
const promotionCandidateSummaryReady = hasPromotionCandidateSummary &&
|
||||
isVirtualHalPromotionCandidateSummaryReady(promotionCandidateSummary);
|
||||
const hasMacroLoadFixtures = artifactObject.virtualHalSimConfigMacroLoadFixtures !== undefined;
|
||||
const macroLoadFixturesReady = hasMacroLoadFixtures &&
|
||||
isVirtualHalSimConfigMacroLoadFixtureReportReady(macroLoadFixtures);
|
||||
@@ -326,6 +463,7 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac
|
||||
...(sourceComplianceReady ? [] : ["virtualHalSourceCompliance"]),
|
||||
...(simConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]),
|
||||
...(promotionCandidatesReady ? [] : ["virtualHalSimConfigPromotionCandidates"]),
|
||||
...(promotionCandidateSummaryReady ? [] : ["virtualHalPromotionCandidateSummary"]),
|
||||
...(macroLoadFixturesReady ? [] : ["virtualHalSimConfigMacroLoadFixtures"]),
|
||||
...(commandScriptFixturesReady ? [] : ["virtualHalCommandScriptFixtures"]),
|
||||
...(motionControllerMatrixReady ? [] : ["virtualHalMotionControllerMatrix"]),
|
||||
@@ -344,6 +482,8 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac
|
||||
simConfigSourceCoverageReady,
|
||||
promotionCandidatesReady,
|
||||
hasPromotionCandidates,
|
||||
promotionCandidateSummaryReady,
|
||||
hasPromotionCandidateSummary,
|
||||
macroLoadFixturesReady,
|
||||
hasMacroLoadFixtures,
|
||||
commandScriptFixturesReady,
|
||||
@@ -385,6 +525,11 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac
|
||||
label: "Virtual HAL sim-config promotion candidates",
|
||||
value: hasPromotionCandidates ? (promotionCandidatesReady ? "ready" : "missing") : "not provided",
|
||||
},
|
||||
{
|
||||
id: "promotion-candidate-summary",
|
||||
label: "Virtual HAL promotion candidate summary",
|
||||
value: hasPromotionCandidateSummary ? (promotionCandidateSummaryReady ? "ready" : "missing") : "not provided",
|
||||
},
|
||||
{
|
||||
id: "sim-config-macro-load-fixtures",
|
||||
label: "Virtual HAL sim-config macro/load fixtures",
|
||||
@@ -1659,6 +1804,8 @@ export function createProjectReleaseReadinessReport({
|
||||
observedOutputs = [],
|
||||
simConfigInventory = DEFAULT_SIM_CONFIG_INVENTORY_BASELINE,
|
||||
virtualHalSimConfigSourceCoverage = null,
|
||||
virtualHalSimConfigPromotionCandidates = null,
|
||||
virtualHalSimConfigMacroLoadFixtures = null,
|
||||
virtualHalMotionControllerMatrix = null,
|
||||
blockedRuntimeFamilies = BLOCKED_RUNTIME_FAMILIES,
|
||||
promotedRuntimeFamilies = [],
|
||||
@@ -1695,6 +1842,14 @@ export function createProjectReleaseReadinessReport({
|
||||
const virtualHalSimConfigSourceCoverageReady = isVirtualHalSimConfigSourceCoverageReady(
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
);
|
||||
const virtualHalSimConfigPromotionCandidatesReady = isVirtualHalSimConfigPromotionCandidateReportReady(
|
||||
virtualHalSimConfigPromotionCandidates,
|
||||
);
|
||||
const virtualHalPromotionFamilySummary = createPromotionFamilySummaryText(virtualHalSimConfigPromotionCandidates);
|
||||
const virtualHalPromotionFamilyRows = createPromotionFamilySummaryRows(virtualHalSimConfigPromotionCandidates);
|
||||
const virtualHalSimConfigMacroLoadFixturesReady = isVirtualHalSimConfigMacroLoadFixtureReportReady(
|
||||
virtualHalSimConfigMacroLoadFixtures,
|
||||
);
|
||||
const virtualHalMotionControllerMatrixReady = isVirtualHalMotionControllerMatrixReady(
|
||||
virtualHalMotionControllerMatrix,
|
||||
);
|
||||
@@ -1704,6 +1859,8 @@ export function createProjectReleaseReadinessReport({
|
||||
const ready = releaseGatePassed &&
|
||||
inventoryReady &&
|
||||
virtualHalSimConfigSourceCoverageReady &&
|
||||
virtualHalSimConfigPromotionCandidatesReady &&
|
||||
virtualHalSimConfigMacroLoadFixturesReady &&
|
||||
virtualHalMotionControllerMatrixReady &&
|
||||
blockedRuntimeReady &&
|
||||
axisScreenshotArtifactsReady;
|
||||
@@ -1711,6 +1868,8 @@ export function createProjectReleaseReadinessReport({
|
||||
...(releaseGatePassed ? [] : ["project-release-gate"]),
|
||||
...(inventoryReady ? [] : ["sim-config-inventory"]),
|
||||
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtual-hal-sim-config-source-coverage"]),
|
||||
...(virtualHalSimConfigPromotionCandidatesReady ? [] : ["virtual-hal-sim-config-promotion-candidates"]),
|
||||
...(virtualHalSimConfigMacroLoadFixturesReady ? [] : ["virtual-hal-sim-config-macro-load-fixtures"]),
|
||||
...(virtualHalMotionControllerMatrixReady ? [] : ["virtual-hal-motion-controller-matrix"]),
|
||||
...(blockedRuntimeReady ? [] : ["blocked-runtime-families"]),
|
||||
...(axisScreenshotArtifactsReady ? [] : ["axis-screenshot-artifacts"]),
|
||||
@@ -1730,6 +1889,12 @@ export function createProjectReleaseReadinessReport({
|
||||
simConfigInventory: inventory,
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
virtualHalSimConfigSourceCoverageReady,
|
||||
virtualHalSimConfigPromotionCandidates,
|
||||
virtualHalSimConfigPromotionCandidatesReady,
|
||||
virtualHalPromotionFamilySummary,
|
||||
virtualHalPromotionFamilyRows,
|
||||
virtualHalSimConfigMacroLoadFixtures,
|
||||
virtualHalSimConfigMacroLoadFixturesReady,
|
||||
virtualHalMotionControllerMatrix,
|
||||
virtualHalMotionControllerMatrixReady,
|
||||
blockedRuntimeFamilies: blockedFamilies,
|
||||
@@ -1758,6 +1923,27 @@ export function createProjectReleaseReadinessReport({
|
||||
label: "Virtual HAL sim-config source coverage",
|
||||
value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-promotion-candidates",
|
||||
label: "Virtual HAL sim-config promotion candidates",
|
||||
value: virtualHalSimConfigPromotionCandidatesReady ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-promotion-families",
|
||||
label: "Virtual HAL promotion families",
|
||||
value: virtualHalPromotionFamilySummary,
|
||||
},
|
||||
...virtualHalPromotionFamilyRows.map((row) => ({
|
||||
id: `virtual-hal-promotion-family-${row.id}`,
|
||||
label: `Promotion family ${row.label}`,
|
||||
value: row.value,
|
||||
sourceFiles: row.sourceFiles,
|
||||
})),
|
||||
{
|
||||
id: "virtual-hal-sim-config-macro-load-fixtures",
|
||||
label: "Virtual HAL sim-config macro/load fixtures",
|
||||
value: virtualHalSimConfigMacroLoadFixturesReady ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-motion-controller-matrix",
|
||||
label: "Virtual HAL motion controller matrix",
|
||||
@@ -1786,6 +1972,8 @@ export function createProjectReleaseReadinessSummaryViewModel(
|
||||
const gateResultMatrix = objectOrEmpty(report?.gateResultMatrix);
|
||||
const simConfigInventory = objectOrEmpty(report?.simConfigInventory);
|
||||
const virtualHalSimConfigSourceCoverageReady = report?.virtualHalSimConfigSourceCoverageReady === true;
|
||||
const virtualHalSimConfigPromotionCandidatesReady = report?.virtualHalSimConfigPromotionCandidatesReady === true;
|
||||
const virtualHalSimConfigMacroLoadFixturesReady = report?.virtualHalSimConfigMacroLoadFixturesReady === true;
|
||||
const virtualHalMotionControllerMatrixReady = report?.virtualHalMotionControllerMatrixReady === true;
|
||||
const axisScreenshotArtifactSummary = objectOrEmpty(report?.axisScreenshotArtifactSummary);
|
||||
const blockedRuntimeFamilies = arrayOrEmpty(report?.blockedRuntimeFamilies);
|
||||
@@ -1823,6 +2011,16 @@ export function createProjectReleaseReadinessSummaryViewModel(
|
||||
label: "Virtual HAL sim-config source coverage",
|
||||
value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-promotion-candidates",
|
||||
label: "Virtual HAL sim-config promotion candidates",
|
||||
value: virtualHalSimConfigPromotionCandidatesReady ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-macro-load-fixtures",
|
||||
label: "Virtual HAL sim-config macro/load fixtures",
|
||||
value: virtualHalSimConfigMacroLoadFixturesReady ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-motion-controller-matrix",
|
||||
label: "Virtual HAL motion controller matrix",
|
||||
@@ -1910,6 +2108,27 @@ export function createProjectReleaseReadinessArtifactValidationSummaryViewModel(
|
||||
label: "Virtual HAL sim-config source coverage",
|
||||
value: validation?.virtualHalSimConfigSourceCoverageReady === true ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-promotion-candidates",
|
||||
label: "Virtual HAL sim-config promotion candidates",
|
||||
value: validation?.virtualHalSimConfigPromotionCandidatesReady === true ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-promotion-families",
|
||||
label: "Virtual HAL promotion families",
|
||||
value: validation?.virtualHalPromotionFamilySummary ?? "not provided",
|
||||
},
|
||||
...arrayOrEmpty(validation?.virtualHalPromotionFamilyRows).map((row) => ({
|
||||
id: `virtual-hal-promotion-family-${row.id}`,
|
||||
label: `Promotion family ${row.label}`,
|
||||
value: row.value,
|
||||
sourceFiles: row.sourceFiles,
|
||||
})),
|
||||
{
|
||||
id: "virtual-hal-sim-config-macro-load-fixtures",
|
||||
label: "Virtual HAL sim-config macro/load fixtures",
|
||||
value: validation?.virtualHalSimConfigMacroLoadFixturesReady === true ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-motion-controller-matrix",
|
||||
label: "Virtual HAL motion controller matrix",
|
||||
@@ -2179,6 +2398,7 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
let diagnosticsHttpStatus = null;
|
||||
let diagnosticsFetchError = null;
|
||||
let diagnosticsValidation = null;
|
||||
let diagnosticsArtifact = null;
|
||||
const diagnosticsMissing = [];
|
||||
if (diagnosticsUrl) {
|
||||
try {
|
||||
@@ -2190,7 +2410,7 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
diagnosticsValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation({});
|
||||
} else {
|
||||
diagnosticsFetched = true;
|
||||
const diagnosticsArtifact = JSON.parse(await diagnosticsResponse.text());
|
||||
diagnosticsArtifact = JSON.parse(await diagnosticsResponse.text());
|
||||
diagnosticsValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation(diagnosticsArtifact);
|
||||
diagnosticsMissing.push(...diagnosticsValidation.missing.map((item) => `diagnostics.${item}`));
|
||||
if (requireSavedSessionDiagnostics && diagnosticsValidation.hasSessionDiagnostics !== true) {
|
||||
@@ -2226,6 +2446,7 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
jsonWorkflow,
|
||||
validation: jsonWorkflow.validation,
|
||||
diagnosticsValidation,
|
||||
diagnosticsArtifact,
|
||||
summaryViewModel: jsonWorkflow.summaryViewModel,
|
||||
actionPlan: jsonWorkflow.actionPlan,
|
||||
missing,
|
||||
@@ -2305,6 +2526,8 @@ export function createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel
|
||||
workflow = {},
|
||||
) {
|
||||
const missing = arrayOrEmpty(workflow?.missing);
|
||||
const promotionCandidateSummary = objectOrEmpty(workflow?.diagnosticsArtifact?.virtualHalPromotionCandidateSummary);
|
||||
const promotionCandidateSummaryReady = promotionCandidateSummary.ready === true;
|
||||
const ready = workflow?.ready === true;
|
||||
const statusText = ready ? "Ready" : "Blocked";
|
||||
const detailText = ready
|
||||
@@ -2340,6 +2563,17 @@ export function createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel
|
||||
label: "Artifact validation",
|
||||
value: workflow?.validation?.ready === true ? "ready" : "blocked",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-promotion-families",
|
||||
label: "Virtual HAL promotion families",
|
||||
value: workflow?.validation?.virtualHalPromotionFamilySummary ?? "not provided",
|
||||
},
|
||||
...arrayOrEmpty(workflow?.validation?.virtualHalPromotionFamilyRows).map((row) => ({
|
||||
id: `virtual-hal-promotion-family-${row.id}`,
|
||||
label: `Promotion family ${row.label}`,
|
||||
value: row.value,
|
||||
sourceFiles: row.sourceFiles,
|
||||
})),
|
||||
{
|
||||
id: "browser-diagnostics",
|
||||
label: "Browser diagnostics",
|
||||
@@ -2347,6 +2581,41 @@ export function createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel
|
||||
? (workflow?.diagnosticsValidation?.ready === true ? "ready" : "blocked")
|
||||
: "not requested",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-promotion-candidate-summary",
|
||||
label: "Virtual HAL promotion candidate summary",
|
||||
value: workflow?.diagnosticsUrl
|
||||
? (promotionCandidateSummaryReady ? "ready" : "not provided")
|
||||
: "not requested",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-promotion-candidate",
|
||||
label: "Virtual HAL promotion candidate",
|
||||
value: promotionCandidateSummary.preferredCandidateId ?? "not provided",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-promotion-candidate-ini",
|
||||
label: "Candidate INI",
|
||||
value: promotionCandidateSummary.preferredIniPath ?? "not provided",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-promotion-candidate-gcode",
|
||||
label: "Candidate G-code",
|
||||
value: promotionCandidateSummary.preferredGcodePath ?? "not provided",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-promotion-candidate-count",
|
||||
label: "Candidate readiness",
|
||||
value: Number.isFinite(promotionCandidateSummary.readyCandidateCount) &&
|
||||
Number.isFinite(promotionCandidateSummary.candidateCount)
|
||||
? `${promotionCandidateSummary.readyCandidateCount}/${promotionCandidateSummary.candidateCount} ready`
|
||||
: "not provided",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-promotion-candidate-baseline",
|
||||
label: "Candidate inventory baseline",
|
||||
value: promotionCandidateSummary.inventoryBaseline ?? "not provided",
|
||||
},
|
||||
{
|
||||
id: "saved-session-diagnostics",
|
||||
label: "Saved-session diagnostics",
|
||||
@@ -2466,6 +2735,8 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
|
||||
const releaseGate = objectOrEmpty(artifactObject.releaseGate);
|
||||
const simConfigInventory = objectOrEmpty(artifactObject.simConfigInventory);
|
||||
const virtualHalSimConfigSourceCoverage = objectOrEmpty(artifactObject.virtualHalSimConfigSourceCoverage);
|
||||
const virtualHalSimConfigPromotionCandidates = objectOrEmpty(artifactObject.virtualHalSimConfigPromotionCandidates);
|
||||
const virtualHalSimConfigMacroLoadFixtures = objectOrEmpty(artifactObject.virtualHalSimConfigMacroLoadFixtures);
|
||||
const virtualHalMotionControllerMatrix = objectOrEmpty(artifactObject.virtualHalMotionControllerMatrix);
|
||||
const gateManifest = objectOrEmpty(artifactObject.gateManifest);
|
||||
const gateExecutionManifest = objectOrEmpty(artifactObject.gateExecutionManifest);
|
||||
@@ -2515,6 +2786,14 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
|
||||
const virtualHalSimConfigSourceCoverageReady = isVirtualHalSimConfigSourceCoverageReady(
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
) && artifactObject.virtualHalSimConfigSourceCoverageReady === true;
|
||||
const virtualHalSimConfigPromotionCandidatesReady = isVirtualHalSimConfigPromotionCandidateReportReady(
|
||||
virtualHalSimConfigPromotionCandidates,
|
||||
) && artifactObject.virtualHalSimConfigPromotionCandidatesReady === true;
|
||||
const virtualHalPromotionFamilySummary = createPromotionFamilySummaryText(virtualHalSimConfigPromotionCandidates);
|
||||
const virtualHalPromotionFamilyRows = createPromotionFamilySummaryRows(virtualHalSimConfigPromotionCandidates);
|
||||
const virtualHalSimConfigMacroLoadFixturesReady = isVirtualHalSimConfigMacroLoadFixtureReportReady(
|
||||
virtualHalSimConfigMacroLoadFixtures,
|
||||
) && artifactObject.virtualHalSimConfigMacroLoadFixturesReady === true;
|
||||
const virtualHalMotionControllerMatrixReady = isVirtualHalMotionControllerMatrixReady(
|
||||
virtualHalMotionControllerMatrix,
|
||||
) && artifactObject.virtualHalMotionControllerMatrixReady === true;
|
||||
@@ -2556,6 +2835,8 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
|
||||
...(simConfigInventory.skipped === 131 ? [] : ["simConfigInventory.skipped"]),
|
||||
...(simConfigInventory.unexpectedFail === 0 ? [] : ["simConfigInventory.unexpectedFail"]),
|
||||
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]),
|
||||
...(virtualHalSimConfigPromotionCandidatesReady ? [] : ["virtualHalSimConfigPromotionCandidates"]),
|
||||
...(virtualHalSimConfigMacroLoadFixturesReady ? [] : ["virtualHalSimConfigMacroLoadFixtures"]),
|
||||
...(virtualHalMotionControllerMatrixReady ? [] : ["virtualHalMotionControllerMatrix"]),
|
||||
...(arrayOrEmpty(artifactObject.blockedRuntimeFamilies).join(",") === BLOCKED_RUNTIME_FAMILIES.join(",")
|
||||
? []
|
||||
@@ -2591,6 +2872,10 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
|
||||
gateResultMatrixReady: matrixReady,
|
||||
gateActionPlanReady: actionPlanReady,
|
||||
virtualHalSimConfigSourceCoverageReady,
|
||||
virtualHalSimConfigPromotionCandidatesReady,
|
||||
virtualHalPromotionFamilySummary,
|
||||
virtualHalPromotionFamilyRows,
|
||||
virtualHalSimConfigMacroLoadFixturesReady,
|
||||
virtualHalMotionControllerMatrixReady,
|
||||
axisScreenshotArtifactSummaryReady: axisScreenshotSummaryReady,
|
||||
axisScreenshotArtifactCount: axisScreenshotArtifacts.length,
|
||||
@@ -2636,6 +2921,27 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
|
||||
label: "Virtual HAL sim-config source coverage",
|
||||
value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-promotion-candidates",
|
||||
label: "Virtual HAL sim-config promotion candidates",
|
||||
value: virtualHalSimConfigPromotionCandidatesReady ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-promotion-families",
|
||||
label: "Virtual HAL promotion families",
|
||||
value: virtualHalPromotionFamilySummary,
|
||||
},
|
||||
...virtualHalPromotionFamilyRows.map((row) => ({
|
||||
id: `virtual-hal-promotion-family-${row.id}`,
|
||||
label: `Promotion family ${row.label}`,
|
||||
value: row.value,
|
||||
sourceFiles: row.sourceFiles,
|
||||
})),
|
||||
{
|
||||
id: "virtual-hal-sim-config-macro-load-fixtures",
|
||||
label: "Virtual HAL sim-config macro/load fixtures",
|
||||
value: virtualHalSimConfigMacroLoadFixturesReady ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-motion-controller-matrix",
|
||||
label: "Virtual HAL motion controller matrix",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -101,6 +101,24 @@
|
||||
font: 13px/1.4 "Segoe UI", "Noto Sans", sans-serif;
|
||||
cursor: pointer;
|
||||
}
|
||||
.source-filter-controls {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
.source-filter-controls label {
|
||||
color: var(--muted);
|
||||
font: 12px/1.4 "Segoe UI", "Noto Sans", sans-serif;
|
||||
}
|
||||
.source-filter-controls select {
|
||||
width: 100%;
|
||||
min-height: 32px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
padding: 6px 8px;
|
||||
color: var(--text);
|
||||
font: 13px/1.4 "Segoe UI", "Noto Sans", sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -135,6 +153,15 @@
|
||||
<p data-workflow-overview-release-readiness-artifact-gate-execution-summary-status>Waiting for URL workflow gate evidence.</p>
|
||||
<dl data-workflow-overview-release-readiness-artifact-gate-execution-summary-rows></dl>
|
||||
</section>
|
||||
<section class="overview-status" aria-label="Promotion candidate drilldown" data-workflow-overview-promotion-candidate-drilldown>
|
||||
<p data-workflow-overview-promotion-candidate-drilldown-status>Waiting for promotion candidate evidence.</p>
|
||||
<dl data-workflow-overview-promotion-candidate-drilldown-rows></dl>
|
||||
</section>
|
||||
<section class="overview-status" aria-label="Promotion family source filter" data-workflow-overview-promotion-family-source-filter>
|
||||
<p data-workflow-overview-promotion-family-source-filter-status>Waiting for promotion family source evidence.</p>
|
||||
<div class="source-filter-controls" data-workflow-overview-promotion-family-source-filter-controls></div>
|
||||
<dl data-workflow-overview-promotion-family-source-filter-rows></dl>
|
||||
</section>
|
||||
</section>
|
||||
</main>
|
||||
<script type="module">
|
||||
@@ -178,6 +205,21 @@
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilyRowsRenderState,
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterViewModel,
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomContract,
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomReadiness,
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan,
|
||||
handleIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionChange,
|
||||
bindIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionControls,
|
||||
renderIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState,
|
||||
mountIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState,
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownViewModel,
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownRenderState,
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomContract,
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState,
|
||||
mountIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomReadiness,
|
||||
@@ -248,8 +290,53 @@
|
||||
renderState: gateExecutionSummaryRenderState,
|
||||
documentRef: document,
|
||||
});
|
||||
const promotionCandidateDrilldown =
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownViewModel(summary, {
|
||||
diagnosticsArtifact: workflow.diagnosticsArtifact,
|
||||
});
|
||||
const promotionCandidateDrilldownRenderState =
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownRenderState(
|
||||
promotionCandidateDrilldown,
|
||||
);
|
||||
const promotionCandidateDrilldownMountResult =
|
||||
mountIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState({
|
||||
renderState: promotionCandidateDrilldownRenderState,
|
||||
documentRef: document,
|
||||
});
|
||||
const promotionFamilyRowsRenderState =
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilyRowsRenderState(summary);
|
||||
const promotionFamilySourceFilter =
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterViewModel(
|
||||
promotionFamilyRowsRenderState,
|
||||
);
|
||||
const promotionFamilySourceFilterMountResult =
|
||||
mountIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState({
|
||||
viewModel: promotionFamilySourceFilter,
|
||||
documentRef: document,
|
||||
});
|
||||
const promotionFamilySourceFilterBinding =
|
||||
bindIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionControls({
|
||||
renderState: promotionFamilyRowsRenderState,
|
||||
viewModel: promotionFamilySourceFilter,
|
||||
documentRef: document,
|
||||
render: true,
|
||||
onChange: (changeResult) => {
|
||||
document.body.dataset.promotionFamilySourceFilterFamily =
|
||||
changeResult.selectedFamilyId;
|
||||
document.body.dataset.promotionFamilySourceFilterSourceKind =
|
||||
changeResult.selectedSourceKind;
|
||||
document.body.dataset.promotionFamilySourceFilterCount =
|
||||
String(changeResult.actionPlan.filteredSourceFileCount);
|
||||
},
|
||||
});
|
||||
document.body.dataset.releaseArtifactUrlWorkflowPhase = workflow.phase;
|
||||
document.body.dataset.releaseArtifactUrlWorkflowReady = workflow.ready ? "true" : "false";
|
||||
document.body.dataset.promotionCandidateDrilldownReady =
|
||||
promotionCandidateDrilldown.ready ? "true" : "false";
|
||||
document.body.dataset.promotionFamilySourceFilterReady =
|
||||
promotionFamilySourceFilter.ready ? "true" : "false";
|
||||
document.body.dataset.promotionFamilySourceFilterCount =
|
||||
String(promotionFamilySourceFilter.filteredSourceFileCount);
|
||||
return {
|
||||
workflow,
|
||||
summary,
|
||||
@@ -261,6 +348,13 @@
|
||||
gateExecutionSummary,
|
||||
gateExecutionSummaryRenderState,
|
||||
gateExecutionSummaryMountResult,
|
||||
promotionCandidateDrilldown,
|
||||
promotionCandidateDrilldownRenderState,
|
||||
promotionCandidateDrilldownMountResult,
|
||||
promotionFamilyRowsRenderState,
|
||||
promotionFamilySourceFilter,
|
||||
promotionFamilySourceFilterMountResult,
|
||||
promotionFamilySourceFilterBinding,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -362,6 +456,69 @@
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
getWorkflowOverviewPromotionFamilyRowsRenderState: (summaryViewModel) => (
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilyRowsRenderState(summaryViewModel)
|
||||
),
|
||||
getWorkflowOverviewPromotionFamilySourceFilterViewModel: (renderState, options) => (
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterViewModel(renderState, options)
|
||||
),
|
||||
getWorkflowOverviewPromotionFamilySourceFilterDomContract: (options) => (
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomContract(options)
|
||||
),
|
||||
getWorkflowOverviewPromotionFamilySourceFilterDomReadiness: (options) => (
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomReadiness(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
getWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan: (renderState, options) => (
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan(renderState, options)
|
||||
),
|
||||
handleWorkflowOverviewPromotionFamilySourceFilterSelectionChange: (options) => (
|
||||
handleIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionChange(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
bindWorkflowOverviewPromotionFamilySourceFilterSelectionControls: (options) => (
|
||||
bindIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionControls(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
renderWorkflowOverviewPromotionFamilySourceFilterState: (viewModel, options) => (
|
||||
renderIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState(
|
||||
viewModel,
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
mountWorkflowOverviewPromotionFamilySourceFilterState: (options) => (
|
||||
mountIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
getWorkflowOverviewPromotionCandidateDrilldownViewModel: (summaryViewModel, options) => (
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownViewModel(summaryViewModel, options)
|
||||
),
|
||||
getWorkflowOverviewPromotionCandidateDrilldownRenderState: (viewModel) => (
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownRenderState(viewModel)
|
||||
),
|
||||
getWorkflowOverviewPromotionCandidateDrilldownDomContract: (options) => (
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomContract(options)
|
||||
),
|
||||
getWorkflowOverviewPromotionCandidateDrilldownDomReadiness: (options) => (
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomReadiness(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
renderWorkflowOverviewPromotionCandidateDrilldownState: (renderState, options) => (
|
||||
renderIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState(
|
||||
renderState,
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
mountWorkflowOverviewPromotionCandidateDrilldownState: (options) => (
|
||||
mountIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState: (actionPlan) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState(actionPlan)
|
||||
),
|
||||
|
||||
@@ -683,6 +683,40 @@
|
||||
font-weight: 700;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.promotion-family-panel {
|
||||
grid-column: 1 / -1;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 4px 7px;
|
||||
border-top: 1px solid #c4c9cc;
|
||||
padding-top: 4px;
|
||||
}
|
||||
.promotion-family-panel div {
|
||||
border: 1px solid #b5bcc1;
|
||||
background: #f1f1f1;
|
||||
padding: 3px 4px;
|
||||
}
|
||||
.promotion-candidate-panel {
|
||||
grid-column: 1 / -1;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 4px 7px;
|
||||
border-top: 1px solid #c4c9cc;
|
||||
padding-top: 4px;
|
||||
}
|
||||
.promotion-candidate-panel div {
|
||||
min-height: 27px;
|
||||
border: 1px solid #b5bcc1;
|
||||
background: #f7f7f7;
|
||||
padding: 3px 4px;
|
||||
}
|
||||
.promotion-candidate-panel strong {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: var(--text);
|
||||
}
|
||||
.status-history {
|
||||
border-top: 1px solid var(--line);
|
||||
background: #f2f2f2;
|
||||
@@ -1709,6 +1743,8 @@ M2
|
||||
<div><span>Last error</span><strong data-diagnostics="last-error">none</strong></div>
|
||||
<div><span>Readiness</span><strong data-diagnostics="readiness">unchecked</strong></div>
|
||||
<div><span>Shortcut</span><strong data-diagnostics="shortcut">-</strong></div>
|
||||
<div class="promotion-family-panel" data-promotion-family-rows></div>
|
||||
<div class="promotion-candidate-panel" data-promotion-candidate-summary></div>
|
||||
<div class="tool-table-mini" data-axis-shell="tool-table">
|
||||
<table aria-label="Loaded tool table">
|
||||
<thead>
|
||||
@@ -2553,6 +2589,99 @@ M2
|
||||
});
|
||||
}
|
||||
|
||||
function renderPromotionFamilyRows(report = createVirtualHalSimConfigPromotionCandidateReport()) {
|
||||
const container = document.querySelector("[data-promotion-family-rows]");
|
||||
if (!container) {
|
||||
return [];
|
||||
}
|
||||
const rows = Array.isArray(report.familyRows) ? report.familyRows : [];
|
||||
container.textContent = "";
|
||||
document.body.dataset.promotionFamilyReady = rows.every((row) =>
|
||||
row.complete === true && row.explicitBrowserDiagnosticsReady === true
|
||||
) ? "true" : "false";
|
||||
document.body.dataset.promotionFamilyCount = String(rows.length);
|
||||
for (const row of rows) {
|
||||
const item = document.createElement("div");
|
||||
item.dataset.promotionFamilyRow = row.id ?? "unknown";
|
||||
item.dataset.promotionFamilyReady = row.complete === true && row.explicitBrowserDiagnosticsReady === true ? "true" : "false";
|
||||
const label = document.createElement("span");
|
||||
label.textContent = row.id ?? "unknown";
|
||||
const value = document.createElement("strong");
|
||||
value.dataset.promotionFamilyValue = row.id ?? "unknown";
|
||||
value.textContent = `${row.completeCount ?? 0}/${row.candidateCount ?? 0} ready; sources=${row.sourceFiles?.length ?? 0}`;
|
||||
item.append(label, value);
|
||||
container.append(item);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
function createVirtualHalPromotionCandidateSummary(
|
||||
report = createVirtualHalSimConfigPromotionCandidateReport(),
|
||||
) {
|
||||
const rows = Array.isArray(report.rows) ? report.rows : [];
|
||||
const familyRows = Array.isArray(report.familyRows) ? report.familyRows : [];
|
||||
const preferredCandidate = rows.find((row) => row.id === "qtdragon-multi-joint-on-abort") ?? rows[0] ?? null;
|
||||
const readyRows = rows.filter((row) => row.complete === true);
|
||||
const blockedRows = rows.filter((row) => row.blockedKind !== "-");
|
||||
const explicitBrowserRows = rows.filter((row) => row.targetBrowserEvidence === "explicit-browser-diagnostics");
|
||||
const sourceFiles = [...new Set(rows.flatMap((row) => row.sourceFiles ?? []))];
|
||||
const ready = report.complete === true &&
|
||||
readyRows.length === rows.length &&
|
||||
blockedRows.length === 0 &&
|
||||
explicitBrowserRows.length === rows.length &&
|
||||
report.inventoryBaselineUnchanged === true;
|
||||
return {
|
||||
apiName: "real-browser-simulation-promotion-candidate-summary",
|
||||
summaryVersion: 1,
|
||||
phase: ready ? "ready" : "blocked",
|
||||
ready,
|
||||
candidateCount: rows.length,
|
||||
readyCandidateCount: readyRows.length,
|
||||
familyCount: familyRows.length,
|
||||
sourceFileCount: sourceFiles.length,
|
||||
preferredCandidateId: preferredCandidate?.id ?? null,
|
||||
preferredIniPath: preferredCandidate?.iniPath ?? null,
|
||||
preferredGcodePath: preferredCandidate?.gcodePath ?? null,
|
||||
blockedCandidateIds: blockedRows.map((row) => row.id),
|
||||
explicitBrowserDiagnosticsCount: explicitBrowserRows.length,
|
||||
inventoryBaseline: "executed=28 passed=28 skipped=131 unexpected_fail=0",
|
||||
sourceFiles,
|
||||
rows: [
|
||||
{ id: "candidate-count", label: "Candidates", value: `${readyRows.length}/${rows.length} ready` },
|
||||
{ id: "family-count", label: "Families", value: `${familyRows.length}` },
|
||||
{ id: "preferred-candidate", label: "First case", value: preferredCandidate?.id ?? "missing" },
|
||||
{ id: "source-files", label: "Source files", value: `${sourceFiles.length}` },
|
||||
{ id: "inventory-baseline", label: "Inventory", value: "28/28 pass; 131 skip unchanged" },
|
||||
{ id: "blocked-candidates", label: "Hard blocks", value: blockedRows.length === 0 ? "excluded" : blockedRows.map((row) => row.id).join(", ") },
|
||||
{ id: "browser-evidence", label: "Browser evidence", value: `${explicitBrowserRows.length}/${rows.length} explicit` },
|
||||
{ id: "preferred-ini", label: "INI", value: preferredCandidate?.iniPath ?? "missing" },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function renderPromotionCandidateSummary(summary = createVirtualHalPromotionCandidateSummary()) {
|
||||
const container = document.querySelector("[data-promotion-candidate-summary]");
|
||||
if (!container) {
|
||||
return summary;
|
||||
}
|
||||
container.textContent = "";
|
||||
document.body.dataset.promotionCandidateReady = summary.ready ? "true" : "false";
|
||||
document.body.dataset.promotionCandidateCount = String(summary.candidateCount);
|
||||
document.body.dataset.promotionCandidatePreferred = summary.preferredCandidateId ?? "";
|
||||
for (const row of summary.rows) {
|
||||
const item = document.createElement("div");
|
||||
item.dataset.promotionCandidateSummaryRow = row.id;
|
||||
const label = document.createElement("span");
|
||||
label.textContent = row.label;
|
||||
const value = document.createElement("strong");
|
||||
value.dataset.promotionCandidateSummaryValue = row.id;
|
||||
value.textContent = row.value;
|
||||
item.append(label, value);
|
||||
container.append(item);
|
||||
}
|
||||
return summary;
|
||||
}
|
||||
|
||||
function renderMachineStatusDom(machineStatus) {
|
||||
const statusMap = {
|
||||
"spindle-state": machineStatus.spindle.state,
|
||||
@@ -2591,6 +2720,9 @@ M2
|
||||
renderVirtualHalDro();
|
||||
}
|
||||
renderLimitsHomeState(createVirtualHalLimitsHomeState());
|
||||
const promotionCandidateReport = createVirtualHalSimConfigPromotionCandidateReport();
|
||||
renderPromotionFamilyRows(promotionCandidateReport);
|
||||
renderPromotionCandidateSummary(createVirtualHalPromotionCandidateSummary(promotionCandidateReport));
|
||||
document.querySelector("[data-axis-machine-control-state]")?.setAttribute("data-state", "ready");
|
||||
return cloneVirtualHalState();
|
||||
}
|
||||
@@ -2642,6 +2774,7 @@ M2
|
||||
virtualHalSourceCompliance: createVirtualHalSourceComplianceReport(),
|
||||
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
|
||||
virtualHalSimConfigPromotionCandidates: createVirtualHalSimConfigPromotionCandidateReport(),
|
||||
virtualHalPromotionCandidateSummary: createVirtualHalPromotionCandidateSummary(),
|
||||
virtualHalSimConfigMacroLoadFixtures: createVirtualHalSimConfigMacroLoadFixtureReport(),
|
||||
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport(),
|
||||
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport(),
|
||||
@@ -4081,6 +4214,8 @@ M2
|
||||
getVirtualHalSourceComplianceReport: () => createVirtualHalSourceComplianceReport(),
|
||||
getVirtualHalSimConfigSourceCoverageReport: () => createVirtualHalSimConfigSourceCoverageReport(),
|
||||
getVirtualHalSimConfigPromotionCandidateReport: () => createVirtualHalSimConfigPromotionCandidateReport(),
|
||||
getVirtualHalPromotionCandidateSummary: () => renderPromotionCandidateSummary(),
|
||||
getVirtualHalPromotionFamilyRows: () => renderPromotionFamilyRows(),
|
||||
getVirtualHalSimConfigMacroLoadFixtureReport: () => createVirtualHalSimConfigMacroLoadFixtureReport(),
|
||||
getVirtualHalCommandScriptFixtureReport: () => createVirtualHalCommandScriptFixtureReport(),
|
||||
getVirtualHalMotionControllerMatrixReport: () => createVirtualHalMotionControllerMatrixReport(),
|
||||
|
||||
@@ -19,6 +19,7 @@ export {
|
||||
VIRTUAL_HAL_AXES,
|
||||
VIRTUAL_HAL_AXISUI_PINS,
|
||||
VIRTUAL_HAL_MOTION_CONTROLLER_MATRIX_FIXTURES,
|
||||
VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_AUDIT_CANDIDATES,
|
||||
VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES,
|
||||
VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES,
|
||||
VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES,
|
||||
|
||||
@@ -66,6 +66,15 @@
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness ||
|
||||
!workflowApi?.renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState ||
|
||||
!workflowApi?.mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState ||
|
||||
!workflowApi?.getWorkflowOverviewPromotionFamilyRowsRenderState ||
|
||||
!workflowApi?.getWorkflowOverviewPromotionFamilySourceFilterViewModel ||
|
||||
!workflowApi?.getWorkflowOverviewPromotionFamilySourceFilterDomContract ||
|
||||
!workflowApi?.getWorkflowOverviewPromotionFamilySourceFilterDomReadiness ||
|
||||
!workflowApi?.getWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan ||
|
||||
!workflowApi?.handleWorkflowOverviewPromotionFamilySourceFilterSelectionChange ||
|
||||
!workflowApi?.bindWorkflowOverviewPromotionFamilySourceFilterSelectionControls ||
|
||||
!workflowApi?.renderWorkflowOverviewPromotionFamilySourceFilterState ||
|
||||
!workflowApi?.mountWorkflowOverviewPromotionFamilySourceFilterState ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomContract ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomReadiness ||
|
||||
@@ -381,6 +390,22 @@
|
||||
manifestEntries: VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
|
||||
}),
|
||||
}),
|
||||
virtualHalPromotionCandidateSummary: {
|
||||
apiName: "real-browser-simulation-promotion-candidate-summary",
|
||||
summaryVersion: 1,
|
||||
phase: "ready",
|
||||
ready: true,
|
||||
candidateCount: 8,
|
||||
readyCandidateCount: 8,
|
||||
familyCount: 3,
|
||||
sourceFileCount: 17,
|
||||
preferredCandidateId: "qtdragon-multi-joint-on-abort",
|
||||
preferredIniPath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
|
||||
preferredGcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
||||
blockedCandidateIds: [],
|
||||
explicitBrowserDiagnosticsCount: 8,
|
||||
inventoryBaseline: "executed=28 passed=28 skipped=131 unexpected_fail=0",
|
||||
},
|
||||
virtualHalSimConfigMacroLoadFixtures: createVirtualHalSimConfigMacroLoadFixtureReport({
|
||||
manifestEntries: [
|
||||
...VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
|
||||
@@ -543,6 +568,11 @@
|
||||
assertEqual(releaseArtifactUrlWorkflow.httpStatus, 200, "workflow overview release artifact URL workflow status");
|
||||
assertEqual(releaseArtifactUrlWorkflow.diagnosticsHttpStatus, 200, "workflow overview diagnostics artifact URL workflow status");
|
||||
assertEqual(releaseArtifactUrlWorkflow.diagnosticsValidation.ready, true, "workflow overview diagnostics artifact validation");
|
||||
assertEqual(
|
||||
releaseArtifactUrlWorkflow.diagnosticsArtifact.virtualHalPromotionCandidateSummary.ready,
|
||||
true,
|
||||
"workflow overview diagnostics promotion candidate summary",
|
||||
);
|
||||
assertEqual(releaseArtifactUrlWorkflow.diagnosticsValidation.hasSessionDiagnostics, true, "workflow overview diagnostics saved-session presence");
|
||||
assertEqual(releaseArtifactUrlWorkflow.diagnosticsValidation.sessionDiagnosticsReady, true, "workflow overview diagnostics saved-session validation");
|
||||
assertEqual(
|
||||
@@ -567,7 +597,7 @@
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlWorkflowRenderResult.rowCount,
|
||||
12,
|
||||
18,
|
||||
"workflow overview release artifact URL workflow render rows",
|
||||
);
|
||||
assertEqual(
|
||||
@@ -575,6 +605,36 @@
|
||||
true,
|
||||
"workflow overview release artifact URL workflow diagnostics row",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("virtual-hal-promotion-candidate-summary"),
|
||||
true,
|
||||
"workflow overview release artifact URL workflow candidate summary row",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate")?.value,
|
||||
"qtdragon-multi-joint-on-abort",
|
||||
"workflow overview release artifact URL workflow candidate id",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-ini")?.value,
|
||||
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
|
||||
"workflow overview release artifact URL workflow candidate INI",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-gcode")?.value,
|
||||
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
||||
"workflow overview release artifact URL workflow candidate G-code",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-count")?.value,
|
||||
"8/8 ready",
|
||||
"workflow overview release artifact URL workflow candidate count",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value,
|
||||
"executed=28 passed=28 skipped=131 unexpected_fail=0",
|
||||
"workflow overview release artifact URL workflow candidate baseline",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("saved-session-diagnostics"),
|
||||
true,
|
||||
@@ -626,16 +686,71 @@
|
||||
true,
|
||||
"workflow overview fixed URL workflow gate execution summary row",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlFormResult.promotionFamilyRowsRenderState.ready,
|
||||
true,
|
||||
"workflow overview fixed URL workflow promotion family rows ready",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlFormResult.promotionFamilyRowsRenderState.sourceFileCount,
|
||||
17,
|
||||
"workflow overview fixed URL workflow promotion family source count",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlFormResult.promotionFamilySourceFilter.ready,
|
||||
true,
|
||||
"workflow overview fixed URL workflow source filter readiness",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlFormResult.promotionFamilySourceFilter.filteredSourceFileCount,
|
||||
17,
|
||||
"workflow overview fixed URL workflow source filter default count",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlFormResult.promotionFamilySourceFilterMountResult.ready,
|
||||
true,
|
||||
"workflow overview fixed URL workflow source filter mount readiness",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlFormResult.promotionFamilySourceFilterBinding.ready,
|
||||
true,
|
||||
"workflow overview fixed URL workflow source filter binding readiness",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlFormResult.promotionFamilySourceFilterBinding.boundControlCount,
|
||||
2,
|
||||
"workflow overview fixed URL workflow source filter bound controls",
|
||||
);
|
||||
assertEqual(
|
||||
workflowDoc.body.dataset.releaseArtifactUrlWorkflowReady,
|
||||
"true",
|
||||
"workflow overview fixed URL workflow form body ready",
|
||||
);
|
||||
assertEqual(
|
||||
workflowDoc.body.dataset.promotionFamilySourceFilterReady,
|
||||
"true",
|
||||
"workflow overview fixed URL workflow source filter body ready",
|
||||
);
|
||||
assertEqual(
|
||||
workflowDoc.body.dataset.promotionFamilySourceFilterCount,
|
||||
"17",
|
||||
"workflow overview fixed URL workflow source filter body default count",
|
||||
);
|
||||
assertEqual(
|
||||
workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-value="browser-diagnostics"]').textContent,
|
||||
"ready",
|
||||
"workflow overview fixed URL workflow form browser diagnostics row",
|
||||
);
|
||||
assertEqual(
|
||||
workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-value="virtual-hal-promotion-candidate"]').textContent,
|
||||
"qtdragon-multi-joint-on-abort",
|
||||
"workflow overview fixed URL workflow form candidate row",
|
||||
);
|
||||
assertEqual(
|
||||
workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-value="virtual-hal-promotion-candidate-count"]').textContent,
|
||||
"8/8 ready",
|
||||
"workflow overview fixed URL workflow form candidate count row",
|
||||
);
|
||||
assertEqual(
|
||||
workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-action-plan-value="next-command"]').textContent,
|
||||
"none",
|
||||
@@ -646,6 +761,59 @@
|
||||
true,
|
||||
"workflow overview fixed URL workflow gate execution summary DOM row",
|
||||
);
|
||||
const sourceFilterMount = workflowDoc.querySelector("[data-workflow-overview-promotion-family-source-filter]");
|
||||
const sourceFilterFamilySelect = workflowDoc.querySelector(
|
||||
'[data-workflow-overview-promotion-family-source-filter-select="family"]',
|
||||
);
|
||||
const sourceFilterKindSelect = workflowDoc.querySelector(
|
||||
'[data-workflow-overview-promotion-family-source-filter-select="source-kind"]',
|
||||
);
|
||||
assertEqual(
|
||||
sourceFilterMount.dataset.selectedFamilyId,
|
||||
"all",
|
||||
"workflow overview source filter default family",
|
||||
);
|
||||
assertEqual(
|
||||
sourceFilterFamilySelect?.value,
|
||||
"all",
|
||||
"workflow overview source filter family select default",
|
||||
);
|
||||
assertEqual(
|
||||
sourceFilterKindSelect?.value,
|
||||
"all",
|
||||
"workflow overview source filter kind select default",
|
||||
);
|
||||
sourceFilterFamilySelect.value = "virtual-hal-promotion-family-rose-engine-rcone-demo";
|
||||
sourceFilterFamilySelect.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
assertEqual(
|
||||
workflowDoc.body.dataset.promotionFamilySourceFilterFamily,
|
||||
"virtual-hal-promotion-family-rose-engine-rcone-demo",
|
||||
"workflow overview source filter change body family",
|
||||
);
|
||||
assertEqual(
|
||||
workflowDoc.body.dataset.promotionFamilySourceFilterCount,
|
||||
"2",
|
||||
"workflow overview source filter change source count",
|
||||
);
|
||||
assertEqual(
|
||||
sourceFilterMount.dataset.selectedFamilyId,
|
||||
"virtual-hal-promotion-family-rose-engine-rcone-demo",
|
||||
"workflow overview source filter changed mount family",
|
||||
);
|
||||
assertEqual(
|
||||
workflowDoc.querySelectorAll(
|
||||
'[data-workflow-overview-promotion-family-source-filter-type="source-row"]',
|
||||
).length,
|
||||
4,
|
||||
"workflow overview source filter changed source DOM nodes",
|
||||
);
|
||||
assertEqual(
|
||||
workflowDoc.querySelector(
|
||||
'[data-workflow-overview-promotion-family-source-filter-value="linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc"]',
|
||||
)?.textContent,
|
||||
"linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
|
||||
"workflow overview source filter changed rose engine source row",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactJsonWorkflow.statusLine,
|
||||
"Ready: Artifact JSON workflow complete",
|
||||
|
||||
@@ -97,6 +97,7 @@
|
||||
"native-runtime-probe-summary.tsv",
|
||||
"next-boundary-recommendations.tsv",
|
||||
"next-boundary-worklist.tsv",
|
||||
"promotion-candidates.tsv",
|
||||
"python-remap-boundary-summary.tsv",
|
||||
"python-remap-family-summary.tsv",
|
||||
"python-remap-native-runtime-alignment.tsv",
|
||||
@@ -150,7 +151,7 @@
|
||||
"build/native/source-probes.tsv",
|
||||
];
|
||||
|
||||
if (wasmArtifactNames.length !== 54 || new Set(wasmArtifactNames).size !== wasmArtifactNames.length) {
|
||||
if (wasmArtifactNames.length !== 55 || 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) {
|
||||
|
||||
@@ -244,6 +244,8 @@
|
||||
!api?.getVirtualHalSourceComplianceReport ||
|
||||
!api?.getVirtualHalSimConfigSourceCoverageReport ||
|
||||
!api?.getVirtualHalSimConfigPromotionCandidateReport ||
|
||||
!api?.getVirtualHalPromotionCandidateSummary ||
|
||||
!api?.getVirtualHalPromotionFamilyRows ||
|
||||
!api?.getVirtualHalSimConfigMacroLoadFixtureReport ||
|
||||
!api?.getVirtualHalCommandScriptFixtureReport ||
|
||||
!api?.getVirtualHalMotionControllerMatrixReport ||
|
||||
@@ -409,6 +411,7 @@
|
||||
throw new Error(`simulation virtual HAL registry/integrity drift: ${JSON.stringify(initialVirtualHalRegistry)}`);
|
||||
}
|
||||
const diagnosticsArtifact = api.exportDiagnosticsArtifact();
|
||||
const promotionCandidateSummary = api.getVirtualHalPromotionCandidateSummary();
|
||||
const expectedPromotionCandidates = [
|
||||
[
|
||||
"qtdragon-multi-joint-on-abort",
|
||||
@@ -458,6 +461,9 @@
|
||||
"linuxcnc/configs/sim/axis/rose_engine/rcone.ngc",
|
||||
false,
|
||||
"linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
|
||||
"rcone.ngc",
|
||||
34,
|
||||
"linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
|
||||
"linuxcnc/configs/sim/axis/rose_engine/rcone.ngc",
|
||||
"linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
|
||||
],
|
||||
@@ -466,6 +472,9 @@
|
||||
"linuxcnc/configs/sim/axis/external_offsets/queuebuster.ngc",
|
||||
true,
|
||||
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
|
||||
"queuebuster.ngc",
|
||||
40,
|
||||
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
|
||||
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc",
|
||||
],
|
||||
];
|
||||
@@ -481,6 +490,18 @@
|
||||
"L4-PYTHON-REMAP",
|
||||
],
|
||||
];
|
||||
const expectedMacroLoadAuditCandidates = [
|
||||
[
|
||||
"gscreen-industrial-lathe-wear-toolchange-audit",
|
||||
"linuxcnc/configs/sim/gscreen/industrial_lathe_wear/toolchange.ngc",
|
||||
"blocked-missing-owning-ini",
|
||||
],
|
||||
[
|
||||
"qtvcp-industrial-lathe-wear-toolchange-audit",
|
||||
"linuxcnc/configs/sim/qtvcp_screens/industrial_lathe_wear/toolchange.ngc",
|
||||
"blocked-missing-owning-ini",
|
||||
],
|
||||
];
|
||||
if (
|
||||
diagnosticsArtifact.apiName !== "real-browser-simulation-diagnostics-artifact" ||
|
||||
diagnosticsArtifact.statusHistory.length === 0 ||
|
||||
@@ -495,18 +516,41 @@
|
||||
!diagnosticsArtifact.virtualHalSimConfigSourceCoverage?.sourceFiles?.includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/qtdragon_xyza.ini") ||
|
||||
!diagnosticsArtifact.virtualHalSimConfigSourceCoverage?.sourceFiles?.includes("linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/qtdragon_hd_z_compensation.ini") ||
|
||||
diagnosticsArtifact.virtualHalSimConfigPromotionCandidates?.complete !== true ||
|
||||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.ready !== true ||
|
||||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.preferredCandidateId !== "qtdragon-multi-joint-on-abort" ||
|
||||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.candidateCount !== 8 ||
|
||||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.readyCandidateCount !== 8 ||
|
||||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.sourceFileCount !== 17 ||
|
||||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.inventoryBaseline !== "executed=28 passed=28 skipped=131 unexpected_fail=0" ||
|
||||
promotionCandidateSummary.ready !== true ||
|
||||
promotionCandidateSummary.preferredIniPath !== "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini" ||
|
||||
doc.body.dataset.promotionCandidateReady !== "true" ||
|
||||
doc.body.dataset.promotionCandidatePreferred !== "qtdragon-multi-joint-on-abort" ||
|
||||
doc.querySelector('[data-promotion-candidate-summary-value="preferred-candidate"]')?.textContent !== "qtdragon-multi-joint-on-abort" ||
|
||||
doc.querySelector('[data-promotion-candidate-summary-value="inventory-baseline"]')?.textContent !== "28/28 pass; 131 skip unchanged" ||
|
||||
!diagnosticsArtifact.virtualHalSimConfigPromotionCandidates?.familyRows?.some((row) =>
|
||||
row.id === "qtdragon-on-abort" &&
|
||||
row.candidateCount === 6 &&
|
||||
row.completeCount === 6 &&
|
||||
row.explicitBrowserDiagnosticsCount === 6 &&
|
||||
row.explicitBrowserDiagnosticsReady === true &&
|
||||
row.complete === true
|
||||
) ||
|
||||
!expectedPromotionCandidates.every(([id, ...sourceFiles]) =>
|
||||
diagnosticsArtifact.virtualHalSimConfigPromotionCandidates?.rows?.some((row) =>
|
||||
row.id === id &&
|
||||
row.complete === true &&
|
||||
row.currentNodeInventoryStatus === "PASS" &&
|
||||
row.currentMatrixBrowserStatus === "explicit-browser-diagnostics" &&
|
||||
row.targetBrowserEvidence === "explicit-browser-diagnostics" &&
|
||||
sourceFiles.every((sourceFile) => row.sourceFiles.includes(sourceFile))
|
||||
)
|
||||
) ||
|
||||
diagnosticsArtifact.virtualHalSimConfigMacroLoadFixtures?.complete !== true ||
|
||||
diagnosticsArtifact.virtualHalSimConfigMacroLoadFixtures?.blockedFixturePromotionViolations?.length !== 0 ||
|
||||
!expectedMacroLoadFixtures.every(([id, fixturePath, declaredOnly, ...sourceFiles]) =>
|
||||
diagnosticsArtifact.virtualHalSimConfigMacroLoadFixtures?.blockedBoundaryEvidenceViolations?.length !== 0 ||
|
||||
diagnosticsArtifact.virtualHalSimConfigMacroLoadFixtures?.auditPromotionViolations?.length !== 0 ||
|
||||
!expectedMacroLoadFixtures.every(([id, fixturePath, declaredOnly, declarationSourceFile, declarationValue, declarationLine, ...sourceFiles]) =>
|
||||
diagnosticsArtifact.virtualHalSimConfigMacroLoadFixtures?.rows?.some((row) =>
|
||||
row.id === id &&
|
||||
row.complete === true &&
|
||||
@@ -514,6 +558,12 @@
|
||||
row.targetBrowserEvidence === "non-main-fixture-diagnostics" &&
|
||||
row.fixturePath === fixturePath &&
|
||||
row.declaredOnly === declaredOnly &&
|
||||
row.requiresDeclarationEvidence === true &&
|
||||
row.declarationEvidenceReady === true &&
|
||||
row.declarationEvidence?.sourceFile === declarationSourceFile &&
|
||||
row.declarationEvidence?.key === "NGCGUI_SUBFILE" &&
|
||||
row.declarationEvidence?.value === declarationValue &&
|
||||
row.declarationEvidence?.line === declarationLine &&
|
||||
sourceFiles.every((sourceFile) => row.sourceFiles.includes(sourceFile))
|
||||
)
|
||||
) ||
|
||||
@@ -523,6 +573,38 @@
|
||||
row.fixturePath === fixturePath &&
|
||||
row.blockedKind === blockedKind &&
|
||||
row.excludedFromPositiveFixtures === true &&
|
||||
row.boundaryEvidenceReady === true &&
|
||||
row.boundaryEvidence?.baselineSummary?.executed === 28 &&
|
||||
row.boundaryEvidence?.baselineSummary?.passed === 28 &&
|
||||
row.boundaryEvidence?.baselineSummary?.skipped === 131 &&
|
||||
row.boundaryEvidence?.baselineSummary?.unexpectedFail === 0 &&
|
||||
row.boundaryEvidence?.sourceArtifactHashes?.["wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv"] === "de6cf57b7c07182e3bcb32e22dbdf202b618cabc14620d6dff1ef815587950b9" &&
|
||||
row.boundaryEvidence?.sourceArtifactHashes?.["wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv"] === "b0afe27224e97a82fbecbbd75a7c86233c98fe957d9c1ba20f0656f8745a5eae" &&
|
||||
row.boundaryEvidence?.boundarySummary?.recommendedBlocked === "UNAVAILABLE" &&
|
||||
row.boundaryEvidence?.boundarySummary?.dependencies?.includes("missing_vendored_ini:") &&
|
||||
row.boundaryEvidence?.iniBoundarySummary?.vendored === 0 &&
|
||||
row.boundaryEvidence?.iniBoundarySummary?.reportAvailable === 0 &&
|
||||
row.complete === true
|
||||
)
|
||||
) ||
|
||||
!expectedMacroLoadAuditCandidates.every(([id, fixturePath, auditStatus]) =>
|
||||
diagnosticsArtifact.virtualHalSimConfigMacroLoadFixtures?.auditRows?.some((row) =>
|
||||
row.id === id &&
|
||||
row.fixturePath === fixturePath &&
|
||||
row.auditStatus === auditStatus &&
|
||||
row.promotionAllowed === false &&
|
||||
row.excludedFromPositiveFixtures === true &&
|
||||
row.boundaryEvidenceReady === true &&
|
||||
row.boundaryEvidence?.baselineSummary?.executed === 28 &&
|
||||
row.boundaryEvidence?.baselineSummary?.passed === 28 &&
|
||||
row.boundaryEvidence?.baselineSummary?.skipped === 131 &&
|
||||
row.boundaryEvidence?.baselineSummary?.unexpectedFail === 0 &&
|
||||
row.boundaryEvidence?.sourceArtifactHashes?.["wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv"] === "de6cf57b7c07182e3bcb32e22dbdf202b618cabc14620d6dff1ef815587950b9" &&
|
||||
row.boundaryEvidence?.sourceArtifactHashes?.["wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv"] === "b0afe27224e97a82fbecbbd75a7c86233c98fe957d9c1ba20f0656f8745a5eae" &&
|
||||
row.boundaryEvidence?.boundarySummary?.recommendedBlocked === "UNAVAILABLE" &&
|
||||
row.boundaryEvidence?.boundarySummary?.dependencies?.includes("missing_vendored_ini:") &&
|
||||
row.boundaryEvidence?.iniBoundarySummary?.vendored === 0 &&
|
||||
row.boundaryEvidence?.iniBoundarySummary?.reportAvailable === 0 &&
|
||||
row.complete === true
|
||||
)
|
||||
) ||
|
||||
@@ -946,6 +1028,7 @@
|
||||
}
|
||||
const virtualHalReplacement = api.getVirtualHalSimulationReplacementReport();
|
||||
const promotionCandidateReport = api.getVirtualHalSimConfigPromotionCandidateReport();
|
||||
const promotionFamilyRows = api.getVirtualHalPromotionFamilyRows();
|
||||
const macroLoadFixtureReport = api.getVirtualHalSimConfigMacroLoadFixtureReport();
|
||||
if (
|
||||
virtualHalReplacement.apiName !== "linuxcnc-wasm-virtual-hal-simulation-replacement-report" ||
|
||||
@@ -966,6 +1049,17 @@
|
||||
!api.getVirtualHalSimConfigSourceCoverageReport().sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc") ||
|
||||
promotionCandidateReport.complete !== true ||
|
||||
promotionCandidateReport.inventoryBaselineUnchanged !== true ||
|
||||
doc.body.dataset.promotionFamilyReady !== "true" ||
|
||||
doc.body.dataset.promotionFamilyCount !== "3" ||
|
||||
promotionFamilyRows.length !== 3 ||
|
||||
![
|
||||
["qtdragon-on-abort", "6/6 ready; sources=12"],
|
||||
["vismach-remap-sims", "1/1 ready; sources=3"],
|
||||
["rose-engine-rcone-demo", "1/1 ready; sources=2"],
|
||||
].every(([id, value]) =>
|
||||
doc.querySelector(`[data-promotion-family-row="${id}"]`)?.dataset.promotionFamilyReady === "true" &&
|
||||
doc.querySelector(`[data-promotion-family-value="${id}"]`)?.textContent === value
|
||||
) ||
|
||||
!expectedPromotionCandidates.every(([id, ...sourceFiles]) =>
|
||||
promotionCandidateReport.rows.some((row) =>
|
||||
row.id === id &&
|
||||
@@ -978,13 +1072,21 @@
|
||||
macroLoadFixtureReport.inventoryBaselineUnchanged !== true ||
|
||||
macroLoadFixtureReport.standaloneMainViolations.length !== 0 ||
|
||||
macroLoadFixtureReport.blockedFixturePromotionViolations.length !== 0 ||
|
||||
!expectedMacroLoadFixtures.every(([id, fixturePath, declaredOnly, ...sourceFiles]) =>
|
||||
macroLoadFixtureReport.blockedBoundaryEvidenceViolations.length !== 0 ||
|
||||
macroLoadFixtureReport.auditPromotionViolations.length !== 0 ||
|
||||
!expectedMacroLoadFixtures.every(([id, fixturePath, declaredOnly, declarationSourceFile, declarationValue, declarationLine, ...sourceFiles]) =>
|
||||
macroLoadFixtureReport.rows.some((row) =>
|
||||
row.id === id &&
|
||||
row.complete === true &&
|
||||
row.nonMainFixture === true &&
|
||||
row.fixturePath === fixturePath &&
|
||||
row.declaredOnly === declaredOnly &&
|
||||
row.requiresDeclarationEvidence === true &&
|
||||
row.declarationEvidenceReady === true &&
|
||||
row.declarationEvidence?.sourceFile === declarationSourceFile &&
|
||||
row.declarationEvidence?.key === "NGCGUI_SUBFILE" &&
|
||||
row.declarationEvidence?.value === declarationValue &&
|
||||
row.declarationEvidence?.line === declarationLine &&
|
||||
sourceFiles.every((sourceFile) => row.sourceFiles.includes(sourceFile))
|
||||
)
|
||||
) ||
|
||||
@@ -994,6 +1096,38 @@
|
||||
row.fixturePath === fixturePath &&
|
||||
row.blockedKind === blockedKind &&
|
||||
row.excludedFromPositiveFixtures === true &&
|
||||
row.boundaryEvidenceReady === true &&
|
||||
row.boundaryEvidence?.baselineSummary?.executed === 28 &&
|
||||
row.boundaryEvidence?.baselineSummary?.passed === 28 &&
|
||||
row.boundaryEvidence?.baselineSummary?.skipped === 131 &&
|
||||
row.boundaryEvidence?.baselineSummary?.unexpectedFail === 0 &&
|
||||
row.boundaryEvidence?.sourceArtifactHashes?.["wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv"] === "de6cf57b7c07182e3bcb32e22dbdf202b618cabc14620d6dff1ef815587950b9" &&
|
||||
row.boundaryEvidence?.sourceArtifactHashes?.["wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv"] === "b0afe27224e97a82fbecbbd75a7c86233c98fe957d9c1ba20f0656f8745a5eae" &&
|
||||
row.boundaryEvidence?.boundarySummary?.recommendedBlocked === "UNAVAILABLE" &&
|
||||
row.boundaryEvidence?.boundarySummary?.dependencies?.includes("missing_vendored_ini:") &&
|
||||
row.boundaryEvidence?.iniBoundarySummary?.vendored === 0 &&
|
||||
row.boundaryEvidence?.iniBoundarySummary?.reportAvailable === 0 &&
|
||||
row.complete === true
|
||||
)
|
||||
) ||
|
||||
!expectedMacroLoadAuditCandidates.every(([id, fixturePath, auditStatus]) =>
|
||||
macroLoadFixtureReport.auditRows.some((row) =>
|
||||
row.id === id &&
|
||||
row.fixturePath === fixturePath &&
|
||||
row.auditStatus === auditStatus &&
|
||||
row.promotionAllowed === false &&
|
||||
row.excludedFromPositiveFixtures === true &&
|
||||
row.boundaryEvidenceReady === true &&
|
||||
row.boundaryEvidence?.baselineSummary?.executed === 28 &&
|
||||
row.boundaryEvidence?.baselineSummary?.passed === 28 &&
|
||||
row.boundaryEvidence?.baselineSummary?.skipped === 131 &&
|
||||
row.boundaryEvidence?.baselineSummary?.unexpectedFail === 0 &&
|
||||
row.boundaryEvidence?.sourceArtifactHashes?.["wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv"] === "de6cf57b7c07182e3bcb32e22dbdf202b618cabc14620d6dff1ef815587950b9" &&
|
||||
row.boundaryEvidence?.sourceArtifactHashes?.["wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv"] === "b0afe27224e97a82fbecbbd75a7c86233c98fe957d9c1ba20f0656f8745a5eae" &&
|
||||
row.boundaryEvidence?.boundarySummary?.recommendedBlocked === "UNAVAILABLE" &&
|
||||
row.boundaryEvidence?.boundarySummary?.dependencies?.includes("missing_vendored_ini:") &&
|
||||
row.boundaryEvidence?.iniBoundarySummary?.vendored === 0 &&
|
||||
row.boundaryEvidence?.iniBoundarySummary?.reportAvailable === 0 &&
|
||||
row.complete === true
|
||||
)
|
||||
) ||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -28,6 +28,8 @@ assert.equal(validation.gateExecutionSummaryReady, true);
|
||||
assert.equal(validation.gateResultMatrixReady, true);
|
||||
assert.equal(validation.gateActionPlanReady, true);
|
||||
assert.equal(validation.virtualHalSimConfigSourceCoverageReady, true);
|
||||
assert.equal(validation.virtualHalSimConfigPromotionCandidatesReady, true);
|
||||
assert.equal(validation.virtualHalSimConfigMacroLoadFixturesReady, true);
|
||||
assert.equal(validation.virtualHalMotionControllerMatrixReady, true);
|
||||
assert.equal(validation.axisScreenshotArtifactSummaryReady, true);
|
||||
assert.ok(validation.axisScreenshotArtifactCount === 0 || validation.axisScreenshotArtifactCount === 4);
|
||||
@@ -60,6 +62,105 @@ assert.equal(artifact.virtualHalSimConfigSourceCoverage.webSimulationSatisfied,
|
||||
assert.equal(artifact.virtualHalSimConfigSourceCoverageReady, true);
|
||||
assert.equal(artifact.virtualHalSimConfigSourceCoverage.sourceFiles.includes("linuxcnc/configs/sim/axis/foam/axis_foam.ini"), true);
|
||||
assert.equal(artifact.virtualHalSimConfigSourceCoverage.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc"), true);
|
||||
assert.equal(artifact.virtualHalSimConfigPromotionCandidates.apiName, "linuxcnc-wasm-virtual-hal-sim-config-promotion-candidate-report");
|
||||
assert.equal(artifact.virtualHalSimConfigPromotionCandidates.complete, true);
|
||||
assert.equal(artifact.virtualHalSimConfigPromotionCandidatesReady, true);
|
||||
assert.equal(
|
||||
artifact.virtualHalPromotionFamilySummary,
|
||||
"qtdragon-on-abort: 6/6 ready; vismach-remap-sims: 1/1 ready; rose-engine-rcone-demo: 1/1 ready",
|
||||
);
|
||||
assert.deepEqual(
|
||||
artifact.virtualHalPromotionFamilyRows.map(({ id, value }) => [id, value]),
|
||||
[
|
||||
["qtdragon-on-abort", "6/6 ready; sources=12; diagnostics=ready"],
|
||||
["vismach-remap-sims", "1/1 ready; sources=3; diagnostics=ready"],
|
||||
["rose-engine-rcone-demo", "1/1 ready; sources=2; diagnostics=ready"],
|
||||
],
|
||||
);
|
||||
assert.equal(
|
||||
artifact.virtualHalSimConfigPromotionCandidates.familyRows.some((row) =>
|
||||
row.id === "qtdragon-on-abort" &&
|
||||
row.candidateCount === 6 &&
|
||||
row.completeCount === 6 &&
|
||||
row.explicitBrowserDiagnosticsCount === 6 &&
|
||||
row.explicitBrowserDiagnosticsReady === true &&
|
||||
row.complete === true
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
artifact.virtualHalSimConfigPromotionCandidates.rows.some((row) =>
|
||||
row.id === "rose-engine-rcone-demo" &&
|
||||
row.currentMatrixBrowserStatus === "explicit-browser-diagnostics" &&
|
||||
row.sourceFiles.includes("linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc")
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(artifact.virtualHalSimConfigMacroLoadFixtures.apiName, "linuxcnc-wasm-virtual-hal-sim-config-macro-load-fixture-report");
|
||||
assert.equal(artifact.virtualHalSimConfigMacroLoadFixtures.complete, true);
|
||||
assert.equal(artifact.virtualHalSimConfigMacroLoadFixturesReady, true);
|
||||
assert.equal(artifact.virtualHalSimConfigMacroLoadFixtures.declarationEvidenceViolations.length, 0);
|
||||
assert.equal(artifact.virtualHalSimConfigMacroLoadFixtures.missingAuditRows.length, 0);
|
||||
assert.equal(artifact.virtualHalSimConfigMacroLoadFixtures.blockedBoundaryEvidenceViolations.length, 0);
|
||||
assert.equal(artifact.virtualHalSimConfigMacroLoadFixtures.auditPromotionViolations.length, 0);
|
||||
assert.equal(artifact.virtualHalSimConfigMacroLoadFixtures.auditBoundaryEvidenceViolations.length, 0);
|
||||
assert.equal(artifact.virtualHalSimConfigMacroLoadFixtures.auditPromotionAllowedViolations.length, 0);
|
||||
assert.equal(
|
||||
artifact.virtualHalSimConfigMacroLoadFixtures.rows.some((row) =>
|
||||
row.id === "rose-engine-rcone-macro-load" &&
|
||||
row.requiresDeclarationEvidence === true &&
|
||||
row.declarationEvidenceReady === true &&
|
||||
row.declarationEvidence.sourceFile === "linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini" &&
|
||||
row.declarationEvidence.key === "NGCGUI_SUBFILE" &&
|
||||
row.declarationEvidence.value === "rcone.ngc" &&
|
||||
row.declarationEvidence.line === 34
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
artifact.virtualHalSimConfigMacroLoadFixtures.rows.some((row) =>
|
||||
row.id === "external-offsets-queuebuster-macro-load" &&
|
||||
row.requiresDeclarationEvidence === true &&
|
||||
row.declarationEvidenceReady === true &&
|
||||
row.declarationEvidence.sourceFile === "linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini" &&
|
||||
row.declarationEvidence.key === "NGCGUI_SUBFILE" &&
|
||||
row.declarationEvidence.value === "queuebuster.ngc" &&
|
||||
row.declarationEvidence.line === 40
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
artifact.virtualHalSimConfigMacroLoadFixtures.auditRows.some((row) =>
|
||||
row.id === "gscreen-industrial-lathe-wear-toolchange-audit" &&
|
||||
row.promotionAllowed === false &&
|
||||
row.excludedFromPositiveFixtures === true &&
|
||||
row.boundaryEvidenceReady === true &&
|
||||
row.boundaryEvidence.baselineSummary.executed === 28 &&
|
||||
row.boundaryEvidence.baselineSummary.passed === 28 &&
|
||||
row.boundaryEvidence.baselineSummary.skipped === 131 &&
|
||||
row.boundaryEvidence.baselineSummary.unexpectedFail === 0 &&
|
||||
row.boundaryEvidence.sourceArtifactHashes["wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv"] === "de6cf57b7c07182e3bcb32e22dbdf202b618cabc14620d6dff1ef815587950b9" &&
|
||||
row.boundaryEvidence.sourceArtifactHashes["wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv"] === "b0afe27224e97a82fbecbbd75a7c86233c98fe957d9c1ba20f0656f8745a5eae" &&
|
||||
row.boundaryEvidence.boundarySummary.dependencies.includes("missing_vendored_ini:configs/sim/gscreen/industrial_lathe_wear/industrial_lathe_wear.ini") &&
|
||||
row.boundaryEvidence.iniBoundarySummary.vendored === 0
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
artifact.virtualHalSimConfigMacroLoadFixtures.blockedRows.some((row) =>
|
||||
row.id === "silverdragon-tool-sensor-python-ui-boundary" &&
|
||||
row.boundaryEvidenceReady === true &&
|
||||
row.boundaryEvidence.baselineSummary.executed === 28 &&
|
||||
row.boundaryEvidence.baselineSummary.passed === 28 &&
|
||||
row.boundaryEvidence.baselineSummary.skipped === 131 &&
|
||||
row.boundaryEvidence.baselineSummary.unexpectedFail === 0 &&
|
||||
row.boundaryEvidence.sourceArtifactHashes["wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv"] === "de6cf57b7c07182e3bcb32e22dbdf202b618cabc14620d6dff1ef815587950b9" &&
|
||||
row.boundaryEvidence.sourceArtifactHashes["wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv"] === "b0afe27224e97a82fbecbbd75a7c86233c98fe957d9c1ba20f0656f8745a5eae" &&
|
||||
row.boundaryEvidence.boundarySummary.dependencies.includes("missing_vendored_ini:configs/sim/gscreen/silverdragon/silverdragon.ini") &&
|
||||
row.boundaryEvidence.iniBoundarySummary.vendored === 0
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(artifact.virtualHalMotionControllerMatrix.apiName, "linuxcnc-wasm-virtual-hal-motion-controller-matrix-report");
|
||||
assert.equal(artifact.virtualHalMotionControllerMatrix.complete, true);
|
||||
assert.equal(artifact.virtualHalMotionControllerMatrix.manifestChecked, true);
|
||||
|
||||
@@ -5,6 +5,8 @@ import { fileURLToPath } from "node:url";
|
||||
import {
|
||||
createProjectReleaseReadinessReport,
|
||||
createVirtualHalMotionControllerMatrixReport,
|
||||
createVirtualHalSimConfigMacroLoadFixtureReport,
|
||||
createVirtualHalSimConfigPromotionCandidateReport,
|
||||
createVirtualHalSimConfigSourceCoverageReport,
|
||||
} from "../../runtime/sdk/src/index.js";
|
||||
|
||||
@@ -31,6 +33,13 @@ const passedGateResults = {
|
||||
"project-release-gate": true,
|
||||
};
|
||||
|
||||
const virtualHalSimConfigSourceCoverage = createVirtualHalSimConfigSourceCoverageReport({
|
||||
manifestText: sourceManifestText,
|
||||
});
|
||||
const virtualHalMotionControllerMatrix = createVirtualHalMotionControllerMatrixReport({
|
||||
manifestText: sourceManifestText,
|
||||
});
|
||||
|
||||
function loadAxisScreenshotArtifacts(directory) {
|
||||
if (!directory || !existsSync(directory)) {
|
||||
return [];
|
||||
@@ -65,12 +74,16 @@ const report = createProjectReleaseReadinessReport({
|
||||
"host_wasm_opfs_browser_smokes=ok",
|
||||
"project_release_gate=ok",
|
||||
],
|
||||
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport({
|
||||
manifestText: sourceManifestText,
|
||||
}),
|
||||
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport({
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
virtualHalSimConfigPromotionCandidates: createVirtualHalSimConfigPromotionCandidateReport({
|
||||
manifestText: sourceManifestText,
|
||||
simConfigSourceCoverage: virtualHalSimConfigSourceCoverage,
|
||||
motionControllerMatrix: virtualHalMotionControllerMatrix,
|
||||
}),
|
||||
virtualHalSimConfigMacroLoadFixtures: createVirtualHalSimConfigMacroLoadFixtureReport({
|
||||
manifestText: sourceManifestText,
|
||||
}),
|
||||
virtualHalMotionControllerMatrix,
|
||||
axisScreenshotArtifacts: loadAxisScreenshotArtifacts(axisScreenshotArtifactDir),
|
||||
});
|
||||
|
||||
|
||||
@@ -43,6 +43,22 @@ const browserDiagnosticsArtifact = {
|
||||
virtualHalSourceCompliance: createVirtualHalSourceComplianceReport({ halState: virtualHalState }),
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
virtualHalSimConfigPromotionCandidates,
|
||||
virtualHalPromotionCandidateSummary: {
|
||||
apiName: "real-browser-simulation-promotion-candidate-summary",
|
||||
summaryVersion: 1,
|
||||
phase: "ready",
|
||||
ready: true,
|
||||
candidateCount: 8,
|
||||
readyCandidateCount: 8,
|
||||
familyCount: 3,
|
||||
sourceFileCount: 17,
|
||||
preferredCandidateId: "qtdragon-multi-joint-on-abort",
|
||||
preferredIniPath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
|
||||
preferredGcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
||||
blockedCandidateIds: [],
|
||||
explicitBrowserDiagnosticsCount: 8,
|
||||
inventoryBaseline: "executed=28 passed=28 skipped=131 unexpected_fail=0",
|
||||
},
|
||||
virtualHalSimConfigMacroLoadFixtures,
|
||||
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport({ halState: virtualHalState }),
|
||||
virtualHalMotionControllerMatrix,
|
||||
@@ -67,6 +83,8 @@ assert.equal(createProjectReleaseBrowserDiagnosticsArtifactValidation(browserDia
|
||||
const readyArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])),
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
virtualHalSimConfigPromotionCandidates,
|
||||
virtualHalSimConfigMacroLoadFixtures,
|
||||
virtualHalMotionControllerMatrix: browserDiagnosticsArtifact.virtualHalMotionControllerMatrix,
|
||||
}));
|
||||
|
||||
@@ -92,6 +110,13 @@ const readyWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
});
|
||||
const readySummary = createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(readyWorkflow);
|
||||
const readyActionPlan = createProjectReleaseReadinessArtifactUrlWorkflowActionPlan(readyWorkflow);
|
||||
const expectedPromotionFamilySummary =
|
||||
"qtdragon-on-abort: 6/6 ready; vismach-remap-sims: 1/1 ready; rose-engine-rcone-demo: 1/1 ready";
|
||||
const expectedPromotionFamilyRows = [
|
||||
["virtual-hal-promotion-family-qtdragon-on-abort", "6/6 ready; sources=12; diagnostics=ready"],
|
||||
["virtual-hal-promotion-family-vismach-remap-sims", "1/1 ready; sources=3; diagnostics=ready"],
|
||||
["virtual-hal-promotion-family-rose-engine-rcone-demo", "1/1 ready; sources=2; diagnostics=ready"],
|
||||
];
|
||||
|
||||
assert.equal(fetchedUrl, "/real-browser-simulation-diagnostics.json");
|
||||
assert.equal(readyWorkflow.apiName, "project-release-readiness-artifact-url-workflow");
|
||||
@@ -103,14 +128,50 @@ assert.equal(readyWorkflow.fetchError, null);
|
||||
assert.equal(readyWorkflow.jsonWorkflow.ready, true);
|
||||
assert.equal(readyWorkflow.validation.ready, true);
|
||||
assert.equal(readyWorkflow.diagnosticsValidation.ready, true);
|
||||
assert.equal(readyWorkflow.diagnosticsArtifact.virtualHalPromotionCandidateSummary.ready, true);
|
||||
assert.equal(readyWorkflow.diagnosticsValidation.hasPromotionCandidates, true);
|
||||
assert.equal(readyWorkflow.diagnosticsValidation.promotionCandidatesReady, true);
|
||||
assert.equal(readyWorkflow.validation.virtualHalPromotionFamilySummary, expectedPromotionFamilySummary);
|
||||
assert.equal(readyWorkflow.diagnosticsValidation.hasMacroLoadFixtures, true);
|
||||
assert.equal(readyWorkflow.diagnosticsValidation.macroLoadFixturesReady, true);
|
||||
assert.equal(readyWorkflow.diagnosticsValidation.hasSessionDiagnostics, true);
|
||||
assert.equal(readyWorkflow.diagnosticsValidation.sessionDiagnosticsReady, true);
|
||||
assert.equal(readyWorkflow.diagnosticsFetched, true);
|
||||
assert.equal(readyWorkflow.diagnosticsHttpStatus, 200);
|
||||
assert.equal(
|
||||
readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-families")?.value,
|
||||
expectedPromotionFamilySummary,
|
||||
);
|
||||
assert.equal(
|
||||
readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-summary")?.value,
|
||||
"ready",
|
||||
);
|
||||
assert.equal(
|
||||
readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate")?.value,
|
||||
"qtdragon-multi-joint-on-abort",
|
||||
);
|
||||
assert.equal(
|
||||
readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-ini")?.value,
|
||||
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
|
||||
);
|
||||
assert.equal(
|
||||
readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-gcode")?.value,
|
||||
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
||||
);
|
||||
assert.equal(
|
||||
readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-count")?.value,
|
||||
"8/8 ready",
|
||||
);
|
||||
assert.equal(
|
||||
readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value,
|
||||
"executed=28 passed=28 skipped=131 unexpected_fail=0",
|
||||
);
|
||||
assert.deepEqual(
|
||||
readySummary.rows
|
||||
.filter(({ id }) => id.startsWith("virtual-hal-promotion-family-"))
|
||||
.map(({ id, value }) => [id, value]),
|
||||
expectedPromotionFamilyRows,
|
||||
);
|
||||
assert.equal(readyWorkflow.actionPlan.ready, true);
|
||||
assert.equal(readySummary.apiName, "project-release-readiness-artifact-url-workflow-summary-view-model");
|
||||
assert.equal(readySummary.statusLine, "Ready: Artifact URL workflow complete");
|
||||
@@ -121,7 +182,15 @@ assert.deepEqual(
|
||||
["fetch", "ready (200)"],
|
||||
["json-workflow", "ready"],
|
||||
["artifact-validation", "ready"],
|
||||
["virtual-hal-promotion-families", expectedPromotionFamilySummary],
|
||||
...expectedPromotionFamilyRows,
|
||||
["browser-diagnostics", "ready"],
|
||||
["virtual-hal-promotion-candidate-summary", "ready"],
|
||||
["virtual-hal-promotion-candidate", "qtdragon-multi-joint-on-abort"],
|
||||
["virtual-hal-promotion-candidate-ini", "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini"],
|
||||
["virtual-hal-promotion-candidate-gcode", "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc"],
|
||||
["virtual-hal-promotion-candidate-count", "8/8 ready"],
|
||||
["virtual-hal-promotion-candidate-baseline", "executed=28 passed=28 skipped=131 unexpected_fail=0"],
|
||||
["saved-session-diagnostics", "ready"],
|
||||
["next-command", "none"],
|
||||
["missing", "none"],
|
||||
@@ -143,6 +212,33 @@ assert.deepEqual(readyActionPlan, {
|
||||
rows: [],
|
||||
});
|
||||
|
||||
const missingPromotionCandidateSummaryArtifact = { ...browserDiagnosticsArtifact };
|
||||
delete missingPromotionCandidateSummaryArtifact.virtualHalPromotionCandidateSummary;
|
||||
const missingPromotionCandidateSummaryWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
artifactUrl: "/project-release-readiness.json",
|
||||
diagnosticsUrl: "/real-browser-simulation-missing-promotion-candidate-summary.json",
|
||||
fetchRef: async (url) => ({
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: async () => url === "/real-browser-simulation-missing-promotion-candidate-summary.json"
|
||||
? JSON.stringify(missingPromotionCandidateSummaryArtifact)
|
||||
: readyArtifactJson,
|
||||
}),
|
||||
});
|
||||
assert.equal(missingPromotionCandidateSummaryWorkflow.ready, false);
|
||||
assert.equal(missingPromotionCandidateSummaryWorkflow.diagnosticsValidation.ready, false);
|
||||
assert.equal(missingPromotionCandidateSummaryWorkflow.diagnosticsValidation.hasPromotionCandidateSummary, false);
|
||||
assert.equal(missingPromotionCandidateSummaryWorkflow.diagnosticsValidation.promotionCandidateSummaryReady, false);
|
||||
assert.equal(
|
||||
missingPromotionCandidateSummaryWorkflow.missing.includes("diagnostics.virtualHalPromotionCandidateSummary"),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(missingPromotionCandidateSummaryWorkflow)
|
||||
.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-summary")?.value,
|
||||
"not provided",
|
||||
);
|
||||
|
||||
const liveOnlyDiagnosticsArtifact = { ...browserDiagnosticsSessionNestedArtifact };
|
||||
const liveOnlyDiagnosticsWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
artifactUrl: "/project-release-readiness.json",
|
||||
|
||||
@@ -3,6 +3,8 @@ import assert from "node:assert/strict";
|
||||
import {
|
||||
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
|
||||
createVirtualHalMotionControllerMatrixReport,
|
||||
createVirtualHalSimConfigMacroLoadFixtureReport,
|
||||
createVirtualHalSimConfigPromotionCandidateReport,
|
||||
createVirtualHalSimConfigSourceCoverageReport,
|
||||
createProjectReleaseGateActionPlan,
|
||||
createProjectReleaseGateExecutionManifest,
|
||||
@@ -24,6 +26,11 @@ const virtualHalSimConfigSourceCoverage = createVirtualHalSimConfigSourceCoverag
|
||||
const virtualHalMotionControllerMatrix = createVirtualHalMotionControllerMatrixReport({
|
||||
manifestEntries: VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS.flatMap((target) => target.sourceFiles ?? []),
|
||||
});
|
||||
const virtualHalSimConfigPromotionCandidates = createVirtualHalSimConfigPromotionCandidateReport({
|
||||
simConfigSourceCoverage: virtualHalSimConfigSourceCoverage,
|
||||
motionControllerMatrix: virtualHalMotionControllerMatrix,
|
||||
});
|
||||
const virtualHalSimConfigMacroLoadFixtures = createVirtualHalSimConfigMacroLoadFixtureReport();
|
||||
|
||||
assert.equal(manifest.apiName, "project-release-gate-manifest");
|
||||
assert.equal(manifest.manifestVersion, 1);
|
||||
@@ -209,6 +216,8 @@ assert.match(partialActionPlan.shellScript, /wasm-port\/tests\/host\/verify_proj
|
||||
const report = createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])),
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
virtualHalSimConfigPromotionCandidates,
|
||||
virtualHalSimConfigMacroLoadFixtures,
|
||||
virtualHalMotionControllerMatrix,
|
||||
});
|
||||
assert.deepEqual(report.gateManifest, manifest);
|
||||
@@ -257,6 +266,16 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
|
||||
label: "Virtual HAL sim-config source coverage",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-promotion-candidates",
|
||||
label: "Virtual HAL sim-config promotion candidates",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-macro-load-fixtures",
|
||||
label: "Virtual HAL sim-config macro/load fixtures",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-motion-controller-matrix",
|
||||
label: "Virtual HAL motion controller matrix",
|
||||
@@ -281,12 +300,14 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
|
||||
});
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport()).statusLine,
|
||||
"Waiting: project-release-gate, virtual-hal-sim-config-source-coverage, virtual-hal-motion-controller-matrix",
|
||||
"Waiting: project-release-gate, virtual-hal-sim-config-source-coverage, virtual-hal-sim-config-promotion-candidates, virtual-hal-sim-config-macro-load-fixtures, virtual-hal-motion-controller-matrix",
|
||||
);
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])),
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
virtualHalSimConfigPromotionCandidates,
|
||||
virtualHalSimConfigMacroLoadFixtures,
|
||||
virtualHalMotionControllerMatrix,
|
||||
promotedRuntimeFamilies: ["L4-TOOL-DB"],
|
||||
})).rows.find(({ id }) => id === "blocked-runtime-families")?.value,
|
||||
@@ -313,6 +334,13 @@ const artifactUrlWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow
|
||||
const artifactUrlWorkflowSummary = createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
|
||||
artifactUrlWorkflow,
|
||||
);
|
||||
const expectedPromotionFamilySummary =
|
||||
"qtdragon-on-abort: 6/6 ready; vismach-remap-sims: 1/1 ready; rose-engine-rcone-demo: 1/1 ready";
|
||||
const expectedPromotionFamilyRows = [
|
||||
["virtual-hal-promotion-family-qtdragon-on-abort", "6/6 ready; sources=12; diagnostics=ready"],
|
||||
["virtual-hal-promotion-family-vismach-remap-sims", "1/1 ready; sources=3; diagnostics=ready"],
|
||||
["virtual-hal-promotion-family-rose-engine-rcone-demo", "1/1 ready; sources=2; diagnostics=ready"],
|
||||
];
|
||||
assert.equal(
|
||||
artifactValidationSummary.apiName,
|
||||
"project-release-readiness-artifact-validation-summary-view-model",
|
||||
@@ -330,6 +358,10 @@ assert.deepEqual(
|
||||
["gate-result-matrix", "ready"],
|
||||
["gate-action-plan", "ready"],
|
||||
["virtual-hal-sim-config-source-coverage", "ready"],
|
||||
["virtual-hal-sim-config-promotion-candidates", "ready"],
|
||||
["virtual-hal-promotion-families", expectedPromotionFamilySummary],
|
||||
...expectedPromotionFamilyRows,
|
||||
["virtual-hal-sim-config-macro-load-fixtures", "ready"],
|
||||
["virtual-hal-motion-controller-matrix", "ready"],
|
||||
["missing", "none"],
|
||||
],
|
||||
@@ -417,7 +449,15 @@ assert.deepEqual(
|
||||
["fetch", "ready (200)"],
|
||||
["json-workflow", "ready"],
|
||||
["artifact-validation", "ready"],
|
||||
["virtual-hal-promotion-families", expectedPromotionFamilySummary],
|
||||
...expectedPromotionFamilyRows,
|
||||
["browser-diagnostics", "not requested"],
|
||||
["virtual-hal-promotion-candidate-summary", "not requested"],
|
||||
["virtual-hal-promotion-candidate", "not provided"],
|
||||
["virtual-hal-promotion-candidate-ini", "not provided"],
|
||||
["virtual-hal-promotion-candidate-gcode", "not provided"],
|
||||
["virtual-hal-promotion-candidate-count", "not provided"],
|
||||
["virtual-hal-promotion-candidate-baseline", "not provided"],
|
||||
["saved-session-diagnostics", "not requested"],
|
||||
["next-command", "none"],
|
||||
["missing", "none"],
|
||||
|
||||
@@ -36,8 +36,11 @@ import {
|
||||
VIRTUAL_HAL_COVERAGE_STATES,
|
||||
VIRTUAL_HAL_MOTION_CONTROLLER_MATRIX_FIXTURES,
|
||||
VIRTUAL_HAL_PROJECT_PIN_GROUPS,
|
||||
VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_AUDIT_CANDIDATES,
|
||||
VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES,
|
||||
VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES,
|
||||
VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES,
|
||||
VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE,
|
||||
VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES,
|
||||
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
|
||||
VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES,
|
||||
@@ -178,6 +181,16 @@ const sdkIndexText = readFileSync(resolve(root, "runtime/sdk/src/index.js"), "ut
|
||||
const sdkReadmeText = readFileSync(resolve(root, "runtime/sdk/README.md"), "utf8");
|
||||
const sourceManifestText = readFileSync(resolve(root, "tools/source-manifest.txt"), "utf8");
|
||||
const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"), "utf8");
|
||||
const expectedPromotionFamilySummary =
|
||||
"qtdragon-on-abort: 6/6 ready; vismach-remap-sims: 1/1 ready; rose-engine-rcone-demo: 1/1 ready";
|
||||
const expectedPromotionFamilyRows = [
|
||||
["virtual-hal-promotion-family-qtdragon-on-abort", "6/6 ready; sources=12; diagnostics=ready"],
|
||||
["virtual-hal-promotion-family-vismach-remap-sims", "1/1 ready; sources=3; diagnostics=ready"],
|
||||
["virtual-hal-promotion-family-rose-engine-rcone-demo", "1/1 ready; sources=2; diagnostics=ready"],
|
||||
];
|
||||
const hasFreshBoundaryArtifactHashes = (boundaryEvidence) =>
|
||||
Object.entries(VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES)
|
||||
.every(([artifactPath, expectedHash]) => boundaryEvidence?.sourceArtifactHashes?.[artifactPath] === expectedHash);
|
||||
|
||||
const requiredExports = [
|
||||
["createLinuxCncIniSdk", createLinuxCncIniSdk],
|
||||
@@ -551,6 +564,10 @@ assert.match(sdkIndexText, /\bVIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES\b/);
|
||||
assert.match(sdkReadmeText, /\bVIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES\b/);
|
||||
assert.match(sdkIndexText, /\bVIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES\b/);
|
||||
assert.match(sdkReadmeText, /\bVIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES\b/);
|
||||
assert.match(sdkIndexText, /\bVIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES\b/);
|
||||
assert.match(sdkReadmeText, /\bVIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES\b/);
|
||||
assert.match(sdkIndexText, /\bVIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE\b/);
|
||||
assert.match(sdkReadmeText, /\bVIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE\b/);
|
||||
assert.match(sdkIndexText, /\bcreateVirtualHalSimConfigMacroLoadFixtureReport\b/);
|
||||
assert.match(sdkReadmeText, /\bcreateVirtualHalSimConfigMacroLoadFixtureReport\b/);
|
||||
assert.match(sdkIndexText, /\bcreateVirtualHalSourceComplianceReport\b/);
|
||||
@@ -784,6 +801,39 @@ assert.equal(simConfigPromotionCandidateReport.missingCandidates.length, 0);
|
||||
assert.equal(simConfigPromotionCandidateReport.blockedCandidateIds.length, 0);
|
||||
assert.equal(simConfigPromotionCandidateReport.missingManifestFiles.length, 0);
|
||||
assert.equal(simConfigPromotionCandidateReport.missingRequiredReports.length, 0);
|
||||
assert.equal(
|
||||
simConfigPromotionCandidateReport.familyRows.some((row) =>
|
||||
row.id === "qtdragon-on-abort" &&
|
||||
row.candidateCount === 6 &&
|
||||
row.completeCount === 6 &&
|
||||
row.explicitBrowserDiagnosticsCount === 6 &&
|
||||
row.explicitBrowserDiagnosticsReady === true &&
|
||||
row.complete === true &&
|
||||
row.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini") &&
|
||||
row.sourceFiles.includes("linuxcnc/configs/sim/qtvcp_screens/qtdragon/on_abort.ngc")
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
simConfigPromotionCandidateReport.familyRows.some((row) =>
|
||||
row.id === "vismach-remap-sims" &&
|
||||
row.candidateCount === 1 &&
|
||||
row.complete === true &&
|
||||
row.explicitBrowserDiagnosticsReady === true &&
|
||||
row.sourceFiles.includes("linuxcnc/configs/sim/axis/vismach/puma/puma_seam_weld.ngc")
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
simConfigPromotionCandidateReport.familyRows.some((row) =>
|
||||
row.id === "rose-engine-rcone-demo" &&
|
||||
row.candidateCount === 1 &&
|
||||
row.complete === true &&
|
||||
row.explicitBrowserDiagnosticsReady === true &&
|
||||
row.sourceFiles.includes("linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc")
|
||||
),
|
||||
true,
|
||||
);
|
||||
const expectedPromotionCandidates = [
|
||||
[
|
||||
"qtdragon-multi-joint-on-abort",
|
||||
@@ -833,6 +883,7 @@ for (const [id, ...sourceFiles] of expectedPromotionCandidates) {
|
||||
row.id === id &&
|
||||
row.complete === true &&
|
||||
row.currentNodeInventoryStatus === "PASS" &&
|
||||
row.currentMatrixBrowserStatus === "explicit-browser-diagnostics" &&
|
||||
row.targetBrowserEvidence === "explicit-browser-diagnostics" &&
|
||||
sourceFiles.every((sourceFile) => row.sourceFiles.includes(sourceFile))
|
||||
),
|
||||
@@ -847,6 +898,19 @@ assert.equal(virtualHalRuntime.getSimConfigPromotionCandidateReport({
|
||||
}).complete, true);
|
||||
assert.equal(VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES.length >= 2, true);
|
||||
assert.equal(VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES.length >= 2, true);
|
||||
assert.equal(VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_AUDIT_CANDIDATES.length >= 2, true);
|
||||
assert.deepEqual(VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE, {
|
||||
executed: 28,
|
||||
passed: 28,
|
||||
skipped: 131,
|
||||
unexpectedFail: 0,
|
||||
});
|
||||
assert.deepEqual(VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES, {
|
||||
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv":
|
||||
"de6cf57b7c07182e3bcb32e22dbdf202b618cabc14620d6dff1ef815587950b9",
|
||||
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv":
|
||||
"b0afe27224e97a82fbecbbd75a7c86233c98fe957d9c1ba20f0656f8745a5eae",
|
||||
});
|
||||
const simConfigMacroLoadFixtureReport = createVirtualHalSimConfigMacroLoadFixtureReport({
|
||||
manifestText: sourceManifestText,
|
||||
});
|
||||
@@ -862,7 +926,13 @@ assert.equal(simConfigMacroLoadFixtureReport.missingFixtures.length, 0);
|
||||
assert.equal(simConfigMacroLoadFixtureReport.blockedFixtureIds.length, 0);
|
||||
assert.equal(simConfigMacroLoadFixtureReport.standaloneMainViolations.length, 0);
|
||||
assert.equal(simConfigMacroLoadFixtureReport.blockedFixturePromotionViolations.length, 0);
|
||||
assert.equal(simConfigMacroLoadFixtureReport.blockedBoundaryEvidenceViolations.length, 0);
|
||||
assert.equal(simConfigMacroLoadFixtureReport.missingAuditRows.length, 0);
|
||||
assert.equal(simConfigMacroLoadFixtureReport.auditPromotionViolations.length, 0);
|
||||
assert.equal(simConfigMacroLoadFixtureReport.auditBoundaryEvidenceViolations.length, 0);
|
||||
assert.equal(simConfigMacroLoadFixtureReport.auditPromotionAllowedViolations.length, 0);
|
||||
assert.equal(simConfigMacroLoadFixtureReport.missingManifestFiles.length, 0);
|
||||
assert.equal(simConfigMacroLoadFixtureReport.declarationEvidenceViolations.length, 0);
|
||||
assert.equal(
|
||||
simConfigMacroLoadFixtureReport.rows.some((row) =>
|
||||
row.id === "rose-engine-rcone-macro-load" &&
|
||||
@@ -870,6 +940,12 @@ assert.equal(
|
||||
row.nonMainFixture === true &&
|
||||
row.targetBrowserEvidence === "non-main-fixture-diagnostics" &&
|
||||
row.fixturePath === "linuxcnc/configs/sim/axis/rose_engine/rcone.ngc" &&
|
||||
row.requiresDeclarationEvidence === true &&
|
||||
row.declarationEvidenceReady === true &&
|
||||
row.declarationEvidence.sourceFile === "linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini" &&
|
||||
row.declarationEvidence.key === "NGCGUI_SUBFILE" &&
|
||||
row.declarationEvidence.value === "rcone.ngc" &&
|
||||
row.declarationEvidence.line === 34 &&
|
||||
row.sourceFiles.includes("linuxcnc/configs/sim/axis/rose_engine/rcone.ngc") &&
|
||||
row.sourceFiles.includes("linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc")
|
||||
),
|
||||
@@ -881,18 +957,50 @@ assert.equal(
|
||||
row.complete === true &&
|
||||
row.nonMainFixture === true &&
|
||||
row.declaredOnly === true &&
|
||||
row.declarationEvidenceReady === true &&
|
||||
row.declarationEvidence.sourceFile === "linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini" &&
|
||||
row.declarationEvidence.key === "NGCGUI_SUBFILE" &&
|
||||
row.declarationEvidence.value === "queuebuster.ngc" &&
|
||||
row.declarationEvidence.line === 40 &&
|
||||
row.fixturePath === "linuxcnc/configs/sim/axis/external_offsets/queuebuster.ngc" &&
|
||||
row.sourceFiles.includes("linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini") &&
|
||||
row.sourceFiles.includes("linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc")
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(createVirtualHalSimConfigMacroLoadFixtureReport({
|
||||
manifestText: sourceManifestText,
|
||||
fixtures: [
|
||||
{
|
||||
...VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES.find(({ id }) => id === "external-offsets-queuebuster-macro-load"),
|
||||
declarationEvidence: null,
|
||||
},
|
||||
],
|
||||
}).declarationEvidenceViolations.includes("external-offsets-queuebuster-macro-load"), true);
|
||||
assert.equal(createVirtualHalSimConfigMacroLoadFixtureReport({
|
||||
manifestText: sourceManifestText,
|
||||
fixtures: [
|
||||
{
|
||||
...VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES.find(({ id }) => id === "rose-engine-rcone-macro-load"),
|
||||
declarationEvidence: null,
|
||||
},
|
||||
],
|
||||
}).declarationEvidenceViolations.includes("rose-engine-rcone-macro-load"), true);
|
||||
assert.equal(
|
||||
simConfigMacroLoadFixtureReport.blockedRows.some((row) =>
|
||||
row.id === "silverdragon-tool-sensor-python-ui-boundary" &&
|
||||
row.fixturePath === "linuxcnc/configs/sim/gscreen/silverdragon/macros/tool_sensor.ngc" &&
|
||||
row.blockedKind === "PYTHON-UI-PROCESS" &&
|
||||
row.excludedFromPositiveFixtures === true &&
|
||||
row.boundaryEvidenceReady === true &&
|
||||
hasFreshBoundaryArtifactHashes(row.boundaryEvidence) &&
|
||||
row.boundaryEvidence.boundarySummary.dependencies.includes("missing_vendored_ini:configs/sim/gscreen/silverdragon/silverdragon.ini") &&
|
||||
row.boundaryEvidence.baselineSummary.executed === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.executed &&
|
||||
row.boundaryEvidence.baselineSummary.passed === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.passed &&
|
||||
row.boundaryEvidence.baselineSummary.skipped === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.skipped &&
|
||||
row.boundaryEvidence.baselineSummary.unexpectedFail === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.unexpectedFail &&
|
||||
row.boundaryEvidence.iniBoundarySummary.vendored === 0 &&
|
||||
row.boundaryEvidence.iniBoundarySummary.reportAvailable === 0 &&
|
||||
row.complete === true
|
||||
),
|
||||
true,
|
||||
@@ -903,10 +1011,170 @@ assert.equal(
|
||||
row.fixturePath === "linuxcnc/configs/sim/gmoccapy/macros/on_abort.ngc" &&
|
||||
row.blockedKind === "L4-PYTHON-REMAP" &&
|
||||
row.excludedFromPositiveFixtures === true &&
|
||||
row.boundaryEvidenceReady === true &&
|
||||
hasFreshBoundaryArtifactHashes(row.boundaryEvidence) &&
|
||||
row.boundaryEvidence.boundarySummary.dependencies.includes("missing_vendored_ini:configs/sim/gmoccapy/6_axis.ini") &&
|
||||
row.boundaryEvidence.baselineSummary.executed === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.executed &&
|
||||
row.boundaryEvidence.baselineSummary.passed === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.passed &&
|
||||
row.boundaryEvidence.baselineSummary.skipped === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.skipped &&
|
||||
row.boundaryEvidence.baselineSummary.unexpectedFail === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.unexpectedFail &&
|
||||
row.boundaryEvidence.iniBoundarySummary.vendored === 0 &&
|
||||
row.boundaryEvidence.iniBoundarySummary.reportAvailable === 0 &&
|
||||
row.complete === true
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
simConfigMacroLoadFixtureReport.auditRows.some((row) =>
|
||||
row.id === "gscreen-industrial-lathe-wear-toolchange-audit" &&
|
||||
row.fixturePath === "linuxcnc/configs/sim/gscreen/industrial_lathe_wear/toolchange.ngc" &&
|
||||
row.auditStatus === "blocked-missing-owning-ini" &&
|
||||
row.promotionAllowed === false &&
|
||||
row.excludedFromPositiveFixtures === true &&
|
||||
row.boundaryEvidenceReady === true &&
|
||||
hasFreshBoundaryArtifactHashes(row.boundaryEvidence) &&
|
||||
row.boundaryEvidence.boundarySummary.recommendedBlocked === "UNAVAILABLE" &&
|
||||
row.boundaryEvidence.boundarySummary.dependencies.includes("missing_vendored_ini:configs/sim/gscreen/industrial_lathe_wear/industrial_lathe_wear.ini") &&
|
||||
row.boundaryEvidence.baselineSummary.executed === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.executed &&
|
||||
row.boundaryEvidence.baselineSummary.passed === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.passed &&
|
||||
row.boundaryEvidence.baselineSummary.skipped === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.skipped &&
|
||||
row.boundaryEvidence.baselineSummary.unexpectedFail === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.unexpectedFail &&
|
||||
row.boundaryEvidence.iniBoundarySummary.vendored === 0 &&
|
||||
row.boundaryEvidence.iniBoundarySummary.reportAvailable === 0 &&
|
||||
row.complete === true
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
simConfigMacroLoadFixtureReport.auditRows.some((row) =>
|
||||
row.id === "qtvcp-industrial-lathe-wear-toolchange-audit" &&
|
||||
row.fixturePath === "linuxcnc/configs/sim/qtvcp_screens/industrial_lathe_wear/toolchange.ngc" &&
|
||||
row.auditStatus === "blocked-missing-owning-ini" &&
|
||||
row.promotionAllowed === false &&
|
||||
row.excludedFromPositiveFixtures === true &&
|
||||
row.boundaryEvidenceReady === true &&
|
||||
hasFreshBoundaryArtifactHashes(row.boundaryEvidence) &&
|
||||
row.boundaryEvidence.boundarySummary.recommendedBlocked === "UNAVAILABLE" &&
|
||||
row.boundaryEvidence.boundarySummary.dependencies.includes("missing_vendored_ini:configs/sim/qtvcp_screens/industrial_lathe_wear/industrial_lathe_wear.ini") &&
|
||||
row.boundaryEvidence.baselineSummary.executed === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.executed &&
|
||||
row.boundaryEvidence.baselineSummary.passed === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.passed &&
|
||||
row.boundaryEvidence.baselineSummary.skipped === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.skipped &&
|
||||
row.boundaryEvidence.baselineSummary.unexpectedFail === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.unexpectedFail &&
|
||||
row.boundaryEvidence.iniBoundarySummary.vendored === 0 &&
|
||||
row.boundaryEvidence.iniBoundarySummary.reportAvailable === 0 &&
|
||||
row.complete === true
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(createVirtualHalSimConfigMacroLoadFixtureReport({
|
||||
manifestText: sourceManifestText,
|
||||
auditCandidates: [],
|
||||
}).complete, true);
|
||||
assert.equal(createVirtualHalSimConfigMacroLoadFixtureReport({
|
||||
manifestText: sourceManifestText,
|
||||
blockedFixtures: [
|
||||
{
|
||||
id: "bad-blocked-boundary-evidence",
|
||||
fixturePath: "linuxcnc/configs/sim/gscreen/silverdragon/macros/tool_sensor.ngc",
|
||||
owningIniPath: "linuxcnc/configs/sim/gscreen/silverdragon/silverdragon.ini",
|
||||
blockedKind: "PYTHON-UI-PROCESS",
|
||||
dependencyClass: "macro-load-python-ui-boundary",
|
||||
reason: "negative fixture: blocked row missing generated boundary evidence must fail",
|
||||
},
|
||||
],
|
||||
}).blockedBoundaryEvidenceViolations.includes("bad-blocked-boundary-evidence"), true);
|
||||
assert.equal(createVirtualHalSimConfigMacroLoadFixtureReport({
|
||||
manifestText: sourceManifestText,
|
||||
blockedFixtures: [
|
||||
{
|
||||
...VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES.find(
|
||||
({ id }) => id === "silverdragon-tool-sensor-python-ui-boundary",
|
||||
),
|
||||
id: "stale-blocked-boundary-evidence",
|
||||
boundaryEvidence: {
|
||||
...VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES.find(
|
||||
({ id }) => id === "silverdragon-tool-sensor-python-ui-boundary",
|
||||
).boundaryEvidence,
|
||||
baselineSummary: {
|
||||
...VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE,
|
||||
skipped: 130,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}).blockedBoundaryEvidenceViolations.includes("stale-blocked-boundary-evidence"), true);
|
||||
assert.equal(createVirtualHalSimConfigMacroLoadFixtureReport({
|
||||
manifestText: sourceManifestText,
|
||||
blockedFixtures: [
|
||||
{
|
||||
...VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES.find(
|
||||
({ id }) => id === "silverdragon-tool-sensor-python-ui-boundary",
|
||||
),
|
||||
id: "stale-hash-blocked-boundary-evidence",
|
||||
boundaryEvidence: {
|
||||
...VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES.find(
|
||||
({ id }) => id === "silverdragon-tool-sensor-python-ui-boundary",
|
||||
).boundaryEvidence,
|
||||
sourceArtifactHashes: {
|
||||
...VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES,
|
||||
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv": "stale",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}).blockedBoundaryEvidenceViolations.includes("stale-hash-blocked-boundary-evidence"), true);
|
||||
assert.equal(createVirtualHalSimConfigMacroLoadFixtureReport({
|
||||
manifestText: sourceManifestText,
|
||||
fixtures: [
|
||||
...VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES,
|
||||
{
|
||||
id: "bad-positive-industrial-lathe-audit",
|
||||
label: "Bad positive industrial lathe audit fixture",
|
||||
fixturePath: "linuxcnc/configs/sim/gscreen/industrial_lathe_wear/toolchange.ngc",
|
||||
sourceFiles: [
|
||||
"linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
|
||||
],
|
||||
owningIniPath: "linuxcnc/configs/sim/gscreen/industrial_lathe_wear/industrial_lathe_wear.ini",
|
||||
pairedMainProgram: "linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
|
||||
declarationSource: "NGCGUI_SUBFILE",
|
||||
currentNodeInventoryStatus: "PASS",
|
||||
dependencyClass: "macro-load-ui-family-audit",
|
||||
targetBrowserEvidence: "non-main-fixture-diagnostics",
|
||||
blockedKind: "-",
|
||||
nonMainFixture: true,
|
||||
declaredOnly: false,
|
||||
evidence: "negative fixture: audit candidate must not be promoted as a positive fixture",
|
||||
},
|
||||
],
|
||||
}).auditPromotionViolations.includes("gscreen-industrial-lathe-wear-toolchange-audit"), true);
|
||||
assert.equal(createVirtualHalSimConfigMacroLoadFixtureReport({
|
||||
manifestText: sourceManifestText,
|
||||
auditCandidates: [
|
||||
{
|
||||
id: "bad-audit-boundary-evidence",
|
||||
fixturePath: "linuxcnc/configs/sim/gscreen/industrial_lathe_wear/toolchange.ngc",
|
||||
owningIniPath: "linuxcnc/configs/sim/gscreen/industrial_lathe_wear/industrial_lathe_wear.ini",
|
||||
dependencyClass: "macro-load-ui-family-audit",
|
||||
auditStatus: "blocked-missing-owning-ini",
|
||||
promotionAllowed: false,
|
||||
reason: "negative fixture: missing generated boundary evidence must fail",
|
||||
},
|
||||
],
|
||||
}).auditBoundaryEvidenceViolations.includes("bad-audit-boundary-evidence"), true);
|
||||
assert.equal(createVirtualHalSimConfigMacroLoadFixtureReport({
|
||||
manifestText: sourceManifestText,
|
||||
auditCandidates: [
|
||||
{
|
||||
id: "bad-audit-promotion-allowed",
|
||||
fixturePath: "linuxcnc/configs/sim/qtvcp_screens/industrial_lathe_wear/toolchange.ngc",
|
||||
owningIniPath: "linuxcnc/configs/sim/qtvcp_screens/industrial_lathe_wear/industrial_lathe_wear.ini",
|
||||
dependencyClass: "macro-load-ui-family-audit",
|
||||
auditStatus: "blocked-missing-owning-ini",
|
||||
promotionAllowed: true,
|
||||
reason: "negative fixture: promotionAllowed true without boundary/source proof must fail",
|
||||
},
|
||||
],
|
||||
}).auditPromotionAllowedViolations.includes("bad-audit-promotion-allowed"), true);
|
||||
assert.equal(virtualHalRuntime.getSimConfigMacroLoadFixtureReport({
|
||||
manifestText: sourceManifestText,
|
||||
}).complete, true);
|
||||
@@ -958,6 +1226,22 @@ const browserDiagnosticsArtifact = {
|
||||
virtualHalSourceCompliance: sourceComplianceReport,
|
||||
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
|
||||
virtualHalSimConfigPromotionCandidates: simConfigPromotionCandidateReport,
|
||||
virtualHalPromotionCandidateSummary: {
|
||||
apiName: "real-browser-simulation-promotion-candidate-summary",
|
||||
summaryVersion: 1,
|
||||
phase: "ready",
|
||||
ready: true,
|
||||
candidateCount: 8,
|
||||
readyCandidateCount: 8,
|
||||
familyCount: 3,
|
||||
sourceFileCount: 17,
|
||||
preferredCandidateId: "qtdragon-multi-joint-on-abort",
|
||||
preferredIniPath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
|
||||
preferredGcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
||||
blockedCandidateIds: [],
|
||||
explicitBrowserDiagnosticsCount: 8,
|
||||
inventoryBaseline: "executed=28 passed=28 skipped=131 unexpected_fail=0",
|
||||
},
|
||||
virtualHalSimConfigMacroLoadFixtures: simConfigMacroLoadFixtureReport,
|
||||
virtualHalCommandScriptFixtures: commandScriptFixtureReport,
|
||||
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
|
||||
@@ -984,6 +1268,8 @@ assert.equal(browserDiagnosticsArtifactValidation.sourceComplianceReady, true);
|
||||
assert.equal(browserDiagnosticsArtifactValidation.simConfigSourceCoverageReady, true);
|
||||
assert.equal(browserDiagnosticsArtifactValidation.promotionCandidatesReady, true);
|
||||
assert.equal(browserDiagnosticsArtifactValidation.hasPromotionCandidates, true);
|
||||
assert.equal(browserDiagnosticsArtifactValidation.promotionCandidateSummaryReady, true);
|
||||
assert.equal(browserDiagnosticsArtifactValidation.hasPromotionCandidateSummary, true);
|
||||
assert.equal(browserDiagnosticsArtifactValidation.macroLoadFixturesReady, true);
|
||||
assert.equal(browserDiagnosticsArtifactValidation.hasMacroLoadFixtures, true);
|
||||
assert.equal(browserDiagnosticsArtifactValidation.commandScriptFixturesReady, true);
|
||||
@@ -1004,6 +1290,38 @@ assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc
|
||||
assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/configs/sim/axis/rose_engine/rcone.ngc"), true);
|
||||
assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini"), true);
|
||||
assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc"), true);
|
||||
assert.equal(createProjectReleaseBrowserDiagnosticsArtifactValidation({
|
||||
...browserDiagnosticsArtifact,
|
||||
virtualHalPromotionCandidateSummary: undefined,
|
||||
}).missing.includes("virtualHalPromotionCandidateSummary"), true);
|
||||
assert.equal(createProjectReleaseBrowserDiagnosticsArtifactValidation({
|
||||
...browserDiagnosticsArtifact,
|
||||
virtualHalPromotionCandidateSummary: {
|
||||
...browserDiagnosticsArtifact.virtualHalPromotionCandidateSummary,
|
||||
readyCandidateCount: 7,
|
||||
},
|
||||
}).promotionCandidateSummaryReady, false);
|
||||
assert.equal(createProjectReleaseBrowserDiagnosticsArtifactValidation({
|
||||
...browserDiagnosticsArtifact,
|
||||
virtualHalSimConfigMacroLoadFixtures: {
|
||||
...simConfigMacroLoadFixtureReport,
|
||||
auditRows: [],
|
||||
},
|
||||
}).macroLoadFixturesReady, false);
|
||||
assert.equal(createProjectReleaseBrowserDiagnosticsArtifactValidation({
|
||||
...browserDiagnosticsArtifact,
|
||||
virtualHalSimConfigMacroLoadFixtures: {
|
||||
...simConfigMacroLoadFixtureReport,
|
||||
auditBoundaryEvidenceViolations: ["gscreen-industrial-lathe-wear-toolchange-audit"],
|
||||
},
|
||||
}).missing.includes("virtualHalSimConfigMacroLoadFixtures"), true);
|
||||
assert.equal(createProjectReleaseBrowserDiagnosticsArtifactValidation({
|
||||
...browserDiagnosticsArtifact,
|
||||
virtualHalSimConfigMacroLoadFixtures: {
|
||||
...simConfigMacroLoadFixtureReport,
|
||||
auditPromotionAllowedViolations: ["qtvcp-industrial-lathe-wear-toolchange-audit"],
|
||||
},
|
||||
}).ready, false);
|
||||
const runtimeProjectReport = virtualHalRuntime.getProjectReport({ availableFunctions: VIRTUAL_HAL_WASM_BRIDGE_FUNCTIONS });
|
||||
assert.equal(runtimeProjectReport.simulationRuntime.replacesHostRuntimeForSimulation, true);
|
||||
assert.equal(runtimeProjectReport.systemCoverage.unclassifiedSnapshotPins.length, 0);
|
||||
@@ -1143,6 +1461,8 @@ assert.equal(releaseReadinessWaiting.ready, false);
|
||||
assert.deepEqual(releaseReadinessWaiting.missing, [
|
||||
"project-release-gate",
|
||||
"virtual-hal-sim-config-source-coverage",
|
||||
"virtual-hal-sim-config-promotion-candidates",
|
||||
"virtual-hal-sim-config-macro-load-fixtures",
|
||||
"virtual-hal-motion-controller-matrix",
|
||||
]);
|
||||
assert.equal(releaseReadinessWaiting.simConfigInventory.unexpectedFail, 0);
|
||||
@@ -1157,11 +1477,23 @@ assert.equal(
|
||||
const releaseReadinessReady = createProjectReleaseReadinessReport({
|
||||
observedOutputs: ["project_release_gate=ok"],
|
||||
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
|
||||
virtualHalSimConfigPromotionCandidates: simConfigPromotionCandidateReport,
|
||||
virtualHalSimConfigMacroLoadFixtures: simConfigMacroLoadFixtureReport,
|
||||
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
|
||||
});
|
||||
assert.equal(releaseReadinessReady.ready, true);
|
||||
assert.equal(releaseReadinessReady.releaseGate.passed, true);
|
||||
assert.equal(releaseReadinessReady.virtualHalSimConfigSourceCoverageReady, true);
|
||||
assert.equal(releaseReadinessReady.virtualHalSimConfigPromotionCandidatesReady, true);
|
||||
assert.equal(releaseReadinessReady.virtualHalPromotionFamilySummary, expectedPromotionFamilySummary);
|
||||
assert.deepEqual(
|
||||
releaseReadinessReady.virtualHalPromotionFamilyRows.map(({ id, value }) => [
|
||||
`virtual-hal-promotion-family-${id}`,
|
||||
value,
|
||||
]),
|
||||
expectedPromotionFamilyRows,
|
||||
);
|
||||
assert.equal(releaseReadinessReady.virtualHalSimConfigMacroLoadFixturesReady, true);
|
||||
assert.equal(releaseReadinessReady.virtualHalMotionControllerMatrixReady, true);
|
||||
assert.deepEqual(releaseReadinessReady.gateManifest, createProjectReleaseGateManifest());
|
||||
assert.equal(releaseReadinessReady.gateResultMatrix.rows.length, createProjectReleaseGateManifest().gateCount);
|
||||
@@ -1179,6 +1511,8 @@ assert.equal(
|
||||
const releaseReadinessWithScreenshots = createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
|
||||
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
|
||||
virtualHalSimConfigPromotionCandidates: simConfigPromotionCandidateReport,
|
||||
virtualHalSimConfigMacroLoadFixtures: simConfigMacroLoadFixtureReport,
|
||||
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
|
||||
axisScreenshotArtifacts: ["desktop-preview", "desktop-dro", "mobile-preview", "mobile-mdi"].map((viewportName) => ({
|
||||
apiName: "real-browser-simulation-axis-screenshot-artifact",
|
||||
@@ -1214,6 +1548,8 @@ assert.equal(
|
||||
const releaseReadinessArtifact = parseProjectReleaseReadinessArtifactJson(JSON.stringify(createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
|
||||
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
|
||||
virtualHalSimConfigPromotionCandidates: simConfigPromotionCandidateReport,
|
||||
virtualHalSimConfigMacroLoadFixtures: simConfigMacroLoadFixtureReport,
|
||||
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
|
||||
})));
|
||||
assert.equal(releaseReadinessArtifact.apiName, "project-release-readiness-report");
|
||||
@@ -1235,6 +1571,10 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
|
||||
gateResultMatrixReady: true,
|
||||
gateActionPlanReady: true,
|
||||
virtualHalSimConfigSourceCoverageReady: true,
|
||||
virtualHalSimConfigPromotionCandidatesReady: true,
|
||||
virtualHalPromotionFamilySummary: expectedPromotionFamilySummary,
|
||||
virtualHalPromotionFamilyRows: releaseReadinessReady.virtualHalPromotionFamilyRows,
|
||||
virtualHalSimConfigMacroLoadFixturesReady: true,
|
||||
virtualHalMotionControllerMatrixReady: true,
|
||||
axisScreenshotArtifactSummaryReady: true,
|
||||
axisScreenshotArtifactCount: 0,
|
||||
@@ -1284,6 +1624,29 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
|
||||
label: "Virtual HAL sim-config source coverage",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-promotion-candidates",
|
||||
label: "Virtual HAL sim-config promotion candidates",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-promotion-families",
|
||||
label: "Virtual HAL promotion families",
|
||||
value: expectedPromotionFamilySummary,
|
||||
},
|
||||
...expectedPromotionFamilyRows.map(([id, value]) => ({
|
||||
id,
|
||||
label: `Promotion family ${id.replace("virtual-hal-promotion-family-", "")}`,
|
||||
value,
|
||||
sourceFiles: releaseReadinessReady.virtualHalPromotionFamilyRows.find((row) =>
|
||||
`virtual-hal-promotion-family-${row.id}` === id
|
||||
)?.sourceFiles ?? [],
|
||||
})),
|
||||
{
|
||||
id: "virtual-hal-sim-config-macro-load-fixtures",
|
||||
label: "Virtual HAL sim-config macro/load fixtures",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-motion-controller-matrix",
|
||||
label: "Virtual HAL motion controller matrix",
|
||||
@@ -1307,6 +1670,20 @@ assert.equal(
|
||||
).statusLine,
|
||||
"Ready: Artifact evidence complete",
|
||||
);
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessArtifactValidationSummaryViewModel(
|
||||
releaseReadinessArtifactValidation,
|
||||
).rows.find(({ id }) => id === "virtual-hal-promotion-families")?.value,
|
||||
expectedPromotionFamilySummary,
|
||||
);
|
||||
assert.deepEqual(
|
||||
createProjectReleaseReadinessArtifactValidationSummaryViewModel(
|
||||
releaseReadinessArtifactValidation,
|
||||
).rows
|
||||
.filter(({ id }) => id.startsWith("virtual-hal-promotion-family-"))
|
||||
.map(({ id, value }) => [id, value]),
|
||||
expectedPromotionFamilyRows,
|
||||
);
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessArtifactValidationActionPlan(releaseReadinessArtifactValidation).shellScript,
|
||||
"# Project release readiness artifact validation is already ready.\n",
|
||||
@@ -1350,6 +1727,34 @@ assert.equal(
|
||||
).statusLine,
|
||||
"Ready: Artifact URL workflow complete",
|
||||
);
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
|
||||
await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
artifactUrl: "/project-release-readiness.json",
|
||||
fetchRef: async () => ({
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: async () => JSON.stringify(releaseReadinessArtifact),
|
||||
}),
|
||||
}),
|
||||
).rows.find(({ id }) => id === "virtual-hal-promotion-families")?.value,
|
||||
expectedPromotionFamilySummary,
|
||||
);
|
||||
assert.deepEqual(
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
|
||||
await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
artifactUrl: "/project-release-readiness.json",
|
||||
fetchRef: async () => ({
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: async () => JSON.stringify(releaseReadinessArtifact),
|
||||
}),
|
||||
}),
|
||||
).rows
|
||||
.filter(({ id }) => id.startsWith("virtual-hal-promotion-family-"))
|
||||
.map(({ id, value }) => [id, value]),
|
||||
expectedPromotionFamilyRows,
|
||||
);
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowActionPlan(
|
||||
await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
@@ -1386,6 +1791,8 @@ assert.deepEqual(
|
||||
"simConfigInventory.skipped",
|
||||
"simConfigInventory.unexpectedFail",
|
||||
"virtualHalSimConfigSourceCoverage",
|
||||
"virtualHalSimConfigPromotionCandidates",
|
||||
"virtualHalSimConfigMacroLoadFixtures",
|
||||
"virtualHalMotionControllerMatrix",
|
||||
"blockedRuntimeFamilies",
|
||||
"gateManifest",
|
||||
|
||||
@@ -84,6 +84,25 @@ import {
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState,
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilyRowsRenderState,
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterViewModel,
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomContract,
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomReadiness,
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan,
|
||||
handleIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionChange,
|
||||
bindIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionControls,
|
||||
renderIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState,
|
||||
mountIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState,
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilyRowsDomContract,
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilyRowsDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewPromotionFamilyRowsState,
|
||||
mountIniPanelShellWorkflowOverviewPromotionFamilyRowsState,
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownViewModel,
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownRenderState,
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomContract,
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState,
|
||||
mountIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
|
||||
@@ -744,6 +763,19 @@ assert.deepEqual(shellViewModel.shellWorkflowOverviewContract, {
|
||||
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState",
|
||||
"getWorkflowOverviewPromotionFamilyRowsRenderState",
|
||||
"getWorkflowOverviewPromotionFamilySourceFilterViewModel",
|
||||
"getWorkflowOverviewPromotionFamilySourceFilterDomContract",
|
||||
"getWorkflowOverviewPromotionFamilySourceFilterDomReadiness",
|
||||
"getWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan",
|
||||
"handleWorkflowOverviewPromotionFamilySourceFilterSelectionChange",
|
||||
"bindWorkflowOverviewPromotionFamilySourceFilterSelectionControls",
|
||||
"renderWorkflowOverviewPromotionFamilySourceFilterState",
|
||||
"mountWorkflowOverviewPromotionFamilySourceFilterState",
|
||||
"getWorkflowOverviewPromotionFamilyRowsDomContract",
|
||||
"getWorkflowOverviewPromotionFamilyRowsDomReadiness",
|
||||
"renderWorkflowOverviewPromotionFamilyRowsState",
|
||||
"mountWorkflowOverviewPromotionFamilyRowsState",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness",
|
||||
"renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState",
|
||||
@@ -1044,7 +1076,27 @@ const mountRowsNode = {
|
||||
const mountNode = { dataset: {} };
|
||||
const mountStatusNode = { textContent: "" };
|
||||
const mountDocument = {
|
||||
createElement: (tagName) => ({ tagName, dataset: {}, textContent: "" }),
|
||||
createElement: (tagName) => ({
|
||||
tagName,
|
||||
dataset: {},
|
||||
textContent: "",
|
||||
children: [],
|
||||
listeners: {},
|
||||
append(...nodes) {
|
||||
this.children.push(...nodes);
|
||||
},
|
||||
addEventListener(eventName, handler) {
|
||||
this.listeners[eventName] = handler;
|
||||
},
|
||||
removeEventListener(eventName, handler) {
|
||||
if (this.listeners[eventName] === handler) {
|
||||
delete this.listeners[eventName];
|
||||
}
|
||||
},
|
||||
dispatchEvent(eventName) {
|
||||
return this.listeners[eventName]?.();
|
||||
},
|
||||
}),
|
||||
};
|
||||
assert.deepEqual(
|
||||
createIniPanelShellSessionHandoffMountDomReadiness({
|
||||
@@ -1366,9 +1418,34 @@ assert.deepEqual(createIniPanelShellWorkflowOverviewContract(), shellViewModel.s
|
||||
const releaseReadinessMotionControllerMatrix = createVirtualHalMotionControllerMatrixReport({
|
||||
manifestEntries: VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
|
||||
});
|
||||
const releaseReadinessPromotionCandidates = createVirtualHalSimConfigPromotionCandidateReport({
|
||||
manifestEntries: [
|
||||
...VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
|
||||
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/qtdragon_xyza.ini",
|
||||
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/on_abort.ngc",
|
||||
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/qtdragon_hd_z_compensation.ini",
|
||||
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/on_abort.ngc",
|
||||
"linuxcnc/configs/sim/axis/vismach/puma/puma_seam_weld.ngc",
|
||||
"linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
|
||||
"linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
|
||||
],
|
||||
motionControllerMatrix: releaseReadinessMotionControllerMatrix,
|
||||
});
|
||||
const releaseReadinessMacroLoadFixtures = createVirtualHalSimConfigMacroLoadFixtureReport({
|
||||
manifestEntries: [
|
||||
...VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
|
||||
"linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
|
||||
"linuxcnc/configs/sim/axis/rose_engine/rcone.ngc",
|
||||
"linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
|
||||
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
|
||||
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc",
|
||||
],
|
||||
});
|
||||
const releaseReadinessArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
|
||||
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
|
||||
virtualHalSimConfigPromotionCandidates: releaseReadinessPromotionCandidates,
|
||||
virtualHalSimConfigMacroLoadFixtures: releaseReadinessMacroLoadFixtures,
|
||||
virtualHalMotionControllerMatrix: releaseReadinessMotionControllerMatrix,
|
||||
}));
|
||||
const browserDiagnosticsVirtualHalState = createVirtualHalState();
|
||||
@@ -1379,29 +1456,24 @@ const browserDiagnosticsArtifact = {
|
||||
virtualHalSimulationReplacement: createVirtualHalSimulationReplacementReport(browserDiagnosticsVirtualHalState),
|
||||
virtualHalSourceCompliance: createVirtualHalSourceComplianceReport({ halState: browserDiagnosticsVirtualHalState }),
|
||||
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
|
||||
virtualHalSimConfigPromotionCandidates: createVirtualHalSimConfigPromotionCandidateReport({
|
||||
manifestEntries: [
|
||||
...VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
|
||||
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/qtdragon_xyza.ini",
|
||||
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/on_abort.ngc",
|
||||
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/qtdragon_hd_z_compensation.ini",
|
||||
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/on_abort.ngc",
|
||||
"linuxcnc/configs/sim/axis/vismach/puma/puma_seam_weld.ngc",
|
||||
"linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
|
||||
"linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
|
||||
],
|
||||
motionControllerMatrix: releaseReadinessMotionControllerMatrix,
|
||||
}),
|
||||
virtualHalSimConfigMacroLoadFixtures: createVirtualHalSimConfigMacroLoadFixtureReport({
|
||||
manifestEntries: [
|
||||
...VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
|
||||
"linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
|
||||
"linuxcnc/configs/sim/axis/rose_engine/rcone.ngc",
|
||||
"linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
|
||||
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
|
||||
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc",
|
||||
],
|
||||
}),
|
||||
virtualHalSimConfigPromotionCandidates: releaseReadinessPromotionCandidates,
|
||||
virtualHalPromotionCandidateSummary: {
|
||||
apiName: "real-browser-simulation-promotion-candidate-summary",
|
||||
summaryVersion: 1,
|
||||
phase: "ready",
|
||||
ready: true,
|
||||
candidateCount: 8,
|
||||
readyCandidateCount: 8,
|
||||
familyCount: 3,
|
||||
sourceFileCount: 17,
|
||||
preferredCandidateId: "qtdragon-multi-joint-on-abort",
|
||||
preferredIniPath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
|
||||
preferredGcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
||||
blockedCandidateIds: [],
|
||||
explicitBrowserDiagnosticsCount: 8,
|
||||
inventoryBaseline: "executed=28 passed=28 skipped=131 unexpected_fail=0",
|
||||
},
|
||||
virtualHalSimConfigMacroLoadFixtures: releaseReadinessMacroLoadFixtures,
|
||||
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport({ halState: browserDiagnosticsVirtualHalState }),
|
||||
virtualHalMotionControllerMatrix: releaseReadinessMotionControllerMatrix,
|
||||
};
|
||||
@@ -1725,7 +1797,7 @@ const releaseArtifactMountResult = mountIniPanelShellWorkflowOverviewReleaseRead
|
||||
rowsNode: releaseArtifactRowsNode,
|
||||
});
|
||||
assert.equal(releaseArtifactRenderResult.rendered, true);
|
||||
assert.equal(releaseArtifactRenderResult.rowCount, 11);
|
||||
assert.equal(releaseArtifactRenderResult.rowCount, 17);
|
||||
assert.equal(releaseArtifactRenderResult.dataset.handoffScope, "workflow-overview-release-readiness-artifact");
|
||||
assert.equal(
|
||||
releaseArtifactRenderResult.rowIds.includes("gate-execution-manifest"),
|
||||
@@ -1735,6 +1807,10 @@ assert.equal(
|
||||
releaseArtifactRenderResult.rowIds.includes("gate-execution-summary"),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
releaseArtifactRenderResult.rowIds.includes("virtual-hal-promotion-families"),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
releaseArtifactRenderResult.rowIds.includes("gate-action-plan"),
|
||||
true,
|
||||
@@ -1752,7 +1828,7 @@ assert.equal(
|
||||
true,
|
||||
);
|
||||
assert.equal(releaseArtifactMountResult.ready, true);
|
||||
assert.equal(releaseArtifactMountResult.renderResult.rowCount, 11);
|
||||
assert.equal(releaseArtifactMountResult.renderResult.rowCount, 17);
|
||||
assert.deepEqual(
|
||||
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson("{").missing,
|
||||
["artifact-json"],
|
||||
@@ -1811,11 +1887,18 @@ assert.equal(
|
||||
);
|
||||
assert.equal(releaseArtifactUrlWorkflow.renderState.statusLine, "Ready: No blocking reasons");
|
||||
assert.equal(releaseArtifactUrlWorkflow.mountResult.ready, true);
|
||||
assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 11);
|
||||
assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 17);
|
||||
const releaseArtifactUrlWorkflowSummary =
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
|
||||
releaseArtifactUrlWorkflow,
|
||||
);
|
||||
const expectedPromotionFamilySummary =
|
||||
"qtdragon-on-abort: 6/6 ready; vismach-remap-sims: 1/1 ready; rose-engine-rcone-demo: 1/1 ready";
|
||||
const expectedPromotionFamilyRows = [
|
||||
["virtual-hal-promotion-family-qtdragon-on-abort", "6/6 ready; sources=12; diagnostics=ready"],
|
||||
["virtual-hal-promotion-family-vismach-remap-sims", "1/1 ready; sources=3; diagnostics=ready"],
|
||||
["virtual-hal-promotion-family-rose-engine-rcone-demo", "1/1 ready; sources=2; diagnostics=ready"],
|
||||
];
|
||||
assert.equal(
|
||||
releaseArtifactUrlWorkflowSummary.statusLine,
|
||||
"Ready: Artifact URL workflow complete",
|
||||
@@ -1836,6 +1919,583 @@ assert.equal(
|
||||
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "browser-diagnostics")?.value,
|
||||
"ready",
|
||||
);
|
||||
assert.equal(
|
||||
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-summary")?.value,
|
||||
"ready",
|
||||
);
|
||||
assert.equal(
|
||||
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate")?.value,
|
||||
"qtdragon-multi-joint-on-abort",
|
||||
);
|
||||
assert.equal(
|
||||
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-ini")?.value,
|
||||
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
|
||||
);
|
||||
assert.equal(
|
||||
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-gcode")?.value,
|
||||
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
||||
);
|
||||
assert.equal(
|
||||
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-count")?.value,
|
||||
"8/8 ready",
|
||||
);
|
||||
assert.equal(
|
||||
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value,
|
||||
"executed=28 passed=28 skipped=131 unexpected_fail=0",
|
||||
);
|
||||
const promotionCandidateDrilldownViewModel =
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownViewModel(
|
||||
releaseArtifactUrlWorkflowSummary,
|
||||
{ diagnosticsArtifact: releaseArtifactUrlWorkflow.diagnosticsArtifact },
|
||||
);
|
||||
const promotionCandidateDrilldownRenderState =
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownRenderState(
|
||||
promotionCandidateDrilldownViewModel,
|
||||
);
|
||||
assert.equal(promotionCandidateDrilldownViewModel.ready, true);
|
||||
assert.equal(
|
||||
promotionCandidateDrilldownViewModel.statusLine,
|
||||
"Ready: qtdragon-multi-joint-on-abort source-derived browser evidence ready",
|
||||
);
|
||||
assert.equal(promotionCandidateDrilldownViewModel.candidateId, "qtdragon-multi-joint-on-abort");
|
||||
assert.equal(promotionCandidateDrilldownViewModel.blockedFamilyExcluded, true);
|
||||
assert.equal(promotionCandidateDrilldownViewModel.nodeInventoryReady, true);
|
||||
assert.equal(promotionCandidateDrilldownViewModel.browserEvidenceReady, true);
|
||||
assert.equal(promotionCandidateDrilldownViewModel.virtualHalReportsReady, true);
|
||||
assert.deepEqual(promotionCandidateDrilldownViewModel.requiredReports, [
|
||||
"source-compliance",
|
||||
"sim-config-source-coverage",
|
||||
"command-script-fixtures",
|
||||
"manifest-backed-motion-matrix",
|
||||
]);
|
||||
assert.equal(
|
||||
promotionCandidateDrilldownViewModel.rows.find(({ id }) => id === "ini")?.value,
|
||||
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
|
||||
);
|
||||
assert.equal(
|
||||
promotionCandidateDrilldownViewModel.rows.find(({ id }) => id === "gcode")?.value,
|
||||
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
||||
);
|
||||
assert.equal(
|
||||
promotionCandidateDrilldownViewModel.rows.find(({ id }) => id === "blocked-family-exclusion")?.value,
|
||||
"passed",
|
||||
);
|
||||
assert.equal(promotionCandidateDrilldownViewModel.sourceFileCount, 2);
|
||||
assert.deepEqual(
|
||||
promotionCandidateDrilldownViewModel.sourceRows.map(({ value, sourceKind, manifestBacked }) => [
|
||||
value,
|
||||
sourceKind,
|
||||
manifestBacked,
|
||||
]),
|
||||
[
|
||||
["linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini", "ini", true],
|
||||
["linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc", "gcode", true],
|
||||
],
|
||||
);
|
||||
assert.equal(promotionCandidateDrilldownRenderState.ready, true);
|
||||
assert.equal(promotionCandidateDrilldownRenderState.rowCount, 11);
|
||||
assert.equal(promotionCandidateDrilldownRenderState.sourceFileCount, 2);
|
||||
assert.equal(
|
||||
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomContract().selectors.mount,
|
||||
"[data-workflow-overview-promotion-candidate-drilldown]",
|
||||
);
|
||||
assert.equal(createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomReadiness().ready, false);
|
||||
const promotionCandidateDrilldownMountNode = mountDocument.createElement("section");
|
||||
promotionCandidateDrilldownMountNode.dataset.workflowOverviewPromotionCandidateDrilldown = "";
|
||||
const promotionCandidateDrilldownStatusNode = mountDocument.createElement("p");
|
||||
promotionCandidateDrilldownStatusNode.dataset.workflowOverviewPromotionCandidateDrilldownStatus = "";
|
||||
const promotionCandidateDrilldownRowsNode = {
|
||||
children: [],
|
||||
dataset: {},
|
||||
append(...nodes) {
|
||||
this.children.push(...nodes);
|
||||
},
|
||||
replaceChildren(...nodes) {
|
||||
this.children = [...nodes];
|
||||
},
|
||||
};
|
||||
promotionCandidateDrilldownRowsNode.dataset.workflowOverviewPromotionCandidateDrilldownRows = "";
|
||||
const promotionCandidateDrilldownRenderResult =
|
||||
renderIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState(
|
||||
promotionCandidateDrilldownRenderState,
|
||||
{
|
||||
documentRef: mountDocument,
|
||||
mountNode: promotionCandidateDrilldownMountNode,
|
||||
statusNode: promotionCandidateDrilldownStatusNode,
|
||||
rowsNode: promotionCandidateDrilldownRowsNode,
|
||||
},
|
||||
);
|
||||
const promotionCandidateDrilldownMountResult =
|
||||
mountIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState({
|
||||
renderState: promotionCandidateDrilldownRenderState,
|
||||
documentRef: mountDocument,
|
||||
mountNode: promotionCandidateDrilldownMountNode,
|
||||
statusNode: promotionCandidateDrilldownStatusNode,
|
||||
rowsNode: promotionCandidateDrilldownRowsNode,
|
||||
});
|
||||
assert.equal(promotionCandidateDrilldownRenderResult.rendered, true);
|
||||
assert.equal(promotionCandidateDrilldownRenderResult.rowCount, 11);
|
||||
assert.deepEqual(promotionCandidateDrilldownRenderResult.dataset, {
|
||||
handoffPhase: "ready",
|
||||
handoffReady: "true",
|
||||
handoffScope: "workflow-overview-promotion-candidate-drilldown",
|
||||
candidateId: "qtdragon-multi-joint-on-abort",
|
||||
});
|
||||
assert.equal(promotionCandidateDrilldownRowsNode.children.some((node) =>
|
||||
node.dataset?.workflowOverviewPromotionCandidateDrilldownValue === "blocked-family-exclusion" &&
|
||||
node.textContent === "passed"
|
||||
), true);
|
||||
assert.equal(promotionCandidateDrilldownRowsNode.children.some((node) =>
|
||||
node.dataset?.workflowOverviewPromotionCandidateDrilldownSourceKind === "gcode" &&
|
||||
node.dataset?.workflowOverviewPromotionCandidateDrilldownManifestBacked === "true" &&
|
||||
node.textContent === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc"
|
||||
), true);
|
||||
assert.equal(promotionCandidateDrilldownMountResult.ready, true);
|
||||
assert.equal(promotionCandidateDrilldownMountResult.renderResult.sourceFileCount, 2);
|
||||
assert.equal(
|
||||
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-families")?.value,
|
||||
expectedPromotionFamilySummary,
|
||||
);
|
||||
assert.deepEqual(
|
||||
releaseArtifactUrlWorkflowSummary.rows
|
||||
.filter(({ id }) => id.startsWith("virtual-hal-promotion-family-"))
|
||||
.map(({ id, value }) => [id, value]),
|
||||
expectedPromotionFamilyRows,
|
||||
);
|
||||
const promotionFamilyRowsRenderState =
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilyRowsRenderState(
|
||||
releaseArtifactUrlWorkflowSummary,
|
||||
);
|
||||
const promotionFamilySourceFilterViewModel =
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterViewModel(
|
||||
promotionFamilyRowsRenderState,
|
||||
);
|
||||
const promotionFamilyRemapSourceFilterViewModel =
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterViewModel(
|
||||
promotionFamilyRowsRenderState,
|
||||
{ sourceKind: "remap-subroutine" },
|
||||
);
|
||||
const promotionFamilyQtDragonGcodeFilterViewModel =
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterViewModel(
|
||||
promotionFamilyRowsRenderState,
|
||||
{
|
||||
familyId: "virtual-hal-promotion-family-qtdragon-on-abort",
|
||||
sourceKind: "gcode",
|
||||
},
|
||||
);
|
||||
assert.equal(promotionFamilyRowsRenderState.ready, true);
|
||||
assert.equal(promotionFamilyRowsRenderState.statusLine, "Ready: 3 promotion families ready");
|
||||
assert.equal(promotionFamilyRowsRenderState.rows.length, 3);
|
||||
assert.equal(promotionFamilyRowsRenderState.sourceFileCount, 17);
|
||||
assert.equal(promotionFamilyRowsRenderState.sourceRows.length, 17);
|
||||
assert.equal(
|
||||
promotionFamilyRowsRenderState.rows.find(({ id }) => id === "virtual-hal-promotion-family-qtdragon-on-abort")
|
||||
?.sourceKindSummary,
|
||||
"ini=6 gcode=6",
|
||||
);
|
||||
assert.deepEqual(
|
||||
promotionFamilyRowsRenderState.rows.find(({ id }) => id === "virtual-hal-promotion-family-vismach-remap-sims")
|
||||
?.sourceKindCounts,
|
||||
{ ini: 1, gcode: 1, "remap-subroutine": 1 },
|
||||
);
|
||||
assert.equal(
|
||||
promotionFamilyRowsRenderState.rows.find(({ id }) => id === "virtual-hal-promotion-family-rose-engine-rcone-demo")
|
||||
?.sourceKindSummary,
|
||||
"ini=1 gcode=1",
|
||||
);
|
||||
assert.equal(promotionFamilySourceFilterViewModel.ready, true);
|
||||
assert.equal(promotionFamilySourceFilterViewModel.statusLine, "Ready: 17/17 source files selected");
|
||||
assert.equal(promotionFamilySourceFilterViewModel.filteredSourceFileCount, 17);
|
||||
assert.equal(promotionFamilySourceFilterViewModel.totalSourceFileCount, 17);
|
||||
assert.deepEqual(
|
||||
promotionFamilySourceFilterViewModel.familyFilters.map(({ id, count, selected }) => [id, count, selected]),
|
||||
[
|
||||
["all", 17, true],
|
||||
["virtual-hal-promotion-family-qtdragon-on-abort", 12, false],
|
||||
["virtual-hal-promotion-family-vismach-remap-sims", 3, false],
|
||||
["virtual-hal-promotion-family-rose-engine-rcone-demo", 2, false],
|
||||
],
|
||||
);
|
||||
assert.deepEqual(
|
||||
promotionFamilySourceFilterViewModel.sourceKindFilters.map(({ id, count, selected }) => [id, count, selected]),
|
||||
[
|
||||
["all", 17, true],
|
||||
["ini", 8, false],
|
||||
["gcode", 8, false],
|
||||
["remap-subroutine", 1, false],
|
||||
],
|
||||
);
|
||||
assert.equal(promotionFamilyRemapSourceFilterViewModel.selectedSourceKind, "remap-subroutine");
|
||||
assert.equal(promotionFamilyRemapSourceFilterViewModel.statusLine, "Ready: 1/17 source files selected");
|
||||
assert.equal(promotionFamilyRemapSourceFilterViewModel.familyRows.length, 1);
|
||||
assert.equal(
|
||||
promotionFamilyRemapSourceFilterViewModel.familyRows[0]?.id,
|
||||
"virtual-hal-promotion-family-vismach-remap-sims",
|
||||
);
|
||||
assert.equal(
|
||||
promotionFamilyRemapSourceFilterViewModel.sourceRows[0]?.value,
|
||||
"linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc",
|
||||
);
|
||||
assert.equal(promotionFamilyQtDragonGcodeFilterViewModel.selectedFamilyId, "virtual-hal-promotion-family-qtdragon-on-abort");
|
||||
assert.equal(promotionFamilyQtDragonGcodeFilterViewModel.selectedSourceKind, "gcode");
|
||||
assert.equal(promotionFamilyQtDragonGcodeFilterViewModel.statusLine, "Ready: 6/17 source files selected");
|
||||
assert.equal(promotionFamilyQtDragonGcodeFilterViewModel.scopedSourceFileCount, 12);
|
||||
assert.equal(promotionFamilyQtDragonGcodeFilterViewModel.filteredSourceFileCount, 6);
|
||||
assert.equal(promotionFamilyQtDragonGcodeFilterViewModel.familyRows.length, 1);
|
||||
assert.equal(
|
||||
promotionFamilyQtDragonGcodeFilterViewModel.familyRows[0]?.sourceKindSummary,
|
||||
"gcode=6",
|
||||
);
|
||||
assert.deepEqual(
|
||||
promotionFamilyQtDragonGcodeFilterViewModel.sourceKindFilters.map(({ id, count, selected }) => [id, count, selected]),
|
||||
[
|
||||
["all", 12, false],
|
||||
["ini", 6, false],
|
||||
["gcode", 6, true],
|
||||
],
|
||||
);
|
||||
assert.equal(
|
||||
promotionFamilyQtDragonGcodeFilterViewModel.sourceRows.every(({ familyId, sourceKind }) =>
|
||||
familyId === "virtual-hal-promotion-family-qtdragon-on-abort" &&
|
||||
sourceKind === "gcode"
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomContract().selectors.mount,
|
||||
"[data-workflow-overview-promotion-family-source-filter]",
|
||||
);
|
||||
assert.equal(createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomReadiness().ready, false);
|
||||
const promotionFamilySourceFilterMountNode = mountDocument.createElement("section");
|
||||
promotionFamilySourceFilterMountNode.dataset.workflowOverviewPromotionFamilySourceFilter = "";
|
||||
const promotionFamilySourceFilterStatusNode = mountDocument.createElement("p");
|
||||
promotionFamilySourceFilterStatusNode.dataset.workflowOverviewPromotionFamilySourceFilterStatus = "";
|
||||
const promotionFamilySourceFilterControlsNode = {
|
||||
...mountDocument.createElement("div"),
|
||||
children: [],
|
||||
replaceChildren() {
|
||||
this.children = [];
|
||||
},
|
||||
append(...nodes) {
|
||||
this.children.push(...nodes);
|
||||
},
|
||||
};
|
||||
promotionFamilySourceFilterControlsNode.dataset.workflowOverviewPromotionFamilySourceFilterControls = "";
|
||||
const promotionFamilySourceFilterRowsNode = {
|
||||
...mountDocument.createElement("dl"),
|
||||
children: [],
|
||||
replaceChildren() {
|
||||
this.children = [];
|
||||
},
|
||||
append(...nodes) {
|
||||
this.children.push(...nodes);
|
||||
},
|
||||
};
|
||||
promotionFamilySourceFilterRowsNode.dataset.workflowOverviewPromotionFamilySourceFilterRows = "";
|
||||
const promotionFamilySourceFilterRenderResult =
|
||||
renderIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState(
|
||||
promotionFamilyQtDragonGcodeFilterViewModel,
|
||||
{
|
||||
documentRef: mountDocument,
|
||||
mountNode: promotionFamilySourceFilterMountNode,
|
||||
statusNode: promotionFamilySourceFilterStatusNode,
|
||||
controlsNode: promotionFamilySourceFilterControlsNode,
|
||||
rowsNode: promotionFamilySourceFilterRowsNode,
|
||||
},
|
||||
);
|
||||
const promotionFamilySourceFilterMountResult =
|
||||
mountIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState({
|
||||
viewModel: promotionFamilyQtDragonGcodeFilterViewModel,
|
||||
documentRef: mountDocument,
|
||||
mountNode: promotionFamilySourceFilterMountNode,
|
||||
statusNode: promotionFamilySourceFilterStatusNode,
|
||||
controlsNode: promotionFamilySourceFilterControlsNode,
|
||||
rowsNode: promotionFamilySourceFilterRowsNode,
|
||||
});
|
||||
assert.equal(promotionFamilySourceFilterRenderResult.rendered, true);
|
||||
assert.equal(promotionFamilySourceFilterRenderResult.statusLine, "Ready: 6/17 source files selected");
|
||||
assert.equal(promotionFamilySourceFilterRenderResult.controlCount, 2);
|
||||
assert.equal(promotionFamilySourceFilterRenderResult.filterRowCount, 7);
|
||||
assert.equal(promotionFamilySourceFilterRenderResult.sourceFileCount, 6);
|
||||
assert.equal(promotionFamilySourceFilterRenderResult.rowCount, 13);
|
||||
assert.deepEqual(promotionFamilySourceFilterRenderResult.dataset, {
|
||||
handoffPhase: "ready",
|
||||
handoffReady: "true",
|
||||
handoffScope: "workflow-overview-promotion-family-source-filter",
|
||||
selectedFamilyId: "virtual-hal-promotion-family-qtdragon-on-abort",
|
||||
selectedSourceKind: "gcode",
|
||||
});
|
||||
assert.equal(promotionFamilySourceFilterControlsNode.children.length, 4);
|
||||
assert.equal(
|
||||
promotionFamilySourceFilterControlsNode.children.some((node) =>
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceFilterControl === "family" &&
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceFilterControlValue ===
|
||||
"virtual-hal-promotion-family-qtdragon-on-abort" &&
|
||||
node.textContent === "Family"
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
promotionFamilySourceFilterControlsNode.children.some((node) =>
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceFilterSelect === "source-kind" &&
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceFilterValue === "gcode" &&
|
||||
node.value === "gcode" &&
|
||||
node.children.some((option) =>
|
||||
option.value === "gcode" &&
|
||||
option.selected === true &&
|
||||
option.dataset?.workflowOverviewPromotionFamilySourceFilterCount === "6"
|
||||
)
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
promotionFamilySourceFilterRowsNode.children.some((node) =>
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceFilterValue ===
|
||||
"virtual-hal-promotion-family-qtdragon-on-abort" &&
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceFilterType === "family-filter" &&
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceFilterSelected === "true" &&
|
||||
node.textContent === "virtual-hal-promotion-family-qtdragon-on-abort: 12 selected"
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
promotionFamilySourceFilterRowsNode.children.some((node) =>
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceFilterValue === "gcode" &&
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceFilterType === "source-kind-filter" &&
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceFilterSelected === "true" &&
|
||||
node.textContent === "gcode: 6 selected"
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
promotionFamilySourceFilterRowsNode.children.some((node) =>
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceFilterType === "source-row" &&
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceFilterFamily ===
|
||||
"virtual-hal-promotion-family-qtdragon-on-abort" &&
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceFilterSourceKind === "gcode" &&
|
||||
node.textContent === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc"
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(promotionFamilySourceFilterMountResult.ready, true);
|
||||
assert.equal(promotionFamilySourceFilterMountResult.renderResult.rowCount, 13);
|
||||
const promotionFamilySourceFilterSelectionActionPlan =
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan(
|
||||
promotionFamilyRowsRenderState,
|
||||
{
|
||||
familyId: "virtual-hal-promotion-family-rose-engine-rcone-demo",
|
||||
sourceKind: "all",
|
||||
previousViewModel: promotionFamilyQtDragonGcodeFilterViewModel,
|
||||
},
|
||||
);
|
||||
assert.equal(promotionFamilySourceFilterSelectionActionPlan.ready, true);
|
||||
assert.equal(promotionFamilySourceFilterSelectionActionPlan.changed, true);
|
||||
assert.equal(
|
||||
promotionFamilySourceFilterSelectionActionPlan.statusLine,
|
||||
"Ready: source filter changed to virtual-hal-promotion-family-rose-engine-rcone-demo / all",
|
||||
);
|
||||
assert.equal(
|
||||
promotionFamilySourceFilterSelectionActionPlan.nextViewModel.statusLine,
|
||||
"Ready: 2/17 source files selected",
|
||||
);
|
||||
assert.equal(promotionFamilySourceFilterSelectionActionPlan.nextViewModel.filteredSourceFileCount, 2);
|
||||
assert.equal(promotionFamilySourceFilterSelectionActionPlan.nextViewModel.scopedSourceFileCount, 2);
|
||||
const sourceFilterFamilySelect = promotionFamilySourceFilterControlsNode.children.find((node) =>
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceFilterSelect === "family"
|
||||
);
|
||||
const sourceFilterKindSelect = promotionFamilySourceFilterControlsNode.children.find((node) =>
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceFilterSelect === "source-kind"
|
||||
);
|
||||
sourceFilterFamilySelect.value = "virtual-hal-promotion-family-rose-engine-rcone-demo";
|
||||
sourceFilterKindSelect.value = "all";
|
||||
const promotionFamilySourceFilterSelectionChange =
|
||||
handleIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionChange({
|
||||
renderState: promotionFamilyRowsRenderState,
|
||||
viewModel: promotionFamilyQtDragonGcodeFilterViewModel,
|
||||
documentRef: mountDocument,
|
||||
mountNode: promotionFamilySourceFilterMountNode,
|
||||
statusNode: promotionFamilySourceFilterStatusNode,
|
||||
controlsNode: promotionFamilySourceFilterControlsNode,
|
||||
rowsNode: promotionFamilySourceFilterRowsNode,
|
||||
render: true,
|
||||
});
|
||||
assert.equal(promotionFamilySourceFilterSelectionChange.ready, true);
|
||||
assert.equal(
|
||||
promotionFamilySourceFilterSelectionChange.selectedFamilyId,
|
||||
"virtual-hal-promotion-family-rose-engine-rcone-demo",
|
||||
);
|
||||
assert.equal(promotionFamilySourceFilterSelectionChange.selectedSourceKind, "all");
|
||||
assert.equal(promotionFamilySourceFilterSelectionChange.actionPlan.changed, true);
|
||||
assert.equal(promotionFamilySourceFilterSelectionChange.viewModel.filteredSourceFileCount, 2);
|
||||
assert.equal(promotionFamilySourceFilterSelectionChange.renderResult.sourceFileCount, 2);
|
||||
assert.equal(promotionFamilySourceFilterSelectionChange.renderResult.dataset.selectedSourceKind, "all");
|
||||
assert.equal(
|
||||
promotionFamilySourceFilterRowsNode.children.some((node) =>
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceFilterType === "source-row" &&
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceFilterFamily ===
|
||||
"virtual-hal-promotion-family-rose-engine-rcone-demo" &&
|
||||
node.textContent === "linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc"
|
||||
),
|
||||
true,
|
||||
);
|
||||
renderIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState(
|
||||
promotionFamilyQtDragonGcodeFilterViewModel,
|
||||
{
|
||||
documentRef: mountDocument,
|
||||
mountNode: promotionFamilySourceFilterMountNode,
|
||||
statusNode: promotionFamilySourceFilterStatusNode,
|
||||
controlsNode: promotionFamilySourceFilterControlsNode,
|
||||
rowsNode: promotionFamilySourceFilterRowsNode,
|
||||
},
|
||||
);
|
||||
let promotionFamilySourceFilterBindingLastChange = null;
|
||||
const promotionFamilySourceFilterSelectionBinding =
|
||||
bindIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionControls({
|
||||
renderState: promotionFamilyRowsRenderState,
|
||||
viewModel: promotionFamilyQtDragonGcodeFilterViewModel,
|
||||
documentRef: mountDocument,
|
||||
mountNode: promotionFamilySourceFilterMountNode,
|
||||
statusNode: promotionFamilySourceFilterStatusNode,
|
||||
controlsNode: promotionFamilySourceFilterControlsNode,
|
||||
rowsNode: promotionFamilySourceFilterRowsNode,
|
||||
render: true,
|
||||
onChange: (changeResult) => {
|
||||
promotionFamilySourceFilterBindingLastChange = changeResult;
|
||||
},
|
||||
});
|
||||
assert.equal(promotionFamilySourceFilterSelectionBinding.ready, true);
|
||||
assert.equal(promotionFamilySourceFilterSelectionBinding.boundControlCount, 2);
|
||||
assert.deepEqual(promotionFamilySourceFilterSelectionBinding.controlIds, ["family", "source-kind"]);
|
||||
const boundSourceFilterFamilySelect = promotionFamilySourceFilterControlsNode.children.find((node) =>
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceFilterSelect === "family"
|
||||
);
|
||||
const boundSourceFilterKindSelect = promotionFamilySourceFilterControlsNode.children.find((node) =>
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceFilterSelect === "source-kind"
|
||||
);
|
||||
boundSourceFilterFamilySelect.value = "virtual-hal-promotion-family-rose-engine-rcone-demo";
|
||||
boundSourceFilterKindSelect.value = "all";
|
||||
const promotionFamilySourceFilterBoundChange = boundSourceFilterFamilySelect.dispatchEvent("change");
|
||||
assert.equal(promotionFamilySourceFilterBoundChange.ready, true);
|
||||
assert.equal(
|
||||
promotionFamilySourceFilterBoundChange.selectedFamilyId,
|
||||
"virtual-hal-promotion-family-rose-engine-rcone-demo",
|
||||
);
|
||||
assert.equal(promotionFamilySourceFilterBoundChange.renderResult.sourceFileCount, 2);
|
||||
assert.equal(
|
||||
promotionFamilySourceFilterBindingLastChange.selectedFamilyId,
|
||||
"virtual-hal-promotion-family-rose-engine-rcone-demo",
|
||||
);
|
||||
assert.equal(promotionFamilySourceFilterSelectionBinding.lastChange.selectedSourceKind, "all");
|
||||
assert.deepEqual(promotionFamilySourceFilterSelectionBinding.unbind(), { unbound: 2 });
|
||||
assert.equal(
|
||||
promotionFamilyRowsRenderState.sourceRows.some(({ value }) =>
|
||||
value === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini"
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
promotionFamilyRowsRenderState.sourceRows.some(({ sourceKind, manifestBacked, status, value }) =>
|
||||
sourceKind === "ini" &&
|
||||
manifestBacked === true &&
|
||||
status === "manifest-backed" &&
|
||||
value === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini"
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
promotionFamilyRowsRenderState.sourceRows.some(({ sourceKind, manifestBacked, status, value }) =>
|
||||
sourceKind === "gcode" &&
|
||||
manifestBacked === true &&
|
||||
status === "manifest-backed" &&
|
||||
value === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc"
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
promotionFamilyRowsRenderState.sourceRows.some(({ value }) =>
|
||||
value === "linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc"
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
promotionFamilyRowsRenderState.sourceRows.some(({ sourceKind, manifestBacked, status, value }) =>
|
||||
sourceKind === "remap-subroutine" &&
|
||||
manifestBacked === true &&
|
||||
status === "manifest-backed" &&
|
||||
value === "linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc"
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
promotionFamilyRowsRenderState.sourceRows.some(({ value }) =>
|
||||
value === "linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc"
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilyRowsDomContract().selectors.mount,
|
||||
"[data-workflow-overview-promotion-family-rows]",
|
||||
);
|
||||
assert.equal(createIniPanelShellWorkflowOverviewPromotionFamilyRowsDomReadiness().ready, false);
|
||||
const promotionFamilyRowsMountNode = mountDocument.createElement("section");
|
||||
promotionFamilyRowsMountNode.dataset.workflowOverviewPromotionFamilyRows = "";
|
||||
const promotionFamilyRowsStatusNode = mountDocument.createElement("p");
|
||||
promotionFamilyRowsStatusNode.dataset.workflowOverviewPromotionFamilyRowsStatus = "";
|
||||
const promotionFamilyRowsNode = {
|
||||
...mountDocument.createElement("dl"),
|
||||
children: [],
|
||||
replaceChildren() {
|
||||
this.children = [];
|
||||
},
|
||||
append(...nodes) {
|
||||
this.children.push(...nodes);
|
||||
},
|
||||
};
|
||||
promotionFamilyRowsNode.dataset.workflowOverviewPromotionFamilyRowList = "";
|
||||
const promotionFamilyRowsRenderResult =
|
||||
renderIniPanelShellWorkflowOverviewPromotionFamilyRowsState(
|
||||
promotionFamilyRowsRenderState,
|
||||
{
|
||||
documentRef: mountDocument,
|
||||
mountNode: promotionFamilyRowsMountNode,
|
||||
statusNode: promotionFamilyRowsStatusNode,
|
||||
rowsNode: promotionFamilyRowsNode,
|
||||
},
|
||||
);
|
||||
const promotionFamilyRowsMountResult =
|
||||
mountIniPanelShellWorkflowOverviewPromotionFamilyRowsState({
|
||||
renderState: promotionFamilyRowsRenderState,
|
||||
documentRef: mountDocument,
|
||||
mountNode: promotionFamilyRowsMountNode,
|
||||
statusNode: promotionFamilyRowsStatusNode,
|
||||
rowsNode: promotionFamilyRowsNode,
|
||||
});
|
||||
assert.equal(promotionFamilyRowsRenderResult.rendered, true);
|
||||
assert.equal(promotionFamilyRowsRenderResult.rowCount, 20);
|
||||
assert.deepEqual(promotionFamilyRowsRenderResult.rowIds, expectedPromotionFamilyRows.map(([id]) => id));
|
||||
assert.equal(promotionFamilyRowsRenderResult.sourceFileCount, 17);
|
||||
assert.equal(
|
||||
promotionFamilyRowsNode.children.some((node) =>
|
||||
node.dataset?.workflowOverviewPromotionFamilyValue ===
|
||||
"virtual-hal-promotion-family-qtdragon-on-abort" &&
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceKindCounts === "ini=6 gcode=6" &&
|
||||
node.textContent === "6/6 ready; sources=12; diagnostics=ready; ini=6 gcode=6"
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
promotionFamilyRowsNode.children.some((node) =>
|
||||
node.dataset?.workflowOverviewPromotionFamilyValue ===
|
||||
"virtual-hal-promotion-family-qtdragon-on-abort-source-1" &&
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceKind === "ini" &&
|
||||
node.dataset?.workflowOverviewPromotionFamilyManifestBacked === "true" &&
|
||||
node.dataset?.workflowOverviewPromotionFamilySourceStatus === "manifest-backed" &&
|
||||
node.textContent === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini"
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(promotionFamilyRowsMountResult.ready, true);
|
||||
assert.equal(promotionFamilyRowsMountResult.renderResult.rowCount, 20);
|
||||
assert.equal(
|
||||
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "saved-session-diagnostics")?.value,
|
||||
"ready",
|
||||
@@ -1905,7 +2565,7 @@ const releaseArtifactUrlWorkflowMountResult =
|
||||
rowsNode: releaseArtifactUrlWorkflowRowsNode,
|
||||
});
|
||||
assert.equal(releaseArtifactUrlWorkflowRenderResult.rendered, true);
|
||||
assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 12);
|
||||
assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 22);
|
||||
assert.equal(
|
||||
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("browser-diagnostics"),
|
||||
true,
|
||||
@@ -1914,12 +2574,16 @@ assert.equal(
|
||||
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("saved-session-diagnostics"),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("virtual-hal-promotion-families"),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("mount-result"),
|
||||
true,
|
||||
);
|
||||
assert.equal(releaseArtifactUrlWorkflowMountResult.ready, true);
|
||||
assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 12);
|
||||
assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 22);
|
||||
const releaseArtifactUrlWorkflowMissingDom = await loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl({
|
||||
artifactUrl: "/project-release-readiness.json",
|
||||
fetchRef: async () => ({
|
||||
@@ -1978,6 +2642,39 @@ const workflowOverviewApi = {
|
||||
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel: () => releaseArtifactUrlWorkflowSummary,
|
||||
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan: () => releaseArtifactUrlWorkflow.actionPlan,
|
||||
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState: () => releaseArtifactUrlWorkflowRenderState,
|
||||
getWorkflowOverviewPromotionFamilyRowsRenderState: () => promotionFamilyRowsRenderState,
|
||||
getWorkflowOverviewPromotionFamilySourceFilterViewModel: () => promotionFamilySourceFilterViewModel,
|
||||
getWorkflowOverviewPromotionFamilySourceFilterDomContract:
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomContract,
|
||||
getWorkflowOverviewPromotionFamilySourceFilterDomReadiness: () => (
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomReadiness({
|
||||
documentRef: mountDocument,
|
||||
mountNode: promotionFamilySourceFilterMountNode,
|
||||
statusNode: promotionFamilySourceFilterStatusNode,
|
||||
controlsNode: promotionFamilySourceFilterControlsNode,
|
||||
rowsNode: promotionFamilySourceFilterRowsNode,
|
||||
})
|
||||
),
|
||||
getWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan: () =>
|
||||
promotionFamilySourceFilterSelectionActionPlan,
|
||||
handleWorkflowOverviewPromotionFamilySourceFilterSelectionChange: () =>
|
||||
promotionFamilySourceFilterSelectionChange,
|
||||
bindWorkflowOverviewPromotionFamilySourceFilterSelectionControls: () =>
|
||||
promotionFamilySourceFilterSelectionBinding,
|
||||
renderWorkflowOverviewPromotionFamilySourceFilterState: () => promotionFamilySourceFilterRenderResult,
|
||||
mountWorkflowOverviewPromotionFamilySourceFilterState: () => promotionFamilySourceFilterMountResult,
|
||||
getWorkflowOverviewPromotionFamilyRowsDomContract:
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilyRowsDomContract,
|
||||
getWorkflowOverviewPromotionFamilyRowsDomReadiness: () => (
|
||||
createIniPanelShellWorkflowOverviewPromotionFamilyRowsDomReadiness({
|
||||
documentRef: mountDocument,
|
||||
mountNode: promotionFamilyRowsMountNode,
|
||||
statusNode: promotionFamilyRowsStatusNode,
|
||||
rowsNode: promotionFamilyRowsNode,
|
||||
})
|
||||
),
|
||||
renderWorkflowOverviewPromotionFamilyRowsState: () => promotionFamilyRowsRenderResult,
|
||||
mountWorkflowOverviewPromotionFamilyRowsState: () => promotionFamilyRowsMountResult,
|
||||
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract:
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract,
|
||||
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness: () => (
|
||||
|
||||
@@ -4,6 +4,7 @@ import { basename, dirname, posix, resolve } from "node:path";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES,
|
||||
analyzeIniRuntimeBoundaries,
|
||||
createLinuxCncInterpSdk,
|
||||
planSimConfigStaging,
|
||||
@@ -100,6 +101,7 @@ const runtimeBoundaryNativeAlignmentSummaryPath = resolve(buildDir, "runtime-bou
|
||||
const runtimeProbeGateAlignmentPath = resolve(buildDir, "runtime-probe-gate-alignment.tsv");
|
||||
const nativeRuntimeProbeExecutionPlanPath = resolve(buildDir, "native-runtime-probe-execution-plan.tsv");
|
||||
const nativeRuntimeProbePassEvidenceContractPath = resolve(buildDir, "native-runtime-probe-pass-evidence-contract.tsv");
|
||||
const promotionCandidatesPath = resolve(buildDir, "promotion-candidates.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");
|
||||
@@ -155,6 +157,7 @@ const generatedSimConfigInventoryArtifactPaths = [
|
||||
runtimeProbeGateAlignmentPath,
|
||||
nativeRuntimeProbeExecutionPlanPath,
|
||||
nativeRuntimeProbePassEvidenceContractPath,
|
||||
promotionCandidatesPath,
|
||||
blockedRuntimePromotionLockPath,
|
||||
runtimeBoundaryPromotionReadinessPath,
|
||||
runtimeBoundaryPromotionBlockersPath,
|
||||
@@ -6087,7 +6090,7 @@ function verifyBoundaryPhaseCompletionSummaryRows(rows) {
|
||||
const rowByCriterion = new Map(parsedRows.map((row) => [row.criterion, row]));
|
||||
assert.equal(
|
||||
rowByCriterion.get("wasm_inventory_artifact_documentation_coverage")?.count,
|
||||
"54",
|
||||
"55",
|
||||
"WASM artifact documentation completion count must match generated artifact baseline",
|
||||
);
|
||||
assert.equal(
|
||||
@@ -8609,7 +8612,7 @@ function verifyGeneratedArtifactDocumentationCoverage({
|
||||
);
|
||||
assert.equal(
|
||||
artifactNames.length,
|
||||
54,
|
||||
55,
|
||||
"generated sim-config inventory artifact list count drift",
|
||||
);
|
||||
assert.deepEqual(
|
||||
@@ -8628,6 +8631,7 @@ function verifyGeneratedArtifactDocumentationCoverage({
|
||||
"native-runtime-probe-summary.tsv",
|
||||
"next-boundary-recommendations.tsv",
|
||||
"next-boundary-worklist.tsv",
|
||||
"promotion-candidates.tsv",
|
||||
"python-remap-boundary-summary.tsv",
|
||||
"python-remap-family-summary.tsv",
|
||||
"python-remap-native-runtime-alignment.tsv",
|
||||
@@ -8703,7 +8707,7 @@ function verifyGeneratedArtifactDocumentationCoverage({
|
||||
"generated sim-config inventory artifacts must be listed in browser smoke",
|
||||
);
|
||||
assert.ok(
|
||||
browserSmokeText.includes("wasmArtifactNames.length !== 54"),
|
||||
browserSmokeText.includes("wasmArtifactNames.length !== 55"),
|
||||
"browser smoke must keep generated WASM artifact fixed-count guard",
|
||||
);
|
||||
assert.ok(
|
||||
@@ -11946,6 +11950,198 @@ function verifyHardBlockedInventorySkips(rows, pathMatrixByPath) {
|
||||
}
|
||||
}
|
||||
|
||||
function sourcePathToInventoryPath(sourcePath) {
|
||||
const prefix = "linuxcnc/configs/sim/";
|
||||
return sourcePath.startsWith(prefix) ? sourcePath.slice(prefix.length) : "";
|
||||
}
|
||||
|
||||
function promotionCandidateRows({
|
||||
summaryRows,
|
||||
boundaryRows,
|
||||
nextBoundaryRecords,
|
||||
trackedMatrixByPath,
|
||||
nativeByPath,
|
||||
}) {
|
||||
const summaryByPath = new Map(summaryRows.map((row) => {
|
||||
const [
|
||||
path,
|
||||
inventoryStatus,
|
||||
reason,
|
||||
className,
|
||||
nativeStatus,
|
||||
nativeExpectedFailure,
|
||||
] = row.split("\t");
|
||||
return [path, {
|
||||
path,
|
||||
inventoryStatus,
|
||||
reason,
|
||||
className,
|
||||
nativeStatus,
|
||||
nativeExpectedFailure,
|
||||
}];
|
||||
}));
|
||||
const boundaryByPath = new Map(boundaryRows.map((row) => [row.path, row]));
|
||||
const nextBoundaryByBlocked = new Map(nextBoundaryRecords.map((row) => [row.blocked, row]));
|
||||
|
||||
const evidenceReadyRows = VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES.map((candidate) => {
|
||||
const inventoryPath = sourcePathToInventoryPath(candidate.gcodePath);
|
||||
const summary = summaryByPath.get(inventoryPath);
|
||||
const matrix = trackedMatrixByPath.get(inventoryPath);
|
||||
const native = nativeByPath.get(inventoryPath);
|
||||
const boundary = boundaryByPath.get(inventoryPath);
|
||||
const virtualHalEvidenceReady = candidate.currentNodeInventoryStatus === "PASS" &&
|
||||
candidate.currentMatrixBrowserStatus === "explicit-browser-diagnostics" &&
|
||||
candidate.targetBrowserEvidence === "explicit-browser-diagnostics" &&
|
||||
candidate.blockedKind === "-" &&
|
||||
summary?.inventoryStatus === "PASS";
|
||||
return [
|
||||
"evidence-ready",
|
||||
candidate.id,
|
||||
inventoryPath,
|
||||
sourcePathToInventoryPath(candidate.iniPath),
|
||||
summary?.inventoryStatus ?? "-",
|
||||
summary?.reason ?? "-",
|
||||
matrix?.layer4Node ?? "-",
|
||||
matrix?.layer4Browser ?? "-",
|
||||
summary?.className ?? native?.class ?? "-",
|
||||
summary?.nativeStatus ?? native?.status ?? "-",
|
||||
summary?.nativeExpectedFailure ?? native?.expected_failure ?? "-",
|
||||
matrix?.blocked ?? candidate.blockedKind ?? "-",
|
||||
candidate.dependencyClass,
|
||||
flagValue(virtualHalEvidenceReady),
|
||||
"0",
|
||||
virtualHalEvidenceReady ? "browser_release_evidence_ready_inventory_baseline_unchanged" : "virtual_hal_evidence_incomplete",
|
||||
"wasm-port/tests/sdk/node/verify_sdk_surface.sh && wasm-port/tests/sdk/node/verify_project_release_gate_manifest.sh",
|
||||
].map(tsvValue).join("\t");
|
||||
});
|
||||
|
||||
const inventoryReadyRows = [...summaryByPath.values()]
|
||||
.filter((row) => row.className === "main" && row.inventoryStatus === "SKIP")
|
||||
.sort((left, right) => left.path.localeCompare(right.path))
|
||||
.map((row) => {
|
||||
const boundary = boundaryByPath.get(row.path);
|
||||
const matrix = trackedMatrixByPath.get(row.path);
|
||||
const boundaryWork = nextBoundaryByBlocked.get(row.reason);
|
||||
const promotionAllowed = row.reason === "-" &&
|
||||
boundary?.recommended_blocked === "-" &&
|
||||
row.nativeStatus === "PASS";
|
||||
const blockReason = promotionAllowed
|
||||
? "-"
|
||||
: (boundaryWork?.next_action ?? `blocked_by_${row.reason}`);
|
||||
const recommendedNextCommand = promotionAllowed
|
||||
? "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh"
|
||||
: (boundaryWork?.required_native_proof ?? "no_direct_baseline_promotion_candidate");
|
||||
return [
|
||||
"inventory-ready",
|
||||
row.path,
|
||||
row.path,
|
||||
boundary?.ini ?? "-",
|
||||
row.inventoryStatus,
|
||||
row.reason,
|
||||
matrix?.layer4Node ?? "-",
|
||||
matrix?.layer4Browser ?? "-",
|
||||
row.className,
|
||||
row.nativeStatus,
|
||||
row.nativeExpectedFailure,
|
||||
matrix?.blocked ?? row.reason,
|
||||
boundary?.dependencies ?? "-",
|
||||
"0",
|
||||
flagValue(promotionAllowed),
|
||||
blockReason,
|
||||
recommendedNextCommand,
|
||||
].map(tsvValue).join("\t");
|
||||
});
|
||||
|
||||
return [...evidenceReadyRows, ...inventoryReadyRows];
|
||||
}
|
||||
|
||||
function verifyPromotionCandidateRows(rows, summaryRows, boundaryRows) {
|
||||
const expectedHeaders = [
|
||||
"candidate_kind",
|
||||
"candidate_id",
|
||||
"path",
|
||||
"ini",
|
||||
"current_status",
|
||||
"skip_kind",
|
||||
"matrix_layer4_node",
|
||||
"matrix_layer4_browser",
|
||||
"class",
|
||||
"native_status",
|
||||
"native_expected_failure",
|
||||
"blocked_kind",
|
||||
"dependency_class",
|
||||
"virtual_hal_evidence_ready",
|
||||
"promotion_allowed",
|
||||
"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 evidenceReadyPaths = VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES
|
||||
.map((candidate) => sourcePathToInventoryPath(candidate.gcodePath))
|
||||
.sort();
|
||||
const boundaryByPath = new Map(boundaryRows.map((row) => [row.path, row]));
|
||||
const parsedEvidenceReady = parsedRows.filter((row) => row.candidate_kind === "evidence-ready");
|
||||
const parsedInventoryReady = parsedRows.filter((row) => row.candidate_kind === "inventory-ready");
|
||||
|
||||
assert.deepEqual(
|
||||
parsedEvidenceReady.map((row) => row.path).sort(),
|
||||
evidenceReadyPaths,
|
||||
"promotion-candidates must cover virtual HAL evidence-ready rows",
|
||||
);
|
||||
assert.deepEqual(
|
||||
parsedInventoryReady.map((row) => row.path).sort(),
|
||||
expectedSkippedMain,
|
||||
"promotion-candidates must cover every skipped main inventory candidate row",
|
||||
);
|
||||
assert.equal(
|
||||
parsedInventoryReady.filter((row) => row.promotion_allowed === "1").length,
|
||||
0,
|
||||
"current baseline has no direct skipped-main inventory promotion candidate",
|
||||
);
|
||||
for (const row of parsedEvidenceReady) {
|
||||
assert.equal(row.current_status, "PASS", `${row.path}: evidence-ready row must be a current inventory PASS`);
|
||||
assert.equal(row.skip_kind, "-", `${row.path}: evidence-ready row must not have a skip kind`);
|
||||
assert.equal(row.blocked_kind, "-", `${row.path}: evidence-ready row must not be hard-blocked`);
|
||||
assert.equal(row.virtual_hal_evidence_ready, "1", `${row.path}: evidence-ready row lacks virtual HAL evidence`);
|
||||
assert.equal(row.promotion_allowed, "0", `${row.path}: evidence-ready row must not claim inventory promotion`);
|
||||
assert.equal(row.matrix_layer4_node, "INV", `${row.path}: evidence-ready row should remain Node INV`);
|
||||
assert.equal(row.matrix_layer4_browser, "-", `${row.path}: evidence-ready row should record explicit diagnostics outside matrix browser REP`);
|
||||
assert.equal(row.block_reason, "browser_release_evidence_ready_inventory_baseline_unchanged");
|
||||
assert.ok(
|
||||
row.recommended_next_command.includes("verify_sdk_surface.sh"),
|
||||
`${row.path}: evidence-ready row lacks virtual HAL gate command`,
|
||||
);
|
||||
}
|
||||
for (const row of parsedInventoryReady) {
|
||||
assert.equal(row.current_status, "SKIP", `${row.path}: promotion candidate status drift`);
|
||||
assert.equal(row.class, "main", `${row.path}: promotion candidate must be main class`);
|
||||
assert.equal(row.blocked_kind, row.skip_kind, `${row.path}: skipped inventory blocked kind drift`);
|
||||
assert.equal(row.virtual_hal_evidence_ready, "0", `${row.path}: skipped inventory row must not claim virtual HAL evidence ready`);
|
||||
assert.ok(
|
||||
["L4-PYTHON-REMAP", "L4-TOOL-DB", "L4-USER-M-PROCESS", "UPSTREAM-DEMO"].includes(row.skip_kind),
|
||||
`${row.path}: direct promotion candidate must remain hard-blocked or upstream-demo`,
|
||||
);
|
||||
const boundary = boundaryByPath.get(row.path);
|
||||
assert.ok(boundary, `${row.path}: promotion candidate lacks boundary evidence`);
|
||||
if (row.skip_kind.startsWith("L4-")) {
|
||||
assert.notEqual(row.recommended_next_command, "-", `${row.path}: blocked row lacks next proof command`);
|
||||
assert.notEqual(row.block_reason, "-", `${row.path}: blocked row lacks block reason`);
|
||||
}
|
||||
}
|
||||
return parsedRows;
|
||||
}
|
||||
|
||||
for (const record of nativeRecords) {
|
||||
const skipReason = blockedKind(record, pathMatrixByPath);
|
||||
if (skipReason) {
|
||||
@@ -12425,6 +12621,44 @@ writeFileSync(
|
||||
].join("\n")}\n`,
|
||||
);
|
||||
|
||||
const promotionCandidateRowsGenerated = promotionCandidateRows({
|
||||
summaryRows,
|
||||
boundaryRows: boundarySummaryRecords,
|
||||
nextBoundaryRecords,
|
||||
trackedMatrixByPath,
|
||||
nativeByPath: new Map(nativeRecords.map((record) => [record.path, record])),
|
||||
});
|
||||
verifyPromotionCandidateRows(
|
||||
promotionCandidateRowsGenerated,
|
||||
summaryRows,
|
||||
boundarySummaryRecords,
|
||||
);
|
||||
writeFileSync(
|
||||
promotionCandidatesPath,
|
||||
`${[
|
||||
[
|
||||
"candidate_kind",
|
||||
"candidate_id",
|
||||
"path",
|
||||
"ini",
|
||||
"current_status",
|
||||
"skip_kind",
|
||||
"matrix_layer4_node",
|
||||
"matrix_layer4_browser",
|
||||
"class",
|
||||
"native_status",
|
||||
"native_expected_failure",
|
||||
"blocked_kind",
|
||||
"dependency_class",
|
||||
"virtual_hal_evidence_ready",
|
||||
"promotion_allowed",
|
||||
"block_reason",
|
||||
"recommended_next_command",
|
||||
].join("\t"),
|
||||
...promotionCandidateRowsGenerated,
|
||||
].join("\n")}\n`,
|
||||
);
|
||||
|
||||
writeFileSync(
|
||||
boundarySummaryPath,
|
||||
`${[
|
||||
|
||||
61
wasm-port/tools/update_sim_config_boundary_hashes.mjs
Executable file
61
wasm-port/tools/update_sim_config_boundary_hashes.mjs
Executable file
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env node
|
||||
import { createHash } from "node:crypto";
|
||||
import { readFileSync, writeFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const root = resolve(fileURLToPath(import.meta.url), "../../..");
|
||||
const mode = process.argv.includes("--write") ? "write" : "check";
|
||||
const artifactPaths = [
|
||||
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv",
|
||||
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv",
|
||||
];
|
||||
const targetFiles = [
|
||||
"wasm-port/runtime/sdk/src/linuxcnc-hal.js",
|
||||
"wasm-port/runtime/sdk/src/project-release-readiness.js",
|
||||
];
|
||||
|
||||
function escapeRegExp(text) {
|
||||
return String(text).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
|
||||
function sha256(path) {
|
||||
return createHash("sha256")
|
||||
.update(readFileSync(resolve(root, path)))
|
||||
.digest("hex");
|
||||
}
|
||||
|
||||
const hashes = Object.fromEntries(artifactPaths.map((path) => [path, sha256(path)]));
|
||||
|
||||
function replaceHashBlock(text) {
|
||||
let nextText = text;
|
||||
for (const [artifactPath, hash] of Object.entries(hashes)) {
|
||||
const pattern = new RegExp(`("${escapeRegExp(artifactPath)}":\\s*\\n\\s*")[a-f0-9]{64}(")`, "g");
|
||||
nextText = nextText.replace(pattern, `$1${hash}$2`);
|
||||
}
|
||||
return nextText;
|
||||
}
|
||||
|
||||
const staleFiles = [];
|
||||
for (const targetFile of targetFiles) {
|
||||
const absolutePath = resolve(root, targetFile);
|
||||
const text = readFileSync(absolutePath, "utf8");
|
||||
const nextText = replaceHashBlock(text);
|
||||
if (nextText !== text) {
|
||||
staleFiles.push(targetFile);
|
||||
if (mode === "write") {
|
||||
writeFileSync(absolutePath, nextText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (staleFiles.length > 0 && mode === "check") {
|
||||
console.error(`sim_config_boundary_hashes=stale files=${staleFiles.join(",")}`);
|
||||
console.error("run: wasm-port/tools/update_sim_config_boundary_hashes.sh --write");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
for (const [artifactPath, hash] of Object.entries(hashes)) {
|
||||
console.log(`${artifactPath} ${hash}`);
|
||||
}
|
||||
console.log(`sim_config_boundary_hashes=${staleFiles.length === 0 ? "ok" : "updated"}`);
|
||||
5
wasm-port/tools/update_sim_config_boundary_hashes.sh
Executable file
5
wasm-port/tools/update_sim_config_boundary_hashes.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
node "$ROOT_DIR/tools/update_sim_config_boundary_hashes.mjs" "$@"
|
||||
Reference in New Issue
Block a user