From 24e1fc18b149084e8b883745eb2db0aa5b8657ac Mon Sep 17 00:00:00 2001 From: wangdequan Date: Tue, 16 Jun 2026 20:59:21 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E7=9C=9F=E5=AE=9E=E6=B5=8F?= =?UTF-8?q?=E8=A7=88=E5=99=A8=E4=BB=BF=E7=9C=9F=E6=8E=A8=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 结论:新增 LinuxCNC WASM 驱动的真实浏览器仿真页面,接入 browser smoke、release gate、SDK 与项目交付文档,并完成当前推进批次的验收记录。 --- PROJECT_COMPLETION_TRACKER.md | 38 +- text15.txt | 1158 +++++++++++++ wasm-port/README.md | 15 +- wasm-port/docs/panel-entry.md | 45 + wasm-port/docs/project-release-handoff.md | 95 ++ .../docs/real-browser-simulation-priority.md | 82 + wasm-port/runtime/sdk/README.md | 123 +- wasm-port/runtime/sdk/src/index.js | 40 + .../sdk/src/project-release-readiness.js | 864 ++++++++++ wasm-port/runtime/ui/ini-panel/launch.html | 110 ++ wasm-port/runtime/ui/ini-panel/ui-shell.js | 1481 +++++++++++++++++ .../ui/ini-panel/workflow-overview.html | 136 ++ wasm-port/runtime/ui/simulation/index.html | 338 ++++ .../runtime/ui/simulation/simulation-app.js | 239 +++ ...anel_shell_integration_workflow_smoke.html | 502 ++++++ .../ini_panel_workflow_overview_smoke.html | 296 +++- .../browser/real_simulation_page_smoke.html | 83 + .../release_artifact_url_workflow_smoke.html | 2 +- .../browser/verify_real_simulation_browser.sh | 78 + .../verify_project_release_handoff_docs.mjs | 107 ++ ..._real_browser_simulation_priority_docs.mjs | 53 + ...y_real_browser_simulation_priority_docs.sh | 6 + .../project-release-readiness-ready.json | 109 +- wasm-port/tests/host/verify_host_smokes.sh | 2 + .../tests/host/verify_project_release_gate.sh | 3 + ...ify_project_release_readiness_artifact.mjs | 9 +- ...ite_project_release_readiness_artifact.mjs | 2 + ...rify_project_batch_acceptance_artifact.mjs | 51 + ...erify_project_batch_acceptance_artifact.sh | 6 + ...rify_project_batch_acceptance_workflow.mjs | 507 ++++++ ...erify_project_batch_acceptance_workflow.sh | 9 + .../verify_project_release_gate_manifest.mjs | 44 +- .../tests/sdk/node/verify_sdk_surface.mjs | 317 ++++ .../tests/sdk/node/verify_sdk_surface.sh | 1 + ...rite_project_batch_acceptance_artifact.mjs | 36 + .../ui/node/verify_ini_panel_entry_docs.mjs | 25 + .../verify_ini_panel_launch_api_manifest.mjs | 54 + ...y_ini_panel_shell_integration_manifest.mjs | 114 +- .../ui/node/verify_ini_panel_ui_shell.mjs | 442 ++++- 39 files changed, 7563 insertions(+), 59 deletions(-) create mode 100644 wasm-port/docs/real-browser-simulation-priority.md create mode 100644 wasm-port/runtime/ui/simulation/index.html create mode 100644 wasm-port/runtime/ui/simulation/simulation-app.js create mode 100644 wasm-port/tests/browser/real_simulation_page_smoke.html create mode 100755 wasm-port/tests/browser/verify_real_simulation_browser.sh create mode 100644 wasm-port/tests/docs/node/verify_real_browser_simulation_priority_docs.mjs create mode 100755 wasm-port/tests/docs/node/verify_real_browser_simulation_priority_docs.sh create mode 100644 wasm-port/tests/sdk/node/verify_project_batch_acceptance_artifact.mjs create mode 100755 wasm-port/tests/sdk/node/verify_project_batch_acceptance_artifact.sh create mode 100644 wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.mjs create mode 100755 wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh create mode 100644 wasm-port/tests/sdk/node/write_project_batch_acceptance_artifact.mjs diff --git a/PROJECT_COMPLETION_TRACKER.md b/PROJECT_COMPLETION_TRACKER.md index c3499ef..782e9b7 100644 --- a/PROJECT_COMPLETION_TRACKER.md +++ b/PROJECT_COMPLETION_TRACKER.md @@ -9,10 +9,11 @@ turn-by-turn continuation notes and next-batch execution records. ## Current Status - Active continuation file: `text15.txt` -- Latest completed batch: project release artifact URL workflow action-plan SDK API +- Latest completed batch: real browser simulation page MVP - Latest relevant commit: use `git log -1 --oneline` after each committed batch - Working tree at tracker creation: clean - Required policy: LinuxCNC remains the only CNC semantic source +- First priority: real UI/browser CNC simulation page; see `wasm-port/docs/real-browser-simulation-priority.md` - Current scope status: complete after final clean-tree release validation ## Completion Definition @@ -20,6 +21,7 @@ turn-by-turn continuation notes and next-batch execution records. The project is complete for the current scope when all of the following are true: - Browser UI workflows are usable and validated: + - real browser CNC simulation page - INI panel - control page - workflow overview @@ -37,18 +39,20 @@ The project is complete for the current scope when all of the following are true | Area | Status | Notes | | --- | --- | --- | | LinuxCNC semantic boundary | Stable | JS/browser remains glue, staging, OPFS, WASM/browser boundary, docs, and tests only. | +| Real browser simulation priority | Active first priority | Future batches should prioritize the real UI/browser CNC simulation page, with visible machine/session loading, G-code program state, LinuxCNC-backed interpreter execution, machine readiness/status panels, toolpath/preview rendering from LinuxCNC-produced output or validated runtime events, and browser smoke coverage. First entry point: `wasm-port/runtime/ui/simulation/index.html`; gate: `wasm-port/tests/browser/verify_real_simulation_browser.sh`. See `wasm-port/docs/real-browser-simulation-priority.md`. | +| Real browser simulation page | MVP active | `runtime/ui/simulation/index.html` loads in Chromium, runs a representative G-code program through LinuxCNC interpreter WASM, renders program lines, final axis readout, canonical event table, raw canonical output, and SVG toolpath preview. | | Vendor/source guard | Stable | `verify_vendor_sync.sh` and standalone semantic guard are required every batch. | | Interpreter WASM smoke | Stable | `interp_wasm_node_smoke=ok`. | | Sim config inventory | Stable with skips | `executed=28`, `passed=28`, `skipped=131`, `unexpected_fail=0`; `docs/sim-configs-coverage-handoff.md` records the release-gate baseline. | | Native nc_files baseline | Stable | Last recorded Layer 1: `total 107`, `pass 101`, `expected_fail 6`, `unexpected_fail 0`. | | Native sim configs baseline | Stable | Last recorded Layer 2: `total 159`, `pass 151`, `expected_fail 8`, `unexpected_fail 0`. | | OPFS/session persistence | Stable for current scope | Browser smoke passes, SDK re-exports OPFS/session helpers including `readMachineSessionReadiness()` and `createMachineSessionPersistenceSummary()`, the UI exposes `getMachineSessionPersistenceSummary()`, and `docs/opfs-session-persistence.md` defines the project-level release gate. | -| SDK/API surface | Stable for current scope | SDK re-exports core, OPFS/session readiness/load helpers, sim-config staging, project release gate manifest/result matrix/action-plan/readiness summary view-model, project release readiness report/artifact JSON workflow/artifact URL workflow summary/action-plan/artifact validation summary/action-plan plus URL loading workflow, INI panel entry/launcher/control-page/shell/workflow overview helpers, the session readiness workflow report, and the shell API surface inventory with dedicated Node smokes. | -| Browser/UI workflow | Stable for current scope | Workflow overview embedding mount DOM closure is complete, and INI panel session workflows plus read-only control/shell handoff expose, render, fetch, validate, summarize, and report OPFS/session readiness, OPFS/session persistence summary, release readiness artifact validation/rendering/URL workflow summaries, and API surface inventory before loading or reading WASM session state. | +| SDK/API surface | Stable for current scope | SDK re-exports core, OPFS/session readiness/load helpers, sim-config staging, project release gate manifest/result matrix/action-plan/readiness summary view-model, project batch acceptance capability matrix/workflow/summary/action-plan/checklist/report validation summary/action-plan, JSON workflow, URL workflow helpers, and artifact writer/validator gate, project release readiness report/artifact JSON workflow/artifact URL workflow summary/action-plan/artifact validation summary/action-plan plus URL loading workflow, INI panel entry/launcher/control-page/shell/workflow overview helpers including release artifact JSON and URL workflow summary/action-plan/render-state DOM mount helpers, validation action-plan render/mount helpers, URL workflow action-plan, and gate execution summary render/mount helpers, the session readiness workflow report, and the shell API surface inventory with dedicated Node smokes. | +| Browser/UI workflow | Stable for current scope | Workflow overview embedding mount DOM closure is complete, and INI panel session workflows plus read-only control/shell handoff expose, render, mount, fetch, validate, summarize, action-plan, and report OPFS/session readiness, OPFS/session persistence summary, release readiness artifact JSON/URL workflows, validation/rendering/URL workflow summaries/action-plans/gate execution summaries, and API surface inventory before loading or reading WASM session state. | | Workflow overview embedding | Stable for current scope | Report/display/render, DOM contract/readiness/renderer, mount result/display/render, mount DOM contract/readiness/renderer/wrapper exist. | | Host/runtime boundary proof | Stable blocked state | Host smoke passes; runtime families remain blocked and `docs/host-runtime-boundary-handoff.md` records host readiness, promotion blockers, dispatch, and evidence gates. | | Documentation/release gate | Stable for current scope | `docs/project-release-handoff.md` links README, panel entry, SDK, OPFS/session, sim-config, host/runtime, source reuse, drift, tracker acceptance paths, and the machine-readable release readiness report/artifact. | -| Project release gate wrapper | Stable | `tests/host/verify_project_release_gate.sh` runs the documented minimum release validation commands including the release artifact URL browser workflow gate and SDK URL workflow gate, writes `build/project-release-readiness.json`, verifies embedded gate manifest/result matrix/action-plan evidence through the SDK artifact validation workflow, and ends with `project_release_gate=ok`. | +| Project release gate wrapper | Stable | `tests/host/verify_project_release_gate.sh` runs the documented minimum release validation commands including the release artifact URL browser workflow gate, SDK URL workflow gate, and project batch acceptance workflow/artifact gate, writes `build/project-release-readiness.json` and `build/project-batch-acceptance.json`, verifies embedded gate manifest/result matrix/action-plan evidence plus batch acceptance report evidence through SDK validation workflows, and ends with `project_release_gate=ok`. | ## Blocked Runtime Families @@ -72,6 +76,29 @@ ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_python_rem Execute these in order unless a later user request explicitly changes priority. +### 0. Real Browser CNC Simulation Page + +Current first priority: + +- Build a real UI/browser CNC simulation page as quickly as possible. +- First MVP exists at `wasm-port/runtime/ui/simulation/index.html`. +- The page must be visible and operator-oriented, not another release dashboard. +- It should use OPFS/session persistence, staged G-code, and existing + LinuxCNC-backed WASM SDK execution. +- It should expose machine/session readiness, active program state, run status, + and a toolpath/preview surface based on LinuxCNC-produced interpreter output, + canonical events, or validated runtime summaries. +- Add browser smoke coverage proving the page loads, renders nonempty runtime + state, and runs a representative LinuxCNC-backed flow. +- Do not implement G-code, tool, parameter, kinematics, remap, planner, or + canonical motion semantics in JavaScript. + +Constraint document: + +```bash +wasm-port/tests/docs/node/verify_real_browser_simulation_priority_docs.sh +``` + ### 1. Browser/UI Workflow Closure Current next task: @@ -210,6 +237,9 @@ SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh +wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh +wasm-port/tests/docs/node/verify_real_browser_simulation_priority_docs.sh +SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh wasm-port/tests/ui/node/verify_ui_node_smokes.sh wasm-port/tests/host/verify_host_smokes.sh ``` diff --git a/text15.txt b/text15.txt index d4dc1d5..219135e 100644 --- a/text15.txt +++ b/text15.txt @@ -472,3 +472,1161 @@ project_release_gate=ok 语义; - 新增内容只包装 release gate evidence 与 artifact validation display rows; - blocked runtime families 仍不 promotion。 + +六十、2026-06-16 继续执行记录:workflow overview release artifact URL action-plan browser API + +本批继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力” +推进,把 release artifact URL workflow 的 action-plan 从 SDK 层接到 workflow overview/launch 真实 +browser API。外部 shell 现在可以通过 iframe 或 launch API 读取 URL workflow 的 ordered next-action / +next-command plan,而不需要自己解释 validation missing fields,也不会执行 release gate。 + +本批新增实质能力: + +- `ui-shell.js` 新增: + - `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan()`; +- `loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl()` 的返回值新增 `actionPlan`; +- URL workflow summary rows 新增 `action-plan` 行; +- `createIniPanelLaunchApiManifest().methods` 和 + `createIniPanelShellWorkflowOverviewContract().methodNames` 新增 + `getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan`; +- `createIniPanelShellViewModel()` 暴露 + `createWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan`; +- `window.linuxCncIniPanelLaunchApi` 与 + `window.linuxCncIniPanelWorkflowOverviewApi` 暴露真实 browser API: + - `getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan()`; +- SDK re-export 新 INI panel workflow overview action-plan helper; +- SDK README、`docs/project-release-handoff.md`、`docs/panel-entry.md` 同步记录 browser workflow + action-plan API; +- workflow overview browser smoke 与 shell integration browser smoke 覆盖 ready URL workflow + action-plan shellScript,并比对 iframe API 与 launch API; +- 继续包含上一批 gate execution summary browser API 暴露与 smoke 覆盖。 + +本批新增/更新可执行验证: + +```bash +node wasm-port/tests/ui/node/verify_ini_panel_ui_shell.mjs +node wasm-port/tests/ui/node/verify_ini_panel_launch_api_manifest.mjs +node wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.mjs +node wasm-port/tests/sdk/node/verify_sdk_surface.mjs +node wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +node wasm-port/tests/ui/node/verify_ini_panel_entry_docs.mjs +wasm-port/tests/ui/node/verify_ui_node_smokes.sh +SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh +SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh +``` + +关键通过输出: + +```text +ini_panel_ui_shell_node_smoke=ok +ini_panel_launch_api_manifest_node_smoke=ok +ini_panel_shell_integration_manifest_node_smoke=ok +sdk_surface_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +ini_panel_entry_docs_node_smoke=ok +ui_node_smokes=ok +browser_ini_workflow_overview_smoke=ok +browser_ini_shell_integration_workflow_smoke=ok +browser_release_artifact_url_workflow_smoke=ok +``` + +本批仍保持 LinuxCNC 语义边界: + +- 未修改 vendored LinuxCNC 源码; +- 未在 JS/browser 中实现 G-code、tool、parameter、planner、kinematics、remap 或 canonical motion + 语义; +- 新增内容只包装 release artifact URL workflow action-plan、browser API surface、docs 和测试; +- browser API 只返回 action-plan 数据,不执行 release gate command; +- blocked runtime families 仍不 promotion。 + +六十一、2026-06-16 继续执行记录:workflow overview release artifact validation action-plan browser API + +本批继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力” +推进,把直接 artifact JSON validation 的 action-plan 接到 workflow overview/launch browser API。上一批 +完成 URL workflow action-plan;本批补齐直接 JSON validation 路径,使外部 shell 在已经持有 artifact JSON +或 validation result 时,也能读取 ordered next-command plan,而不需要走 URL fetch workflow。 + +本批新增实质能力: + +- `ui-shell.js` 新增: + - `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan()`; +- `validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson()` 的返回值新增 `actionPlan`; +- `createIniPanelLaunchApiManifest().methods` 和 + `createIniPanelShellWorkflowOverviewContract().methodNames` 新增 + `getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan`; +- `createIniPanelShellViewModel()` 暴露 + `createWorkflowOverviewReleaseReadinessArtifactValidationActionPlan`; +- `window.linuxCncIniPanelLaunchApi` 与 + `window.linuxCncIniPanelWorkflowOverviewApi` 暴露真实 browser API: + - `getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan()`; +- SDK re-export 新 INI panel workflow overview validation action-plan helper; +- SDK README、`docs/project-release-handoff.md`、`docs/panel-entry.md` 同步记录 direct validation + action-plan API; +- workflow overview browser smoke 与 shell integration browser smoke 覆盖 ready validation + action-plan shellScript,并比对 iframe API 与 launch API。 + +本批新增/更新可执行验证: + +```bash +node wasm-port/tests/ui/node/verify_ini_panel_ui_shell.mjs +node wasm-port/tests/ui/node/verify_ini_panel_launch_api_manifest.mjs +node wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.mjs +node wasm-port/tests/sdk/node/verify_sdk_surface.mjs +node wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +node wasm-port/tests/ui/node/verify_ini_panel_entry_docs.mjs +wasm-port/tests/ui/node/verify_ui_node_smokes.sh +SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh +``` + +关键通过输出: + +```text +ini_panel_ui_shell_node_smoke=ok +ini_panel_launch_api_manifest_node_smoke=ok +ini_panel_shell_integration_manifest_node_smoke=ok +sdk_surface_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +ini_panel_entry_docs_node_smoke=ok +ui_node_smokes=ok +browser_ini_workflow_overview_smoke=ok +browser_ini_shell_integration_workflow_smoke=ok +``` + +本批仍保持 LinuxCNC 语义边界: + +- 未修改 vendored LinuxCNC 源码; +- 未在 JS/browser 中实现 G-code、tool、parameter、planner、kinematics、remap 或 canonical motion + 语义; +- 新增内容只包装 release artifact validation action-plan、browser API surface、docs 和测试; +- browser API 只返回 action-plan 数据,不执行 release gate command; +- blocked runtime families 仍不 promotion。 + +六十二、2026-06-16 继续执行记录:workflow overview release artifact JSON workflow browser API + +本批继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力” +推进,把已有 SDK `createProjectReleaseReadinessArtifactJsonWorkflow()` 接到 workflow overview/launch +真实 browser API。外部 shell 在已经持有 artifact JSON 时,现在可以一次调用获得 parse、validation、 +summary、action-plan 与 gate execution summary,而不需要手动串多个 helper,也不需要走 URL fetch。 + +本批新增实质能力: + +- `ui-shell.js` 新增: + - `runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow()`; +- 新 workflow 返回: + - `apiName: ini-panel-shell-workflow-overview-release-readiness-artifact-json-workflow`; + - `phase/ready/parsed/parseError/artifactApiName/missing`; + - SDK `jsonWorkflow`; + - UI validation wrapper; + - validation summary view-model; + - gate execution summary view-model; + - validation action-plan; + - stable rows; + - `statusLine`; +- `createIniPanelLaunchApiManifest().methods` 和 + `createIniPanelShellWorkflowOverviewContract().methodNames` 新增 + `runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow`; +- `createIniPanelShellViewModel()` 暴露 + `runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow`; +- `window.linuxCncIniPanelLaunchApi` 与 + `window.linuxCncIniPanelWorkflowOverviewApi` 暴露真实 browser API: + - `runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow()`; +- SDK re-export 新 INI panel workflow overview JSON workflow helper; +- SDK README、`docs/project-release-handoff.md`、`docs/panel-entry.md` 同步记录 JSON workflow API; +- workflow overview browser smoke 与 shell integration browser smoke 覆盖 ready JSON workflow、 + gate execution summary 和 action-plan,并比对 iframe API 与 launch API。 + +本批新增/更新可执行验证: + +```bash +node wasm-port/tests/ui/node/verify_ini_panel_ui_shell.mjs +node wasm-port/tests/ui/node/verify_ini_panel_launch_api_manifest.mjs +node wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.mjs +node wasm-port/tests/sdk/node/verify_sdk_surface.mjs +node wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +node wasm-port/tests/ui/node/verify_ini_panel_entry_docs.mjs +wasm-port/tests/ui/node/verify_ui_node_smokes.sh +SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh +``` + +关键通过输出: + +```text +ini_panel_ui_shell_node_smoke=ok +ini_panel_launch_api_manifest_node_smoke=ok +ini_panel_shell_integration_manifest_node_smoke=ok +sdk_surface_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +ini_panel_entry_docs_node_smoke=ok +ui_node_smokes=ok +browser_ini_workflow_overview_smoke=ok +browser_ini_shell_integration_workflow_smoke=ok +``` + +本批仍保持 LinuxCNC 语义边界: + +- 未修改 vendored LinuxCNC 源码; +- 未在 JS/browser 中实现 G-code、tool、parameter、planner、kinematics、remap 或 canonical motion + 语义; +- 新增内容只包装 release artifact JSON workflow、browser API surface、docs 和测试; +- browser API 只返回 workflow/action-plan 数据,不执行 release gate command; +- blocked runtime families 仍不 promotion。 + +六十三、2026-06-16 继续执行记录:workflow overview release artifact JSON workflow summary browser API + +本批继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力” +推进,在上一批 JSON workflow browser API 之上,新增稳定 summary view-model。外部 shell 现在不需要 +自己读取 workflow 的 `statusLine` 和内部 rows,可以直接调用 summary helper 获得 dashboard 友好的 +status text 与 rows。 + +本批新增实质能力: + +- `ui-shell.js` 新增: + - `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel()`; +- 新 summary view-model 返回: + - `apiName: ini-panel-shell-workflow-overview-release-readiness-artifact-json-workflow-summary-view-model`; + - `viewModelVersion: 1`; + - `phase/ready/title/statusText/detailText/statusLine`; + - rows:`parse`、`validation-summary`、`gate-execution-summary`、`action-plan`、`missing`; +- `createIniPanelLaunchApiManifest().methods` 和 + `createIniPanelShellWorkflowOverviewContract().methodNames` 新增 + `getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel`; +- `createIniPanelShellViewModel()` 暴露 + `createWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel`; +- `window.linuxCncIniPanelLaunchApi` 与 + `window.linuxCncIniPanelWorkflowOverviewApi` 暴露真实 browser API: + - `getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel()`; +- SDK re-export 新 INI panel workflow overview JSON workflow summary helper; +- SDK README、`docs/project-release-handoff.md`、`docs/panel-entry.md` 同步记录 JSON workflow + summary view-model API; +- workflow overview browser smoke 与 shell integration browser smoke 覆盖 ready JSON workflow summary, + 并比对 iframe API 与 launch API。 + +本批新增/更新可执行验证: + +```bash +node wasm-port/tests/ui/node/verify_ini_panel_ui_shell.mjs +node wasm-port/tests/ui/node/verify_ini_panel_launch_api_manifest.mjs +node wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.mjs +node wasm-port/tests/sdk/node/verify_sdk_surface.mjs +node wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +node wasm-port/tests/ui/node/verify_ini_panel_entry_docs.mjs +wasm-port/tests/ui/node/verify_ui_node_smokes.sh +SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh +``` + +关键通过输出: + +```text +ini_panel_ui_shell_node_smoke=ok +ini_panel_launch_api_manifest_node_smoke=ok +ini_panel_shell_integration_manifest_node_smoke=ok +sdk_surface_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +ini_panel_entry_docs_node_smoke=ok +ui_node_smokes=ok +browser_ini_workflow_overview_smoke=ok +browser_ini_shell_integration_workflow_smoke=ok +``` + +本批仍保持 LinuxCNC 语义边界: + +- 未修改 vendored LinuxCNC 源码; +- 未在 JS/browser 中实现 G-code、tool、parameter、planner、kinematics、remap 或 canonical motion + 语义; +- 新增内容只包装 release artifact JSON workflow summary、browser API surface、docs 和测试; +- browser API 只返回 workflow summary 数据,不执行 release gate command; +- blocked runtime families 仍不 promotion。 + +六十四、2026-06-16 继续执行记录:workflow overview release artifact JSON workflow action-plan browser API + +本批继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力” +推进,在 JSON workflow 和 JSON workflow summary 之上,新增对称的 JSON workflow action-plan helper。 +外部 shell 现在可以直接读取 JSON workflow 的 next-command plan,而不需要知道 workflow 内部字段。 + +本批新增实质能力: + +- `ui-shell.js` 新增: + - `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan()`; +- 新 helper 返回 workflow 自带 `actionPlan`,或从 workflow validation wrapper 生成 validation + action-plan; +- `createIniPanelLaunchApiManifest().methods` 和 + `createIniPanelShellWorkflowOverviewContract().methodNames` 新增 + `getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan`; +- `createIniPanelShellViewModel()` 暴露 + `createWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan`; +- `window.linuxCncIniPanelLaunchApi` 与 + `window.linuxCncIniPanelWorkflowOverviewApi` 暴露真实 browser API: + - `getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan()`; +- SDK re-export 新 INI panel workflow overview JSON workflow action-plan helper; +- SDK README、`docs/project-release-handoff.md`、`docs/panel-entry.md` 同步记录 JSON workflow + action-plan API; +- workflow overview browser smoke 与 shell integration browser smoke 覆盖 ready JSON workflow + action-plan,并比对 iframe API 与 launch API。 + +本批新增/更新可执行验证: + +```bash +node wasm-port/tests/ui/node/verify_ini_panel_ui_shell.mjs +node wasm-port/tests/ui/node/verify_ini_panel_launch_api_manifest.mjs +node wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.mjs +node wasm-port/tests/sdk/node/verify_sdk_surface.mjs +node wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +node wasm-port/tests/ui/node/verify_ini_panel_entry_docs.mjs +wasm-port/tests/ui/node/verify_ui_node_smokes.sh +SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh +``` + +关键通过输出: + +```text +ini_panel_ui_shell_node_smoke=ok +ini_panel_launch_api_manifest_node_smoke=ok +ini_panel_shell_integration_manifest_node_smoke=ok +sdk_surface_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +ini_panel_entry_docs_node_smoke=ok +ui_node_smokes=ok +browser_ini_workflow_overview_smoke=ok +browser_ini_shell_integration_workflow_smoke=ok +``` + +本批仍保持 LinuxCNC 语义边界: + +- 未修改 vendored LinuxCNC 源码; +- 未在 JS/browser 中实现 G-code、tool、parameter、planner、kinematics、remap 或 canonical motion + 语义; +- 新增内容只包装 release artifact JSON workflow action-plan、browser API surface、docs 和测试; +- browser API 只返回 action-plan 数据,不执行 release gate command; +- blocked runtime families 仍不 promotion。 + +六十五、2026-06-16 继续执行记录:workflow overview release artifact JSON workflow DOM mount browser API + +本批继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力” +推进,在 JSON workflow render-state 之上新增真实 DOM contract/readiness/render/mount 能力。外部 +shell 现在不只可以读取 JSON workflow 数据,也可以把该 workflow 状态块挂载到 caller-owned DOM。 + +本批新增实质能力: + +- `ui-shell.js` 新增: + - `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract()`; + - `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness()`; + - `renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()`; + - `mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()`; +- 新 DOM contract 默认 selectors: + - `[data-workflow-overview-release-readiness-artifact-json-workflow]`; + - `[data-workflow-overview-release-readiness-artifact-json-workflow-status]`; + - `[data-workflow-overview-release-readiness-artifact-json-workflow-rows]`; +- render/mount result 返回 `statusLine`、`rowCount`、`rowIds` 与 handoff dataset; +- `createIniPanelLaunchApiManifest().methods` 和 + `createIniPanelShellWorkflowOverviewContract().methodNames` 新增四个 JSON workflow DOM 方法; +- `createIniPanelShellViewModel()` 暴露四个 JSON workflow DOM helper; +- `window.linuxCncIniPanelLaunchApi` 与 + `window.linuxCncIniPanelWorkflowOverviewApi` 暴露真实 browser API: + - `getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract()`; + - `getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness()`; + - `renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()`; + - `mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()`; +- SDK re-export 新 INI panel workflow overview JSON workflow DOM helpers; +- SDK README、`docs/project-release-handoff.md`、`docs/panel-entry.md` 同步记录 JSON workflow DOM + render/mount API; +- workflow overview browser smoke 与 shell integration browser smoke 创建真实 DOM 节点,覆盖 + ready JSON workflow render/mount,并比对 iframe API 与 launch API。 + +本批新增/更新可执行验证: + +```bash +node wasm-port/tests/ui/node/verify_ini_panel_ui_shell.mjs +node wasm-port/tests/ui/node/verify_ini_panel_launch_api_manifest.mjs +node wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.mjs +node wasm-port/tests/sdk/node/verify_sdk_surface.mjs +node wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +node wasm-port/tests/ui/node/verify_ini_panel_entry_docs.mjs +wasm-port/tests/ui/node/verify_ui_node_smokes.sh +wasm-port/tests/browser/verify_ini_panel_browser.sh +wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh +node wasm-port/tests/sdk/node/verify_project_release_artifact_url_workflow.mjs +git diff --check +``` + +关键通过输出: + +```text +ini_panel_ui_shell_node_smoke=ok +ini_panel_launch_api_manifest_node_smoke=ok +ini_panel_shell_integration_manifest_node_smoke=ok +sdk_surface_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +ini_panel_entry_docs_node_smoke=ok +ui_node_smokes=ok +browser_ini_workflow_overview_smoke=ok +browser_ini_shell_integration_workflow_smoke=ok +browser_release_artifact_url_workflow_smoke=ok +project_release_artifact_url_workflow_node_smoke=ok +``` + +本批仍保持 LinuxCNC 语义边界: + +- 未修改 vendored LinuxCNC 源码; +- 未在 JS/browser 中实现 G-code、tool、parameter、planner、kinematics、remap 或 canonical motion + 语义; +- 新增内容只包装 release artifact JSON workflow render-state DOM contract/readiness/render/mount、 + browser API surface、docs 和测试; +- browser API 只渲染/mount 已有 workflow 状态,不执行 release gate command; +- blocked runtime families 仍不 promotion。 + +六十六、2026-06-16 继续执行记录:workflow overview release artifact URL workflow DOM mount browser API + +本批继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力” +推进,在上一批 JSON workflow DOM mount 之上补齐 URL workflow 对称能力。外部 shell 现在可以把 +artifact URL fetch/validate/render/mount workflow 的整体状态渲染到 caller-owned DOM,而不需要自己解释 +URL workflow result 字段。 + +本批新增实质能力: + +- `ui-shell.js` 新增: + - `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState()`; + - `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract()`; + - `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness()`; + - `renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()`; + - `mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()`; +- 新 render-state 返回: + - `apiName: ini-panel-shell-workflow-overview-release-readiness-artifact-url-workflow-render-state`; + - `renderStateVersion: 1`; + - `phase/ready/title/statusText/detailText/statusLine/rows/dataset`; + - dataset `handoffScope: workflow-overview-release-readiness-artifact-url-workflow`; +- 新 DOM contract 默认 selectors: + - `[data-workflow-overview-release-readiness-artifact-url-workflow]`; + - `[data-workflow-overview-release-readiness-artifact-url-workflow-status]`; + - `[data-workflow-overview-release-readiness-artifact-url-workflow-rows]`; +- `createIniPanelLaunchApiManifest().methods` 和 + `createIniPanelShellWorkflowOverviewContract().methodNames` 新增五个 URL workflow render/mount 方法; +- `createIniPanelShellViewModel()` 暴露五个 URL workflow render/mount helper; +- `window.linuxCncIniPanelLaunchApi` 与 + `window.linuxCncIniPanelWorkflowOverviewApi` 暴露真实 browser API: + - `getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState()`; + - `getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract()`; + - `getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness()`; + - `renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()`; + - `mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()`; +- SDK re-export 新 INI panel workflow overview URL workflow render/mount helpers; +- SDK README、`docs/project-release-handoff.md`、`docs/panel-entry.md` 同步记录 URL workflow + render/mount API; +- workflow overview browser smoke 与 shell integration browser smoke 创建真实 DOM 节点,覆盖 + ready URL workflow render/mount,并比对 iframe API 与 launch API。 + +本批新增/更新可执行验证: + +```bash +node wasm-port/tests/ui/node/verify_ini_panel_ui_shell.mjs +node wasm-port/tests/ui/node/verify_ini_panel_launch_api_manifest.mjs +node wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.mjs +node wasm-port/tests/sdk/node/verify_sdk_surface.mjs +node wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +node wasm-port/tests/ui/node/verify_ini_panel_entry_docs.mjs +wasm-port/tests/ui/node/verify_ui_node_smokes.sh +wasm-port/tests/browser/verify_ini_panel_browser.sh +wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh +node wasm-port/tests/sdk/node/verify_project_release_artifact_url_workflow.mjs +git diff --check +``` + +关键通过输出: + +```text +ini_panel_ui_shell_node_smoke=ok +ini_panel_launch_api_manifest_node_smoke=ok +ini_panel_shell_integration_manifest_node_smoke=ok +sdk_surface_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +ini_panel_entry_docs_node_smoke=ok +ui_node_smokes=ok +browser_ini_workflow_overview_smoke=ok +browser_ini_shell_integration_workflow_smoke=ok +browser_release_artifact_url_workflow_smoke=ok +project_release_artifact_url_workflow_node_smoke=ok +``` + +本批仍保持 LinuxCNC 语义边界: + +- 未修改 vendored LinuxCNC 源码; +- 未在 JS/browser 中实现 G-code、tool、parameter、planner、kinematics、remap 或 canonical motion + 语义; +- 新增内容只包装 release artifact URL workflow render-state DOM contract/readiness/render/mount、 + browser API surface、docs 和测试; +- browser API 只渲染/mount 已有 URL workflow 状态,不执行 release gate command; +- blocked runtime families 仍不 promotion。 + +六十七、2026-06-16 继续执行记录:workflow overview release gate execution summary DOM mount browser API + +本批继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力” +推进,在已有 gate execution summary view-model 之上新增 render-state 与 DOM mount 能力。外部 shell +现在可以直接把 release gate execution summary 和每个 gate row 渲染到 caller-owned DOM。 + +本批新增实质能力: + +- `ui-shell.js` 新增: + - `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState()`; + - `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract()`; + - `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness()`; + - `renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()`; + - `mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()`; +- 新 render-state 返回: + - `apiName: ini-panel-shell-workflow-overview-release-readiness-artifact-gate-execution-summary-render-state`; + - `renderStateVersion: 1`; + - summary rows + normalized per-gate rows; + - dataset `handoffScope: workflow-overview-release-readiness-artifact-gate-execution-summary`; +- 新 DOM contract 默认 selectors: + - `[data-workflow-overview-release-readiness-artifact-gate-execution-summary]`; + - `[data-workflow-overview-release-readiness-artifact-gate-execution-summary-status]`; + - `[data-workflow-overview-release-readiness-artifact-gate-execution-summary-rows]`; +- `createIniPanelLaunchApiManifest().methods` 和 + `createIniPanelShellWorkflowOverviewContract().methodNames` 新增五个 gate execution summary + render/mount 方法; +- `createIniPanelShellViewModel()` 暴露五个 gate execution summary render/mount helper; +- `window.linuxCncIniPanelLaunchApi` 与 + `window.linuxCncIniPanelWorkflowOverviewApi` 暴露真实 browser API: + - `getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState()`; + - `getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract()`; + - `getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness()`; + - `renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()`; + - `mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()`; +- SDK re-export 新 INI panel workflow overview gate execution summary render/mount helpers; +- SDK README、`docs/project-release-handoff.md`、`docs/panel-entry.md` 同步记录 gate execution + summary render/mount API; +- workflow overview browser smoke 与 shell integration browser smoke 创建真实 DOM 节点,覆盖 + gate execution summary render/mount,并比对 iframe API 与 launch API。 + +本批新增/更新可执行验证: + +```bash +node wasm-port/tests/ui/node/verify_ini_panel_ui_shell.mjs +node wasm-port/tests/ui/node/verify_ini_panel_launch_api_manifest.mjs +node wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.mjs +node wasm-port/tests/sdk/node/verify_sdk_surface.mjs +node wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +node wasm-port/tests/ui/node/verify_ini_panel_entry_docs.mjs +wasm-port/tests/ui/node/verify_ui_node_smokes.sh +wasm-port/tests/browser/verify_ini_panel_browser.sh +wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh +node wasm-port/tests/sdk/node/verify_project_release_artifact_url_workflow.mjs +git diff --check +``` + +关键通过输出: + +```text +ini_panel_ui_shell_node_smoke=ok +ini_panel_launch_api_manifest_node_smoke=ok +ini_panel_shell_integration_manifest_node_smoke=ok +sdk_surface_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +ini_panel_entry_docs_node_smoke=ok +ui_node_smokes=ok +browser_ini_workflow_overview_smoke=ok +browser_ini_shell_integration_workflow_smoke=ok +browser_release_artifact_url_workflow_smoke=ok +project_release_artifact_url_workflow_node_smoke=ok +``` + +本批仍保持 LinuxCNC 语义边界: + +- 未修改 vendored LinuxCNC 源码; +- 未在 JS/browser 中实现 G-code、tool、parameter、planner、kinematics、remap 或 canonical motion + 语义; +- 新增内容只包装 release gate execution summary render-state DOM contract/readiness/render/mount、 + browser API surface、docs 和测试; +- browser API 只渲染/mount 已有 gate execution summary,不执行 release gate command; +- blocked runtime families 仍不 promotion。 + +六十八、2026-06-16 继续执行记录:workflow overview release artifact validation action-plan DOM mount browser API + +本批继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力” +推进,在已有 artifact validation action-plan 之上新增 render-state 与 DOM mount 能力。外部 shell +现在可以直接把直接 JSON validation 的 next-command plan 渲染到 caller-owned DOM,而不需要自己解释 +`actionPlan.rows`、`commands` 或 `shellScript`。 + +本批新增实质能力: + +- `ui-shell.js` 新增: + - `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState()`; + - `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract()`; + - `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness()`; + - `renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()`; + - `mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()`; +- 新 render-state 返回: + - `apiName: ini-panel-shell-workflow-overview-release-readiness-artifact-validation-action-plan-render-state`; + - `renderStateVersion: 1`; + - action-plan rows + command rows; + - `nextActionId`、`nextCommand`、`shellScript`; + - dataset `handoffScope: workflow-overview-release-readiness-artifact-validation-action-plan`; +- 新 DOM contract 默认 selectors: + - `[data-workflow-overview-release-readiness-artifact-validation-action-plan]`; + - `[data-workflow-overview-release-readiness-artifact-validation-action-plan-status]`; + - `[data-workflow-overview-release-readiness-artifact-validation-action-plan-rows]`; +- `createIniPanelLaunchApiManifest().methods` 和 + `createIniPanelShellWorkflowOverviewContract().methodNames` 新增五个 validation action-plan + render/mount 方法; +- `createIniPanelShellViewModel()` 暴露五个 validation action-plan render/mount helper; +- `window.linuxCncIniPanelLaunchApi` 与 + `window.linuxCncIniPanelWorkflowOverviewApi` 暴露真实 browser API: + - `getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState()`; + - `getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract()`; + - `getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness()`; + - `renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()`; + - `mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()`; +- SDK re-export 新 INI panel workflow overview validation action-plan render/mount helpers; +- SDK README、`docs/project-release-handoff.md`、`docs/panel-entry.md` 同步记录 validation + action-plan render/mount API; +- workflow overview browser smoke 与 shell integration browser smoke 创建真实 DOM 节点,覆盖 + validation action-plan render/mount,并比对 iframe API 与 launch API。 + +本批新增/更新可执行验证: + +```bash +node wasm-port/tests/ui/node/verify_ini_panel_ui_shell.mjs +node wasm-port/tests/ui/node/verify_ini_panel_launch_api_manifest.mjs +node wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.mjs +node wasm-port/tests/sdk/node/verify_sdk_surface.mjs +node wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +node wasm-port/tests/ui/node/verify_ini_panel_entry_docs.mjs +wasm-port/tests/ui/node/verify_ui_node_smokes.sh +wasm-port/tests/browser/verify_ini_panel_browser.sh +wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh +node wasm-port/tests/sdk/node/verify_project_release_artifact_url_workflow.mjs +git diff --check +``` + +关键通过输出: + +```text +ini_panel_ui_shell_node_smoke=ok +ini_panel_launch_api_manifest_node_smoke=ok +ini_panel_shell_integration_manifest_node_smoke=ok +sdk_surface_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +ini_panel_entry_docs_node_smoke=ok +ui_node_smokes=ok +browser_ini_workflow_overview_smoke=ok +browser_ini_shell_integration_workflow_smoke=ok +browser_release_artifact_url_workflow_smoke=ok +project_release_artifact_url_workflow_node_smoke=ok +``` + +本批仍保持 LinuxCNC 语义边界: + +- 未修改 vendored LinuxCNC 源码; +- 未在 JS/browser 中实现 G-code、tool、parameter、planner、kinematics、remap 或 canonical motion + 语义; +- 新增内容只包装 release artifact validation action-plan render-state DOM contract/readiness/render/mount、 + browser API surface、docs 和测试; +- browser API 只渲染/mount 已有 validation action-plan,不执行 release gate command; +- blocked runtime families 仍不 promotion。 + +六十九、2026-06-16 继续执行记录:project batch acceptance workflow SDK gate + +本批直接响应“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力”, +把该规则转成机器可读 workflow 和可执行 gate,而不是只停留在文字约束。 + +本批新增实质能力: + +- 新增 SDK API: + - `createProjectBatchAcceptanceWorkflow()`; + - `createProjectBatchAcceptanceSummaryViewModel()`; + - `createProjectBatchAcceptanceActionPlan()`; +- batch acceptance workflow 接收 `batchId`、`capabilities`、`gateResults`、`observedOutputs`, + 只接受 `api`、`workflow`、`gate`、`browser` 四类实质能力; +- workflow ready 条件为: + - 至少一个 accepted capability; + - release gate manifest 中 `project-batch-acceptance` gate 有通过证据; +- blocked workflow 会返回 `missing: ["batch-capability", "project-batch-acceptance"]` + 或相应缺失项,并通过 action-plan 给出下一步 gate command; +- 新增可执行 SDK gate: + `wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh`, + 输出 `project_batch_acceptance_workflow_node_smoke=ok`; +- `createProjectReleaseGateManifest()` 新增 `project-batch-acceptance` gate; +- `verify_project_release_gate.sh` 在 UI/browser gates 后运行 batch acceptance gate; +- `write_project_release_readiness_artifact.mjs` 和 + `tests/fixtures/project-release-readiness-ready.json` 已同步到 12 个 release gates; +- SDK surface、SDK README、project release handoff docs、tracker、artifact validation 和 + workflow overview/browser smoke 断言同步到新 gate。 + +本批新增/更新可执行验证: + +```bash +node wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.mjs +wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh +node wasm-port/tests/sdk/node/verify_project_release_gate_manifest.mjs +wasm-port/tests/sdk/node/verify_sdk_surface.sh +node wasm-port/tests/host/verify_project_release_readiness_artifact.mjs wasm-port/tests/fixtures/project-release-readiness-ready.json +wasm-port/tests/ui/node/verify_ui_node_smokes.sh +wasm-port/tests/browser/verify_ini_panel_browser.sh +wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh +node wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +``` + +关键通过输出: + +```text +project_batch_acceptance_workflow_node_smoke=ok +project_release_gate_manifest_node_smoke=ok +sdk_surface_node_smoke=ok +project_release_readiness_artifact_node_smoke=ok +ui_node_smokes=ok +browser_ini_workflow_overview_smoke=ok +browser_ini_shell_integration_workflow_smoke=ok +browser_release_artifact_url_workflow_smoke=ok +project_release_handoff_docs_node_smoke=ok +``` + +本批仍保持 LinuxCNC 语义边界: + +- 未修改 vendored LinuxCNC 源码; +- 未在 JS/browser 中实现 G-code、tool、parameter、planner、kinematics、remap 或 canonical motion + 语义; +- 新增内容只包装项目批次验收 evidence、SDK API、release gate manifest、docs 和测试; +- batch acceptance gate 不执行 CNC runtime,不改变 blocked runtime family promotion 状态。 + +七十、2026-06-16 继续执行记录:project batch acceptance capability matrix SDK API + +本批继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力” +推进,在上一批 batch acceptance workflow 之上新增可独立调用的 capability matrix。 + +本批新增实质能力: + +- 新增 SDK API:`createProjectBatchAcceptanceCapabilityMatrix()`; +- capability matrix 负责规范化每批 `capabilities`,只接受: + - `api`; + - `workflow`; + - `gate`; + - `browser`; +- docs-only 或 unknown type 会进入 `rejectedCapabilities`,不会让批次通过; +- matrix 输出: + - `acceptedCapabilityTypes`; + - `capabilityTypes`; + - `countsByType`; + - `acceptedCount` / `rejectedCount`; + - `missing: ["batch-capability"]`; + - dashboard rows; +- `createProjectBatchAcceptanceWorkflow()` 现在复用 capability matrix,外部工具可以先独立判断 + 能力证据,再结合 `project-batch-acceptance` gate 判断 workflow ready; +- SDK README、project release handoff docs、tracker 和 docs smoke 同步记录新 API。 + +本批新增/更新可执行验证: + +```bash +node wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.mjs +node wasm-port/tests/sdk/node/verify_sdk_surface.mjs +wasm-port/tests/sdk/node/verify_sdk_surface.sh +node wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +``` + +关键通过输出: + +```text +project_batch_acceptance_workflow_node_smoke=ok +sdk_surface_node_smoke=ok +project_release_gate_manifest_node_smoke=ok +project_release_artifact_url_workflow_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +``` + +本批仍保持 LinuxCNC 语义边界: + +- 未修改 vendored LinuxCNC 源码; +- 未在 JS/browser 中实现 G-code、tool、parameter、planner、kinematics、remap 或 canonical motion + 语义; +- 新增内容只包装项目批次能力证据分类、SDK API、docs 和测试; +- capability matrix 不执行 CNC runtime,不改变 blocked runtime family promotion 状态。 + +七十一、2026-06-16 继续执行记录:project batch acceptance checklist SDK API + +本批继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力” +推进,在 batch acceptance capability matrix/workflow 之上新增可直接展示的 checklist API。 + +本批新增实质能力: + +- 新增 SDK API:`createProjectBatchAcceptanceChecklist()`; +- checklist 接收 batch acceptance workflow 与 action plan,输出: + - `accepted-capability`; + - `batch-acceptance-gate`; + - `next-action`; +- 每个 checklist item 都包含 `status: pass|blocked`、`passed` 与 `detail`; +- ready workflow 返回 3 个 pass item; +- blocked workflow 返回缺少 capability、缺少 batch gate、存在 next action 的 blocked item; +- SDK surface、SDK README、project release handoff docs、tracker 和 docs smoke 同步记录新 API。 + +本批新增/更新可执行验证: + +```bash +node wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.mjs +node wasm-port/tests/sdk/node/verify_sdk_surface.mjs +wasm-port/tests/sdk/node/verify_sdk_surface.sh +node wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +``` + +关键通过输出: + +```text +project_batch_acceptance_workflow_node_smoke=ok +sdk_surface_node_smoke=ok +project_release_gate_manifest_node_smoke=ok +project_release_artifact_url_workflow_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +``` + +本批仍保持 LinuxCNC 语义边界: + +- 未修改 vendored LinuxCNC 源码; +- 未在 JS/browser 中实现 G-code、tool、parameter、planner、kinematics、remap 或 canonical motion + 语义; +- 新增内容只包装项目批次验收 checklist、SDK API、docs 和测试; +- checklist 不执行 CNC runtime,不改变 blocked runtime family promotion 状态。 + +七十二、2026-06-16 继续执行记录:project batch acceptance report SDK API + +本批继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力” +推进,在 batch acceptance matrix/workflow/summary/action-plan/checklist 之上新增单对象 report。 + +本批新增实质能力: + +- 新增 SDK API:`createProjectBatchAcceptanceReport()`; +- report 聚合: + - `capabilityMatrix`; + - `workflow`; + - `summaryViewModel`; + - `actionPlan`; + - `checklist`; +- ready report 输出 `summary`、`capabilities`、`checklist`、`next-command` dashboard rows; +- blocked report 保留 workflow missing reasons,并把下一步 gate command 放在 rows 中; +- SDK surface、SDK README、project release handoff docs、tracker 和 docs smoke 同步记录新 API。 + +本批新增/更新可执行验证: + +```bash +node wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.mjs +node wasm-port/tests/sdk/node/verify_sdk_surface.mjs +wasm-port/tests/sdk/node/verify_sdk_surface.sh +node wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +``` + +关键通过输出: + +```text +project_batch_acceptance_workflow_node_smoke=ok +sdk_surface_node_smoke=ok +project_release_gate_manifest_node_smoke=ok +project_release_artifact_url_workflow_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +``` + +本批仍保持 LinuxCNC 语义边界: + +- 未修改 vendored LinuxCNC 源码; +- 未在 JS/browser 中实现 G-code、tool、parameter、planner、kinematics、remap 或 canonical motion + 语义; +- 新增内容只包装项目批次验收 report、SDK API、docs 和测试; +- report 不执行 CNC runtime,不改变 blocked runtime family promotion 状态。 + +七十三、2026-06-16 继续执行记录:project batch acceptance report validation JSON workflow SDK API + +本批继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力” +推进,在 batch acceptance report 之上新增可验证 report 与 JSON workflow。 + +本批新增实质能力: + +- 新增 SDK API: + - `createProjectBatchAcceptanceReportValidation()`; + - `createProjectBatchAcceptanceReportJsonWorkflow()`; +- report validation 校验: + - `apiName` / `reportVersion`; + - ready phase; + - capability matrix; + - workflow; + - summary view-model; + - action plan; + - checklist; + - checklist row; +- JSON workflow 对外提供非抛错解析: + - valid ready report 返回 `parsed: true`、`ready: true`; + - blocked report 返回 missing evidence; + - invalid JSON 返回 `parsed: false` 和 `report-json` missing; +- SDK surface、SDK README、project release handoff docs、tracker 和 docs smoke 同步记录新 API。 + +本批新增/更新可执行验证: + +```bash +node wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.mjs +node wasm-port/tests/sdk/node/verify_sdk_surface.mjs +wasm-port/tests/sdk/node/verify_sdk_surface.sh +node wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +``` + +关键通过输出: + +```text +project_batch_acceptance_workflow_node_smoke=ok +sdk_surface_node_smoke=ok +project_release_gate_manifest_node_smoke=ok +project_release_artifact_url_workflow_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +``` + +本批仍保持 LinuxCNC 语义边界: + +- 未修改 vendored LinuxCNC 源码; +- 未在 JS/browser 中实现 G-code、tool、parameter、planner、kinematics、remap 或 canonical motion + 语义; +- 新增内容只包装项目批次验收 report validation/JSON workflow、SDK API、docs 和测试; +- JSON workflow 不执行 CNC runtime,不改变 blocked runtime family promotion 状态。 + +七十四、2026-06-16 继续执行记录:project batch acceptance report URL workflow SDK API + +本批继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力” +推进,在 batch acceptance report JSON workflow 之上新增可拉取 hosted report 的 URL workflow。 + +本批新增实质能力: + +- 新增 SDK API: + - `loadProjectBatchAcceptanceReportUrlWorkflow()`; + - `createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel()`; + - `createProjectBatchAcceptanceReportUrlWorkflowActionPlan()`; +- URL workflow 通过调用方提供的 `fetch` 拉取 batch acceptance report JSON; +- ready fetch 返回 `fetched: true`、`httpStatus`、JSON workflow、validation 与 missing evidence; +- 缺少 URL、缺少 fetch、HTTP failure、fetch throw 都返回结构化 blocked workflow,不向外抛错; +- action-plan 区分 input、fetch、command action,并在需要时给出 + `wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh`; +- SDK surface、SDK README、project release handoff docs、tracker 和 docs smoke 同步记录新 API。 + +本批新增/更新可执行验证: + +```bash +node wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.mjs +node wasm-port/tests/sdk/node/verify_sdk_surface.mjs +wasm-port/tests/sdk/node/verify_sdk_surface.sh +node wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +``` + +关键通过输出: + +```text +project_batch_acceptance_workflow_node_smoke=ok +sdk_surface_node_smoke=ok +project_release_gate_manifest_node_smoke=ok +project_release_artifact_url_workflow_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +``` + +本批仍保持 LinuxCNC 语义边界: + +- 未修改 vendored LinuxCNC 源码; +- 未在 JS/browser 中实现 G-code、tool、parameter、planner、kinematics、remap 或 canonical motion + 语义; +- 新增内容只包装项目批次验收 report URL workflow、SDK API、docs 和测试; +- URL workflow 只处理项目级验收 JSON,不执行 CNC runtime,不改变 blocked runtime family promotion 状态。 + +七十五、2026-06-16 继续执行记录:project batch acceptance artifact SDK gate + +本批继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力” +推进,在 batch acceptance report/JSON/URL workflow 之上新增真实落盘 artifact gate。 + +本批新增实质能力: + +- 新增可执行 artifact writer: + - `tests/sdk/node/write_project_batch_acceptance_artifact.mjs`; +- 新增可执行 artifact validator: + - `tests/sdk/node/verify_project_batch_acceptance_artifact.mjs`; + - `tests/sdk/node/verify_project_batch_acceptance_artifact.sh`; +- `verify_project_batch_acceptance_workflow.sh` 现在会: + - 运行 batch acceptance workflow smoke; + - 写出 `build/project-batch-acceptance.json`; + - 通过 JSON workflow/report validation 验证该 artifact; +- artifact 验证覆盖 report api/version、ready phase、capability matrix、workflow、summary、 + action-plan、checklist 和 dashboard rows; +- SDK README、project release handoff docs、tracker 和 docs smoke 同步记录 artifact gate。 + +本批新增/更新可执行验证: + +```bash +node wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.mjs +wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh +wasm-port/tests/sdk/node/verify_project_batch_acceptance_artifact.sh build/project-batch-acceptance.json +node wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +``` + +关键通过输出: + +```text +project_batch_acceptance_workflow_node_smoke=ok +project_batch_acceptance_artifact=/home/cnc/桌面/cnc_wams/wasm-port/build/project-batch-acceptance.json +project_batch_acceptance_artifact_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +``` + +本批仍保持 LinuxCNC 语义边界: + +- 未修改 vendored LinuxCNC 源码; +- 未在 JS/browser 中实现 G-code、tool、parameter、planner、kinematics、remap 或 canonical motion + 语义; +- 新增内容只包装项目批次验收 artifact 写入/验证、SDK gate、docs 和测试; +- artifact gate 只处理项目级验收 JSON,不执行 CNC runtime,不改变 blocked runtime family promotion 状态。 + +七十六、2026-06-16 继续执行记录:project batch acceptance report validation summary/action-plan SDK API + +本批继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力” +推进,在 batch acceptance report validation 之上新增可直接给 CI/UI 使用的 summary 与 action-plan。 + +本批新增实质能力: + +- 新增 SDK API: + - `createProjectBatchAcceptanceReportValidationSummaryViewModel()`; + - `createProjectBatchAcceptanceReportValidationActionPlan()`; +- validation summary 输出 stable dashboard rows: + - report; + - batch; + - validation; + - missing; +- validation action-plan 对 ready validation 返回 no-op shell script; +- blocked validation 返回下一步可执行命令: + - `wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh`; + - expected output: `project_batch_acceptance_artifact_node_smoke=ok`; +- artifact validator 现在会校验 summary/action-plan 输出,确保真实 artifact gate 覆盖新 API; +- SDK surface、SDK README、project release handoff docs、tracker 和 docs smoke 同步记录新 API。 + +本批新增/更新可执行验证: + +```bash +node wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.mjs +wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh +wasm-port/tests/sdk/node/verify_project_batch_acceptance_artifact.sh build/project-batch-acceptance.json +node wasm-port/tests/sdk/node/verify_sdk_surface.mjs +node wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +``` + +关键通过输出: + +```text +project_batch_acceptance_workflow_node_smoke=ok +project_batch_acceptance_artifact_node_smoke=ok +sdk_surface_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +``` + +本批仍保持 LinuxCNC 语义边界: + +- 未修改 vendored LinuxCNC 源码; +- 未在 JS/browser 中实现 G-code、tool、parameter、planner、kinematics、remap 或 canonical motion + 语义; +- 新增内容只包装项目批次验收 validation summary/action-plan、SDK API、docs 和测试; +- validation action-plan 只指向项目级 artifact gate,不执行 CNC runtime,不改变 blocked runtime family promotion 状态。 + +七十七、2026-06-16 继续执行记录:real browser simulation priority docs gate + +本批响应新的后续工作方向,把“真实 UI/browser 数控仿真页面优先”写入项目约束文档, +并新增可执行 docs gate,确保后续批次不能继续只偏向低收益 metadata/API 工作。 + +本批新增实质能力: + +- 新增约束文档: + - `docs/real-browser-simulation-priority.md`; +- 明确后续第一要务: + - 尽快实现真实 browser CNC simulation page; + - 页面应具备 machine/session loader; + - G-code program view; + - LinuxCNC-backed interpreter execution; + - machine readiness/status panels; + - toolpath/preview area; + - browser smoke coverage; +- 明确批次优先级: + 1. real UI/browser simulation page capability; + 2. browser workflow smoke; + 3. UI APIs; + 4. OPFS/session integration; + 5. 只在直接 unblock 仿真页面时做 SDK/API helper; + 6. 只在创建 executable gate 或保护仿真方向时做文档/release metadata; +- 新增可执行 docs gate: + - `tests/docs/node/verify_real_browser_simulation_priority_docs.mjs`; + - `tests/docs/node/verify_real_browser_simulation_priority_docs.sh`; +- 将新 gate 接入: + - `tests/host/verify_host_smokes.sh`; + - `tests/host/verify_project_release_gate.sh`; + - `tests/docs/node/verify_project_release_handoff_docs.mjs`; +- README、project release handoff、PROJECT_COMPLETION_TRACKER 同步记录该约束。 + +本批新增/更新可执行验证: + +```bash +wasm-port/tests/docs/node/verify_real_browser_simulation_priority_docs.sh +node wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tests/host/verify_project_release_gate.sh +``` + +关键通过输出: + +```text +real_browser_simulation_priority_docs_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +host_wasm_opfs_browser_smokes=ok +project_release_gate=ok +``` + +本批仍保持 LinuxCNC 语义边界: + +- 明确真实 browser simulation page 不能在 JS/browser 中实现 G-code、tool、parameter、 + kinematics、remap、planner 或 canonical motion 语义; +- 页面只能负责 layout、interaction state、OPFS/session、file staging、SDK/WASM calls、 + 以及 LinuxCNC-produced output / validated runtime summary rendering; +- 下一批开始应优先实现真实仿真页面能力,而不是继续扩展 dashboard-only helper。 + +七十八、2026-06-16 继续执行记录:real browser simulation page MVP + +本批按新的第一要务推进,直接新增真实 browser CNC simulation page MVP,而不是继续扩展 +dashboard-only helper。 + +本批新增实质能力: + +- 新增真实仿真页面入口: + - `runtime/ui/simulation/index.html`; +- 新增页面 runtime: + - `runtime/ui/simulation/simulation-app.js`; +- 页面加载后会直接调用 LinuxCNC-backed interpreter WASM: + - `createLinuxCncInterpSdk()`; + - `interp.runProgram(DEFAULT_SIMULATION_PROGRAM)`; +- 页面渲染真实运行结果: + - G-code program view; + - LinuxCNC canonical output; + - active line / statement; + - final axis readout; + - motion event table; + - SVG toolpath preview; +- 新增 browser gate: + - `tests/browser/real_simulation_page_smoke.html`; + - `tests/browser/verify_real_simulation_browser.sh`; +- 新 gate 验证页面在 Chromium 中真实加载,WASM interpreter 执行成功,DOM 暴露 ready state, + toolpath polyline 非空,motion rows 与 LinuxCNC canonical motion count 一致; +- launch 页面新增 “Open real browser simulation” 链接; +- host smoke、project release gate、priority docs、README、handoff docs、tracker 同步接入新页面和 gate。 + +本批新增/更新可执行验证: + +```bash +SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh +wasm-port/tests/docs/node/verify_real_browser_simulation_priority_docs.sh +node wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +``` + +关键通过输出: + +```text +browser_real_simulation_page_smoke=ok +real_browser_simulation_priority_docs_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +``` + +本批仍保持 LinuxCNC 语义边界: + +- 页面没有在 JS/browser 中实现 G-code、tool、parameter、kinematics、remap、planner 或 + canonical motion 语义; +- JS 只调用 LinuxCNC-backed WASM SDK,并把 LinuxCNC-produced canonical output 渲染成 + program state、axis readout、motion table 和 SVG preview; +- canonical output 中坐标字段的读取只用于 UI 渲染,不作为 CNC 解释或运动规划来源。 diff --git a/wasm-port/README.md b/wasm-port/README.md index c91e554..9d5a2e8 100644 --- a/wasm-port/README.md +++ b/wasm-port/README.md @@ -7,7 +7,17 @@ This directory is the standalone workspace for the WASM-based simulation port. Start from `docs/project-release-handoff.md` for the current project-level handoff. It links the browser/UI workflow surface, SDK surface, OPFS/session persistence scope, sim-config coverage baseline, blocked -host/runtime families, source reuse rules, drift rules, and acceptance tracker. +host/runtime families, source reuse rules, drift rules, the real browser +simulation priority constraint, and acceptance tracker. + +The first priority for subsequent work is documented in +`docs/real-browser-simulation-priority.md`: move quickly toward a real browser +CNC simulation page with visible machine/session loading, G-code program state, +LinuxCNC-backed interpreter execution, and browser smoke coverage. This does +not relax the LinuxCNC semantic boundary; browser code still must not implement +G-code, tool, parameter, kinematics, remap, or planner semantics. +The first real simulation entry point is `runtime/ui/simulation/index.html`, +validated by `tests/browser/verify_real_simulation_browser.sh`. The project release gate for the current scope is: @@ -35,6 +45,7 @@ project_release_handoff_docs_node_smoke=ok opfs_session_docs_node_smoke=ok sim_configs_coverage_docs_node_smoke=ok host_runtime_boundary_docs_node_smoke=ok +real_browser_simulation_priority_docs_node_smoke=ok sdk_surface_node_smoke=ok host_wasm_opfs_browser_smokes=ok project_release_gate=ok @@ -174,6 +185,8 @@ Directory intent: Project-level OPFS/session persistence and release-gate handoff is documented in `docs/opfs-session-persistence.md`. +The real browser CNC simulation page priority is documented in +`docs/real-browser-simulation-priority.md`. Sim-config inventory coverage and release-gate handoff is documented in `docs/sim-configs-coverage-handoff.md`. Host/runtime boundary release-gate handoff is documented in diff --git a/wasm-port/docs/panel-entry.md b/wasm-port/docs/panel-entry.md index ce58540..1fbfc65 100644 --- a/wasm-port/docs/panel-entry.md +++ b/wasm-port/docs/panel-entry.md @@ -163,6 +163,31 @@ when an outer shell needs to validate a project release readiness artifact JSON through the same read-only workflow overview surface. It parses the artifact, returns stable validation rows, and does not execute release gates or CNC behavior. +Use `runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow()` or +`linuxCncIniPanelWorkflowOverviewApi.runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow()` +when the shell already has artifact JSON and needs parse, validation, summary, +action-plan, and gate execution summary output in one workflow result. +Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel()` or +`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel()` +when the shell needs the artifact's release gate execution summary rows. +Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState()`, +`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract()`, +`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness()`, +`renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()`, or +`mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()` when the +shell needs to render those per-gate rows into caller-owned DOM. +Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel()` or +`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel()` +when the shell needs stable status text and rows for that JSON workflow. +Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan()` or +`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan()` +when the shell needs the JSON workflow next-command plan without inspecting +internal workflow fields. +Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract()`, +`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness()`, +`renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()`, or +`mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()` when the +shell needs to render that JSON workflow state into caller-owned DOM. Use `loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl()` or `linuxCncIniPanelWorkflowOverviewApi.loadWorkflowOverviewReleaseReadinessArtifactUrl()` when the shell should fetch the artifact JSON URL, validate it, and optionally @@ -173,10 +198,30 @@ Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSumma `linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel()` when the shell needs one stable status line and dashboard rows for the whole artifact URL fetch/validate/render/mount workflow. +Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState()`, +`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract()`, +`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness()`, +`renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()`, or +`mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()` when the +shell needs to render that URL workflow state into caller-owned DOM. +Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan()` or +`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan()` +when the shell needs the ordered next-action/next-command plan for that URL +workflow without executing release gates. Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel()` or `linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel()` when the shell needs stable artifact validation status text and dashboard rows without interpreting validation flags itself. +Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan()` or +`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan()` +when direct artifact JSON validation needs its own ordered next-command plan +without executing release gates. +Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState()`, +`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract()`, +`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness()`, +`renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()`, or +`mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()` when the +shell needs to render that validation action plan into caller-owned DOM. Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel()`, `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState()`, `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract()`, diff --git a/wasm-port/docs/project-release-handoff.md b/wasm-port/docs/project-release-handoff.md index f3823d1..0efaf55 100644 --- a/wasm-port/docs/project-release-handoff.md +++ b/wasm-port/docs/project-release-handoff.md @@ -13,6 +13,8 @@ Use these documents as the current handoff set: - `docs/panel-entry.md` for browser/UI entry points and shell handoff helpers. - `runtime/sdk/README.md` for stable SDK imports from `runtime/sdk/src/index.js`. - `docs/opfs-session-persistence.md` for OPFS/session persistence scope. +- `docs/real-browser-simulation-priority.md` for the standing first priority: + build the real browser CNC simulation page before lower-value metadata work. - `docs/sim-configs-coverage-handoff.md` for sim-config inventory status. - `docs/host-runtime-boundary-handoff.md` for blocked runtime families. - `docs/source-reuse-map.md` and `docs/drift-report.md` for LinuxCNC source @@ -24,6 +26,26 @@ continuation records are in `../text15.txt`. ## Supported current workflows +## First Priority Constraint + +Subsequent work must prioritize a real browser CNC simulation page. The page +should converge on visible machine/session loading, G-code program state, +LinuxCNC-backed interpreter execution, machine readiness/status panels, and a +toolpath/preview area backed by LinuxCNC-produced output or validated runtime +events. The authoritative constraint is +`docs/real-browser-simulation-priority.md`. +Its executable docs gate is +`verify_real_browser_simulation_priority_docs.sh`, which is included in the +host smoke and project release gate and ends with +`real_browser_simulation_priority_docs_node_smoke=ok`. +The first implementation entry point is `runtime/ui/simulation/index.html`, +validated by `verify_real_simulation_browser.sh`, which ends with +`browser_real_simulation_page_smoke=ok`. + +This priority does not permit JavaScript-owned CNC semantics. G-code, tool, +parameter, kinematics, remap, planner, and canonical motion behavior must remain +owned by vendored LinuxCNC source and existing LinuxCNC-backed WASM boundaries. + - INI panel launch, edit/run page, read-only control page, workflow overview, and external shell handoff are exposed from `runtime/ui/ini-panel/`. - External shells should use the read-only helpers documented in @@ -76,15 +98,86 @@ continuation records are in `../text15.txt`. that SDK URL workflow into stable dashboard rows and status text. `createProjectReleaseReadinessArtifactUrlWorkflowActionPlan()` turns blocked URL workflows into ordered input, fetch, and command actions for CI tooling. + `createProjectBatchAcceptanceCapabilityMatrix()` classifies per-batch evidence + into accepted API/workflow/gate/browser capabilities and rejected entries. + `createProjectBatchAcceptanceWorkflow()` records the per-batch acceptance rule + that each batch must ship at least one callable API, verifiable workflow, + executable gate, or browser capability. Its summary view-model and action-plan + helpers provide dashboard rows and the next batch-acceptance gate command. + `createProjectBatchAcceptanceChecklist()` combines those results into + pass/blocked checklist rows for external release dashboards. + `createProjectBatchAcceptanceReport()` packages the matrix, workflow, + summary, action plan, and checklist into one stable CI/dashboard object. + `createProjectBatchAcceptanceReportValidation()` and + `createProjectBatchAcceptanceReportJsonWorkflow()` validate that object from + parsed data or JSON text without throwing. + `createProjectBatchAcceptanceReportValidationSummaryViewModel()` and + `createProjectBatchAcceptanceReportValidationActionPlan()` expose dashboard + rows and the next artifact gate command for blocked report validation. + `loadProjectBatchAcceptanceReportUrlWorkflow()` fetches hosted batch + acceptance report JSON through a caller-provided `fetch` implementation, then + runs the same validation workflow without throwing. + `createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel()` and + `createProjectBatchAcceptanceReportUrlWorkflowActionPlan()` expose dashboard + rows and ordered input/fetch/gate actions for that URL workflow. + The executable batch acceptance gate writes + `build/project-batch-acceptance.json` with + `write_project_batch_acceptance_artifact.mjs` and validates it through + `verify_project_batch_acceptance_artifact.sh`, ending with + `project_batch_acceptance_artifact_node_smoke=ok`. + Workflow overview shells can use + `getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel()` + and + `getWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel()` + to display the embedded release gate execution summary without parsing + artifact internals. They can render or mount those per-gate rows through + `getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState()`, + `getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract()`, + `getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness()`, + `renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()`, and + `mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()`, and + `getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan()` to show + the URL workflow next action or command without executing gates. Direct JSON + validation callers can use + `getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan()` for the + artifact-validation next-command plan. They can render or mount that action + plan through + `getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState()`, + `getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract()`, + `getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness()`, + `renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()`, and + `mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()`. Browser shells can also pass the JSON to `linuxCncIniPanelWorkflowOverviewApi.validateWorkflowOverviewReleaseReadinessArtifactJson()` through the workflow overview iframe and receive stable validation rows. They + can call `runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow()` when they + need parse, validation, summary, action-plan, and gate execution summary + output in one workflow result, and + `getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel()` + when they need stable dashboard rows for that JSON workflow. The JSON + workflow action plan is available through + `getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan()`. They + can render or mount the JSON workflow state into caller-owned DOM through + `getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract()`, + `getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness()`, + `renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()`, and + `mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()`. They can also call `linuxCncIniPanelWorkflowOverviewApi.loadWorkflowOverviewReleaseReadinessArtifactUrl()` to fetch the artifact URL, validate it, and optionally mount the resulting read-only render state. That URL workflow includes a validation summary view-model and can be summarized with `getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel()`. + The same workflow can expose the ordered next-action plan through + `getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan()`. Shells + can render or mount the URL workflow state through + `getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState()`, + `getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract()`, + `getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness()`, + `renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()`, and + `mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()`. + Direct artifact JSON validation can expose its own next-command plan through + `getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan()`. When the outer shell needs DOM output, the workflow overview iframe also exposes display, render-state, DOM readiness, renderer, and mount helpers for the @@ -126,6 +219,7 @@ SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm. SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh +wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh wasm-port/tests/ui/node/verify_ui_node_smokes.sh wasm-port/tests/sdk/node/verify_project_release_artifact_url_workflow.sh wasm-port/tests/host/verify_host_smokes.sh @@ -166,6 +260,7 @@ opfs_session_docs_node_smoke=ok sim_configs_coverage_docs_node_smoke=ok host_runtime_boundary_docs_node_smoke=ok sdk_surface_node_smoke=ok +project_batch_acceptance_workflow_node_smoke=ok project_release_artifact_url_workflow_node_smoke=ok ``` diff --git a/wasm-port/docs/real-browser-simulation-priority.md b/wasm-port/docs/real-browser-simulation-priority.md new file mode 100644 index 0000000..ce41ccf --- /dev/null +++ b/wasm-port/docs/real-browser-simulation-priority.md @@ -0,0 +1,82 @@ +# Real Browser Simulation Priority + +This document is a standing project constraint for all work after 2026-06-16. + +## Primary Objective + +The first priority is to deliver a real browser CNC simulation page, not more +project metadata or dashboard-only helpers. Future batches should prefer +visible, interactive browser capability that moves the product toward an +operator-usable CNC simulation surface. + +The target page must become a real CNC simulation page with: + +- a machine/session loader backed by the existing OPFS/session workflow; +- a G-code program view backed by persisted or staged program text; +- LinuxCNC-backed interpreter execution through the existing WASM SDK; +- machine state panels for run status, active program, machine files, and + missing readiness; +- a toolpath/preview area driven by LinuxCNC-produced interpreter/canonical + output or existing validated runtime events, not JavaScript-owned CNC + semantics; +- browser smoke coverage that proves the page loads, renders nonempty runtime + state, and can run a representative LinuxCNC-backed program flow. + +The first implementation entry point is: + +```text +runtime/ui/simulation/index.html +``` + +Its browser gate is: + +```bash +wasm-port/tests/browser/verify_real_simulation_browser.sh +``` + +## Batch Priority + +Unless a user explicitly changes priority, choose work in this order: + +1. Real UI/browser simulation page capability. +2. Browser workflow smoke for that simulation capability. +3. UI APIs needed by the simulation page. +4. OPFS/session integration needed by the simulation page. +5. SDK/API helpers only when they directly unblock the simulation page. +6. Documentation or release metadata only when it creates an executable gate or + protects the simulation-page direction. + +Every implementation batch still must produce at least one callable API, +verifiable workflow, executable gate, or real UI/browser capability. + +## Boundary Rules + +The browser simulation page must not implement CNC semantics in JavaScript. +G-code interpretation, canonical motion behavior, tool semantics, parameter +semantics, kinematics, remap behavior, and planner behavior must remain owned by +vendored LinuxCNC source and existing LinuxCNC-backed WASM C ABI boundaries. + +JavaScript/browser code may own: + +- layout and interaction state; +- OPFS/session persistence; +- file staging into the Emscripten filesystem; +- calls into the SDK/WASM boundary; +- rendering LinuxCNC-produced output, canonical events, and validated runtime + summaries; +- non-CNC UI state such as selected file, active tab, viewport mode, and + validation messages. + +## Required Evidence + +The priority is enforced by: + +```bash +wasm-port/tests/docs/node/verify_real_browser_simulation_priority_docs.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tests/host/verify_project_release_gate.sh +``` + +The docs gate must verify that this priority document, the release handoff, the +README, and `PROJECT_COMPLETION_TRACKER.md` all point future work toward a real +browser CNC simulation page while preserving the LinuxCNC semantic boundary. diff --git a/wasm-port/runtime/sdk/README.md b/wasm-port/runtime/sdk/README.md index f7ad49a..95a5bce 100644 --- a/wasm-port/runtime/sdk/README.md +++ b/wasm-port/runtime/sdk/README.md @@ -44,8 +44,27 @@ import { createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomReadiness, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState, + runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel, createLinuxCncIniSdk, createLinuxCncInterpSdk, @@ -87,14 +106,35 @@ import { machineFilePaths, machineIniPath, mountIniPanelShellWorkflowOverviewEmbeddingMountState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState, mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactState, normalizeOpfsPath, parameterFilePath, parseProjectReleaseReadinessArtifactJson, planIniFileContextStaging, planSimConfigStaging, + createProjectBatchAcceptanceActionPlan, + createProjectBatchAcceptanceCapabilityMatrix, + createProjectBatchAcceptanceChecklist, + createProjectBatchAcceptanceReport, + createProjectBatchAcceptanceReportJsonWorkflow, + createProjectBatchAcceptanceReportUrlWorkflowActionPlan, + createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel, + createProjectBatchAcceptanceReportValidationActionPlan, + createProjectBatchAcceptanceReportValidationSummaryViewModel, + createProjectBatchAcceptanceReportValidation, + createProjectBatchAcceptanceSummaryViewModel, + createProjectBatchAcceptanceWorkflow, + loadProjectBatchAcceptanceReportUrlWorkflow, readMachineSessionReadiness, renderIniPanelShellWorkflowOverviewEmbeddingMountState, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState, renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactState, restoreMachineParametersFromOpfs, saveMachineSessionSnapshot, @@ -227,6 +267,35 @@ such as `project_release_gate=ok` or explicit gate results. `createProjectReleaseReadinessSummaryViewModel()` turns the report into stable status text, a status line, and display rows for dashboards without executing any gate. +`createProjectBatchAcceptanceCapabilityMatrix()` normalizes per-batch evidence +and classifies accepted `api`, `workflow`, `gate`, and `browser` capabilities +separately from rejected docs-only or unknown entries. +`createProjectBatchAcceptanceWorkflow()` packages that capability matrix with +release-gate evidence for the project rule that each batch must produce at +least one callable API, verifiable workflow, executable gate, or browser +capability. The companion +`createProjectBatchAcceptanceSummaryViewModel()` and +`createProjectBatchAcceptanceActionPlan()` helpers make that evidence +dashboard-friendly and provide the next gate command when the batch acceptance +smoke has not been observed. `createProjectBatchAcceptanceChecklist()` combines +the capability matrix, gate evidence, and action plan into pass/blocked rows +for release dashboards. `createProjectBatchAcceptanceReport()` packages the +matrix, workflow, summary, action plan, and checklist into one stable object +for external CI or release dashboards. +`createProjectBatchAcceptanceReportValidation()` validates that report shape +without copying test assertions. +`createProjectBatchAcceptanceReportValidationSummaryViewModel()` and +`createProjectBatchAcceptanceReportValidationActionPlan()` expose dashboard rows +and the next artifact gate command for blocked report validation. +`createProjectBatchAcceptanceReportJsonWorkflow()` is the non-throwing JSON +workflow for external CI systems that exchange batch acceptance reports as +strings. +`loadProjectBatchAcceptanceReportUrlWorkflow()` is the fetch-driven URL +workflow for hosted batch acceptance reports. It returns fetch status, the JSON +workflow, report validation, and missing evidence without throwing. +`createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel()` and +`createProjectBatchAcceptanceReportUrlWorkflowActionPlan()` turn the same URL +workflow into dashboard rows and ordered input/fetch/gate actions. This helper does not execute shell commands and does not inspect or implement CNC behavior. It only packages project-level release evidence and blocked @@ -273,6 +342,13 @@ dedicated executable SDK gate for that URL workflow. It verifies caller-provided fetch handling, ready summaries, action plans, missing input, HTTP failure, thrown fetch errors, and ends with `project_release_artifact_url_workflow_node_smoke=ok`. +`tests/sdk/node/verify_project_batch_acceptance_workflow.sh` is the executable +SDK gate for the per-batch acceptance workflow. It runs the workflow smoke, +writes `build/project-batch-acceptance.json` through +`write_project_batch_acceptance_artifact.mjs`, validates that artifact through +`verify_project_batch_acceptance_artifact.sh`, and emits +`project_batch_acceptance_workflow_node_smoke=ok` before ending with +`project_batch_acceptance_artifact_node_smoke=ok`. ## OPFS/session persistence exports @@ -339,12 +415,57 @@ for external browser shells: - `validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson()` for the read-only workflow overview artifact validation wrapper exposed by `workflow-overview.html`. Use + `runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow()` when + a shell already has artifact JSON and needs parse, validation, summary, + action-plan, and gate execution summary output in one non-throwing workflow. + `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel()` + turns that JSON workflow into stable dashboard status text and rows. + `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan()` + exposes the JSON workflow next-command plan without requiring callers to read + internal workflow fields. + `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract()`, + `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness()`, + `renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()`, + and `mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()` + let a shell render or mount that JSON workflow render-state into caller-owned + DOM without executing release gate commands. + Use `loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl()` when a browser or shell should fetch the artifact JSON URL, validate it, and optionally mount the resulting render-state into caller-owned DOM. The URL - workflow result includes `validationSummaryViewModel` for dashboards. + workflow result includes `validationSummaryViewModel` and + `gateExecutionSummaryViewModel` for dashboards. `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel()` turns the full URL workflow result into stable status text and rows. + `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan()` + exposes the same URL workflow next-action/next-command plan through the + browser workflow overview surface. + `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState()`, + `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract()`, + `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness()`, + `renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()`, + and `mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()` + let callers render the URL workflow status into their own DOM. + `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan()` + exposes the direct artifact-validation next-command plan when callers already + have parsed or validated artifact JSON. + `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState()`, + `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract()`, + `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness()`, + `renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()`, + and `mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()` + let callers render that validation action plan into caller-owned DOM. + `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel()` + and + `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel()` + expose the embedded release gate execution summary for external shells without + requiring callers to parse artifact internals. + `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState()`, + `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract()`, + `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness()`, + `renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()`, + and `mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()` + let callers render the per-gate execution summary rows into caller-owned DOM. `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel()`, `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState()`, `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel()`, diff --git a/wasm-port/runtime/sdk/src/index.js b/wasm-port/runtime/sdk/src/index.js index d36340c..2442f46 100644 --- a/wasm-port/runtime/sdk/src/index.js +++ b/wasm-port/runtime/sdk/src/index.js @@ -1,6 +1,18 @@ export { createLinuxCncIniSdk } from "./linuxcnc-ini.js"; export { createLinuxCncInterpSdk } from "./linuxcnc-interp.js"; export { + createProjectBatchAcceptanceActionPlan, + createProjectBatchAcceptanceCapabilityMatrix, + createProjectBatchAcceptanceChecklist, + createProjectBatchAcceptanceReport, + createProjectBatchAcceptanceReportJsonWorkflow, + createProjectBatchAcceptanceReportUrlWorkflowActionPlan, + createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel, + createProjectBatchAcceptanceReportValidationActionPlan, + createProjectBatchAcceptanceReportValidationSummaryViewModel, + createProjectBatchAcceptanceReportValidation, + createProjectBatchAcceptanceSummaryViewModel, + createProjectBatchAcceptanceWorkflow, createProjectReleaseGateActionPlan, createProjectReleaseGateExecutionManifest, createProjectReleaseGateExecutionSummaryViewModel, @@ -14,6 +26,7 @@ export { createProjectReleaseReadinessArtifactValidationSummaryViewModel, createProjectReleaseReadinessReport, createProjectReleaseReadinessSummaryViewModel, + loadProjectBatchAcceptanceReportUrlWorkflow, loadProjectReleaseReadinessArtifactUrlWorkflow, parseProjectReleaseReadinessArtifactJson, } from "./project-release-readiness.js"; @@ -109,8 +122,27 @@ export { createIniPanelShellWorkflowOverviewEmbeddingMountRenderState, createIniPanelShellWorkflowOverviewEmbeddingRenderState, createIniPanelShellWorkflowOverviewEmbeddingReport, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomReadiness, @@ -125,6 +157,14 @@ export { isSupportedIniPanelShellSessionHandoffPackage, isSupportedIniPanelShellSessionHandoffSummary, loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl, + runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState, mountIniPanelShellSessionHandoff, mountIniPanelShellWorkflowOverviewEmbedding, mountIniPanelShellWorkflowOverviewEmbeddingMountState, diff --git a/wasm-port/runtime/sdk/src/project-release-readiness.js b/wasm-port/runtime/sdk/src/project-release-readiness.js index b7736e9..c788bcb 100644 --- a/wasm-port/runtime/sdk/src/project-release-readiness.js +++ b/wasm-port/runtime/sdk/src/project-release-readiness.js @@ -46,6 +46,12 @@ const REQUIRED_RELEASE_GATES = [ 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", }, + { + id: "project-batch-acceptance", + label: "Project batch acceptance workflow smoke", + command: "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", + expectedOutput: "project_batch_acceptance_workflow_node_smoke=ok", + }, { id: "ui-node-smokes", label: "UI Node smokes", @@ -336,6 +342,864 @@ export function createProjectReleaseGateActionPlan({ }; } +const BATCH_ACCEPTANCE_CAPABILITY_TYPES = [ + "api", + "workflow", + "gate", + "browser", +]; + +function normalizeBatchCapabilities(capabilities) { + return arrayOrEmpty(capabilities) + .map((capability) => objectOrEmpty(capability)) + .filter(({ id, type }) => typeof id === "string" && typeof type === "string") + .map((capability) => ({ + id: capability.id, + type: capability.type, + label: capability.label ?? capability.id, + evidence: capability.evidence ?? null, + command: capability.command ?? null, + })); +} + +export function createProjectBatchAcceptanceCapabilityMatrix({ + capabilities = [], +} = {}) { + const normalizedCapabilities = normalizeBatchCapabilities(capabilities); + const acceptedCapabilities = normalizedCapabilities.filter(({ type }) => + BATCH_ACCEPTANCE_CAPABILITY_TYPES.includes(type) + ); + const rejectedCapabilities = normalizedCapabilities.filter(({ type }) => + !BATCH_ACCEPTANCE_CAPABILITY_TYPES.includes(type) + ); + const capabilityTypes = [...new Set(acceptedCapabilities.map(({ type }) => type))]; + const countsByType = BATCH_ACCEPTANCE_CAPABILITY_TYPES.reduce((counts, type) => ({ + ...counts, + [type]: acceptedCapabilities.filter((capability) => capability.type === type).length, + }), {}); + const missing = acceptedCapabilities.length > 0 ? [] : ["batch-capability"]; + + return { + apiName: "project-batch-acceptance-capability-matrix", + matrixVersion: 1, + phase: missing.length === 0 ? "ready" : "blocked", + ready: missing.length === 0, + acceptedCapabilityTypes: BATCH_ACCEPTANCE_CAPABILITY_TYPES, + capabilityTypes, + capabilityCount: acceptedCapabilities.length, + acceptedCount: acceptedCapabilities.length, + rejectedCount: rejectedCapabilities.length, + countsByType, + capabilities: acceptedCapabilities, + rejectedCapabilities, + missing, + rows: [ + { + id: "accepted-capabilities", + label: "Accepted capabilities", + value: `${acceptedCapabilities.length}`, + }, + { + id: "accepted-types", + label: "Accepted capability types", + value: capabilityTypes.join(", ") || "none", + }, + { + id: "rejected-capabilities", + label: "Rejected capabilities", + value: `${rejectedCapabilities.length}`, + }, + { + id: "missing", + label: "Missing capability evidence", + value: missing.length > 0 ? missing.join(", ") : "none", + }, + ], + }; +} + +export function createProjectBatchAcceptanceWorkflow({ + batchId = "current-batch", + capabilities = [], + gateResults = {}, + observedOutputs = [], +} = {}) { + const capabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({ capabilities }); + const gateManifest = createProjectReleaseGateManifest(); + const gateResultMatrix = createProjectReleaseGateResultMatrix({ + gateResults, + observedOutputs, + manifest: gateManifest, + }); + const batchGatePassed = gateResultMatrix.rows + .find(({ id }) => id === "project-batch-acceptance")?.passed === true; + const ready = capabilityMatrix.ready && batchGatePassed; + const missing = [ + ...capabilityMatrix.missing, + ...(batchGatePassed ? [] : ["project-batch-acceptance"]), + ]; + + return { + apiName: "project-batch-acceptance-workflow", + workflowVersion: 1, + phase: ready ? "ready" : "blocked", + ready, + batchId, + acceptedCapabilityTypes: capabilityMatrix.acceptedCapabilityTypes, + capabilityTypes: capabilityMatrix.capabilityTypes, + capabilityCount: capabilityMatrix.capabilityCount, + capabilityMatrix, + capabilities: capabilityMatrix.capabilities, + rejectedCapabilities: capabilityMatrix.rejectedCapabilities, + gateResultMatrix, + missing, + rows: [ + { + id: "batch", + label: "Batch", + value: batchId, + }, + { + id: "capabilities", + label: "Accepted capabilities", + value: capabilityMatrix.capabilityTypes.join(", ") || "none", + }, + { + id: "project-batch-acceptance", + label: "Batch acceptance gate", + value: batchGatePassed ? "passed" : "unknown", + }, + { + id: "missing", + label: "Missing acceptance evidence", + value: missing.length > 0 ? missing.join(", ") : "none", + }, + ], + }; +} + +export function createProjectBatchAcceptanceSummaryViewModel( + workflow = createProjectBatchAcceptanceWorkflow(), +) { + const missing = arrayOrEmpty(workflow?.missing); + const ready = workflow?.ready === true; + const statusText = ready ? "Ready" : "Blocked"; + const detailText = ready + ? "Batch acceptance evidence complete" + : (missing.length > 0 ? missing.join(", ") : "batch acceptance evidence missing"); + return { + apiName: "project-batch-acceptance-summary-view-model", + viewModelVersion: 1, + phase: ready ? "ready" : "blocked", + ready, + title: "Project batch acceptance", + statusText, + detailText, + statusLine: `${statusText}: ${detailText}`, + rows: [ + { + id: "batch", + label: "Batch", + value: workflow?.batchId ?? "current-batch", + }, + { + id: "capability-count", + label: "Accepted capability count", + value: `${workflow?.capabilityCount ?? 0}`, + }, + { + id: "capability-types", + label: "Accepted capability types", + value: arrayOrEmpty(workflow?.capabilityTypes).join(", ") || "none", + }, + { + id: "missing", + label: "Missing acceptance evidence", + value: missing.length > 0 ? missing.join(", ") : "none", + }, + ], + }; +} + +export function createProjectBatchAcceptanceActionPlan( + workflow = createProjectBatchAcceptanceWorkflow(), +) { + const missing = arrayOrEmpty(workflow?.missing); + const ready = workflow?.ready === true; + const commands = ready ? [] : [ + ...(missing.includes("project-batch-acceptance") + ? [{ + step: 1, + id: "project-batch-acceptance", + label: "Run project batch acceptance workflow smoke", + command: "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", + expectedOutput: "project_batch_acceptance_workflow_node_smoke=ok", + }] + : []), + ]; + const shellScript = commands.length > 0 + ? [ + "#!/usr/bin/env bash", + "set -euo pipefail", + "", + ...commands.map(({ command }) => command), + "", + ].join("\n") + : (ready + ? "# Project batch acceptance workflow is already ready.\n" + : "# Project batch acceptance workflow requires at least one API, workflow, gate, or browser capability.\n"); + + return { + apiName: "project-batch-acceptance-action-plan", + planVersion: 1, + phase: ready ? "ready" : "blocked", + ready, + missing, + commandCount: commands.length, + nextActionId: commands[0]?.id ?? (missing[0] ?? null), + nextCommand: commands[0]?.command ?? null, + commands, + shellScript, + rows: [ + { + id: "batch-acceptance", + label: "Batch acceptance", + value: ready ? "ready" : "blocked", + }, + { + id: "missing", + label: "Missing acceptance evidence", + value: missing.length > 0 ? missing.join(", ") : "none", + }, + { + id: "next-command", + label: "Next command", + value: commands[0]?.command ?? "none", + }, + ], + }; +} + +export function createProjectBatchAcceptanceChecklist({ + workflow = createProjectBatchAcceptanceWorkflow(), + actionPlan = createProjectBatchAcceptanceActionPlan(workflow), +} = {}) { + const capabilityMatrix = objectOrEmpty(workflow?.capabilityMatrix); + const ready = workflow?.ready === true; + const items = [ + { + id: "accepted-capability", + label: "At least one API, workflow, gate, or browser capability", + status: capabilityMatrix.ready === true ? "pass" : "blocked", + passed: capabilityMatrix.ready === true, + detail: arrayOrEmpty(workflow?.capabilityTypes).join(", ") || "none", + }, + { + id: "batch-acceptance-gate", + label: "Batch acceptance gate evidence", + status: !arrayOrEmpty(workflow?.missing).includes("project-batch-acceptance") ? "pass" : "blocked", + passed: !arrayOrEmpty(workflow?.missing).includes("project-batch-acceptance"), + detail: workflow?.gateResultMatrix?.rows + ?.find?.(({ id }) => id === "project-batch-acceptance")?.status ?? "unknown", + }, + { + id: "next-action", + label: "Next acceptance action", + status: actionPlan?.nextActionId ? "blocked" : "pass", + passed: !actionPlan?.nextActionId, + detail: actionPlan?.nextCommand ?? "none", + }, + ]; + + return { + apiName: "project-batch-acceptance-checklist", + checklistVersion: 1, + phase: ready ? "ready" : "blocked", + ready, + batchId: workflow?.batchId ?? "current-batch", + passedCount: items.filter(({ passed }) => passed).length, + blockedCount: items.filter(({ passed }) => !passed).length, + items, + rows: items.map(({ id, label, status, detail }) => ({ + id, + label, + value: `${status}: ${detail}`, + })), + }; +} + +export function createProjectBatchAcceptanceReport({ + batchId = "current-batch", + capabilities = [], + gateResults = {}, + observedOutputs = [], +} = {}) { + const capabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({ capabilities }); + const workflow = createProjectBatchAcceptanceWorkflow({ + batchId, + capabilities, + gateResults, + observedOutputs, + }); + const summaryViewModel = createProjectBatchAcceptanceSummaryViewModel(workflow); + const actionPlan = createProjectBatchAcceptanceActionPlan(workflow); + const checklist = createProjectBatchAcceptanceChecklist({ workflow, actionPlan }); + + return { + apiName: "project-batch-acceptance-report", + reportVersion: 1, + phase: workflow.phase, + ready: workflow.ready, + batchId: workflow.batchId, + missing: [...workflow.missing], + capabilityMatrix, + workflow, + summaryViewModel, + actionPlan, + checklist, + rows: [ + { + id: "summary", + label: "Batch acceptance summary", + value: summaryViewModel.statusLine, + }, + { + id: "capabilities", + label: "Accepted capabilities", + value: capabilityMatrix.capabilityTypes.join(", ") || "none", + }, + { + id: "checklist", + label: "Checklist", + value: `${checklist.passedCount}/${checklist.items.length} passed`, + }, + { + id: "next-command", + label: "Next command", + value: actionPlan.nextCommand ?? "none", + }, + ], + }; +} + +export function createProjectBatchAcceptanceReportValidation(report = {}) { + const reportObject = objectOrEmpty(report); + const capabilityMatrix = objectOrEmpty(reportObject.capabilityMatrix); + const workflow = objectOrEmpty(reportObject.workflow); + const summaryViewModel = objectOrEmpty(reportObject.summaryViewModel); + const actionPlan = objectOrEmpty(reportObject.actionPlan); + const checklist = objectOrEmpty(reportObject.checklist); + const rows = arrayOrEmpty(reportObject.rows); + const missing = [ + ...(reportObject.apiName === "project-batch-acceptance-report" ? [] : ["apiName"]), + ...(reportObject.reportVersion === 1 ? [] : ["reportVersion"]), + ...(reportObject.phase === "ready" ? [] : ["phase"]), + ...(reportObject.ready === true ? [] : ["ready"]), + ...(Array.isArray(reportObject.missing) && reportObject.missing.length === 0 ? [] : ["missing"]), + ...(capabilityMatrix.apiName === "project-batch-acceptance-capability-matrix" + && capabilityMatrix.ready === true + ? [] + : ["capabilityMatrix"]), + ...(workflow.apiName === "project-batch-acceptance-workflow" && workflow.ready === true + ? [] + : ["workflow"]), + ...(summaryViewModel.apiName === "project-batch-acceptance-summary-view-model" + && summaryViewModel.ready === true + ? [] + : ["summaryViewModel"]), + ...(actionPlan.apiName === "project-batch-acceptance-action-plan" && actionPlan.ready === true + ? [] + : ["actionPlan"]), + ...(checklist.apiName === "project-batch-acceptance-checklist" && checklist.ready === true + ? [] + : ["checklist"]), + ...(rows.find(({ id, value }) => id === "checklist" && value === "3/3 passed") + ? [] + : ["rows.checklist"]), + ]; + + return { + apiName: "project-batch-acceptance-report-validation", + validationVersion: 1, + phase: missing.length === 0 ? "ready" : "blocked", + ready: missing.length === 0, + missing, + batchId: reportObject.batchId ?? null, + reportApiName: reportObject.apiName ?? null, + reportVersion: reportObject.reportVersion ?? null, + rows: [ + { + id: "report", + label: "Batch acceptance report", + value: reportObject.apiName ?? "missing", + }, + { + id: "capability-matrix", + label: "Capability matrix", + value: capabilityMatrix.ready === true ? "ready" : "missing", + }, + { + id: "workflow", + label: "Batch workflow", + value: workflow.ready === true ? "ready" : "missing", + }, + { + id: "checklist", + label: "Checklist", + value: checklist.ready === true ? "ready" : "missing", + }, + { + id: "missing", + label: "Missing report evidence", + value: missing.length > 0 ? missing.join(", ") : "none", + }, + ], + }; +} + +export function createProjectBatchAcceptanceReportValidationSummaryViewModel( + validation = createProjectBatchAcceptanceReportValidation(), +) { + const missing = arrayOrEmpty(validation?.missing); + const ready = validation?.ready === true; + const statusText = ready ? "Ready" : "Blocked"; + const detailText = ready + ? "Batch acceptance report evidence complete" + : (missing.length > 0 ? missing.join(", ") : "batch acceptance report evidence missing"); + return { + apiName: "project-batch-acceptance-report-validation-summary-view-model", + viewModelVersion: 1, + phase: ready ? "ready" : "blocked", + ready, + title: "Project batch acceptance report validation", + statusText, + detailText, + statusLine: `${statusText}: ${detailText}`, + rows: [ + { + id: "report", + label: "Batch acceptance report", + value: validation?.reportApiName ?? "missing", + }, + { + id: "batch", + label: "Batch", + value: validation?.batchId ?? "missing", + }, + { + id: "validation", + label: "Report validation", + value: ready ? "ready" : "blocked", + }, + { + id: "missing", + label: "Missing report evidence", + value: missing.length > 0 ? missing.join(", ") : "none", + }, + ], + }; +} + +export function createProjectBatchAcceptanceReportValidationActionPlan( + validation = createProjectBatchAcceptanceReportValidation(), +) { + const missing = arrayOrEmpty(validation?.missing); + const ready = validation?.ready === true; + const commands = ready ? [] : [ + { + step: 1, + id: "project-batch-acceptance", + label: "Run project batch acceptance workflow and artifact gate", + command: "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", + expectedOutput: "project_batch_acceptance_artifact_node_smoke=ok", + }, + ]; + const shellScript = commands.length > 0 + ? [ + "#!/usr/bin/env bash", + "set -euo pipefail", + "", + ...commands.map(({ command }) => command), + "", + ].join("\n") + : "# Project batch acceptance report validation is already ready.\n"; + + return { + apiName: "project-batch-acceptance-report-validation-action-plan", + planVersion: 1, + phase: ready ? "ready" : "blocked", + ready, + missing, + missingCount: missing.length, + commandCount: commands.length, + nextActionId: commands[0]?.id ?? null, + nextCommand: commands[0]?.command ?? null, + commands, + shellScript, + rows: [ + { + id: "report-validation", + label: "Report validation", + value: ready ? "ready" : "blocked", + }, + { + id: "missing", + label: "Missing report evidence", + value: missing.length > 0 ? missing.join(", ") : "none", + }, + { + id: "next-command", + label: "Next command", + value: commands[0]?.command ?? "none", + }, + ], + }; +} + +export function createProjectBatchAcceptanceReportJsonWorkflow({ reportJson = "" } = {}) { + try { + const report = JSON.parse(String(reportJson)); + const validation = createProjectBatchAcceptanceReportValidation(report); + return { + apiName: "project-batch-acceptance-report-json-workflow", + workflowVersion: 1, + phase: validation.ready ? "ready" : "blocked", + ready: validation.ready, + parsed: true, + parseError: null, + report, + validation, + missing: validation.missing, + rows: [ + { + id: "parse", + label: "Batch report JSON parse", + value: "ready", + }, + { + id: "validation", + label: "Batch report validation", + value: validation.ready ? "ready" : "blocked", + }, + { + id: "missing", + label: "Missing report evidence", + value: validation.missing.length > 0 ? validation.missing.join(", ") : "none", + }, + ], + }; + } catch (error) { + const validation = createProjectBatchAcceptanceReportValidation({}); + return { + apiName: "project-batch-acceptance-report-json-workflow", + workflowVersion: 1, + phase: "blocked", + ready: false, + parsed: false, + parseError: error?.message ?? "batch acceptance report JSON parse error", + report: null, + validation, + missing: ["report-json", ...validation.missing], + rows: [ + { + id: "parse", + label: "Batch report JSON parse", + value: "blocked", + }, + { + id: "validation", + label: "Batch report validation", + value: "blocked", + }, + { + id: "missing", + label: "Missing report evidence", + value: ["report-json", ...validation.missing].join(", "), + }, + ], + }; + } +} + +export async function loadProjectBatchAcceptanceReportUrlWorkflow({ + reportUrl = "", + fetchRef = globalThis.fetch, +} = {}) { + if (!reportUrl || typeof fetchRef !== "function") { + const jsonWorkflow = createProjectBatchAcceptanceReportJsonWorkflow({ reportJson: "" }); + const missing = [ + ...(reportUrl ? [] : ["report-url"]), + ...(typeof fetchRef === "function" ? [] : ["fetch"]), + ...jsonWorkflow.missing, + ]; + return { + apiName: "project-batch-acceptance-report-url-workflow", + workflowVersion: 1, + phase: "blocked", + ready: false, + reportUrl, + fetched: false, + httpStatus: null, + fetchError: null, + jsonWorkflow, + validation: jsonWorkflow.validation, + missing, + rows: [ + { + id: "fetch", + label: "Batch report URL fetch", + value: "blocked", + }, + { + id: "json-workflow", + label: "Batch report JSON workflow", + value: "blocked", + }, + { + id: "missing", + label: "Missing report evidence", + value: missing.join(", "), + }, + ], + }; + } + + try { + const response = await fetchRef(reportUrl); + const httpStatus = response?.status ?? null; + if (!response?.ok) { + const jsonWorkflow = createProjectBatchAcceptanceReportJsonWorkflow({ reportJson: "" }); + const fetchError = `batch report URL fetch failed${httpStatus ? `: ${httpStatus}` : ""}`; + return { + apiName: "project-batch-acceptance-report-url-workflow", + workflowVersion: 1, + phase: "blocked", + ready: false, + reportUrl, + fetched: false, + httpStatus, + fetchError, + jsonWorkflow, + validation: jsonWorkflow.validation, + missing: ["report-fetch", ...jsonWorkflow.missing], + rows: [ + { + id: "fetch", + label: "Batch report URL fetch", + value: "blocked", + }, + { + id: "json-workflow", + label: "Batch report JSON workflow", + value: "blocked", + }, + { + id: "missing", + label: "Missing report evidence", + value: ["report-fetch", ...jsonWorkflow.missing].join(", "), + }, + ], + }; + } + + const reportJson = await response.text(); + const jsonWorkflow = createProjectBatchAcceptanceReportJsonWorkflow({ reportJson }); + return { + apiName: "project-batch-acceptance-report-url-workflow", + workflowVersion: 1, + phase: jsonWorkflow.ready === true ? "ready" : "blocked", + ready: jsonWorkflow.ready === true, + reportUrl, + fetched: true, + httpStatus, + fetchError: null, + jsonWorkflow, + validation: jsonWorkflow.validation, + missing: jsonWorkflow.missing, + rows: [ + { + id: "fetch", + label: "Batch report URL fetch", + value: "ready", + }, + { + id: "json-workflow", + label: "Batch report JSON workflow", + value: jsonWorkflow.ready === true ? "ready" : "blocked", + }, + { + id: "missing", + label: "Missing report evidence", + value: jsonWorkflow.missing.length > 0 ? jsonWorkflow.missing.join(", ") : "none", + }, + ], + }; + } catch (error) { + const jsonWorkflow = createProjectBatchAcceptanceReportJsonWorkflow({ reportJson: "" }); + return { + apiName: "project-batch-acceptance-report-url-workflow", + workflowVersion: 1, + phase: "blocked", + ready: false, + reportUrl, + fetched: false, + httpStatus: null, + fetchError: error?.message ?? "batch report URL fetch error", + jsonWorkflow, + validation: jsonWorkflow.validation, + missing: ["report-fetch", ...jsonWorkflow.missing], + rows: [ + { + id: "fetch", + label: "Batch report URL fetch", + value: "blocked", + }, + { + id: "json-workflow", + label: "Batch report JSON workflow", + value: "blocked", + }, + { + id: "missing", + label: "Missing report evidence", + value: ["report-fetch", ...jsonWorkflow.missing].join(", "), + }, + ], + }; + } +} + +export function createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel( + workflow = {}, +) { + const missing = arrayOrEmpty(workflow?.missing); + const ready = workflow?.ready === true; + const statusText = ready ? "Ready" : "Blocked"; + const detailText = ready + ? "Batch report URL workflow complete" + : (missing.length > 0 ? missing.join(", ") : workflow?.fetchError ?? "batch report URL workflow blocked"); + return { + apiName: "project-batch-acceptance-report-url-workflow-summary-view-model", + viewModelVersion: 1, + phase: ready ? "ready" : "blocked", + ready, + title: "Project batch acceptance report URL workflow", + statusText, + detailText, + statusLine: `${statusText}: ${detailText}`, + rows: [ + { + id: "report-url", + label: "Report URL", + value: workflow?.reportUrl || "missing", + }, + { + id: "fetch", + label: "Fetch", + value: workflow?.fetched === true ? `ready${workflow?.httpStatus ? ` (${workflow.httpStatus})` : ""}` : "blocked", + }, + { + id: "json-workflow", + label: "JSON workflow", + value: workflow?.jsonWorkflow?.ready === true ? "ready" : "blocked", + }, + { + id: "report-validation", + label: "Report validation", + value: workflow?.validation?.ready === true ? "ready" : "blocked", + }, + { + id: "missing", + label: "Missing workflow evidence", + value: missing.length > 0 ? missing.join(", ") : "none", + }, + ], + }; +} + +export function createProjectBatchAcceptanceReportUrlWorkflowActionPlan( + workflow = {}, +) { + const missing = arrayOrEmpty(workflow?.missing); + const ready = workflow?.ready === true; + const inputActions = [ + ...(missing.includes("report-url") + ? [{ + id: "provide-report-url", + label: "Provide project batch acceptance report URL", + kind: "input", + command: null, + expectedOutput: null, + }] + : []), + ...(missing.includes("fetch") + ? [{ + id: "provide-fetch", + label: "Provide fetch implementation", + kind: "input", + command: null, + expectedOutput: null, + }] + : []), + ...(missing.includes("report-fetch") + ? [{ + id: "verify-report-url-fetch", + label: "Verify project batch acceptance report URL fetch", + kind: "fetch", + command: null, + expectedOutput: null, + }] + : []), + ]; + const commandActions = ready ? [] : [ + { + id: "project-batch-acceptance", + label: "Run project batch acceptance workflow smoke", + kind: "command", + command: "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", + expectedOutput: "project_batch_acceptance_workflow_node_smoke=ok", + }, + ]; + const actions = ready ? [] : [...inputActions, ...commandActions].map((action, index) => ({ + step: index + 1, + ...action, + })); + const commands = actions.filter(({ command }) => typeof command === "string" && command.length > 0); + const shellScript = commands.length > 0 + ? [ + "#!/usr/bin/env bash", + "set -euo pipefail", + "", + ...commands.map(({ command }) => command), + "", + ].join("\n") + : (ready + ? "# Project batch acceptance report URL workflow is already ready.\n" + : "# Project batch acceptance report URL workflow requires non-shell input.\n"); + + return { + apiName: "project-batch-acceptance-report-url-workflow-action-plan", + planVersion: 1, + phase: ready ? "ready" : "blocked", + ready, + missing, + actionCount: actions.length, + commandCount: commands.length, + nextActionId: actions[0]?.id ?? null, + nextCommand: commands[0]?.command ?? null, + actions, + commands, + shellScript, + rows: actions.map(({ id, label, kind, command, expectedOutput }) => ({ + id, + label, + kind, + value: command ?? kind, + expectedOutput, + })), + }; +} + export function createProjectReleaseReadinessReport({ gateResults = {}, observedOutputs = [], diff --git a/wasm-port/runtime/ui/ini-panel/launch.html b/wasm-port/runtime/ui/ini-panel/launch.html index 0a7c305..bbf3c3d 100644 --- a/wasm-port/runtime/ui/ini-panel/launch.html +++ b/wasm-port/runtime/ui/ini-panel/launch.html @@ -91,6 +91,7 @@ Open edit and run panel Open read-only control view Open shell handoff overview + Open real browser simulation

Checking shell handoff compatibility.

@@ -297,15 +298,124 @@ mountWorkflowOverviewEmbedding: (options) => ( shellViewModel.mountWorkflowOverviewEmbedding(options) ), + runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow: (options) => ( + shellViewModel.runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow(options) + ), + getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel: (workflow) => ( + shellViewModel.createWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel(workflow) + ), + getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan: (workflow) => ( + shellViewModel.createWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan(workflow) + ), + getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState: (summaryViewModel) => ( + shellViewModel.createWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState(summaryViewModel) + ), + getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract: (options) => ( + shellViewModel.createWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract(options) + ), + getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness: (options) => ( + shellViewModel.createWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness( + options ?? { documentRef: document }, + ) + ), + renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState: (renderState, options) => ( + shellViewModel.renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState( + renderState, + options ?? { documentRef: document }, + ) + ), + mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState: (options) => ( + shellViewModel.mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState( + options ?? { documentRef: document }, + ) + ), loadWorkflowOverviewReleaseReadinessArtifactUrl: (options) => ( shellViewModel.loadWorkflowOverviewReleaseReadinessArtifactUrl(options) ), getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel: (workflow) => ( shellViewModel.createWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel(workflow) ), + getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan: (workflow) => ( + shellViewModel.createWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan(workflow) + ), + getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState: (summaryViewModel) => ( + shellViewModel.createWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState(summaryViewModel) + ), + getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract: (options) => ( + shellViewModel.createWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract(options) + ), + getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness: (options) => ( + shellViewModel.createWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness( + options ?? { documentRef: document }, + ) + ), + renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState: (renderState, options) => ( + shellViewModel.renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState( + renderState, + options ?? { documentRef: document }, + ) + ), + mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState: (options) => ( + shellViewModel.mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState( + options ?? { documentRef: document }, + ) + ), getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel: (validation) => ( shellViewModel.createWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel(validation) ), + getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan: (validation) => ( + shellViewModel.createWorkflowOverviewReleaseReadinessArtifactValidationActionPlan(validation) + ), + getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState: (actionPlan) => ( + shellViewModel.createWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState(actionPlan) + ), + getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract: (options) => ( + shellViewModel.createWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract(options) + ), + getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness: (options) => ( + shellViewModel.createWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness( + options ?? { documentRef: document }, + ) + ), + renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState: (renderState, options) => ( + shellViewModel.renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState( + renderState, + options ?? { documentRef: document }, + ) + ), + mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState: (options) => ( + shellViewModel.mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState( + options ?? { documentRef: document }, + ) + ), + getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel: (validation) => ( + shellViewModel.createWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel(validation) + ), + getWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel: (workflow) => ( + shellViewModel.createWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel(workflow) + ), + getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState: (summaryViewModel) => ( + shellViewModel.createWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState(summaryViewModel) + ), + getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract: (options) => ( + shellViewModel.createWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract(options) + ), + getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness: (options) => ( + shellViewModel.createWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness( + options ?? { documentRef: document }, + ) + ), + renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState: (renderState, options) => ( + shellViewModel.renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState( + renderState, + options ?? { documentRef: document }, + ) + ), + mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState: (options) => ( + shellViewModel.mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState( + options ?? { documentRef: document }, + ) + ), getWorkflowOverviewEmbeddingReport: (embeddingResult, contract) => ( shellViewModel.createWorkflowOverviewEmbeddingReport(embeddingResult, contract) ), diff --git a/wasm-port/runtime/ui/ini-panel/ui-shell.js b/wasm-port/runtime/ui/ini-panel/ui-shell.js index a4d2f7b..dbf6182 100644 --- a/wasm-port/runtime/ui/ini-panel/ui-shell.js +++ b/wasm-port/runtime/ui/ini-panel/ui-shell.js @@ -32,7 +32,11 @@ import { createMachineSessionPersistenceSummary, } from "./panel-state-summary.js"; import { + createProjectReleaseGateExecutionSummaryViewModel, + createProjectReleaseReadinessArtifactJsonWorkflow, + createProjectReleaseReadinessArtifactUrlWorkflowActionPlan, createProjectReleaseReadinessArtifactValidation, + createProjectReleaseReadinessArtifactValidationActionPlan, createProjectReleaseReadinessArtifactValidationSummaryViewModel, parseProjectReleaseReadinessArtifactJson, } from "../../sdk/src/project-release-readiness.js"; @@ -78,7 +82,11 @@ export { createMachineSessionWorkflowStatus, } from "./panel-state-summary.js"; export { + createProjectReleaseGateExecutionSummaryViewModel, + createProjectReleaseReadinessArtifactJsonWorkflow, + createProjectReleaseReadinessArtifactUrlWorkflowActionPlan, createProjectReleaseReadinessArtifactValidation, + createProjectReleaseReadinessArtifactValidationActionPlan, createProjectReleaseReadinessArtifactValidationSummaryViewModel, parseProjectReleaseReadinessArtifactJson, } from "../../sdk/src/project-release-readiness.js"; @@ -196,9 +204,36 @@ export function createIniPanelLaunchApiManifest(entries = getVisibleIniPanelEntr "getWorkflowOverviewContract", "getWorkflowOverviewReadiness", "mountWorkflowOverviewEmbedding", + "runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState", + "mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState", "loadWorkflowOverviewReleaseReadinessArtifactUrl", "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState", + "mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState", "getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState", + "mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState", + "mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState", "getWorkflowOverviewEmbeddingReport", "getWorkflowOverviewEmbeddingDisplayViewModel", "getWorkflowOverviewEmbeddingRenderState", @@ -1879,9 +1914,36 @@ export function createIniPanelShellWorkflowOverviewContract() { "getWorkflowOverviewApiSurfaceInventory", "getWorkflowOverviewPersistenceSummary", "validateWorkflowOverviewReleaseReadinessArtifactJson", + "runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState", + "mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState", "loadWorkflowOverviewReleaseReadinessArtifactUrl", "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState", + "mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState", "getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState", + "mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState", + "mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState", "getWorkflowOverviewReleaseReadinessArtifactDisplayViewModel", "getWorkflowOverviewReleaseReadinessArtifactRenderState", "getWorkflowOverviewReleaseReadinessArtifactDomContract", @@ -1901,6 +1963,7 @@ export function validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJso try { const artifact = parseProjectReleaseReadinessArtifactJson(artifactJson); const validation = createProjectReleaseReadinessArtifactValidation(artifact); + const actionPlan = createProjectReleaseReadinessArtifactValidationActionPlan(validation); return { apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-validation", validationVersion: 1, @@ -1912,9 +1975,15 @@ export function validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJso artifactVersion: validation.artifactVersion, gateCount: validation.gateCount, validation, + gateExecutionSummaryViewModel: artifact.gateExecutionSummaryViewModel + ?? createProjectReleaseGateExecutionSummaryViewModel(artifact.gateExecutionManifest), + actionPlan, rows: [...validation.rows], }; } catch (error) { + const actionPlan = createProjectReleaseReadinessArtifactValidationActionPlan( + createProjectReleaseReadinessArtifactValidation({}), + ); return { apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-validation", validationVersion: 1, @@ -1926,6 +1995,8 @@ export function validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJso artifactVersion: null, gateCount: 0, validation: null, + gateExecutionSummaryViewModel: null, + actionPlan, rows: [ { id: "artifact-json", @@ -1937,6 +2008,340 @@ export function validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJso } } +export function runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow({ + artifactJson = "", +} = {}) { + const jsonWorkflow = createProjectReleaseReadinessArtifactJsonWorkflow({ artifactJson }); + const validation = validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson(artifactJson); + const validationSummaryViewModel = + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel(validation); + const gateExecutionSummaryViewModel = + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel(validation); + const actionPlan = + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan(validation); + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-json-workflow", + workflowVersion: 1, + phase: jsonWorkflow.ready ? "ready" : "blocked", + ready: jsonWorkflow.ready, + parsed: jsonWorkflow.parsed, + parseError: jsonWorkflow.parseError, + artifactApiName: jsonWorkflow.artifactApiName, + missing: [...jsonWorkflow.missing], + jsonWorkflow, + validation, + validationSummaryViewModel, + gateExecutionSummaryViewModel, + actionPlan, + rows: [ + { + id: "parse", + label: "Artifact JSON parse", + value: jsonWorkflow.parsed ? "ready" : "blocked", + }, + { + id: "validation-summary", + label: "Validation summary", + value: validationSummaryViewModel.statusLine, + }, + { + id: "gate-execution-summary", + label: "Gate execution summary", + value: gateExecutionSummaryViewModel.statusLine, + }, + { + id: "next-command", + label: "Next command", + value: actionPlan.nextCommand ?? "none", + }, + ], + statusLine: jsonWorkflow.ready + ? "Ready: Artifact JSON workflow complete" + : `Blocked: ${jsonWorkflow.missing.join(", ")}`, + }; +} + +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel( + workflow = {}, +) { + const missing = Array.isArray(workflow?.missing) ? workflow.missing : []; + const ready = workflow?.ready === true; + const statusText = ready ? "Ready" : "Blocked"; + const detailText = ready + ? "Artifact JSON workflow complete" + : (missing.length > 0 ? missing.join(", ") : workflow?.parseError ?? "artifact JSON workflow blocked"); + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-json-workflow-summary-view-model", + viewModelVersion: 1, + phase: ready ? "ready" : "blocked", + ready, + title: "Release artifact JSON workflow", + statusText, + detailText, + statusLine: `${statusText}: ${detailText}`, + rows: [ + { + id: "parse", + label: "Artifact JSON parse", + value: workflow?.parsed === true ? "ready" : "blocked", + }, + { + id: "validation-summary", + label: "Validation summary", + value: workflow?.validationSummaryViewModel?.statusLine ?? "missing", + }, + { + id: "gate-execution-summary", + label: "Gate execution summary", + value: workflow?.gateExecutionSummaryViewModel?.statusLine ?? "missing", + }, + { + id: "action-plan", + label: "Action plan", + value: workflow?.actionPlan?.ready === true + ? "ready" + : (workflow?.actionPlan?.nextActionId ?? "missing"), + }, + { + id: "missing", + label: "Missing workflow evidence", + value: missing.length > 0 ? missing.join(", ") : "none", + }, + ], + }; +} + +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan( + workflow = {}, +) { + return workflow?.actionPlan + ?? createProjectReleaseReadinessArtifactValidationActionPlan(workflow?.validation?.validation); +} + +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState( + summaryViewModel = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel(), +) { + const rows = Array.isArray(summaryViewModel?.rows) + ? summaryViewModel.rows.map(({ id, label, value }) => ({ + id, + label, + value: value == null ? "" : String(value), + })) + : []; + const ready = summaryViewModel?.phase === "ready" && summaryViewModel?.statusText === "Ready"; + const statusText = summaryViewModel?.statusText ?? (ready ? "Ready" : "Blocked"); + const detailText = summaryViewModel?.detailText ?? "artifact JSON workflow blocked"; + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-json-workflow-render-state", + renderStateVersion: 1, + phase: ready ? "ready" : "blocked", + ready, + title: summaryViewModel?.title ?? "Release artifact JSON workflow", + statusText, + detailText, + statusLine: `${statusText}: ${detailText}`, + rows, + dataset: { + handoffPhase: ready ? "ready" : "blocked", + handoffReady: ready ? "true" : "false", + handoffScope: "workflow-overview-release-readiness-artifact-json-workflow", + }, + }; +} + +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract({ + rowDatasetPrefix = "workflowOverviewReleaseReadinessArtifactJsonWorkflow", + selectors = {}, +} = {}) { + const mountSelector = selectors.mount ?? "[data-workflow-overview-release-readiness-artifact-json-workflow]"; + const statusSelector = selectors.status + ?? "[data-workflow-overview-release-readiness-artifact-json-workflow-status]"; + const rowsSelector = selectors.rows + ?? "[data-workflow-overview-release-readiness-artifact-json-workflow-rows]"; + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-json-workflow-dom-contract", + contractVersion: 1, + requiredNodeIds: ["mount", "status", "rows"], + selectors: { + mount: mountSelector, + status: statusSelector, + rows: rowsSelector, + }, + mountDatasetKeys: ["handoffPhase", "handoffReady", "handoffScope"], + rowDatasetPrefix, + rowDatasetKeys: { + term: [`${rowDatasetPrefix}Row`, `${rowDatasetPrefix}Status`], + detail: [`${rowDatasetPrefix}Value`, `${rowDatasetPrefix}Status`], + }, + renderResultFields: ["rendered", "statusLine", "rowCount", "rowIds", "dataset"], + }; +} + +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness({ + rowDatasetPrefix = "workflowOverviewReleaseReadinessArtifactJsonWorkflow", + contract = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract({ + rowDatasetPrefix, + }), + documentRef = globalThis.document, + mountNode = documentRef?.querySelector?.(contract.selectors.mount), + statusNode = documentRef?.querySelector?.(contract.selectors.status), + rowsNode = documentRef?.querySelector?.(contract.selectors.rows), +} = {}) { + const checks = { + document: Boolean(documentRef?.createElement), + mount: Boolean(mountNode), + status: Boolean(statusNode), + rows: Boolean(rowsNode), + rowDatasetPrefix: Boolean(contract.rowDatasetPrefix), + }; + const missing = Object.entries(checks) + .filter(([, passed]) => !passed) + .map(([name]) => name); + const ready = missing.length === 0; + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-json-workflow-dom-readiness", + readinessVersion: 1, + phase: ready ? "ready" : "blocked", + ready, + missing, + checks, + contract, + }; +} + +export function renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState( + renderState = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState(), + { + rowDatasetPrefix = "workflowOverviewReleaseReadinessArtifactJsonWorkflow", + contract = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract({ + rowDatasetPrefix, + }), + documentRef = globalThis.document, + mountNode = documentRef?.querySelector?.(contract.selectors.mount), + statusNode = documentRef?.querySelector?.(contract.selectors.status), + rowsNode = documentRef?.querySelector?.(contract.selectors.rows), + } = {}, +) { + const readiness = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness({ + contract, + documentRef, + mountNode, + statusNode, + rowsNode, + }); + if (!readiness.ready) { + throw new Error( + `Workflow overview release readiness artifact JSON workflow DOM is not ready: ${readiness.missing.join(", ")}`, + ); + } + + mountNode.dataset.handoffPhase = renderState?.dataset?.handoffPhase ?? "blocked"; + mountNode.dataset.handoffReady = renderState?.dataset?.handoffReady ?? "false"; + mountNode.dataset.handoffScope = renderState?.dataset?.handoffScope + ?? "workflow-overview-release-readiness-artifact-json-workflow"; + statusNode.textContent = renderState?.statusLine + ?? "Blocked: No release readiness artifact JSON workflow render state"; + rowsNode.replaceChildren(); + + const rows = Array.isArray(renderState?.rows) ? renderState.rows : []; + const effectiveRowDatasetPrefix = contract.rowDatasetPrefix; + for (const row of rows) { + const term = documentRef.createElement("dt"); + term.dataset[`${effectiveRowDatasetPrefix}Row`] = row.id; + term.dataset[`${effectiveRowDatasetPrefix}Status`] = renderState?.ready ? "pass" : "blocked"; + term.textContent = row.label; + const detail = documentRef.createElement("dd"); + detail.dataset[`${effectiveRowDatasetPrefix}Value`] = row.id; + detail.dataset[`${effectiveRowDatasetPrefix}Status`] = renderState?.ready ? "pass" : "blocked"; + detail.textContent = row.value; + rowsNode.append(term, detail); + } + + return { + rendered: true, + statusLine: statusNode.textContent, + rowCount: rows.length, + rowIds: rows.map(({ id }) => id), + dataset: { + handoffPhase: mountNode.dataset.handoffPhase, + handoffReady: mountNode.dataset.handoffReady, + handoffScope: mountNode.dataset.handoffScope, + }, + }; +} + +export function mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState({ + renderState = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState(), + rowDatasetPrefix = "workflowOverviewReleaseReadinessArtifactJsonWorkflow", + contract = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract({ + rowDatasetPrefix, + }), + documentRef = globalThis.document, + mountNode = documentRef?.querySelector?.(contract.selectors.mount), + statusNode = documentRef?.querySelector?.(contract.selectors.status), + rowsNode = documentRef?.querySelector?.(contract.selectors.rows), +} = {}) { + const domReadiness = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness({ + contract, + documentRef, + mountNode, + statusNode, + rowsNode, + }); + if (!domReadiness.ready) { + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-json-workflow-dom-mount", + workflowVersion: 1, + phase: "blocked", + ready: false, + statusLine: `Blocked: ${domReadiness.missing.join(", ")}`, + contract, + domReadiness, + renderState, + renderResult: null, + error: null, + }; + } + + try { + const renderResult = renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState( + renderState, + { + contract, + documentRef, + mountNode, + statusNode, + rowsNode, + }, + ); + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-json-workflow-dom-mount", + workflowVersion: 1, + phase: renderState?.phase === "ready" ? "ready" : "blocked", + ready: renderState?.ready === true, + statusLine: renderResult.statusLine, + contract, + domReadiness, + renderState, + renderResult, + error: null, + }; + } catch (error) { + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-json-workflow-dom-mount", + workflowVersion: 1, + phase: "blocked", + ready: false, + statusLine: `Blocked: ${error?.message ?? "release readiness artifact JSON workflow DOM mount error"}`, + contract, + domReadiness, + renderState, + renderResult: null, + error: error?.message ?? String(error), + }; + } +} + export async function loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl({ artifactUrl = "", fetchRef = globalThis.fetch, @@ -1948,6 +2353,10 @@ export async function loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactU ...(typeof fetchRef === "function" ? [] : ["fetch"]), ]; if (missing.length > 0) { + const actionPlan = createProjectReleaseReadinessArtifactUrlWorkflowActionPlan({ + ready: false, + missing, + }); return { apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-url-workflow", workflowVersion: 1, @@ -1959,6 +2368,8 @@ export async function loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactU error: null, validation: null, validationSummaryViewModel: null, + gateExecutionSummaryViewModel: null, + actionPlan, displayViewModel: null, renderState: null, mountResult: null, @@ -1970,6 +2381,10 @@ export async function loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactU const response = await fetchRef(artifactUrl); const httpStatus = response?.status ?? null; if (!response?.ok) { + const actionPlan = createProjectReleaseReadinessArtifactUrlWorkflowActionPlan({ + ready: false, + missing: ["artifact-fetch"], + }); return { apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-url-workflow", workflowVersion: 1, @@ -1981,6 +2396,8 @@ export async function loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactU error: `HTTP ${httpStatus ?? "error"}`, validation: null, validationSummaryViewModel: null, + gateExecutionSummaryViewModel: null, + actionPlan, displayViewModel: null, renderState: null, mountResult: null, @@ -1990,6 +2407,10 @@ export async function loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactU const artifactJson = await response.text(); const validation = validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson(artifactJson); + const gateExecutionSummaryViewModel = + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel( + validation, + ); const validationSummaryViewModel = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel( validation, @@ -2014,6 +2435,14 @@ export async function loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactU ...mountMissing, ]; const ready = validation.ready === true && (!mount || mountResult?.ready === true); + const validationActionPlan = createProjectReleaseReadinessArtifactValidationActionPlan( + validation.validation, + ); + const actionPlan = createProjectReleaseReadinessArtifactUrlWorkflowActionPlan({ + ready, + missing: workflowMissing, + actionPlan: validationActionPlan, + }); return { apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-url-workflow", workflowVersion: 1, @@ -2025,12 +2454,18 @@ export async function loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactU error: null, validation, validationSummaryViewModel, + gateExecutionSummaryViewModel, + actionPlan, displayViewModel, renderState, mountResult, statusLine: ready ? renderState.statusLine : `Blocked: ${workflowMissing.join(", ")}`, }; } catch (error) { + const actionPlan = createProjectReleaseReadinessArtifactUrlWorkflowActionPlan({ + ready: false, + missing: ["artifact-fetch"], + }); return { apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-url-workflow", workflowVersion: 1, @@ -2042,6 +2477,8 @@ export async function loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactU error: error?.message ?? String(error), validation: null, validationSummaryViewModel: null, + gateExecutionSummaryViewModel: null, + actionPlan, displayViewModel: null, renderState: null, mountResult: null, @@ -2084,6 +2521,18 @@ export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWo label: "Validation summary", value: workflow?.validationSummaryViewModel?.statusLine ?? "missing", }, + { + id: "gate-execution-summary", + label: "Gate execution summary", + value: workflow?.gateExecutionSummaryViewModel?.statusLine ?? "missing", + }, + { + id: "action-plan", + label: "Action plan", + value: workflow?.actionPlan?.ready === true + ? "ready" + : (workflow?.actionPlan?.nextActionId ?? "missing"), + }, { id: "render-state", label: "Render state", @@ -2105,6 +2554,984 @@ export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWo }; } +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan( + workflow = {}, +) { + return workflow?.actionPlan + ?? createProjectReleaseReadinessArtifactUrlWorkflowActionPlan(workflow); +} + +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState( + actionPlan = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan(), +) { + const actionRows = Array.isArray(actionPlan?.rows) + ? actionPlan.rows.map(({ id, label, value }) => ({ + id: `action-${id}`, + label, + value: value == null ? "" : String(value), + })) + : []; + const ready = actionPlan?.ready === true; + const statusText = ready ? "Ready" : "Blocked"; + const detailText = ready + ? "Artifact URL workflow action plan complete" + : (actionPlan?.nextCommand ?? actionPlan?.nextActionId ?? "artifact URL workflow action required"); + const rows = [ + { + id: "action-count", + label: "Action count", + value: String(actionPlan?.actionCount ?? actionRows.length), + }, + { + id: "next-action", + label: "Next action", + value: actionPlan?.nextActionId ?? "none", + }, + { + id: "next-command", + label: "Next command", + value: actionPlan?.nextCommand ?? "none", + }, + ...actionRows, + ]; + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-url-workflow-action-plan-render-state", + renderStateVersion: 1, + phase: ready ? "ready" : "blocked", + ready, + title: "Release artifact URL workflow action plan", + statusText, + detailText, + statusLine: `${statusText}: ${detailText}`, + rows, + actionRows, + nextActionId: actionPlan?.nextActionId ?? null, + nextCommand: actionPlan?.nextCommand ?? null, + shellScript: actionPlan?.shellScript ?? "", + dataset: { + handoffPhase: ready ? "ready" : "blocked", + handoffReady: ready ? "true" : "false", + handoffScope: "workflow-overview-release-readiness-artifact-url-workflow-action-plan", + }, + }; +} + +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomContract({ + rowDatasetPrefix = "workflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan", + selectors = {}, +} = {}) { + const mountSelector = selectors.mount + ?? "[data-workflow-overview-release-readiness-artifact-url-workflow-action-plan]"; + const statusSelector = selectors.status + ?? "[data-workflow-overview-release-readiness-artifact-url-workflow-action-plan-status]"; + const rowsSelector = selectors.rows + ?? "[data-workflow-overview-release-readiness-artifact-url-workflow-action-plan-rows]"; + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-url-workflow-action-plan-dom-contract", + contractVersion: 1, + requiredNodeIds: ["mount", "status", "rows"], + selectors: { + mount: mountSelector, + status: statusSelector, + rows: rowsSelector, + }, + mountDatasetKeys: ["handoffPhase", "handoffReady", "handoffScope"], + rowDatasetPrefix, + rowDatasetKeys: { + term: [`${rowDatasetPrefix}Row`, `${rowDatasetPrefix}Status`], + detail: [`${rowDatasetPrefix}Value`, `${rowDatasetPrefix}Status`], + }, + renderResultFields: ["rendered", "statusLine", "rowCount", "rowIds", "dataset"], + }; +} + +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomReadiness({ + rowDatasetPrefix = "workflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan", + contract = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomContract({ + rowDatasetPrefix, + }), + documentRef = globalThis.document, + mountNode = documentRef?.querySelector?.(contract.selectors.mount), + statusNode = documentRef?.querySelector?.(contract.selectors.status), + rowsNode = documentRef?.querySelector?.(contract.selectors.rows), +} = {}) { + const checks = { + document: Boolean(documentRef?.createElement), + mount: Boolean(mountNode), + status: Boolean(statusNode), + rows: Boolean(rowsNode), + rowDatasetPrefix: Boolean(contract.rowDatasetPrefix), + }; + const missing = Object.entries(checks) + .filter(([, passed]) => !passed) + .map(([name]) => name); + const ready = missing.length === 0; + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-url-workflow-action-plan-dom-readiness", + readinessVersion: 1, + phase: ready ? "ready" : "blocked", + ready, + missing, + checks, + contract, + }; +} + +export function renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState( + renderState = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState(), + { + rowDatasetPrefix = "workflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan", + contract = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomContract({ + rowDatasetPrefix, + }), + documentRef = globalThis.document, + mountNode = documentRef?.querySelector?.(contract.selectors.mount), + statusNode = documentRef?.querySelector?.(contract.selectors.status), + rowsNode = documentRef?.querySelector?.(contract.selectors.rows), + } = {}, +) { + const readiness = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomReadiness({ + contract, + documentRef, + mountNode, + statusNode, + rowsNode, + }); + if (!readiness.ready) { + throw new Error( + `Workflow overview release readiness artifact URL workflow action plan DOM is not ready: ${readiness.missing.join(", ")}`, + ); + } + + mountNode.dataset.handoffPhase = renderState?.dataset?.handoffPhase ?? "blocked"; + mountNode.dataset.handoffReady = renderState?.dataset?.handoffReady ?? "false"; + mountNode.dataset.handoffScope = renderState?.dataset?.handoffScope + ?? "workflow-overview-release-readiness-artifact-url-workflow-action-plan"; + statusNode.textContent = renderState?.statusLine + ?? "Blocked: No release artifact URL workflow action-plan render state"; + rowsNode.replaceChildren(); + + const rows = Array.isArray(renderState?.rows) ? renderState.rows : []; + const effectiveRowDatasetPrefix = contract.rowDatasetPrefix; + for (const row of rows) { + const term = documentRef.createElement("dt"); + term.dataset[`${effectiveRowDatasetPrefix}Row`] = row.id; + term.dataset[`${effectiveRowDatasetPrefix}Status`] = renderState?.ready ? "pass" : "blocked"; + term.textContent = row.label; + const detail = documentRef.createElement("dd"); + detail.dataset[`${effectiveRowDatasetPrefix}Value`] = row.id; + detail.dataset[`${effectiveRowDatasetPrefix}Status`] = renderState?.ready ? "pass" : "blocked"; + detail.textContent = row.value; + rowsNode.append(term, detail); + } + + return { + rendered: true, + statusLine: statusNode.textContent, + rowCount: rows.length, + rowIds: rows.map(({ id }) => id), + dataset: { + handoffPhase: mountNode.dataset.handoffPhase, + handoffReady: mountNode.dataset.handoffReady, + handoffScope: mountNode.dataset.handoffScope, + }, + }; +} + +export function mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState({ + renderState = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState(), + rowDatasetPrefix = "workflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan", + contract = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomContract({ + rowDatasetPrefix, + }), + documentRef = globalThis.document, + mountNode = documentRef?.querySelector?.(contract.selectors.mount), + statusNode = documentRef?.querySelector?.(contract.selectors.status), + rowsNode = documentRef?.querySelector?.(contract.selectors.rows), +} = {}) { + const domReadiness = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomReadiness({ + contract, + documentRef, + mountNode, + statusNode, + rowsNode, + }); + if (!domReadiness.ready) { + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-url-workflow-action-plan-dom-mount", + workflowVersion: 1, + phase: "blocked", + ready: false, + statusLine: `Blocked: ${domReadiness.missing.join(", ")}`, + contract, + domReadiness, + renderState, + renderResult: null, + error: null, + }; + } + + try { + const renderResult = renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState( + renderState, + { + contract, + documentRef, + mountNode, + statusNode, + rowsNode, + }, + ); + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-url-workflow-action-plan-dom-mount", + workflowVersion: 1, + phase: renderState?.phase === "ready" ? "ready" : "blocked", + ready: renderState?.ready === true, + statusLine: renderResult.statusLine, + contract, + domReadiness, + renderState, + renderResult, + error: null, + }; + } catch (error) { + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-url-workflow-action-plan-dom-mount", + workflowVersion: 1, + phase: "blocked", + ready: false, + statusLine: `Blocked: ${error?.message ?? "release artifact URL workflow action-plan DOM mount error"}`, + contract, + domReadiness, + renderState, + renderResult: null, + error: error?.message ?? String(error), + }; + } +} + +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState( + summaryViewModel = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel(), +) { + const rows = Array.isArray(summaryViewModel?.rows) + ? summaryViewModel.rows.map(({ id, label, value }) => ({ + id, + label, + value: value == null ? "" : String(value), + })) + : []; + const ready = summaryViewModel?.phase === "ready" && summaryViewModel?.statusText === "Ready"; + const statusText = summaryViewModel?.statusText ?? (ready ? "Ready" : "Blocked"); + const detailText = summaryViewModel?.detailText ?? "artifact URL workflow blocked"; + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-url-workflow-render-state", + renderStateVersion: 1, + phase: ready ? "ready" : "blocked", + ready, + title: summaryViewModel?.title ?? "Release artifact URL workflow", + statusText, + detailText, + statusLine: `${statusText}: ${detailText}`, + rows, + dataset: { + handoffPhase: ready ? "ready" : "blocked", + handoffReady: ready ? "true" : "false", + handoffScope: "workflow-overview-release-readiness-artifact-url-workflow", + }, + }; +} + +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract({ + rowDatasetPrefix = "workflowOverviewReleaseReadinessArtifactUrlWorkflow", + selectors = {}, +} = {}) { + const mountSelector = selectors.mount ?? "[data-workflow-overview-release-readiness-artifact-url-workflow]"; + const statusSelector = selectors.status + ?? "[data-workflow-overview-release-readiness-artifact-url-workflow-status]"; + const rowsSelector = selectors.rows + ?? "[data-workflow-overview-release-readiness-artifact-url-workflow-rows]"; + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-url-workflow-dom-contract", + contractVersion: 1, + requiredNodeIds: ["mount", "status", "rows"], + selectors: { + mount: mountSelector, + status: statusSelector, + rows: rowsSelector, + }, + mountDatasetKeys: ["handoffPhase", "handoffReady", "handoffScope"], + rowDatasetPrefix, + rowDatasetKeys: { + term: [`${rowDatasetPrefix}Row`, `${rowDatasetPrefix}Status`], + detail: [`${rowDatasetPrefix}Value`, `${rowDatasetPrefix}Status`], + }, + renderResultFields: ["rendered", "statusLine", "rowCount", "rowIds", "dataset"], + }; +} + +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness({ + rowDatasetPrefix = "workflowOverviewReleaseReadinessArtifactUrlWorkflow", + contract = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract({ + rowDatasetPrefix, + }), + documentRef = globalThis.document, + mountNode = documentRef?.querySelector?.(contract.selectors.mount), + statusNode = documentRef?.querySelector?.(contract.selectors.status), + rowsNode = documentRef?.querySelector?.(contract.selectors.rows), +} = {}) { + const checks = { + document: Boolean(documentRef?.createElement), + mount: Boolean(mountNode), + status: Boolean(statusNode), + rows: Boolean(rowsNode), + rowDatasetPrefix: Boolean(contract.rowDatasetPrefix), + }; + const missing = Object.entries(checks) + .filter(([, passed]) => !passed) + .map(([name]) => name); + const ready = missing.length === 0; + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-url-workflow-dom-readiness", + readinessVersion: 1, + phase: ready ? "ready" : "blocked", + ready, + missing, + checks, + contract, + }; +} + +export function renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState( + renderState = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState(), + { + rowDatasetPrefix = "workflowOverviewReleaseReadinessArtifactUrlWorkflow", + contract = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract({ + rowDatasetPrefix, + }), + documentRef = globalThis.document, + mountNode = documentRef?.querySelector?.(contract.selectors.mount), + statusNode = documentRef?.querySelector?.(contract.selectors.status), + rowsNode = documentRef?.querySelector?.(contract.selectors.rows), + } = {}, +) { + const readiness = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness({ + contract, + documentRef, + mountNode, + statusNode, + rowsNode, + }); + if (!readiness.ready) { + throw new Error( + `Workflow overview release readiness artifact URL workflow DOM is not ready: ${readiness.missing.join(", ")}`, + ); + } + + mountNode.dataset.handoffPhase = renderState?.dataset?.handoffPhase ?? "blocked"; + mountNode.dataset.handoffReady = renderState?.dataset?.handoffReady ?? "false"; + mountNode.dataset.handoffScope = renderState?.dataset?.handoffScope + ?? "workflow-overview-release-readiness-artifact-url-workflow"; + statusNode.textContent = renderState?.statusLine + ?? "Blocked: No release readiness artifact URL workflow render state"; + rowsNode.replaceChildren(); + + const rows = Array.isArray(renderState?.rows) ? renderState.rows : []; + const effectiveRowDatasetPrefix = contract.rowDatasetPrefix; + for (const row of rows) { + const term = documentRef.createElement("dt"); + term.dataset[`${effectiveRowDatasetPrefix}Row`] = row.id; + term.dataset[`${effectiveRowDatasetPrefix}Status`] = renderState?.ready ? "pass" : "blocked"; + term.textContent = row.label; + const detail = documentRef.createElement("dd"); + detail.dataset[`${effectiveRowDatasetPrefix}Value`] = row.id; + detail.dataset[`${effectiveRowDatasetPrefix}Status`] = renderState?.ready ? "pass" : "blocked"; + detail.textContent = row.value; + rowsNode.append(term, detail); + } + + return { + rendered: true, + statusLine: statusNode.textContent, + rowCount: rows.length, + rowIds: rows.map(({ id }) => id), + dataset: { + handoffPhase: mountNode.dataset.handoffPhase, + handoffReady: mountNode.dataset.handoffReady, + handoffScope: mountNode.dataset.handoffScope, + }, + }; +} + +export function mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState({ + renderState = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState(), + rowDatasetPrefix = "workflowOverviewReleaseReadinessArtifactUrlWorkflow", + contract = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract({ + rowDatasetPrefix, + }), + documentRef = globalThis.document, + mountNode = documentRef?.querySelector?.(contract.selectors.mount), + statusNode = documentRef?.querySelector?.(contract.selectors.status), + rowsNode = documentRef?.querySelector?.(contract.selectors.rows), +} = {}) { + const domReadiness = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness({ + contract, + documentRef, + mountNode, + statusNode, + rowsNode, + }); + if (!domReadiness.ready) { + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-url-workflow-dom-mount", + workflowVersion: 1, + phase: "blocked", + ready: false, + statusLine: `Blocked: ${domReadiness.missing.join(", ")}`, + contract, + domReadiness, + renderState, + renderResult: null, + error: null, + }; + } + + try { + const renderResult = renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState( + renderState, + { + contract, + documentRef, + mountNode, + statusNode, + rowsNode, + }, + ); + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-url-workflow-dom-mount", + workflowVersion: 1, + phase: renderState?.phase === "ready" ? "ready" : "blocked", + ready: renderState?.ready === true, + statusLine: renderResult.statusLine, + contract, + domReadiness, + renderState, + renderResult, + error: null, + }; + } catch (error) { + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-url-workflow-dom-mount", + workflowVersion: 1, + phase: "blocked", + ready: false, + statusLine: `Blocked: ${error?.message ?? "release readiness artifact URL workflow DOM mount error"}`, + contract, + domReadiness, + renderState, + renderResult: null, + error: error?.message ?? String(error), + }; + } +} + +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan( + validation = validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson("{}"), +) { + return validation?.actionPlan + ?? createProjectReleaseReadinessArtifactValidationActionPlan(validation?.validation); +} + +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState( + actionPlan = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan(), +) { + const rows = Array.isArray(actionPlan?.rows) + ? actionPlan.rows.map(({ id, label, value }) => ({ + id, + label, + value: value == null ? "" : String(value), + })) + : []; + const commandRows = Array.isArray(actionPlan?.commands) + ? actionPlan.commands.map((command) => ({ + id: `command-${command.id}`, + label: command.label ?? command.id, + value: command.command ?? "", + })) + : []; + const ready = actionPlan?.ready === true; + const statusText = ready ? "Ready" : "Blocked"; + const detailText = ready + ? "Artifact validation action plan complete" + : (actionPlan?.nextCommand ?? "artifact validation action required"); + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-validation-action-plan-render-state", + renderStateVersion: 1, + phase: ready ? "ready" : "blocked", + ready, + title: "Release artifact validation action plan", + statusText, + detailText, + statusLine: `${statusText}: ${detailText}`, + rows: [...rows, ...commandRows], + commandRows, + nextActionId: actionPlan?.nextActionId ?? null, + nextCommand: actionPlan?.nextCommand ?? null, + shellScript: actionPlan?.shellScript ?? "", + dataset: { + handoffPhase: ready ? "ready" : "blocked", + handoffReady: ready ? "true" : "false", + handoffScope: "workflow-overview-release-readiness-artifact-validation-action-plan", + }, + }; +} + +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract({ + rowDatasetPrefix = "workflowOverviewReleaseReadinessArtifactValidationActionPlan", + selectors = {}, +} = {}) { + const mountSelector = selectors.mount + ?? "[data-workflow-overview-release-readiness-artifact-validation-action-plan]"; + const statusSelector = selectors.status + ?? "[data-workflow-overview-release-readiness-artifact-validation-action-plan-status]"; + const rowsSelector = selectors.rows + ?? "[data-workflow-overview-release-readiness-artifact-validation-action-plan-rows]"; + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-validation-action-plan-dom-contract", + contractVersion: 1, + requiredNodeIds: ["mount", "status", "rows"], + selectors: { + mount: mountSelector, + status: statusSelector, + rows: rowsSelector, + }, + mountDatasetKeys: ["handoffPhase", "handoffReady", "handoffScope"], + rowDatasetPrefix, + rowDatasetKeys: { + term: [`${rowDatasetPrefix}Row`, `${rowDatasetPrefix}Status`], + detail: [`${rowDatasetPrefix}Value`, `${rowDatasetPrefix}Status`], + }, + renderResultFields: ["rendered", "statusLine", "rowCount", "rowIds", "dataset"], + }; +} + +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness({ + rowDatasetPrefix = "workflowOverviewReleaseReadinessArtifactValidationActionPlan", + contract = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract({ + rowDatasetPrefix, + }), + documentRef = globalThis.document, + mountNode = documentRef?.querySelector?.(contract.selectors.mount), + statusNode = documentRef?.querySelector?.(contract.selectors.status), + rowsNode = documentRef?.querySelector?.(contract.selectors.rows), +} = {}) { + const checks = { + document: Boolean(documentRef?.createElement), + mount: Boolean(mountNode), + status: Boolean(statusNode), + rows: Boolean(rowsNode), + rowDatasetPrefix: Boolean(contract.rowDatasetPrefix), + }; + const missing = Object.entries(checks) + .filter(([, passed]) => !passed) + .map(([name]) => name); + const ready = missing.length === 0; + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-validation-action-plan-dom-readiness", + readinessVersion: 1, + phase: ready ? "ready" : "blocked", + ready, + missing, + checks, + contract, + }; +} + +export function renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState( + renderState = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState(), + { + rowDatasetPrefix = "workflowOverviewReleaseReadinessArtifactValidationActionPlan", + contract = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract({ + rowDatasetPrefix, + }), + documentRef = globalThis.document, + mountNode = documentRef?.querySelector?.(contract.selectors.mount), + statusNode = documentRef?.querySelector?.(contract.selectors.status), + rowsNode = documentRef?.querySelector?.(contract.selectors.rows), + } = {}, +) { + const readiness = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness({ + contract, + documentRef, + mountNode, + statusNode, + rowsNode, + }); + if (!readiness.ready) { + throw new Error( + `Workflow overview release readiness artifact validation action plan DOM is not ready: ${readiness.missing.join(", ")}`, + ); + } + + mountNode.dataset.handoffPhase = renderState?.dataset?.handoffPhase ?? "blocked"; + mountNode.dataset.handoffReady = renderState?.dataset?.handoffReady ?? "false"; + mountNode.dataset.handoffScope = renderState?.dataset?.handoffScope + ?? "workflow-overview-release-readiness-artifact-validation-action-plan"; + statusNode.textContent = renderState?.statusLine + ?? "Blocked: No release artifact validation action-plan render state"; + rowsNode.replaceChildren(); + + const rows = Array.isArray(renderState?.rows) ? renderState.rows : []; + const effectiveRowDatasetPrefix = contract.rowDatasetPrefix; + for (const row of rows) { + const term = documentRef.createElement("dt"); + term.dataset[`${effectiveRowDatasetPrefix}Row`] = row.id; + term.dataset[`${effectiveRowDatasetPrefix}Status`] = renderState?.ready ? "pass" : "blocked"; + term.textContent = row.label; + const detail = documentRef.createElement("dd"); + detail.dataset[`${effectiveRowDatasetPrefix}Value`] = row.id; + detail.dataset[`${effectiveRowDatasetPrefix}Status`] = renderState?.ready ? "pass" : "blocked"; + detail.textContent = row.value; + rowsNode.append(term, detail); + } + + return { + rendered: true, + statusLine: statusNode.textContent, + rowCount: rows.length, + rowIds: rows.map(({ id }) => id), + dataset: { + handoffPhase: mountNode.dataset.handoffPhase, + handoffReady: mountNode.dataset.handoffReady, + handoffScope: mountNode.dataset.handoffScope, + }, + }; +} + +export function mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState({ + renderState = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState(), + rowDatasetPrefix = "workflowOverviewReleaseReadinessArtifactValidationActionPlan", + contract = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract({ + rowDatasetPrefix, + }), + documentRef = globalThis.document, + mountNode = documentRef?.querySelector?.(contract.selectors.mount), + statusNode = documentRef?.querySelector?.(contract.selectors.status), + rowsNode = documentRef?.querySelector?.(contract.selectors.rows), +} = {}) { + const domReadiness = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness({ + contract, + documentRef, + mountNode, + statusNode, + rowsNode, + }); + if (!domReadiness.ready) { + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-validation-action-plan-dom-mount", + workflowVersion: 1, + phase: "blocked", + ready: false, + statusLine: `Blocked: ${domReadiness.missing.join(", ")}`, + contract, + domReadiness, + renderState, + renderResult: null, + error: null, + }; + } + + try { + const renderResult = renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState( + renderState, + { + contract, + documentRef, + mountNode, + statusNode, + rowsNode, + }, + ); + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-validation-action-plan-dom-mount", + workflowVersion: 1, + phase: renderState?.phase === "ready" ? "ready" : "blocked", + ready: renderState?.ready === true, + statusLine: renderResult.statusLine, + contract, + domReadiness, + renderState, + renderResult, + error: null, + }; + } catch (error) { + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-validation-action-plan-dom-mount", + workflowVersion: 1, + phase: "blocked", + ready: false, + statusLine: `Blocked: ${error?.message ?? "release artifact validation action-plan DOM mount error"}`, + contract, + domReadiness, + renderState, + renderResult: null, + error: error?.message ?? String(error), + }; + } +} + +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel( + validation = validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson("{}"), +) { + if (validation?.gateExecutionSummaryViewModel) { + return validation.gateExecutionSummaryViewModel; + } + return createProjectReleaseGateExecutionSummaryViewModel( + validation?.validation?.artifact?.gateExecutionManifest, + ); +} + +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel( + workflow = {}, +) { + return workflow?.gateExecutionSummaryViewModel + ?? createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel( + workflow?.validation, + ); +} + +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState( + summaryViewModel = createProjectReleaseGateExecutionSummaryViewModel(), +) { + const summaryRows = Array.isArray(summaryViewModel?.rows) + ? summaryViewModel.rows.map(({ id, label, value }) => ({ + id, + label, + value: value == null ? "" : String(value), + })) + : []; + const gateRows = Array.isArray(summaryViewModel?.gateRows) + ? summaryViewModel.gateRows.map((row) => ({ + id: `gate-${row.id}`, + label: row.label ?? row.id, + value: `${row.status ?? (row.passed ? "passed" : "unknown")}: ${row.evidence ?? "missing"}`, + })) + : []; + const ready = summaryViewModel?.phase === "ready" && summaryViewModel?.ready === true; + const statusText = summaryViewModel?.statusText ?? (ready ? "Ready" : "Waiting"); + const detailText = summaryViewModel?.detailText ?? "release gate evidence missing"; + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-gate-execution-summary-render-state", + renderStateVersion: 1, + phase: ready ? "ready" : "waiting", + ready, + title: summaryViewModel?.title ?? "Project release gate execution", + statusText, + detailText, + statusLine: `${statusText}: ${detailText}`, + rows: [...summaryRows, ...gateRows], + gateRows, + dataset: { + handoffPhase: ready ? "ready" : "waiting", + handoffReady: ready ? "true" : "false", + handoffScope: "workflow-overview-release-readiness-artifact-gate-execution-summary", + }, + }; +} + +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract({ + rowDatasetPrefix = "workflowOverviewReleaseReadinessArtifactGateExecutionSummary", + selectors = {}, +} = {}) { + const mountSelector = selectors.mount + ?? "[data-workflow-overview-release-readiness-artifact-gate-execution-summary]"; + const statusSelector = selectors.status + ?? "[data-workflow-overview-release-readiness-artifact-gate-execution-summary-status]"; + const rowsSelector = selectors.rows + ?? "[data-workflow-overview-release-readiness-artifact-gate-execution-summary-rows]"; + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-gate-execution-summary-dom-contract", + contractVersion: 1, + requiredNodeIds: ["mount", "status", "rows"], + selectors: { + mount: mountSelector, + status: statusSelector, + rows: rowsSelector, + }, + mountDatasetKeys: ["handoffPhase", "handoffReady", "handoffScope"], + rowDatasetPrefix, + rowDatasetKeys: { + term: [`${rowDatasetPrefix}Row`, `${rowDatasetPrefix}Status`], + detail: [`${rowDatasetPrefix}Value`, `${rowDatasetPrefix}Status`], + }, + renderResultFields: ["rendered", "statusLine", "rowCount", "rowIds", "dataset"], + }; +} + +export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness({ + rowDatasetPrefix = "workflowOverviewReleaseReadinessArtifactGateExecutionSummary", + contract = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract({ + rowDatasetPrefix, + }), + documentRef = globalThis.document, + mountNode = documentRef?.querySelector?.(contract.selectors.mount), + statusNode = documentRef?.querySelector?.(contract.selectors.status), + rowsNode = documentRef?.querySelector?.(contract.selectors.rows), +} = {}) { + const checks = { + document: Boolean(documentRef?.createElement), + mount: Boolean(mountNode), + status: Boolean(statusNode), + rows: Boolean(rowsNode), + rowDatasetPrefix: Boolean(contract.rowDatasetPrefix), + }; + const missing = Object.entries(checks) + .filter(([, passed]) => !passed) + .map(([name]) => name); + const ready = missing.length === 0; + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-gate-execution-summary-dom-readiness", + readinessVersion: 1, + phase: ready ? "ready" : "blocked", + ready, + missing, + checks, + contract, + }; +} + +export function renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState( + renderState = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState(), + { + rowDatasetPrefix = "workflowOverviewReleaseReadinessArtifactGateExecutionSummary", + contract = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract({ + rowDatasetPrefix, + }), + documentRef = globalThis.document, + mountNode = documentRef?.querySelector?.(contract.selectors.mount), + statusNode = documentRef?.querySelector?.(contract.selectors.status), + rowsNode = documentRef?.querySelector?.(contract.selectors.rows), + } = {}, +) { + const readiness = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness({ + contract, + documentRef, + mountNode, + statusNode, + rowsNode, + }); + if (!readiness.ready) { + throw new Error( + `Workflow overview release readiness artifact gate execution summary DOM is not ready: ${readiness.missing.join(", ")}`, + ); + } + + mountNode.dataset.handoffPhase = renderState?.dataset?.handoffPhase ?? "waiting"; + mountNode.dataset.handoffReady = renderState?.dataset?.handoffReady ?? "false"; + mountNode.dataset.handoffScope = renderState?.dataset?.handoffScope + ?? "workflow-overview-release-readiness-artifact-gate-execution-summary"; + statusNode.textContent = renderState?.statusLine + ?? "Waiting: No release gate execution summary render state"; + rowsNode.replaceChildren(); + + const rows = Array.isArray(renderState?.rows) ? renderState.rows : []; + const effectiveRowDatasetPrefix = contract.rowDatasetPrefix; + for (const row of rows) { + const term = documentRef.createElement("dt"); + term.dataset[`${effectiveRowDatasetPrefix}Row`] = row.id; + term.dataset[`${effectiveRowDatasetPrefix}Status`] = renderState?.ready ? "pass" : "waiting"; + term.textContent = row.label; + const detail = documentRef.createElement("dd"); + detail.dataset[`${effectiveRowDatasetPrefix}Value`] = row.id; + detail.dataset[`${effectiveRowDatasetPrefix}Status`] = renderState?.ready ? "pass" : "waiting"; + detail.textContent = row.value; + rowsNode.append(term, detail); + } + + return { + rendered: true, + statusLine: statusNode.textContent, + rowCount: rows.length, + rowIds: rows.map(({ id }) => id), + dataset: { + handoffPhase: mountNode.dataset.handoffPhase, + handoffReady: mountNode.dataset.handoffReady, + handoffScope: mountNode.dataset.handoffScope, + }, + }; +} + +export function mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState({ + renderState = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState(), + rowDatasetPrefix = "workflowOverviewReleaseReadinessArtifactGateExecutionSummary", + contract = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract({ + rowDatasetPrefix, + }), + documentRef = globalThis.document, + mountNode = documentRef?.querySelector?.(contract.selectors.mount), + statusNode = documentRef?.querySelector?.(contract.selectors.status), + rowsNode = documentRef?.querySelector?.(contract.selectors.rows), +} = {}) { + const domReadiness = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness({ + contract, + documentRef, + mountNode, + statusNode, + rowsNode, + }); + if (!domReadiness.ready) { + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-gate-execution-summary-dom-mount", + workflowVersion: 1, + phase: "blocked", + ready: false, + statusLine: `Blocked: ${domReadiness.missing.join(", ")}`, + contract, + domReadiness, + renderState, + renderResult: null, + error: null, + }; + } + + try { + const renderResult = renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState( + renderState, + { + contract, + documentRef, + mountNode, + statusNode, + rowsNode, + }, + ); + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-gate-execution-summary-dom-mount", + workflowVersion: 1, + phase: renderState?.phase === "ready" ? "ready" : "waiting", + ready: renderState?.ready === true, + statusLine: renderResult.statusLine, + contract, + domReadiness, + renderState, + renderResult, + error: null, + }; + } catch (error) { + return { + apiName: "ini-panel-shell-workflow-overview-release-readiness-artifact-gate-execution-summary-dom-mount", + workflowVersion: 1, + phase: "blocked", + ready: false, + statusLine: `Blocked: ${error?.message ?? "release gate execution summary DOM mount error"}`, + contract, + domReadiness, + renderState, + renderResult: null, + error: error?.message ?? String(error), + }; + } +} + export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel( validation = validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson("{}"), ) { @@ -3347,10 +4774,64 @@ export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries mountShellSessionHandoffWorkflow: mountIniPanelShellSessionHandoffWorkflow, createWorkflowOverviewContract: createIniPanelShellWorkflowOverviewContract, createWorkflowOverviewReadiness: createIniPanelShellWorkflowOverviewReadiness, + runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow: + runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow, + createWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel, + createWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan, + createWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState, + createWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract, + createWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness, + renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState: + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState, + mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState: + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState, createWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel: createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel, + createWorkflowOverviewReleaseReadinessArtifactValidationActionPlan: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan, + createWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState, + createWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract, + createWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness, + renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState: + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState, + mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState: + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState, createWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel: createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel, + createWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan, + createWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState, + createWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract, + createWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness, + renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState: + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState, + mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState: + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState, + createWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel, + createWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel, + createWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState, + createWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract, + createWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness, + renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState: + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState, + mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState: + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState, loadWorkflowOverviewReleaseReadinessArtifactUrl: loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl, mountWorkflowOverviewEmbedding: mountIniPanelShellWorkflowOverviewEmbedding, diff --git a/wasm-port/runtime/ui/ini-panel/workflow-overview.html b/wasm-port/runtime/ui/ini-panel/workflow-overview.html index f25a96e..d3ecd58 100644 --- a/wasm-port/runtime/ui/ini-panel/workflow-overview.html +++ b/wasm-port/runtime/ui/ini-panel/workflow-overview.html @@ -106,9 +106,36 @@ renderIniPanelShellWorkflowOverviewEmbeddingMountState, mountIniPanelShellWorkflowOverviewEmbeddingMountState, validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson, + runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState, loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract, @@ -146,15 +173,124 @@ validateWorkflowOverviewReleaseReadinessArtifactJson: (artifactJson) => ( validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson(artifactJson) ), + runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow: (options) => ( + runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow(options) + ), + getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel: (workflow) => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel(workflow) + ), + getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan: (workflow) => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan(workflow) + ), + getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState: (summaryViewModel) => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState(summaryViewModel) + ), + getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract: (options) => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract(options) + ), + getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness: (options) => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness( + options ?? { documentRef: document }, + ) + ), + renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState: (renderState, options) => ( + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState( + renderState, + options ?? { documentRef: document }, + ) + ), + mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState: (options) => ( + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState( + options ?? { documentRef: document }, + ) + ), loadWorkflowOverviewReleaseReadinessArtifactUrl: (options) => ( loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl(options) ), getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel: (workflow) => ( createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel(workflow) ), + getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan: (workflow) => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan(workflow) + ), + getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState: (summaryViewModel) => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState(summaryViewModel) + ), + getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract: (options) => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract(options) + ), + getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness: (options) => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness( + options ?? { documentRef: document }, + ) + ), + renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState: (renderState, options) => ( + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState( + renderState, + options ?? { documentRef: document }, + ) + ), + mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState: (options) => ( + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState( + options ?? { documentRef: document }, + ) + ), getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel: (validation) => ( createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel(validation) ), + getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan: (validation) => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan(validation) + ), + getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState: (actionPlan) => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState(actionPlan) + ), + getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract: (options) => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract(options) + ), + getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness: (options) => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness( + options ?? { documentRef: document }, + ) + ), + renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState: (renderState, options) => ( + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState( + renderState, + options ?? { documentRef: document }, + ) + ), + mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState: (options) => ( + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState( + options ?? { documentRef: document }, + ) + ), + getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel: (validation) => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel(validation) + ), + getWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel: (workflow) => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel(workflow) + ), + getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState: (summaryViewModel) => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState(summaryViewModel) + ), + getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract: (options) => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract(options) + ), + getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness: (options) => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness( + options ?? { documentRef: document }, + ) + ), + renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState: (renderState, options) => ( + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState( + renderState, + options ?? { documentRef: document }, + ) + ), + mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState: (options) => ( + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState( + options ?? { documentRef: document }, + ) + ), getWorkflowOverviewReleaseReadinessArtifactDisplayViewModel: (validation) => ( createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel(validation) ), diff --git a/wasm-port/runtime/ui/simulation/index.html b/wasm-port/runtime/ui/simulation/index.html new file mode 100644 index 0000000..643101e --- /dev/null +++ b/wasm-port/runtime/ui/simulation/index.html @@ -0,0 +1,338 @@ + + + + + + LinuxCNC Browser Simulation + + + +
+
+

LinuxCNC Browser Simulation

+
+ Simulation loading + LinuxCNC interpreter WASM +
+
+
+
+
+

Program

+

Executed through the LinuxCNC-backed WASM interpreter.

+
+
+
+
+
+

Toolpath Preview

+

Rendered from LinuxCNC canonical motion events.

+
+
+
+ + + + + + +
+
+
+
X0.000
+
Y0.000
+
Z0.000
+
A0.000
+
B0.000
+
C0.000
+
+
+
+
+
+
+
+

Machine State

+

Active line, statement, motion rows, and canonical output.

+
+
+ + + + + + + + + + +
MotionLinePositionStatement
+
+
+
Active line
+
-
+
Statement
+
-
+
+
+

+        
+
+
+
+ + + diff --git a/wasm-port/runtime/ui/simulation/simulation-app.js b/wasm-port/runtime/ui/simulation/simulation-app.js new file mode 100644 index 0000000..8d725af --- /dev/null +++ b/wasm-port/runtime/ui/simulation/simulation-app.js @@ -0,0 +1,239 @@ +import { createLinuxCncInterpSdk } from "../../sdk/src/index.js"; + +export const DEFAULT_SIMULATION_PROGRAM = [ + "G0 X0 Y0 Z0", + "G1 X1 Y0 Z0 F100", + "G1 X1 Y1 Z0", + "G1 X0 Y1 Z0", + "G1 X0 Y0 Z0", + "M2", + "", +].join("\n"); + +const AXES = ["x", "y", "z", "a", "b", "c", "u", "v", "w"]; + +function readCanonicalNumber(line, name) { + const match = line.match(new RegExp(`(?:^| )${name}=([-+0-9.eE]+)`)); + return match ? Number(match[1]) : null; +} + +function programLineMap(programText) { + const lines = new Map(); + programText.split(/\r?\n/).forEach((line, index) => { + lines.set(index + 1, line.trim() || "(blank)"); + }); + return lines; +} + +export function parseLinuxCncCanonicalMotion(resultText, programText = "") { + const axes = Object.fromEntries(AXES.map((axis) => [axis, 0])); + const sourceLines = programLineMap(programText); + const motion = []; + + for (const line of String(resultText).split("\n")) { + const event = line.match(/^canon_event=(STRAIGHT_TRAVERSE|STRAIGHT_FEED|ARC_FEED)\b/); + if (!event) { + continue; + } + + for (const axis of AXES) { + const value = readCanonicalNumber(line, axis); + if (value !== null && Number.isFinite(value)) { + axes[axis] = value; + } + } + + const sourceLine = readCanonicalNumber(line, "line"); + motion.push({ + type: event[1], + line: Number.isFinite(sourceLine) ? sourceLine : null, + statement: Number.isFinite(sourceLine) ? (sourceLines.get(sourceLine) ?? "-") : "-", + axes: { ...axes }, + raw: line, + }); + } + + return motion; +} + +export function createSimulationSummary({ programText, resultText, motion }) { + const canonicalLines = String(resultText).split("\n").filter((line) => line.startsWith("canon_event=")); + const finalAxes = motion.at(-1)?.axes ?? Object.fromEntries(AXES.map((axis) => [axis, 0])); + return { + apiName: "real-browser-simulation-summary", + summaryVersion: 1, + ready: motion.length > 0, + phase: motion.length > 0 ? "ready" : "blocked", + programLineCount: programText.split(/\r?\n/).filter(Boolean).length, + canonicalEventCount: canonicalLines.length, + motionEventCount: motion.length, + finalAxes, + rows: [ + { id: "runtime", label: "Runtime", value: "LinuxCNC interpreter WASM" }, + { id: "program-lines", label: "Program lines", value: `${programText.split(/\r?\n/).filter(Boolean).length}` }, + { id: "canonical-events", label: "Canonical events", value: `${canonicalLines.length}` }, + { id: "motion-events", label: "Motion events", value: `${motion.length}` }, + { id: "final-position", label: "Final XYZ", value: formatPosition(finalAxes) }, + ], + }; +} + +export function createToolpathViewBox(motion, padding = 0.25) { + const points = motion.map(({ axes }) => ({ x: axes.x ?? 0, y: axes.y ?? 0 })); + if (points.length === 0) { + return { minX: -1, minY: -1, width: 2, height: 2 }; + } + const xs = points.map(({ x }) => x); + const ys = points.map(({ y }) => y); + const minX = Math.min(...xs) - padding; + const maxX = Math.max(...xs) + padding; + const minY = Math.min(...ys) - padding; + const maxY = Math.max(...ys) + padding; + return { + minX, + minY, + width: Math.max(0.1, maxX - minX), + height: Math.max(0.1, maxY - minY), + }; +} + +export function createToolpathPolylinePoints(motion) { + return motion.map(({ axes }) => `${axes.x ?? 0},${-(axes.y ?? 0)}`).join(" "); +} + +export function formatPosition(axes = {}) { + return `X ${formatAxis(axes.x)} Y ${formatAxis(axes.y)} Z ${formatAxis(axes.z)}`; +} + +function formatAxis(value) { + return Number.isFinite(value) ? Number(value).toFixed(3) : "0.000"; +} + +function setText(documentRef, selector, value) { + const node = documentRef.querySelector(selector); + if (node) { + node.textContent = value; + } +} + +function renderRows(documentRef, rows) { + const container = documentRef.querySelector("[data-simulation-summary-rows]"); + if (!container) { + return; + } + container.textContent = ""; + for (const row of rows) { + const term = documentRef.createElement("dt"); + term.textContent = row.label; + term.dataset.simulationRow = row.id; + const detail = documentRef.createElement("dd"); + detail.textContent = row.value; + detail.dataset.simulationValue = row.id; + container.append(term, detail); + } +} + +function renderProgramLines(documentRef, programText, activeLine) { + const container = documentRef.querySelector("[data-program-lines]"); + if (!container) { + return; + } + container.textContent = ""; + programText.split(/\r?\n/).forEach((line, index) => { + if (!line && index === programText.split(/\r?\n/).length - 1) { + return; + } + const row = documentRef.createElement("div"); + row.dataset.programLine = `${index + 1}`; + row.dataset.active = activeLine === index + 1 ? "true" : "false"; + const number = documentRef.createElement("span"); + number.textContent = `${index + 1}`; + const text = documentRef.createElement("code"); + text.textContent = line || " "; + row.append(number, text); + container.append(row); + }); +} + +function renderAxisReadout(documentRef, axes) { + for (const axis of ["x", "y", "z", "a", "b", "c"]) { + setText(documentRef, `[data-axis="${axis}"]`, formatAxis(axes?.[axis])); + } +} + +function renderToolpath(documentRef, motion) { + const svg = documentRef.querySelector("[data-toolpath-svg]"); + const polyline = documentRef.querySelector("[data-toolpath-polyline]"); + const head = documentRef.querySelector("[data-toolpath-head]"); + if (!svg || !polyline || !head) { + return; + } + + const viewBox = createToolpathViewBox(motion); + svg.setAttribute( + "viewBox", + `${viewBox.minX} ${-(viewBox.minY + viewBox.height)} ${viewBox.width} ${viewBox.height}`, + ); + polyline.setAttribute("points", createToolpathPolylinePoints(motion)); + const last = motion.at(-1)?.axes ?? { x: 0, y: 0 }; + head.setAttribute("cx", `${last.x ?? 0}`); + head.setAttribute("cy", `${-(last.y ?? 0)}`); +} + +function renderMotionTable(documentRef, motion) { + const body = documentRef.querySelector("[data-motion-rows]"); + if (!body) { + return; + } + body.textContent = ""; + for (const [index, item] of motion.entries()) { + const row = documentRef.createElement("tr"); + row.dataset.motionRow = `${index}`; + for (const value of [ + item.type, + item.line ?? "-", + formatPosition(item.axes), + item.statement, + ]) { + const cell = documentRef.createElement("td"); + cell.textContent = `${value}`; + row.append(cell); + } + body.append(row); + } +} + +export function renderSimulationState(documentRef, state) { + documentRef.body.dataset.simulationReady = state.summary.ready ? "true" : "false"; + setText(documentRef, "[data-simulation-status]", state.summary.ready ? "ready" : "blocked"); + setText(documentRef, "[data-runtime-status]", state.summary.rows[0].value); + setText(documentRef, "[data-active-line]", `${state.motion.at(-1)?.line ?? "-"}`); + setText(documentRef, "[data-active-statement]", state.motion.at(-1)?.statement ?? "-"); + setText(documentRef, "[data-canonical-output]", state.resultText); + renderRows(documentRef, state.summary.rows); + renderProgramLines(documentRef, state.programText, state.motion.at(-1)?.line ?? null); + renderAxisReadout(documentRef, state.summary.finalAxes); + renderToolpath(documentRef, state.motion); + renderMotionTable(documentRef, state.motion); +} + +export async function runRealBrowserSimulation({ + documentRef = document, + programText = DEFAULT_SIMULATION_PROGRAM, + interpFactory = createLinuxCncInterpSdk, +} = {}) { + const interp = await interpFactory(); + const resultText = interp.runProgram(programText); + const motion = parseLinuxCncCanonicalMotion(resultText, programText); + const summary = createSimulationSummary({ programText, resultText, motion }); + const state = { + apiName: "real-browser-simulation-state", + stateVersion: 1, + programText, + resultText, + motion, + summary, + }; + renderSimulationState(documentRef, state); + return state; +} diff --git a/wasm-port/tests/browser/ini_panel_shell_integration_workflow_smoke.html b/wasm-port/tests/browser/ini_panel_shell_integration_workflow_smoke.html index 7c40510..b7b4b4a 100644 --- a/wasm-port/tests/browser/ini_panel_shell_integration_workflow_smoke.html +++ b/wasm-port/tests/browser/ini_panel_shell_integration_workflow_smoke.html @@ -22,6 +22,22 @@

waiting

+
+

waiting

+
+
+
+

waiting

+
+
+
+

waiting

+
+
+
+

waiting

+
+
+ + diff --git a/wasm-port/tests/browser/release_artifact_url_workflow_smoke.html b/wasm-port/tests/browser/release_artifact_url_workflow_smoke.html index 9b8af12..bb9c600 100644 --- a/wasm-port/tests/browser/release_artifact_url_workflow_smoke.html +++ b/wasm-port/tests/browser/release_artifact_url_workflow_smoke.html @@ -68,7 +68,7 @@ ); assertEqual(readyWorkflow.ready, true, "ready workflow readiness"); assertEqual(readyWorkflow.httpStatus, 200, "ready workflow HTTP status"); - assertEqual(readyWorkflow.validation.gateCount, 11, "ready workflow gate count"); + assertEqual(readyWorkflow.validation.gateCount, 12, "ready workflow gate count"); assertEqual( readyWorkflow.validationSummaryViewModel.statusLine, "Ready: Artifact evidence complete", diff --git a/wasm-port/tests/browser/verify_real_simulation_browser.sh b/wasm-port/tests/browser/verify_real_simulation_browser.sh new file mode 100755 index 0000000..e40ec57 --- /dev/null +++ b/wasm-port/tests/browser/verify_real_simulation_browser.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)" +CHROMIUM="${CHROMIUM:-$(command -v chromium || command -v chromium-browser || command -v google-chrome || command -v google-chrome-stable || true)}" + +if [[ -z "$CHROMIUM" ]]; then + echo "missing Chromium-compatible browser; set CHROMIUM=/path/to/browser" >&2 + exit 1 +fi + +if [[ "${SKIP_INTERP_BUILD:-0}" != "1" ]]; then + "$ROOT_DIR/tools/build_wasm_core.sh" +fi + +TMP_DIR="$(mktemp -d)" +PORT_FILE="$TMP_DIR/port" +SERVER_LOG="$TMP_DIR/server.log" +CHROME_PROFILE="$TMP_DIR/chrome-profile" +mkdir -p "$CHROME_PROFILE" + +cleanup() { + if [[ -n "${SERVER_PID:-}" ]]; then + kill "$SERVER_PID" 2>/dev/null || true + wait "$SERVER_PID" 2>/dev/null || true + fi + rm -rf "$TMP_DIR" +} +trap cleanup EXIT + +python3 - <<'PY' "$ROOT_DIR" "$PORT_FILE" >"$SERVER_LOG" 2>&1 & +import functools +import http.server +import pathlib +import socketserver +import sys + +root = pathlib.Path(sys.argv[1]) +port_file = pathlib.Path(sys.argv[2]) + +handler = functools.partial(http.server.SimpleHTTPRequestHandler, directory=str(root)) +with socketserver.TCPServer(("127.0.0.1", 0), handler) as httpd: + port_file.write_text(str(httpd.server_address[1]), encoding="ascii") + httpd.serve_forever() +PY +SERVER_PID=$! + +for _ in $(seq 1 100); do + [[ -s "$PORT_FILE" ]] && break + sleep 0.05 +done + +if [[ ! -s "$PORT_FILE" ]]; then + echo "real simulation browser smoke HTTP server did not start" >&2 + cat "$SERVER_LOG" >&2 || true + exit 1 +fi + +PORT="$(cat "$PORT_FILE")" +URL="http://127.0.0.1:$PORT/tests/browser/real_simulation_page_smoke.html" +OUT="$TMP_DIR/chromium-real-simulation.out" + +"$CHROMIUM" \ + --headless=new \ + --disable-gpu \ + --no-sandbox \ + --user-data-dir="$CHROME_PROFILE" \ + --virtual-time-budget=10000 \ + --dump-dom \ + "$URL" >"$OUT" 2>&1 + +if ! grep -Fq "browser_real_simulation_page_smoke=ok" "$OUT"; then + echo "browser real simulation page smoke failed" >&2 + sed -n '1,260p' "$OUT" >&2 + exit 1 +fi + +echo "browser_real_simulation_page_smoke=ok" diff --git a/wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs b/wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs index 00ad59d..e504069 100644 --- a/wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +++ b/wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs @@ -14,6 +14,10 @@ const handoffText = readFileSync(resolve(root, "docs/project-release-handoff.md" const readmeText = readFileSync(resolve(root, "README.md"), "utf8"); const panelEntryText = readFileSync(resolve(root, "docs/panel-entry.md"), "utf8"); const sdkReadmeText = readFileSync(resolve(root, "runtime/sdk/README.md"), "utf8"); +const realBrowserSimulationPriorityText = readFileSync( + resolve(root, "docs/real-browser-simulation-priority.md"), + "utf8", +); const sourceReuseText = readFileSync(resolve(root, "docs/source-reuse-map.md"), "utf8"); const driftText = readFileSync(resolve(root, "docs/drift-report.md"), "utf8"); const hostSmokeText = readFileSync(resolve(root, "tests/host/verify_host_smokes.sh"), "utf8"); @@ -25,12 +29,18 @@ for (const phrase of [ "docs/panel-entry.md", "runtime/sdk/README.md", "docs/opfs-session-persistence.md", + "docs/real-browser-simulation-priority.md", "docs/sim-configs-coverage-handoff.md", "docs/host-runtime-boundary-handoff.md", "docs/source-reuse-map.md", "docs/drift-report.md", "PROJECT_COMPLETION_TRACKER.md", "runtime/sdk/src/index.js", + "real browser CNC simulation page", + "runtime/ui/simulation/index.html", + "verify_real_simulation_browser.sh", + "LinuxCNC-backed interpreter execution", + "toolpath/preview area", "createProjectReleaseGateManifest()", "createProjectReleaseGateResultMatrix()", "createProjectReleaseGateActionPlan()", @@ -46,9 +56,48 @@ for (const phrase of [ "loadProjectReleaseReadinessArtifactUrlWorkflow()", "createProjectReleaseReadinessArtifactUrlWorkflowActionPlan()", "createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel()", + "createProjectBatchAcceptanceCapabilityMatrix()", + "createProjectBatchAcceptanceWorkflow()", + "createProjectBatchAcceptanceChecklist()", + "createProjectBatchAcceptanceReport()", + "createProjectBatchAcceptanceReportValidation()", + "createProjectBatchAcceptanceReportValidationSummaryViewModel()", + "createProjectBatchAcceptanceReportValidationActionPlan()", + "createProjectBatchAcceptanceReportJsonWorkflow()", + "loadProjectBatchAcceptanceReportUrlWorkflow()", + "createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel()", + "createProjectBatchAcceptanceReportUrlWorkflowActionPlan()", + "write_project_batch_acceptance_artifact.mjs", + "verify_project_batch_acceptance_artifact.sh", "embedded gate manifest, execution manifest, result matrix, and", "validateWorkflowOverviewReleaseReadinessArtifactJson()", + "runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow()", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel()", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan()", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract()", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness()", + "renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()", + "mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()", "loadWorkflowOverviewReleaseReadinessArtifactUrl()", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel()", + "getWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel()", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState()", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract()", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness()", + "renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()", + "mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan()", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState()", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract()", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness()", + "renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()", + "mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan()", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState()", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract()", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness()", + "renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()", + "mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()", "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel()", "render-state", "DOM readiness", @@ -61,15 +110,21 @@ for (const phrase of [ "build/project-release-readiness.json", "verify_project_release_readiness_artifact.sh", "verify_project_release_artifact_url_workflow.sh", + "verify_project_batch_acceptance_workflow.sh", + "verify_real_browser_simulation_priority_docs.sh", "verify_opfs_session_workflow_browser.sh", "host_wasm_opfs_browser_smokes=ok", "project_release_readiness_artifact_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", + "real_browser_simulation_priority_docs_node_smoke=ok", "project_release_gate=ok", "sim_configs_wasm_node_inventory_unexpected_fail=0", "browser_ini_shell_integration_workflow_smoke=ok", "browser_opfs_session_workflow_smoke=ok", "browser_release_artifact_url_workflow_smoke=ok", + "browser_real_simulation_page_smoke=ok", "project_release_handoff_docs_node_smoke=ok", "L4-USER-M-PROCESS", "L4-TOOL-DB", @@ -80,7 +135,9 @@ for (const phrase of [ for (const phrase of [ "docs/project-release-handoff.md", + "docs/real-browser-simulation-priority.md", "project_release_handoff_docs_node_smoke=ok", + "real_browser_simulation_priority_docs_node_smoke=ok", ]) { assert.match(readmeText, literalRegExp(phrase)); assert.match(hostSmokeText, literalRegExp("verify_project_release_handoff_docs.sh")); @@ -95,11 +152,15 @@ for (const phrase of [ "verify_ini_panel_browser.sh", "verify_opfs_session_workflow_browser.sh", "verify_release_artifact_url_workflow_browser.sh", + "verify_project_batch_acceptance_workflow.sh", + "verify_real_browser_simulation_priority_docs.sh", "verify_ui_node_smokes.sh", "verify_host_smokes.sh", "write_project_release_readiness_artifact.mjs", "verify_project_release_readiness_artifact.sh", "verify_project_release_artifact_url_workflow.sh", + "verify_project_batch_acceptance_workflow.sh", + "verify_real_browser_simulation_priority_docs.sh", "project_release_gate=ok", ]) { assert.match(projectReleaseGateText, literalRegExp(phrase)); @@ -136,8 +197,51 @@ for (const phrase of [ "loadProjectReleaseReadinessArtifactUrlWorkflow()", "createProjectReleaseReadinessArtifactUrlWorkflowActionPlan()", "createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel()", + "createProjectBatchAcceptanceCapabilityMatrix()", + "createProjectBatchAcceptanceWorkflow()", + "createProjectBatchAcceptanceChecklist()", + "createProjectBatchAcceptanceReport()", + "createProjectBatchAcceptanceReportValidation()", + "createProjectBatchAcceptanceReportValidationSummaryViewModel()", + "createProjectBatchAcceptanceReportValidationActionPlan()", + "createProjectBatchAcceptanceReportJsonWorkflow()", + "loadProjectBatchAcceptanceReportUrlWorkflow()", + "createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel()", + "createProjectBatchAcceptanceReportUrlWorkflowActionPlan()", + "createProjectBatchAcceptanceSummaryViewModel()", + "createProjectBatchAcceptanceActionPlan()", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel()", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel()", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState()", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract()", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness()", + "renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()", + "mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan()", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState()", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract()", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness()", + "renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()", + "mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan()", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState()", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract()", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness()", + "renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()", + "mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()", + "runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow()", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel()", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan()", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract()", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness()", + "renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()", + "mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()", "verify_project_release_artifact_url_workflow.sh", + "verify_project_batch_acceptance_workflow.sh", + "verify_project_batch_acceptance_artifact.sh", "project_release_artifact_url_workflow_node_smoke=ok", + "project_batch_acceptance_workflow_node_smoke=ok", + "project_batch_acceptance_artifact_node_smoke=ok", "build/project-release-readiness.json", "INI panel shell handoff exports", "createIniPanelShellApiSurfaceInventory()", @@ -148,7 +252,10 @@ for (const phrase of [ assert.match(sourceReuseText, literalRegExp("docs/project-release-handoff.md")); assert.match(driftText, literalRegExp("docs/project-release-handoff.md")); +assert.match(realBrowserSimulationPriorityText, literalRegExp("first priority is to deliver a real browser CNC simulation page")); +assert.match(realBrowserSimulationPriorityText, literalRegExp("must not implement CNC semantics in JavaScript")); assert.match(trackerText, /Documentation\/release gate/); +assert.match(trackerText, /Real browser simulation priority/); assert.match(trackerText, /Project release gate wrapper/); assert.match(trackerText, /Final Acceptance Checklist/); diff --git a/wasm-port/tests/docs/node/verify_real_browser_simulation_priority_docs.mjs b/wasm-port/tests/docs/node/verify_real_browser_simulation_priority_docs.mjs new file mode 100644 index 0000000..afbd38c --- /dev/null +++ b/wasm-port/tests/docs/node/verify_real_browser_simulation_priority_docs.mjs @@ -0,0 +1,53 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const root = resolve(__dirname, "../../.."); + +function literalRegExp(text) { + return new RegExp(text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")); +} + +const priorityText = readFileSync(resolve(root, "docs/real-browser-simulation-priority.md"), "utf8"); +const handoffText = readFileSync(resolve(root, "docs/project-release-handoff.md"), "utf8"); +const readmeText = readFileSync(resolve(root, "README.md"), "utf8"); +const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"), "utf8"); +const hostSmokeText = readFileSync(resolve(root, "tests/host/verify_host_smokes.sh"), "utf8"); +const releaseGateText = readFileSync(resolve(root, "tests/host/verify_project_release_gate.sh"), "utf8"); +const simulationHtmlText = readFileSync(resolve(root, "runtime/ui/simulation/index.html"), "utf8"); +const simulationSmokeText = readFileSync(resolve(root, "tests/browser/verify_real_simulation_browser.sh"), "utf8"); + +for (const phrase of [ + "Real Browser Simulation Priority", + "first priority is to deliver a real browser CNC simulation page", + "machine/session loader", + "G-code program view", + "LinuxCNC-backed interpreter execution", + "toolpath/preview area", + "browser smoke coverage", + "must not implement CNC semantics in JavaScript", + "vendored LinuxCNC source", + "wasm-port/tests/docs/node/verify_real_browser_simulation_priority_docs.sh", + "runtime/ui/simulation/index.html", + "wasm-port/tests/browser/verify_real_simulation_browser.sh", +]) { + assert.match(priorityText, literalRegExp(phrase)); +} + +for (const text of [handoffText, readmeText, trackerText]) { + assert.match(text, literalRegExp("docs/real-browser-simulation-priority.md")); + assert.match(text, /real browser CNC simulation page|真实 UI\/browser 数控仿真页面/); + assert.match(text, /LinuxCNC semantic boundary|LinuxCNC 语义边界|LinuxCNC-backed/); +} + +assert.match(hostSmokeText, /verify_real_browser_simulation_priority_docs\.sh/); +assert.match(hostSmokeText, /verify_real_simulation_browser\.sh/); +assert.match(releaseGateText, /verify_real_simulation_browser\.sh/); +assert.match(simulationHtmlText, /LinuxCNC Browser Simulation/); +assert.match(simulationHtmlText, /data-toolpath-svg/); +assert.match(simulationHtmlText, /data-program-lines/); +assert.match(simulationSmokeText, /browser_real_simulation_page_smoke=ok/); + +console.log("real_browser_simulation_priority_docs_node_smoke=ok"); diff --git a/wasm-port/tests/docs/node/verify_real_browser_simulation_priority_docs.sh b/wasm-port/tests/docs/node/verify_real_browser_simulation_priority_docs.sh new file mode 100755 index 0000000..be1e5c9 --- /dev/null +++ b/wasm-port/tests/docs/node/verify_real_browser_simulation_priority_docs.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)" + +node "$ROOT_DIR/tests/docs/node/verify_real_browser_simulation_priority_docs.mjs" diff --git a/wasm-port/tests/fixtures/project-release-readiness-ready.json b/wasm-port/tests/fixtures/project-release-readiness-ready.json index 00e0823..b091f1a 100644 --- a/wasm-port/tests/fixtures/project-release-readiness-ready.json +++ b/wasm-port/tests/fixtures/project-release-readiness-ready.json @@ -25,7 +25,7 @@ "gateManifest": { "apiName": "project-release-gate-manifest", "manifestVersion": 1, - "gateCount": 11, + "gateCount": 12, "gateIds": [ "diff-check", "vendor-sync", @@ -35,6 +35,7 @@ "ini-panel-browser", "opfs-session-browser", "release-artifact-url-browser", + "project-batch-acceptance", "ui-node-smokes", "host-smokes", "project-release-gate" @@ -88,6 +89,12 @@ "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" }, + { + "id": "project-batch-acceptance", + "label": "Project batch acceptance workflow smoke", + "command": "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", + "expectedOutput": "project_batch_acceptance_workflow_node_smoke=ok" + }, { "id": "ui-node-smokes", "label": "UI Node smokes", @@ -113,14 +120,14 @@ "manifestVersion": 1, "phase": "ready", "ready": true, - "gateCount": 11, - "passedCount": 11, + "gateCount": 12, + "passedCount": 12, "unknownCount": 0, "missingGateIds": [], "manifest": { "apiName": "project-release-gate-manifest", "manifestVersion": 1, - "gateCount": 11, + "gateCount": 12, "gateIds": [ "diff-check", "vendor-sync", @@ -130,6 +137,7 @@ "ini-panel-browser", "opfs-session-browser", "release-artifact-url-browser", + "project-batch-acceptance", "ui-node-smokes", "host-smokes", "project-release-gate" @@ -183,6 +191,12 @@ "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" }, + { + "id": "project-batch-acceptance", + "label": "Project batch acceptance workflow smoke", + "command": "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", + "expectedOutput": "project_batch_acceptance_workflow_node_smoke=ok" + }, { "id": "ui-node-smokes", "label": "UI Node smokes", @@ -210,7 +224,7 @@ "command": "git diff --check", "expectedOutput": null, "observedOutput": null, - "observedOutputCount": 10, + "observedOutputCount": 11, "status": "passed", "passed": true, "evidence": "explicit-result" @@ -221,7 +235,7 @@ "command": "wasm-port/tools/verify_vendor_sync.sh", "expectedOutput": "vendor sync up to date", "observedOutput": "vendor sync up to date", - "observedOutputCount": 10, + "observedOutputCount": 11, "status": "passed", "passed": true, "evidence": "expected-output" @@ -232,7 +246,7 @@ "command": "wasm-port/tools/verify_no_standalone_cnc_semantics.sh", "expectedOutput": "standalone CNC semantics guard complete", "observedOutput": "standalone CNC semantics guard complete", - "observedOutputCount": 10, + "observedOutputCount": 11, "status": "passed", "passed": true, "evidence": "expected-output" @@ -243,7 +257,7 @@ "command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh", "expectedOutput": "interp_wasm_node_smoke=ok", "observedOutput": "interp_wasm_node_smoke=ok", - "observedOutputCount": 10, + "observedOutputCount": 11, "status": "passed", "passed": true, "evidence": "expected-output" @@ -254,7 +268,7 @@ "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", "observedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0", - "observedOutputCount": 10, + "observedOutputCount": 11, "status": "passed", "passed": true, "evidence": "expected-output" @@ -265,7 +279,7 @@ "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", "observedOutput": "browser_ini_shell_integration_workflow_smoke=ok", - "observedOutputCount": 10, + "observedOutputCount": 11, "status": "passed", "passed": true, "evidence": "expected-output" @@ -276,7 +290,7 @@ "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", "observedOutput": "browser_opfs_session_workflow_smoke=ok", - "observedOutputCount": 10, + "observedOutputCount": 11, "status": "passed", "passed": true, "evidence": "expected-output" @@ -287,7 +301,18 @@ "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", "observedOutput": "browser_release_artifact_url_workflow_smoke=ok", - "observedOutputCount": 10, + "observedOutputCount": 11, + "status": "passed", + "passed": true, + "evidence": "expected-output" + }, + { + "id": "project-batch-acceptance", + "label": "Project batch acceptance workflow smoke", + "command": "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", + "expectedOutput": "project_batch_acceptance_workflow_node_smoke=ok", + "observedOutput": "project_batch_acceptance_workflow_node_smoke=ok", + "observedOutputCount": 11, "status": "passed", "passed": true, "evidence": "expected-output" @@ -298,7 +323,7 @@ "command": "wasm-port/tests/ui/node/verify_ui_node_smokes.sh", "expectedOutput": "ui_node_smokes=ok", "observedOutput": "ui_node_smokes=ok", - "observedOutputCount": 10, + "observedOutputCount": 11, "status": "passed", "passed": true, "evidence": "expected-output" @@ -309,7 +334,7 @@ "command": "wasm-port/tests/host/verify_host_smokes.sh", "expectedOutput": "host_wasm_opfs_browser_smokes=ok", "observedOutput": "host_wasm_opfs_browser_smokes=ok", - "observedOutputCount": 10, + "observedOutputCount": 11, "status": "passed", "passed": true, "evidence": "expected-output" @@ -320,7 +345,7 @@ "command": "wasm-port/tests/host/verify_project_release_gate.sh", "expectedOutput": "project_release_gate=ok", "observedOutput": "project_release_gate=ok", - "observedOutputCount": 10, + "observedOutputCount": 11, "status": "passed", "passed": true, "evidence": "expected-output" @@ -336,20 +361,20 @@ "statusText": "Ready", "detailText": "All release gate evidence present", "statusLine": "Ready: All release gate evidence present", - "gateCount": 11, - "passedCount": 11, + "gateCount": 12, + "passedCount": 12, "unknownCount": 0, "missingGateIds": [], "nextGateId": null, "evidenceCounts": { "explicit-result": 1, - "expected-output": 10 + "expected-output": 11 }, "rows": [ { "id": "gate-results", "label": "Release gate evidence", - "value": "11/11 passed" + "value": "12/12 passed" }, { "id": "unknown-gates", @@ -364,7 +389,7 @@ { "id": "expected-output-evidence", "label": "Expected-output evidence", - "value": "10" + "value": "11" }, { "id": "explicit-result-evidence", @@ -458,6 +483,16 @@ "status": "passed", "passed": true }, + { + "id": "project-batch-acceptance", + "label": "Project batch acceptance workflow smoke", + "command": "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", + "expectedOutput": "project_batch_acceptance_workflow_node_smoke=ok", + "observedOutput": "project_batch_acceptance_workflow_node_smoke=ok", + "evidence": "expected-output", + "status": "passed", + "passed": true + }, { "id": "ui-node-smokes", "label": "UI Node smokes", @@ -495,14 +530,14 @@ "matrixVersion": 1, "phase": "ready", "ready": true, - "gateCount": 11, - "passedCount": 11, + "gateCount": 12, + "passedCount": 12, "unknownCount": 0, "missingGateIds": [], "manifest": { "apiName": "project-release-gate-manifest", "manifestVersion": 1, - "gateCount": 11, + "gateCount": 12, "gateIds": [ "diff-check", "vendor-sync", @@ -512,6 +547,7 @@ "ini-panel-browser", "opfs-session-browser", "release-artifact-url-browser", + "project-batch-acceptance", "ui-node-smokes", "host-smokes", "project-release-gate" @@ -565,6 +601,12 @@ "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" }, + { + "id": "project-batch-acceptance", + "label": "Project batch acceptance workflow smoke", + "command": "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", + "expectedOutput": "project_batch_acceptance_workflow_node_smoke=ok" + }, { "id": "ui-node-smokes", "label": "UI Node smokes", @@ -650,6 +692,14 @@ "status": "passed", "passed": true }, + { + "id": "project-batch-acceptance", + "label": "Project batch acceptance workflow smoke", + "command": "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", + "expectedOutput": "project_batch_acceptance_workflow_node_smoke=ok", + "status": "passed", + "passed": true + }, { "id": "ui-node-smokes", "label": "UI Node smokes", @@ -681,8 +731,8 @@ "planVersion": 1, "phase": "ready", "ready": true, - "gateCount": 11, - "completedCount": 11, + "gateCount": 12, + "completedCount": 12, "pendingCount": 0, "completedGateIds": [ "diff-check", @@ -693,6 +743,7 @@ "ini-panel-browser", "opfs-session-browser", "release-artifact-url-browser", + "project-batch-acceptance", "ui-node-smokes", "host-smokes", "project-release-gate" @@ -769,6 +820,14 @@ "status": "passed", "passed": true }, + { + "id": "project-batch-acceptance", + "label": "Project batch acceptance workflow smoke", + "command": "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", + "expectedOutput": "project_batch_acceptance_workflow_node_smoke=ok", + "status": "passed", + "passed": true + }, { "id": "ui-node-smokes", "label": "UI Node smokes", diff --git a/wasm-port/tests/host/verify_host_smokes.sh b/wasm-port/tests/host/verify_host_smokes.sh index 238576b..2a0df99 100755 --- a/wasm-port/tests/host/verify_host_smokes.sh +++ b/wasm-port/tests/host/verify_host_smokes.sh @@ -18,8 +18,10 @@ SKIP_TP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_tp_wasm.sh" "$ROOT_DIR/tests/docs/node/verify_sim_configs_coverage_docs.sh" "$ROOT_DIR/tests/docs/node/verify_host_runtime_boundary_docs.sh" "$ROOT_DIR/tests/docs/node/verify_project_release_handoff_docs.sh" +"$ROOT_DIR/tests/docs/node/verify_real_browser_simulation_priority_docs.sh" "$ROOT_DIR/tests/ui/node/verify_ui_node_smokes.sh" SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_ini_panel_browser.sh" +SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_real_simulation_browser.sh" SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_opfs_session_workflow_browser.sh" SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_interp_browser.sh" diff --git a/wasm-port/tests/host/verify_project_release_gate.sh b/wasm-port/tests/host/verify_project_release_gate.sh index fbaf480..7d1ed01 100755 --- a/wasm-port/tests/host/verify_project_release_gate.sh +++ b/wasm-port/tests/host/verify_project_release_gate.sh @@ -12,10 +12,13 @@ git diff --check SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_interp_wasm.sh" SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_sim_configs_inventory_wasm.sh" SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_ini_panel_browser.sh" +SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_real_simulation_browser.sh" SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_opfs_session_workflow_browser.sh" SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_release_artifact_url_workflow_browser.sh" +"$ROOT_DIR/tests/sdk/node/verify_project_batch_acceptance_workflow.sh" "$ROOT_DIR/tests/ui/node/verify_ui_node_smokes.sh" "$ROOT_DIR/tests/sdk/node/verify_project_release_artifact_url_workflow.sh" +"$ROOT_DIR/tests/docs/node/verify_real_browser_simulation_priority_docs.sh" "$ROOT_DIR/tests/host/verify_host_smokes.sh" node "$ROOT_DIR/tests/host/write_project_release_readiness_artifact.mjs" diff --git a/wasm-port/tests/host/verify_project_release_readiness_artifact.mjs b/wasm-port/tests/host/verify_project_release_readiness_artifact.mjs index d20e073..c6b69a6 100644 --- a/wasm-port/tests/host/verify_project_release_readiness_artifact.mjs +++ b/wasm-port/tests/host/verify_project_release_readiness_artifact.mjs @@ -20,8 +20,8 @@ assert.equal(validation.phase, "ready"); assert.deepEqual(validation.missing, []); assert.equal(validation.artifactApiName, "project-release-readiness-report"); assert.equal(validation.artifactVersion, 1); -assert.equal(validation.gateCount, 11); -assert.equal(validation.expectedGateCount, 11); +assert.equal(validation.gateCount, 12); +assert.equal(validation.expectedGateCount, 12); assert.equal(validation.gateManifestReady, true); assert.equal(validation.gateExecutionManifestReady, true); assert.equal(validation.gateExecutionSummaryReady, true); @@ -36,15 +36,16 @@ assert.deepEqual(validation.expectedGateIds, [ "ini-panel-browser", "opfs-session-browser", "release-artifact-url-browser", + "project-batch-acceptance", "ui-node-smokes", "host-smokes", "project-release-gate", ]); assert.equal(artifact.gateManifest.apiName, "project-release-gate-manifest"); assert.equal(artifact.gateExecutionManifest.apiName, "project-release-gate-execution-manifest"); -assert.equal(artifact.gateExecutionManifest.passedCount, 11); +assert.equal(artifact.gateExecutionManifest.passedCount, 12); assert.equal(artifact.gateExecutionSummaryViewModel.apiName, "project-release-gate-execution-summary-view-model"); -assert.equal(artifact.gateExecutionSummaryViewModel.rows[0].value, "11/11 passed"); +assert.equal(artifact.gateExecutionSummaryViewModel.rows[0].value, "12/12 passed"); assert.equal(artifact.gateResultMatrix.apiName, "project-release-gate-result-matrix"); assert.equal(artifact.gateActionPlan.apiName, "project-release-gate-action-plan"); assert.equal(artifact.gateActionPlan.pendingCount, 0); diff --git a/wasm-port/tests/host/write_project_release_readiness_artifact.mjs b/wasm-port/tests/host/write_project_release_readiness_artifact.mjs index 4c0d546..12e8452 100644 --- a/wasm-port/tests/host/write_project_release_readiness_artifact.mjs +++ b/wasm-port/tests/host/write_project_release_readiness_artifact.mjs @@ -17,6 +17,7 @@ const passedGateResults = { "ini-panel-browser": true, "opfs-session-browser": true, "release-artifact-url-browser": true, + "project-batch-acceptance": true, "ui-node-smokes": true, "host-smokes": true, "project-release-gate": true, @@ -32,6 +33,7 @@ const report = createProjectReleaseReadinessReport({ "browser_ini_shell_integration_workflow_smoke=ok", "browser_opfs_session_workflow_smoke=ok", "browser_release_artifact_url_workflow_smoke=ok", + "project_batch_acceptance_workflow_node_smoke=ok", "ui_node_smokes=ok", "host_wasm_opfs_browser_smokes=ok", "project_release_gate=ok", diff --git a/wasm-port/tests/sdk/node/verify_project_batch_acceptance_artifact.mjs b/wasm-port/tests/sdk/node/verify_project_batch_acceptance_artifact.mjs new file mode 100644 index 0000000..9b1e517 --- /dev/null +++ b/wasm-port/tests/sdk/node/verify_project_batch_acceptance_artifact.mjs @@ -0,0 +1,51 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +import { + createProjectBatchAcceptanceReportJsonWorkflow, + createProjectBatchAcceptanceReportValidationActionPlan, + createProjectBatchAcceptanceReportValidationSummaryViewModel, + createProjectBatchAcceptanceReportValidation, +} from "../../../runtime/sdk/src/index.js"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const root = resolve(__dirname, "../../.."); +const artifactPath = process.argv[2] ?? resolve(root, "build/project-batch-acceptance.json"); +const artifactJson = readFileSync(artifactPath, "utf8"); +const jsonWorkflow = createProjectBatchAcceptanceReportJsonWorkflow({ reportJson: artifactJson }); +const validation = createProjectBatchAcceptanceReportValidation(jsonWorkflow.report); +const validationSummary = createProjectBatchAcceptanceReportValidationSummaryViewModel(validation); +const validationActionPlan = createProjectBatchAcceptanceReportValidationActionPlan(validation); + +assert.equal(jsonWorkflow.apiName, "project-batch-acceptance-report-json-workflow"); +assert.equal(jsonWorkflow.ready, true); +assert.equal(jsonWorkflow.phase, "ready"); +assert.equal(jsonWorkflow.parsed, true); +assert.equal(jsonWorkflow.parseError, null); +assert.deepEqual(jsonWorkflow.missing, []); +assert.equal(validation.apiName, "project-batch-acceptance-report-validation"); +assert.equal(validation.ready, true); +assert.equal(validation.phase, "ready"); +assert.deepEqual(validation.missing, []); +assert.equal(validation.batchId, "project-batch-acceptance-artifact"); +assert.equal(jsonWorkflow.report.apiName, "project-batch-acceptance-report"); +assert.equal(jsonWorkflow.report.reportVersion, 1); +assert.equal(jsonWorkflow.report.ready, true); +assert.equal(jsonWorkflow.report.capabilityMatrix.ready, true); +assert.deepEqual(jsonWorkflow.report.capabilityMatrix.capabilityTypes, ["workflow", "gate"]); +assert.equal(jsonWorkflow.report.workflow.ready, true); +assert.equal(jsonWorkflow.report.summaryViewModel.statusLine, "Ready: Batch acceptance evidence complete"); +assert.equal(jsonWorkflow.report.actionPlan.nextCommand, null); +assert.equal(jsonWorkflow.report.checklist.passedCount, 3); +assert.equal(jsonWorkflow.report.rows.find(({ id }) => id === "checklist")?.value, "3/3 passed"); +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.rows.find(({ id }) => id === "validation")?.value, "ready"); +assert.equal(validationActionPlan.apiName, "project-batch-acceptance-report-validation-action-plan"); +assert.equal(validationActionPlan.ready, true); +assert.equal(validationActionPlan.nextCommand, null); +assert.equal(validationActionPlan.shellScript, "# Project batch acceptance report validation is already ready.\n"); + +console.log("project_batch_acceptance_artifact_node_smoke=ok"); diff --git a/wasm-port/tests/sdk/node/verify_project_batch_acceptance_artifact.sh b/wasm-port/tests/sdk/node/verify_project_batch_acceptance_artifact.sh new file mode 100755 index 0000000..67f07e6 --- /dev/null +++ b/wasm-port/tests/sdk/node/verify_project_batch_acceptance_artifact.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)" + +node "$ROOT_DIR/tests/sdk/node/verify_project_batch_acceptance_artifact.mjs" "$@" diff --git a/wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.mjs b/wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.mjs new file mode 100644 index 0000000..12279e8 --- /dev/null +++ b/wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.mjs @@ -0,0 +1,507 @@ +import assert from "node:assert/strict"; + +import { + createProjectBatchAcceptanceActionPlan, + createProjectBatchAcceptanceCapabilityMatrix, + createProjectBatchAcceptanceChecklist, + createProjectBatchAcceptanceReport, + createProjectBatchAcceptanceReportJsonWorkflow, + createProjectBatchAcceptanceReportUrlWorkflowActionPlan, + createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel, + createProjectBatchAcceptanceReportValidationActionPlan, + createProjectBatchAcceptanceReportValidationSummaryViewModel, + createProjectBatchAcceptanceReportValidation, + createProjectBatchAcceptanceSummaryViewModel, + createProjectBatchAcceptanceWorkflow, + loadProjectBatchAcceptanceReportUrlWorkflow, +} from "../../../runtime/sdk/src/index.js"; + +const readyCapabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({ + capabilities: [ + { + id: "callable-api", + type: "api", + label: "Callable API", + evidence: "createProjectBatchAcceptanceCapabilityMatrix", + }, + { + id: "browser-smoke", + type: "browser", + label: "Browser smoke", + evidence: "browser smoke output", + }, + { + id: "docs-only-note", + type: "docs", + label: "Docs-only note", + }, + ], +}); + +assert.equal(readyCapabilityMatrix.apiName, "project-batch-acceptance-capability-matrix"); +assert.equal(readyCapabilityMatrix.matrixVersion, 1); +assert.equal(readyCapabilityMatrix.phase, "ready"); +assert.equal(readyCapabilityMatrix.ready, true); +assert.deepEqual(readyCapabilityMatrix.acceptedCapabilityTypes, ["api", "workflow", "gate", "browser"]); +assert.deepEqual(readyCapabilityMatrix.capabilityTypes, ["api", "browser"]); +assert.equal(readyCapabilityMatrix.capabilityCount, 2); +assert.equal(readyCapabilityMatrix.acceptedCount, 2); +assert.equal(readyCapabilityMatrix.rejectedCount, 1); +assert.deepEqual(readyCapabilityMatrix.countsByType, { + api: 1, + workflow: 0, + gate: 0, + browser: 1, +}); +assert.equal(readyCapabilityMatrix.rejectedCapabilities[0].type, "docs"); +assert.deepEqual(readyCapabilityMatrix.missing, []); +assert.deepEqual( + readyCapabilityMatrix.rows.map(({ id, value }) => [id, value]), + [ + ["accepted-capabilities", "2"], + ["accepted-types", "api, browser"], + ["rejected-capabilities", "1"], + ["missing", "none"], + ], +); + +const blockedCapabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({ + capabilities: [ + { + id: "docs-only-note", + type: "docs", + label: "Docs-only note", + }, + ], +}); +assert.equal(blockedCapabilityMatrix.phase, "blocked"); +assert.equal(blockedCapabilityMatrix.ready, false); +assert.equal(blockedCapabilityMatrix.acceptedCount, 0); +assert.equal(blockedCapabilityMatrix.rejectedCount, 1); +assert.deepEqual(blockedCapabilityMatrix.missing, ["batch-capability"]); + +const readyWorkflow = createProjectBatchAcceptanceWorkflow({ + batchId: "batch-acceptance-workflow", + capabilities: [ + { + id: "project-batch-acceptance-workflow-api", + type: "api", + label: "Project batch acceptance workflow API", + evidence: "createProjectBatchAcceptanceWorkflow", + }, + { + id: "project-batch-acceptance-node-gate", + type: "gate", + label: "Project batch acceptance Node gate", + evidence: "project_batch_acceptance_workflow_node_smoke=ok", + command: "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", + }, + ], + observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"], +}); + +assert.equal(readyWorkflow.apiName, "project-batch-acceptance-workflow"); +assert.equal(readyWorkflow.workflowVersion, 1); +assert.equal(readyWorkflow.phase, "ready"); +assert.equal(readyWorkflow.ready, true); +assert.equal(readyWorkflow.batchId, "batch-acceptance-workflow"); +assert.deepEqual(readyWorkflow.acceptedCapabilityTypes, ["api", "workflow", "gate", "browser"]); +assert.deepEqual(readyWorkflow.capabilityTypes, ["api", "gate"]); +assert.equal(readyWorkflow.capabilityCount, 2); +assert.equal(readyWorkflow.capabilityMatrix.apiName, "project-batch-acceptance-capability-matrix"); +assert.equal(readyWorkflow.capabilityMatrix.ready, true); +assert.deepEqual(readyWorkflow.missing, []); +assert.equal( + readyWorkflow.gateResultMatrix.rows.find(({ id }) => id === "project-batch-acceptance")?.passed, + true, +); +assert.deepEqual( + readyWorkflow.rows.map(({ id, value }) => [id, value]), + [ + ["batch", "batch-acceptance-workflow"], + ["capabilities", "api, gate"], + ["project-batch-acceptance", "passed"], + ["missing", "none"], + ], +); + +const readySummary = createProjectBatchAcceptanceSummaryViewModel(readyWorkflow); +assert.equal(readySummary.apiName, "project-batch-acceptance-summary-view-model"); +assert.equal(readySummary.phase, "ready"); +assert.equal(readySummary.statusLine, "Ready: Batch acceptance evidence complete"); +assert.equal(readySummary.rows.find(({ id }) => id === "capability-count")?.value, "2"); + +const readyActionPlan = createProjectBatchAcceptanceActionPlan(readyWorkflow); +assert.equal(readyActionPlan.apiName, "project-batch-acceptance-action-plan"); +assert.equal(readyActionPlan.phase, "ready"); +assert.equal(readyActionPlan.ready, true); +assert.equal(readyActionPlan.commandCount, 0); +assert.equal(readyActionPlan.nextCommand, null); +assert.equal(readyActionPlan.shellScript, "# Project batch acceptance workflow is already ready.\n"); + +const readyChecklist = createProjectBatchAcceptanceChecklist({ + workflow: readyWorkflow, + actionPlan: readyActionPlan, +}); +assert.equal(readyChecklist.apiName, "project-batch-acceptance-checklist"); +assert.equal(readyChecklist.checklistVersion, 1); +assert.equal(readyChecklist.phase, "ready"); +assert.equal(readyChecklist.ready, true); +assert.equal(readyChecklist.batchId, "batch-acceptance-workflow"); +assert.equal(readyChecklist.passedCount, 3); +assert.equal(readyChecklist.blockedCount, 0); +assert.deepEqual( + readyChecklist.rows.map(({ id, value }) => [id, value]), + [ + ["accepted-capability", "pass: api, gate"], + ["batch-acceptance-gate", "pass: passed"], + ["next-action", "pass: none"], + ], +); + +const readyReport = createProjectBatchAcceptanceReport({ + batchId: "batch-acceptance-workflow", + capabilities: readyWorkflow.capabilities, + observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"], +}); +assert.equal(readyReport.apiName, "project-batch-acceptance-report"); +assert.equal(readyReport.reportVersion, 1); +assert.equal(readyReport.phase, "ready"); +assert.equal(readyReport.ready, true); +assert.equal(readyReport.batchId, "batch-acceptance-workflow"); +assert.deepEqual(readyReport.missing, []); +assert.equal(readyReport.capabilityMatrix.apiName, "project-batch-acceptance-capability-matrix"); +assert.equal(readyReport.workflow.apiName, "project-batch-acceptance-workflow"); +assert.equal(readyReport.summaryViewModel.statusLine, "Ready: Batch acceptance evidence complete"); +assert.equal(readyReport.actionPlan.nextCommand, null); +assert.equal(readyReport.checklist.passedCount, 3); +assert.deepEqual( + readyReport.rows.map(({ id, value }) => [id, value]), + [ + ["summary", "Ready: Batch acceptance evidence complete"], + ["capabilities", "api, gate"], + ["checklist", "3/3 passed"], + ["next-command", "none"], + ], +); + +const readyReportValidation = createProjectBatchAcceptanceReportValidation(readyReport); +const readyReportValidationSummary = createProjectBatchAcceptanceReportValidationSummaryViewModel( + readyReportValidation, +); +const readyReportValidationActionPlan = createProjectBatchAcceptanceReportValidationActionPlan( + readyReportValidation, +); +assert.equal(readyReportValidation.apiName, "project-batch-acceptance-report-validation"); +assert.equal(readyReportValidation.validationVersion, 1); +assert.equal(readyReportValidation.phase, "ready"); +assert.equal(readyReportValidation.ready, true); +assert.deepEqual(readyReportValidation.missing, []); +assert.equal(readyReportValidation.batchId, "batch-acceptance-workflow"); +assert.deepEqual( + readyReportValidation.rows.map(({ id, value }) => [id, value]), + [ + ["report", "project-batch-acceptance-report"], + ["capability-matrix", "ready"], + ["workflow", "ready"], + ["checklist", "ready"], + ["missing", "none"], + ], +); +assert.equal( + readyReportValidationSummary.statusLine, + "Ready: Batch acceptance report evidence complete", +); +assert.deepEqual( + readyReportValidationSummary.rows.map(({ id, value }) => [id, value]), + [ + ["report", "project-batch-acceptance-report"], + ["batch", "batch-acceptance-workflow"], + ["validation", "ready"], + ["missing", "none"], + ], +); +assert.deepEqual(readyReportValidationActionPlan, { + apiName: "project-batch-acceptance-report-validation-action-plan", + planVersion: 1, + phase: "ready", + ready: true, + missing: [], + missingCount: 0, + commandCount: 0, + nextActionId: null, + nextCommand: null, + commands: [], + shellScript: "# Project batch acceptance report validation is already ready.\n", + rows: [ + ["report-validation", "Report validation", "ready"], + ["missing", "Missing report evidence", "none"], + ["next-command", "Next command", "none"], + ].map(([id, label, value]) => ({ id, label, value })), +}); + +const readyReportJsonWorkflow = createProjectBatchAcceptanceReportJsonWorkflow({ + reportJson: JSON.stringify(readyReport), +}); +assert.equal(readyReportJsonWorkflow.apiName, "project-batch-acceptance-report-json-workflow"); +assert.equal(readyReportJsonWorkflow.workflowVersion, 1); +assert.equal(readyReportJsonWorkflow.phase, "ready"); +assert.equal(readyReportJsonWorkflow.ready, true); +assert.equal(readyReportJsonWorkflow.parsed, true); +assert.equal(readyReportJsonWorkflow.parseError, null); +assert.equal(readyReportJsonWorkflow.validation.ready, true); +assert.deepEqual(readyReportJsonWorkflow.missing, []); + +let fetchedReportUrl = null; +const readyReportUrlWorkflow = await loadProjectBatchAcceptanceReportUrlWorkflow({ + reportUrl: "/project-batch-acceptance.json", + fetchRef: async (url) => { + fetchedReportUrl = url; + return { + ok: true, + status: 200, + text: async () => JSON.stringify(readyReport), + }; + }, +}); +const readyReportUrlSummary = createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel( + readyReportUrlWorkflow, +); +const readyReportUrlActionPlan = createProjectBatchAcceptanceReportUrlWorkflowActionPlan( + readyReportUrlWorkflow, +); + +assert.equal(fetchedReportUrl, "/project-batch-acceptance.json"); +assert.equal(readyReportUrlWorkflow.apiName, "project-batch-acceptance-report-url-workflow"); +assert.equal(readyReportUrlWorkflow.workflowVersion, 1); +assert.equal(readyReportUrlWorkflow.phase, "ready"); +assert.equal(readyReportUrlWorkflow.ready, true); +assert.equal(readyReportUrlWorkflow.fetched, true); +assert.equal(readyReportUrlWorkflow.httpStatus, 200); +assert.equal(readyReportUrlWorkflow.fetchError, null); +assert.equal(readyReportUrlWorkflow.jsonWorkflow.ready, true); +assert.equal(readyReportUrlWorkflow.validation.ready, true); +assert.deepEqual(readyReportUrlWorkflow.missing, []); +assert.equal( + readyReportUrlSummary.statusLine, + "Ready: Batch report URL workflow complete", +); +assert.deepEqual( + readyReportUrlSummary.rows.map(({ id, value }) => [id, value]), + [ + ["report-url", "/project-batch-acceptance.json"], + ["fetch", "ready (200)"], + ["json-workflow", "ready"], + ["report-validation", "ready"], + ["missing", "none"], + ], +); +assert.deepEqual(readyReportUrlActionPlan, { + apiName: "project-batch-acceptance-report-url-workflow-action-plan", + planVersion: 1, + phase: "ready", + ready: true, + missing: [], + actionCount: 0, + commandCount: 0, + nextActionId: null, + nextCommand: null, + actions: [], + commands: [], + shellScript: "# Project batch acceptance report URL workflow is already ready.\n", + rows: [], +}); + +const blockedWorkflow = createProjectBatchAcceptanceWorkflow({ + batchId: "docs-only-batch", + capabilities: [ + { + id: "docs-only-note", + type: "docs", + label: "Docs-only note", + }, + ], +}); +assert.equal(blockedWorkflow.phase, "blocked"); +assert.equal(blockedWorkflow.ready, false); +assert.deepEqual(blockedWorkflow.capabilityTypes, []); +assert.equal(blockedWorkflow.rejectedCapabilities[0].type, "docs"); +assert.deepEqual(blockedWorkflow.missing, ["batch-capability", "project-batch-acceptance"]); + +const blockedSummary = createProjectBatchAcceptanceSummaryViewModel(blockedWorkflow); +assert.equal(blockedSummary.statusLine, "Blocked: batch-capability, project-batch-acceptance"); + +const blockedActionPlan = createProjectBatchAcceptanceActionPlan(blockedWorkflow); +assert.equal(blockedActionPlan.phase, "blocked"); +assert.equal(blockedActionPlan.ready, false); +assert.equal(blockedActionPlan.commandCount, 1); +assert.equal(blockedActionPlan.nextActionId, "project-batch-acceptance"); +assert.equal( + blockedActionPlan.nextCommand, + "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", +); +assert.match(blockedActionPlan.shellScript, /verify_project_batch_acceptance_workflow\.sh/); + +const blockedChecklist = createProjectBatchAcceptanceChecklist({ + workflow: blockedWorkflow, + actionPlan: blockedActionPlan, +}); +assert.equal(blockedChecklist.phase, "blocked"); +assert.equal(blockedChecklist.ready, false); +assert.equal(blockedChecklist.passedCount, 0); +assert.equal(blockedChecklist.blockedCount, 3); +assert.deepEqual( + blockedChecklist.items.map(({ id, status }) => [id, status]), + [ + ["accepted-capability", "blocked"], + ["batch-acceptance-gate", "blocked"], + ["next-action", "blocked"], + ], +); + +const blockedReport = createProjectBatchAcceptanceReport({ + batchId: "docs-only-batch", + capabilities: blockedWorkflow.rejectedCapabilities, +}); +assert.equal(blockedReport.phase, "blocked"); +assert.equal(blockedReport.ready, false); +assert.deepEqual(blockedReport.missing, ["batch-capability", "project-batch-acceptance"]); +assert.equal(blockedReport.checklist.blockedCount, 3); +assert.equal( + blockedReport.rows.find(({ id }) => id === "next-command")?.value, + "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", +); + +const blockedReportValidation = createProjectBatchAcceptanceReportValidation(blockedReport); +const blockedReportValidationSummary = createProjectBatchAcceptanceReportValidationSummaryViewModel( + blockedReportValidation, +); +const blockedReportValidationActionPlan = createProjectBatchAcceptanceReportValidationActionPlan( + blockedReportValidation, +); +assert.equal(blockedReportValidation.phase, "blocked"); +assert.equal(blockedReportValidation.ready, false); +assert.deepEqual(blockedReportValidation.missing, [ + "phase", + "ready", + "missing", + "capabilityMatrix", + "workflow", + "summaryViewModel", + "actionPlan", + "checklist", + "rows.checklist", +]); +assert.match(blockedReportValidationSummary.statusLine, /^Blocked: phase, ready, missing/); +assert.equal(blockedReportValidationSummary.rows.find(({ id }) => id === "validation")?.value, "blocked"); +assert.equal(blockedReportValidationActionPlan.phase, "blocked"); +assert.equal(blockedReportValidationActionPlan.ready, false); +assert.equal(blockedReportValidationActionPlan.commandCount, 1); +assert.equal( + blockedReportValidationActionPlan.nextCommand, + "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", +); +assert.equal( + blockedReportValidationActionPlan.commands[0].expectedOutput, + "project_batch_acceptance_artifact_node_smoke=ok", +); + +const invalidReportJsonWorkflow = createProjectBatchAcceptanceReportJsonWorkflow({ + reportJson: "{not-json", +}); +assert.equal(invalidReportJsonWorkflow.phase, "blocked"); +assert.equal(invalidReportJsonWorkflow.ready, false); +assert.equal(invalidReportJsonWorkflow.parsed, false); +assert.match(invalidReportJsonWorkflow.parseError, /JSON/); +assert.equal(invalidReportJsonWorkflow.missing[0], "report-json"); + +const missingInputReportUrlWorkflow = await loadProjectBatchAcceptanceReportUrlWorkflow({ + reportUrl: "", + fetchRef: null, +}); +const missingInputReportUrlActionPlan = createProjectBatchAcceptanceReportUrlWorkflowActionPlan( + missingInputReportUrlWorkflow, +); +assert.equal(missingInputReportUrlWorkflow.ready, false); +assert.equal(missingInputReportUrlWorkflow.fetched, false); +assert.deepEqual(missingInputReportUrlWorkflow.missing.slice(0, 2), ["report-url", "fetch"]); +assert.match( + createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel( + missingInputReportUrlWorkflow, + ).statusLine, + /^Blocked: report-url, fetch/, +); +assert.equal(missingInputReportUrlActionPlan.actionCount, 3); +assert.equal(missingInputReportUrlActionPlan.commandCount, 1); +assert.equal(missingInputReportUrlActionPlan.nextActionId, "provide-report-url"); +assert.equal( + missingInputReportUrlActionPlan.nextCommand, + "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", +); +assert.deepEqual( + missingInputReportUrlActionPlan.actions.slice(0, 2).map(({ id, kind, command }) => [id, kind, command]), + [ + ["provide-report-url", "input", null], + ["provide-fetch", "input", null], + ], +); + +const httpFailureReportUrlWorkflow = await loadProjectBatchAcceptanceReportUrlWorkflow({ + reportUrl: "/missing-batch-report.json", + fetchRef: async () => ({ + ok: false, + status: 404, + text: async () => "", + }), +}); +const httpFailureReportUrlActionPlan = createProjectBatchAcceptanceReportUrlWorkflowActionPlan( + httpFailureReportUrlWorkflow, +); +assert.equal(httpFailureReportUrlWorkflow.ready, false); +assert.equal(httpFailureReportUrlWorkflow.fetched, false); +assert.equal(httpFailureReportUrlWorkflow.httpStatus, 404); +assert.match(httpFailureReportUrlWorkflow.fetchError, /404/); +assert.equal(httpFailureReportUrlWorkflow.missing[0], "report-fetch"); +assert.match( + createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel( + httpFailureReportUrlWorkflow, + ).statusLine, + /^Blocked: report-fetch/, +); +assert.equal(httpFailureReportUrlActionPlan.nextActionId, "verify-report-url-fetch"); +assert.equal(httpFailureReportUrlActionPlan.commands.length, 1); + +const thrownFetchReportUrlWorkflow = await loadProjectBatchAcceptanceReportUrlWorkflow({ + reportUrl: "/throws-batch-report.json", + fetchRef: async () => { + throw new Error("batch report network unavailable"); + }, +}); +assert.equal(thrownFetchReportUrlWorkflow.ready, false); +assert.equal(thrownFetchReportUrlWorkflow.fetched, false); +assert.equal(thrownFetchReportUrlWorkflow.httpStatus, null); +assert.equal(thrownFetchReportUrlWorkflow.fetchError, "batch report network unavailable"); +assert.equal(thrownFetchReportUrlWorkflow.missing[0], "report-fetch"); +assert.equal( + createProjectBatchAcceptanceReportUrlWorkflowActionPlan( + thrownFetchReportUrlWorkflow, + ).nextActionId, + "verify-report-url-fetch", +); + +const capabilityOnlyWorkflow = createProjectBatchAcceptanceWorkflow({ + capabilities: [ + { + id: "callable-helper", + type: "api", + label: "Callable helper", + }, + ], +}); +assert.deepEqual(capabilityOnlyWorkflow.missing, ["project-batch-acceptance"]); +assert.equal( + createProjectBatchAcceptanceActionPlan(capabilityOnlyWorkflow).nextCommand, + "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", +); + +console.log("project_batch_acceptance_workflow_node_smoke=ok"); diff --git a/wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh b/wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh new file mode 100755 index 0000000..776b342 --- /dev/null +++ b/wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)" +ARTIFACT_PATH="${1:-$ROOT_DIR/build/project-batch-acceptance.json}" + +node "$ROOT_DIR/tests/sdk/node/verify_project_batch_acceptance_workflow.mjs" +node "$ROOT_DIR/tests/sdk/node/write_project_batch_acceptance_artifact.mjs" "$ARTIFACT_PATH" +"$ROOT_DIR/tests/sdk/node/verify_project_batch_acceptance_artifact.sh" "$ARTIFACT_PATH" diff --git a/wasm-port/tests/sdk/node/verify_project_release_gate_manifest.mjs b/wasm-port/tests/sdk/node/verify_project_release_gate_manifest.mjs index 4ae9f31..349cd5a 100644 --- a/wasm-port/tests/sdk/node/verify_project_release_gate_manifest.mjs +++ b/wasm-port/tests/sdk/node/verify_project_release_gate_manifest.mjs @@ -20,7 +20,7 @@ const manifest = createProjectReleaseGateManifest(); assert.equal(manifest.apiName, "project-release-gate-manifest"); assert.equal(manifest.manifestVersion, 1); -assert.equal(manifest.gateCount, 11); +assert.equal(manifest.gateCount, 12); assert.deepEqual(manifest.gateIds, [ "diff-check", "vendor-sync", @@ -30,6 +30,7 @@ assert.deepEqual(manifest.gateIds, [ "ini-panel-browser", "opfs-session-browser", "release-artifact-url-browser", + "project-batch-acceptance", "ui-node-smokes", "host-smokes", "project-release-gate", @@ -45,6 +46,7 @@ assert.deepEqual( "browser_ini_shell_integration_workflow_smoke=ok", "browser_opfs_session_workflow_smoke=ok", "browser_release_artifact_url_workflow_smoke=ok", + "project_batch_acceptance_workflow_node_smoke=ok", "ui_node_smokes=ok", "host_wasm_opfs_browser_smokes=ok", "project_release_gate=ok", @@ -58,6 +60,10 @@ assert.equal( manifest.gates.find(({ id }) => id === "opfs-session-browser")?.command, "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh", ); +assert.equal( + manifest.gates.find(({ id }) => id === "project-batch-acceptance")?.command, + "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", +); const partialMatrix = createProjectReleaseGateResultMatrix({ gateResults: { @@ -72,15 +78,16 @@ assert.equal(partialMatrix.apiName, "project-release-gate-result-matrix"); assert.equal(partialMatrix.matrixVersion, 1); assert.equal(partialMatrix.phase, "waiting"); assert.equal(partialMatrix.ready, false); -assert.equal(partialMatrix.gateCount, 11); +assert.equal(partialMatrix.gateCount, 12); assert.equal(partialMatrix.passedCount, 3); -assert.equal(partialMatrix.unknownCount, 8); +assert.equal(partialMatrix.unknownCount, 9); assert.deepEqual(partialMatrix.missingGateIds, [ "standalone-cnc-semantics", "interp-wasm", "sim-config-inventory", "ini-panel-browser", "opfs-session-browser", + "project-batch-acceptance", "ui-node-smokes", "host-smokes", "project-release-gate", @@ -107,9 +114,9 @@ assert.equal(partialExecutionManifest.apiName, "project-release-gate-execution-m assert.equal(partialExecutionManifest.manifestVersion, 1); assert.equal(partialExecutionManifest.phase, "waiting"); assert.equal(partialExecutionManifest.ready, false); -assert.equal(partialExecutionManifest.gateCount, 11); +assert.equal(partialExecutionManifest.gateCount, 12); assert.equal(partialExecutionManifest.passedCount, 3); -assert.equal(partialExecutionManifest.unknownCount, 8); +assert.equal(partialExecutionManifest.unknownCount, 9); assert.deepEqual(partialExecutionManifest.missingGateIds, partialMatrix.missingGateIds); assert.deepEqual( partialExecutionManifest.rows.filter(({ passed }) => passed).map(({ id, evidence }) => [id, evidence]), @@ -132,25 +139,25 @@ assert.equal(partialExecutionSummary.apiName, "project-release-gate-execution-su assert.equal(partialExecutionSummary.viewModelVersion, 1); assert.equal(partialExecutionSummary.phase, "waiting"); assert.equal(partialExecutionSummary.ready, false); -assert.equal(partialExecutionSummary.statusLine, "Waiting: standalone-cnc-semantics, interp-wasm, sim-config-inventory, ini-panel-browser, opfs-session-browser, ui-node-smokes, host-smokes, project-release-gate"); -assert.equal(partialExecutionSummary.gateCount, 11); +assert.equal(partialExecutionSummary.statusLine, "Waiting: standalone-cnc-semantics, interp-wasm, sim-config-inventory, ini-panel-browser, opfs-session-browser, project-batch-acceptance, ui-node-smokes, host-smokes, project-release-gate"); +assert.equal(partialExecutionSummary.gateCount, 12); assert.equal(partialExecutionSummary.passedCount, 3); -assert.equal(partialExecutionSummary.unknownCount, 8); +assert.equal(partialExecutionSummary.unknownCount, 9); assert.equal(partialExecutionSummary.nextGateId, "standalone-cnc-semantics"); assert.deepEqual(partialExecutionSummary.evidenceCounts, { "explicit-result": 1, "expected-output": 2, - missing: 8, + missing: 9, }); assert.deepEqual( partialExecutionSummary.rows.map(({ id, value }) => [id, value]), [ - ["gate-results", "3/11 passed"], - ["unknown-gates", "8"], + ["gate-results", "3/12 passed"], + ["unknown-gates", "9"], ["next-gate", "standalone-cnc-semantics"], ["expected-output-evidence", "2"], ["explicit-result-evidence", "1"], - ["missing", "standalone-cnc-semantics, interp-wasm, sim-config-inventory, ini-panel-browser, opfs-session-browser, ui-node-smokes, host-smokes, project-release-gate"], + ["missing", "standalone-cnc-semantics, interp-wasm, sim-config-inventory, ini-panel-browser, opfs-session-browser, project-batch-acceptance, ui-node-smokes, host-smokes, project-release-gate"], ], ); assert.equal(partialExecutionSummary.gateRows.length, manifest.gateCount); @@ -164,9 +171,9 @@ assert.equal(partialActionPlan.apiName, "project-release-gate-action-plan"); assert.equal(partialActionPlan.planVersion, 1); assert.equal(partialActionPlan.phase, "waiting"); assert.equal(partialActionPlan.ready, false); -assert.equal(partialActionPlan.gateCount, 11); +assert.equal(partialActionPlan.gateCount, 12); assert.equal(partialActionPlan.completedCount, 3); -assert.equal(partialActionPlan.pendingCount, 8); +assert.equal(partialActionPlan.pendingCount, 9); assert.deepEqual(partialActionPlan.completedGateIds, [ "diff-check", "vendor-sync", @@ -178,13 +185,14 @@ assert.deepEqual(partialActionPlan.pendingGateIds, [ "sim-config-inventory", "ini-panel-browser", "opfs-session-browser", + "project-batch-acceptance", "ui-node-smokes", "host-smokes", "project-release-gate", ]); assert.equal(partialActionPlan.nextGateId, "standalone-cnc-semantics"); assert.equal(partialActionPlan.nextCommand, "wasm-port/tools/verify_no_standalone_cnc_semantics.sh"); -assert.equal(partialActionPlan.commands.length, 8); +assert.equal(partialActionPlan.commands.length, 9); assert.equal(partialActionPlan.commands[0].step, 1); assert.equal(partialActionPlan.commands[0].gateId, "standalone-cnc-semantics"); assert.equal(partialActionPlan.rows[0].id, "standalone-cnc-semantics"); @@ -200,7 +208,7 @@ assert.equal(report.gateExecutionManifest.ready, true); assert.equal(report.gateExecutionManifest.passedCount, manifest.gateCount); assert.equal(report.gateExecutionSummaryViewModel.apiName, "project-release-gate-execution-summary-view-model"); assert.equal(report.gateExecutionSummaryViewModel.statusLine, "Ready: All release gate evidence present"); -assert.equal(report.gateExecutionSummaryViewModel.rows[0].value, "11/11 passed"); +assert.equal(report.gateExecutionSummaryViewModel.rows[0].value, "12/12 passed"); assert.equal(report.gateExecutionSummaryViewModel.nextGateId, null); assert.equal(report.gateResultMatrix.ready, true); assert.equal(report.gateResultMatrix.passedCount, manifest.gateCount); @@ -223,7 +231,7 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), { { id: "gate-results", label: "Release gates", - value: "11/11 passed", + value: "12/12 passed", }, { id: "unknown-gates", @@ -289,7 +297,7 @@ assert.deepEqual( artifactValidationSummary.rows.map(({ id, value }) => [id, value]), [ ["artifact", "project-release-readiness-report"], - ["gate-count", "11/11"], + ["gate-count", "12/12"], ["gate-manifest", "ready"], ["gate-execution-manifest", "ready"], ["gate-execution-summary", "ready"], diff --git a/wasm-port/tests/sdk/node/verify_sdk_surface.mjs b/wasm-port/tests/sdk/node/verify_sdk_surface.mjs index 4d53ef0..e45f28e 100644 --- a/wasm-port/tests/sdk/node/verify_sdk_surface.mjs +++ b/wasm-port/tests/sdk/node/verify_sdk_surface.mjs @@ -25,8 +25,12 @@ import { createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomReadiness, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel, createLinuxCncIniSdk, createLinuxCncInterpSdk, @@ -34,6 +38,18 @@ import { createMachineSessionPersistenceDisplayViewModel, createMachineSessionPersistenceRenderState, createMachineSessionPersistenceSummary, + createProjectBatchAcceptanceActionPlan, + createProjectBatchAcceptanceCapabilityMatrix, + createProjectBatchAcceptanceChecklist, + createProjectBatchAcceptanceReport, + createProjectBatchAcceptanceReportJsonWorkflow, + createProjectBatchAcceptanceReportUrlWorkflowActionPlan, + createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel, + createProjectBatchAcceptanceReportValidationActionPlan, + createProjectBatchAcceptanceReportValidationSummaryViewModel, + createProjectBatchAcceptanceReportValidation, + createProjectBatchAcceptanceSummaryViewModel, + createProjectBatchAcceptanceWorkflow, createProjectReleaseGateActionPlan, createProjectReleaseGateExecutionManifest, createProjectReleaseGateExecutionSummaryViewModel, @@ -60,6 +76,30 @@ import { isSupportedIniPanelLaunchApiManifest, isSupportedIniPanelShellIntegrationManifest, loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl, + loadProjectBatchAcceptanceReportUrlWorkflow, + runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState, loadProjectReleaseReadinessArtifactUrlWorkflow, loadMachineSessionFromOpfs, loadMachineSessionSnapshot, @@ -97,6 +137,30 @@ const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md const requiredExports = [ ["createLinuxCncIniSdk", createLinuxCncIniSdk], ["createLinuxCncInterpSdk", createLinuxCncInterpSdk], + ["createProjectBatchAcceptanceActionPlan", createProjectBatchAcceptanceActionPlan], + ["createProjectBatchAcceptanceCapabilityMatrix", createProjectBatchAcceptanceCapabilityMatrix], + ["createProjectBatchAcceptanceChecklist", createProjectBatchAcceptanceChecklist], + ["createProjectBatchAcceptanceReport", createProjectBatchAcceptanceReport], + ["createProjectBatchAcceptanceReportJsonWorkflow", createProjectBatchAcceptanceReportJsonWorkflow], + [ + "createProjectBatchAcceptanceReportUrlWorkflowActionPlan", + createProjectBatchAcceptanceReportUrlWorkflowActionPlan, + ], + [ + "createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel", + createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel, + ], + [ + "createProjectBatchAcceptanceReportValidationActionPlan", + createProjectBatchAcceptanceReportValidationActionPlan, + ], + [ + "createProjectBatchAcceptanceReportValidationSummaryViewModel", + createProjectBatchAcceptanceReportValidationSummaryViewModel, + ], + ["createProjectBatchAcceptanceReportValidation", createProjectBatchAcceptanceReportValidation], + ["createProjectBatchAcceptanceSummaryViewModel", createProjectBatchAcceptanceSummaryViewModel], + ["createProjectBatchAcceptanceWorkflow", createProjectBatchAcceptanceWorkflow], ["createProjectReleaseGateActionPlan", createProjectReleaseGateActionPlan], ["createProjectReleaseGateExecutionManifest", createProjectReleaseGateExecutionManifest], ["createProjectReleaseGateExecutionSummaryViewModel", createProjectReleaseGateExecutionSummaryViewModel], @@ -127,10 +191,103 @@ const requiredExports = [ "validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson", validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson, ], + [ + "runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow", + runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow, + ], + [ + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel", + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel, + ], + [ + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan", + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan, + ], + [ + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState", + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState, + ], + [ + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract", + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract, + ], + [ + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness", + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness, + ], + [ + "renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState", + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState, + ], + [ + "mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState", + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState, + ], + [ + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState", + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState, + ], + [ + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract", + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract, + ], + [ + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness", + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness, + ], + [ + "renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState", + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState, + ], + [ + "mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState", + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState, + ], + [ + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState", + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState, + ], + [ + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract", + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract, + ], + [ + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness", + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness, + ], + [ + "renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState", + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState, + ], + [ + "mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState", + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState, + ], + [ + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState", + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState, + ], + [ + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract", + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract, + ], + [ + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness", + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness, + ], + [ + "renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState", + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState, + ], + [ + "mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState", + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState, + ], [ "loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl", loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl, ], + ["loadProjectBatchAcceptanceReportUrlWorkflow", loadProjectBatchAcceptanceReportUrlWorkflow], ["loadProjectReleaseReadinessArtifactUrlWorkflow", loadProjectReleaseReadinessArtifactUrlWorkflow], ["planIniFileContextStaging", planIniFileContextStaging], ["planSimConfigStaging", planSimConfigStaging], @@ -194,10 +351,26 @@ const requiredExports = [ "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel", createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel, ], + [ + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan", + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan, + ], [ "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel", createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel, ], + [ + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan", + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan, + ], + [ + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel", + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel, + ], + [ + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel", + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel, + ], [ "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel", createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel, @@ -246,6 +419,106 @@ assert.equal(normalizeOpfsPath("linuxcnc/machines/sdk-mill/tool.tbl"), "linuxcnc assert.equal(gcodeFilenameFromProgramPath("linuxcnc/gcode/demo.ngc"), "demo.ngc"); const releaseReadinessWaiting = createProjectReleaseReadinessReport(); +const batchCapabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({ + capabilities: [ + { + id: "sdk-surface-api", + type: "api", + label: "SDK surface API", + }, + ], +}); +assert.equal(batchCapabilityMatrix.ready, true); +assert.equal(batchCapabilityMatrix.countsByType.api, 1); +const batchAcceptanceWorkflow = createProjectBatchAcceptanceWorkflow({ + batchId: "sdk-surface-batch", + capabilities: [ + { + id: "sdk-surface-api", + type: "api", + label: "SDK surface API", + }, + ], + observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"], +}); +assert.equal(batchAcceptanceWorkflow.ready, true); +assert.equal(batchAcceptanceWorkflow.capabilityTypes[0], "api"); +assert.equal( + createProjectBatchAcceptanceSummaryViewModel(batchAcceptanceWorkflow).statusLine, + "Ready: Batch acceptance evidence complete", +); +assert.equal( + createProjectBatchAcceptanceActionPlan(batchAcceptanceWorkflow).shellScript, + "# Project batch acceptance workflow is already ready.\n", +); +assert.equal( + createProjectBatchAcceptanceChecklist({ workflow: batchAcceptanceWorkflow }).rows[0].value, + "pass: api", +); +assert.equal( + createProjectBatchAcceptanceReport({ + batchId: "sdk-surface-batch", + capabilities: batchAcceptanceWorkflow.capabilities, + observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"], + }).rows.find(({ id }) => id === "checklist")?.value, + "3/3 passed", +); +const batchAcceptanceReport = createProjectBatchAcceptanceReport({ + batchId: "sdk-surface-batch", + capabilities: batchAcceptanceWorkflow.capabilities, + observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"], +}); +assert.equal(createProjectBatchAcceptanceReportValidation(batchAcceptanceReport).ready, true); +assert.equal( + createProjectBatchAcceptanceReportValidationSummaryViewModel( + createProjectBatchAcceptanceReportValidation(batchAcceptanceReport), + ).statusLine, + "Ready: Batch acceptance report evidence complete", +); +assert.equal( + createProjectBatchAcceptanceReportValidationActionPlan( + createProjectBatchAcceptanceReportValidation(batchAcceptanceReport), + ).shellScript, + "# Project batch acceptance report validation is already ready.\n", +); +assert.equal( + createProjectBatchAcceptanceReportJsonWorkflow({ + reportJson: JSON.stringify(batchAcceptanceReport), + }).ready, + true, +); +const batchAcceptanceReportUrlWorkflow = await loadProjectBatchAcceptanceReportUrlWorkflow({ + reportUrl: "/project-batch-acceptance.json", + fetchRef: async () => ({ + ok: true, + status: 200, + text: async () => JSON.stringify(batchAcceptanceReport), + }), +}); +assert.equal(batchAcceptanceReportUrlWorkflow.ready, true); +assert.equal( + createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel( + batchAcceptanceReportUrlWorkflow, + ).statusLine, + "Ready: Batch report URL workflow complete", +); +assert.equal( + createProjectBatchAcceptanceReportUrlWorkflowActionPlan( + batchAcceptanceReportUrlWorkflow, + ).shellScript, + "# Project batch acceptance report URL workflow is already ready.\n", +); +assert.equal( + (await loadProjectBatchAcceptanceReportUrlWorkflow({ + reportUrl: "/missing-project-batch-acceptance.json", + fetchRef: async () => ({ ok: false, status: 404, text: async () => "" }), + })).missing[0], + "report-fetch", +); +assert.equal( + createProjectBatchAcceptanceActionPlan(createProjectBatchAcceptanceWorkflow()).nextCommand, + "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", +); assert.equal(releaseReadinessWaiting.apiName, "project-release-readiness-report"); assert.equal(releaseReadinessWaiting.ready, false); assert.deepEqual(releaseReadinessWaiting.missing, ["project-release-gate"]); @@ -446,6 +719,44 @@ assert.equal( validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson(JSON.stringify(releaseReadinessArtifact)).ready, true, ); +assert.equal( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan( + validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson(JSON.stringify(releaseReadinessArtifact)), + ).shellScript, + "# Project release readiness artifact validation is already ready.\n", +); +assert.equal( + runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow({ + artifactJson: JSON.stringify(releaseReadinessArtifact), + }).statusLine, + "Ready: Artifact JSON workflow complete", +); +assert.equal( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel( + runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow({ + artifactJson: JSON.stringify(releaseReadinessArtifact), + }), + ).statusLine, + "Ready: Artifact JSON workflow complete", +); +assert.equal( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan( + runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow({ + artifactJson: JSON.stringify(releaseReadinessArtifact), + }), + ).nextCommand, + null, +); +assert.equal( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel( + runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow({ + artifactJson: JSON.stringify(releaseReadinessArtifact), + }), + ), + ).dataset.handoffScope, + "workflow-overview-release-readiness-artifact-json-workflow", +); const releaseReadinessArtifactUrlWorkflow = await loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl({ artifactUrl: "/project-release-readiness.json", fetchRef: async () => ({ @@ -465,6 +776,12 @@ assert.equal( ).statusLine, "Ready: Artifact URL workflow complete", ); +assert.equal( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan( + releaseReadinessArtifactUrlWorkflow, + ).shellScript, + "# Project release readiness artifact URL workflow is already ready.\n", +); assert.equal( createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState( createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel( diff --git a/wasm-port/tests/sdk/node/verify_sdk_surface.sh b/wasm-port/tests/sdk/node/verify_sdk_surface.sh index 1c860ff..e2b60ec 100755 --- a/wasm-port/tests/sdk/node/verify_sdk_surface.sh +++ b/wasm-port/tests/sdk/node/verify_sdk_surface.sh @@ -6,3 +6,4 @@ ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)" node "$ROOT_DIR/tests/sdk/node/verify_sdk_surface.mjs" "$ROOT_DIR/tests/sdk/node/verify_project_release_gate_manifest.sh" "$ROOT_DIR/tests/sdk/node/verify_project_release_artifact_url_workflow.sh" +"$ROOT_DIR/tests/sdk/node/verify_project_batch_acceptance_workflow.sh" diff --git a/wasm-port/tests/sdk/node/write_project_batch_acceptance_artifact.mjs b/wasm-port/tests/sdk/node/write_project_batch_acceptance_artifact.mjs new file mode 100644 index 0000000..92c99e1 --- /dev/null +++ b/wasm-port/tests/sdk/node/write_project_batch_acceptance_artifact.mjs @@ -0,0 +1,36 @@ +import { mkdirSync, renameSync, writeFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +import { createProjectBatchAcceptanceReport } from "../../../runtime/sdk/src/index.js"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const root = resolve(__dirname, "../../.."); +const outputPath = process.argv[2] ?? resolve(root, "build/project-batch-acceptance.json"); + +const report = createProjectBatchAcceptanceReport({ + batchId: "project-batch-acceptance-artifact", + capabilities: [ + { + id: "project-batch-acceptance-report-artifact", + type: "workflow", + label: "Project batch acceptance report artifact", + evidence: "project_batch_acceptance_artifact_node_smoke=ok", + command: "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", + }, + { + id: "project-batch-acceptance-workflow-gate", + type: "gate", + label: "Project batch acceptance workflow gate", + evidence: "project_batch_acceptance_workflow_node_smoke=ok", + command: "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", + }, + ], + observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"], +}); + +mkdirSync(dirname(outputPath), { recursive: true }); +writeFileSync(`${outputPath}.tmp`, `${JSON.stringify(report, null, 2)}\n`); +renameSync(`${outputPath}.tmp`, outputPath); + +console.log(`project_batch_acceptance_artifact=${outputPath}`); diff --git a/wasm-port/tests/ui/node/verify_ini_panel_entry_docs.mjs b/wasm-port/tests/ui/node/verify_ini_panel_entry_docs.mjs index 35b377a..a46685f 100644 --- a/wasm-port/tests/ui/node/verify_ini_panel_entry_docs.mjs +++ b/wasm-port/tests/ui/node/verify_ini_panel_entry_docs.mjs @@ -163,9 +163,34 @@ const requiredShellExports = [ "renderIniPanelShellWorkflowOverviewEmbeddingMountState", "mountIniPanelShellWorkflowOverviewEmbeddingMountState", "validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson", + "runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness", + "renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState", + "mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness", + "renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState", + "mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState", "loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl", "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness", + "renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState", + "mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState", "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract", + "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness", + "renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState", + "mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState", "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel", "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState", "createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract", diff --git a/wasm-port/tests/ui/node/verify_ini_panel_launch_api_manifest.mjs b/wasm-port/tests/ui/node/verify_ini_panel_launch_api_manifest.mjs index 7126498..b9ed509 100644 --- a/wasm-port/tests/ui/node/verify_ini_panel_launch_api_manifest.mjs +++ b/wasm-port/tests/ui/node/verify_ini_panel_launch_api_manifest.mjs @@ -82,9 +82,36 @@ assert.deepEqual(manifest, { "getWorkflowOverviewContract", "getWorkflowOverviewReadiness", "mountWorkflowOverviewEmbedding", + "runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState", + "mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState", "loadWorkflowOverviewReleaseReadinessArtifactUrl", "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState", + "mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState", "getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState", + "mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState", + "mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState", "getWorkflowOverviewEmbeddingReport", "getWorkflowOverviewEmbeddingDisplayViewModel", "getWorkflowOverviewEmbeddingRenderState", @@ -244,9 +271,36 @@ assert.match(launchText, /\bmountShellSessionHandoffWorkflow\b/); assert.match(launchText, /\bgetWorkflowOverviewContract\b/); assert.match(launchText, /\bgetWorkflowOverviewReadiness\b/); assert.match(launchText, /\bmountWorkflowOverviewEmbedding\b/); +assert.match(launchText, /\brunWorkflowOverviewReleaseReadinessArtifactJsonWorkflow\b/); +assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel\b/); +assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan\b/); +assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState\b/); +assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract\b/); +assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness\b/); +assert.match(launchText, /\brenderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState\b/); +assert.match(launchText, /\bmountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState\b/); assert.match(launchText, /\bloadWorkflowOverviewReleaseReadinessArtifactUrl\b/); assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel\b/); +assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan\b/); +assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState\b/); +assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract\b/); +assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness\b/); +assert.match(launchText, /\brenderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState\b/); +assert.match(launchText, /\bmountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState\b/); assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel\b/); +assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactValidationActionPlan\b/); +assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState\b/); +assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract\b/); +assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness\b/); +assert.match(launchText, /\brenderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState\b/); +assert.match(launchText, /\bmountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState\b/); +assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel\b/); +assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel\b/); +assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState\b/); +assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract\b/); +assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness\b/); +assert.match(launchText, /\brenderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState\b/); +assert.match(launchText, /\bmountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState\b/); assert.match(launchText, /\bgetWorkflowOverviewEmbeddingReport\b/); assert.match(launchText, /\bgetWorkflowOverviewEmbeddingDisplayViewModel\b/); assert.match(launchText, /\bgetWorkflowOverviewEmbeddingRenderState\b/); diff --git a/wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.mjs b/wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.mjs index 3d3f8ac..26e6ae9 100644 --- a/wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.mjs +++ b/wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.mjs @@ -52,9 +52,36 @@ import { renderIniPanelShellWorkflowOverviewEmbeddingMountState, mountIniPanelShellWorkflowOverviewEmbeddingMountState, validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson, + runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState, loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract, @@ -189,7 +216,7 @@ assert.deepEqual(readiness, { counts: { pages: 3, launcherLinks: 3, - launchMethods: 61, + launchMethods: 88, controlPageMethods: 11, persistenceCapabilities: 3, }, @@ -252,7 +279,7 @@ assert.deepEqual(handoffSummary, { counts: { pages: 3, launcherLinks: 3, - launchMethods: 61, + launchMethods: 88, controlPageMethods: 11, persistenceCapabilities: 3, }, @@ -791,9 +818,36 @@ assert.deepEqual(createIniPanelShellWorkflowOverviewContract(), { "getWorkflowOverviewApiSurfaceInventory", "getWorkflowOverviewPersistenceSummary", "validateWorkflowOverviewReleaseReadinessArtifactJson", + "runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState", + "mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState", "loadWorkflowOverviewReleaseReadinessArtifactUrl", "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState", + "mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState", "getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState", + "mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState", + "mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState", "getWorkflowOverviewReleaseReadinessArtifactDisplayViewModel", "getWorkflowOverviewReleaseReadinessArtifactRenderState", "getWorkflowOverviewReleaseReadinessArtifactDomContract", @@ -838,9 +892,36 @@ assert.equal(typeof createIniPanelShellWorkflowOverviewEmbeddingMountDomReadines assert.equal(typeof renderIniPanelShellWorkflowOverviewEmbeddingMountState, "function"); assert.equal(typeof mountIniPanelShellWorkflowOverviewEmbeddingMountState, "function"); assert.equal(typeof validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson, "function"); +assert.equal(typeof runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow, "function"); +assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel, "function"); +assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan, "function"); +assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState, "function"); +assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract, "function"); +assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness, "function"); +assert.equal(typeof renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState, "function"); +assert.equal(typeof mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState, "function"); assert.equal(typeof loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl, "function"); assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel, "function"); +assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan, "function"); +assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState, "function"); +assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract, "function"); +assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness, "function"); +assert.equal(typeof renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState, "function"); +assert.equal(typeof mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState, "function"); assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel, "function"); +assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan, "function"); +assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState, "function"); +assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract, "function"); +assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness, "function"); +assert.equal(typeof renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState, "function"); +assert.equal(typeof mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState, "function"); +assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel, "function"); +assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel, "function"); +assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState, "function"); +assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract, "function"); +assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness, "function"); +assert.equal(typeof renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState, "function"); +assert.equal(typeof mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState, "function"); assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel, "function"); assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState, "function"); assert.equal(typeof createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract, "function"); @@ -872,9 +953,36 @@ assert.deepEqual( "getWorkflowOverviewSummary", "getWorkflowOverviewPersistenceSummary", "validateWorkflowOverviewReleaseReadinessArtifactJson", + "runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState", + "mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState", "loadWorkflowOverviewReleaseReadinessArtifactUrl", "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState", + "mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState", "getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState", + "mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState", + "mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState", "getWorkflowOverviewReleaseReadinessArtifactDisplayViewModel", "getWorkflowOverviewReleaseReadinessArtifactRenderState", "getWorkflowOverviewReleaseReadinessArtifactDomContract", @@ -1300,7 +1408,7 @@ assert.deepEqual(createIniPanelShellIntegrationReadiness(blockedManifest), { counts: { pages: 3, launcherLinks: 3, - launchMethods: 61, + launchMethods: 88, controlPageMethods: 11, persistenceCapabilities: 3, }, diff --git a/wasm-port/tests/ui/node/verify_ini_panel_ui_shell.mjs b/wasm-port/tests/ui/node/verify_ini_panel_ui_shell.mjs index 64fc631..d8172ae 100644 --- a/wasm-port/tests/ui/node/verify_ini_panel_ui_shell.mjs +++ b/wasm-port/tests/ui/node/verify_ini_panel_ui_shell.mjs @@ -57,9 +57,36 @@ import { renderIniPanelShellWorkflowOverviewEmbeddingMountState, mountIniPanelShellWorkflowOverviewEmbeddingMountState, validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson, + runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState, loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract, + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness, + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState, + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState, createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract, @@ -690,9 +717,36 @@ assert.deepEqual(shellViewModel.shellWorkflowOverviewContract, { "getWorkflowOverviewApiSurfaceInventory", "getWorkflowOverviewPersistenceSummary", "validateWorkflowOverviewReleaseReadinessArtifactJson", + "runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract", + "getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState", + "mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState", "loadWorkflowOverviewReleaseReadinessArtifactUrl", "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract", + "getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState", + "mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState", "getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract", + "getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState", + "mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract", + "getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness", + "renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState", + "mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState", "getWorkflowOverviewReleaseReadinessArtifactDisplayViewModel", "getWorkflowOverviewReleaseReadinessArtifactRenderState", "getWorkflowOverviewReleaseReadinessArtifactDomContract", @@ -1300,13 +1354,104 @@ const releaseReadinessArtifactJson = JSON.stringify(createProjectReleaseReadines const releaseArtifactValidation = validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson( releaseReadinessArtifactJson, ); +const releaseArtifactJsonWorkflow = runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow({ + artifactJson: releaseReadinessArtifactJson, +}); +assert.equal( + releaseArtifactJsonWorkflow.apiName, + "ini-panel-shell-workflow-overview-release-readiness-artifact-json-workflow", +); +assert.equal(releaseArtifactJsonWorkflow.ready, true); +assert.equal(releaseArtifactJsonWorkflow.parsed, true); +assert.equal(releaseArtifactJsonWorkflow.statusLine, "Ready: Artifact JSON workflow complete"); +assert.equal(releaseArtifactJsonWorkflow.actionPlan.nextCommand, null); +assert.equal(releaseArtifactJsonWorkflow.gateExecutionSummaryViewModel.statusLine, "Ready: All release gate evidence present"); +const releaseArtifactJsonWorkflowSummary = + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel( + releaseArtifactJsonWorkflow, + ); +const releaseArtifactJsonWorkflowActionPlan = + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan( + releaseArtifactJsonWorkflow, + ); +const releaseArtifactJsonWorkflowRenderState = + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState( + releaseArtifactJsonWorkflowSummary, + ); +assert.equal( + releaseArtifactJsonWorkflowSummary.statusLine, + "Ready: Artifact JSON workflow complete", +); +assert.equal( + releaseArtifactJsonWorkflowActionPlan.shellScript, + "# Project release readiness artifact validation is already ready.\n", +); +assert.equal( + releaseArtifactJsonWorkflowSummary.rows.find(({ id }) => id === "action-plan")?.value, + "ready", +); +assert.equal(releaseArtifactJsonWorkflowRenderState.statusLine, "Ready: Artifact JSON workflow complete"); +assert.equal( + releaseArtifactJsonWorkflowRenderState.dataset.handoffScope, + "workflow-overview-release-readiness-artifact-json-workflow", +); +assert.equal( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract().selectors.mount, + "[data-workflow-overview-release-readiness-artifact-json-workflow]", +); +assert.equal(createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness().ready, false); +const releaseArtifactJsonWorkflowMountNode = mountDocument.createElement("section"); +releaseArtifactJsonWorkflowMountNode.dataset.workflowOverviewReleaseReadinessArtifactJsonWorkflow = ""; +const releaseArtifactJsonWorkflowStatusNode = mountDocument.createElement("p"); +releaseArtifactJsonWorkflowStatusNode.dataset.workflowOverviewReleaseReadinessArtifactJsonWorkflowStatus = ""; +const releaseArtifactJsonWorkflowRowsNode = { + ...mountDocument.createElement("dl"), + children: [], + replaceChildren() { + this.children = []; + }, + append(...nodes) { + this.children.push(...nodes); + }, +}; +releaseArtifactJsonWorkflowRowsNode.dataset.workflowOverviewReleaseReadinessArtifactJsonWorkflowRows = ""; +const releaseArtifactJsonWorkflowRenderResult = + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState( + releaseArtifactJsonWorkflowRenderState, + { + documentRef: mountDocument, + mountNode: releaseArtifactJsonWorkflowMountNode, + statusNode: releaseArtifactJsonWorkflowStatusNode, + rowsNode: releaseArtifactJsonWorkflowRowsNode, + }, + ); +const releaseArtifactJsonWorkflowMountResult = + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState({ + renderState: releaseArtifactJsonWorkflowRenderState, + documentRef: mountDocument, + mountNode: releaseArtifactJsonWorkflowMountNode, + statusNode: releaseArtifactJsonWorkflowStatusNode, + rowsNode: releaseArtifactJsonWorkflowRowsNode, + }); +assert.equal(releaseArtifactJsonWorkflowRenderResult.rendered, true); +assert.equal(releaseArtifactJsonWorkflowRenderResult.rowCount, 5); +assert.equal( + releaseArtifactJsonWorkflowRenderResult.dataset.handoffScope, + "workflow-overview-release-readiness-artifact-json-workflow", +); +assert.equal( + releaseArtifactJsonWorkflowRenderResult.rowIds.includes("gate-execution-summary"), + true, +); +assert.equal(releaseArtifactJsonWorkflowMountResult.ready, true); +assert.equal(releaseArtifactJsonWorkflowMountResult.renderResult.rowCount, 5); assert.equal( releaseArtifactValidation.apiName, "ini-panel-shell-workflow-overview-release-readiness-artifact-validation", ); assert.equal(releaseArtifactValidation.ready, true); assert.equal(releaseArtifactValidation.validation.ready, true); -assert.equal(releaseArtifactValidation.gateCount, 11); +assert.equal(releaseArtifactValidation.gateCount, 12); const releaseArtifactValidationSummary = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel( releaseArtifactValidation, @@ -1316,6 +1461,150 @@ assert.equal( "project-release-readiness-artifact-validation-summary-view-model", ); assert.equal(releaseArtifactValidationSummary.statusLine, "Ready: Artifact evidence complete"); +assert.equal(releaseArtifactValidation.actionPlan.ready, true); +assert.equal(releaseArtifactValidation.actionPlan.nextCommand, null); +assert.equal( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan( + releaseArtifactValidation, + ).shellScript, + "# Project release readiness artifact validation is already ready.\n", +); +const releaseArtifactValidationActionPlan = + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan( + releaseArtifactValidation, + ); +const releaseArtifactValidationActionPlanRenderState = + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState( + releaseArtifactValidationActionPlan, + ); +assert.equal( + releaseArtifactValidationActionPlanRenderState.statusLine, + "Ready: Artifact validation action plan complete", +); +assert.equal(releaseArtifactValidationActionPlanRenderState.rows.length, 3); +assert.equal( + releaseArtifactValidationActionPlanRenderState.dataset.handoffScope, + "workflow-overview-release-readiness-artifact-validation-action-plan", +); +assert.equal( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract().selectors.mount, + "[data-workflow-overview-release-readiness-artifact-validation-action-plan]", +); +assert.equal(createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness().ready, false); +const releaseArtifactValidationActionPlanMountNode = mountDocument.createElement("section"); +releaseArtifactValidationActionPlanMountNode.dataset.workflowOverviewReleaseReadinessArtifactValidationActionPlan = ""; +const releaseArtifactValidationActionPlanStatusNode = mountDocument.createElement("p"); +releaseArtifactValidationActionPlanStatusNode.dataset.workflowOverviewReleaseReadinessArtifactValidationActionPlanStatus = ""; +const releaseArtifactValidationActionPlanRowsNode = { + ...mountDocument.createElement("dl"), + children: [], + replaceChildren() { + this.children = []; + }, + append(...nodes) { + this.children.push(...nodes); + }, +}; +releaseArtifactValidationActionPlanRowsNode.dataset.workflowOverviewReleaseReadinessArtifactValidationActionPlanRows = ""; +const releaseArtifactValidationActionPlanRenderResult = + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState( + releaseArtifactValidationActionPlanRenderState, + { + documentRef: mountDocument, + mountNode: releaseArtifactValidationActionPlanMountNode, + statusNode: releaseArtifactValidationActionPlanStatusNode, + rowsNode: releaseArtifactValidationActionPlanRowsNode, + }, + ); +const releaseArtifactValidationActionPlanMountResult = + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState({ + renderState: releaseArtifactValidationActionPlanRenderState, + documentRef: mountDocument, + mountNode: releaseArtifactValidationActionPlanMountNode, + statusNode: releaseArtifactValidationActionPlanStatusNode, + rowsNode: releaseArtifactValidationActionPlanRowsNode, + }); +assert.equal(releaseArtifactValidationActionPlanRenderResult.rendered, true); +assert.equal(releaseArtifactValidationActionPlanRenderResult.rowCount, 3); +assert.equal( + releaseArtifactValidationActionPlanRenderResult.rowIds.includes("next-command"), + true, +); +assert.equal(releaseArtifactValidationActionPlanMountResult.ready, true); +assert.equal(releaseArtifactValidationActionPlanMountResult.renderResult.rowCount, 3); +const releaseArtifactGateExecutionSummary = + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel( + releaseArtifactValidation, + ); +assert.equal( + releaseArtifactGateExecutionSummary.apiName, + "project-release-gate-execution-summary-view-model", +); +assert.equal( + releaseArtifactGateExecutionSummary.statusLine, + "Ready: All release gate evidence present", +); +assert.equal(releaseArtifactGateExecutionSummary.rows[0].value, "12/12 passed"); +const releaseArtifactGateExecutionSummaryRenderState = + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState( + releaseArtifactGateExecutionSummary, + ); +assert.equal( + releaseArtifactGateExecutionSummaryRenderState.statusLine, + "Ready: All release gate evidence present", +); +assert.equal(releaseArtifactGateExecutionSummaryRenderState.rows.length, 18); +assert.equal(releaseArtifactGateExecutionSummaryRenderState.gateRows.length, 12); +assert.equal( + releaseArtifactGateExecutionSummaryRenderState.dataset.handoffScope, + "workflow-overview-release-readiness-artifact-gate-execution-summary", +); +assert.equal( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract().selectors.mount, + "[data-workflow-overview-release-readiness-artifact-gate-execution-summary]", +); +assert.equal(createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness().ready, false); +const releaseArtifactGateExecutionSummaryMountNode = mountDocument.createElement("section"); +releaseArtifactGateExecutionSummaryMountNode.dataset.workflowOverviewReleaseReadinessArtifactGateExecutionSummary = ""; +const releaseArtifactGateExecutionSummaryStatusNode = mountDocument.createElement("p"); +releaseArtifactGateExecutionSummaryStatusNode.dataset.workflowOverviewReleaseReadinessArtifactGateExecutionSummaryStatus = ""; +const releaseArtifactGateExecutionSummaryRowsNode = { + ...mountDocument.createElement("dl"), + children: [], + replaceChildren() { + this.children = []; + }, + append(...nodes) { + this.children.push(...nodes); + }, +}; +releaseArtifactGateExecutionSummaryRowsNode.dataset.workflowOverviewReleaseReadinessArtifactGateExecutionSummaryRows = ""; +const releaseArtifactGateExecutionSummaryRenderResult = + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState( + releaseArtifactGateExecutionSummaryRenderState, + { + documentRef: mountDocument, + mountNode: releaseArtifactGateExecutionSummaryMountNode, + statusNode: releaseArtifactGateExecutionSummaryStatusNode, + rowsNode: releaseArtifactGateExecutionSummaryRowsNode, + }, + ); +const releaseArtifactGateExecutionSummaryMountResult = + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState({ + renderState: releaseArtifactGateExecutionSummaryRenderState, + documentRef: mountDocument, + mountNode: releaseArtifactGateExecutionSummaryMountNode, + statusNode: releaseArtifactGateExecutionSummaryStatusNode, + rowsNode: releaseArtifactGateExecutionSummaryRowsNode, + }); +assert.equal(releaseArtifactGateExecutionSummaryRenderResult.rendered, true); +assert.equal(releaseArtifactGateExecutionSummaryRenderResult.rowCount, 18); +assert.equal( + releaseArtifactGateExecutionSummaryRenderResult.rowIds.includes("gate-ui-node-smokes"), + true, +); +assert.equal(releaseArtifactGateExecutionSummaryMountResult.ready, true); +assert.equal(releaseArtifactGateExecutionSummaryMountResult.renderResult.rowCount, 18); assert.equal( createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel().phase, "blocked", @@ -1435,6 +1724,86 @@ assert.equal( releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "validation-summary")?.value, "Ready: Artifact evidence complete", ); +assert.equal( + releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "gate-execution-summary")?.value, + "Ready: All release gate evidence present", +); +assert.equal( + releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "action-plan")?.value, + "ready", +); +assert.equal(releaseArtifactUrlWorkflow.actionPlan.ready, true); +assert.equal(releaseArtifactUrlWorkflow.actionPlan.nextCommand, null); +assert.equal( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan( + releaseArtifactUrlWorkflow, + ).shellScript, + "# Project release readiness artifact URL workflow is already ready.\n", +); +assert.equal( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel( + releaseArtifactUrlWorkflow, + ).statusLine, + "Ready: All release gate evidence present", +); +const releaseArtifactUrlWorkflowRenderState = + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState( + releaseArtifactUrlWorkflowSummary, + ); +assert.equal( + releaseArtifactUrlWorkflowRenderState.statusLine, + "Ready: Artifact URL workflow complete", +); +assert.equal( + releaseArtifactUrlWorkflowRenderState.dataset.handoffScope, + "workflow-overview-release-readiness-artifact-url-workflow", +); +assert.equal( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract().selectors.mount, + "[data-workflow-overview-release-readiness-artifact-url-workflow]", +); +assert.equal(createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness().ready, false); +const releaseArtifactUrlWorkflowMountNode = mountDocument.createElement("section"); +releaseArtifactUrlWorkflowMountNode.dataset.workflowOverviewReleaseReadinessArtifactUrlWorkflow = ""; +const releaseArtifactUrlWorkflowStatusNode = mountDocument.createElement("p"); +releaseArtifactUrlWorkflowStatusNode.dataset.workflowOverviewReleaseReadinessArtifactUrlWorkflowStatus = ""; +const releaseArtifactUrlWorkflowRowsNode = { + ...mountDocument.createElement("dl"), + children: [], + replaceChildren() { + this.children = []; + }, + append(...nodes) { + this.children.push(...nodes); + }, +}; +releaseArtifactUrlWorkflowRowsNode.dataset.workflowOverviewReleaseReadinessArtifactUrlWorkflowRows = ""; +const releaseArtifactUrlWorkflowRenderResult = + renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState( + releaseArtifactUrlWorkflowRenderState, + { + documentRef: mountDocument, + mountNode: releaseArtifactUrlWorkflowMountNode, + statusNode: releaseArtifactUrlWorkflowStatusNode, + rowsNode: releaseArtifactUrlWorkflowRowsNode, + }, + ); +const releaseArtifactUrlWorkflowMountResult = + mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState({ + renderState: releaseArtifactUrlWorkflowRenderState, + documentRef: mountDocument, + mountNode: releaseArtifactUrlWorkflowMountNode, + statusNode: releaseArtifactUrlWorkflowStatusNode, + rowsNode: releaseArtifactUrlWorkflowRowsNode, + }); +assert.equal(releaseArtifactUrlWorkflowRenderResult.rendered, true); +assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 8); +assert.equal( + releaseArtifactUrlWorkflowRenderResult.rowIds.includes("mount-result"), + true, +); +assert.equal(releaseArtifactUrlWorkflowMountResult.ready, true); +assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 8); const releaseArtifactUrlWorkflowMissingDom = await loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl({ artifactUrl: "/project-release-readiness.json", fetchRef: async () => ({ @@ -1473,9 +1842,80 @@ const workflowOverviewApi = { getWorkflowOverviewApiSurfaceInventory: () => shellViewModel.shellApiSurfaceInventory, getWorkflowOverviewPersistenceSummary: () => shellViewModel.shellPersistenceSummary, validateWorkflowOverviewReleaseReadinessArtifactJson: () => ({ ready: true }), + runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow: () => releaseArtifactJsonWorkflow, + getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel: () => releaseArtifactJsonWorkflowSummary, + getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan: () => releaseArtifactJsonWorkflowActionPlan, + getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState: () => releaseArtifactJsonWorkflowRenderState, + getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract, + getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness: () => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness({ + documentRef: mountDocument, + mountNode: releaseArtifactJsonWorkflowMountNode, + statusNode: releaseArtifactJsonWorkflowStatusNode, + rowsNode: releaseArtifactJsonWorkflowRowsNode, + }) + ), + renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState: () => releaseArtifactJsonWorkflowRenderResult, + mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState: () => releaseArtifactJsonWorkflowMountResult, loadWorkflowOverviewReleaseReadinessArtifactUrl: loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl, getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel: () => releaseArtifactUrlWorkflowSummary, + getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan: () => releaseArtifactUrlWorkflow.actionPlan, + getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState: () => releaseArtifactUrlWorkflowRenderState, + getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract, + getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness: () => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness({ + documentRef: mountDocument, + mountNode: releaseArtifactUrlWorkflowMountNode, + statusNode: releaseArtifactUrlWorkflowStatusNode, + rowsNode: releaseArtifactUrlWorkflowRowsNode, + }) + ), + renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState: () => releaseArtifactUrlWorkflowRenderResult, + mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState: () => releaseArtifactUrlWorkflowMountResult, getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel: () => releaseArtifactValidationSummary, + getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan: () => releaseArtifactValidation.actionPlan, + getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel: () => releaseArtifactGateExecutionSummary, + getWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel: () => releaseArtifactGateExecutionSummary, + getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState: () => ( + releaseArtifactValidationActionPlanRenderState + ), + getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract, + getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness: () => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness({ + documentRef: mountDocument, + mountNode: releaseArtifactValidationActionPlanMountNode, + statusNode: releaseArtifactValidationActionPlanStatusNode, + rowsNode: releaseArtifactValidationActionPlanRowsNode, + }) + ), + renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState: () => ( + releaseArtifactValidationActionPlanRenderResult + ), + mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState: () => ( + releaseArtifactValidationActionPlanMountResult + ), + getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState: () => ( + releaseArtifactGateExecutionSummaryRenderState + ), + getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract: + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract, + getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness: () => ( + createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness({ + documentRef: mountDocument, + mountNode: releaseArtifactGateExecutionSummaryMountNode, + statusNode: releaseArtifactGateExecutionSummaryStatusNode, + rowsNode: releaseArtifactGateExecutionSummaryRowsNode, + }) + ), + renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState: () => ( + releaseArtifactGateExecutionSummaryRenderResult + ), + mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState: () => ( + releaseArtifactGateExecutionSummaryMountResult + ), getWorkflowOverviewReleaseReadinessArtifactDisplayViewModel: () => releaseArtifactDisplay, getWorkflowOverviewReleaseReadinessArtifactRenderState: () => releaseArtifactRenderState, getWorkflowOverviewReleaseReadinessArtifactDomContract: createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract,