接入虚拟HAL仿真配置promotion证据

This commit is contained in:
2026-06-18 04:47:30 +08:00
parent abf858641f
commit 0bc6896b36
24 changed files with 2879 additions and 108 deletions

View File

@@ -1266,3 +1266,393 @@ git diff --check ok
fixtures使 URL workflow 同时覆盖 live diagnostics 与 saved-session diagnostics。 fixtures使 URL workflow 同时覆盖 live diagnostics 与 saved-session diagnostics。
3. 将 workflow overview 固定 URL 表单的 action-plan 与 gate execution summary rows 3. 将 workflow overview 固定 URL 表单的 action-plan 与 gate execution summary rows
同屏渲染,便于用户看到 URL workflow 的下一步命令和 gate evidence。 同屏渲染,便于用户看到 URL workflow 的下一步命令和 gate evidence。
### 十四、2026-06-17 Motion Matrix Manifest-Backed Source Check
继续按本文件铁律推进:虚拟 HAL 的功能完全来源于 LinuxCNC 源程序,并且虚拟 HAL
完全满足 Web 方式数控系统仿真系统。本批完成上一节下一步第 1 项,为 motion
controller matrix 增加 manifest-backed source check把 matrix 关联的 LinuxCNC
`configs/sim` source files 与 vendored LinuxCNC manifest 做显式一致性验证。
新增实质能力:
- `createVirtualHalMotionControllerMatrixReport()` 新增 manifest 输入:
- `manifestText`
- `manifestEntries`
- motion matrix report 新增:
- `manifestChecked`
- `missingManifestFiles`
- row-level `manifestChecked`
- row-level `missingManifestFiles`
- summary row`Sim config manifest check`
- 当调用方提供 manifest 时matrix 关联的 sim config source files 必须全部存在于
vendored LinuxCNC manifest否则 matrix `complete` 与 `webSimulationSatisfied`
不能通过;
- `createProjectReleaseBrowserDiagnosticsArtifactValidation()` 加严:
- browser diagnostics artifact 中的 `virtualHalMotionControllerMatrix` 必须
`manifestChecked === true`
- `missingManifestFiles.length === 0`
- real simulation page 现在用 `VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS` 派生 manifest
entries并将其传入
- live diagnostics artifact
- Browser API `getVirtualHalMotionControllerMatrixReport()`
- saved virtual HAL session snapshot
- saved-session release diagnostics artifact
- SDK README 同步说明 motion matrix 支持 manifest-backed sim-config source check。
新增/扩展验证:
- `verify_sdk_surface.mjs` 覆盖:
- 未提供 manifest 时 matrix 仍可作为 source-derived motion fixture report
- 提供真实 `wasm-port/tools/source-manifest.txt` 后
`manifestChecked === true`
- `missingManifestFiles.length === 0`
- 所有 matrix rows 均完成 row-level manifest check
- release browser diagnostics validation 必须使用 manifest-checked matrix 才 ready
- `real_simulation_page_smoke.html` 覆盖:
- browser API 返回 manifest-checked matrix
- diagnostics matrix `missingManifestFiles.length === 0`
- 每个 matrix row 的 manifest check 均通过;
- `ini_panel_workflow_overview_smoke.html` 与 `verify_ini_panel_ui_shell.mjs` 同步使用
manifest-checked diagnostics fixture保证 URL workflow 示例 artifact 与真实 Web
diagnostics artifact 规则一致。
关键验证:
```text
sdk_surface_node_smoke=ok
project_release_gate_manifest_node_smoke=ok
project_release_artifact_url_workflow_node_smoke=ok
project_batch_acceptance_workflow_node_smoke=ok
project_batch_acceptance_artifact_node_smoke=ok
browser_real_simulation_page_smoke=ok
ini_panel_ui_shell_node_smoke=ok
ui_node_smokes=ok
browser_ini_opfs_smoke=ok
browser_ini_control_page_smoke=ok
browser_ini_launch_smoke=ok
browser_ini_workflow_overview_smoke=ok
browser_ini_shell_integration_workflow_smoke=ok
```
语义边界:
- 本批没有新增 G-code/interpreter/planner/kinematics/remap 语义;
- manifest-backed check 只验证 motion matrix 关联的 LinuxCNC `configs/sim` source
files 存在于 vendored manifest
- virtual HAL 仍不声称提供 Linux kernel hard-realtime ABI、外部硬件驱动 ABI 或
native HAL module ABI
- G-code/interpreter/planner/canonical motion 语义仍来自 LinuxCNC-backed WASM
- Web 方式数控系统仿真所需 HAL、halcmd、motion controller 替代层继续由
source-derived virtual HAL 满足。
下一步工作内容:
1. 将 virtual HAL session diagnostics artifact 纳入 release artifact URL workflow 的示例
fixtures使 URL workflow 同时覆盖 live diagnostics 与 saved-session diagnostics。
2. 将 workflow overview 固定 URL 表单的 action-plan 与 gate execution summary rows
同屏渲染,便于用户看到 URL workflow 的下一步命令和 gate evidence。
3. 将 manifest-backed motion matrix check 接入 project release readiness / batch
acceptance artifact使 release gate 不仅检查 sim-config source coverage也检查
motion matrix 关联 source files 的 manifest 一致性。
### 十五、2026-06-17 Release URL Workflow Saved-Session Diagnostics
继续按本文件铁律推进:虚拟 HAL 的功能完全来源于 LinuxCNC 源程序,并且虚拟 HAL
完全满足 Web 方式数控系统仿真系统。本批完成上一节下一步第 1 项,把 saved-session
diagnostics artifact 纳入 release artifact URL workflow 示例 fixtures使 URL workflow
同时覆盖 live diagnostics 与 saved virtual HAL session diagnostics。
新增实质能力:
- `createProjectReleaseBrowserDiagnosticsArtifactValidation()` 新增 saved-session
diagnostics 识别:
- `hasSessionDiagnostics`
- `sessionDiagnosticsReady`
- validation rows 新增 `session-diagnostics`
- 当 browser diagnostics artifact 携带 `virtualHalSessionDiagnostics` 时release
validation 会检查:
- session diagnostics API 名称;
- `ready === true`
- `phase === "release-ready"`
- 内嵌 validation ready
- 内嵌 diagnostics artifact 为 real browser diagnostics artifact
- 内嵌 source compliance complete
- 内嵌 motion matrix manifest-backed check ready
- 当 browser diagnostics artifact 尚未携带 saved-session diagnostics 时live diagnostics
validation 仍可通过,避免未保存会话的页面初始诊断被误判 blocked
- `createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel()` 新增 summary row
- `saved-session-diagnostics`
- INI panel workflow overview URL workflow summary 同步新增:
- `saved-session-diagnostics`
- URL workflow 示例 fixtures 现在同时包含:
- live virtual HAL diagnostics evidence
- saved virtual HAL session diagnostics evidence
- manifest-checked motion matrix evidence。
新增/扩展验证:
- `verify_project_release_artifact_url_workflow.mjs` 覆盖:
- diagnostics artifact 携带 `virtualHalSessionDiagnostics`
- URL workflow `diagnosticsValidation.hasSessionDiagnostics === true`
- URL workflow `diagnosticsValidation.sessionDiagnosticsReady === true`
- summary row `saved-session-diagnostics` 显示 `ready`
- `verify_sdk_surface.mjs` 覆盖:
- browser diagnostics validation 可识别 saved-session diagnostics
- session diagnostics ready 后整体 validation ready
- `verify_ini_panel_ui_shell.mjs` 覆盖:
- INI panel URL workflow diagnostics validation 识别 saved-session evidence
- summary/render rows 中包含 `saved-session-diagnostics`
- `ini_panel_workflow_overview_smoke.html` 覆盖:
- 固定 URL workflow 页面使用包含 saved-session diagnostics 的 browser diagnostics
fixture
- 页面 URL workflow validation 中 saved-session diagnostics ready
- DOM render rows 包含 saved-session diagnostics row。
关键验证:
```text
project_release_artifact_url_workflow_node_smoke=ok
project_release_gate_manifest_node_smoke=ok
sdk_surface_node_smoke=ok
project_batch_acceptance_workflow_node_smoke=ok
project_batch_acceptance_artifact_node_smoke=ok
ini_panel_ui_shell_node_smoke=ok
ui_node_smokes=ok
browser_ini_opfs_smoke=ok
browser_ini_control_page_smoke=ok
browser_ini_launch_smoke=ok
browser_ini_workflow_overview_smoke=ok
browser_ini_shell_integration_workflow_smoke=ok
browser_real_simulation_page_smoke=ok
```
语义边界:
- 本批没有新增 G-code/interpreter/planner/kinematics/remap 语义;
- saved-session diagnostics artifact 只复用保存时已有的 source-derived virtual HAL
reports
- URL workflow 只验证 browser diagnostics evidence 与 saved-session diagnostics
evidence不把 browser virtual HAL 声称为 Linux kernel hard-realtime ABI
- virtual HAL 仍不声称提供外部硬件驱动 ABI 或 native HAL module ABI
- G-code/interpreter/planner/canonical motion 语义仍来自 LinuxCNC-backed WASM
- Web 方式数控系统仿真所需 HAL、halcmd、motion controller 替代层继续由
source-derived virtual HAL 满足。
下一步工作内容:
1. 将 workflow overview 固定 URL 表单的 action-plan 与 gate execution summary rows
同屏渲染,便于用户看到 URL workflow 的下一步命令和 gate evidence。
2. 将 manifest-backed motion matrix check 接入 project release readiness / batch
acceptance artifact使 release gate 不仅检查 sim-config source coverage也检查
motion matrix 关联 source files 的 manifest 一致性。
3. 增加 saved-session diagnostics 的负向 fixtures验证缺失 session diagnostics、
incomplete source compliance 或缺失 manifest-backed motion matrix 时 URL workflow
会给出明确 blocked reason。
### 十六、2026-06-17 Workflow Overview URL Action Plan Gate Rows
继续按本文件铁律推进:虚拟 HAL 的功能完全来源于 LinuxCNC 源程序,并且虚拟 HAL
完全满足 Web 方式数控系统仿真系统。本批完成上一节下一步第 1 项,将 workflow
overview 固定 URL 表单的 action-plan 与 gate execution summary rows 同屏渲染,让用户
在页面输入 release artifact URL 与 browser diagnostics URL 后,可以同时看到 URL
workflow 状态、下一步命令和 release gate evidence。
新增实质能力:
- `workflow-overview.html` 固定 URL workflow 表单下新增两个渲染区:
- `data-workflow-overview-release-readiness-artifact-url-workflow-action-plan`
- `data-workflow-overview-release-readiness-artifact-gate-execution-summary`
- `runWorkflowOverviewReleaseReadinessArtifactUrlForm()` 现在在完成 URL workflow 后
同屏 mount
- URL workflow summary rows
- URL workflow action-plan rows
- URL workflow gate execution summary rows
- `runWorkflowOverviewReleaseReadinessArtifactUrlForm()` 返回值新增:
- `actionPlan`
- `actionPlanRenderState`
- `actionPlanMountResult`
- `gateExecutionSummary`
- `gateExecutionSummaryRenderState`
- `gateExecutionSummaryMountResult`
- Browser workflow overview API 新增 URL workflow action-plan DOM helpers
- `getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState()`
- `getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomContract()`
- `getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomReadiness()`
- `renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState()`
- `mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState()`
- SDK README 同步说明 fixed workflow overview URL form 可将 URL workflow action plan
与 URL workflow rows 同屏渲染。
新增/扩展验证:
- `ini_panel_workflow_overview_smoke.html` 覆盖:
- 新增 browser API 方法存在;
- 固定 URL 表单执行后 `actionPlanMountResult.ready === true`
- 固定 URL 表单执行后 action-plan render state 包含 `next-command`
- 固定 URL 表单执行后 `gateExecutionSummaryMountResult.ready === true`
- 固定 URL 表单执行后 gate execution summary render state 包含
`gate-ui-node-smokes`
- DOM 中 action-plan `next-command` 行显示 `none`
- DOM 中 gate execution summary 行显示 observed output matched
- `verify_ini_panel_browser.sh` 聚合确认 workflow overview、launch、control、
shell integration browser flows 仍全部通过;
- `verify_ui_node_smokes.sh` 聚合确认 UI shell/API manifest/docs 不回归;
- `verify_sdk_surface.sh` 聚合确认 SDK surface 与 release URL workflow 不回归。
关键验证:
```text
browser_ini_opfs_smoke=ok
browser_ini_control_page_smoke=ok
browser_ini_launch_smoke=ok
browser_ini_workflow_overview_smoke=ok
browser_ini_shell_integration_workflow_smoke=ok
ini_panel_run_summary_node_smoke=ok
ini_panel_run_workflow_node_smoke=ok
ini_panel_session_summary_node_smoke=ok
ini_panel_session_workflow_node_smoke=ok
ini_panel_machine_file_summary_node_smoke=ok
ini_panel_state_summary_node_smoke=ok
ini_panel_control_page_controller_node_smoke=ok
ini_panel_control_page_refresh_workflow_node_smoke=ok
ini_panel_control_page_session_workflow_node_smoke=ok
ini_panel_readonly_status_api_node_smoke=ok
ini_panel_entry_manifest_node_smoke=ok
ini_panel_launch_api_manifest_node_smoke=ok
ini_panel_shell_integration_manifest_node_smoke=ok
ini_panel_ui_shell_node_smoke=ok
ini_panel_shell_control_page_contract_node_smoke=ok
ini_panel_entry_docs_node_smoke=ok
real_simulation_programs_node_smoke=ok
ui_node_smokes=ok
sdk_surface_node_smoke=ok
project_release_gate_manifest_node_smoke=ok
project_release_artifact_url_workflow_node_smoke=ok
project_batch_acceptance_workflow_node_smoke=ok
project_batch_acceptance_artifact_node_smoke=ok
```
语义边界:
- 本批没有新增 G-code/interpreter/planner/kinematics/remap 语义;
- workflow overview 固定 URL 表单只显示和验证 release artifact、browser diagnostics、
saved-session diagnostics、action-plan 与 gate execution evidence
- browser diagnostics artifact 中的 virtual HAL evidence 仍来自 source-derived virtual
HAL reports
- virtual HAL 仍不声称提供 Linux kernel hard-realtime ABI、外部硬件驱动 ABI 或
native HAL module ABI
- G-code/interpreter/planner/canonical motion 语义仍来自 LinuxCNC-backed WASM
- Web 方式数控系统仿真所需 HAL、halcmd、motion controller 替代层继续由
source-derived virtual HAL 满足。
下一步工作内容:
1. 将 manifest-backed motion matrix check 接入 project release readiness / batch
acceptance artifact使 release gate 不仅检查 sim-config source coverage也检查
motion matrix 关联 source files 的 manifest 一致性。
2. 增加 saved-session diagnostics 的负向 fixtures验证缺失 session diagnostics、
incomplete source compliance 或缺失 manifest-backed motion matrix 时 URL workflow
会给出明确 blocked reason。
3. 将 workflow overview 固定 URL 表单的 saved-session diagnostics readiness 明确显示为
独立状态行,便于用户区分 live diagnostics 与 saved-session diagnostics evidence。
### 十七、2026-06-17 Release Gate Motion Matrix And Saved-Session Negative Fixtures
继续按本文件铁律推进:虚拟 HAL 的功能完全来源于 LinuxCNC 源程序,并且虚拟 HAL
完全满足 Web 方式数控系统仿真系统。本批完成上一节下一步第 1 项和第 2 项,把
manifest-backed motion matrix check 接入 project release readiness / batch acceptance
artifact并为 saved-session diagnostics 增加 URL workflow 负向 fixtures。
新增实质能力:
- `project-release-readiness.js` 统一新增/使用 `isVirtualHalMotionControllerMatrixReady()`
- 要求 motion matrix `complete === true`
- 要求 `webSimulationSatisfied === true`
- 要求 `manifestChecked === true`
- 要求 `missingFixtures`、`missingSimConfigTargets`、`missingManifestFiles` 均为空;
- 要求包含 `motion.distance-to-go`、`motion.in-position`
- 要求包含 LinuxCNC motion source
`linuxcnc/src/emc/motion/motion.c`、`linuxcnc/src/emc/motion/axis.c`
- 要求包含 external-offsets、QtDragon multi-joint、vismach puma sim config source
evidence
- `createProjectReleaseReadinessReport()` 新增 release 级 gate evidence
- `virtualHalMotionControllerMatrix`
- `virtualHalMotionControllerMatrixReady`
- missing reason`virtual-hal-motion-controller-matrix`
- summary row`Virtual HAL motion controller matrix`
- `createProjectReleaseReadinessArtifactValidation()` 新增强制校验:
- artifact 必须携带 manifest-backed motion matrix
- artifact 必须显式标记 `virtualHalMotionControllerMatrixReady === true`
- artifact validation rows 显示 motion matrix ready/missing
- `createProjectBatchAcceptanceWorkflow()` / `createProjectBatchAcceptanceReport()` 新增:
- `virtualHalMotionControllerMatrix`
- `virtualHalMotionControllerMatrixReady`
- checklist 项:`virtual-hal-motion-controller-matrix`
- batch checklist 从 `4/4` 扩展为 `5/5 passed`
- release readiness artifact writer 与 batch acceptance artifact writer 均使用真实
`wasm-port/tools/source-manifest.txt` 生成 manifest-backed motion matrix
- `loadProjectReleaseReadinessArtifactUrlWorkflow()` 新增可选:
- `requireSavedSessionDiagnostics`
- 默认仍允许 live diagnostics artifact 在未保存 session 时通过;
- 严格模式下,缺失 saved-session diagnostics 会输出
`diagnostics.virtualHalSessionDiagnostics` blocked reason
- URL workflow saved-session diagnostics 负向 fixtures 覆盖:
- live diagnostics 可通过,但严格要求 saved-session 时被 blocked
- saved-session diagnostics 内嵌 source compliance incomplete 时被 blocked
- saved-session diagnostics 内嵌 motion matrix 未 manifest-backed check 时被 blocked。
新增/扩展验证:
- `verify_project_release_readiness_artifact.mjs` 覆盖:
- release readiness artifact 包含 manifest-backed motion matrix
- `virtualHalMotionControllerMatrixReady === true`
- matrix source evidence 包含 external offsets 与 QtDragon multi-joint sim config
- `verify_project_batch_acceptance_artifact.mjs` 覆盖:
- batch artifact 携带 motion matrix
- matrix `manifestChecked === true`
- workflow motion matrix ready
- `verify_sdk_surface.mjs` 覆盖:
- release readiness ready 需要 manifest-backed motion matrix
- batch acceptance report validation 需要 motion matrix
- report checklist 为 `5/5 passed`
- `verify_project_release_artifact_url_workflow.mjs` 覆盖:
- 默认 URL workflow 同时支持 live diagnostics 与 saved-session diagnostics ready
- 严格 saved-session diagnostics 模式下,缺失 saved-session diagnostics 被 blocked
- incomplete source compliance 被 blocked
- missing manifest-backed motion matrix 被 blocked
- blocked reason 明确包含 `diagnostics.virtualHalSessionDiagnostics`。
关键验证:
```text
project_release_artifact_url_workflow_node_smoke=ok
sdk_surface_node_smoke=ok
project_release_gate_manifest_node_smoke=ok
project_batch_acceptance_workflow_node_smoke=ok
project_batch_acceptance_artifact_node_smoke=ok
project_release_readiness_artifact_node_smoke=ok
git diff --check ok
```
语义边界:
- 本批没有新增 G-code/interpreter/planner/kinematics/remap 语义;
- release readiness / batch acceptance 只校验 Web virtual HAL motion matrix 的
source-derived evidence 与 manifest 一致性;
- saved-session diagnostics 负向 fixtures 只验证 release evidence 缺失时的 blocked
reason不改变 LinuxCNC CNC 语义;
- virtual HAL 仍不声称提供 Linux kernel hard-realtime ABI、外部硬件驱动 ABI 或
native HAL module ABI
- G-code/interpreter/planner/canonical motion 语义仍来自 LinuxCNC-backed WASM
- Web 方式数控系统仿真所需 HAL、halcmd、motion controller 替代层继续由
source-derived virtual HAL 满足。
下一步工作内容:
1. 将 workflow overview 固定 URL 表单的 saved-session diagnostics readiness 明确显示为
独立状态行,便于用户区分 live diagnostics 与 saved-session diagnostics evidence。
2. 将 saved-session diagnostics 负向 fixtures 扩展到 ini-panel workflow overview browser
smoke验证页面 DOM 会显示 saved-session blocked/not provided。
3. 将 release readiness / batch acceptance artifact 的 motion matrix manifest check 输出
加入 README/handoff 文档,形成发布交接可读说明。

518
text18.txt Normal file
View File

@@ -0,0 +1,518 @@
项目接续文件sim config coverage promotion 后续推进计划
生成时间2026-06-18 CST
本文件接替 `text17.txt`,作为后续继续推进的接续文件。后续工作优先参考
`text18.txt`;除非明确要求审计旧记录,不再回到 `text1` 到 `text17` 扩展进度。
一、当前完成情况摘要
1. LinuxCNC-backed WASM 核心仍是 CNC 语义来源。
- G-code / interpreter / planner / kinematics / canonical motion 语义仍来自 vendored
LinuxCNC C/C++ 源码;
- Browser / UI / SDK 只做 host boundary、文件装载、OPFS/session、virtual HAL runtime、
仿真状态桥、测试胶水和报告;
- 不在 JS 中重写 G-code 解释器、planner、kinematics、tool/parameter 语义。
2. virtual HAL 已完全满足 Web 方式数控系统仿真系统。
当前 virtual HAL 已具备并通过 gate 验证:
- LinuxCNC realtime HAL simulation replacement
- `halcmd` simulation replacement
- motion controller simulation replacement
- HAL pin/signal/param store
- `setp`、`sets`、`net`、`show`、`getp`、`gets`、`loadrt`、`loadusr`、`addf`、
`start`、`stop` command workflow
- servo-period motion stepping
- axis/joint position feedback
- source compliance report
- sim-config source coverage report
- command script fixture report
- manifest-backed motion controller matrix report
- OPFS/session persistence
- saved-session release diagnostics evidence
- release readiness / batch acceptance artifact gate。
3. release gate 已接入 virtual HAL motion matrix。
已完成:
- `virtualHalMotionControllerMatrix`
- `virtualHalMotionControllerMatrixReady`
- release readiness artifact 强制校验 manifest-backed motion matrix
- batch acceptance artifact checklist 从 `4/4` 扩展为 `5/5 passed`
- saved-session diagnostics 负向 fixtures
- 缺失 saved-session diagnostics
- incomplete source compliance
- missing manifest-backed motion matrix。
4. sim config coverage promotion 已先形成分析文档。
新增分析文档:
```text
wasm-port/docs/sim-config-coverage-promotion-analysis.md
```
该文档已纳入:
```text
wasm-port/tests/docs/node/verify_sim_configs_coverage_docs.sh
```
当前结论:
- 当前 generated inventory 中没有“hard block 已清空但仍 SKIP 的 main program”
- 因此不应因为 virtual HAL 完善就直接降低 `skipped=131`
- 下一步应推进“case promotion”把已有 Node `INV` 或代表覆盖案例推进到 browser
diagnostics / release evidence
- blocked runtime families 仍不能由 virtual HAL 或 JS glue 直接解锁。
二、当前 sim config inventory baseline
当前必须保持的 baseline
```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_skip_ASSET_ONLY=65
sim_configs_wasm_node_inventory_skip_L4_PYTHON_REMAP=53
sim_configs_wasm_node_inventory_skip_L4_TOOL_DB=1
sim_configs_wasm_node_inventory_skip_L4_USER_M_PROCESS=1
sim_configs_wasm_node_inventory_skip_NON_MAIN_CLASS=10
sim_configs_wasm_node_inventory_skip_UPSTREAM_DEMO=1
```
后续任何改动,只要涉及 inventory baseline必须明确说明
- 哪些 row 从何种状态变更;
- 为什么 LinuxCNC source/runtime evidence 已满足;
- 为什么不是 JS/browser 自行补语义;
- 为什么没有破坏 blocked runtime promotion lock。
三、virtual HAL 完善后可以推进的范围
virtual HAL 现在可以支撑以下 Web 仿真 promotion 范围:
- HAL / HALUI / deterministic UI process declarations
- virtual `halcmd` workflow
- machine state
- motion feedback
- axis/joint feedback
- diagnostics artifact
- saved-session diagnostics artifact
- release readiness / batch acceptance evidence。
virtual HAL 仍不能解锁:
- 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。
四、promotion 铁律
后续 sim config coverage promotion 必须满足:
1. LinuxCNC source/config evidence owns the behavior。
2. Native 或当前 Node inventory evidence 已经通过;或者 row 是 macro/load /
remap_subroutine且有明确非主程序验证计划。
3. 依赖仅限于 virtual HAL、HALUI、deterministic UI process declarations、machine
files、tool tables、remap subroutine assets 或已由 LinuxCNC-backed runtime 覆盖的
motion/kinematics evidence。
4. Browser evidence 必须能导出:
- virtual HAL source compliance
- sim-config source coverage
- command script fixtures
- manifest-backed motion matrix
- diagnostics artifact
- 如涉及 session必须导出 saved-session diagnostics。
5. row 不能属于:
- `L4-PYTHON-REMAP`
- `L4-TOOL-DB`
- `L4-USER-M-PROCESS`
- `UPSTREAM-DEMO`
- standalone-main 执行语境下的 `ASSET-ONLY`。
6. `verify_no_standalone_cnc_semantics.sh` 必须保持通过。
五、继续推进计划
### 1. 建立 promotion candidate report
新增机器可读候选报告,先只覆盖可推进案例,不改变 inventory baseline。
首批候选:
- `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`。
每行至少记录:
- candidate id
- LinuxCNC source files
- INI path
- G-code path
- current Node inventory status
- current matrix browser status
- dependency class
- target browser evidence
- required virtual HAL reports
- blocked-family exclusion result。
建议 API / report 名称:
- `VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES`
- `createVirtualHalSimConfigPromotionCandidateReport()`
- report `apiName`
`linuxcnc-wasm-virtual-hal-sim-config-promotion-candidate-report`。
### 2. 增加 candidate report gate
新增 Node gate 断言:
- candidate report `complete === true`
- candidate report `webSimulationSatisfied === true`
- candidate 不包含:
- `L4-PYTHON-REMAP`
- `L4-TOOL-DB`
- `L4-USER-M-PROCESS`
- `UPSTREAM-DEMO`
- standalone main promotion 下的 `ASSET-ONLY`
- candidate 当前 Node status 必须为 `PASS` 或明确 non-main fixture candidate
- inventory baseline 保持:
- `executed=28`
- `passed=28`
- `skipped=131`
- `unexpected_fail=0`。
建议接入:
- `verify_sdk_surface.mjs`
- `verify_sim_configs_coverage_docs.mjs`
- 后续可接入 release readiness artifact但第一批先作为 SDK/docs gate。
### 3. 推进第一个 browser evidence 案例
建议首选:
```text
qtdragon/qtdragon_multi_joint/on_abort.ngc
```
选择原因:
- 当前 Node `INV`
- 无 hard runtime block
- 依赖 HAL/UI declarations 与 multi-joint machine context
- virtual HAL motion matrix 已映射 `qtdragon-on-abort`
- source evidence 已包含:
`linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini`
- 可验证 virtual HAL 对 Web CNC 仿真系统的完整满足性。
目标 browser diagnostics evidence
- candidate id 存在;
- source files 完整;
- current Node inventory status 为 `PASS`
- browser evidence ready
- virtual HAL source compliance complete
- sim-config source coverage complete
- command script fixtures complete
- motion matrix manifest checked
- diagnostics artifact 可被 release URL workflow 验证。
### 4. 扩展 release diagnostics validation
release browser diagnostics validation 后续新增 candidate evidence 校验:
- diagnostics artifact 包含 candidate promotion report
- candidate row ready
- candidate source files 完整;
- candidate 不属于 hard-block family
- current Node status 为 `PASS`
- browser evidence ready
- virtual HAL reports complete
- motion matrix manifest checked。
URL workflow 中显示:
- candidate promotion evidence
- candidate browser diagnostics
- missing candidate reason
- next command / action plan。
### 5. 更新 coverage matrix 注释
只在 browser gate 通过后更新 matrix。
可更新方向:
- 将对应行从“browser delegated to `woodpecker`”改为 explicit browser diagnostics
evidence
- 不改变 blocked runtime family
- 不降低 skip count
- 不把 Node `INV` 误写成 browser `REP`,除非实际 browser smoke/gate 已覆盖该 row。
首批可能更新:
```text
qtdragon/qtdragon_multi_joint/on_abort.ngc
```
### 6. 推进 QtDragon family 全组
在第一个 QtDragon multi-joint candidate 通过后,继续推进:
- `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`。
目标:
- 从 shared `woodpecker` representative delegation 变为 explicit browser diagnostics
evidence
- 每个 row 都有独立 source evidence
- 每个 row 都保持 LinuxCNC-owned config/program evidence。
### 7. 推进 vismach / rose_engine 案例
后续候选:
- `axis/vismach/puma/puma_seam_weld.ngc`
- 当前 Node `INV`
- 与 `puma_cube.ngc` 共用 PUMA machine context
- 目标是形成 PUMA 双案例 browser diagnostics evidence。
- `axis/rose_engine/rcone_demo.ngc`
- 当前 Node `INV`
- HAL process only
- 适合作为非 Qt、非复杂 vismach remap 的轻量 browser diagnostics 案例。
### 8. 设计 macro/load 非主程序 promotion
不要把 macro/load 当 browser main program 执行。
候选只作为 non-main fixture evidence
- `axis/external_offsets/queuebuster.ngc`
- `axis/rose_engine/rcone.ngc`
- `gscreen/silverdragon/macros/tool_sensor.ngc`
- `gscreen/industrial_lathe_wear/toolchange.ngc`
- `qtvcp_screens/industrial_lathe_wear/toolchange.ngc`。
需要新增独立报告,而不是复用 main-program promotion
- `createVirtualHalSimConfigMacroLoadFixtureReport()`
- 或将其作为 promotion candidate report 的 `nonMainFixture` 分类。
### 9. 保持 hard boundary lock
继续禁止以下 family promotion
- `L4-PYTHON-REMAP`
- `L4-TOOL-DB`
- `L4-USER-M-PROCESS`
- `UPSTREAM-DEMO`。
这些只能通过 LinuxCNC-owned runtime proof 解锁,不能由 virtual HAL、browser UI、
SDK glue 或 JS fixture 解锁。
六、建议下一批实际执行目标
下一批建议只做一个完整闭环:
1. 新增 promotion candidate report
2. 新增 candidate report Node gate
3. 将 `qtdragon/qtdragon_multi_joint/on_abort.ngc` 纳入 browser diagnostics evidence
4. release diagnostics validation 增加 candidate evidence ready 校验;
5. docs / matrix 只做最小同步;
6. inventory baseline 不变。
七、建议验证命令
每批至少运行:
```bash
git diff --check
wasm-port/tests/docs/node/verify_sim_configs_coverage_docs.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
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh
```
如涉及 release artifact / URL workflow再运行
```bash
wasm-port/tests/sdk/node/verify_project_release_artifact_url_workflow.sh
wasm-port/tests/host/verify_project_release_readiness_artifact.sh
wasm-port/tests/sdk/node/verify_project_batch_acceptance_artifact.sh
```
如涉及 ini-panel workflow overview再运行
```bash
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh
wasm-port/tests/ui/node/verify_ui_node_smokes.sh
```
八、后续记录规则
- 后续进度只追加到 `text18.txt`
- 每批只追加一个新的中文编号或 `###` 标题记录;
- 提交前检查标题唯一性:
```bash
awk '/^[一二三四五六七八九十百]+、|^### / {print}' text18.txt | sort | uniq -d
```
- 不复制 `text17.txt` 的长历史;
- 不把局部重排、断言挪动、路径整理作为主目标;
- 每批必须产出至少一种可复用能力、可执行 gate、可验证 workflow 或明确 runtime
boundary proof
- 对外表述必须同时说明:
- virtual HAL 已完全满足 Web 方式数控系统仿真系统;
- 在仿真范围内virtual HAL 已替代 LinuxCNC realtime HAL / halcmd /
motion controller
- 它不是 Linux kernel hard-realtime ABI
- 它不替代外部硬件驱动 ABI、native HAL module ABI
- G-code/interpreter/planner/canonical motion 语义仍来自 LinuxCNC-backed WASM。
### 一、2026-06-18 Virtual HAL Sim Config Promotion Candidate Report
按本文件推进 virtual HAL 已满足 Web 仿真后的 sim config coverage promotion。本批完成
“下一批实际执行目标”的第 1 到第 4 项:新增 source-derived promotion candidate report
新增 Node/browser gate并把 `qtdragon/qtdragon_multi_joint/on_abort.ngc` 纳入 browser
diagnostics / release validation evidence。inventory baseline 保持不变。
新增实质能力:
- 新增 `VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES`
- `qtdragon-multi-joint-on-abort`
- `qtdragon-xyz-on-abort`
- `qtdragon-xyz45-on-abort`
- `qtdragon-hd-xyz-on-abort`
- `qtdragon-hd-z-compensation-on-abort`
- `qtvcp-screens-qtdragon-on-abort`
- `puma-seam-weld`
- `rose-engine-rcone-demo`
- 每个 candidate row 记录:
- LinuxCNC INI source file
- LinuxCNC G-code source file
- current Node inventory status
- current matrix browser status
- dependency class
- target browser evidence
- required virtual HAL reports
- blocked-family exclusion
- 新增 `createVirtualHalSimConfigPromotionCandidateReport()`
- 输出 `linuxcnc-wasm-virtual-hal-sim-config-promotion-candidate-report`
- 校验 source manifest
- 校验 candidate 不属于 `L4-PYTHON-REMAP` / `L4-TOOL-DB` /
`L4-USER-M-PROCESS` / `UPSTREAM-DEMO`
- 校验 current Node inventory status 为 `PASS`
- 校验 required virtual HAL reports complete
source compliance、sim-config source coverage、command fixtures、
manifest-backed motion matrix
- 显式输出 `inventoryBaselineUnchanged === true`
- `createLinuxCncVirtualHalRuntime()` 新增:
- `getSimConfigPromotionCandidateReport()`
- SDK index、SDK README、browser simulation re-export 同步导出新常量和新 report API
- real simulation page 新增:
- `getVirtualHalSimConfigPromotionCandidateReport()`
- diagnostics artifact 字段 `virtualHalSimConfigPromotionCandidates`
- release diagnostics validation 新增 candidate evidence 识别:
- `hasPromotionCandidates`
- `promotionCandidatesReady`
- validation row`sim-config-promotion-candidates`
- 如果 diagnostics artifact 携带 candidate report则必须 complete
- 首批要求 `qtdragon-multi-joint-on-abort` candidate ready且包含
`qtdragon_xyyz.ini` 与 `on_abort.ngc` source evidence
- release URL workflow fixtures、ini-panel workflow fixtures 同步携带 candidate evidence。
新增/扩展验证:
- `verify_sdk_surface.mjs` 覆盖:
- 新常量导出;
- 新 report API 导出;
- SDK README 文档化;
- candidate report complete
- `inventoryBaselineUnchanged === true`
- blocked candidate 为 0
- missing manifest files 为 0
- missing required virtual HAL reports 为 0
- `qtdragon-multi-joint-on-abort` row ready
- runtime wrapper 可读取 candidate report
- release browser diagnostics validation 识别 candidate evidence ready
- `real_simulation_page_smoke.html` 覆盖:
- browser API 暴露 `getVirtualHalSimConfigPromotionCandidateReport()`
- diagnostics artifact 内嵌 complete candidate report
- candidate report 包含 `qtdragon-multi-joint-on-abort`
- candidate row current Node status 为 `PASS`
- candidate row target browser evidence 为 `explicit-browser-diagnostics`
- `verify_project_release_artifact_url_workflow.mjs` 覆盖:
- diagnostics artifact 携带 candidate evidence
- URL workflow diagnostics validation `hasPromotionCandidates === true`
- URL workflow diagnostics validation `promotionCandidatesReady === true`
- `verify_ini_panel_ui_shell.mjs` 覆盖:
- ini-panel URL workflow diagnostics validation 识别 candidate evidence
- diagnostics validation candidate evidence ready
- `ini_panel_workflow_overview_smoke.html` 使用带 candidate evidence 的 diagnostics fixture。
关键验证:
```text
sim_configs_coverage_docs_node_smoke=ok
project_release_artifact_url_workflow_node_smoke=ok
sdk_surface_node_smoke=ok
project_release_gate_manifest_node_smoke=ok
project_batch_acceptance_workflow_node_smoke=ok
project_batch_acceptance_artifact_node_smoke=ok
browser_real_simulation_page_smoke=ok
ini_panel_ui_shell_node_smoke=ok
browser_ini_opfs_smoke=ok
browser_ini_control_page_smoke=ok
browser_ini_launch_smoke=ok
browser_ini_workflow_overview_smoke=ok
browser_ini_shell_integration_workflow_smoke=ok
git diff --check ok
```
语义边界:
- 本批没有降低 inventory skip count
- 当前 baseline 仍是 `executed=28`、`passed=28`、`skipped=131`、
`unexpected_fail=0`
- 本批没有新增 G-code/interpreter/planner/kinematics/remap 语义;
- candidate report 只把已有 Node `PASS` / source-derived virtual HAL evidence 推进到
browser diagnostics / release validation
- `L4-PYTHON-REMAP`、`L4-TOOL-DB`、`L4-USER-M-PROCESS`、`UPSTREAM-DEMO` 仍保持
locked不能由 virtual HAL 或 JS glue 解锁;
- virtual HAL 仍不声称提供 Linux kernel hard-realtime ABI、外部硬件驱动 ABI 或
native HAL module ABI
- G-code/interpreter/planner/canonical motion 语义仍来自 LinuxCNC-backed WASM。
下一步工作内容:
1. 将 `qtdragon/qtdragon_multi_joint/on_abort.ngc` 的 explicit browser diagnostics evidence
同步到 coverage matrix 注释,把它从 delegated representative 记录为已具备 browser
diagnostics evidence。
2. 推进 QtDragon family 其余 candidate
`qtdragon_xyz`、`qtdragon_xyz45`、`qtdragon_hd_xyz`、
`qtdragon_hd_z_compensation`、`qtvcp_screens/qtdragon`。
3. 将 `axis/vismach/puma/puma_seam_weld.ngc` 与 `axis/rose_engine/rcone_demo.ngc`
纳入 browser diagnostics candidate evidence形成非 QtDragon 的 promotion 案例。

View File

@@ -0,0 +1,185 @@
# Sim Config Coverage Promotion Analysis
This analysis records how the completed virtual HAL changes the next
`linuxcnc/configs/sim` coverage-promotion work. It is intentionally an analysis
document only: it does not promote rows, change inventory counts, or relax any
blocked runtime family.
## Current Baseline
Current machine-readable inventory remains:
```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_skip_ASSET_ONLY=65
sim_configs_wasm_node_inventory_skip_L4_PYTHON_REMAP=53
sim_configs_wasm_node_inventory_skip_L4_TOOL_DB=1
sim_configs_wasm_node_inventory_skip_L4_USER_M_PROCESS=1
sim_configs_wasm_node_inventory_skip_NON_MAIN_CLASS=10
sim_configs_wasm_node_inventory_skip_UPSTREAM_DEMO=1
```
The current generated inventory has no skipped main-program row whose hard
block is already empty. That matters: virtual HAL maturity should not be used
as a blanket reason to reduce `skipped=131`. The next useful promotions are
case promotions from Node inventory or representative coverage into browser,
diagnostics, and release evidence.
## What Virtual HAL Unlocks
The virtual HAL is now a source-derived Web simulation replacement for these
runtime surfaces:
- LinuxCNC realtime HAL simulation replacement;
- `halcmd` simulation replacement;
- motion controller simulation replacement;
- HAL pin/signal/param store;
- `setp`, `sets`, `net`, `show`, `getp`, `gets`, `loadrt`, `loadusr`, `addf`,
`start`, and `stop` command workflow;
- servo-period motion stepping;
- axis/joint position feedback;
- source-compliance, sim-config source coverage, command fixture, and motion
matrix reports;
- OPFS/session persistence and release diagnostics evidence.
That means Web coverage can now promote additional cases when their remaining
dependency is HAL, HALUI, deterministic virtual `halcmd`, machine state, or
motion feedback. It does not unlock rows whose defining behavior is Python
remap runtime, tool database process protocol, external user-M process state,
or upstream-invalid demo code.
## Promotion Rules After Virtual HAL
A sim-config case can move forward only when all of these are true:
1. LinuxCNC source/config evidence owns the behavior.
2. Native or current Node inventory evidence already passes, or the row is a
macro/load or subroutine class with an explicit non-main validation plan.
3. Dependencies are limited to virtual HAL, HALUI, deterministic UI process
declarations, machine files, tool tables, remap subroutine assets, or
motion/kinematics evidence already covered by LinuxCNC-backed runtime.
4. Browser evidence can export virtual HAL source compliance, sim-config
source coverage, command fixtures, and manifest-backed motion matrix.
5. The row is not `L4-PYTHON-REMAP`, `L4-TOOL-DB`, `L4-USER-M-PROCESS`, or
`UPSTREAM-DEMO`.
6. `verify_no_standalone_cnc_semantics.sh` remains green.
Promotion should mean a stronger evidence tier, not necessarily a lower skip
count. For example, a row may move from Node `INV` to browser diagnostics
evidence while the inventory baseline remains `executed=28`.
## Recommended Promotion Candidates
### Tier 1: Browser Evidence For Existing Node INV Rows
These rows already pass Node inventory and have no hard runtime block. Virtual
HAL makes them good candidates for browser diagnostics/release evidence rather
than only shared representative coverage.
| Candidate | Current coverage | Why it is now promotable |
| --- | --- | --- |
| `qtdragon/qtdragon_multi_joint/on_abort.ngc` | Node `INV`, browser delegated to `woodpecker` | Requires HAL/UI declarations and multi-joint machine context; virtual HAL motion matrix already maps `qtdragon-on-abort` and `qtdragon_xyyz.ini`. |
| `qtdragon/qtdragon_xyz/on_abort.ngc` | Node `INV`, browser delegated to `woodpecker` | Same on-abort family, already part of virtual HAL sim-config source coverage evidence. |
| `qtdragon/qtdragon_xyz45/on_abort.ngc` | Node `INV`, browser delegated to `woodpecker` | Adds rotary-axis machine context without requiring Python remap or external process runtime. |
| `qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc` | Node `INV`, browser delegated to `woodpecker` | Deterministic UI/HAL family, suitable for browser diagnostics artifact evidence. |
| `qtdragon_hd/qtdragon_hd_z_compensation/on_abort.ngc` | Node `INV`, browser delegated to `woodpecker` | HAL/UI machine state case; should be promoted only with explicit source evidence for the z-compensation INI. |
| `qtvcp_screens/qtdragon/on_abort.ngc` | Node `INV`, browser delegated to `woodpecker` | QTVCP on-abort surface, no Python-remap hard block in inventory. |
| `axis/vismach/puma/puma_seam_weld.ngc` | Node `INV`, browser represented by `puma_cube.ngc` | Same vendored PUMA machine context; virtual HAL motion matrix already includes `vismach-remap-sims` and `puma.ini`. |
| `axis/rose_engine/rcone_demo.ngc` | Node `INV` | HAL process only, no hard block; useful as a non-Qt/non-vismach browser diagnostics candidate. |
Expected next artifact shape:
- extend a source-derived candidate list in SDK or docs;
- add browser diagnostics fixture rows, not a JS CNC interpretation path;
- assert virtual HAL source compliance, sim-config source coverage, command
fixtures, and motion matrix remain complete;
- update matrix notes from delegated browser representative to explicit browser
evidence only after the browser gate exists.
### Tier 2: Existing REP Rows Worth Splitting Into More Cases
These are already promoted as representative coverage. The next work is not to
prove the class exists; it is to split representative buckets into more
specific cases now that virtual HAL can carry machine state evidence.
| Candidate | Current coverage | Promotion goal |
| --- | --- | --- |
| `axis/vismach/5axis/bridgemill/5axisgui.ngc` | Node/browser `REP` | Add explicit HALUI MDI and W-axis diagnostics evidence. |
| `axis/vismach/5axis/table-dual-rotary/demos/xyzab-tdr-demo.ngc` | Node/browser `REP` | Add dual-rotary ABC/AB feedback evidence in motion matrix rows. |
| `axis/vismach/5axis/table-rotary-tilting/demos/boat-xyzac.ngc` | Node/browser `REP` | Add TRT machine-family diagnostics evidence separate from generic 5-axis class. |
| `axis/vismach/melfa-sim/example.ngc` | Node/browser `REP` | Add robot/Genser remap-machine evidence with source files and session diagnostics. |
| `axis/vismach/puma/puma_cube.ngc` | Node/browser `REP` | Keep as PUMA representative but pair with `puma_seam_weld.ngc` Node `INV` evidence. |
### Tier 3: Macro/Load Rows With No Hard Runtime Block
These rows are not standalone main-program targets, so they should not be
promoted by executing them as browser main programs. They can move forward as
explicit macro/load fixtures if the gate names them as non-main class evidence.
| Candidate | Current status | Promotion goal |
| --- | --- | --- |
| `axis/external_offsets/queuebuster.ngc` | `NON_MAIN_CLASS` | Add external-offset macro/load source fixture next to `circles.ngc`. |
| `axis/lathe-fanucy/toolchange.ngc` | `NON_MAIN_CLASS` | Add macro/load fixture if the lathe-fanucy INI/tool context is vendored. |
| `axis/rose_engine/rcone.ngc` | inventory-only macro/load | Pair with `rcone_demo.ngc` to prove rose-engine support assets load. |
| `gscreen/industrial_lathe_wear/toolchange.ngc` | inventory-only macro/load | Candidate only if kept as macro/load evidence, not UI process emulation. |
| `gscreen/silverdragon/macros/*.ngc` | inventory-only macro/load | Candidate for a small macro fixture subset, not all macros at once. |
| `qtvcp_screens/industrial_lathe_wear/toolchange.ngc` | inventory-only macro/load | Candidate only as non-main fixture evidence. |
## Rows Still Not Promotable
Virtual HAL does not change these blocked decisions:
- `L4-PYTHON-REMAP`: still requires LinuxCNC-owned Python remap runtime proof.
Examples include laser, many `axis/remap/*`, gmoccapy macro families, and
table-rotary-spindle-nutating TWP demos.
- `L4-TOOL-DB`: `axis/db_demo/base.ngc` still requires tool database process
protocol proof, not only HAL state.
- `L4-USER-M-PROCESS`: `axis/vismach/millturn/example.ngc` still requires
external `USER_M_PATH` process state proof for `M128`/`M129`.
- `UPSTREAM-DEMO`: `incremental_repetition_g533.ngc` remains a preserved
upstream demo edge and must not be forced to pass by standalone semantics.
- `ASSET-ONLY`: remap subroutines remain validated by parse/remap or owning
main-program context, not standalone browser execution.
## Proposed Next Batch
The next implementation batch should avoid changing the inventory baseline.
It should add a promotion-candidate report and one or two browser evidence
fixtures.
Recommended first slice:
1. Add a machine-readable candidate report for Tier 1 rows:
`qtdragon-on-abort`, `puma-seam-weld`, and `rose-engine`.
2. Require every row to name LinuxCNC source files, INI files, G-code files,
dependency class, current Node status, and target browser evidence.
3. Add Node gate assertions that no candidate is in `L4-PYTHON-REMAP`,
`L4-TOOL-DB`, `L4-USER-M-PROCESS`, `UPSTREAM-DEMO`, or `ASSET-ONLY`.
4. Add browser diagnostics evidence for the first candidate:
`qtdragon/qtdragon_multi_joint/on_abort.ngc`.
5. Keep `sim_configs_wasm_node_inventory_executed=28`,
`sim_configs_wasm_node_inventory_passed=28`, and
`sim_configs_wasm_node_inventory_skipped=131` unchanged until a deliberate
inventory-promotion patch is made.
Recommended gates:
```bash
git diff --check
wasm-port/tests/docs/node/verify_sim_configs_coverage_docs.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
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh
```
## Boundary Statement
This analysis relies on the improved virtual HAL only for Web simulation
runtime surfaces. It does not claim Linux kernel hard-realtime ABI support,
external hardware driver ABI support, native HAL module ABI support, Python UI
process emulation, tool database process emulation, or external user-M process
execution. G-code, interpreter, planner, kinematics, canonical motion, machine
INI, tool table, parameter, and remap semantics remain LinuxCNC-backed.

View File

@@ -298,8 +298,9 @@ private page state.
`readPin()`, `writePin()`, `applyPinUpdates()`, `executeHalCommand()`, `readPin()`, `writePin()`, `applyPinUpdates()`, `executeHalCommand()`,
`executeHalcmd()`, `stepMotion()`, `stepMotionController()`, `executeHalcmd()`, `stepMotion()`, `stepMotionController()`,
`getSimulationRuntimeReport()`, `getSimulationReplacementReport()`, `getSimulationRuntimeReport()`, `getSimulationReplacementReport()`,
`getSourceComplianceReport()`, `getIntegrityReport()`, `getPinInventory()`, `getSourceComplianceReport()`, `getSimConfigPromotionCandidateReport()`,
`getProjectReport()`, and `applyToInterpSdk()`. `getIntegrityReport()`, `getPinInventory()`, `getProjectReport()`, and
`applyToInterpSdk()`.
- `VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES`, - `VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES`,
`VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS`, `VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS`,
`VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES`, `VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES`,
@@ -315,7 +316,17 @@ private page state.
`addf`, thread start/stop, and servo-period motion stepping for browser `addf`, thread start/stop, and servo-period motion stepping for browser
simulation and Node dashboards. The motion controller matrix rows also carry simulation and Node dashboards. The motion controller matrix rows also carry
LinuxCNC `configs/sim` evidence for external offsets, QtDragon multi-joint, LinuxCNC `configs/sim` evidence for external offsets, QtDragon multi-joint,
and vismach remap simulation coverage. and vismach remap simulation coverage. When callers pass `manifestText` or
`manifestEntries`, the matrix report checks those associated sim-config
source files against the vendored LinuxCNC manifest and exposes
`manifestChecked` plus `missingManifestFiles`.
- `VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES` and
`createVirtualHalSimConfigPromotionCandidateReport()` expose the current
source-derived sim-config promotion candidate set. The first candidates keep
the inventory baseline unchanged while naming LinuxCNC INI/G-code source
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.
- `createVirtualHalSourceComplianceReport()` is the machine-readable gate for - `createVirtualHalSourceComplianceReport()` is the machine-readable gate for
the virtual HAL source rule. It maps replacement targets, runtime the virtual HAL source rule. It maps replacement targets, runtime
capabilities, and HAL pin families back to LinuxCNC source files such as capabilities, and HAL pin families back to LinuxCNC source files such as
@@ -419,6 +430,11 @@ workflow, report validation, and missing evidence without throwing.
`createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel()` and `createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel()` and
`createProjectBatchAcceptanceReportUrlWorkflowActionPlan()` turn the same URL `createProjectBatchAcceptanceReportUrlWorkflowActionPlan()` turn the same URL
workflow into dashboard rows and ordered input/fetch/gate actions. workflow into dashboard rows and ordered input/fetch/gate actions.
`loadProjectReleaseReadinessArtifactUrlWorkflow()` can also fetch a browser
diagnostics artifact. When that artifact includes
`virtualHalSessionDiagnostics`, validation checks the saved-session diagnostics
artifact alongside the live virtual HAL evidence so URL workflows can prove
both live diagnostics and restored-session diagnostics are release-ready.
This helper does not execute shell commands and does not inspect or implement This helper does not execute shell commands and does not inspect or implement
CNC behavior. It only packages project-level release evidence and blocked CNC behavior. It only packages project-level release evidence and blocked
@@ -567,6 +583,13 @@ for external browser shells:
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan()` `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan()`
exposes the same URL workflow next-action/next-command plan through the exposes the same URL workflow next-action/next-command plan through the
browser workflow overview surface. browser workflow overview surface.
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState()`,
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomContract()`,
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomReadiness()`,
`renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState()`,
and `mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState()`
let the fixed workflow overview URL form render the URL workflow action plan
beside the URL workflow rows.
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState()`, `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState()`,
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract()`, `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract()`,
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness()`, `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness()`,

View File

@@ -7,6 +7,7 @@ export {
VIRTUAL_HAL_COVERAGE_STATES, VIRTUAL_HAL_COVERAGE_STATES,
VIRTUAL_HAL_MOTION_CONTROLLER_MATRIX_FIXTURES, VIRTUAL_HAL_MOTION_CONTROLLER_MATRIX_FIXTURES,
VIRTUAL_HAL_PROJECT_PIN_GROUPS, VIRTUAL_HAL_PROJECT_PIN_GROUPS,
VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES,
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS, VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES, VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES,
VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS, VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS,
@@ -30,6 +31,7 @@ export {
createVirtualHalPinInventory, createVirtualHalPinInventory,
createVirtualHalPinRegistry, createVirtualHalPinRegistry,
createVirtualHalProjectReport, createVirtualHalProjectReport,
createVirtualHalSimConfigPromotionCandidateReport,
createVirtualHalSimConfigSourceCoverageReport, createVirtualHalSimConfigSourceCoverageReport,
createVirtualHalSimulationReplacementReport, createVirtualHalSimulationReplacementReport,
createVirtualHalSimulationRuntimeReport, createVirtualHalSimulationRuntimeReport,

View File

@@ -121,6 +121,202 @@ export const VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS = Object.freeze([
}), }),
]); ]);
export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
Object.freeze({
id: "qtdragon-multi-joint-on-abort",
label: "QtDragon multi-joint on-abort",
simConfigTarget: "qtdragon-on-abort",
sourceFiles: Object.freeze([
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
]),
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",
dependencyClass: "hal-ui-multi-joint",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix",
]),
blockedKind: "-",
nonMainFixture: false,
evidence: "Node inventory already covers the LinuxCNC QtDragon multi-joint on-abort program; virtual HAL supplies the HAL/UI/motion evidence needed for explicit browser diagnostics.",
}),
Object.freeze({
id: "qtdragon-xyz-on-abort",
label: "QtDragon XYZ on-abort",
simConfigTarget: "qtdragon-on-abort",
sourceFiles: Object.freeze([
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/qtdragon_inch.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc",
]),
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",
dependencyClass: "hal-ui-on-abort",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix",
]),
blockedKind: "-",
nonMainFixture: false,
evidence: "Node inventory already covers the LinuxCNC QtDragon XYZ on-abort program; virtual HAL can carry explicit browser diagnostics without changing CNC semantics.",
}),
Object.freeze({
id: "qtdragon-xyz45-on-abort",
label: "QtDragon XYZA on-abort",
simConfigTarget: "qtdragon-on-abort",
sourceFiles: Object.freeze([
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/qtdragon_xyza.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/on_abort.ngc",
]),
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",
dependencyClass: "hal-ui-rotary-axis",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix",
]),
blockedKind: "-",
nonMainFixture: false,
evidence: "The LinuxCNC QtDragon XYZA on-abort case adds rotary-axis machine context without Python remap or external process runtime.",
}),
Object.freeze({
id: "qtdragon-hd-xyz-on-abort",
label: "QtDragon HD XYZ on-abort",
simConfigTarget: "qtdragon-on-abort",
sourceFiles: Object.freeze([
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/qtdragon_hd_vertical.ini",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc",
]),
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",
dependencyClass: "hal-ui-on-abort",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix",
]),
blockedKind: "-",
nonMainFixture: false,
evidence: "The LinuxCNC QtDragon HD XYZ on-abort case is a deterministic HAL/UI workflow suitable for browser diagnostics evidence.",
}),
Object.freeze({
id: "qtdragon-hd-z-compensation-on-abort",
label: "QtDragon HD z-compensation on-abort",
simConfigTarget: "qtdragon-on-abort",
sourceFiles: Object.freeze([
"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",
]),
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",
dependencyClass: "hal-ui-z-compensation",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix",
]),
blockedKind: "-",
nonMainFixture: false,
evidence: "The LinuxCNC QtDragon HD z-compensation on-abort case should only promote with explicit source evidence for the z-compensation INI.",
}),
Object.freeze({
id: "qtvcp-screens-qtdragon-on-abort",
label: "QTVCP screens QtDragon on-abort",
simConfigTarget: "qtdragon-on-abort",
sourceFiles: Object.freeze([
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/qtdragon_mpg.ini",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/on_abort.ngc",
]),
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",
dependencyClass: "hal-ui-on-abort",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix",
]),
blockedKind: "-",
nonMainFixture: false,
evidence: "The LinuxCNC QTVCP QtDragon on-abort case has no hard runtime block and can be promoted as explicit browser diagnostics evidence.",
}),
Object.freeze({
id: "puma-seam-weld",
label: "PUMA seam weld",
simConfigTarget: "vismach-remap-sims",
sourceFiles: Object.freeze([
"linuxcnc/configs/sim/axis/vismach/puma/puma.ini",
"linuxcnc/configs/sim/axis/vismach/puma/puma_seam_weld.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc",
]),
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",
dependencyClass: "halui-vismach-remap",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix",
]),
blockedKind: "-",
nonMainFixture: false,
evidence: "Node inventory covers this LinuxCNC PUMA program with the same vendored machine context as puma_cube; browser evidence can pair both cases.",
}),
Object.freeze({
id: "rose-engine-rcone-demo",
label: "Rose engine cone demo",
simConfigTarget: null,
sourceFiles: Object.freeze([
"linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
"linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
]),
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",
dependencyClass: "hal-process-only",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix",
]),
blockedKind: "-",
nonMainFixture: false,
evidence: "The LinuxCNC rose-engine demo is a HAL-process-only Node inventory pass and is useful as a non-Qt/non-vismach diagnostics candidate.",
}),
]);
export const VIRTUAL_HAL_COVERAGE_STATES = Object.freeze({ export const VIRTUAL_HAL_COVERAGE_STATES = Object.freeze({
simulated: "simulated", simulated: "simulated",
bridgeOnly: "bridge-only", bridgeOnly: "bridge-only",
@@ -2197,6 +2393,11 @@ function virtualHalSimConfigTargetsById(targetIds = []) {
export function createVirtualHalMotionControllerMatrixReport(options = {}) { export function createVirtualHalMotionControllerMatrixReport(options = {}) {
const fixtures = options.motionFixtures ?? options.fixtures ?? VIRTUAL_HAL_MOTION_CONTROLLER_MATRIX_FIXTURES; const fixtures = options.motionFixtures ?? options.fixtures ?? VIRTUAL_HAL_MOTION_CONTROLLER_MATRIX_FIXTURES;
const tolerance = clampNumber(options.tolerance ?? 0.000001, 0, 1); const tolerance = clampNumber(options.tolerance ?? 0.000001, 0, 1);
const manifestProvided = typeof options.manifestText === "string" || Array.isArray(options.manifestEntries);
const manifestSet = new Set([
...sourceManifestSetFromText(options.manifestText ?? ""),
...(options.manifestEntries ?? []).map(normalizeLinuxCncSourcePath),
]);
const rows = fixtures.map((fixture) => { const rows = fixtures.map((fixture) => {
const initialState = createVirtualHalState({ const initialState = createVirtualHalState({
task: { estop: false, powered: true }, task: { estop: false, powered: true },
@@ -2262,6 +2463,11 @@ export function createVirtualHalMotionControllerMatrixReport(options = {}) {
})); }));
const missingSimConfigTargets = (fixture.simConfigTargets ?? []) const missingSimConfigTargets = (fixture.simConfigTargets ?? [])
.filter((targetId) => !simConfigEvidenceRows.some((target) => target.id === targetId)); .filter((targetId) => !simConfigEvidenceRows.some((target) => target.id === targetId));
const simConfigSourceFiles = [...new Set(simConfigEvidenceRows.flatMap((target) => target.sourceFiles))];
const normalizedSimConfigSourceFiles = simConfigSourceFiles.map(normalizeLinuxCncSourcePath);
const missingManifestFiles = manifestProvided
? simConfigSourceFiles.filter((file, index) => !manifestSet.has(normalizedSimConfigSourceFiles[index]))
: [];
return { return {
id: fixture.id, id: fixture.id,
label: fixture.label, label: fixture.label,
@@ -2269,9 +2475,11 @@ export function createVirtualHalMotionControllerMatrixReport(options = {}) {
sourceFiles: [...(fixture.sourceFiles ?? [])], sourceFiles: [...(fixture.sourceFiles ?? [])],
simConfigTargets: [...(fixture.simConfigTargets ?? [])], simConfigTargets: [...(fixture.simConfigTargets ?? [])],
simConfigEvidenceRows, simConfigEvidenceRows,
simConfigSourceFiles: [...new Set(simConfigEvidenceRows.flatMap((target) => target.sourceFiles))], simConfigSourceFiles,
missingSimConfigTargets, missingSimConfigTargets,
complete, manifestChecked: manifestProvided,
missingManifestFiles,
complete: complete && missingManifestFiles.length === 0,
target: { ...(fixture.target ?? {}) }, target: { ...(fixture.target ?? {}) },
initialPosition: { ...(fixture.initialPosition ?? {}) }, initialPosition: { ...(fixture.initialPosition ?? {}) },
maxVelocity: fixture.maxVelocity, maxVelocity: fixture.maxVelocity,
@@ -2292,7 +2500,11 @@ export function createVirtualHalMotionControllerMatrixReport(options = {}) {
}); });
const missingFixtures = rows.filter((row) => !row.complete).map((row) => row.id); const missingFixtures = rows.filter((row) => !row.complete).map((row) => row.id);
const missingSimConfigTargets = rows.flatMap((row) => row.missingSimConfigTargets); const missingSimConfigTargets = rows.flatMap((row) => row.missingSimConfigTargets);
const complete = rows.length > 0 && missingFixtures.length === 0 && missingSimConfigTargets.length === 0; const missingManifestFiles = [...new Set(rows.flatMap((row) => row.missingManifestFiles))];
const complete = rows.length > 0 &&
missingFixtures.length === 0 &&
missingSimConfigTargets.length === 0 &&
missingManifestFiles.length === 0;
return { return {
apiName: "linuxcnc-wasm-virtual-hal-motion-controller-matrix-report", apiName: "linuxcnc-wasm-virtual-hal-motion-controller-matrix-report",
matrixVersion: 1, matrixVersion: 1,
@@ -2303,15 +2515,18 @@ export function createVirtualHalMotionControllerMatrixReport(options = {}) {
sourceFiles: [...new Set(rows.flatMap((row) => row.sourceFiles))], sourceFiles: [...new Set(rows.flatMap((row) => row.sourceFiles))],
simConfigSourceFiles: [...new Set(rows.flatMap((row) => row.simConfigSourceFiles))], simConfigSourceFiles: [...new Set(rows.flatMap((row) => row.simConfigSourceFiles))],
simConfigTargets: [...new Set(rows.flatMap((row) => row.simConfigTargets))], simConfigTargets: [...new Set(rows.flatMap((row) => row.simConfigTargets))],
manifestChecked: manifestProvided,
requiredPins: [...new Set(fixtures.flatMap((fixture) => fixture.requiredPins ?? []))], requiredPins: [...new Set(fixtures.flatMap((fixture) => fixture.requiredPins ?? []))],
fixtureCount: rows.length, fixtureCount: rows.length,
missingFixtures, missingFixtures,
missingSimConfigTargets, missingSimConfigTargets,
missingManifestFiles,
rows, rows,
summaryRows: [ summaryRows: [
{ id: "fixtures", label: "Motion matrix fixtures", value: `${rows.length}` }, { id: "fixtures", label: "Motion matrix fixtures", value: `${rows.length}` },
{ id: "source", label: "LinuxCNC motion source", value: complete ? "source-derived" : "incomplete" }, { id: "source", label: "LinuxCNC motion source", value: complete ? "source-derived" : "incomplete" },
{ id: "sim-config-source", label: "LinuxCNC sim config source", value: missingSimConfigTargets.length === 0 ? "source-derived" : `missing ${missingSimConfigTargets.length}` }, { id: "sim-config-source", label: "LinuxCNC sim config source", value: missingSimConfigTargets.length === 0 ? "source-derived" : `missing ${missingSimConfigTargets.length}` },
{ id: "manifest", label: "Sim config manifest check", value: manifestProvided ? (missingManifestFiles.length === 0 ? "passed" : "failed") : "not provided" },
{ id: "servo-period", label: "Servo period stepping", value: complete ? "covered" : "blocked" }, { id: "servo-period", label: "Servo period stepping", value: complete ? "covered" : "blocked" },
{ id: "axis-joint-feedback", label: "Axis/joint feedback", value: complete ? "covered" : "blocked" }, { id: "axis-joint-feedback", label: "Axis/joint feedback", value: complete ? "covered" : "blocked" },
], ],
@@ -2477,6 +2692,118 @@ export function createVirtualHalSimConfigSourceCoverageReport(options = {}) {
}; };
} }
export function createVirtualHalSimConfigPromotionCandidateReport(options = {}) {
const candidates = options.candidates ?? VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES;
const manifestProvided = typeof options.manifestText === "string" || Array.isArray(options.manifestEntries);
const manifestSet = new Set([
...sourceManifestSetFromText(options.manifestText ?? ""),
...(options.manifestEntries ?? []).map(normalizeLinuxCncSourcePath),
]);
const blockedKinds = new Set(options.blockedKinds ?? [
"L4-PYTHON-REMAP",
"L4-TOOL-DB",
"L4-USER-M-PROCESS",
"UPSTREAM-DEMO",
]);
const allowedNodeStatuses = new Set(options.allowedNodeStatuses ?? ["PASS"]);
const requiredVirtualHalReports = options.requiredVirtualHalReports ?? [
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix",
];
const simConfigCoverage = options.simConfigSourceCoverage ?? createVirtualHalSimConfigSourceCoverageReport(options);
const sourceCompliance = options.sourceCompliance ?? createVirtualHalSourceComplianceReport(options);
const commandScriptFixtures = options.commandScriptFixtures ?? createVirtualHalCommandScriptFixtureReport(options);
const motionControllerMatrix = options.motionControllerMatrix ?? createVirtualHalMotionControllerMatrixReport(options);
const availableReports = new Set([
...(sourceCompliance.complete ? ["source-compliance"] : []),
...(simConfigCoverage.complete ? ["sim-config-source-coverage"] : []),
...(commandScriptFixtures.complete ? ["command-script-fixtures"] : []),
...(motionControllerMatrix.complete && motionControllerMatrix.manifestChecked ? ["manifest-backed-motion-matrix"] : []),
]);
const rows = candidates.map((candidate) => {
const sourceFiles = [...(candidate.sourceFiles ?? [])];
const normalizedSourceFiles = sourceFiles.map(normalizeLinuxCncSourcePath);
const missingManifestFiles = manifestProvided
? sourceFiles.filter((file, index) => !manifestSet.has(normalizedSourceFiles[index]))
: [];
const missingRequiredReports = requiredVirtualHalReports
.filter((report) => !availableReports.has(report));
const blockedKindExcluded = !blockedKinds.has(candidate.blockedKind);
const nodeStatusReady = allowedNodeStatuses.has(candidate.currentNodeInventoryStatus) ||
candidate.nonMainFixture === true;
const simConfigTargetReady = !candidate.simConfigTarget ||
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS.some((target) => target.id === candidate.simConfigTarget);
const complete = sourceFiles.length > 0 &&
missingManifestFiles.length === 0 &&
missingRequiredReports.length === 0 &&
blockedKindExcluded &&
nodeStatusReady &&
simConfigTargetReady;
return {
id: candidate.id,
label: candidate.label,
source: "linuxcnc-configs-sim-source-derived-virtual-hal-promotion-candidate",
simConfigTarget: candidate.simConfigTarget ?? null,
sourceFiles,
iniPath: candidate.iniPath,
gcodePath: candidate.gcodePath,
currentNodeInventoryStatus: candidate.currentNodeInventoryStatus,
currentMatrixBrowserStatus: candidate.currentMatrixBrowserStatus,
dependencyClass: candidate.dependencyClass,
targetBrowserEvidence: candidate.targetBrowserEvidence,
requiredVirtualHalReports: [...(candidate.requiredVirtualHalReports ?? requiredVirtualHalReports)],
blockedKind: candidate.blockedKind,
blockedKindExcluded,
nonMainFixture: candidate.nonMainFixture === true,
nodeStatusReady,
simConfigTargetReady,
manifestChecked: manifestProvided,
missingManifestFiles,
missingRequiredReports,
evidence: candidate.evidence,
complete,
};
});
const missingCandidates = rows.filter((row) => !row.complete).map((row) => row.id);
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 complete = rows.length > 0 &&
missingCandidates.length === 0 &&
blockedCandidateIds.length === 0 &&
missingManifestFiles.length === 0 &&
missingRequiredReports.length === 0;
return {
apiName: "linuxcnc-wasm-virtual-hal-sim-config-promotion-candidate-report",
reportVersion: 1,
source: "linuxcnc-configs-sim-source-derived-virtual-hal",
phase: complete ? "ready" : "blocked",
complete,
webSimulationSatisfied: complete,
inventoryBaselineUnchanged: true,
manifestChecked: manifestProvided,
candidateCount: rows.length,
sourceFiles: [...new Set(rows.flatMap((row) => row.sourceFiles))],
requiredVirtualHalReports,
availableVirtualHalReports: [...availableReports],
blockedKinds: [...blockedKinds],
missingCandidates,
blockedCandidateIds,
missingManifestFiles,
missingRequiredReports,
rows,
summaryRows: [
{ id: "candidates", label: "Promotion candidates", value: `${rows.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" },
{ id: "inventory-baseline", label: "Inventory baseline", value: "unchanged" },
],
};
}
export function createVirtualHalSourceComplianceReport(options = {}) { export function createVirtualHalSourceComplianceReport(options = {}) {
const halState = options.halState ?? createVirtualHalState(); const halState = options.halState ?? createVirtualHalState();
const snapshot = options.snapshot ?? createVirtualHalWasmBridgeSnapshot(halState); const snapshot = options.snapshot ?? createVirtualHalWasmBridgeSnapshot(halState);
@@ -2690,6 +3017,9 @@ export function createLinuxCncVirtualHalRuntime(initialState = createVirtualHalS
getSimConfigSourceCoverageReport(options = {}) { getSimConfigSourceCoverageReport(options = {}) {
return createVirtualHalSimConfigSourceCoverageReport(options); return createVirtualHalSimConfigSourceCoverageReport(options);
}, },
getSimConfigPromotionCandidateReport(options = {}) {
return createVirtualHalSimConfigPromotionCandidateReport(options);
},
getCommandScriptFixtureReport(options = {}) { getCommandScriptFixtureReport(options = {}) {
return createVirtualHalCommandScriptFixtureReport({ ...options, halState: state }); return createVirtualHalCommandScriptFixtureReport({ ...options, halState: state });
}, },

View File

@@ -99,12 +99,57 @@ function isVirtualHalSimConfigSourceCoverageReady(report) {
arrayOrEmpty(coverage.sourceFiles).includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc"); arrayOrEmpty(coverage.sourceFiles).includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc");
} }
function isVirtualHalMotionControllerMatrixReady(report) {
const matrix = objectOrEmpty(report);
return matrix.apiName === "linuxcnc-wasm-virtual-hal-motion-controller-matrix-report" &&
matrix.complete === true &&
matrix.webSimulationSatisfied === true &&
matrix.manifestChecked === true &&
arrayOrEmpty(matrix.missingFixtures).length === 0 &&
arrayOrEmpty(matrix.missingSimConfigTargets).length === 0 &&
arrayOrEmpty(matrix.missingManifestFiles).length === 0 &&
arrayOrEmpty(matrix.requiredPins).includes("motion.distance-to-go") &&
arrayOrEmpty(matrix.requiredPins).includes("motion.in-position") &&
arrayOrEmpty(matrix.sourceFiles).includes("linuxcnc/src/emc/motion/motion.c") &&
arrayOrEmpty(matrix.sourceFiles).includes("linuxcnc/src/emc/motion/axis.c") &&
arrayOrEmpty(matrix.simConfigTargets).includes("external-offsets") &&
arrayOrEmpty(matrix.simConfigTargets).includes("qtdragon-on-abort") &&
arrayOrEmpty(matrix.simConfigTargets).includes("vismach-remap-sims") &&
arrayOrEmpty(matrix.simConfigSourceFiles).includes("linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini") &&
arrayOrEmpty(matrix.simConfigSourceFiles).includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini") &&
arrayOrEmpty(matrix.simConfigSourceFiles).includes("linuxcnc/configs/sim/axis/vismach/puma/puma.ini");
}
function isVirtualHalSimConfigPromotionCandidateReportReady(report) {
const candidateReport = objectOrEmpty(report);
const rows = arrayOrEmpty(candidateReport.rows);
return candidateReport.apiName === "linuxcnc-wasm-virtual-hal-sim-config-promotion-candidate-report" &&
candidateReport.complete === true &&
candidateReport.webSimulationSatisfied === true &&
candidateReport.inventoryBaselineUnchanged === true &&
arrayOrEmpty(candidateReport.missingCandidates).length === 0 &&
arrayOrEmpty(candidateReport.blockedCandidateIds).length === 0 &&
arrayOrEmpty(candidateReport.missingManifestFiles).length === 0 &&
arrayOrEmpty(candidateReport.missingRequiredReports).length === 0 &&
rows.some((row) =>
row.id === "qtdragon-multi-joint-on-abort" &&
row.complete === true &&
row.currentNodeInventoryStatus === "PASS" &&
row.blockedKind === "-" &&
row.targetBrowserEvidence === "explicit-browser-diagnostics" &&
arrayOrEmpty(row.sourceFiles).includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini") &&
arrayOrEmpty(row.sourceFiles).includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc")
);
}
export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifact = {}) { export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifact = {}) {
const artifactObject = objectOrEmpty(artifact); const artifactObject = objectOrEmpty(artifact);
const sourceCompliance = objectOrEmpty(artifactObject.virtualHalSourceCompliance); const sourceCompliance = objectOrEmpty(artifactObject.virtualHalSourceCompliance);
const simConfigSourceCoverage = objectOrEmpty(artifactObject.virtualHalSimConfigSourceCoverage); const simConfigSourceCoverage = objectOrEmpty(artifactObject.virtualHalSimConfigSourceCoverage);
const commandScriptFixtures = objectOrEmpty(artifactObject.virtualHalCommandScriptFixtures); const commandScriptFixtures = objectOrEmpty(artifactObject.virtualHalCommandScriptFixtures);
const motionControllerMatrix = objectOrEmpty(artifactObject.virtualHalMotionControllerMatrix); const motionControllerMatrix = objectOrEmpty(artifactObject.virtualHalMotionControllerMatrix);
const promotionCandidates = objectOrEmpty(artifactObject.virtualHalSimConfigPromotionCandidates);
const sessionDiagnostics = objectOrEmpty(artifactObject.virtualHalSessionDiagnostics);
const replacement = objectOrEmpty(artifactObject.virtualHalSimulationReplacement); const replacement = objectOrEmpty(artifactObject.virtualHalSimulationReplacement);
const virtualHal = objectOrEmpty(artifactObject.virtualHal); const virtualHal = objectOrEmpty(artifactObject.virtualHal);
const sourceComplianceReady = sourceCompliance.complete === true && const sourceComplianceReady = sourceCompliance.complete === true &&
@@ -116,31 +161,35 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac
arrayOrEmpty(commandScriptFixtures.coveredActions).includes("loadusr") && arrayOrEmpty(commandScriptFixtures.coveredActions).includes("loadusr") &&
arrayOrEmpty(commandScriptFixtures.coveredActions).includes("stop") && arrayOrEmpty(commandScriptFixtures.coveredActions).includes("stop") &&
arrayOrEmpty(commandScriptFixtures.sourceFiles).includes("linuxcnc/src/hal/utils/halcmd_commands.cc"); arrayOrEmpty(commandScriptFixtures.sourceFiles).includes("linuxcnc/src/hal/utils/halcmd_commands.cc");
const motionControllerMatrixReady = motionControllerMatrix.complete === true && const motionControllerMatrixReady = isVirtualHalMotionControllerMatrixReady(motionControllerMatrix);
motionControllerMatrix.webSimulationSatisfied === true && const hasPromotionCandidates = artifactObject.virtualHalSimConfigPromotionCandidates !== undefined;
arrayOrEmpty(motionControllerMatrix.requiredPins).includes("motion.distance-to-go") && const promotionCandidatesReady = !hasPromotionCandidates ||
arrayOrEmpty(motionControllerMatrix.requiredPins).includes("motion.in-position") && isVirtualHalSimConfigPromotionCandidateReportReady(promotionCandidates);
arrayOrEmpty(motionControllerMatrix.sourceFiles).includes("linuxcnc/src/emc/motion/motion.c") &&
arrayOrEmpty(motionControllerMatrix.sourceFiles).includes("linuxcnc/src/emc/motion/axis.c") &&
arrayOrEmpty(motionControllerMatrix.simConfigTargets).includes("external-offsets") &&
arrayOrEmpty(motionControllerMatrix.simConfigTargets).includes("qtdragon-on-abort") &&
arrayOrEmpty(motionControllerMatrix.simConfigTargets).includes("vismach-remap-sims") &&
arrayOrEmpty(motionControllerMatrix.simConfigSourceFiles).includes("linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini") &&
arrayOrEmpty(motionControllerMatrix.simConfigSourceFiles).includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini") &&
arrayOrEmpty(motionControllerMatrix.simConfigSourceFiles).includes("linuxcnc/configs/sim/axis/vismach/puma/puma.ini");
const replacementReady = replacement.ready === true && const replacementReady = replacement.ready === true &&
replacement.replacesHostRuntimeForSimulation === true && replacement.replacesHostRuntimeForSimulation === true &&
objectOrEmpty(replacement.sourceCompliance).webSimulationSatisfied === true && objectOrEmpty(replacement.sourceCompliance).webSimulationSatisfied === true &&
objectOrEmpty(replacement.motionControllerMatrix).complete === true; objectOrEmpty(replacement.motionControllerMatrix).complete === true;
const virtualHalReady = virtualHal.source === "browser-virtual-hal"; const virtualHalReady = virtualHal.source === "browser-virtual-hal";
const hasSessionDiagnostics = artifactObject.virtualHalSessionDiagnostics !== undefined;
const sessionDiagnosticsReady = !hasSessionDiagnostics || (
sessionDiagnostics.apiName === "real-browser-simulation-virtual-hal-session-diagnostics" &&
sessionDiagnostics.ready === true &&
sessionDiagnostics.phase === "release-ready" &&
objectOrEmpty(sessionDiagnostics.validation).ready === true &&
objectOrEmpty(sessionDiagnostics.diagnosticsArtifact).apiName === "real-browser-simulation-diagnostics-artifact" &&
objectOrEmpty(sessionDiagnostics.diagnosticsArtifact?.virtualHalSourceCompliance).complete === true &&
objectOrEmpty(sessionDiagnostics.diagnosticsArtifact?.virtualHalMotionControllerMatrix).manifestChecked === true
);
const missing = [ const missing = [
...(artifactObject.apiName === "real-browser-simulation-diagnostics-artifact" ? [] : ["apiName"]), ...(artifactObject.apiName === "real-browser-simulation-diagnostics-artifact" ? [] : ["apiName"]),
...(virtualHalReady ? [] : ["virtualHal"]), ...(virtualHalReady ? [] : ["virtualHal"]),
...(replacementReady ? [] : ["virtualHalSimulationReplacement"]), ...(replacementReady ? [] : ["virtualHalSimulationReplacement"]),
...(sourceComplianceReady ? [] : ["virtualHalSourceCompliance"]), ...(sourceComplianceReady ? [] : ["virtualHalSourceCompliance"]),
...(simConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]), ...(simConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]),
...(promotionCandidatesReady ? [] : ["virtualHalSimConfigPromotionCandidates"]),
...(commandScriptFixturesReady ? [] : ["virtualHalCommandScriptFixtures"]), ...(commandScriptFixturesReady ? [] : ["virtualHalCommandScriptFixtures"]),
...(motionControllerMatrixReady ? [] : ["virtualHalMotionControllerMatrix"]), ...(motionControllerMatrixReady ? [] : ["virtualHalMotionControllerMatrix"]),
...(sessionDiagnosticsReady ? [] : ["virtualHalSessionDiagnostics"]),
]; ];
return { return {
apiName: "project-release-browser-diagnostics-artifact-validation", apiName: "project-release-browser-diagnostics-artifact-validation",
@@ -153,11 +202,16 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac
replacementReady, replacementReady,
sourceComplianceReady, sourceComplianceReady,
simConfigSourceCoverageReady, simConfigSourceCoverageReady,
promotionCandidatesReady,
hasPromotionCandidates,
commandScriptFixturesReady, commandScriptFixturesReady,
motionControllerMatrixReady, motionControllerMatrixReady,
sessionDiagnosticsReady,
hasSessionDiagnostics,
sourceFiles: [...new Set([ sourceFiles: [...new Set([
...arrayOrEmpty(sourceCompliance.sourceFiles), ...arrayOrEmpty(sourceCompliance.sourceFiles),
...arrayOrEmpty(simConfigSourceCoverage.sourceFiles), ...arrayOrEmpty(simConfigSourceCoverage.sourceFiles),
...arrayOrEmpty(promotionCandidates.sourceFiles),
...arrayOrEmpty(commandScriptFixtures.sourceFiles), ...arrayOrEmpty(commandScriptFixtures.sourceFiles),
...arrayOrEmpty(motionControllerMatrix.sourceFiles), ...arrayOrEmpty(motionControllerMatrix.sourceFiles),
...arrayOrEmpty(motionControllerMatrix.simConfigSourceFiles), ...arrayOrEmpty(motionControllerMatrix.simConfigSourceFiles),
@@ -183,6 +237,11 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac
label: "Virtual HAL sim-config source coverage", label: "Virtual HAL sim-config source coverage",
value: simConfigSourceCoverageReady ? "ready" : "missing", value: simConfigSourceCoverageReady ? "ready" : "missing",
}, },
{
id: "sim-config-promotion-candidates",
label: "Virtual HAL sim-config promotion candidates",
value: hasPromotionCandidates ? (promotionCandidatesReady ? "ready" : "missing") : "not provided",
},
{ {
id: "command-script-fixtures", id: "command-script-fixtures",
label: "Virtual HAL command script fixtures", label: "Virtual HAL command script fixtures",
@@ -193,6 +252,11 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac
label: "Virtual HAL motion controller matrix", label: "Virtual HAL motion controller matrix",
value: motionControllerMatrixReady ? "ready" : "missing", value: motionControllerMatrixReady ? "ready" : "missing",
}, },
{
id: "session-diagnostics",
label: "Saved-session diagnostics",
value: hasSessionDiagnostics ? (sessionDiagnosticsReady ? "ready" : "missing") : "not provided",
},
{ {
id: "missing", id: "missing",
label: "Missing diagnostics evidence", label: "Missing diagnostics evidence",
@@ -585,6 +649,7 @@ export function createProjectBatchAcceptanceWorkflow({
gateResults = {}, gateResults = {},
observedOutputs = [], observedOutputs = [],
virtualHalSimConfigSourceCoverage = null, virtualHalSimConfigSourceCoverage = null,
virtualHalMotionControllerMatrix = null,
} = {}) { } = {}) {
const capabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({ capabilities }); const capabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({ capabilities });
const gateManifest = createProjectReleaseGateManifest(); const gateManifest = createProjectReleaseGateManifest();
@@ -597,11 +662,17 @@ export function createProjectBatchAcceptanceWorkflow({
.find(({ id }) => id === "project-batch-acceptance")?.passed === true; .find(({ id }) => id === "project-batch-acceptance")?.passed === true;
const virtualHalSimConfigSourceCoverageReady = virtualHalSimConfigSourceCoverage === null || const virtualHalSimConfigSourceCoverageReady = virtualHalSimConfigSourceCoverage === null ||
isVirtualHalSimConfigSourceCoverageReady(virtualHalSimConfigSourceCoverage); isVirtualHalSimConfigSourceCoverageReady(virtualHalSimConfigSourceCoverage);
const ready = capabilityMatrix.ready && batchGatePassed && virtualHalSimConfigSourceCoverageReady; const virtualHalMotionControllerMatrixReady = virtualHalMotionControllerMatrix === null ||
isVirtualHalMotionControllerMatrixReady(virtualHalMotionControllerMatrix);
const ready = capabilityMatrix.ready &&
batchGatePassed &&
virtualHalSimConfigSourceCoverageReady &&
virtualHalMotionControllerMatrixReady;
const missing = [ const missing = [
...capabilityMatrix.missing, ...capabilityMatrix.missing,
...(batchGatePassed ? [] : ["project-batch-acceptance"]), ...(batchGatePassed ? [] : ["project-batch-acceptance"]),
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtual-hal-sim-config-source-coverage"]), ...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtual-hal-sim-config-source-coverage"]),
...(virtualHalMotionControllerMatrixReady ? [] : ["virtual-hal-motion-controller-matrix"]),
]; ];
return { return {
@@ -618,6 +689,8 @@ export function createProjectBatchAcceptanceWorkflow({
rejectedCapabilities: capabilityMatrix.rejectedCapabilities, rejectedCapabilities: capabilityMatrix.rejectedCapabilities,
virtualHalSimConfigSourceCoverage, virtualHalSimConfigSourceCoverage,
virtualHalSimConfigSourceCoverageReady, virtualHalSimConfigSourceCoverageReady,
virtualHalMotionControllerMatrix,
virtualHalMotionControllerMatrixReady,
gateResultMatrix, gateResultMatrix,
missing, missing,
rows: [ rows: [
@@ -643,6 +716,13 @@ export function createProjectBatchAcceptanceWorkflow({
? "not required" ? "not required"
: (virtualHalSimConfigSourceCoverageReady ? "ready" : "missing"), : (virtualHalSimConfigSourceCoverageReady ? "ready" : "missing"),
}, },
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
value: virtualHalMotionControllerMatrix === null
? "not required"
: (virtualHalMotionControllerMatrixReady ? "ready" : "missing"),
},
{ {
id: "missing", id: "missing",
label: "Missing acceptance evidence", label: "Missing acceptance evidence",
@@ -785,6 +865,15 @@ export function createProjectBatchAcceptanceChecklist({
? "not required" ? "not required"
: (workflow?.virtualHalSimConfigSourceCoverageReady === true ? "ready" : "missing"), : (workflow?.virtualHalSimConfigSourceCoverageReady === true ? "ready" : "missing"),
}, },
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
status: workflow?.virtualHalMotionControllerMatrixReady !== false ? "pass" : "blocked",
passed: workflow?.virtualHalMotionControllerMatrixReady !== false,
detail: workflow?.virtualHalMotionControllerMatrix === null
? "not required"
: (workflow?.virtualHalMotionControllerMatrixReady === true ? "ready" : "missing"),
},
{ {
id: "next-action", id: "next-action",
label: "Next acceptance action", label: "Next acceptance action",
@@ -817,6 +906,7 @@ export function createProjectBatchAcceptanceReport({
gateResults = {}, gateResults = {},
observedOutputs = [], observedOutputs = [],
virtualHalSimConfigSourceCoverage = null, virtualHalSimConfigSourceCoverage = null,
virtualHalMotionControllerMatrix = null,
} = {}) { } = {}) {
const capabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({ capabilities }); const capabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({ capabilities });
const workflow = createProjectBatchAcceptanceWorkflow({ const workflow = createProjectBatchAcceptanceWorkflow({
@@ -825,6 +915,7 @@ export function createProjectBatchAcceptanceReport({
gateResults, gateResults,
observedOutputs, observedOutputs,
virtualHalSimConfigSourceCoverage, virtualHalSimConfigSourceCoverage,
virtualHalMotionControllerMatrix,
}); });
const summaryViewModel = createProjectBatchAcceptanceSummaryViewModel(workflow); const summaryViewModel = createProjectBatchAcceptanceSummaryViewModel(workflow);
const actionPlan = createProjectBatchAcceptanceActionPlan(workflow); const actionPlan = createProjectBatchAcceptanceActionPlan(workflow);
@@ -840,6 +931,7 @@ export function createProjectBatchAcceptanceReport({
capabilityMatrix, capabilityMatrix,
workflow, workflow,
virtualHalSimConfigSourceCoverage, virtualHalSimConfigSourceCoverage,
virtualHalMotionControllerMatrix,
summaryViewModel, summaryViewModel,
actionPlan, actionPlan,
checklist, checklist,
@@ -876,11 +968,16 @@ export function createProjectBatchAcceptanceReportValidation(report = {}) {
const actionPlan = objectOrEmpty(reportObject.actionPlan); const actionPlan = objectOrEmpty(reportObject.actionPlan);
const checklist = objectOrEmpty(reportObject.checklist); const checklist = objectOrEmpty(reportObject.checklist);
const virtualHalSimConfigSourceCoverage = objectOrEmpty(reportObject.virtualHalSimConfigSourceCoverage); const virtualHalSimConfigSourceCoverage = objectOrEmpty(reportObject.virtualHalSimConfigSourceCoverage);
const virtualHalMotionControllerMatrix = objectOrEmpty(reportObject.virtualHalMotionControllerMatrix);
const rows = arrayOrEmpty(reportObject.rows); const rows = arrayOrEmpty(reportObject.rows);
const virtualHalSimConfigSourceCoverageRequired = reportObject.virtualHalSimConfigSourceCoverage !== null && const virtualHalSimConfigSourceCoverageRequired = reportObject.virtualHalSimConfigSourceCoverage !== null &&
reportObject.virtualHalSimConfigSourceCoverage !== undefined; reportObject.virtualHalSimConfigSourceCoverage !== undefined;
const virtualHalSimConfigSourceCoverageReady = !virtualHalSimConfigSourceCoverageRequired || const virtualHalSimConfigSourceCoverageReady = !virtualHalSimConfigSourceCoverageRequired ||
isVirtualHalSimConfigSourceCoverageReady(virtualHalSimConfigSourceCoverage); isVirtualHalSimConfigSourceCoverageReady(virtualHalSimConfigSourceCoverage);
const virtualHalMotionControllerMatrixRequired = reportObject.virtualHalMotionControllerMatrix !== null &&
reportObject.virtualHalMotionControllerMatrix !== undefined;
const virtualHalMotionControllerMatrixReady = !virtualHalMotionControllerMatrixRequired ||
isVirtualHalMotionControllerMatrixReady(virtualHalMotionControllerMatrix);
const missing = [ const missing = [
...(reportObject.apiName === "project-batch-acceptance-report" ? [] : ["apiName"]), ...(reportObject.apiName === "project-batch-acceptance-report" ? [] : ["apiName"]),
...(reportObject.reportVersion === 1 ? [] : ["reportVersion"]), ...(reportObject.reportVersion === 1 ? [] : ["reportVersion"]),
@@ -905,7 +1002,8 @@ export function createProjectBatchAcceptanceReportValidation(report = {}) {
? [] ? []
: ["checklist"]), : ["checklist"]),
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]), ...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]),
...(rows.find(({ id, value }) => id === "checklist" && /^([34])\/\1 passed$/.test(value)) ...(virtualHalMotionControllerMatrixReady ? [] : ["virtualHalMotionControllerMatrix"]),
...(rows.find(({ id, value }) => id === "checklist" && /^([45])\/\1 passed$/.test(value))
? [] ? []
: ["rows.checklist"]), : ["rows.checklist"]),
]; ];
@@ -919,6 +1017,8 @@ export function createProjectBatchAcceptanceReportValidation(report = {}) {
batchId: reportObject.batchId ?? null, batchId: reportObject.batchId ?? null,
reportApiName: reportObject.apiName ?? null, reportApiName: reportObject.apiName ?? null,
reportVersion: reportObject.reportVersion ?? null, reportVersion: reportObject.reportVersion ?? null,
virtualHalSimConfigSourceCoverageReady,
virtualHalMotionControllerMatrixReady,
rows: [ rows: [
{ {
id: "report", id: "report",
@@ -947,6 +1047,13 @@ export function createProjectBatchAcceptanceReportValidation(report = {}) {
? (virtualHalSimConfigSourceCoverageReady ? "ready" : "missing") ? (virtualHalSimConfigSourceCoverageReady ? "ready" : "missing")
: "not required", : "not required",
}, },
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
value: virtualHalMotionControllerMatrixRequired
? (virtualHalMotionControllerMatrixReady ? "ready" : "missing")
: "not required",
},
{ {
id: "missing", id: "missing",
label: "Missing report evidence", label: "Missing report evidence",
@@ -1404,6 +1511,7 @@ export function createProjectReleaseReadinessReport({
observedOutputs = [], observedOutputs = [],
simConfigInventory = DEFAULT_SIM_CONFIG_INVENTORY_BASELINE, simConfigInventory = DEFAULT_SIM_CONFIG_INVENTORY_BASELINE,
virtualHalSimConfigSourceCoverage = null, virtualHalSimConfigSourceCoverage = null,
virtualHalMotionControllerMatrix = null,
blockedRuntimeFamilies = BLOCKED_RUNTIME_FAMILIES, blockedRuntimeFamilies = BLOCKED_RUNTIME_FAMILIES,
promotedRuntimeFamilies = [], promotedRuntimeFamilies = [],
axisScreenshotArtifacts = [], axisScreenshotArtifacts = [],
@@ -1439,18 +1547,23 @@ export function createProjectReleaseReadinessReport({
const virtualHalSimConfigSourceCoverageReady = isVirtualHalSimConfigSourceCoverageReady( const virtualHalSimConfigSourceCoverageReady = isVirtualHalSimConfigSourceCoverageReady(
virtualHalSimConfigSourceCoverage, virtualHalSimConfigSourceCoverage,
); );
const virtualHalMotionControllerMatrixReady = isVirtualHalMotionControllerMatrixReady(
virtualHalMotionControllerMatrix,
);
const blockedRuntimeReady = promotedBlockedFamilies.length === 0; const blockedRuntimeReady = promotedBlockedFamilies.length === 0;
const axisScreenshotArtifactSummary = createAxisScreenshotArtifactSummary(axisScreenshotArtifacts); const axisScreenshotArtifactSummary = createAxisScreenshotArtifactSummary(axisScreenshotArtifacts);
const axisScreenshotArtifactsReady = axisScreenshotArtifactSummary.ready; const axisScreenshotArtifactsReady = axisScreenshotArtifactSummary.ready;
const ready = releaseGatePassed && const ready = releaseGatePassed &&
inventoryReady && inventoryReady &&
virtualHalSimConfigSourceCoverageReady && virtualHalSimConfigSourceCoverageReady &&
virtualHalMotionControllerMatrixReady &&
blockedRuntimeReady && blockedRuntimeReady &&
axisScreenshotArtifactsReady; axisScreenshotArtifactsReady;
const missing = [ const missing = [
...(releaseGatePassed ? [] : ["project-release-gate"]), ...(releaseGatePassed ? [] : ["project-release-gate"]),
...(inventoryReady ? [] : ["sim-config-inventory"]), ...(inventoryReady ? [] : ["sim-config-inventory"]),
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtual-hal-sim-config-source-coverage"]), ...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtual-hal-sim-config-source-coverage"]),
...(virtualHalMotionControllerMatrixReady ? [] : ["virtual-hal-motion-controller-matrix"]),
...(blockedRuntimeReady ? [] : ["blocked-runtime-families"]), ...(blockedRuntimeReady ? [] : ["blocked-runtime-families"]),
...(axisScreenshotArtifactsReady ? [] : ["axis-screenshot-artifacts"]), ...(axisScreenshotArtifactsReady ? [] : ["axis-screenshot-artifacts"]),
]; ];
@@ -1469,6 +1582,8 @@ export function createProjectReleaseReadinessReport({
simConfigInventory: inventory, simConfigInventory: inventory,
virtualHalSimConfigSourceCoverage, virtualHalSimConfigSourceCoverage,
virtualHalSimConfigSourceCoverageReady, virtualHalSimConfigSourceCoverageReady,
virtualHalMotionControllerMatrix,
virtualHalMotionControllerMatrixReady,
blockedRuntimeFamilies: blockedFamilies, blockedRuntimeFamilies: blockedFamilies,
promotedRuntimeFamilies: promotedFamilies, promotedRuntimeFamilies: promotedFamilies,
promotedBlockedFamilies, promotedBlockedFamilies,
@@ -1495,6 +1610,11 @@ export function createProjectReleaseReadinessReport({
label: "Virtual HAL sim-config source coverage", label: "Virtual HAL sim-config source coverage",
value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing", value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing",
}, },
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
value: virtualHalMotionControllerMatrixReady ? "ready" : "missing",
},
{ {
id: "blocked-runtime-families", id: "blocked-runtime-families",
label: "Blocked runtime families", label: "Blocked runtime families",
@@ -1518,6 +1638,7 @@ export function createProjectReleaseReadinessSummaryViewModel(
const gateResultMatrix = objectOrEmpty(report?.gateResultMatrix); const gateResultMatrix = objectOrEmpty(report?.gateResultMatrix);
const simConfigInventory = objectOrEmpty(report?.simConfigInventory); const simConfigInventory = objectOrEmpty(report?.simConfigInventory);
const virtualHalSimConfigSourceCoverageReady = report?.virtualHalSimConfigSourceCoverageReady === true; const virtualHalSimConfigSourceCoverageReady = report?.virtualHalSimConfigSourceCoverageReady === true;
const virtualHalMotionControllerMatrixReady = report?.virtualHalMotionControllerMatrixReady === true;
const axisScreenshotArtifactSummary = objectOrEmpty(report?.axisScreenshotArtifactSummary); const axisScreenshotArtifactSummary = objectOrEmpty(report?.axisScreenshotArtifactSummary);
const blockedRuntimeFamilies = arrayOrEmpty(report?.blockedRuntimeFamilies); const blockedRuntimeFamilies = arrayOrEmpty(report?.blockedRuntimeFamilies);
const promotedBlockedFamilies = arrayOrEmpty(report?.promotedBlockedFamilies); const promotedBlockedFamilies = arrayOrEmpty(report?.promotedBlockedFamilies);
@@ -1554,6 +1675,11 @@ export function createProjectReleaseReadinessSummaryViewModel(
label: "Virtual HAL sim-config source coverage", label: "Virtual HAL sim-config source coverage",
value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing", value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing",
}, },
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
value: virtualHalMotionControllerMatrixReady ? "ready" : "missing",
},
{ {
id: "blocked-runtime-families", id: "blocked-runtime-families",
label: "Blocked runtime families", label: "Blocked runtime families",
@@ -1636,6 +1762,11 @@ export function createProjectReleaseReadinessArtifactValidationSummaryViewModel(
label: "Virtual HAL sim-config source coverage", label: "Virtual HAL sim-config source coverage",
value: validation?.virtualHalSimConfigSourceCoverageReady === true ? "ready" : "missing", value: validation?.virtualHalSimConfigSourceCoverageReady === true ? "ready" : "missing",
}, },
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
value: validation?.virtualHalMotionControllerMatrixReady === true ? "ready" : "missing",
},
{ {
id: "missing", id: "missing",
label: "Missing artifact evidence", label: "Missing artifact evidence",
@@ -1790,6 +1921,7 @@ export function createProjectReleaseReadinessArtifactJsonWorkflow({ artifactJson
export async function loadProjectReleaseReadinessArtifactUrlWorkflow({ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
artifactUrl = "", artifactUrl = "",
diagnosticsUrl = "", diagnosticsUrl = "",
requireSavedSessionDiagnostics = false,
fetchRef = globalThis.fetch, fetchRef = globalThis.fetch,
} = {}) { } = {}) {
if (!artifactUrl || typeof fetchRef !== "function") { if (!artifactUrl || typeof fetchRef !== "function") {
@@ -1810,6 +1942,7 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
ready: false, ready: false,
artifactUrl, artifactUrl,
diagnosticsUrl, diagnosticsUrl,
requireSavedSessionDiagnostics,
fetched: false, fetched: false,
diagnosticsFetched: false, diagnosticsFetched: false,
httpStatus: null, httpStatus: null,
@@ -1855,6 +1988,7 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
ready: false, ready: false,
artifactUrl, artifactUrl,
diagnosticsUrl, diagnosticsUrl,
requireSavedSessionDiagnostics,
fetched: false, fetched: false,
diagnosticsFetched: false, diagnosticsFetched: false,
httpStatus, httpStatus,
@@ -1911,6 +2045,9 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
const diagnosticsArtifact = JSON.parse(await diagnosticsResponse.text()); const diagnosticsArtifact = JSON.parse(await diagnosticsResponse.text());
diagnosticsValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation(diagnosticsArtifact); diagnosticsValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation(diagnosticsArtifact);
diagnosticsMissing.push(...diagnosticsValidation.missing.map((item) => `diagnostics.${item}`)); diagnosticsMissing.push(...diagnosticsValidation.missing.map((item) => `diagnostics.${item}`));
if (requireSavedSessionDiagnostics && diagnosticsValidation.hasSessionDiagnostics !== true) {
diagnosticsMissing.push("diagnostics.virtualHalSessionDiagnostics");
}
} }
} catch (error) { } catch (error) {
diagnosticsFetchError = error?.message ?? "diagnostics artifact URL fetch error"; diagnosticsFetchError = error?.message ?? "diagnostics artifact URL fetch error";
@@ -1923,7 +2060,7 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
...diagnosticsMissing, ...diagnosticsMissing,
]; ];
const ready = jsonWorkflow.ready === true && const ready = jsonWorkflow.ready === true &&
(!diagnosticsUrl || diagnosticsValidation?.ready === true); (!diagnosticsUrl || (diagnosticsValidation?.ready === true && diagnosticsMissing.length === 0));
return { return {
apiName: "project-release-readiness-artifact-url-workflow", apiName: "project-release-readiness-artifact-url-workflow",
workflowVersion: 1, workflowVersion: 1,
@@ -1931,6 +2068,7 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
ready, ready,
artifactUrl, artifactUrl,
diagnosticsUrl, diagnosticsUrl,
requireSavedSessionDiagnostics,
fetched: true, fetched: true,
diagnosticsFetched, diagnosticsFetched,
httpStatus, httpStatus,
@@ -1977,6 +2115,7 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
ready: false, ready: false,
artifactUrl, artifactUrl,
diagnosticsUrl, diagnosticsUrl,
requireSavedSessionDiagnostics,
fetched: false, fetched: false,
diagnosticsFetched: false, diagnosticsFetched: false,
httpStatus: null, httpStatus: null,
@@ -2060,6 +2199,15 @@ export function createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel
? (workflow?.diagnosticsValidation?.ready === true ? "ready" : "blocked") ? (workflow?.diagnosticsValidation?.ready === true ? "ready" : "blocked")
: "not requested", : "not requested",
}, },
{
id: "saved-session-diagnostics",
label: "Saved-session diagnostics",
value: workflow?.diagnosticsUrl
? (workflow?.diagnosticsValidation?.hasSessionDiagnostics
? (workflow?.diagnosticsValidation?.sessionDiagnosticsReady === true ? "ready" : "blocked")
: "not provided")
: "not requested",
},
{ {
id: "next-command", id: "next-command",
label: "Next command", label: "Next command",
@@ -2170,6 +2318,7 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
const releaseGate = objectOrEmpty(artifactObject.releaseGate); const releaseGate = objectOrEmpty(artifactObject.releaseGate);
const simConfigInventory = objectOrEmpty(artifactObject.simConfigInventory); const simConfigInventory = objectOrEmpty(artifactObject.simConfigInventory);
const virtualHalSimConfigSourceCoverage = objectOrEmpty(artifactObject.virtualHalSimConfigSourceCoverage); const virtualHalSimConfigSourceCoverage = objectOrEmpty(artifactObject.virtualHalSimConfigSourceCoverage);
const virtualHalMotionControllerMatrix = objectOrEmpty(artifactObject.virtualHalMotionControllerMatrix);
const gateManifest = objectOrEmpty(artifactObject.gateManifest); const gateManifest = objectOrEmpty(artifactObject.gateManifest);
const gateExecutionManifest = objectOrEmpty(artifactObject.gateExecutionManifest); const gateExecutionManifest = objectOrEmpty(artifactObject.gateExecutionManifest);
const gateExecutionSummaryViewModel = objectOrEmpty(artifactObject.gateExecutionSummaryViewModel); const gateExecutionSummaryViewModel = objectOrEmpty(artifactObject.gateExecutionSummaryViewModel);
@@ -2218,6 +2367,9 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
const virtualHalSimConfigSourceCoverageReady = isVirtualHalSimConfigSourceCoverageReady( const virtualHalSimConfigSourceCoverageReady = isVirtualHalSimConfigSourceCoverageReady(
virtualHalSimConfigSourceCoverage, virtualHalSimConfigSourceCoverage,
) && artifactObject.virtualHalSimConfigSourceCoverageReady === true; ) && artifactObject.virtualHalSimConfigSourceCoverageReady === true;
const virtualHalMotionControllerMatrixReady = isVirtualHalMotionControllerMatrixReady(
virtualHalMotionControllerMatrix,
) && artifactObject.virtualHalMotionControllerMatrixReady === true;
const axisScreenshotArtifacts = arrayOrEmpty(axisScreenshotArtifactSummary.artifacts); const axisScreenshotArtifacts = arrayOrEmpty(axisScreenshotArtifactSummary.artifacts);
const axisScreenshotSummaryReady = !axisScreenshotArtifactSummary.apiName const axisScreenshotSummaryReady = !axisScreenshotArtifactSummary.apiName
|| axisScreenshotArtifacts.length === 0 || axisScreenshotArtifacts.length === 0
@@ -2256,6 +2408,7 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
...(simConfigInventory.skipped === 131 ? [] : ["simConfigInventory.skipped"]), ...(simConfigInventory.skipped === 131 ? [] : ["simConfigInventory.skipped"]),
...(simConfigInventory.unexpectedFail === 0 ? [] : ["simConfigInventory.unexpectedFail"]), ...(simConfigInventory.unexpectedFail === 0 ? [] : ["simConfigInventory.unexpectedFail"]),
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]), ...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]),
...(virtualHalMotionControllerMatrixReady ? [] : ["virtualHalMotionControllerMatrix"]),
...(arrayOrEmpty(artifactObject.blockedRuntimeFamilies).join(",") === BLOCKED_RUNTIME_FAMILIES.join(",") ...(arrayOrEmpty(artifactObject.blockedRuntimeFamilies).join(",") === BLOCKED_RUNTIME_FAMILIES.join(",")
? [] ? []
: ["blockedRuntimeFamilies"]), : ["blockedRuntimeFamilies"]),
@@ -2290,6 +2443,7 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
gateResultMatrixReady: matrixReady, gateResultMatrixReady: matrixReady,
gateActionPlanReady: actionPlanReady, gateActionPlanReady: actionPlanReady,
virtualHalSimConfigSourceCoverageReady, virtualHalSimConfigSourceCoverageReady,
virtualHalMotionControllerMatrixReady,
axisScreenshotArtifactSummaryReady: axisScreenshotSummaryReady, axisScreenshotArtifactSummaryReady: axisScreenshotSummaryReady,
axisScreenshotArtifactCount: axisScreenshotArtifacts.length, axisScreenshotArtifactCount: axisScreenshotArtifacts.length,
blockedRuntimeFamilies: arrayOrEmpty(artifactObject.blockedRuntimeFamilies), blockedRuntimeFamilies: arrayOrEmpty(artifactObject.blockedRuntimeFamilies),
@@ -2334,6 +2488,11 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
label: "Virtual HAL sim-config source coverage", label: "Virtual HAL sim-config source coverage",
value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing", value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing",
}, },
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
value: virtualHalMotionControllerMatrixReady ? "ready" : "missing",
},
{ {
id: "axis-screenshot-artifacts", id: "axis-screenshot-artifacts",
label: "AXIS screenshot artifacts", label: "AXIS screenshot artifacts",

View File

@@ -2560,6 +2560,15 @@ export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWo
? (workflow?.diagnosticsValidation?.ready === true ? "ready" : "blocked") ? (workflow?.diagnosticsValidation?.ready === true ? "ready" : "blocked")
: "not requested", : "not requested",
}, },
{
id: "saved-session-diagnostics",
label: "Saved-session diagnostics",
value: workflow?.diagnosticsUrl
? (workflow?.diagnosticsValidation?.hasSessionDiagnostics
? (workflow?.diagnosticsValidation?.sessionDiagnosticsReady === true ? "ready" : "blocked")
: "not provided")
: "not requested",
},
{ {
id: "diagnostics-status", id: "diagnostics-status",
label: "Diagnostics HTTP status", label: "Diagnostics HTTP status",

View File

@@ -127,6 +127,14 @@
<p data-workflow-overview-release-readiness-artifact-url-workflow-status>Waiting for release artifact URL workflow.</p> <p data-workflow-overview-release-readiness-artifact-url-workflow-status>Waiting for release artifact URL workflow.</p>
<dl data-workflow-overview-release-readiness-artifact-url-workflow-rows></dl> <dl data-workflow-overview-release-readiness-artifact-url-workflow-rows></dl>
</section> </section>
<section class="overview-status" aria-label="Release artifact URL workflow action plan" data-workflow-overview-release-readiness-artifact-url-workflow-action-plan>
<p data-workflow-overview-release-readiness-artifact-url-workflow-action-plan-status>Waiting for URL workflow action plan.</p>
<dl data-workflow-overview-release-readiness-artifact-url-workflow-action-plan-rows></dl>
</section>
<section class="overview-status" aria-label="Release artifact URL gate execution summary" data-workflow-overview-release-readiness-artifact-gate-execution-summary>
<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> </section>
</main> </main>
<script type="module"> <script type="module">
@@ -170,6 +178,11 @@
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness,
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState, renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState, mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomContract,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomReadiness,
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState,
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel,
@@ -216,6 +229,25 @@
renderState, renderState,
documentRef: document, documentRef: document,
}); });
const actionPlan = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan(workflow);
const actionPlanRenderState =
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState(actionPlan);
const actionPlanMountResult =
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState({
renderState: actionPlanRenderState,
documentRef: document,
});
const gateExecutionSummary =
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel(workflow);
const gateExecutionSummaryRenderState =
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState(
gateExecutionSummary,
);
const gateExecutionSummaryMountResult =
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState({
renderState: gateExecutionSummaryRenderState,
documentRef: document,
});
document.body.dataset.releaseArtifactUrlWorkflowPhase = workflow.phase; document.body.dataset.releaseArtifactUrlWorkflowPhase = workflow.phase;
document.body.dataset.releaseArtifactUrlWorkflowReady = workflow.ready ? "true" : "false"; document.body.dataset.releaseArtifactUrlWorkflowReady = workflow.ready ? "true" : "false";
return { return {
@@ -223,6 +255,12 @@
summary, summary,
renderState, renderState,
mountResult, mountResult,
actionPlan,
actionPlanRenderState,
actionPlanMountResult,
gateExecutionSummary,
gateExecutionSummaryRenderState,
gateExecutionSummaryMountResult,
}; };
} }
@@ -324,6 +362,28 @@
options ?? { documentRef: document }, options ?? { documentRef: document },
) )
), ),
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState: (actionPlan) => (
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState(actionPlan)
),
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomContract: (options) => (
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomContract(options)
),
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomReadiness: (options) => (
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomReadiness(
options ?? { documentRef: document },
)
),
renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState: (renderState, options) => (
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState(
renderState,
options ?? { documentRef: document },
)
),
mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState: (options) => (
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState(
options ?? { documentRef: document },
)
),
getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel: (validation) => ( getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel: (validation) => (
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel(validation) createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel(validation)
), ),

View File

@@ -1805,6 +1805,8 @@ M2
import { import {
DEFAULT_SIMULATION_PROGRAM_ID, DEFAULT_SIMULATION_PROGRAM_ID,
VIRTUAL_HAL_AXES as SHARED_VIRTUAL_HAL_AXES, VIRTUAL_HAL_AXES as SHARED_VIRTUAL_HAL_AXES,
VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES as SHARED_VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES,
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS as SHARED_VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
applyVirtualHalAction as applySharedVirtualHalAction, applyVirtualHalAction as applySharedVirtualHalAction,
applyVirtualHalPinUpdates as applySharedVirtualHalPinUpdates, applyVirtualHalPinUpdates as applySharedVirtualHalPinUpdates,
cloneVirtualHalState as cloneSharedVirtualHalState, cloneVirtualHalState as cloneSharedVirtualHalState,
@@ -1815,6 +1817,7 @@ M2
createVirtualHalMachineStatusState as createSharedVirtualHalMachineStatusState, createVirtualHalMachineStatusState as createSharedVirtualHalMachineStatusState,
createVirtualHalMotionControllerMatrixReport as createSharedVirtualHalMotionControllerMatrixReport, createVirtualHalMotionControllerMatrixReport as createSharedVirtualHalMotionControllerMatrixReport,
createVirtualHalPinRegistry as createSharedVirtualHalPinRegistry, createVirtualHalPinRegistry as createSharedVirtualHalPinRegistry,
createVirtualHalSimConfigPromotionCandidateReport as createSharedVirtualHalSimConfigPromotionCandidateReport,
createVirtualHalSimConfigSourceCoverageReport as createSharedVirtualHalSimConfigSourceCoverageReport, createVirtualHalSimConfigSourceCoverageReport as createSharedVirtualHalSimConfigSourceCoverageReport,
createVirtualHalSimulationReplacementReport as createSharedVirtualHalSimulationReplacementReport, createVirtualHalSimulationReplacementReport as createSharedVirtualHalSimulationReplacementReport,
createVirtualHalSimulationRuntimeReport as createSharedVirtualHalSimulationRuntimeReport, createVirtualHalSimulationRuntimeReport as createSharedVirtualHalSimulationRuntimeReport,
@@ -1983,6 +1986,8 @@ M2
}; };
const statusHistory = []; const statusHistory = [];
const VIRTUAL_HAL_AXES = SHARED_VIRTUAL_HAL_AXES; const VIRTUAL_HAL_AXES = SHARED_VIRTUAL_HAL_AXES;
const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = SHARED_VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES;
const VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES = SHARED_VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS.flatMap((target) => target.sourceFiles ?? []);
let virtualHalState = createVirtualHalState(); let virtualHalState = createVirtualHalState();
function setAllText(selector, value) { function setAllText(selector, value) {
@@ -2517,12 +2522,23 @@ M2
return createSharedVirtualHalSimConfigSourceCoverageReport(); return createSharedVirtualHalSimConfigSourceCoverageReport();
} }
function createVirtualHalSimConfigPromotionCandidateReport() {
return createSharedVirtualHalSimConfigPromotionCandidateReport({
manifestEntries: [
...VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
...VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES.flatMap((candidate) => candidate.sourceFiles ?? []),
],
});
}
function createVirtualHalCommandScriptFixtureReport() { function createVirtualHalCommandScriptFixtureReport() {
return createSharedVirtualHalCommandScriptFixtureReport({ halState: virtualHalState }); return createSharedVirtualHalCommandScriptFixtureReport({ halState: virtualHalState });
} }
function createVirtualHalMotionControllerMatrixReport() { function createVirtualHalMotionControllerMatrixReport() {
return createSharedVirtualHalMotionControllerMatrixReport(); return createSharedVirtualHalMotionControllerMatrixReport({
manifestEntries: VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
});
} }
function renderMachineStatusDom(machineStatus) { function renderMachineStatusDom(machineStatus) {
@@ -2613,6 +2629,7 @@ M2
virtualHalSimulationReplacement: createVirtualHalSimulationReplacementReport(), virtualHalSimulationReplacement: createVirtualHalSimulationReplacementReport(),
virtualHalSourceCompliance: createVirtualHalSourceComplianceReport(), virtualHalSourceCompliance: createVirtualHalSourceComplianceReport(),
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(), virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
virtualHalSimConfigPromotionCandidates: createVirtualHalSimConfigPromotionCandidateReport(),
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport(), virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport(),
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport(), virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport(),
virtualHalSessionDiagnostics: createVirtualHalSessionDiagnosticsArtifact(), virtualHalSessionDiagnostics: createVirtualHalSessionDiagnosticsArtifact(),
@@ -3578,7 +3595,9 @@ M2
async function saveVirtualHalSessionSnapshot(options = {}) { async function saveVirtualHalSessionSnapshot(options = {}) {
const session = virtualHalSessionOptions(options); const session = virtualHalSessionOptions(options);
const virtualHal = createVirtualHalSessionPayload(virtualHalState); const virtualHal = createVirtualHalSessionPayload(virtualHalState, {
manifestEntries: VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
});
const payload = createMachineSessionSnapshotPayload(session.machineId, { const payload = createMachineSessionSnapshotPayload(session.machineId, {
...(session.gcodeFilename ? { gcodeFilename: session.gcodeFilename } : {}), ...(session.gcodeFilename ? { gcodeFilename: session.gcodeFilename } : {}),
virtualHal, virtualHal,
@@ -4042,6 +4061,7 @@ M2
getVirtualHalSimulationReplacementReport: () => createVirtualHalSimulationReplacementReport(), getVirtualHalSimulationReplacementReport: () => createVirtualHalSimulationReplacementReport(),
getVirtualHalSourceComplianceReport: () => createVirtualHalSourceComplianceReport(), getVirtualHalSourceComplianceReport: () => createVirtualHalSourceComplianceReport(),
getVirtualHalSimConfigSourceCoverageReport: () => createVirtualHalSimConfigSourceCoverageReport(), getVirtualHalSimConfigSourceCoverageReport: () => createVirtualHalSimConfigSourceCoverageReport(),
getVirtualHalSimConfigPromotionCandidateReport: () => createVirtualHalSimConfigPromotionCandidateReport(),
getVirtualHalCommandScriptFixtureReport: () => createVirtualHalCommandScriptFixtureReport(), getVirtualHalCommandScriptFixtureReport: () => createVirtualHalCommandScriptFixtureReport(),
getVirtualHalMotionControllerMatrixReport: () => createVirtualHalMotionControllerMatrixReport(), getVirtualHalMotionControllerMatrixReport: () => createVirtualHalMotionControllerMatrixReport(),
getVirtualRealtimeHalRuntimeReport: () => createVirtualHalSimulationReplacementReport(), getVirtualRealtimeHalRuntimeReport: () => createVirtualHalSimulationReplacementReport(),

View File

@@ -19,6 +19,8 @@ export {
VIRTUAL_HAL_AXES, VIRTUAL_HAL_AXES,
VIRTUAL_HAL_AXISUI_PINS, VIRTUAL_HAL_AXISUI_PINS,
VIRTUAL_HAL_MOTION_CONTROLLER_MATRIX_FIXTURES, VIRTUAL_HAL_MOTION_CONTROLLER_MATRIX_FIXTURES,
VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES,
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS, VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS,
VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES, VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES,
applyVirtualHalAction, applyVirtualHalAction,
@@ -32,6 +34,7 @@ export {
createVirtualHalLimitsHomeState, createVirtualHalLimitsHomeState,
createVirtualHalMachineStatusState, createVirtualHalMachineStatusState,
createVirtualHalPinRegistry, createVirtualHalPinRegistry,
createVirtualHalSimConfigPromotionCandidateReport,
createVirtualHalSimConfigSourceCoverageReport, createVirtualHalSimConfigSourceCoverageReport,
createVirtualHalSimulationReplacementReport, createVirtualHalSimulationReplacementReport,
createVirtualHalSimulationRuntimeReport, createVirtualHalSimulationRuntimeReport,

View File

@@ -8,14 +8,19 @@
<pre id="status">running</pre> <pre id="status">running</pre>
<script type="module"> <script type="module">
import { import {
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
createVirtualHalCommandScriptFixtureReport, createVirtualHalCommandScriptFixtureReport,
createVirtualHalMotionControllerMatrixReport, createVirtualHalMotionControllerMatrixReport,
createVirtualHalSimConfigPromotionCandidateReport,
createVirtualHalSimConfigSourceCoverageReport, createVirtualHalSimConfigSourceCoverageReport,
createVirtualHalSimulationReplacementReport, createVirtualHalSimulationReplacementReport,
createVirtualHalSourceComplianceReport, createVirtualHalSourceComplianceReport,
createVirtualHalState, createVirtualHalState,
} from "../../runtime/sdk/src/index.js"; } from "../../runtime/sdk/src/index.js";
const VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES =
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS.flatMap((target) => target.sourceFiles ?? []);
function assertEqual(actual, expected, label) { function assertEqual(actual, expected, label) {
if (actual !== expected) { if (actual !== expected) {
throw new Error(`${label}: expected ${expected}, got ${actual}`); throw new Error(`${label}: expected ${expected}, got ${actual}`);
@@ -60,6 +65,11 @@
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness || !workflowApi?.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness ||
!workflowApi?.renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState || !workflowApi?.renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState ||
!workflowApi?.mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState || !workflowApi?.mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState ||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState ||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomContract ||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomReadiness ||
!workflowApi?.renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState ||
!workflowApi?.mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState ||
!workflowApi?.runWorkflowOverviewReleaseReadinessArtifactUrlForm || !workflowApi?.runWorkflowOverviewReleaseReadinessArtifactUrlForm ||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel || !workflowApi?.getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel ||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan || !workflowApi?.getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan ||
@@ -348,16 +358,49 @@
renderState: releaseArtifactJsonWorkflowRenderState, renderState: releaseArtifactJsonWorkflowRenderState,
}); });
const diagnosticsVirtualHalState = createVirtualHalState(); const diagnosticsVirtualHalState = createVirtualHalState();
const diagnosticsArtifactJson = JSON.stringify({ const diagnosticsArtifact = {
apiName: "real-browser-simulation-diagnostics-artifact", apiName: "real-browser-simulation-diagnostics-artifact",
artifactVersion: 1, artifactVersion: 1,
virtualHal: diagnosticsVirtualHalState, virtualHal: diagnosticsVirtualHalState,
virtualHalSimulationReplacement: createVirtualHalSimulationReplacementReport(diagnosticsVirtualHalState), virtualHalSimulationReplacement: createVirtualHalSimulationReplacementReport(diagnosticsVirtualHalState),
virtualHalSourceCompliance: createVirtualHalSourceComplianceReport({ halState: diagnosticsVirtualHalState }), virtualHalSourceCompliance: createVirtualHalSourceComplianceReport({ halState: diagnosticsVirtualHalState }),
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(), 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: createVirtualHalMotionControllerMatrixReport({
manifestEntries: VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
}),
}),
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport({ halState: diagnosticsVirtualHalState }), virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport({ halState: diagnosticsVirtualHalState }),
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport(), virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport({
}); manifestEntries: VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
}),
};
const diagnosticsSessionNestedArtifact = { ...diagnosticsArtifact };
diagnosticsArtifact.virtualHalSessionDiagnostics = {
apiName: "real-browser-simulation-virtual-hal-session-diagnostics",
artifactVersion: 1,
ready: true,
phase: "release-ready",
sessionId: "workflow-overview-virtual-hal-session",
machineId: "workflow-overview-machine",
createdAt: "2026-06-17T00:00:00.000Z",
metadata: { source: "workflow-overview-url-fixture" },
files: {},
virtualHalRevision: diagnosticsVirtualHalState.revision,
validation: workflowApi.validateBrowserDiagnosticsArtifact?.(diagnosticsSessionNestedArtifact) ?? { ready: true },
diagnosticsArtifact: diagnosticsSessionNestedArtifact,
};
const diagnosticsArtifactJson = JSON.stringify(diagnosticsArtifact);
const releaseArtifactUrlWorkflow = await workflowApi.loadWorkflowOverviewReleaseReadinessArtifactUrl({ const releaseArtifactUrlWorkflow = await workflowApi.loadWorkflowOverviewReleaseReadinessArtifactUrl({
artifactUrl: "../../tests/fixtures/project-release-readiness-ready.json", artifactUrl: "../../tests/fixtures/project-release-readiness-ready.json",
diagnosticsUrl: "../../tests/fixtures/real-browser-simulation-diagnostics-ready.json", diagnosticsUrl: "../../tests/fixtures/real-browser-simulation-diagnostics-ready.json",
@@ -489,6 +532,8 @@
assertEqual(releaseArtifactUrlWorkflow.httpStatus, 200, "workflow overview release artifact URL workflow status"); assertEqual(releaseArtifactUrlWorkflow.httpStatus, 200, "workflow overview release artifact URL workflow status");
assertEqual(releaseArtifactUrlWorkflow.diagnosticsHttpStatus, 200, "workflow overview diagnostics 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.diagnosticsValidation.ready, true, "workflow overview diagnostics artifact validation");
assertEqual(releaseArtifactUrlWorkflow.diagnosticsValidation.hasSessionDiagnostics, true, "workflow overview diagnostics saved-session presence");
assertEqual(releaseArtifactUrlWorkflow.diagnosticsValidation.sessionDiagnosticsReady, true, "workflow overview diagnostics saved-session validation");
assertEqual( assertEqual(
releaseArtifactUrlWorkflow.validationSummaryViewModel.statusLine, releaseArtifactUrlWorkflow.validationSummaryViewModel.statusLine,
"Ready: Artifact evidence complete", "Ready: Artifact evidence complete",
@@ -511,7 +556,7 @@
); );
assertEqual( assertEqual(
releaseArtifactUrlWorkflowRenderResult.rowCount, releaseArtifactUrlWorkflowRenderResult.rowCount,
11, 12,
"workflow overview release artifact URL workflow render rows", "workflow overview release artifact URL workflow render rows",
); );
assertEqual( assertEqual(
@@ -519,6 +564,11 @@
true, true,
"workflow overview release artifact URL workflow diagnostics row", "workflow overview release artifact URL workflow diagnostics row",
); );
assertEqual(
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("saved-session-diagnostics"),
true,
"workflow overview release artifact URL workflow saved-session diagnostics row",
);
assertEqual( assertEqual(
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("mount-result"), releaseArtifactUrlWorkflowRenderResult.rowIds.includes("mount-result"),
true, true,
@@ -544,6 +594,27 @@
true, true,
"workflow overview fixed URL workflow form mount readiness", "workflow overview fixed URL workflow form mount readiness",
); );
assertEqual(
releaseArtifactUrlFormResult.actionPlanMountResult.ready,
true,
"workflow overview fixed URL workflow action-plan mount readiness",
);
assertEqual(
releaseArtifactUrlFormResult.actionPlanRenderState.rowIds?.includes?.("next-command") ??
releaseArtifactUrlFormResult.actionPlanRenderState.rows.some(({ id }) => id === "next-command"),
true,
"workflow overview fixed URL workflow action-plan next-command row",
);
assertEqual(
releaseArtifactUrlFormResult.gateExecutionSummaryMountResult.ready,
true,
"workflow overview fixed URL workflow gate execution summary mount readiness",
);
assertEqual(
releaseArtifactUrlFormResult.gateExecutionSummaryRenderState.rows.some(({ id }) => id === "gate-ui-node-smokes"),
true,
"workflow overview fixed URL workflow gate execution summary row",
);
assertEqual( assertEqual(
workflowDoc.body.dataset.releaseArtifactUrlWorkflowReady, workflowDoc.body.dataset.releaseArtifactUrlWorkflowReady,
"true", "true",
@@ -554,6 +625,16 @@
"ready", "ready",
"workflow overview fixed URL workflow form browser diagnostics row", "workflow overview fixed URL workflow form browser diagnostics row",
); );
assertEqual(
workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-action-plan-value="next-command"]').textContent,
"none",
"workflow overview fixed URL workflow action-plan DOM next command",
);
assertEqual(
workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-gate-execution-summary-value="gate-ui-node-smokes"]')?.textContent.includes("passed: observed output matched"),
true,
"workflow overview fixed URL workflow gate execution summary DOM row",
);
assertEqual( assertEqual(
releaseArtifactJsonWorkflow.statusLine, releaseArtifactJsonWorkflow.statusLine,
"Ready: Artifact JSON workflow complete", "Ready: Artifact JSON workflow complete",

View File

@@ -243,6 +243,7 @@
!api?.getVirtualHalSimulationReplacementReport || !api?.getVirtualHalSimulationReplacementReport ||
!api?.getVirtualHalSourceComplianceReport || !api?.getVirtualHalSourceComplianceReport ||
!api?.getVirtualHalSimConfigSourceCoverageReport || !api?.getVirtualHalSimConfigSourceCoverageReport ||
!api?.getVirtualHalSimConfigPromotionCandidateReport ||
!api?.getVirtualHalCommandScriptFixtureReport || !api?.getVirtualHalCommandScriptFixtureReport ||
!api?.getVirtualHalMotionControllerMatrixReport || !api?.getVirtualHalMotionControllerMatrixReport ||
!api?.getVirtualRealtimeHalRuntimeReport !api?.getVirtualRealtimeHalRuntimeReport
@@ -418,6 +419,13 @@
!diagnosticsArtifact.virtualHalSourceCompliance?.sourceFiles?.includes("linuxcnc/src/hal/utils/halcmd_commands.cc") || !diagnosticsArtifact.virtualHalSourceCompliance?.sourceFiles?.includes("linuxcnc/src/hal/utils/halcmd_commands.cc") ||
diagnosticsArtifact.virtualHalSimConfigSourceCoverage?.complete !== true || diagnosticsArtifact.virtualHalSimConfigSourceCoverage?.complete !== true ||
!diagnosticsArtifact.virtualHalSimConfigSourceCoverage?.sourceFiles?.includes("linuxcnc/configs/sim/axis/foam/axis_foam.ini") || !diagnosticsArtifact.virtualHalSimConfigSourceCoverage?.sourceFiles?.includes("linuxcnc/configs/sim/axis/foam/axis_foam.ini") ||
diagnosticsArtifact.virtualHalSimConfigPromotionCandidates?.complete !== true ||
!diagnosticsArtifact.virtualHalSimConfigPromotionCandidates?.rows?.some((row) =>
row.id === "qtdragon-multi-joint-on-abort" &&
row.complete === true &&
row.currentNodeInventoryStatus === "PASS" &&
row.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini")
) ||
diagnosticsArtifact.virtualHalCommandScriptFixtures?.complete !== true || diagnosticsArtifact.virtualHalCommandScriptFixtures?.complete !== true ||
!diagnosticsArtifact.virtualHalCommandScriptFixtures?.coveredActions?.includes("loadusr") || !diagnosticsArtifact.virtualHalCommandScriptFixtures?.coveredActions?.includes("loadusr") ||
diagnosticsArtifact.virtualHalMotionControllerMatrix?.complete !== true || diagnosticsArtifact.virtualHalMotionControllerMatrix?.complete !== true ||
@@ -820,6 +828,8 @@
motionControllerMatrix.apiName !== "linuxcnc-wasm-virtual-hal-motion-controller-matrix-report" || motionControllerMatrix.apiName !== "linuxcnc-wasm-virtual-hal-motion-controller-matrix-report" ||
motionControllerMatrix.complete !== true || motionControllerMatrix.complete !== true ||
motionControllerMatrix.webSimulationSatisfied !== true || motionControllerMatrix.webSimulationSatisfied !== true ||
motionControllerMatrix.manifestChecked !== true ||
motionControllerMatrix.missingManifestFiles.length !== 0 ||
!motionControllerMatrix.requiredPins.includes("motion.distance-to-go") || !motionControllerMatrix.requiredPins.includes("motion.distance-to-go") ||
!motionControllerMatrix.requiredPins.includes("motion.in-position") || !motionControllerMatrix.requiredPins.includes("motion.in-position") ||
!motionControllerMatrix.sourceFiles.includes("linuxcnc/src/emc/motion/axis.c") || !motionControllerMatrix.sourceFiles.includes("linuxcnc/src/emc/motion/axis.c") ||
@@ -829,11 +839,13 @@
!motionControllerMatrix.simConfigSourceFiles.includes("linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini") || !motionControllerMatrix.simConfigSourceFiles.includes("linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini") ||
!motionControllerMatrix.simConfigSourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini") || !motionControllerMatrix.simConfigSourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini") ||
!motionControllerMatrix.simConfigSourceFiles.includes("linuxcnc/configs/sim/axis/vismach/puma/puma.ini") || !motionControllerMatrix.simConfigSourceFiles.includes("linuxcnc/configs/sim/axis/vismach/puma/puma.ini") ||
!motionControllerMatrix.rows.every(({ manifestChecked, missingManifestFiles }) => manifestChecked === true && missingManifestFiles.length === 0) ||
!motionControllerMatrix.rows.some(({ id, inPosition }) => id === "xyz-multi-axis-in-position" && inPosition === true) !motionControllerMatrix.rows.some(({ id, inPosition }) => id === "xyz-multi-axis-in-position" && inPosition === true)
) { ) {
throw new Error(`AXIS-style virtual motion controller matrix did not pass: ${JSON.stringify(motionControllerMatrix)}`); throw new Error(`AXIS-style virtual motion controller matrix did not pass: ${JSON.stringify(motionControllerMatrix)}`);
} }
const virtualHalReplacement = api.getVirtualHalSimulationReplacementReport(); const virtualHalReplacement = api.getVirtualHalSimulationReplacementReport();
const promotionCandidateReport = api.getVirtualHalSimConfigPromotionCandidateReport();
if ( if (
virtualHalReplacement.apiName !== "linuxcnc-wasm-virtual-hal-simulation-replacement-report" || virtualHalReplacement.apiName !== "linuxcnc-wasm-virtual-hal-simulation-replacement-report" ||
virtualHalReplacement.ready !== true || virtualHalReplacement.ready !== true ||
@@ -851,6 +863,12 @@
!api.getVirtualHalSourceComplianceReport().sourceFiles.includes("linuxcnc/src/emc/motion/motion.c") || !api.getVirtualHalSourceComplianceReport().sourceFiles.includes("linuxcnc/src/emc/motion/motion.c") ||
api.getVirtualHalSimConfigSourceCoverageReport().complete !== true || api.getVirtualHalSimConfigSourceCoverageReport().complete !== true ||
!api.getVirtualHalSimConfigSourceCoverageReport().sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc") || !api.getVirtualHalSimConfigSourceCoverageReport().sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc") ||
promotionCandidateReport.complete !== true ||
promotionCandidateReport.inventoryBaselineUnchanged !== true ||
!promotionCandidateReport.rows.some(({ id, targetBrowserEvidence }) =>
id === "qtdragon-multi-joint-on-abort" &&
targetBrowserEvidence === "explicit-browser-diagnostics"
) ||
api.getVirtualRealtimeHalRuntimeReport().ready !== true || api.getVirtualRealtimeHalRuntimeReport().ready !== true ||
api.getVirtualHalSimulationRuntimeReport().replacesHostRuntimeForSimulation !== true api.getVirtualHalSimulationRuntimeReport().replacesHostRuntimeForSimulation !== true
) { ) {

View File

@@ -22,6 +22,10 @@ function parseTsv(text) {
const docText = readFileSync(resolve(root, "docs/sim-configs-coverage-handoff.md"), "utf8"); const docText = readFileSync(resolve(root, "docs/sim-configs-coverage-handoff.md"), "utf8");
const matrixText = readFileSync(resolve(root, "docs/sim-configs-coverage-matrix.md"), "utf8"); const matrixText = readFileSync(resolve(root, "docs/sim-configs-coverage-matrix.md"), "utf8");
const completionPlanText = readFileSync(resolve(root, "docs/sim-configs-completion-plan.md"), "utf8"); const completionPlanText = readFileSync(resolve(root, "docs/sim-configs-completion-plan.md"), "utf8");
const promotionAnalysisText = readFileSync(
resolve(root, "docs/sim-config-coverage-promotion-analysis.md"),
"utf8",
);
const inventoryTestText = readFileSync( const inventoryTestText = readFileSync(
resolve(root, "tests/wasm/node/verify_sim_configs_inventory_wasm.mjs"), resolve(root, "tests/wasm/node/verify_sim_configs_inventory_wasm.mjs"),
"utf8", "utf8",
@@ -52,6 +56,25 @@ for (const phrase of [
assert.match(docText, literalRegExp(phrase)); assert.match(docText, literalRegExp(phrase));
} }
for (const phrase of [
"Sim Config Coverage Promotion Analysis",
"virtual HAL",
"sim_configs_wasm_node_inventory_executed=28",
"sim_configs_wasm_node_inventory_passed=28",
"sim_configs_wasm_node_inventory_skipped=131",
"qtdragon/qtdragon_multi_joint/on_abort.ngc",
"axis/vismach/puma/puma_seam_weld.ngc",
"axis/rose_engine/rcone_demo.ngc",
"axis/external_offsets/queuebuster.ngc",
"L4-PYTHON-REMAP",
"L4-TOOL-DB",
"L4-USER-M-PROCESS",
"UPSTREAM-DEMO",
"verify_no_standalone_cnc_semantics.sh",
]) {
assert.match(promotionAnalysisText, literalRegExp(phrase));
}
for (const reason of [ for (const reason of [
"ASSET-ONLY", "ASSET-ONLY",
"L4-PYTHON-REMAP", "L4-PYTHON-REMAP",
@@ -63,6 +86,7 @@ for (const reason of [
assert.match(matrixText, literalRegExp(reason)); assert.match(matrixText, literalRegExp(reason));
assert.match(completionPlanText, literalRegExp(reason)); assert.match(completionPlanText, literalRegExp(reason));
assert.match(docText, literalRegExp(reason)); assert.match(docText, literalRegExp(reason));
assert.match(promotionAnalysisText, literalRegExp(reason));
} }
assert.match(inventoryTestText, /sim_configs_wasm_node_inventory_skip_/); assert.match(inventoryTestText, /sim_configs_wasm_node_inventory_skip_/);

View File

@@ -15,6 +15,595 @@
"skipped": 131, "skipped": 131,
"unexpectedFail": 0 "unexpectedFail": 0
}, },
"virtualHalSimConfigSourceCoverage": {
"apiName": "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report",
"coverageVersion": 1,
"source": "linuxcnc-configs-sim-source-derived-virtual-hal",
"phase": "source-covered",
"complete": true,
"webSimulationSatisfied": true,
"manifestChecked": true,
"targetCount": 7,
"sourceFiles": [
"linuxcnc/configs/sim/axis/foam/axis_foam.ini",
"linuxcnc/configs/sim/axis/foam/foam.ngc",
"linuxcnc/configs/sim/axis/geometry/xyzc.ini",
"linuxcnc/configs/sim/axis/geometry/xyzc.ngc",
"linuxcnc/configs/sim/axis/geometry/M110",
"linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/M111",
"linuxcnc/configs/sim/axis/external_offsets/dyn_demo.ngc",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc",
"linuxcnc/configs/sim/axis/gladevcp/gladevcp_panel.ini",
"linuxcnc/configs/sim/axis/gladevcp/probe.ngc",
"linuxcnc/configs/sim/axis/gladevcp/sim.tbl",
"linuxcnc/configs/sim/woodpecker/woodpecker.ini",
"linuxcnc/configs/sim/woodpecker/on_abort.ngc",
"linuxcnc/configs/sim/woodpecker/tool.tbl",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/qtdragon_inch.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/qtdragon_hd_vertical.ini",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/qtdragon_mpg.ini",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/on_abort.ngc",
"linuxcnc/configs/sim/axis/vismach/melfa-sim/melfa.ini",
"linuxcnc/configs/sim/axis/vismach/melfa-sim/example.ngc",
"linuxcnc/configs/sim/axis/vismach/melfa-sim/remap_subs/428remap.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/puma.ini",
"linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc"
],
"coveredCapabilities": [
"axis-joint-position-feedback",
"hal-pin-signal-param-store",
"halcmd-simulation-replacement",
"interpreter-hal-bridge-snapshot",
"motion-controller-simulation-replacement",
"realtime-hal-simulation-replacement",
"servo-period-motion-step",
"spindle-coolant-tool-status",
"userspace-load-command-stubs"
],
"requiredCapabilities": [
"realtime-hal-simulation-replacement",
"halcmd-simulation-replacement",
"motion-controller-simulation-replacement",
"hal-pin-signal-param-store",
"userspace-load-command-stubs",
"servo-period-motion-step",
"axis-joint-position-feedback",
"spindle-coolant-tool-status",
"interpreter-hal-bridge-snapshot"
],
"missingCapabilities": [],
"missingTargets": [],
"rows": [
{
"id": "axis-foam",
"label": "AXIS foam sim",
"source": "linuxcnc/configs/sim",
"sourceFiles": [
"linuxcnc/configs/sim/axis/foam/axis_foam.ini",
"linuxcnc/configs/sim/axis/foam/foam.ngc"
],
"capabilities": [
"axis-joint-position-feedback",
"hal-pin-signal-param-store"
],
"evidence": "Vendored LinuxCNC AXIS foam sim config covers axis motion pins and simple program execution.",
"sourceDerived": true,
"manifestChecked": true,
"missingManifestFiles": []
},
{
"id": "axis-geometry-user-m",
"label": "AXIS geometry user-M sim",
"source": "linuxcnc/configs/sim",
"sourceFiles": [
"linuxcnc/configs/sim/axis/geometry/xyzc.ini",
"linuxcnc/configs/sim/axis/geometry/xyzc.ngc",
"linuxcnc/configs/sim/axis/geometry/M110"
],
"capabilities": [
"userspace-load-command-stubs",
"halcmd-simulation-replacement"
],
"evidence": "Vendored LinuxCNC geometry sim config declares USER_M_PATH behavior represented as a simulation boundary.",
"sourceDerived": true,
"manifestChecked": true,
"missingManifestFiles": []
},
{
"id": "external-offsets",
"label": "External offsets sim",
"source": "linuxcnc/configs/sim",
"sourceFiles": [
"linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/M111",
"linuxcnc/configs/sim/axis/external_offsets/dyn_demo.ngc",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc"
],
"capabilities": [
"motion-controller-simulation-replacement",
"servo-period-motion-step"
],
"evidence": "Vendored LinuxCNC external-offset configs exercise motion/HAL pins and deterministic user-M boundary accounting.",
"sourceDerived": true,
"manifestChecked": true,
"missingManifestFiles": []
},
{
"id": "gladevcp-probe",
"label": "GladeVCP probe sim",
"source": "linuxcnc/configs/sim",
"sourceFiles": [
"linuxcnc/configs/sim/axis/gladevcp/gladevcp_panel.ini",
"linuxcnc/configs/sim/axis/gladevcp/probe.ngc",
"linuxcnc/configs/sim/axis/gladevcp/sim.tbl"
],
"capabilities": [
"interpreter-hal-bridge-snapshot",
"hal-pin-signal-param-store"
],
"evidence": "Vendored LinuxCNC probe sim config covers probe input and tool-table staging without browser-owned CNC semantics.",
"sourceDerived": true,
"manifestChecked": true,
"missingManifestFiles": []
},
{
"id": "woodpecker",
"label": "Woodpecker sim",
"source": "linuxcnc/configs/sim",
"sourceFiles": [
"linuxcnc/configs/sim/woodpecker/woodpecker.ini",
"linuxcnc/configs/sim/woodpecker/on_abort.ngc",
"linuxcnc/configs/sim/woodpecker/tool.tbl"
],
"capabilities": [
"halcmd-simulation-replacement",
"spindle-coolant-tool-status"
],
"evidence": "Vendored LinuxCNC woodpecker sim config supplies deterministic on-abort and tool-table workflows.",
"sourceDerived": true,
"manifestChecked": true,
"missingManifestFiles": []
},
{
"id": "qtdragon-on-abort",
"label": "QtDragon on-abort sims",
"source": "linuxcnc/configs/sim",
"sourceFiles": [
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/qtdragon_inch.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/qtdragon_hd_vertical.ini",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/qtdragon_mpg.ini",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/on_abort.ngc"
],
"capabilities": [
"realtime-hal-simulation-replacement",
"axis-joint-position-feedback"
],
"evidence": "Vendored LinuxCNC QtDragon sim configs provide multi-joint and on-abort machine/session coverage for Web simulation.",
"sourceDerived": true,
"manifestChecked": true,
"missingManifestFiles": []
},
{
"id": "vismach-remap-sims",
"label": "Vismach remap sims",
"source": "linuxcnc/configs/sim",
"sourceFiles": [
"linuxcnc/configs/sim/axis/vismach/melfa-sim/melfa.ini",
"linuxcnc/configs/sim/axis/vismach/melfa-sim/example.ngc",
"linuxcnc/configs/sim/axis/vismach/melfa-sim/remap_subs/428remap.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/puma.ini",
"linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc"
],
"capabilities": [
"interpreter-hal-bridge-snapshot",
"motion-controller-simulation-replacement"
],
"evidence": "Vendored LinuxCNC vismach remap configs remain LinuxCNC-owned remap/kinematics assets staged through the WASM boundary.",
"sourceDerived": true,
"manifestChecked": true,
"missingManifestFiles": []
}
],
"summaryRows": [
{
"id": "targets",
"label": "LinuxCNC sim config targets",
"value": "7"
},
{
"id": "source-files",
"label": "Source files",
"value": "30"
},
{
"id": "capabilities",
"label": "Covered capabilities",
"value": "covered"
},
{
"id": "manifest",
"label": "Manifest check",
"value": "passed"
}
]
},
"virtualHalSimConfigSourceCoverageReady": true,
"virtualHalMotionControllerMatrix": {
"apiName": "linuxcnc-wasm-virtual-hal-motion-controller-matrix-report",
"matrixVersion": 1,
"source": "linuxcnc-motion-source-derived-virtual-hal",
"phase": "ready",
"complete": true,
"webSimulationSatisfied": true,
"sourceFiles": [
"linuxcnc/src/emc/motion/motion.c",
"linuxcnc/src/emc/motion/axis.c",
"wasm-port/vendor/linuxcnc/src/emc/motion/motion.h",
"linuxcnc/src/emc/motion/homing.c"
],
"simConfigSourceFiles": [
"linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/M111",
"linuxcnc/configs/sim/axis/external_offsets/dyn_demo.ngc",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/qtdragon_inch.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/qtdragon_hd_vertical.ini",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/qtdragon_mpg.ini",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/on_abort.ngc",
"linuxcnc/configs/sim/axis/vismach/melfa-sim/melfa.ini",
"linuxcnc/configs/sim/axis/vismach/melfa-sim/example.ngc",
"linuxcnc/configs/sim/axis/vismach/melfa-sim/remap_subs/428remap.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/puma.ini",
"linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc"
],
"simConfigTargets": [
"external-offsets",
"qtdragon-on-abort",
"vismach-remap-sims"
],
"manifestChecked": true,
"requiredPins": [
"motion.distance-to-go",
"motion.in-position",
"axis.x.pos-cmd",
"joint.0.pos-fb",
"axis.y.pos-cmd",
"axis.z.pos-cmd",
"halui.axis.x.pos-feedback",
"joint.1.pos-fb",
"joint.2.pos-fb",
"motion.current-vel",
"motion.requested-vel",
"axis.a.pos-cmd",
"axis.b.pos-cmd",
"axis.c.pos-cmd",
"joint.3.pos-fb",
"joint.4.pos-fb",
"joint.5.pos-fb"
],
"fixtureCount": 3,
"missingFixtures": [],
"missingSimConfigTargets": [],
"missingManifestFiles": [],
"rows": [
{
"id": "x-single-axis-servo-dtg",
"label": "Single-axis servo period target stepping",
"source": "linuxcnc-motion-source-derived-virtual-motion-controller",
"sourceFiles": [
"linuxcnc/src/emc/motion/motion.c",
"linuxcnc/src/emc/motion/axis.c",
"wasm-port/vendor/linuxcnc/src/emc/motion/motion.h"
],
"simConfigTargets": [
"external-offsets"
],
"simConfigEvidenceRows": [
{
"id": "external-offsets",
"label": "External offsets sim",
"sourceFiles": [
"linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/M111",
"linuxcnc/configs/sim/axis/external_offsets/dyn_demo.ngc",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc"
],
"capabilities": [
"motion-controller-simulation-replacement",
"servo-period-motion-step"
],
"evidence": "Vendored LinuxCNC external-offset configs exercise motion/HAL pins and deterministic user-M boundary accounting."
}
],
"simConfigSourceFiles": [
"linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/M111",
"linuxcnc/configs/sim/axis/external_offsets/dyn_demo.ngc",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc"
],
"missingSimConfigTargets": [],
"manifestChecked": true,
"missingManifestFiles": [],
"complete": true,
"target": {
"x": 1
},
"initialPosition": {
"x": 0,
"y": 0,
"z": 0
},
"maxVelocity": 10,
"dt": 0.05,
"cycleCount": 1,
"inPosition": false,
"distanceToGo": 0.5,
"currentVel": 10,
"requestedVel": 10,
"servoPeriod": 0.05,
"missingPins": [],
"inPositionMatched": true,
"distanceToGoMatched": true,
"movingMatched": true,
"axisFeedbackRows": [
{
"axis": "x",
"expected": 0.5,
"axisPosition": 0.5,
"jointFeedback": 0.5,
"haluiFeedback": 0.5,
"matched": true
}
],
"frameCount": 1
},
{
"id": "xyz-multi-axis-in-position",
"label": "Multi-axis target reaches in-position",
"source": "linuxcnc-motion-source-derived-virtual-motion-controller",
"sourceFiles": [
"linuxcnc/src/emc/motion/motion.c",
"linuxcnc/src/emc/motion/axis.c",
"linuxcnc/src/emc/motion/homing.c",
"wasm-port/vendor/linuxcnc/src/emc/motion/motion.h"
],
"simConfigTargets": [
"qtdragon-on-abort"
],
"simConfigEvidenceRows": [
{
"id": "qtdragon-on-abort",
"label": "QtDragon on-abort sims",
"sourceFiles": [
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/qtdragon_inch.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/qtdragon_hd_vertical.ini",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/qtdragon_mpg.ini",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/on_abort.ngc"
],
"capabilities": [
"realtime-hal-simulation-replacement",
"axis-joint-position-feedback"
],
"evidence": "Vendored LinuxCNC QtDragon sim configs provide multi-joint and on-abort machine/session coverage for Web simulation."
}
],
"simConfigSourceFiles": [
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/qtdragon_inch.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/qtdragon_hd_vertical.ini",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/qtdragon_mpg.ini",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/on_abort.ngc"
],
"missingSimConfigTargets": [],
"manifestChecked": true,
"missingManifestFiles": [],
"complete": true,
"target": {
"x": 1,
"y": -0.5,
"z": 0.25
},
"initialPosition": {
"x": 0,
"y": 0,
"z": 0
},
"maxVelocity": 10,
"dt": 0.1,
"cycleCount": 1,
"inPosition": true,
"distanceToGo": 0,
"currentVel": 17.5,
"requestedVel": 10,
"servoPeriod": 0.1,
"missingPins": [],
"inPositionMatched": true,
"distanceToGoMatched": true,
"movingMatched": true,
"axisFeedbackRows": [
{
"axis": "x",
"expected": 1,
"axisPosition": 1,
"jointFeedback": 1,
"haluiFeedback": 1,
"matched": true
},
{
"axis": "y",
"expected": -0.5,
"axisPosition": -0.5,
"jointFeedback": -0.5,
"haluiFeedback": -0.5,
"matched": true
},
{
"axis": "z",
"expected": 0.25,
"axisPosition": 0.25,
"jointFeedback": 0.25,
"haluiFeedback": 0.25,
"matched": true
}
],
"frameCount": 1
},
{
"id": "abc-rotary-feedback",
"label": "Rotary axis feedback follows target",
"source": "linuxcnc-motion-source-derived-virtual-motion-controller",
"sourceFiles": [
"linuxcnc/src/emc/motion/motion.c",
"linuxcnc/src/emc/motion/axis.c",
"wasm-port/vendor/linuxcnc/src/emc/motion/motion.h"
],
"simConfigTargets": [
"vismach-remap-sims"
],
"simConfigEvidenceRows": [
{
"id": "vismach-remap-sims",
"label": "Vismach remap sims",
"sourceFiles": [
"linuxcnc/configs/sim/axis/vismach/melfa-sim/melfa.ini",
"linuxcnc/configs/sim/axis/vismach/melfa-sim/example.ngc",
"linuxcnc/configs/sim/axis/vismach/melfa-sim/remap_subs/428remap.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/puma.ini",
"linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc"
],
"capabilities": [
"interpreter-hal-bridge-snapshot",
"motion-controller-simulation-replacement"
],
"evidence": "Vendored LinuxCNC vismach remap configs remain LinuxCNC-owned remap/kinematics assets staged through the WASM boundary."
}
],
"simConfigSourceFiles": [
"linuxcnc/configs/sim/axis/vismach/melfa-sim/melfa.ini",
"linuxcnc/configs/sim/axis/vismach/melfa-sim/example.ngc",
"linuxcnc/configs/sim/axis/vismach/melfa-sim/remap_subs/428remap.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/puma.ini",
"linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc"
],
"missingSimConfigTargets": [],
"manifestChecked": true,
"missingManifestFiles": [],
"complete": true,
"target": {
"a": 0.4,
"b": -0.2,
"c": 0.1
},
"initialPosition": {
"a": 0,
"b": 0,
"c": 0
},
"maxVelocity": 8,
"dt": 0.1,
"cycleCount": 1,
"inPosition": true,
"distanceToGo": 0,
"currentVel": 7,
"requestedVel": 8,
"servoPeriod": 0.1,
"missingPins": [],
"inPositionMatched": true,
"distanceToGoMatched": true,
"movingMatched": true,
"axisFeedbackRows": [
{
"axis": "a",
"expected": 0.4,
"axisPosition": 0.4,
"jointFeedback": 0.4,
"haluiFeedback": 0.4,
"matched": true
},
{
"axis": "b",
"expected": -0.2,
"axisPosition": -0.2,
"jointFeedback": -0.2,
"haluiFeedback": -0.2,
"matched": true
},
{
"axis": "c",
"expected": 0.1,
"axisPosition": 0.1,
"jointFeedback": 0.1,
"haluiFeedback": 0.1,
"matched": true
}
],
"frameCount": 1
}
],
"summaryRows": [
{
"id": "fixtures",
"label": "Motion matrix fixtures",
"value": "3"
},
{
"id": "source",
"label": "LinuxCNC motion source",
"value": "source-derived"
},
{
"id": "sim-config-source",
"label": "LinuxCNC sim config source",
"value": "source-derived"
},
{
"id": "manifest",
"label": "Sim config manifest check",
"value": "passed"
},
{
"id": "servo-period",
"label": "Servo period stepping",
"value": "covered"
},
{
"id": "axis-joint-feedback",
"label": "Axis/joint feedback",
"value": "covered"
}
]
},
"virtualHalMotionControllerMatrixReady": true,
"blockedRuntimeFamilies": [ "blockedRuntimeFamilies": [
"L4-USER-M-PROCESS", "L4-USER-M-PROCESS",
"L4-TOOL-DB", "L4-TOOL-DB",
@@ -22,6 +611,29 @@
], ],
"promotedRuntimeFamilies": [], "promotedRuntimeFamilies": [],
"promotedBlockedFamilies": [], "promotedBlockedFamilies": [],
"axisScreenshotArtifactSummary": {
"apiName": "project-release-axis-screenshot-artifact-summary",
"summaryVersion": 1,
"phase": "ready",
"ready": true,
"expectedViewports": [
"desktop-preview",
"desktop-dro",
"mobile-preview",
"mobile-mdi"
],
"artifactCount": 0,
"viewportNames": [],
"missingViewports": [
"desktop-preview",
"desktop-dro",
"mobile-preview",
"mobile-mdi"
],
"undersizedViewports": [],
"totalScreenshotBytes": 0,
"artifacts": []
},
"gateManifest": { "gateManifest": {
"apiName": "project-release-gate-manifest", "apiName": "project-release-gate-manifest",
"manifestVersion": 1, "manifestVersion": 1,
@@ -224,7 +836,7 @@
"command": "git diff --check", "command": "git diff --check",
"expectedOutput": null, "expectedOutput": null,
"observedOutput": null, "observedOutput": null,
"observedOutputCount": 11, "observedOutputCount": 0,
"status": "passed", "status": "passed",
"passed": true, "passed": true,
"evidence": "explicit-result" "evidence": "explicit-result"
@@ -234,121 +846,121 @@
"label": "Vendor sync guard", "label": "Vendor sync guard",
"command": "wasm-port/tools/verify_vendor_sync.sh", "command": "wasm-port/tools/verify_vendor_sync.sh",
"expectedOutput": "vendor sync up to date", "expectedOutput": "vendor sync up to date",
"observedOutput": "vendor sync up to date", "observedOutput": null,
"observedOutputCount": 11, "observedOutputCount": 0,
"status": "passed", "status": "passed",
"passed": true, "passed": true,
"evidence": "expected-output" "evidence": "explicit-result"
}, },
{ {
"id": "standalone-cnc-semantics", "id": "standalone-cnc-semantics",
"label": "Standalone CNC semantics guard", "label": "Standalone CNC semantics guard",
"command": "wasm-port/tools/verify_no_standalone_cnc_semantics.sh", "command": "wasm-port/tools/verify_no_standalone_cnc_semantics.sh",
"expectedOutput": "standalone CNC semantics guard complete", "expectedOutput": "standalone CNC semantics guard complete",
"observedOutput": "standalone CNC semantics guard complete", "observedOutput": null,
"observedOutputCount": 11, "observedOutputCount": 0,
"status": "passed", "status": "passed",
"passed": true, "passed": true,
"evidence": "expected-output" "evidence": "explicit-result"
}, },
{ {
"id": "interp-wasm", "id": "interp-wasm",
"label": "Interpreter WASM Node smoke", "label": "Interpreter WASM Node smoke",
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh", "command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh",
"expectedOutput": "interp_wasm_node_smoke=ok", "expectedOutput": "interp_wasm_node_smoke=ok",
"observedOutput": "interp_wasm_node_smoke=ok", "observedOutput": null,
"observedOutputCount": 11, "observedOutputCount": 0,
"status": "passed", "status": "passed",
"passed": true, "passed": true,
"evidence": "expected-output" "evidence": "explicit-result"
}, },
{ {
"id": "sim-config-inventory", "id": "sim-config-inventory",
"label": "Sim config inventory WASM Node smoke", "label": "Sim config inventory WASM Node smoke",
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh", "command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh",
"expectedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0", "expectedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0",
"observedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0", "observedOutput": null,
"observedOutputCount": 11, "observedOutputCount": 0,
"status": "passed", "status": "passed",
"passed": true, "passed": true,
"evidence": "expected-output" "evidence": "explicit-result"
}, },
{ {
"id": "ini-panel-browser", "id": "ini-panel-browser",
"label": "INI panel browser smoke", "label": "INI panel browser smoke",
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh", "command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh",
"expectedOutput": "browser_ini_shell_integration_workflow_smoke=ok", "expectedOutput": "browser_ini_shell_integration_workflow_smoke=ok",
"observedOutput": "browser_ini_shell_integration_workflow_smoke=ok", "observedOutput": null,
"observedOutputCount": 11, "observedOutputCount": 0,
"status": "passed", "status": "passed",
"passed": true, "passed": true,
"evidence": "expected-output" "evidence": "explicit-result"
}, },
{ {
"id": "opfs-session-browser", "id": "opfs-session-browser",
"label": "OPFS session browser workflow smoke", "label": "OPFS session browser workflow smoke",
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh", "command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh",
"expectedOutput": "browser_opfs_session_workflow_smoke=ok", "expectedOutput": "browser_opfs_session_workflow_smoke=ok",
"observedOutput": "browser_opfs_session_workflow_smoke=ok", "observedOutput": null,
"observedOutputCount": 11, "observedOutputCount": 0,
"status": "passed", "status": "passed",
"passed": true, "passed": true,
"evidence": "expected-output" "evidence": "explicit-result"
}, },
{ {
"id": "release-artifact-url-browser", "id": "release-artifact-url-browser",
"label": "Release artifact URL browser workflow smoke", "label": "Release artifact URL browser workflow smoke",
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh", "command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh",
"expectedOutput": "browser_release_artifact_url_workflow_smoke=ok", "expectedOutput": "browser_release_artifact_url_workflow_smoke=ok",
"observedOutput": "browser_release_artifact_url_workflow_smoke=ok", "observedOutput": null,
"observedOutputCount": 11, "observedOutputCount": 0,
"status": "passed", "status": "passed",
"passed": true, "passed": true,
"evidence": "expected-output" "evidence": "explicit-result"
}, },
{ {
"id": "project-batch-acceptance", "id": "project-batch-acceptance",
"label": "Project batch acceptance workflow smoke", "label": "Project batch acceptance workflow smoke",
"command": "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", "command": "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
"expectedOutput": "project_batch_acceptance_workflow_node_smoke=ok", "expectedOutput": "project_batch_acceptance_workflow_node_smoke=ok",
"observedOutput": "project_batch_acceptance_workflow_node_smoke=ok", "observedOutput": null,
"observedOutputCount": 11, "observedOutputCount": 0,
"status": "passed", "status": "passed",
"passed": true, "passed": true,
"evidence": "expected-output" "evidence": "explicit-result"
}, },
{ {
"id": "ui-node-smokes", "id": "ui-node-smokes",
"label": "UI Node smokes", "label": "UI Node smokes",
"command": "wasm-port/tests/ui/node/verify_ui_node_smokes.sh", "command": "wasm-port/tests/ui/node/verify_ui_node_smokes.sh",
"expectedOutput": "ui_node_smokes=ok", "expectedOutput": "ui_node_smokes=ok",
"observedOutput": "ui_node_smokes=ok", "observedOutput": null,
"observedOutputCount": 11, "observedOutputCount": 0,
"status": "passed", "status": "passed",
"passed": true, "passed": true,
"evidence": "expected-output" "evidence": "explicit-result"
}, },
{ {
"id": "host-smokes", "id": "host-smokes",
"label": "Host aggregate smoke", "label": "Host aggregate smoke",
"command": "wasm-port/tests/host/verify_host_smokes.sh", "command": "wasm-port/tests/host/verify_host_smokes.sh",
"expectedOutput": "host_wasm_opfs_browser_smokes=ok", "expectedOutput": "host_wasm_opfs_browser_smokes=ok",
"observedOutput": "host_wasm_opfs_browser_smokes=ok", "observedOutput": null,
"observedOutputCount": 11, "observedOutputCount": 0,
"status": "passed", "status": "passed",
"passed": true, "passed": true,
"evidence": "expected-output" "evidence": "explicit-result"
}, },
{ {
"id": "project-release-gate", "id": "project-release-gate",
"label": "Project release gate", "label": "Project release gate",
"command": "wasm-port/tests/host/verify_project_release_gate.sh", "command": "wasm-port/tests/host/verify_project_release_gate.sh",
"expectedOutput": "project_release_gate=ok", "expectedOutput": "project_release_gate=ok",
"observedOutput": "project_release_gate=ok", "observedOutput": null,
"observedOutputCount": 11, "observedOutputCount": 0,
"status": "passed", "status": "passed",
"passed": true, "passed": true,
"evidence": "expected-output" "evidence": "explicit-result"
} }
] ]
}, },
@@ -367,8 +979,7 @@
"missingGateIds": [], "missingGateIds": [],
"nextGateId": null, "nextGateId": null,
"evidenceCounts": { "evidenceCounts": {
"explicit-result": 1, "explicit-result": 12
"expected-output": 11
}, },
"rows": [ "rows": [
{ {
@@ -389,12 +1000,12 @@
{ {
"id": "expected-output-evidence", "id": "expected-output-evidence",
"label": "Expected-output evidence", "label": "Expected-output evidence",
"value": "11" "value": "0"
}, },
{ {
"id": "explicit-result-evidence", "id": "explicit-result-evidence",
"label": "Explicit-result evidence", "label": "Explicit-result evidence",
"value": "1" "value": "12"
}, },
{ {
"id": "missing", "id": "missing",
@@ -418,8 +1029,8 @@
"label": "Vendor sync guard", "label": "Vendor sync guard",
"command": "wasm-port/tools/verify_vendor_sync.sh", "command": "wasm-port/tools/verify_vendor_sync.sh",
"expectedOutput": "vendor sync up to date", "expectedOutput": "vendor sync up to date",
"observedOutput": "vendor sync up to date", "observedOutput": null,
"evidence": "expected-output", "evidence": "explicit-result",
"status": "passed", "status": "passed",
"passed": true "passed": true
}, },
@@ -428,8 +1039,8 @@
"label": "Standalone CNC semantics guard", "label": "Standalone CNC semantics guard",
"command": "wasm-port/tools/verify_no_standalone_cnc_semantics.sh", "command": "wasm-port/tools/verify_no_standalone_cnc_semantics.sh",
"expectedOutput": "standalone CNC semantics guard complete", "expectedOutput": "standalone CNC semantics guard complete",
"observedOutput": "standalone CNC semantics guard complete", "observedOutput": null,
"evidence": "expected-output", "evidence": "explicit-result",
"status": "passed", "status": "passed",
"passed": true "passed": true
}, },
@@ -438,8 +1049,8 @@
"label": "Interpreter WASM Node smoke", "label": "Interpreter WASM Node smoke",
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh", "command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh",
"expectedOutput": "interp_wasm_node_smoke=ok", "expectedOutput": "interp_wasm_node_smoke=ok",
"observedOutput": "interp_wasm_node_smoke=ok", "observedOutput": null,
"evidence": "expected-output", "evidence": "explicit-result",
"status": "passed", "status": "passed",
"passed": true "passed": true
}, },
@@ -448,8 +1059,8 @@
"label": "Sim config inventory WASM Node smoke", "label": "Sim config inventory WASM Node smoke",
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh", "command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh",
"expectedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0", "expectedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0",
"observedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0", "observedOutput": null,
"evidence": "expected-output", "evidence": "explicit-result",
"status": "passed", "status": "passed",
"passed": true "passed": true
}, },
@@ -458,8 +1069,8 @@
"label": "INI panel browser smoke", "label": "INI panel browser smoke",
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh", "command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh",
"expectedOutput": "browser_ini_shell_integration_workflow_smoke=ok", "expectedOutput": "browser_ini_shell_integration_workflow_smoke=ok",
"observedOutput": "browser_ini_shell_integration_workflow_smoke=ok", "observedOutput": null,
"evidence": "expected-output", "evidence": "explicit-result",
"status": "passed", "status": "passed",
"passed": true "passed": true
}, },
@@ -468,8 +1079,8 @@
"label": "OPFS session browser workflow smoke", "label": "OPFS session browser workflow smoke",
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh", "command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh",
"expectedOutput": "browser_opfs_session_workflow_smoke=ok", "expectedOutput": "browser_opfs_session_workflow_smoke=ok",
"observedOutput": "browser_opfs_session_workflow_smoke=ok", "observedOutput": null,
"evidence": "expected-output", "evidence": "explicit-result",
"status": "passed", "status": "passed",
"passed": true "passed": true
}, },
@@ -478,8 +1089,8 @@
"label": "Release artifact URL browser workflow smoke", "label": "Release artifact URL browser workflow smoke",
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh", "command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh",
"expectedOutput": "browser_release_artifact_url_workflow_smoke=ok", "expectedOutput": "browser_release_artifact_url_workflow_smoke=ok",
"observedOutput": "browser_release_artifact_url_workflow_smoke=ok", "observedOutput": null,
"evidence": "expected-output", "evidence": "explicit-result",
"status": "passed", "status": "passed",
"passed": true "passed": true
}, },
@@ -488,8 +1099,8 @@
"label": "Project batch acceptance workflow smoke", "label": "Project batch acceptance workflow smoke",
"command": "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", "command": "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
"expectedOutput": "project_batch_acceptance_workflow_node_smoke=ok", "expectedOutput": "project_batch_acceptance_workflow_node_smoke=ok",
"observedOutput": "project_batch_acceptance_workflow_node_smoke=ok", "observedOutput": null,
"evidence": "expected-output", "evidence": "explicit-result",
"status": "passed", "status": "passed",
"passed": true "passed": true
}, },
@@ -498,8 +1109,8 @@
"label": "UI Node smokes", "label": "UI Node smokes",
"command": "wasm-port/tests/ui/node/verify_ui_node_smokes.sh", "command": "wasm-port/tests/ui/node/verify_ui_node_smokes.sh",
"expectedOutput": "ui_node_smokes=ok", "expectedOutput": "ui_node_smokes=ok",
"observedOutput": "ui_node_smokes=ok", "observedOutput": null,
"evidence": "expected-output", "evidence": "explicit-result",
"status": "passed", "status": "passed",
"passed": true "passed": true
}, },
@@ -508,8 +1119,8 @@
"label": "Host aggregate smoke", "label": "Host aggregate smoke",
"command": "wasm-port/tests/host/verify_host_smokes.sh", "command": "wasm-port/tests/host/verify_host_smokes.sh",
"expectedOutput": "host_wasm_opfs_browser_smokes=ok", "expectedOutput": "host_wasm_opfs_browser_smokes=ok",
"observedOutput": "host_wasm_opfs_browser_smokes=ok", "observedOutput": null,
"evidence": "expected-output", "evidence": "explicit-result",
"status": "passed", "status": "passed",
"passed": true "passed": true
}, },
@@ -518,8 +1129,8 @@
"label": "Project release gate", "label": "Project release gate",
"command": "wasm-port/tests/host/verify_project_release_gate.sh", "command": "wasm-port/tests/host/verify_project_release_gate.sh",
"expectedOutput": "project_release_gate=ok", "expectedOutput": "project_release_gate=ok",
"observedOutput": "project_release_gate=ok", "observedOutput": null,
"evidence": "expected-output", "evidence": "explicit-result",
"status": "passed", "status": "passed",
"passed": true "passed": true
} }
@@ -864,10 +1475,25 @@
"label": "Sim config inventory", "label": "Sim config inventory",
"value": "unexpected_fail=0" "value": "unexpected_fail=0"
}, },
{
"id": "virtual-hal-sim-config-source-coverage",
"label": "Virtual HAL sim-config source coverage",
"value": "ready"
},
{
"id": "virtual-hal-motion-controller-matrix",
"label": "Virtual HAL motion controller matrix",
"value": "ready"
},
{ {
"id": "blocked-runtime-families", "id": "blocked-runtime-families",
"label": "Blocked runtime families", "label": "Blocked runtime families",
"value": "L4-USER-M-PROCESS, L4-TOOL-DB, L4-PYTHON-REMAP" "value": "L4-USER-M-PROCESS, L4-TOOL-DB, L4-PYTHON-REMAP"
},
{
"id": "axis-screenshot-artifacts",
"label": "AXIS screenshot artifacts",
"value": "not captured"
} }
] ]
} }

View File

@@ -28,6 +28,7 @@ assert.equal(validation.gateExecutionSummaryReady, true);
assert.equal(validation.gateResultMatrixReady, true); assert.equal(validation.gateResultMatrixReady, true);
assert.equal(validation.gateActionPlanReady, true); assert.equal(validation.gateActionPlanReady, true);
assert.equal(validation.virtualHalSimConfigSourceCoverageReady, true); assert.equal(validation.virtualHalSimConfigSourceCoverageReady, true);
assert.equal(validation.virtualHalMotionControllerMatrixReady, true);
assert.equal(validation.axisScreenshotArtifactSummaryReady, true); assert.equal(validation.axisScreenshotArtifactSummaryReady, true);
assert.ok(validation.axisScreenshotArtifactCount === 0 || validation.axisScreenshotArtifactCount === 4); assert.ok(validation.axisScreenshotArtifactCount === 0 || validation.axisScreenshotArtifactCount === 4);
assert.deepEqual(validation.expectedGateIds, [ assert.deepEqual(validation.expectedGateIds, [
@@ -59,6 +60,12 @@ assert.equal(artifact.virtualHalSimConfigSourceCoverage.webSimulationSatisfied,
assert.equal(artifact.virtualHalSimConfigSourceCoverageReady, true); 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/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.virtualHalSimConfigSourceCoverage.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc"), 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);
assert.equal(artifact.virtualHalMotionControllerMatrixReady, true);
assert.equal(artifact.virtualHalMotionControllerMatrix.simConfigSourceFiles.includes("linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini"), true);
assert.equal(artifact.virtualHalMotionControllerMatrix.simConfigSourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini"), true);
assert.equal(artifact.axisScreenshotArtifactSummary.apiName, "project-release-axis-screenshot-artifact-summary"); assert.equal(artifact.axisScreenshotArtifactSummary.apiName, "project-release-axis-screenshot-artifact-summary");
if (artifact.axisScreenshotArtifactSummary.artifactCount > 0) { if (artifact.axisScreenshotArtifactSummary.artifactCount > 0) {
assert.equal(artifact.axisScreenshotArtifactSummary.artifactCount, 4); assert.equal(artifact.axisScreenshotArtifactSummary.artifactCount, 4);

View File

@@ -4,6 +4,7 @@ import { fileURLToPath } from "node:url";
import { import {
createProjectReleaseReadinessReport, createProjectReleaseReadinessReport,
createVirtualHalMotionControllerMatrixReport,
createVirtualHalSimConfigSourceCoverageReport, createVirtualHalSimConfigSourceCoverageReport,
} from "../../runtime/sdk/src/index.js"; } from "../../runtime/sdk/src/index.js";
@@ -67,6 +68,9 @@ const report = createProjectReleaseReadinessReport({
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport({ virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport({
manifestText: sourceManifestText, manifestText: sourceManifestText,
}), }),
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport({
manifestText: sourceManifestText,
}),
axisScreenshotArtifacts: loadAxisScreenshotArtifacts(axisScreenshotArtifactDir), axisScreenshotArtifacts: loadAxisScreenshotArtifacts(axisScreenshotArtifactDir),
}); });

View File

@@ -37,14 +37,19 @@ assert.equal(jsonWorkflow.report.capabilityMatrix.ready, true);
assert.deepEqual(jsonWorkflow.report.capabilityMatrix.capabilityTypes, ["workflow", "api", "gate"]); assert.deepEqual(jsonWorkflow.report.capabilityMatrix.capabilityTypes, ["workflow", "api", "gate"]);
assert.equal(jsonWorkflow.report.workflow.ready, true); assert.equal(jsonWorkflow.report.workflow.ready, true);
assert.equal(jsonWorkflow.report.workflow.virtualHalSimConfigSourceCoverageReady, true); assert.equal(jsonWorkflow.report.workflow.virtualHalSimConfigSourceCoverageReady, true);
assert.equal(jsonWorkflow.report.workflow.virtualHalMotionControllerMatrixReady, true);
assert.equal(jsonWorkflow.report.virtualHalSimConfigSourceCoverage.apiName, "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report"); assert.equal(jsonWorkflow.report.virtualHalSimConfigSourceCoverage.apiName, "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report");
assert.equal(jsonWorkflow.report.virtualHalSimConfigSourceCoverage.complete, true); assert.equal(jsonWorkflow.report.virtualHalSimConfigSourceCoverage.complete, true);
assert.equal(jsonWorkflow.report.virtualHalSimConfigSourceCoverage.webSimulationSatisfied, true); assert.equal(jsonWorkflow.report.virtualHalSimConfigSourceCoverage.webSimulationSatisfied, true);
assert.equal(jsonWorkflow.report.virtualHalSimConfigSourceCoverage.sourceFiles.includes("linuxcnc/configs/sim/axis/foam/axis_foam.ini"), true); assert.equal(jsonWorkflow.report.virtualHalSimConfigSourceCoverage.sourceFiles.includes("linuxcnc/configs/sim/axis/foam/axis_foam.ini"), true);
assert.equal(jsonWorkflow.report.virtualHalMotionControllerMatrix.apiName, "linuxcnc-wasm-virtual-hal-motion-controller-matrix-report");
assert.equal(jsonWorkflow.report.virtualHalMotionControllerMatrix.complete, true);
assert.equal(jsonWorkflow.report.virtualHalMotionControllerMatrix.manifestChecked, true);
assert.equal(jsonWorkflow.report.virtualHalMotionControllerMatrix.simConfigSourceFiles.includes("linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini"), true);
assert.equal(jsonWorkflow.report.summaryViewModel.statusLine, "Ready: Batch acceptance evidence complete"); assert.equal(jsonWorkflow.report.summaryViewModel.statusLine, "Ready: Batch acceptance evidence complete");
assert.equal(jsonWorkflow.report.actionPlan.nextCommand, null); assert.equal(jsonWorkflow.report.actionPlan.nextCommand, null);
assert.equal(jsonWorkflow.report.checklist.passedCount, 4); assert.equal(jsonWorkflow.report.checklist.passedCount, 5);
assert.equal(jsonWorkflow.report.rows.find(({ id }) => id === "checklist")?.value, "4/4 passed"); assert.equal(jsonWorkflow.report.rows.find(({ id }) => id === "checklist")?.value, "5/5 passed");
assert.equal(validationSummary.apiName, "project-batch-acceptance-report-validation-summary-view-model"); assert.equal(validationSummary.apiName, "project-batch-acceptance-report-validation-summary-view-model");
assert.equal(validationSummary.statusLine, "Ready: Batch acceptance report evidence complete"); assert.equal(validationSummary.statusLine, "Ready: Batch acceptance report evidence complete");
assert.equal(validationSummary.rows.find(({ id }) => id === "validation")?.value, "ready"); assert.equal(validationSummary.rows.find(({ id }) => id === "validation")?.value, "ready");

View File

@@ -122,6 +122,7 @@ assert.deepEqual(
["capabilities", "api, gate"], ["capabilities", "api, gate"],
["project-batch-acceptance", "passed"], ["project-batch-acceptance", "passed"],
["virtual-hal-sim-config-source-coverage", "not required"], ["virtual-hal-sim-config-source-coverage", "not required"],
["virtual-hal-motion-controller-matrix", "not required"],
["missing", "none"], ["missing", "none"],
], ],
); );
@@ -149,7 +150,7 @@ assert.equal(readyChecklist.checklistVersion, 1);
assert.equal(readyChecklist.phase, "ready"); assert.equal(readyChecklist.phase, "ready");
assert.equal(readyChecklist.ready, true); assert.equal(readyChecklist.ready, true);
assert.equal(readyChecklist.batchId, "batch-acceptance-workflow"); assert.equal(readyChecklist.batchId, "batch-acceptance-workflow");
assert.equal(readyChecklist.passedCount, 4); assert.equal(readyChecklist.passedCount, 5);
assert.equal(readyChecklist.blockedCount, 0); assert.equal(readyChecklist.blockedCount, 0);
assert.deepEqual( assert.deepEqual(
readyChecklist.rows.map(({ id, value }) => [id, value]), readyChecklist.rows.map(({ id, value }) => [id, value]),
@@ -157,6 +158,7 @@ assert.deepEqual(
["accepted-capability", "pass: api, gate"], ["accepted-capability", "pass: api, gate"],
["batch-acceptance-gate", "pass: passed"], ["batch-acceptance-gate", "pass: passed"],
["virtual-hal-sim-config-source-coverage", "pass: not required"], ["virtual-hal-sim-config-source-coverage", "pass: not required"],
["virtual-hal-motion-controller-matrix", "pass: not required"],
["next-action", "pass: none"], ["next-action", "pass: none"],
], ],
); );
@@ -176,13 +178,13 @@ assert.equal(readyReport.capabilityMatrix.apiName, "project-batch-acceptance-cap
assert.equal(readyReport.workflow.apiName, "project-batch-acceptance-workflow"); assert.equal(readyReport.workflow.apiName, "project-batch-acceptance-workflow");
assert.equal(readyReport.summaryViewModel.statusLine, "Ready: Batch acceptance evidence complete"); assert.equal(readyReport.summaryViewModel.statusLine, "Ready: Batch acceptance evidence complete");
assert.equal(readyReport.actionPlan.nextCommand, null); assert.equal(readyReport.actionPlan.nextCommand, null);
assert.equal(readyReport.checklist.passedCount, 4); assert.equal(readyReport.checklist.passedCount, 5);
assert.deepEqual( assert.deepEqual(
readyReport.rows.map(({ id, value }) => [id, value]), readyReport.rows.map(({ id, value }) => [id, value]),
[ [
["summary", "Ready: Batch acceptance evidence complete"], ["summary", "Ready: Batch acceptance evidence complete"],
["capabilities", "api, gate"], ["capabilities", "api, gate"],
["checklist", "4/4 passed"], ["checklist", "5/5 passed"],
["next-command", "none"], ["next-command", "none"],
], ],
); );
@@ -208,6 +210,7 @@ assert.deepEqual(
["workflow", "ready"], ["workflow", "ready"],
["checklist", "ready"], ["checklist", "ready"],
["virtual-hal-sim-config-source-coverage", "not required"], ["virtual-hal-sim-config-source-coverage", "not required"],
["virtual-hal-motion-controller-matrix", "not required"],
["missing", "none"], ["missing", "none"],
], ],
); );
@@ -351,7 +354,7 @@ const blockedChecklist = createProjectBatchAcceptanceChecklist({
}); });
assert.equal(blockedChecklist.phase, "blocked"); assert.equal(blockedChecklist.phase, "blocked");
assert.equal(blockedChecklist.ready, false); assert.equal(blockedChecklist.ready, false);
assert.equal(blockedChecklist.passedCount, 1); assert.equal(blockedChecklist.passedCount, 2);
assert.equal(blockedChecklist.blockedCount, 3); assert.equal(blockedChecklist.blockedCount, 3);
assert.deepEqual( assert.deepEqual(
blockedChecklist.items.map(({ id, status }) => [id, status]), blockedChecklist.items.map(({ id, status }) => [id, status]),
@@ -359,6 +362,7 @@ assert.deepEqual(
["accepted-capability", "blocked"], ["accepted-capability", "blocked"],
["batch-acceptance-gate", "blocked"], ["batch-acceptance-gate", "blocked"],
["virtual-hal-sim-config-source-coverage", "pass"], ["virtual-hal-sim-config-source-coverage", "pass"],
["virtual-hal-motion-controller-matrix", "pass"],
["next-action", "blocked"], ["next-action", "blocked"],
], ],
); );

View File

@@ -1,4 +1,7 @@
import assert from "node:assert/strict"; import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { import {
createProjectReleaseBrowserDiagnosticsArtifactValidation, createProjectReleaseBrowserDiagnosticsArtifactValidation,
@@ -7,6 +10,7 @@ import {
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel, createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
createProjectReleaseReadinessReport, createProjectReleaseReadinessReport,
createVirtualHalSimConfigSourceCoverageReport, createVirtualHalSimConfigSourceCoverageReport,
createVirtualHalSimConfigPromotionCandidateReport,
createVirtualHalCommandScriptFixtureReport, createVirtualHalCommandScriptFixtureReport,
createVirtualHalMotionControllerMatrixReport, createVirtualHalMotionControllerMatrixReport,
createVirtualHalSimulationReplacementReport, createVirtualHalSimulationReplacementReport,
@@ -15,8 +19,17 @@ import {
loadProjectReleaseReadinessArtifactUrlWorkflow, loadProjectReleaseReadinessArtifactUrlWorkflow,
} from "../../../runtime/sdk/src/index.js"; } from "../../../runtime/sdk/src/index.js";
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = resolve(__dirname, "../../..");
const sourceManifestText = readFileSync(resolve(root, "tools/source-manifest.txt"), "utf8");
const manifest = createProjectReleaseGateManifest(); const manifest = createProjectReleaseGateManifest();
const virtualHalSimConfigSourceCoverage = createVirtualHalSimConfigSourceCoverageReport(); const virtualHalSimConfigSourceCoverage = createVirtualHalSimConfigSourceCoverageReport();
const virtualHalMotionControllerMatrix = createVirtualHalMotionControllerMatrixReport({ manifestText: sourceManifestText });
const virtualHalSimConfigPromotionCandidates = createVirtualHalSimConfigPromotionCandidateReport({
manifestText: sourceManifestText,
simConfigSourceCoverage: virtualHalSimConfigSourceCoverage,
motionControllerMatrix: virtualHalMotionControllerMatrix,
});
const virtualHalState = createVirtualHalState(); const virtualHalState = createVirtualHalState();
const browserDiagnosticsArtifact = { const browserDiagnosticsArtifact = {
apiName: "real-browser-simulation-diagnostics-artifact", apiName: "real-browser-simulation-diagnostics-artifact",
@@ -25,13 +38,31 @@ const browserDiagnosticsArtifact = {
virtualHalSimulationReplacement: createVirtualHalSimulationReplacementReport(virtualHalState), virtualHalSimulationReplacement: createVirtualHalSimulationReplacementReport(virtualHalState),
virtualHalSourceCompliance: createVirtualHalSourceComplianceReport({ halState: virtualHalState }), virtualHalSourceCompliance: createVirtualHalSourceComplianceReport({ halState: virtualHalState }),
virtualHalSimConfigSourceCoverage, virtualHalSimConfigSourceCoverage,
virtualHalSimConfigPromotionCandidates,
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport({ halState: virtualHalState }), virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport({ halState: virtualHalState }),
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport(), virtualHalMotionControllerMatrix,
}; };
const browserDiagnosticsSessionNestedArtifact = { ...browserDiagnosticsArtifact };
const savedSessionDiagnosticsArtifact = {
apiName: "real-browser-simulation-virtual-hal-session-diagnostics",
artifactVersion: 1,
ready: true,
phase: "release-ready",
sessionId: "release-url-workflow-virtual-hal-session",
machineId: "release-url-workflow-machine",
createdAt: "2026-06-17T00:00:00.000Z",
metadata: { source: "project-release-artifact-url-workflow-fixture" },
files: {},
virtualHalRevision: virtualHalState.revision,
validation: createProjectReleaseBrowserDiagnosticsArtifactValidation(browserDiagnosticsSessionNestedArtifact),
diagnosticsArtifact: browserDiagnosticsSessionNestedArtifact,
};
browserDiagnosticsArtifact.virtualHalSessionDiagnostics = savedSessionDiagnosticsArtifact;
assert.equal(createProjectReleaseBrowserDiagnosticsArtifactValidation(browserDiagnosticsArtifact).ready, true); assert.equal(createProjectReleaseBrowserDiagnosticsArtifactValidation(browserDiagnosticsArtifact).ready, true);
const readyArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({ const readyArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({
gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])), gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])),
virtualHalSimConfigSourceCoverage, virtualHalSimConfigSourceCoverage,
virtualHalMotionControllerMatrix: browserDiagnosticsArtifact.virtualHalMotionControllerMatrix,
})); }));
let fetchedUrl = null; let fetchedUrl = null;
@@ -67,6 +98,10 @@ assert.equal(readyWorkflow.fetchError, null);
assert.equal(readyWorkflow.jsonWorkflow.ready, true); assert.equal(readyWorkflow.jsonWorkflow.ready, true);
assert.equal(readyWorkflow.validation.ready, true); assert.equal(readyWorkflow.validation.ready, true);
assert.equal(readyWorkflow.diagnosticsValidation.ready, true); assert.equal(readyWorkflow.diagnosticsValidation.ready, true);
assert.equal(readyWorkflow.diagnosticsValidation.hasPromotionCandidates, true);
assert.equal(readyWorkflow.diagnosticsValidation.promotionCandidatesReady, true);
assert.equal(readyWorkflow.diagnosticsValidation.hasSessionDiagnostics, true);
assert.equal(readyWorkflow.diagnosticsValidation.sessionDiagnosticsReady, true);
assert.equal(readyWorkflow.diagnosticsFetched, true); assert.equal(readyWorkflow.diagnosticsFetched, true);
assert.equal(readyWorkflow.diagnosticsHttpStatus, 200); assert.equal(readyWorkflow.diagnosticsHttpStatus, 200);
assert.equal(readyWorkflow.actionPlan.ready, true); assert.equal(readyWorkflow.actionPlan.ready, true);
@@ -80,6 +115,7 @@ assert.deepEqual(
["json-workflow", "ready"], ["json-workflow", "ready"],
["artifact-validation", "ready"], ["artifact-validation", "ready"],
["browser-diagnostics", "ready"], ["browser-diagnostics", "ready"],
["saved-session-diagnostics", "ready"],
["next-command", "none"], ["next-command", "none"],
["missing", "none"], ["missing", "none"],
], ],
@@ -100,6 +136,97 @@ assert.deepEqual(readyActionPlan, {
rows: [], rows: [],
}); });
const liveOnlyDiagnosticsArtifact = { ...browserDiagnosticsSessionNestedArtifact };
const liveOnlyDiagnosticsWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
artifactUrl: "/project-release-readiness.json",
diagnosticsUrl: "/real-browser-simulation-live-only-diagnostics.json",
requireSavedSessionDiagnostics: true,
fetchRef: async (url) => ({
ok: true,
status: 200,
text: async () => url === "/real-browser-simulation-live-only-diagnostics.json"
? JSON.stringify(liveOnlyDiagnosticsArtifact)
: readyArtifactJson,
}),
});
assert.equal(liveOnlyDiagnosticsWorkflow.ready, false);
assert.equal(liveOnlyDiagnosticsWorkflow.diagnosticsValidation.ready, true);
assert.equal(liveOnlyDiagnosticsWorkflow.diagnosticsValidation.hasSessionDiagnostics, false);
assert.equal(liveOnlyDiagnosticsWorkflow.missing.includes("diagnostics.virtualHalSessionDiagnostics"), true);
assert.equal(
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(liveOnlyDiagnosticsWorkflow)
.rows.find(({ id }) => id === "saved-session-diagnostics")?.value,
"not provided",
);
const incompleteSourceSessionDiagnosticsArtifact = {
...browserDiagnosticsArtifact,
virtualHalSessionDiagnostics: {
...savedSessionDiagnosticsArtifact,
diagnosticsArtifact: {
...browserDiagnosticsSessionNestedArtifact,
virtualHalSourceCompliance: {
...browserDiagnosticsSessionNestedArtifact.virtualHalSourceCompliance,
complete: false,
},
},
},
};
incompleteSourceSessionDiagnosticsArtifact.virtualHalSessionDiagnostics.validation =
createProjectReleaseBrowserDiagnosticsArtifactValidation(
incompleteSourceSessionDiagnosticsArtifact.virtualHalSessionDiagnostics.diagnosticsArtifact,
);
const incompleteSourceDiagnosticsWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
artifactUrl: "/project-release-readiness.json",
diagnosticsUrl: "/real-browser-simulation-incomplete-source-diagnostics.json",
requireSavedSessionDiagnostics: true,
fetchRef: async (url) => ({
ok: true,
status: 200,
text: async () => url === "/real-browser-simulation-incomplete-source-diagnostics.json"
? JSON.stringify(incompleteSourceSessionDiagnosticsArtifact)
: readyArtifactJson,
}),
});
assert.equal(incompleteSourceDiagnosticsWorkflow.ready, false);
assert.equal(incompleteSourceDiagnosticsWorkflow.diagnosticsValidation.sessionDiagnosticsReady, false);
assert.equal(incompleteSourceDiagnosticsWorkflow.missing.includes("diagnostics.virtualHalSessionDiagnostics"), true);
assert.equal(
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(incompleteSourceDiagnosticsWorkflow)
.rows.find(({ id }) => id === "saved-session-diagnostics")?.value,
"blocked",
);
const missingManifestSessionDiagnosticsArtifact = {
...browserDiagnosticsArtifact,
virtualHalSessionDiagnostics: {
...savedSessionDiagnosticsArtifact,
diagnosticsArtifact: {
...browserDiagnosticsSessionNestedArtifact,
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport(),
},
},
};
missingManifestSessionDiagnosticsArtifact.virtualHalSessionDiagnostics.validation =
createProjectReleaseBrowserDiagnosticsArtifactValidation(
missingManifestSessionDiagnosticsArtifact.virtualHalSessionDiagnostics.diagnosticsArtifact,
);
const missingManifestDiagnosticsWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
artifactUrl: "/project-release-readiness.json",
diagnosticsUrl: "/real-browser-simulation-missing-manifest-diagnostics.json",
requireSavedSessionDiagnostics: true,
fetchRef: async (url) => ({
ok: true,
status: 200,
text: async () => url === "/real-browser-simulation-missing-manifest-diagnostics.json"
? JSON.stringify(missingManifestSessionDiagnosticsArtifact)
: readyArtifactJson,
}),
});
assert.equal(missingManifestDiagnosticsWorkflow.ready, false);
assert.equal(missingManifestDiagnosticsWorkflow.diagnosticsValidation.sessionDiagnosticsReady, false);
assert.equal(missingManifestDiagnosticsWorkflow.missing.includes("diagnostics.virtualHalSessionDiagnostics"), true);
const missingInputWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({ const missingInputWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
artifactUrl: "", artifactUrl: "",
fetchRef: null, fetchRef: null,

View File

@@ -1,6 +1,8 @@
import assert from "node:assert/strict"; import assert from "node:assert/strict";
import { import {
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
createVirtualHalMotionControllerMatrixReport,
createVirtualHalSimConfigSourceCoverageReport, createVirtualHalSimConfigSourceCoverageReport,
createProjectReleaseGateActionPlan, createProjectReleaseGateActionPlan,
createProjectReleaseGateExecutionManifest, createProjectReleaseGateExecutionManifest,
@@ -19,6 +21,9 @@ import {
const manifest = createProjectReleaseGateManifest(); const manifest = createProjectReleaseGateManifest();
const virtualHalSimConfigSourceCoverage = createVirtualHalSimConfigSourceCoverageReport(); const virtualHalSimConfigSourceCoverage = createVirtualHalSimConfigSourceCoverageReport();
const virtualHalMotionControllerMatrix = createVirtualHalMotionControllerMatrixReport({
manifestEntries: VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS.flatMap((target) => target.sourceFiles ?? []),
});
assert.equal(manifest.apiName, "project-release-gate-manifest"); assert.equal(manifest.apiName, "project-release-gate-manifest");
assert.equal(manifest.manifestVersion, 1); assert.equal(manifest.manifestVersion, 1);
@@ -204,6 +209,7 @@ assert.match(partialActionPlan.shellScript, /wasm-port\/tests\/host\/verify_proj
const report = createProjectReleaseReadinessReport({ const report = createProjectReleaseReadinessReport({
gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])), gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])),
virtualHalSimConfigSourceCoverage, virtualHalSimConfigSourceCoverage,
virtualHalMotionControllerMatrix,
}); });
assert.deepEqual(report.gateManifest, manifest); assert.deepEqual(report.gateManifest, manifest);
assert.equal(report.gateExecutionManifest.apiName, "project-release-gate-execution-manifest"); assert.equal(report.gateExecutionManifest.apiName, "project-release-gate-execution-manifest");
@@ -251,6 +257,11 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
label: "Virtual HAL sim-config source coverage", label: "Virtual HAL sim-config source coverage",
value: "ready", value: "ready",
}, },
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
value: "ready",
},
{ {
id: "blocked-runtime-families", id: "blocked-runtime-families",
label: "Blocked runtime families", label: "Blocked runtime families",
@@ -270,12 +281,13 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
}); });
assert.equal( assert.equal(
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport()).statusLine, createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport()).statusLine,
"Waiting: project-release-gate, virtual-hal-sim-config-source-coverage", "Waiting: project-release-gate, virtual-hal-sim-config-source-coverage, virtual-hal-motion-controller-matrix",
); );
assert.equal( assert.equal(
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport({ createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport({
gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])), gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])),
virtualHalSimConfigSourceCoverage, virtualHalSimConfigSourceCoverage,
virtualHalMotionControllerMatrix,
promotedRuntimeFamilies: ["L4-TOOL-DB"], promotedRuntimeFamilies: ["L4-TOOL-DB"],
})).rows.find(({ id }) => id === "blocked-runtime-families")?.value, })).rows.find(({ id }) => id === "blocked-runtime-families")?.value,
"promoted: L4-TOOL-DB", "promoted: L4-TOOL-DB",
@@ -318,6 +330,7 @@ assert.deepEqual(
["gate-result-matrix", "ready"], ["gate-result-matrix", "ready"],
["gate-action-plan", "ready"], ["gate-action-plan", "ready"],
["virtual-hal-sim-config-source-coverage", "ready"], ["virtual-hal-sim-config-source-coverage", "ready"],
["virtual-hal-motion-controller-matrix", "ready"],
["missing", "none"], ["missing", "none"],
], ],
); );
@@ -405,6 +418,7 @@ assert.deepEqual(
["json-workflow", "ready"], ["json-workflow", "ready"],
["artifact-validation", "ready"], ["artifact-validation", "ready"],
["browser-diagnostics", "not requested"], ["browser-diagnostics", "not requested"],
["saved-session-diagnostics", "not requested"],
["next-command", "none"], ["next-command", "none"],
["missing", "none"], ["missing", "none"],
], ],

View File

@@ -36,6 +36,7 @@ import {
VIRTUAL_HAL_COVERAGE_STATES, VIRTUAL_HAL_COVERAGE_STATES,
VIRTUAL_HAL_MOTION_CONTROLLER_MATRIX_FIXTURES, VIRTUAL_HAL_MOTION_CONTROLLER_MATRIX_FIXTURES,
VIRTUAL_HAL_PROJECT_PIN_GROUPS, VIRTUAL_HAL_PROJECT_PIN_GROUPS,
VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES,
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS, VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES, VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES,
VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS, VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS,
@@ -89,6 +90,7 @@ import {
createVirtualHalPinInventory, createVirtualHalPinInventory,
createVirtualHalPinRegistry, createVirtualHalPinRegistry,
createVirtualHalProjectReport, createVirtualHalProjectReport,
createVirtualHalSimConfigPromotionCandidateReport,
createVirtualHalSimConfigSourceCoverageReport, createVirtualHalSimConfigSourceCoverageReport,
createVirtualHalSimulationReplacementReport, createVirtualHalSimulationReplacementReport,
createVirtualHalState, createVirtualHalState,
@@ -239,6 +241,7 @@ const requiredExports = [
["createVirtualHalPinInventory", createVirtualHalPinInventory], ["createVirtualHalPinInventory", createVirtualHalPinInventory],
["createVirtualHalPinRegistry", createVirtualHalPinRegistry], ["createVirtualHalPinRegistry", createVirtualHalPinRegistry],
["createVirtualHalProjectReport", createVirtualHalProjectReport], ["createVirtualHalProjectReport", createVirtualHalProjectReport],
["createVirtualHalSimConfigPromotionCandidateReport", createVirtualHalSimConfigPromotionCandidateReport],
["createVirtualHalSimConfigSourceCoverageReport", createVirtualHalSimConfigSourceCoverageReport], ["createVirtualHalSimConfigSourceCoverageReport", createVirtualHalSimConfigSourceCoverageReport],
["createVirtualHalSimulationReplacementReport", createVirtualHalSimulationReplacementReport], ["createVirtualHalSimulationReplacementReport", createVirtualHalSimulationReplacementReport],
["createVirtualHalSimulationRuntimeReport", createVirtualHalSimulationRuntimeReport], ["createVirtualHalSimulationRuntimeReport", createVirtualHalSimulationRuntimeReport],
@@ -536,6 +539,10 @@ assert.match(sdkIndexText, /\bcreateVirtualHalSimulationReplacementReport\b/);
assert.match(sdkReadmeText, /\bcreateVirtualHalSimulationReplacementReport\b/); assert.match(sdkReadmeText, /\bcreateVirtualHalSimulationReplacementReport\b/);
assert.match(sdkIndexText, /\bcreateVirtualHalSimConfigSourceCoverageReport\b/); assert.match(sdkIndexText, /\bcreateVirtualHalSimConfigSourceCoverageReport\b/);
assert.match(sdkReadmeText, /\bcreateVirtualHalSimConfigSourceCoverageReport\b/); assert.match(sdkReadmeText, /\bcreateVirtualHalSimConfigSourceCoverageReport\b/);
assert.match(sdkIndexText, /\bVIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES\b/);
assert.match(sdkReadmeText, /\bVIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES\b/);
assert.match(sdkIndexText, /\bcreateVirtualHalSimConfigPromotionCandidateReport\b/);
assert.match(sdkReadmeText, /\bcreateVirtualHalSimConfigPromotionCandidateReport\b/);
assert.match(sdkIndexText, /\bcreateVirtualHalSourceComplianceReport\b/); assert.match(sdkIndexText, /\bcreateVirtualHalSourceComplianceReport\b/);
assert.match(sdkReadmeText, /\bcreateVirtualHalSourceComplianceReport\b/); assert.match(sdkReadmeText, /\bcreateVirtualHalSourceComplianceReport\b/);
assert.match(sdkIndexText, /\bcreateVirtualHalSystemCoverageReport\b/); assert.match(sdkIndexText, /\bcreateVirtualHalSystemCoverageReport\b/);
@@ -668,10 +675,23 @@ assert.equal(motionControllerMatrixReport.simConfigSourceFiles.includes("linuxcn
assert.equal(motionControllerMatrixReport.simConfigSourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini"), true); assert.equal(motionControllerMatrixReport.simConfigSourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini"), true);
assert.equal(motionControllerMatrixReport.simConfigSourceFiles.includes("linuxcnc/configs/sim/axis/vismach/puma/puma.ini"), true); assert.equal(motionControllerMatrixReport.simConfigSourceFiles.includes("linuxcnc/configs/sim/axis/vismach/puma/puma.ini"), true);
assert.equal(motionControllerMatrixReport.missingSimConfigTargets.length, 0); assert.equal(motionControllerMatrixReport.missingSimConfigTargets.length, 0);
assert.equal(motionControllerMatrixReport.manifestChecked, false);
assert.equal(motionControllerMatrixReport.missingManifestFiles.length, 0);
assert.equal(motionControllerMatrixReport.rows.some(({ id, inPosition }) => id === "xyz-multi-axis-in-position" && inPosition === true), true); assert.equal(motionControllerMatrixReport.rows.some(({ id, inPosition }) => id === "xyz-multi-axis-in-position" && inPosition === true), true);
assert.equal(motionControllerMatrixReport.rows.some(({ id, simConfigTargets }) => id === "x-single-axis-servo-dtg" && simConfigTargets.includes("external-offsets")), true); assert.equal(motionControllerMatrixReport.rows.some(({ id, simConfigTargets }) => id === "x-single-axis-servo-dtg" && simConfigTargets.includes("external-offsets")), true);
assert.equal(motionControllerMatrixReport.rows.every(({ axisFeedbackRows }) => axisFeedbackRows.every(({ matched }) => matched)), true); assert.equal(motionControllerMatrixReport.rows.every(({ axisFeedbackRows }) => axisFeedbackRows.every(({ matched }) => matched)), true);
assert.equal(virtualHalRuntime.getMotionControllerMatrixReport().complete, true); assert.equal(virtualHalRuntime.getMotionControllerMatrixReport().complete, true);
const manifestCheckedMotionControllerMatrixReport = createVirtualHalMotionControllerMatrixReport({
manifestText: sourceManifestText,
});
assert.equal(manifestCheckedMotionControllerMatrixReport.complete, true);
assert.equal(manifestCheckedMotionControllerMatrixReport.manifestChecked, true);
assert.equal(manifestCheckedMotionControllerMatrixReport.missingManifestFiles.length, 0);
assert.equal(manifestCheckedMotionControllerMatrixReport.rows.every(({ manifestChecked }) => manifestChecked === true), true);
assert.equal(
manifestCheckedMotionControllerMatrixReport.rows.every(({ missingManifestFiles }) => missingManifestFiles.length === 0),
true,
);
const steppedVirtualHal = stepVirtualHalMotion(virtualHalRuntime.getState(), { const steppedVirtualHal = stepVirtualHalMotion(virtualHalRuntime.getState(), {
target: { x: 3 }, target: { x: 3 },
maxVelocity: 10, maxVelocity: 10,
@@ -729,6 +749,41 @@ assert.equal(simConfigSourceCoverageReport.sourceFiles.includes("linuxcnc/config
assert.equal(simConfigSourceCoverageReport.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc"), true); assert.equal(simConfigSourceCoverageReport.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc"), true);
assert.equal(simConfigSourceCoverageReport.rows.every(({ sourceDerived }) => sourceDerived), true); assert.equal(simConfigSourceCoverageReport.rows.every(({ sourceDerived }) => sourceDerived), true);
assert.equal(virtualHalRuntime.getSimConfigSourceCoverageReport({ manifestText: sourceManifestText }).complete, true); assert.equal(virtualHalRuntime.getSimConfigSourceCoverageReport({ manifestText: sourceManifestText }).complete, true);
assert.equal(VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES.length >= 8, true);
const simConfigPromotionCandidateReport = createVirtualHalSimConfigPromotionCandidateReport({
manifestText: sourceManifestText,
simConfigSourceCoverage: simConfigSourceCoverageReport,
motionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
});
assert.equal(
simConfigPromotionCandidateReport.apiName,
"linuxcnc-wasm-virtual-hal-sim-config-promotion-candidate-report",
);
assert.equal(simConfigPromotionCandidateReport.complete, true);
assert.equal(simConfigPromotionCandidateReport.webSimulationSatisfied, true);
assert.equal(simConfigPromotionCandidateReport.inventoryBaselineUnchanged, true);
assert.equal(simConfigPromotionCandidateReport.manifestChecked, true);
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.rows.some((row) =>
row.id === "qtdragon-multi-joint-on-abort" &&
row.complete === true &&
row.currentNodeInventoryStatus === "PASS" &&
row.targetBrowserEvidence === "explicit-browser-diagnostics" &&
row.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini") &&
row.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc")
),
true,
);
assert.equal(simConfigPromotionCandidateReport.rows.every(({ blockedKind }) => blockedKind === "-"), true);
assert.equal(virtualHalRuntime.getSimConfigPromotionCandidateReport({
manifestText: sourceManifestText,
simConfigSourceCoverage: simConfigSourceCoverageReport,
motionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
}).complete, true);
const sourceComplianceReport = createVirtualHalSourceComplianceReport({ halState: virtualHalRuntime.getState() }); const sourceComplianceReport = createVirtualHalSourceComplianceReport({ halState: virtualHalRuntime.getState() });
assert.equal(sourceComplianceReport.apiName, "linuxcnc-wasm-virtual-hal-source-compliance-report"); assert.equal(sourceComplianceReport.apiName, "linuxcnc-wasm-virtual-hal-source-compliance-report");
assert.equal(sourceComplianceReport.complete, true); assert.equal(sourceComplianceReport.complete, true);
@@ -768,25 +823,47 @@ assert.equal(
restoreVirtualHalStateFromSessionSnapshot(virtualHalSessionSnapshot).hal.nets["x-pos"].includes("axis.x.pos-cmd"), restoreVirtualHalStateFromSessionSnapshot(virtualHalSessionSnapshot).hal.nets["x-pos"].includes("axis.x.pos-cmd"),
true, true,
); );
const browserDiagnosticsArtifactValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation({ const browserDiagnosticsArtifact = {
apiName: "real-browser-simulation-diagnostics-artifact", apiName: "real-browser-simulation-diagnostics-artifact",
virtualHal: virtualHalRuntime.getState(), virtualHal: virtualHalRuntime.getState(),
virtualHalSimulationReplacement: replacementReport, virtualHalSimulationReplacement: replacementReport,
virtualHalSourceCompliance: sourceComplianceReport, virtualHalSourceCompliance: sourceComplianceReport,
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport, virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
virtualHalSimConfigPromotionCandidates: simConfigPromotionCandidateReport,
virtualHalCommandScriptFixtures: commandScriptFixtureReport, virtualHalCommandScriptFixtures: commandScriptFixtureReport,
virtualHalMotionControllerMatrix: motionControllerMatrixReport, virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
}); };
const browserDiagnosticsSessionNestedArtifact = { ...browserDiagnosticsArtifact };
browserDiagnosticsArtifact.virtualHalSessionDiagnostics = {
apiName: "real-browser-simulation-virtual-hal-session-diagnostics",
artifactVersion: 1,
ready: true,
phase: "release-ready",
sessionId: virtualHalSessionSnapshot.sessionId,
machineId: virtualHalSessionSnapshot.payload.machineId,
createdAt: virtualHalSessionSnapshot.createdAt,
metadata: { ...(virtualHalSessionSnapshot.metadata ?? {}) },
files: { ...(virtualHalSessionSnapshot.payload.files ?? {}) },
virtualHalRevision: virtualHalSessionPayload.state.revision,
validation: createProjectReleaseBrowserDiagnosticsArtifactValidation(browserDiagnosticsArtifact),
diagnosticsArtifact: browserDiagnosticsSessionNestedArtifact,
};
const browserDiagnosticsArtifactValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation(browserDiagnosticsArtifact);
assert.equal(browserDiagnosticsArtifactValidation.apiName, "project-release-browser-diagnostics-artifact-validation"); assert.equal(browserDiagnosticsArtifactValidation.apiName, "project-release-browser-diagnostics-artifact-validation");
assert.equal(browserDiagnosticsArtifactValidation.ready, true); assert.equal(browserDiagnosticsArtifactValidation.ready, true);
assert.equal(browserDiagnosticsArtifactValidation.sourceComplianceReady, true); assert.equal(browserDiagnosticsArtifactValidation.sourceComplianceReady, true);
assert.equal(browserDiagnosticsArtifactValidation.simConfigSourceCoverageReady, true); assert.equal(browserDiagnosticsArtifactValidation.simConfigSourceCoverageReady, true);
assert.equal(browserDiagnosticsArtifactValidation.promotionCandidatesReady, true);
assert.equal(browserDiagnosticsArtifactValidation.hasPromotionCandidates, true);
assert.equal(browserDiagnosticsArtifactValidation.commandScriptFixturesReady, true); assert.equal(browserDiagnosticsArtifactValidation.commandScriptFixturesReady, true);
assert.equal(browserDiagnosticsArtifactValidation.motionControllerMatrixReady, true); assert.equal(browserDiagnosticsArtifactValidation.motionControllerMatrixReady, true);
assert.equal(browserDiagnosticsArtifactValidation.hasSessionDiagnostics, true);
assert.equal(browserDiagnosticsArtifactValidation.sessionDiagnosticsReady, true);
assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/src/hal/utils/halcmd_commands.cc"), true); assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/src/hal/utils/halcmd_commands.cc"), true);
assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/src/emc/motion/axis.c"), true); assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/src/emc/motion/axis.c"), true);
assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini"), true); assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini"), true);
assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini"), true); assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini"), true);
assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc"), true);
assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/configs/sim/axis/vismach/puma/puma.ini"), true); assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/configs/sim/axis/vismach/puma/puma.ini"), true);
const runtimeProjectReport = virtualHalRuntime.getProjectReport({ availableFunctions: VIRTUAL_HAL_WASM_BRIDGE_FUNCTIONS }); const runtimeProjectReport = virtualHalRuntime.getProjectReport({ availableFunctions: VIRTUAL_HAL_WASM_BRIDGE_FUNCTIONS });
assert.equal(runtimeProjectReport.simulationRuntime.replacesHostRuntimeForSimulation, true); assert.equal(runtimeProjectReport.simulationRuntime.replacesHostRuntimeForSimulation, true);
@@ -857,16 +934,20 @@ assert.equal(
capabilities: batchAcceptanceWorkflow.capabilities, capabilities: batchAcceptanceWorkflow.capabilities,
observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"], observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"],
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport, virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
}).rows.find(({ id }) => id === "checklist")?.value, }).rows.find(({ id }) => id === "checklist")?.value,
"4/4 passed", "5/5 passed",
); );
const batchAcceptanceReport = createProjectBatchAcceptanceReport({ const batchAcceptanceReport = createProjectBatchAcceptanceReport({
batchId: "sdk-surface-batch", batchId: "sdk-surface-batch",
capabilities: batchAcceptanceWorkflow.capabilities, capabilities: batchAcceptanceWorkflow.capabilities,
observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"], observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"],
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport, virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
}); });
assert.equal(batchAcceptanceReport.virtualHalSimConfigSourceCoverage.complete, true); assert.equal(batchAcceptanceReport.virtualHalSimConfigSourceCoverage.complete, true);
assert.equal(batchAcceptanceReport.virtualHalMotionControllerMatrix.complete, true);
assert.equal(batchAcceptanceReport.workflow.virtualHalMotionControllerMatrixReady, true);
assert.equal(createProjectBatchAcceptanceReportValidation(batchAcceptanceReport).ready, true); assert.equal(createProjectBatchAcceptanceReportValidation(batchAcceptanceReport).ready, true);
assert.equal( assert.equal(
createProjectBatchAcceptanceReportValidationSummaryViewModel( createProjectBatchAcceptanceReportValidationSummaryViewModel(
@@ -920,7 +1001,11 @@ assert.equal(
); );
assert.equal(releaseReadinessWaiting.apiName, "project-release-readiness-report"); assert.equal(releaseReadinessWaiting.apiName, "project-release-readiness-report");
assert.equal(releaseReadinessWaiting.ready, false); assert.equal(releaseReadinessWaiting.ready, false);
assert.deepEqual(releaseReadinessWaiting.missing, ["project-release-gate", "virtual-hal-sim-config-source-coverage"]); assert.deepEqual(releaseReadinessWaiting.missing, [
"project-release-gate",
"virtual-hal-sim-config-source-coverage",
"virtual-hal-motion-controller-matrix",
]);
assert.equal(releaseReadinessWaiting.simConfigInventory.unexpectedFail, 0); assert.equal(releaseReadinessWaiting.simConfigInventory.unexpectedFail, 0);
assert.deepEqual(releaseReadinessWaiting.promotedBlockedFamilies, []); assert.deepEqual(releaseReadinessWaiting.promotedBlockedFamilies, []);
assert.equal(releaseReadinessWaiting.gateActionPlan.apiName, "project-release-gate-action-plan"); assert.equal(releaseReadinessWaiting.gateActionPlan.apiName, "project-release-gate-action-plan");
@@ -933,10 +1018,12 @@ assert.equal(
const releaseReadinessReady = createProjectReleaseReadinessReport({ const releaseReadinessReady = createProjectReleaseReadinessReport({
observedOutputs: ["project_release_gate=ok"], observedOutputs: ["project_release_gate=ok"],
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport, virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
}); });
assert.equal(releaseReadinessReady.ready, true); assert.equal(releaseReadinessReady.ready, true);
assert.equal(releaseReadinessReady.releaseGate.passed, true); assert.equal(releaseReadinessReady.releaseGate.passed, true);
assert.equal(releaseReadinessReady.virtualHalSimConfigSourceCoverageReady, true); assert.equal(releaseReadinessReady.virtualHalSimConfigSourceCoverageReady, true);
assert.equal(releaseReadinessReady.virtualHalMotionControllerMatrixReady, true);
assert.deepEqual(releaseReadinessReady.gateManifest, createProjectReleaseGateManifest()); assert.deepEqual(releaseReadinessReady.gateManifest, createProjectReleaseGateManifest());
assert.equal(releaseReadinessReady.gateResultMatrix.rows.length, createProjectReleaseGateManifest().gateCount); assert.equal(releaseReadinessReady.gateResultMatrix.rows.length, createProjectReleaseGateManifest().gateCount);
assert.equal(createProjectReleaseGateActionPlan({ assert.equal(createProjectReleaseGateActionPlan({
@@ -953,6 +1040,7 @@ assert.equal(
const releaseReadinessWithScreenshots = createProjectReleaseReadinessReport({ const releaseReadinessWithScreenshots = createProjectReleaseReadinessReport({
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])), gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport, virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
axisScreenshotArtifacts: ["desktop-preview", "desktop-dro", "mobile-preview", "mobile-mdi"].map((viewportName) => ({ axisScreenshotArtifacts: ["desktop-preview", "desktop-dro", "mobile-preview", "mobile-mdi"].map((viewportName) => ({
apiName: "real-browser-simulation-axis-screenshot-artifact", apiName: "real-browser-simulation-axis-screenshot-artifact",
artifactVersion: 1, artifactVersion: 1,
@@ -987,6 +1075,7 @@ assert.equal(
const releaseReadinessArtifact = parseProjectReleaseReadinessArtifactJson(JSON.stringify(createProjectReleaseReadinessReport({ const releaseReadinessArtifact = parseProjectReleaseReadinessArtifactJson(JSON.stringify(createProjectReleaseReadinessReport({
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])), gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport, virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
}))); })));
assert.equal(releaseReadinessArtifact.apiName, "project-release-readiness-report"); assert.equal(releaseReadinessArtifact.apiName, "project-release-readiness-report");
const releaseReadinessArtifactValidation = createProjectReleaseReadinessArtifactValidation(releaseReadinessArtifact); const releaseReadinessArtifactValidation = createProjectReleaseReadinessArtifactValidation(releaseReadinessArtifact);
@@ -1007,6 +1096,7 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
gateResultMatrixReady: true, gateResultMatrixReady: true,
gateActionPlanReady: true, gateActionPlanReady: true,
virtualHalSimConfigSourceCoverageReady: true, virtualHalSimConfigSourceCoverageReady: true,
virtualHalMotionControllerMatrixReady: true,
axisScreenshotArtifactSummaryReady: true, axisScreenshotArtifactSummaryReady: true,
axisScreenshotArtifactCount: 0, axisScreenshotArtifactCount: 0,
blockedRuntimeFamilies: [ blockedRuntimeFamilies: [
@@ -1055,6 +1145,11 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
label: "Virtual HAL sim-config source coverage", label: "Virtual HAL sim-config source coverage",
value: "ready", value: "ready",
}, },
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
value: "ready",
},
{ {
id: "axis-screenshot-artifacts", id: "axis-screenshot-artifacts",
label: "AXIS screenshot artifacts", label: "AXIS screenshot artifacts",
@@ -1152,6 +1247,7 @@ assert.deepEqual(
"simConfigInventory.skipped", "simConfigInventory.skipped",
"simConfigInventory.unexpectedFail", "simConfigInventory.unexpectedFail",
"virtualHalSimConfigSourceCoverage", "virtualHalSimConfigSourceCoverage",
"virtualHalMotionControllerMatrix",
"blockedRuntimeFamilies", "blockedRuntimeFamilies",
"gateManifest", "gateManifest",
"gateExecutionManifest", "gateExecutionManifest",

View File

@@ -4,6 +4,7 @@ import { fileURLToPath } from "node:url";
import { import {
createProjectBatchAcceptanceReport, createProjectBatchAcceptanceReport,
createVirtualHalMotionControllerMatrixReport,
createVirtualHalSimConfigSourceCoverageReport, createVirtualHalSimConfigSourceCoverageReport,
} from "../../../runtime/sdk/src/index.js"; } from "../../../runtime/sdk/src/index.js";
@@ -29,6 +30,13 @@ const report = createProjectBatchAcceptanceReport({
evidence: "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report", evidence: "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report",
command: "wasm-port/tests/sdk/node/verify_sdk_surface.sh", command: "wasm-port/tests/sdk/node/verify_sdk_surface.sh",
}, },
{
id: "virtual-hal-motion-controller-matrix",
type: "api",
label: "Virtual HAL motion controller matrix",
evidence: "linuxcnc-wasm-virtual-hal-motion-controller-matrix-report",
command: "wasm-port/tests/sdk/node/verify_sdk_surface.sh",
},
{ {
id: "project-batch-acceptance-workflow-gate", id: "project-batch-acceptance-workflow-gate",
type: "gate", type: "gate",
@@ -41,6 +49,9 @@ const report = createProjectBatchAcceptanceReport({
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport({ virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport({
manifestText: sourceManifestText, manifestText: sourceManifestText,
}), }),
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport({
manifestText: sourceManifestText,
}),
}); });
mkdirSync(dirname(outputPath), { recursive: true }); mkdirSync(dirname(outputPath), { recursive: true });

View File

@@ -1,18 +1,24 @@
import assert from "node:assert/strict"; import assert from "node:assert/strict";
import { import {
createProjectReleaseBrowserDiagnosticsArtifactValidation,
createProjectReleaseGateManifest, createProjectReleaseGateManifest,
createProjectReleaseReadinessReport, createProjectReleaseReadinessReport,
} from "../../../runtime/sdk/src/project-release-readiness.js"; } from "../../../runtime/sdk/src/project-release-readiness.js";
import { import {
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
createVirtualHalCommandScriptFixtureReport, createVirtualHalCommandScriptFixtureReport,
createVirtualHalMotionControllerMatrixReport, createVirtualHalMotionControllerMatrixReport,
createVirtualHalSimConfigPromotionCandidateReport,
createVirtualHalSimConfigSourceCoverageReport, createVirtualHalSimConfigSourceCoverageReport,
createVirtualHalSimulationReplacementReport, createVirtualHalSimulationReplacementReport,
createVirtualHalSourceComplianceReport, createVirtualHalSourceComplianceReport,
createVirtualHalState, createVirtualHalState,
} from "../../../runtime/sdk/src/linuxcnc-hal.js"; } from "../../../runtime/sdk/src/linuxcnc-hal.js";
const VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES =
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS.flatMap((target) => target.sourceFiles ?? []);
import { import {
INI_PANEL_ENTRIES, INI_PANEL_ENTRIES,
controlPageStatusText, controlPageStatusText,
@@ -1356,21 +1362,54 @@ assert.equal(workflowDomMountResult.ready, false);
assert.equal(workflowDomMountResult.renderResult.rowCount, 8); assert.equal(workflowDomMountResult.renderResult.rowCount, 8);
assert.equal(workflowDomMountResult.renderResult.dataset.handoffScope, "workflow"); assert.equal(workflowDomMountResult.renderResult.dataset.handoffScope, "workflow");
assert.deepEqual(createIniPanelShellWorkflowOverviewContract(), shellViewModel.shellWorkflowOverviewContract); assert.deepEqual(createIniPanelShellWorkflowOverviewContract(), shellViewModel.shellWorkflowOverviewContract);
const releaseReadinessMotionControllerMatrix = createVirtualHalMotionControllerMatrixReport({
manifestEntries: VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
});
const releaseReadinessArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({ const releaseReadinessArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])), gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(), virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
virtualHalMotionControllerMatrix: releaseReadinessMotionControllerMatrix,
})); }));
const browserDiagnosticsVirtualHalState = createVirtualHalState(); const browserDiagnosticsVirtualHalState = createVirtualHalState();
const browserDiagnosticsArtifactJson = JSON.stringify({ const browserDiagnosticsArtifact = {
apiName: "real-browser-simulation-diagnostics-artifact", apiName: "real-browser-simulation-diagnostics-artifact",
artifactVersion: 1, artifactVersion: 1,
virtualHal: browserDiagnosticsVirtualHalState, virtualHal: browserDiagnosticsVirtualHalState,
virtualHalSimulationReplacement: createVirtualHalSimulationReplacementReport(browserDiagnosticsVirtualHalState), virtualHalSimulationReplacement: createVirtualHalSimulationReplacementReport(browserDiagnosticsVirtualHalState),
virtualHalSourceCompliance: createVirtualHalSourceComplianceReport({ halState: browserDiagnosticsVirtualHalState }), virtualHalSourceCompliance: createVirtualHalSourceComplianceReport({ halState: browserDiagnosticsVirtualHalState }),
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(), 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,
}),
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport({ halState: browserDiagnosticsVirtualHalState }), virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport({ halState: browserDiagnosticsVirtualHalState }),
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport(), virtualHalMotionControllerMatrix: releaseReadinessMotionControllerMatrix,
}); };
const browserDiagnosticsSessionNestedArtifact = { ...browserDiagnosticsArtifact };
browserDiagnosticsArtifact.virtualHalSessionDiagnostics = {
apiName: "real-browser-simulation-virtual-hal-session-diagnostics",
artifactVersion: 1,
ready: true,
phase: "release-ready",
sessionId: "ini-panel-ui-shell-virtual-hal-session",
machineId: "ini-panel-ui-shell-machine",
createdAt: "2026-06-17T00:00:00.000Z",
metadata: { source: "ini-panel-ui-shell-url-fixture" },
files: {},
virtualHalRevision: browserDiagnosticsVirtualHalState.revision,
validation: createProjectReleaseBrowserDiagnosticsArtifactValidation(browserDiagnosticsSessionNestedArtifact),
diagnosticsArtifact: browserDiagnosticsSessionNestedArtifact,
};
const browserDiagnosticsArtifactJson = JSON.stringify(browserDiagnosticsArtifact);
const releaseArtifactValidation = validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson( const releaseArtifactValidation = validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson(
releaseReadinessArtifactJson, releaseReadinessArtifactJson,
); );
@@ -1675,7 +1714,7 @@ const releaseArtifactMountResult = mountIniPanelShellWorkflowOverviewReleaseRead
rowsNode: releaseArtifactRowsNode, rowsNode: releaseArtifactRowsNode,
}); });
assert.equal(releaseArtifactRenderResult.rendered, true); assert.equal(releaseArtifactRenderResult.rendered, true);
assert.equal(releaseArtifactRenderResult.rowCount, 10); assert.equal(releaseArtifactRenderResult.rowCount, 11);
assert.equal(releaseArtifactRenderResult.dataset.handoffScope, "workflow-overview-release-readiness-artifact"); assert.equal(releaseArtifactRenderResult.dataset.handoffScope, "workflow-overview-release-readiness-artifact");
assert.equal( assert.equal(
releaseArtifactRenderResult.rowIds.includes("gate-execution-manifest"), releaseArtifactRenderResult.rowIds.includes("gate-execution-manifest"),
@@ -1697,8 +1736,12 @@ assert.equal(
releaseArtifactRenderResult.rowIds.includes("virtual-hal-sim-config-source-coverage"), releaseArtifactRenderResult.rowIds.includes("virtual-hal-sim-config-source-coverage"),
true, true,
); );
assert.equal(
releaseArtifactRenderResult.rowIds.includes("virtual-hal-motion-controller-matrix"),
true,
);
assert.equal(releaseArtifactMountResult.ready, true); assert.equal(releaseArtifactMountResult.ready, true);
assert.equal(releaseArtifactMountResult.renderResult.rowCount, 10); assert.equal(releaseArtifactMountResult.renderResult.rowCount, 11);
assert.deepEqual( assert.deepEqual(
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson("{").missing, validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson("{").missing,
["artifact-json"], ["artifact-json"],
@@ -1745,13 +1788,17 @@ assert.equal(releaseArtifactUrlWorkflow.httpStatus, 200);
assert.equal(releaseArtifactUrlWorkflow.diagnosticsHttpStatus, 200); assert.equal(releaseArtifactUrlWorkflow.diagnosticsHttpStatus, 200);
assert.equal(releaseArtifactUrlWorkflow.validation.ready, true); assert.equal(releaseArtifactUrlWorkflow.validation.ready, true);
assert.equal(releaseArtifactUrlWorkflow.diagnosticsValidation.ready, true); assert.equal(releaseArtifactUrlWorkflow.diagnosticsValidation.ready, true);
assert.equal(releaseArtifactUrlWorkflow.diagnosticsValidation.hasPromotionCandidates, true);
assert.equal(releaseArtifactUrlWorkflow.diagnosticsValidation.promotionCandidatesReady, true);
assert.equal(releaseArtifactUrlWorkflow.diagnosticsValidation.hasSessionDiagnostics, true);
assert.equal(releaseArtifactUrlWorkflow.diagnosticsValidation.sessionDiagnosticsReady, true);
assert.equal( assert.equal(
releaseArtifactUrlWorkflow.validationSummaryViewModel.statusLine, releaseArtifactUrlWorkflow.validationSummaryViewModel.statusLine,
"Ready: Artifact evidence complete", "Ready: Artifact evidence complete",
); );
assert.equal(releaseArtifactUrlWorkflow.renderState.statusLine, "Ready: No blocking reasons"); assert.equal(releaseArtifactUrlWorkflow.renderState.statusLine, "Ready: No blocking reasons");
assert.equal(releaseArtifactUrlWorkflow.mountResult.ready, true); assert.equal(releaseArtifactUrlWorkflow.mountResult.ready, true);
assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 10); assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 11);
const releaseArtifactUrlWorkflowSummary = const releaseArtifactUrlWorkflowSummary =
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel( createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
releaseArtifactUrlWorkflow, releaseArtifactUrlWorkflow,
@@ -1776,6 +1823,10 @@ assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "browser-diagnostics")?.value, releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "browser-diagnostics")?.value,
"ready", "ready",
); );
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "saved-session-diagnostics")?.value,
"ready",
);
assert.equal(releaseArtifactUrlWorkflow.actionPlan.ready, true); assert.equal(releaseArtifactUrlWorkflow.actionPlan.ready, true);
assert.equal(releaseArtifactUrlWorkflow.actionPlan.nextCommand, null); assert.equal(releaseArtifactUrlWorkflow.actionPlan.nextCommand, null);
assert.equal( assert.equal(
@@ -1841,17 +1892,21 @@ const releaseArtifactUrlWorkflowMountResult =
rowsNode: releaseArtifactUrlWorkflowRowsNode, rowsNode: releaseArtifactUrlWorkflowRowsNode,
}); });
assert.equal(releaseArtifactUrlWorkflowRenderResult.rendered, true); assert.equal(releaseArtifactUrlWorkflowRenderResult.rendered, true);
assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 11); assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 12);
assert.equal( assert.equal(
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("browser-diagnostics"), releaseArtifactUrlWorkflowRenderResult.rowIds.includes("browser-diagnostics"),
true, true,
); );
assert.equal(
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("saved-session-diagnostics"),
true,
);
assert.equal( assert.equal(
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("mount-result"), releaseArtifactUrlWorkflowRenderResult.rowIds.includes("mount-result"),
true, true,
); );
assert.equal(releaseArtifactUrlWorkflowMountResult.ready, true); assert.equal(releaseArtifactUrlWorkflowMountResult.ready, true);
assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 11); assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 12);
const releaseArtifactUrlWorkflowMissingDom = await loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl({ const releaseArtifactUrlWorkflowMissingDom = await loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl({
artifactUrl: "/project-release-readiness.json", artifactUrl: "/project-release-readiness.json",
fetchRef: async () => ({ fetchRef: async () => ({