From d3ee5d49ff5ce965adce2666dfe196c7dd6207ad Mon Sep 17 00:00:00 2001 From: wangdequan Date: Tue, 16 Jun 2026 09:00:29 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B6=E5=8F=A3=20INI=20=E9=9D=A2=E6=9D=BF?= =?UTF-8?q?=20persistence=20summary=20=E5=8F=91=E5=B8=83=E9=97=A8=E7=A6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PROJECT_COMPLETION_TRACKER.md | 12 +- text14.txt | 636 ++++++++++++++++++ wasm-port/docs/panel-entry.md | 25 +- wasm-port/docs/project-release-handoff.md | 11 +- wasm-port/runtime/sdk/README.md | 50 +- wasm-port/runtime/sdk/src/index.js | 13 + wasm-port/runtime/ui/ini-panel/app.js | 28 + wasm-port/runtime/ui/ini-panel/launch.html | 4 + .../ui/ini-panel/panel-state-summary.js | 150 +++++ wasm-port/runtime/ui/ini-panel/ui-shell.js | 128 +++- .../ui/ini-panel/workflow-overview.html | 4 + .../tests/browser/ini_panel_launch_smoke.html | 21 +- ...anel_shell_integration_workflow_smoke.html | 36 +- wasm-port/tests/browser/ini_panel_smoke.html | 41 ++ .../ini_panel_workflow_overview_smoke.html | 29 +- .../tests/sdk/node/verify_sdk_surface.mjs | 118 ++++ .../ui/node/verify_ini_panel_entry_docs.mjs | 3 + .../verify_ini_panel_launch_api_manifest.mjs | 2 + ...y_ini_panel_shell_integration_manifest.mjs | 8 +- .../node/verify_ini_panel_state_summary.mjs | 142 ++++ .../ui/node/verify_ini_panel_ui_shell.mjs | 43 +- 21 files changed, 1458 insertions(+), 46 deletions(-) diff --git a/PROJECT_COMPLETION_TRACKER.md b/PROJECT_COMPLETION_TRACKER.md index 1a01676..f95e3ba 100644 --- a/PROJECT_COMPLETION_TRACKER.md +++ b/PROJECT_COMPLETION_TRACKER.md @@ -9,7 +9,7 @@ turn-by-turn continuation notes and next-batch execution records. ## Current Status - Active continuation file: `text14.txt` -- Latest completed batch: INI panel shell API surface inventory +- Latest completed batch: project release gate validation closure - 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 @@ -42,9 +42,9 @@ The project is complete for the current scope when all of the following are true | 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 `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, INI panel shell/workflow overview helpers, the session readiness workflow report, and the shell API surface inventory with a Node surface smoke. | -| 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, and report OPFS/session readiness before loading or reading WASM session state. | +| 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, INI panel entry/launcher/control-page/shell/workflow overview helpers, the session readiness workflow report, and the shell API surface inventory with a Node surface smoke. | +| 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, and report OPFS/session readiness, OPFS/session persistence summary, 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, and tracker acceptance paths. | @@ -111,9 +111,9 @@ Completion criteria: Work items: - Audit `runtime/sdk/src/index.js`. Done. -- Export stable helpers that external callers should use. Done for core, OPFS/session, sim-config staging, and INI panel shell/workflow overview helpers. +- Export stable helpers that external callers should use. Done for core, OPFS/session, sim-config staging, INI panel entry/launcher/control-page/shell/workflow overview helpers. - Add or update SDK smoke for export shape and manifest compatibility. Done via `tests/sdk/node/verify_sdk_surface.sh`. -- Update `runtime/sdk/README.md` with usage examples and unsupported areas. Initial SDK surface update done; final docs pass still pending. +- Update `runtime/sdk/README.md` with usage examples and unsupported areas. Done for the current SDK surface. Completion criteria: diff --git a/text14.txt b/text14.txt index 53fd925..22219e9 100644 --- a/text14.txt +++ b/text14.txt @@ -2135,3 +2135,639 @@ host_wasm_opfs_browser_smokes=ok 继续以实质能力推进为主。下一批可把 shell API surface inventory 渲染为 browser-visible summary row 或挂载到 workflow overview embedding report,方便验收页面直接显示 API surface 覆盖;仍保持只读, 不新增控制按钮,不解析 G-code,不扩大 LinuxCNC 语义边界。 + +二十七、2026-06-16 继续执行记录:INI panel SDK shell/control-page negotiation surface + +本轮继续以功能实质性推进为主,把已经稳定存在于 UI shell 的 entry manifest、launcher link、 +control-page browser API、shell integration negotiation 与 workflow overview contract helper 纳入 +`runtime/sdk/src/index.js` 稳定导出。外部调用者现在可以只从 SDK 入口完成 launch/control-page/shell +协商,不需要导入 `runtime/ui/ini-panel/` 私有路径。 + +完成内容: + +- `wasm-port/runtime/sdk/src/index.js` re-export: + - `INI_PANEL_ENTRIES`; + - `getIniPanelEntryManifest()`; + - `getIniPanelEntryByHref()`; + - `getVisibleIniPanelEntries()`; + - `createIniPanelLauncherLinkViewModel()`; + - `createControlPageBrowserApiSchema()`; + - `createControlPageBrowserApiManifest()`; + - `createControlPageBrowserApiMethods()`; + - `createIniPanelShellControlPageContract()`; + - `isSupportedIniPanelControlPageApiManifest()`; + - `createIniPanelShellIntegrationSchema()`; + - `createIniPanelShellIntegrationManifest()`; + - `createIniPanelShellIntegrationReadiness()`; + - `createIniPanelShellIntegrationWorkflowPlan()`; + - `isSupportedIniPanelShellIntegrationManifest()`; + - `createIniPanelShellWorkflowOverviewContract()`; +- `wasm-port/tests/sdk/node/verify_sdk_surface.mjs` 覆盖: + - 新 SDK export shape; + - SDK README drift; + - entry manifest 与 visible entries 一致; + - launcher link view-model shape; + - control-page manifest/schema/methods 支持判断; + - shell integration manifest/readiness/workflow plan ready; + - workflow overview contract 包含 API surface inventory method; +- `wasm-port/runtime/sdk/README.md` 的 stable imports 示例和 INI panel shell handoff exports + 记录新增 SDK surface; +- `PROJECT_COMPLETION_TRACKER.md` 同步 latest completed batch 与 SDK/API surface 状态; +- 未新增控制按钮; +- 未执行 action-plan; +- 未解析 G-code; +- 未解释 canonical events; +- 未新增 JS CNC 语义; +- 未运行 opt-in runtime probes; +- 未 promotion blocked runtime family; +- 未改变 LinuxCNC interpreter、tool、parameter、planner、kinematics、remap 或 LinuxCNC-owned + runtime semantics。 + +验证已通过: + +```bash +git diff --check +wasm-port/tests/sdk/node/verify_sdk_surface.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/ui/node/verify_ui_node_smokes.sh +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 +wasm-port/tests/host/verify_host_smokes.sh +``` + +关键输出: + +```text +sdk_surface_node_smoke=ok +vendor sync up to date +standalone CNC semantics guard complete +ui_node_smokes=ok +interp_wasm_node_smoke=ok +sim_configs_wasm_node_inventory_unexpected_fail=0 +browser_ini_launch_smoke=ok +browser_ini_workflow_overview_smoke=ok +browser_ini_shell_integration_workflow_smoke=ok +host_wasm_opfs_browser_smokes=ok +``` + +下一步工作计划: + +继续以实质能力推进为主。下一批可把 shell API surface inventory 渲染为 browser-visible summary +row 或接入 workflow overview embedding report,让验收页面直接显示 API surface 覆盖;仍保持只读, +不新增控制按钮,不解析 G-code,不扩大 LinuxCNC 语义边界。 + +二十八、2026-06-16 继续执行记录:INI panel workflow overview API surface summary row + +本轮继续以功能实质性推进为主,把上一批可调用的 shell API surface inventory 接入真实 workflow +overview 和外部 shell 可见的只读 summary/render path。验收页面现在不只能够通过 API 读取 +`createIniPanelShellApiSurfaceInventory()`,还会在 workflow summary DOM rows 中显示 +`api-surface-inventory` 行,直接暴露 API surface 覆盖状态。 + +完成内容: + +- `createIniPanelShellSessionHandoffWorkflowSummary()` 新增可选 `apiSurfaceInventory` 输入; +- workflow summary 输出新增: + - `apiSurfaceInventory` 摘要对象; + - `api-surface-inventory` display row; +- `createIniPanelShellViewModel()` 构建流程调整为: + - 先生成基础 workflow summary; + - 生成 session readiness workflow report; + - 生成 shell API surface inventory; + - 再生成包含 API surface inventory 行的最终 workflow summary/display/render-state; +- `workflow-overview.html` 现有渲染路径自动显示 `api-surface-inventory` row; +- Browser smoke 覆盖真实 DOM: + - workflow overview 主 DOM row count; + - `data-handoff-workflow-value="api-surface-inventory"`; + - launch iframe workflow overview embedding row count; + - external shell workflow overview embedding row count; + - external shell 手动注入 `apiSurfaceInventory` 后的 workflow DOM mount row count; +- SDK smoke 覆盖 `createIniPanelShellViewModel()` 的 workflow summary 包含 + `api-surface-inventory: ready: 5 rows`; +- `runtime/sdk/README.md`、`docs/panel-entry.md`、`docs/project-release-handoff.md` 记录: + - API surface inventory 可以作为 workflow summary row; + - browser overview 和外部 shell 可直接显示 API 覆盖; +- 未新增控制按钮; +- 未执行 action-plan; +- 未解析 G-code; +- 未解释 canonical events; +- 未新增 JS CNC 语义; +- 未运行 opt-in runtime probes; +- 未 promotion blocked runtime family; +- 未改变 LinuxCNC interpreter、tool、parameter、planner、kinematics、remap 或 LinuxCNC-owned + runtime semantics。 + +验证已通过: + +```bash +git diff --check +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +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 +wasm-port/tests/sdk/node/verify_sdk_surface.sh +wasm-port/tests/ui/node/verify_ini_panel_entry_docs.sh +wasm-port/tests/docs/node/verify_project_release_handoff_docs.sh +wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.sh +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 +wasm-port/tests/host/verify_host_smokes.sh +``` + +关键输出: + +```text +vendor sync up to date +standalone CNC semantics guard complete +ui_node_smokes=ok +browser_ini_workflow_overview_smoke=ok +browser_ini_shell_integration_workflow_smoke=ok +sdk_surface_node_smoke=ok +ini_panel_entry_docs_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +ini_panel_shell_integration_manifest_node_smoke=ok +interp_wasm_node_smoke=ok +sim_configs_wasm_node_inventory_unexpected_fail=0 +host_wasm_opfs_browser_smokes=ok +``` + +下一步工作计划: + +继续以实质能力推进为主。下一批可把 OPFS/session persistence 也做成类似的 browser-visible +summary row 或 report,汇总 machine files、session snapshot、readonly status、handoff readiness; +仍保持只读,不新增控制按钮,不解析 G-code,不扩大 LinuxCNC 语义边界。 + +二十九、2026-06-16 继续执行记录:INI panel OPFS/session persistence summary + +本轮继续以功能实质性推进为主,为 OPFS/session persistence 增加可复用、可由浏览器真实流程读取的 +只读 summary helper。外部调用者和 INI panel 页面现在可以通过稳定 API 一次查看 machine files、 +session snapshot、session readiness、session load、readonly status、handoff readiness 的汇总 rows。 + +完成内容: + +- `panel-state-summary.js` 新增 `createMachineSessionPersistenceSummary()`; +- persistence summary 输出: + - `apiName: "machine-session-persistence-summary"`; + - `summaryVersion`; + - `phase`; + - `ready`; + - `checks`; + - `missing`; + - `counts`; + - rows: + - `opfs`; + - `machine-files`; + - `session-snapshot`; + - `session-readiness`; + - `session-load`; + - `readonly-status`; + - `handoff-readiness`; +- `app.js` 暴露: + - `linuxCncIniPanelApi.getMachineSessionPersistenceSummary()`; + - legacy `window.getMachineSessionPersistenceSummary()`; +- `ui-shell.js` re-export `createMachineSessionPersistenceSummary()`; +- `runtime/sdk/src/index.js` re-export `createMachineSessionPersistenceSummary()`; +- `verify_ini_panel_state_summary.mjs` 覆盖 ready summary 与 missing summary; +- `verify_ini_panel_ui_shell.mjs` 覆盖 ui-shell export; +- `verify_sdk_surface.mjs` 覆盖 SDK export 与 session-load row; +- `ini_panel_smoke.html` 覆盖真实 browser OPFS/session persistence workflow 后的 summary: + - `ready === true`; + - machine files row; + - session load row; +- `runtime/sdk/README.md`、`docs/panel-entry.md`、`docs/project-release-handoff.md` 记录该 helper 和 + browser API; +- `PROJECT_COMPLETION_TRACKER.md` 同步 latest completed batch 与 OPFS/session persistence 状态; +- 未新增控制按钮; +- 未执行 action-plan; +- 未解析 G-code; +- 未解释 canonical events; +- 未新增 JS CNC 语义; +- 未运行 opt-in runtime probes; +- 未 promotion blocked runtime family; +- 未改变 LinuxCNC interpreter、tool、parameter、planner、kinematics、remap 或 LinuxCNC-owned + runtime semantics。 + +验证已通过: + +```bash +git diff --check +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/ui/node/verify_ini_panel_state_summary.sh +wasm-port/tests/ui/node/verify_ini_panel_ui_shell.sh +wasm-port/tests/sdk/node/verify_sdk_surface.sh +SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh +wasm-port/tests/ui/node/verify_ui_node_smokes.sh +wasm-port/tests/ui/node/verify_ini_panel_entry_docs.sh +wasm-port/tests/docs/node/verify_project_release_handoff_docs.sh +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 +wasm-port/tests/host/verify_host_smokes.sh +``` + +关键输出: + +```text +vendor sync up to date +standalone CNC semantics guard complete +ini_panel_state_summary_node_smoke=ok +ini_panel_ui_shell_node_smoke=ok +sdk_surface_node_smoke=ok +browser_ini_opfs_smoke=ok +ui_node_smokes=ok +ini_panel_entry_docs_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +interp_wasm_node_smoke=ok +sim_configs_wasm_node_inventory_unexpected_fail=0 +host_wasm_opfs_browser_smokes=ok +``` + +下一步工作计划: + +继续以实质能力推进为主。下一批可把 `createMachineSessionPersistenceSummary()` 接入 shell handoff +workflow summary 或 workflow overview API surface inventory,让外部 shell 在一个 overview 中同时看到 +API surface 与 OPFS/session persistence 状态;仍保持只读,不新增控制按钮,不解析 G-code,不扩大 +LinuxCNC 语义边界。 + +三十、2026-06-16 继续执行记录:INI panel shell workflow persistence summary row + +本轮继续以功能实质性推进为主,把 OPFS/session persistence summary 接入 shell handoff workflow +summary 和 workflow overview render path。外部 shell 现在可以在同一个 overview 中同时显示 +`persistence-summary` 与 `api-surface-inventory` rows,直接看到 OPFS/session persistence 状态和 API +surface 覆盖。 + +完成内容: + +- `createIniPanelShellSessionHandoffWorkflowSummary()` 新增可选 `persistenceSummary` 输入; +- workflow summary 输出新增: + - `persistenceSummary` 摘要对象; + - `persistence-summary` display row; +- `createIniPanelShellViewModel()` 默认生成 `shellPersistenceSummary`,并将其纳入最终 + shell workflow summary/display/render-state; +- `createIniPanelShellViewModel()` 暴露 `shellPersistenceSummary` 和 + `createMachineSessionPersistenceSummary`; +- Workflow overview browser DOM 默认显示: + - `persistence-summary: waiting: 5 missing`; + - `api-surface-inventory: ready: 5 rows`; +- External shell browser workflow smoke 在真实 panel/control-page workflow 后读取 + `linuxCncIniPanelApi.getMachineSessionPersistenceSummary()`,再注入 + `getShellSessionHandoffWorkflowSummary({ persistenceSummary })`,验证: + - `persistence-summary: ready: 7 rows`; + - workflow DOM mount row count; +- SDK smoke 覆盖 `createIniPanelShellViewModel()` 的 workflow summary 包含 + `persistence-summary` row; +- `runtime/sdk/README.md`、`docs/panel-entry.md`、`docs/project-release-handoff.md` 记录该 workflow + summary row; +- `PROJECT_COMPLETION_TRACKER.md` 同步 latest completed batch 与 Browser/UI workflow 状态; +- 未新增控制按钮; +- 未执行 action-plan; +- 未解析 G-code; +- 未解释 canonical events; +- 未新增 JS CNC 语义; +- 未运行 opt-in runtime probes; +- 未 promotion blocked runtime family; +- 未改变 LinuxCNC interpreter、tool、parameter、planner、kinematics、remap 或 LinuxCNC-owned + runtime semantics。 + +验证已通过: + +```bash +git diff --check +wasm-port/tests/ui/node/verify_ini_panel_entry_docs.sh +wasm-port/tests/docs/node/verify_project_release_handoff_docs.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +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 +wasm-port/tests/ui/node/verify_ui_node_smokes.sh +wasm-port/tests/sdk/node/verify_sdk_surface.sh +SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +``` + +关键输出: + +```text +ini_panel_entry_docs_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +vendor sync up to date +standalone CNC semantics guard complete +interp_wasm_node_smoke=ok +sim_configs_wasm_node_inventory_unexpected_fail=0 +ui_node_smokes=ok +sdk_surface_node_smoke=ok +browser_ini_workflow_overview_smoke=ok +browser_ini_shell_integration_workflow_smoke=ok +host_wasm_opfs_browser_smokes=ok +``` + +下一步工作计划: + +继续以实质能力推进为主。下一批可补齐 release/project docs smoke 对 `persistence-summary` row 的显式 +断言,或开始收口最终提交前的全量 project release gate;仍保持只读,不新增控制按钮,不解析 G-code, +不扩大 LinuxCNC 语义边界。 + +三十一、2026-06-16 继续执行记录:INI panel launch/workflow persistence summary API + +本轮继续以功能实质性推进为主,把 `createMachineSessionPersistenceSummary()` 从 SDK/UI helper 推进为 +真实 launch API 和 workflow overview API surface。外部 shell 现在可以从页面 API 直接构造 OPFS/session +persistence summary,不需要导入私有模块或绕过 browser namespace。 + +完成内容: + +- `createIniPanelLaunchApiManifest().methods` 新增 `getMachineSessionPersistenceSummary`; +- `createIniPanelShellWorkflowOverviewContract().methodNames` 新增 + `getWorkflowOverviewPersistenceSummary`; +- `launch.html` 暴露: + - `linuxCncIniPanelLaunchApi.getMachineSessionPersistenceSummary()`; +- `workflow-overview.html` 暴露: + - `linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewPersistenceSummary()`; +- Browser launch smoke 覆盖 launch API persistence summary API name 与 rows; +- Browser workflow overview smoke 覆盖 workflow overview API persistence summary API name 与 rows; +- UI Node smoke 覆盖 launch manifest、workflow overview contract/readiness 和 shell integration manifest; +- `docs/panel-entry.md`、`docs/project-release-handoff.md` 记录 launch/workflow overview API 暴露该 helper; +- `PROJECT_COMPLETION_TRACKER.md` 同步 latest completed batch; +- 未新增控制按钮; +- 未执行 action-plan; +- 未解析 G-code; +- 未解释 canonical events; +- 未新增 JS CNC 语义; +- 未运行 opt-in runtime probes; +- 未 promotion blocked runtime family; +- 未改变 LinuxCNC interpreter、tool、parameter、planner、kinematics、remap 或 LinuxCNC-owned + runtime semantics。 + +验证已通过: + +```bash +git diff --check +wasm-port/tests/ui/node/verify_ini_panel_entry_docs.sh +wasm-port/tests/docs/node/verify_project_release_handoff_docs.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +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 +wasm-port/tests/ui/node/verify_ini_panel_launch_api_manifest.sh +wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.sh +wasm-port/tests/ui/node/verify_ini_panel_ui_shell.sh +wasm-port/tests/ui/node/verify_ui_node_smokes.sh +wasm-port/tests/sdk/node/verify_sdk_surface.sh +SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +``` + +关键输出: + +```text +ini_panel_entry_docs_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +vendor sync up to date +standalone CNC semantics guard complete +interp_wasm_node_smoke=ok +sim_configs_wasm_node_inventory_unexpected_fail=0 +ini_panel_launch_api_manifest_node_smoke=ok +ini_panel_shell_integration_manifest_node_smoke=ok +ini_panel_ui_shell_node_smoke=ok +ui_node_smokes=ok +sdk_surface_node_smoke=ok +browser_ini_launch_smoke=ok +browser_ini_workflow_overview_smoke=ok +host_wasm_opfs_browser_smokes=ok +``` + +下一步工作计划: + +继续以实质能力推进为主。下一批建议运行完整 project release gate 并根据结果只修实质性 gate 缺口; +仍保持只读,不新增控制按钮,不解析 G-code,不扩大 LinuxCNC 语义边界。 + +三十二、2026-06-16 继续执行记录:INI panel API inventory persistence summary rows + +本轮继续以功能实质性推进为主,把 OPFS/session persistence summary 纳入 shell API surface inventory。 +外部 shell 现在通过 `getShellApiSurfaceInventory()` 或 +`getWorkflowOverviewApiSurfaceInventory()` 可以同时看到 API 方法覆盖、session readiness report rows +和 persistence summary rows。 + +完成内容: + +- `createIniPanelShellApiSurfaceInventory()` 新增可选 `persistenceSummary` 输入; +- inventory `counts` 新增 `persistenceSummaryRows`; +- inventory `apiNames` 新增 `persistenceSummary`; +- inventory `entries` 新增 `persistence-summary`; +- inventory `rows` 新增 `persistence-summary`; +- `createIniPanelShellViewModel()` 将 `shellPersistenceSummary` 注入 `shellApiSurfaceInventory`; +- Browser launch smoke 覆盖 `shellApiSurfaceInventory.counts.persistenceSummaryRows`; +- Browser workflow overview smoke 覆盖 workflow overview API inventory 的 persistence summary rows; +- Browser shell integration workflow smoke 在真实 panel summary 下把 persistence summary 注入 + `getShellApiSurfaceInventory()` 并验证 row count; +- SDK/UI node smoke 覆盖 workflow summary 的 API inventory row 从 `ready: 5 rows` 推进为 + `ready: 6 rows`; +- `runtime/sdk/README.md`、`docs/panel-entry.md` 记录 inventory 包含 persistence summary row counts; +- `PROJECT_COMPLETION_TRACKER.md` 同步 latest completed batch; +- 未新增控制按钮; +- 未执行 action-plan; +- 未解析 G-code; +- 未解释 canonical events; +- 未新增 JS CNC 语义; +- 未运行 opt-in runtime probes; +- 未 promotion blocked runtime family; +- 未改变 LinuxCNC interpreter、tool、parameter、planner、kinematics、remap 或 LinuxCNC-owned + runtime semantics。 + +验证已通过: + +```bash +git diff --check +wasm-port/tests/ui/node/verify_ini_panel_entry_docs.sh +wasm-port/tests/docs/node/verify_project_release_handoff_docs.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +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 +wasm-port/tests/ui/node/verify_ini_panel_ui_shell.sh +wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.sh +wasm-port/tests/ui/node/verify_ui_node_smokes.sh +wasm-port/tests/sdk/node/verify_sdk_surface.sh +SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +``` + +关键输出: + +```text +ini_panel_entry_docs_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +vendor sync up to date +standalone CNC semantics guard complete +interp_wasm_node_smoke=ok +sim_configs_wasm_node_inventory_unexpected_fail=0 +ini_panel_ui_shell_node_smoke=ok +ini_panel_shell_integration_manifest_node_smoke=ok +ui_node_smokes=ok +sdk_surface_node_smoke=ok +browser_ini_launch_smoke=ok +browser_ini_workflow_overview_smoke=ok +browser_ini_shell_integration_workflow_smoke=ok +host_wasm_opfs_browser_smokes=ok +``` + +下一步工作计划: + +继续以实质能力推进为主。下一批建议运行完整 project release gate,若通过则整理最终提交;若失败, +只修实质性 gate 缺口;仍保持只读,不新增控制按钮,不解析 G-code,不扩大 LinuxCNC 语义边界。 + +三十三、2026-06-16 继续执行记录:INI panel persistence summary display/render state + +本轮继续以功能实质性推进为主,把 OPFS/session persistence summary 从数据 report 推进为可直接显示的 +display view-model 和 render-state。外部 shell 现在可以拿到稳定标题、状态、明细、rows、statusLine +和 dataset 字段,不需要自行拼接 persistence 状态文案。 + +完成内容: + +- `panel-state-summary.js` 新增: + - `createMachineSessionPersistenceDisplayViewModel()`; + - `createMachineSessionPersistenceRenderState()`; +- persistence render-state 输出: + - `apiName: "machine-session-persistence-render-state"`; + - `renderStateVersion`; + - `phase`; + - `ready`; + - `title`; + - `statusText`; + - `detailText`; + - `statusLine`; + - `rows`; + - `dataset.persistencePhase`; + - `dataset.persistenceReady`; + - `dataset.persistenceScope`; +- `app.js` 暴露: + - `linuxCncIniPanelApi.getMachineSessionPersistenceDisplayViewModel()`; + - `linuxCncIniPanelApi.getMachineSessionPersistenceRenderState()`; + - legacy window getters; +- `ui-shell.js` re-export 新 display/render helpers; +- `runtime/sdk/src/index.js` re-export 新 display/render helpers; +- `verify_ini_panel_state_summary.mjs` 覆盖 ready display view-model、ready render-state 和 waiting + render-state; +- `ini_panel_smoke.html` 覆盖真实 browser OPFS/session workflow 后的 display/render state; +- `verify_sdk_surface.mjs` 覆盖 SDK export 和 render-state API name; +- `docs/panel-entry.md`、`docs/project-release-handoff.md`、`runtime/sdk/README.md` 记录新 helper; +- `PROJECT_COMPLETION_TRACKER.md` 同步 latest completed batch; +- 未新增控制按钮; +- 未执行 action-plan; +- 未解析 G-code; +- 未解释 canonical events; +- 未新增 JS CNC 语义; +- 未运行 opt-in runtime probes; +- 未 promotion blocked runtime family; +- 未改变 LinuxCNC interpreter、tool、parameter、planner、kinematics、remap 或 LinuxCNC-owned + runtime semantics。 + +验证已通过: + +```bash +git diff --check +wasm-port/tests/docs/node/verify_project_release_handoff_docs.sh +wasm-port/tests/ui/node/verify_ui_node_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +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 +wasm-port/tests/ui/node/verify_ini_panel_state_summary.sh +wasm-port/tests/ui/node/verify_ini_panel_ui_shell.sh +wasm-port/tests/sdk/node/verify_sdk_surface.sh +wasm-port/tests/ui/node/verify_ini_panel_entry_docs.sh +SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +``` + +关键输出: + +```text +project_release_handoff_docs_node_smoke=ok +ui_node_smokes=ok +vendor sync up to date +standalone CNC semantics guard complete +interp_wasm_node_smoke=ok +sim_configs_wasm_node_inventory_unexpected_fail=0 +ini_panel_state_summary_node_smoke=ok +ini_panel_ui_shell_node_smoke=ok +sdk_surface_node_smoke=ok +ini_panel_entry_docs_node_smoke=ok +browser_ini_opfs_smoke=ok +host_wasm_opfs_browser_smokes=ok +``` + +下一步工作计划: + +继续以实质能力推进为主。下一批建议运行完整 project release gate,若通过则整理最终提交;若失败, +只修实质性 gate 缺口;仍保持只读,不新增控制按钮,不解析 G-code,不扩大 LinuxCNC 语义边界。 + +三十四、2026-06-16 继续执行记录:project release gate validation closure + +本轮按上一批计划运行完整 project release gate,把当前 OPFS/session persistence display/render helper、 +SDK/API surface、browser/UI workflow、docs smoke、host/runtime boundary smoke 一次性收口到可交付验证状态。 + +完成内容: + +- 运行 `wasm-port/tests/host/verify_project_release_gate.sh`; +- 验证当前未提交实现可通过统一发布门禁; +- 确认 browser INI panel、control page、launch、workflow overview、shell integration workflow smoke 仍通过; +- 确认 SDK surface、OPFS/session docs、sim-config coverage docs、host/runtime boundary docs、project release handoff docs + 均在 host aggregate gate 中通过; +- 确认 `browser_interp_smoke=ok` 仍通过; +- 确认 sim config inventory 仍为: + - `executed=28`; + - `passed=28`; + - `skipped=131`; + - `unexpected_fail=0`; +- 确认 blocked runtime family 未 promotion: + - `L4-USER-M-PROCESS`; + - `L4-TOOL-DB`; + - `L4-PYTHON-REMAP`; +- 未新增控制按钮; +- 未执行 action-plan; +- 未解析 G-code; +- 未解释 canonical events; +- 未新增 JS CNC 语义; +- 未运行 opt-in runtime probes; +- 未改变 LinuxCNC interpreter、tool、parameter、planner、kinematics、remap 或 LinuxCNC-owned + runtime semantics。 + +验证已通过: + +```bash +wasm-port/tests/host/verify_project_release_gate.sh +``` + +关键输出: + +```text +vendor sync up to date +standalone CNC semantics guard complete +interp_wasm_node_smoke=ok +sim_configs_wasm_node_inventory_unexpected_fail=0 +browser_ini_opfs_smoke=ok +browser_ini_control_page_smoke=ok +browser_ini_launch_smoke=ok +browser_ini_workflow_overview_smoke=ok +browser_ini_shell_integration_workflow_smoke=ok +ui_node_smokes=ok +ini_wasm_node_smoke=ok +sim_configs_wasm_node_smoke=ok +nc_files_wasm_node_smoke=ok +tp_wasm_node_smoke=ok +opfs_file_service_node_smoke=ok +sdk_surface_node_smoke=ok +opfs_session_docs_node_smoke=ok +sim_configs_coverage_docs_node_smoke=ok +host_runtime_boundary_docs_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +browser_interp_smoke=ok +host_wasm_opfs_browser_smokes=ok +project_release_gate=ok +``` + +下一步工作计划: + +整理并提交当前批次。提交后若工作树 clean,则当前 scope 已达到 `PROJECT_COMPLETION_TRACKER.md` +定义的发布验证闭环;后续只在新 scope 请求或真实 LinuxCNC host runtime 可用时继续推进 blocked runtime family。 diff --git a/wasm-port/docs/panel-entry.md b/wasm-port/docs/panel-entry.md index 3d7174d..4020d5f 100644 --- a/wasm-port/docs/panel-entry.md +++ b/wasm-port/docs/panel-entry.md @@ -76,6 +76,9 @@ import { createIniPanelShellViewModel, getIniPanelEntryByHref, getVisibleIniPanelEntries, + createMachineSessionPersistenceDisplayViewModel, + createMachineSessionPersistenceRenderState, + createMachineSessionPersistenceSummary, createMachineSessionControlPageController, createMachineSessionReadonlyStatus, createMachineSessionReadonlyStatusApiManifest, @@ -138,6 +141,17 @@ needs a read-only `ready`/`blocked` readiness report before loading a machine session into WASM. `createMachineSessionReadinessFieldState()` and `machineSessionReadinessLogLines()` turn that report into stable fields and status text without parsing G-code or executing LinuxCNC behavior. +Use `createMachineSessionPersistenceSummary()` or +`linuxCncIniPanelApi.getMachineSessionPersistenceSummary()` when a browser +panel or outer shell needs one read-only OPFS/session persistence summary with +machine-file, session snapshot, session readiness, session load, readonly +status, and handoff readiness rows. The launch page exposes the same helper as +`linuxCncIniPanelLaunchApi.getMachineSessionPersistenceSummary()`, and the +workflow overview page exposes it as +`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewPersistenceSummary()`. +Use `createMachineSessionPersistenceDisplayViewModel()` and +`createMachineSessionPersistenceRenderState()` when that summary needs stable +display text, rows, status line, and dataset fields for caller-owned DOM. Use `createIniPanelShellControlPageContract()` or `controlPage.contract` when an outer shell needs a fast contract check for `createController`, `loadSessionState`, `readStatus`, `refresh`, `renderView`, the readonly status @@ -306,9 +320,16 @@ the same report through Use `createIniPanelShellApiSurfaceInventory()` or `linuxCncIniPanelLaunchApi.getShellApiSurfaceInventory()` when an outer shell needs a machine-readable inventory of launch, control-page, workflow overview, -persistence, and session readiness report API counts. The workflow overview -page exposes the same inventory through +persistence capabilities, session readiness report rows, and persistence +summary rows. The workflow overview page exposes the same inventory through `linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewApiSurfaceInventory()`. +When provided to `createIniPanelShellSessionHandoffWorkflowSummary()`, the +inventory is also rendered as the read-only `api-surface-inventory` workflow +summary row for browser overview pages and external shells. The same workflow +summary accepts `persistenceSummary` from +`createMachineSessionPersistenceSummary()` and renders it as the +`persistence-summary` row, so a shell overview can show API coverage and +OPFS/session persistence state together. Use `createIniPanelShellSessionHandoffWorkflowDisplayViewModel()` or `linuxCncIniPanelLaunchApi.getShellSessionHandoffWorkflowDisplayViewModel()` diff --git a/wasm-port/docs/project-release-handoff.md b/wasm-port/docs/project-release-handoff.md index 8c27a10..d7ed566 100644 --- a/wasm-port/docs/project-release-handoff.md +++ b/wasm-port/docs/project-release-handoff.md @@ -32,7 +32,16 @@ continuation records are in `../text14.txt`. - SDK callers should import stable helpers from `runtime/sdk/src/index.js`, including `createIniPanelShellApiSurfaceInventory()` for API discovery and `createIniPanelShellSessionReadinessWorkflowReport()` when they need - machine-readable session readiness phase and missing reasons. + machine-readable session readiness phase and missing reasons. The workflow + overview exposes the API surface inventory as a read-only summary row. +- OPFS/session callers can use `createMachineSessionPersistenceSummary()` or + `linuxCncIniPanelApi.getMachineSessionPersistenceSummary()` to inspect + machine files, session snapshot, session readiness, session load, readonly + status, and shell handoff readiness as stable rows. Shell handoff workflow + summaries can include that report as the `persistence-summary` row beside the + API surface inventory row, and launch/workflow-overview APIs expose the same + helper for external shells. The summary also has display and render-state + helpers for caller-owned DOM. - OPFS/session persistence is host-side storage glue. Parameter and tool-table behavior still comes from the LinuxCNC-backed interpreter SDK calls. - Sim-config inventory promotion remains evidence-driven and must keep diff --git a/wasm-port/runtime/sdk/README.md b/wasm-port/runtime/sdk/README.md index 549fa3a..5e93862 100644 --- a/wasm-port/runtime/sdk/README.md +++ b/wasm-port/runtime/sdk/README.md @@ -23,22 +23,41 @@ Use `src/index.js` for stable imports: ```js import { + INI_PANEL_ENTRIES, analyzeIniRuntimeBoundaries, + createControlPageBrowserApiManifest, + createControlPageBrowserApiMethods, + createControlPageBrowserApiSchema, createIniPanelLaunchApiManifest, + createIniPanelLauncherLinkViewModel, createIniPanelShellApiSurfaceInventory, + createIniPanelShellControlPageContract, + createIniPanelShellIntegrationManifest, + createIniPanelShellIntegrationReadiness, + createIniPanelShellIntegrationSchema, + createIniPanelShellIntegrationWorkflowPlan, createIniPanelShellSessionReadinessWorkflowReport, createIniPanelShellViewModel, + createIniPanelShellWorkflowOverviewContract, createIniPanelShellWorkflowOverviewEmbeddingMountDomContract, createIniPanelShellWorkflowOverviewEmbeddingMountDomReadiness, createLinuxCncIniSdk, createLinuxCncInterpSdk, + createMachineSessionPersistenceDisplayViewModel, + createMachineSessionPersistenceRenderState, + createMachineSessionPersistenceSummary, createMachineSessionSnapshotPayload, createSessionSnapshot, defaultMachinePaths, gcodeFilenameFromProgramPath, gcodeProgramPath, + getIniPanelEntryByHref, + getIniPanelEntryManifest, getOpfsRoot, + getVisibleIniPanelEntries, + isSupportedIniPanelControlPageApiManifest, isSupportedIniPanelLaunchApiManifest, + isSupportedIniPanelShellIntegrationManifest, loadMachineSessionFromOpfs, loadMachineSessionSnapshot, loadMachineTextFiles, @@ -189,13 +208,42 @@ effects still come from the LinuxCNC-backed interpreter SDK methods they call. The SDK entrypoint also re-exports the stable INI panel shell handoff surface for external browser shells: +- `INI_PANEL_ENTRIES`, `getIniPanelEntryManifest()`, + `getIniPanelEntryByHref()`, `getVisibleIniPanelEntries()`, and + `createIniPanelLauncherLinkViewModel()` for discovering launch pages and + rendering shell-owned launcher links from the stable entry manifest. - `createIniPanelLaunchApiManifest()` and `isSupportedIniPanelLaunchApiManifest()` for launch API negotiation. +- `createControlPageBrowserApiSchema()`, + `createControlPageBrowserApiManifest()`, + `createControlPageBrowserApiMethods()`, + `createIniPanelShellControlPageContract()`, and + `isSupportedIniPanelControlPageApiManifest()` for read-only control-page API + negotiation. +- `createIniPanelShellIntegrationSchema()`, + `createIniPanelShellIntegrationManifest()`, + `createIniPanelShellIntegrationReadiness()`, + `createIniPanelShellIntegrationWorkflowPlan()`, and + `isSupportedIniPanelShellIntegrationManifest()` for checking launch and + control-page compatibility before embedding. - `createIniPanelShellViewModel()` for a complete read-only shell integration bundle. +- `createMachineSessionPersistenceSummary()` for a machine-readable OPFS/session + persistence summary covering machine files, session snapshot, session + readiness, session load, readonly status, and shell handoff readiness rows. + `createMachineSessionPersistenceDisplayViewModel()` and + `createMachineSessionPersistenceRenderState()` turn the summary into stable + display and render data. + `createIniPanelShellSessionHandoffWorkflowSummary()` can include this as the + read-only `persistence-summary` row. +- `createIniPanelShellWorkflowOverviewContract()` for discovering the workflow + overview iframe API contract. - `createIniPanelShellApiSurfaceInventory()` for a machine-readable inventory of launch, control-page, workflow overview, persistence, and session - readiness report API counts. + readiness report API counts, including persistence summary row counts. + `createIniPanelShellViewModel()` also includes + this inventory as a read-only workflow summary row so browser overview pages + and external shells can display API coverage without private imports. - `createIniPanelShellSessionReadinessWorkflowReport()` for a machine-readable session readiness workflow report with phase, missing reasons, status line, and stable rows. diff --git a/wasm-port/runtime/sdk/src/index.js b/wasm-port/runtime/sdk/src/index.js index 2926985..c2f2675 100644 --- a/wasm-port/runtime/sdk/src/index.js +++ b/wasm-port/runtime/sdk/src/index.js @@ -54,12 +54,21 @@ export { loadMachineSessionFromOpfs } from "../../opfs/linuxcnc-machine-session- export { readMachineSessionReadiness } from "../../opfs/machine-session-readiness.js"; export { + INI_PANEL_ENTRIES, + createControlPageBrowserApiManifest, + createControlPageBrowserApiMethods, + createControlPageBrowserApiSchema, createIniPanelLaunchApiManifest, createIniPanelLaunchApiSchema, + createIniPanelLauncherLinkViewModel, + createMachineSessionPersistenceDisplayViewModel, + createMachineSessionPersistenceRenderState, + createMachineSessionPersistenceSummary, createIniPanelShellIntegrationManifest, createIniPanelShellIntegrationReadiness, createIniPanelShellIntegrationSchema, createIniPanelShellIntegrationWorkflowPlan, + createIniPanelShellControlPageContract, createIniPanelShellSessionHandoffCompatibilityReport, createIniPanelShellSessionHandoffPackage, createIniPanelShellSessionHandoffPackageMountDisplayViewModel, @@ -84,6 +93,10 @@ export { createIniPanelShellWorkflowOverviewEmbeddingRenderState, createIniPanelShellWorkflowOverviewEmbeddingReport, createIniPanelShellWorkflowOverviewReadiness, + getIniPanelEntryByHref, + getIniPanelEntryManifest, + getVisibleIniPanelEntries, + isSupportedIniPanelControlPageApiManifest, isSupportedIniPanelLaunchApiManifest, isSupportedIniPanelShellIntegrationManifest, isSupportedIniPanelShellSessionHandoffPackage, diff --git a/wasm-port/runtime/ui/ini-panel/app.js b/wasm-port/runtime/ui/ini-panel/app.js index 062b547..1051d38 100644 --- a/wasm-port/runtime/ui/ini-panel/app.js +++ b/wasm-port/runtime/ui/ini-panel/app.js @@ -46,6 +46,9 @@ import { } from "./session-workflow.js"; import { createMachineSessionControlView, + createMachineSessionPersistenceDisplayViewModel, + createMachineSessionPersistenceRenderState, + createMachineSessionPersistenceSummary, createMachineSessionStateSnapshot, createMachineSessionStateBundle, createMachineSessionStateReport, @@ -171,6 +174,25 @@ function getMachineSessionWorkflowStatus() { return createMachineSessionWorkflowStatus(window.linuxCncIniPanelMachineSessionState); } +function getMachineSessionPersistenceSummary(options = {}) { + return createMachineSessionPersistenceSummary({ + state: window.linuxCncIniPanelMachineSessionState, + ...options, + }); +} + +function getMachineSessionPersistenceDisplayViewModel(options = {}) { + return createMachineSessionPersistenceDisplayViewModel( + getMachineSessionPersistenceSummary(options), + ); +} + +function getMachineSessionPersistenceRenderState(options = {}) { + return createMachineSessionPersistenceRenderState( + getMachineSessionPersistenceDisplayViewModel(options), + ); +} + function getMachineSessionStateBundle() { return createMachineSessionStateBundle(window.linuxCncIniPanelMachineSessionState); } @@ -203,6 +225,9 @@ window.linuxCncIniPanelApi = { getMachineSessionStateReport, getMachineSessionStateReportExport, getMachineSessionWorkflowStatus, + getMachineSessionPersistenceSummary, + getMachineSessionPersistenceDisplayViewModel, + getMachineSessionPersistenceRenderState, renderMachineSessionControlView: renderCurrentMachineSessionControlView, }; window.getMachineSessionControlView = getMachineSessionControlView; @@ -210,6 +235,9 @@ window.getMachineSessionStateBundle = getMachineSessionStateBundle; window.getMachineSessionStateReport = getMachineSessionStateReport; window.getMachineSessionStateReportExport = getMachineSessionStateReportExport; window.getMachineSessionWorkflowStatus = getMachineSessionWorkflowStatus; +window.getMachineSessionPersistenceSummary = getMachineSessionPersistenceSummary; +window.getMachineSessionPersistenceDisplayViewModel = getMachineSessionPersistenceDisplayViewModel; +window.getMachineSessionPersistenceRenderState = getMachineSessionPersistenceRenderState; window.renderMachineSessionControlView = renderCurrentMachineSessionControlView; window.linuxCncIniPanelMachineSessionState = panelMachineSessionState; renderCurrentMachineSessionControlView(); diff --git a/wasm-port/runtime/ui/ini-panel/launch.html b/wasm-port/runtime/ui/ini-panel/launch.html index f99d7b7..854eef3 100644 --- a/wasm-port/runtime/ui/ini-panel/launch.html +++ b/wasm-port/runtime/ui/ini-panel/launch.html @@ -116,6 +116,7 @@ mountIniPanelShellSessionHandoff, mountIniPanelShellSessionHandoffFromControlPage, readIniPanelShellSessionHandoffStatusFromControlPage, + createMachineSessionPersistenceSummary, createIniPanelShellSessionHandoffWorkflowSummary, createIniPanelShellSessionReadinessWorkflowReport, createIniPanelShellSessionHandoffWorkflowDisplayViewModel, @@ -229,6 +230,9 @@ ...(options ?? {}), }) ), + getMachineSessionPersistenceSummary: (options) => ( + createMachineSessionPersistenceSummary(options) + ), getShellSessionHandoffWorkflowSummary: (options) => ( createIniPanelShellSessionHandoffWorkflowSummary(options ?? { mountWorkflow: { diff --git a/wasm-port/runtime/ui/ini-panel/panel-state-summary.js b/wasm-port/runtime/ui/ini-panel/panel-state-summary.js index 251cc5a..1ec9092 100644 --- a/wasm-port/runtime/ui/ini-panel/panel-state-summary.js +++ b/wasm-port/runtime/ui/ini-panel/panel-state-summary.js @@ -156,6 +156,156 @@ export function createMachineSessionWorkflowStatus(state = {}) { }; } +export function createMachineSessionPersistenceSummary({ + state = {}, + report = createMachineSessionStateReport(state), + workflowStatus = createMachineSessionWorkflowStatus(state), + readonlyStatus = null, + handoffSummary = null, + handoffWorkflowSummary = null, +} = {}) { + const readiness = report.readiness ?? {}; + const machineFiles = state.machineFiles ?? {}; + const session = report.session ?? {}; + const loaded = report.loaded ?? {}; + const sessionReadiness = report.sessionReadiness ?? workflowStatus.sessionReadiness ?? null; + const opfsReady = String(readiness.opfs ?? "").toLowerCase().includes("ready"); + const machineFileLabels = [ + machineFiles.iniOpfsPath || session.ini ? "ini" : null, + machineFiles.parameterOpfsPath || session.parameters ? "parameters" : null, + machineFiles.toolTableOpfsPath || session.toolTable ? "tool-table" : null, + machineFiles.gcodeOpfsPath || session.gcode ? "g-code" : null, + ].filter(Boolean); + const machineFilesReady = machineFileLabels.length > 0; + const snapshotReady = Boolean(session.snapshotLabel); + const sessionReadinessReady = sessionReadiness?.ready === true; + const sessionLoadReady = Boolean(loaded.iniWasmPath); + const readonlyStatusValue = readonlyStatus + ? (readonlyStatus.ready === true ? "ready" : readonlyStatus.phase ?? "available") + : "not-provided"; + const handoffValue = handoffWorkflowSummary + ? (handoffWorkflowSummary.ready === true ? "ready" : handoffWorkflowSummary.phase ?? "blocked") + : ( + handoffSummary + ? (handoffSummary.ready === true ? "ready" : handoffSummary.phase ?? "blocked") + : "not-provided" + ); + const checks = { + opfs: opfsReady, + machineFiles: machineFilesReady, + sessionSnapshot: snapshotReady, + sessionReadiness: sessionReadinessReady, + sessionLoad: sessionLoadReady, + }; + const missing = Object.entries(checks) + .filter(([, passed]) => !passed) + .map(([name]) => name); + const ready = missing.length === 0; + + return { + apiName: "machine-session-persistence-summary", + summaryVersion: 1, + phase: ready ? "ready" : "waiting", + ready, + checks, + missing, + counts: { + machineFiles: machineFileLabels.length, + missing: missing.length, + }, + rows: [ + { + id: "opfs", + label: "OPFS", + value: readiness.opfs ?? "missing", + }, + { + id: "machine-files", + label: "Machine files", + value: machineFileLabels.length > 0 ? machineFileLabels.join(", ") : "missing", + }, + { + id: "session-snapshot", + label: "Session snapshot", + value: session.snapshotLabel ?? "missing", + }, + { + id: "session-readiness", + label: "Session readiness", + value: sessionReadiness + ? (sessionReadiness.ready ? "ready" : `blocked: ${sessionReadiness.missing?.join(", ") || "unknown"}`) + : "missing", + }, + { + id: "session-load", + label: "Session load", + value: loaded.iniWasmPath ? `loaded: ${loaded.iniWasmPath}` : "missing", + }, + { + id: "readonly-status", + label: "Readonly status", + value: readonlyStatusValue, + }, + { + id: "handoff-readiness", + label: "Handoff readiness", + value: handoffValue, + }, + ], + }; +} + +export function createMachineSessionPersistenceDisplayViewModel( + summary = createMachineSessionPersistenceSummary(), +) { + const missing = Array.isArray(summary?.missing) ? summary.missing : []; + const rows = Array.isArray(summary?.rows) + ? summary.rows.map(({ id, label, value }) => ({ + id, + label, + value: value == null ? "" : String(value), + })) + : []; + return { + phase: summary?.phase === "ready" ? "ready" : "waiting", + title: "OPFS/session persistence", + statusText: summary?.ready ? "Ready" : "Waiting", + detailText: missing.length > 0 ? missing.join(", ") : "No missing persistence requirements", + rows, + }; +} + +export function createMachineSessionPersistenceRenderState( + displayViewModel = createMachineSessionPersistenceDisplayViewModel(), +) { + const rows = Array.isArray(displayViewModel?.rows) + ? displayViewModel.rows.map(({ id, label, value }) => ({ + id, + label, + value: value == null ? "" : String(value), + })) + : []; + const ready = displayViewModel?.phase === "ready" && displayViewModel?.statusText === "Ready"; + const statusText = displayViewModel?.statusText ?? (ready ? "Ready" : "Waiting"); + const detailText = displayViewModel?.detailText ?? "No missing persistence requirements"; + return { + apiName: "machine-session-persistence-render-state", + renderStateVersion: 1, + phase: ready ? "ready" : "waiting", + ready, + title: displayViewModel?.title ?? "OPFS/session persistence", + statusText, + detailText, + statusLine: `${statusText}: ${detailText}`, + rows, + dataset: { + persistencePhase: ready ? "ready" : "waiting", + persistenceReady: ready ? "true" : "false", + persistenceScope: "opfs-session", + }, + }; +} + export function createMachineSessionWorkflowOverview(report = {}) { const readiness = report.readiness ?? {}; const loaded = report.loaded ?? {}; diff --git a/wasm-port/runtime/ui/ini-panel/ui-shell.js b/wasm-port/runtime/ui/ini-panel/ui-shell.js index 53d9f38..0b20bfb 100644 --- a/wasm-port/runtime/ui/ini-panel/ui-shell.js +++ b/wasm-port/runtime/ui/ini-panel/ui-shell.js @@ -28,6 +28,9 @@ import { createMachineSessionReadonlyStatusApiManifest, readMachineSessionReadonlyStatus, } from "./readonly-status-api.js"; +import { + createMachineSessionPersistenceSummary, +} from "./panel-state-summary.js"; export { controlPageStatusText, createControlPageRefreshState, @@ -58,6 +61,17 @@ export { export { renderMachineSessionControlView, } from "./control-view-renderer.js"; +export { + createMachineSessionControlView, + createMachineSessionPersistenceDisplayViewModel, + createMachineSessionPersistenceRenderState, + createMachineSessionPersistenceSummary, + createMachineSessionStateBundle, + createMachineSessionStateReport, + createMachineSessionStateReportExport, + createMachineSessionWorkflowOverview, + createMachineSessionWorkflowStatus, +} from "./panel-state-summary.js"; export function getVisibleIniPanelEntries() { return getIniPanelEntryManifest(); @@ -159,6 +173,7 @@ export function createIniPanelLaunchApiManifest(entries = getVisibleIniPanelEntr "mountShellSessionHandoff", "mountShellSessionHandoffFromControlPage", "readShellSessionHandoffStatusFromControlPage", + "getMachineSessionPersistenceSummary", "getShellSessionHandoffWorkflowSummary", "getShellSessionReadinessWorkflowReport", "getShellApiSurfaceInventory", @@ -1428,6 +1443,8 @@ export function createIniPanelShellSessionHandoffWorkflowSummary({ statusLine: "Blocked: readonly status unavailable", missingStatusFields: [], }, + persistenceSummary = null, + apiSurfaceInventory = null, } = {}) { const sessionReadiness = readonlyStatusResult?.readonlyStatus?.sessionReadiness ?? null; const sessionReadinessMissing = Array.isArray(sessionReadiness?.missing) @@ -1456,6 +1473,32 @@ export function createIniPanelShellSessionHandoffWorkflowSummary({ blockingReasons.push(`readonly-status:${readonlyDetail}`); } const ready = blockingReasons.length === 0; + const apiSurfaceRows = Array.isArray(apiSurfaceInventory?.rows) + ? apiSurfaceInventory.rows + : []; + const apiSurfaceValue = apiSurfaceInventory + ? ( + apiSurfaceInventory.ready + ? `ready: ${apiSurfaceRows.length} rows` + : `blocked: ${apiSurfaceInventory.phase ?? "unknown"}` + ) + : "missing"; + const apiSurfaceCounts = apiSurfaceInventory?.counts && typeof apiSurfaceInventory.counts === "object" + ? { ...apiSurfaceInventory.counts } + : null; + const persistenceRows = Array.isArray(persistenceSummary?.rows) + ? persistenceSummary.rows + : []; + const persistenceMissing = Array.isArray(persistenceSummary?.missing) + ? persistenceSummary.missing + : []; + const persistenceValue = persistenceSummary + ? ( + persistenceSummary.ready + ? `ready: ${persistenceRows.length} rows` + : `${persistenceSummary.phase ?? "waiting"}: ${persistenceMissing.length} missing` + ) + : "missing"; return { apiName: "ini-panel-shell-session-handoff-workflow-summary", @@ -1490,6 +1533,22 @@ export function createIniPanelShellSessionHandoffWorkflowSummary({ missing: sessionReadinessMissing, } : null, + apiSurfaceInventory: apiSurfaceInventory + ? { + phase: apiSurfaceInventory.phase ?? (apiSurfaceInventory.ready ? "ready" : "blocked"), + ready: apiSurfaceInventory.ready === true, + counts: apiSurfaceCounts, + apiNames: apiSurfaceInventory.apiNames ? { ...apiSurfaceInventory.apiNames } : null, + } + : null, + persistenceSummary: persistenceSummary + ? { + phase: persistenceSummary.phase ?? (persistenceSummary.ready ? "ready" : "waiting"), + ready: persistenceSummary.ready === true, + counts: persistenceSummary.counts ? { ...persistenceSummary.counts } : null, + missing: persistenceMissing, + } + : null, blockingReasons, rows: [ { @@ -1520,6 +1579,16 @@ export function createIniPanelShellSessionHandoffWorkflowSummary({ ? readonlyStatusResult.missingStatusFields.join(", ") : "none", }, + { + id: "persistence-summary", + label: "Persistence summary", + value: persistenceValue, + }, + { + id: "api-surface-inventory", + label: "API surface inventory", + value: apiSurfaceValue, + }, { id: "blocking-reasons", label: "Blocking reasons", @@ -1609,6 +1678,7 @@ export function createIniPanelShellApiSurfaceInventory({ controlPageApiManifest = createControlPageBrowserApiManifest(), workflowOverviewContract = createIniPanelShellWorkflowOverviewContract(), sessionReadinessWorkflowReport = createIniPanelShellSessionReadinessWorkflowReport(), + persistenceSummary = createMachineSessionPersistenceSummary(), } = {}) { const launchMethods = Array.isArray(launchApiManifest?.methods) ? launchApiManifest.methods : []; const controlPageMethods = Array.isArray(controlPageApiManifest?.methods) ? controlPageApiManifest.methods : []; @@ -1621,11 +1691,15 @@ export function createIniPanelShellApiSurfaceInventory({ const sessionReadinessRows = Array.isArray(sessionReadinessWorkflowReport?.rows) ? sessionReadinessWorkflowReport.rows : []; + const persistenceSummaryRows = Array.isArray(persistenceSummary?.rows) + ? persistenceSummary.rows + : []; const ready = Boolean( launchApiManifest?.apiName && controlPageApiManifest?.apiName && workflowOverviewContract?.apiName - && sessionReadinessWorkflowReport?.apiName, + && sessionReadinessWorkflowReport?.apiName + && persistenceSummary?.apiName, ); return { @@ -1639,12 +1713,14 @@ export function createIniPanelShellApiSurfaceInventory({ workflowOverviewMethods: workflowOverviewMethods.length, persistenceCapabilities: persistenceCapabilities.length, sessionReadinessRows: sessionReadinessRows.length, + persistenceSummaryRows: persistenceSummaryRows.length, }, apiNames: { launch: launchApiManifest?.apiName ?? null, controlPage: controlPageApiManifest?.apiName ?? null, workflowOverview: workflowOverviewContract?.apiName ?? null, sessionReadinessReport: sessionReadinessWorkflowReport?.apiName ?? null, + persistenceSummary: persistenceSummary?.apiName ?? null, }, entries: [ { @@ -1671,6 +1747,12 @@ export function createIniPanelShellApiSurfaceInventory({ apiName: sessionReadinessWorkflowReport?.apiName ?? null, count: sessionReadinessRows.length, }, + { + id: "persistence-summary", + label: "Persistence summary", + apiName: persistenceSummary?.apiName ?? null, + count: persistenceSummaryRows.length, + }, { id: "persistence-capabilities", label: "Persistence capabilities", @@ -1699,6 +1781,11 @@ export function createIniPanelShellApiSurfaceInventory({ label: "Session readiness report", value: sessionReadinessWorkflowReport?.apiName ?? "missing", }, + { + id: "persistence-summary", + label: "Persistence summary", + value: persistenceSummary?.apiName ?? "missing", + }, { id: "persistence-capabilities", label: "Persistence capabilities", @@ -1777,6 +1864,7 @@ export function createIniPanelShellWorkflowOverviewContract() { "getWorkflowOverviewSummary", "getWorkflowOverviewSessionReadinessReport", "getWorkflowOverviewApiSurfaceInventory", + "getWorkflowOverviewPersistenceSummary", "getWorkflowOverviewDomContract", "getWorkflowOverviewDomReadiness", "renderWorkflowOverview", @@ -2668,7 +2756,10 @@ export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries shellSessionHandoffPackageMountDisplayViewModel, ); const shellSessionHandoffMountDomContract = createIniPanelShellSessionHandoffMountDomContract(); - const shellSessionHandoffWorkflowSummary = createIniPanelShellSessionHandoffWorkflowSummary({ + const shellSessionHandoffReadonlyStatusResult = readIniPanelShellSessionHandoffStatusFromControlPage({ + handoffSummary: shellSessionHandoffSummary, + }); + const baseShellSessionHandoffWorkflowSummary = createIniPanelShellSessionHandoffWorkflowSummary({ mountWorkflow: { phase: shellSessionHandoffPackageMountRenderState.phase, ready: shellSessionHandoffPackageMountRenderState.ready, @@ -2679,12 +2770,27 @@ export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries ready: shellSessionHandoffPackageMountState.ready, statusLine: shellSessionHandoffPackageMountState.statusLine, }, - readonlyStatusResult: readIniPanelShellSessionHandoffStatusFromControlPage({ - handoffSummary: shellSessionHandoffSummary, - }), + readonlyStatusResult: shellSessionHandoffReadonlyStatusResult, }); + const shellSessionHandoffWorkflowDomContract = createIniPanelShellSessionHandoffWorkflowDomContract(); + const shellWorkflowOverviewContract = createIniPanelShellWorkflowOverviewContract(); const shellSessionReadinessWorkflowReport = createIniPanelShellSessionReadinessWorkflowReport({ - workflowSummary: shellSessionHandoffWorkflowSummary, + workflowSummary: baseShellSessionHandoffWorkflowSummary, + }); + const shellPersistenceSummary = createMachineSessionPersistenceSummary(); + const shellApiSurfaceInventory = createIniPanelShellApiSurfaceInventory({ + launchApiManifest: shellIntegrationManifest.launchApiManifest, + controlPageApiManifest: shellIntegrationManifest.controlPageApiManifest, + workflowOverviewContract: shellWorkflowOverviewContract, + sessionReadinessWorkflowReport: shellSessionReadinessWorkflowReport, + persistenceSummary: shellPersistenceSummary, + }); + const shellSessionHandoffWorkflowSummary = createIniPanelShellSessionHandoffWorkflowSummary({ + mountWorkflow: baseShellSessionHandoffWorkflowSummary.mountWorkflow, + controlPageMountWorkflow: baseShellSessionHandoffWorkflowSummary.controlPageMountWorkflow, + readonlyStatusResult: shellSessionHandoffReadonlyStatusResult, + persistenceSummary: shellPersistenceSummary, + apiSurfaceInventory: shellApiSurfaceInventory, }); const shellSessionHandoffWorkflowDisplayViewModel = createIniPanelShellSessionHandoffWorkflowDisplayViewModel( shellSessionHandoffWorkflowSummary, @@ -2692,14 +2798,6 @@ export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries const shellSessionHandoffWorkflowRenderState = createIniPanelShellSessionHandoffWorkflowRenderState( shellSessionHandoffWorkflowDisplayViewModel, ); - const shellSessionHandoffWorkflowDomContract = createIniPanelShellSessionHandoffWorkflowDomContract(); - const shellWorkflowOverviewContract = createIniPanelShellWorkflowOverviewContract(); - const shellApiSurfaceInventory = createIniPanelShellApiSurfaceInventory({ - launchApiManifest: shellIntegrationManifest.launchApiManifest, - controlPageApiManifest: shellIntegrationManifest.controlPageApiManifest, - workflowOverviewContract: shellWorkflowOverviewContract, - sessionReadinessWorkflowReport: shellSessionReadinessWorkflowReport, - }); return { pages: entries.map(({ id, href, title }) => ({ id, @@ -2734,6 +2832,7 @@ export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries shellSessionHandoffWorkflowRenderState, shellSessionHandoffWorkflowDomContract, shellWorkflowOverviewContract, + shellPersistenceSummary, shellApiSurfaceInventory, launchApiSchema: createIniPanelLaunchApiSchema(), launchApiManifest: createIniPanelLaunchApiManifest(entries), @@ -2764,6 +2863,7 @@ export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries mountShellSessionHandoffFromControlPage: mountIniPanelShellSessionHandoffFromControlPage, readShellSessionHandoffStatusFromControlPage: readIniPanelShellSessionHandoffStatusFromControlPage, createShellSessionHandoffWorkflowSummary: createIniPanelShellSessionHandoffWorkflowSummary, + createMachineSessionPersistenceSummary, createShellSessionReadinessWorkflowReport: createIniPanelShellSessionReadinessWorkflowReport, createShellApiSurfaceInventory: createIniPanelShellApiSurfaceInventory, createShellSessionHandoffWorkflowDisplayViewModel: createIniPanelShellSessionHandoffWorkflowDisplayViewModel, diff --git a/wasm-port/runtime/ui/ini-panel/workflow-overview.html b/wasm-port/runtime/ui/ini-panel/workflow-overview.html index 9c17076..4b99170 100644 --- a/wasm-port/runtime/ui/ini-panel/workflow-overview.html +++ b/wasm-port/runtime/ui/ini-panel/workflow-overview.html @@ -96,6 +96,7 @@ createIniPanelShellWorkflowOverviewEmbeddingDomContract, createIniPanelShellWorkflowOverviewEmbeddingDomReadiness, mountIniPanelShellWorkflowOverviewEmbedding, + createMachineSessionPersistenceSummary, renderIniPanelShellWorkflowOverviewEmbeddingState, mountIniPanelShellWorkflowOverviewEmbeddingState, createIniPanelShellWorkflowOverviewEmbeddingMountDisplayViewModel, @@ -129,6 +130,9 @@ sessionReadinessWorkflowReport: shellViewModel.shellSessionReadinessWorkflowReport, }) ), + getWorkflowOverviewPersistenceSummary: (options) => ( + createMachineSessionPersistenceSummary(options) + ), getWorkflowOverviewDomContract: (options) => ( createIniPanelShellSessionHandoffWorkflowDomContract(options) ), diff --git a/wasm-port/tests/browser/ini_panel_launch_smoke.html b/wasm-port/tests/browser/ini_panel_launch_smoke.html index 13c46c6..4754244 100644 --- a/wasm-port/tests/browser/ini_panel_launch_smoke.html +++ b/wasm-port/tests/browser/ini_panel_launch_smoke.html @@ -101,6 +101,7 @@ !launchApi?.getShellSessionHandoffPackageMountRenderState || !launchApi?.getShellSessionReadinessWorkflowReport || !launchApi?.getShellApiSurfaceInventory || + !launchApi?.getMachineSessionPersistenceSummary || !launchApi?.renderShellSessionHandoffMountState || !launchApi?.getWorkflowOverviewContract || !launchApi?.getWorkflowOverviewReadiness || @@ -182,6 +183,7 @@ const shellSessionHandoffPackageMountRenderState = launchApi.getShellSessionHandoffPackageMountRenderState(); const shellSessionReadinessWorkflowReport = launchApi.getShellSessionReadinessWorkflowReport(); const shellApiSurfaceInventory = launchApi.getShellApiSurfaceInventory(); + const launchPersistenceSummary = launchApi.getMachineSessionPersistenceSummary(); const workflowOverviewContract = launchApi.getWorkflowOverviewContract(); assertEqual( shellIntegrationSchema.manifestVersion, @@ -448,6 +450,21 @@ 3, "shell API surface inventory session readiness rows", ); + assertEqual( + shellApiSurfaceInventory.counts.persistenceSummaryRows, + 7, + "shell API surface inventory persistence summary rows", + ); + assertEqual( + launchPersistenceSummary.apiName, + "machine-session-persistence-summary", + "launch API persistence summary API", + ); + assertEqual( + launchPersistenceSummary.rows.length, + 7, + "launch API persistence summary rows", + ); assertEqual( workflowOverviewContract.pageHref, "./workflow-overview.html", @@ -709,12 +726,12 @@ assertEqual(workflowOverviewEmbedding.pageHref, "./workflow-overview.html", "workflow overview embedding page"); assertEqual(workflowOverviewEmbedding.ready, false, "workflow overview embedding default readiness"); assertEqual(workflowOverviewEmbedding.renderState.dataset.handoffScope, "workflow", "workflow overview embedding scope"); - assertEqual(workflowOverviewEmbedding.mountResult.renderResult.rowCount, 5, "workflow overview embedding rows"); + assertEqual(workflowOverviewEmbedding.mountResult.renderResult.rowCount, 8, "workflow overview embedding rows"); assertEqual(workflowOverviewEmbedding.error, null, "workflow overview embedding error"); assertEqual(workflowOverviewEmbeddingReport.apiName, "ini-panel-shell-workflow-overview-embedding-report", "workflow overview embedding report API name"); assertEqual(workflowOverviewEmbeddingReport.ready, false, "workflow overview embedding report readiness"); assertEqual(workflowOverviewEmbeddingReport.missingMethods.join(","), "", "workflow overview embedding report missing methods"); - assertEqual(workflowOverviewEmbeddingReport.renderRowCount, 5, "workflow overview embedding report row count"); + assertEqual(workflowOverviewEmbeddingReport.renderRowCount, 8, "workflow overview embedding report row count"); assertEqual(workflowOverviewEmbeddingReport.rows.length, 6, "workflow overview embedding report rows"); assertEqual(workflowOverviewEmbeddingDisplay.title, "Workflow overview embedding", "workflow overview embedding display title"); assertEqual(workflowOverviewEmbeddingDisplay.statusText, "Blocked", "workflow overview embedding display status"); 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 1c238c3..a3f2fa9 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 @@ -372,11 +372,11 @@ TOOL_TABLE = browser-shell-tool.tbl assertEqual(workflowOverviewReadiness.missing.join(","), "", "shell workflow overview iframe missing methods"); assertEqual(workflowOverviewEmbedding.apiName, "ini-panel-shell-workflow-overview-embedding", "shell workflow overview embedding API"); assertEqual(workflowOverviewEmbedding.renderState.dataset.handoffScope, "workflow", "shell workflow overview embedding scope"); - assertEqual(workflowOverviewEmbedding.mountResult.renderResult.rowCount, 5, "shell workflow overview embedding rows"); + assertEqual(workflowOverviewEmbedding.mountResult.renderResult.rowCount, 8, "shell workflow overview embedding rows"); assertEqual(workflowOverviewEmbedding.error, null, "shell workflow overview embedding error"); assertEqual(workflowOverviewEmbeddingReport.ready, false, "shell workflow overview embedding report readiness"); assertEqual(workflowOverviewEmbeddingReport.rows.length, 6, "shell workflow overview embedding report rows"); - assertEqual(workflowOverviewEmbeddingReport.renderRowCount, 5, "shell workflow overview embedding report row count"); + assertEqual(workflowOverviewEmbeddingReport.renderRowCount, 8, "shell workflow overview embedding report row count"); assertEqual(workflowOverviewEmbeddingReport.error, null, "shell workflow overview embedding report error"); assertEqual(workflowOverviewEmbeddingDisplay.statusText, "Blocked", "shell workflow overview embedding display status"); assertEqual(workflowOverviewEmbeddingDisplay.rows.length, 6, "shell workflow overview embedding display rows"); @@ -508,16 +508,27 @@ TOOL_TABLE = browser-shell-tool.tbl const readonlyStatusResult = launchApi.readShellSessionHandoffStatusFromControlPage({ controlPageApi, }); - const workflowSummaryResult = launchApi.getShellSessionHandoffWorkflowSummary({ + const baseWorkflowSummaryResult = launchApi.getShellSessionHandoffWorkflowSummary({ mountWorkflow: readyMountWorkflow, controlPageMountWorkflow: readyControlPageMountWorkflow, readonlyStatusResult, }); const sessionReadinessWorkflowReport = launchApi.getShellSessionReadinessWorkflowReport({ - workflowSummary: workflowSummaryResult, + workflowSummary: baseWorkflowSummaryResult, }); const shellApiSurfaceInventory = launchApi.getShellApiSurfaceInventory({ sessionReadinessWorkflowReport, + persistenceSummary: panelDoc.defaultView.linuxCncIniPanelApi.getMachineSessionPersistenceSummary(), + }); + const persistenceSummary = panelDoc.defaultView.linuxCncIniPanelApi.getMachineSessionPersistenceSummary({ + readonlyStatus, + }); + const workflowSummaryResult = launchApi.getShellSessionHandoffWorkflowSummary({ + mountWorkflow: readyMountWorkflow, + controlPageMountWorkflow: readyControlPageMountWorkflow, + readonlyStatusResult, + persistenceSummary, + apiSurfaceInventory: shellApiSurfaceInventory, }); const workflowDisplayResult = launchApi.getShellSessionHandoffWorkflowDisplayViewModel( workflowSummaryResult, @@ -576,6 +587,21 @@ TOOL_TABLE = browser-shell-tool.tbl 3, "shell handoff API surface inventory session readiness rows", ); + assertEqual( + shellApiSurfaceInventory.counts.persistenceSummaryRows, + 7, + "shell handoff API surface inventory persistence summary rows", + ); + assertEqual( + workflowSummaryResult.rows.some((row) => row.id === "persistence-summary" && row.value === "ready: 7 rows"), + true, + "shell handoff workflow summary persistence summary row", + ); + assertEqual( + workflowSummaryResult.rows.some((row) => row.id === "api-surface-inventory" && row.value === "ready: 6 rows"), + true, + "shell handoff workflow summary API surface inventory row", + ); assertEqual( workflowSummaryResult.sessionReadiness.ready, true, @@ -595,7 +621,7 @@ TOOL_TABLE = browser-shell-tool.tbl assertEqual(workflowRenderState.dataset.handoffScope, "workflow", "shell handoff workflow render-state scope"); assertEqual(externalWorkflowDomReadiness.ready, true, "shell handoff workflow DOM readiness"); assertEqual(workflowDomMountResult.phase, "ready", "shell handoff workflow DOM mount phase"); - assertEqual(workflowDomMountResult.renderResult.rowCount, 6, "shell handoff workflow DOM mount rows"); + assertEqual(workflowDomMountResult.renderResult.rowCount, 8, "shell handoff workflow DOM mount rows"); assertEqual( document.querySelector("[data-external-shell-workflow]").dataset.handoffScope, "workflow", diff --git a/wasm-port/tests/browser/ini_panel_smoke.html b/wasm-port/tests/browser/ini_panel_smoke.html index ee82de7..3b5684b 100644 --- a/wasm-port/tests/browser/ini_panel_smoke.html +++ b/wasm-port/tests/browser/ini_panel_smoke.html @@ -376,6 +376,9 @@ TOOL_TABLE = browser-tool.tbl !uiApi?.getMachineSessionStateReport || !uiApi?.getMachineSessionStateReportExport || !uiApi?.getMachineSessionWorkflowStatus || + !uiApi?.getMachineSessionPersistenceSummary || + !uiApi?.getMachineSessionPersistenceDisplayViewModel || + !uiApi?.getMachineSessionPersistenceRenderState || !uiApi?.renderMachineSessionControlView ) { throw new Error("INI panel API namespace did not expose state getters"); @@ -813,6 +816,44 @@ TOOL_TABLE = browser-tool.tbl 2, "UI machine session workflow status canonical event count", ); + const machineSessionPersistenceSummary = uiApi.getMachineSessionPersistenceSummary(); + assertEqual( + machineSessionPersistenceSummary.apiName, + "machine-session-persistence-summary", + "UI machine session persistence summary API name", + ); + assertEqual( + machineSessionPersistenceSummary.ready, + true, + "UI machine session persistence summary readiness", + ); + assertEqual( + machineSessionPersistenceSummary.rows.find((row) => row.id === "machine-files")?.value, + "ini, parameters, tool-table, g-code", + "UI machine session persistence summary machine files", + ); + assertEqual( + machineSessionPersistenceSummary.rows.find((row) => row.id === "session-load")?.value, + "loaded: /work/session-machine.ini", + "UI machine session persistence summary session load", + ); + const machineSessionPersistenceDisplay = uiApi.getMachineSessionPersistenceDisplayViewModel(); + assertEqual( + machineSessionPersistenceDisplay.statusText, + "Ready", + "UI machine session persistence display status", + ); + const machineSessionPersistenceRenderState = uiApi.getMachineSessionPersistenceRenderState(); + assertEqual( + machineSessionPersistenceRenderState.statusLine, + "Ready: No missing persistence requirements", + "UI machine session persistence render-state status", + ); + assertEqual( + machineSessionPersistenceRenderState.dataset.persistenceScope, + "opfs-session", + "UI machine session persistence render-state scope", + ); const machineSessionStateBundle = uiApi.getMachineSessionStateBundle(); assertEqual( machineSessionStateBundle.report.status.lastAction, diff --git a/wasm-port/tests/browser/ini_panel_workflow_overview_smoke.html b/wasm-port/tests/browser/ini_panel_workflow_overview_smoke.html index fbf2ce4..47bc126 100644 --- a/wasm-port/tests/browser/ini_panel_workflow_overview_smoke.html +++ b/wasm-port/tests/browser/ini_panel_workflow_overview_smoke.html @@ -33,6 +33,7 @@ !workflowApi?.getWorkflowOverviewSummary || !workflowApi?.getWorkflowOverviewSessionReadinessReport || !workflowApi?.getWorkflowOverviewApiSurfaceInventory || + !workflowApi?.getWorkflowOverviewPersistenceSummary || !workflowApi?.getWorkflowOverviewDomContract || !workflowApi?.getWorkflowOverviewDomReadiness || !workflowApi?.renderWorkflowOverview || @@ -60,6 +61,7 @@ const summary = workflowApi.getWorkflowOverviewSummary(); const sessionReadinessReport = workflowApi.getWorkflowOverviewSessionReadinessReport(); const apiSurfaceInventory = workflowApi.getWorkflowOverviewApiSurfaceInventory(); + const persistenceSummary = workflowApi.getWorkflowOverviewPersistenceSummary(); const domContract = workflowApi.getWorkflowOverviewDomContract(); const domReadiness = workflowApi.getWorkflowOverviewDomReadiness(); const renderResult = workflowApi.renderWorkflowOverview(); @@ -93,19 +95,30 @@ "ini-panel-shell-api-surface-inventory", "workflow overview API surface inventory API name", ); - assertEqual(apiSurfaceInventory.rows.length, 5, "workflow overview API surface inventory rows"); + assertEqual(apiSurfaceInventory.rows.length, 6, "workflow overview API surface inventory rows"); + assertEqual( + apiSurfaceInventory.counts.persistenceSummaryRows, + 7, + "workflow overview API surface inventory persistence summary rows", + ); + assertEqual( + persistenceSummary.apiName, + "machine-session-persistence-summary", + "workflow overview persistence summary API name", + ); + assertEqual(persistenceSummary.rows.length, 7, "workflow overview persistence summary rows"); assertEqual(displayViewModel.title, "Shell handoff workflow", "workflow overview display title"); assertEqual(renderState.renderStateVersion, 1, "workflow overview render-state version"); assertEqual(renderState.dataset.handoffScope, "workflow", "workflow overview render-state scope"); assertEqual(domContract.selectors.mount, "[data-handoff-workflow]", "workflow overview DOM contract mount selector"); assertEqual(domReadiness.ready, true, "workflow overview DOM readiness"); assertEqual(renderResult.rendered, true, "workflow overview render result"); - assertEqual(renderResult.rowCount, 5, "workflow overview render row count"); + assertEqual(renderResult.rowCount, 8, "workflow overview render row count"); assertEqual(mountResult.ready, false, "workflow overview mount result readiness"); assertEqual(mountResult.renderResult.rendered, true, "workflow overview mount result rendered"); assertEqual(embeddingReport.apiName, "ini-panel-shell-workflow-overview-embedding-report", "workflow overview embedding report API name"); assertEqual(embeddingReport.ready, false, "workflow overview embedding report readiness"); - assertEqual(embeddingReport.renderRowCount, 5, "workflow overview embedding report row count"); + assertEqual(embeddingReport.renderRowCount, 8, "workflow overview embedding report row count"); assertEqual(embeddingReport.rows.length, 6, "workflow overview embedding report rows"); assertEqual(embeddingDisplay.title, "Workflow overview embedding", "workflow overview embedding display title"); assertEqual(embeddingDisplay.statusText, "Blocked", "workflow overview embedding display status"); @@ -191,6 +204,16 @@ true, "workflow overview DOM blocking reasons row", ); + assertEqual( + workflowDoc.querySelector('[data-handoff-workflow-value="api-surface-inventory"]').textContent, + "ready: 6 rows", + "workflow overview DOM API surface inventory row", + ); + assertEqual( + workflowDoc.querySelector('[data-handoff-workflow-value="persistence-summary"]').textContent, + "waiting: 5 missing", + "workflow overview DOM persistence summary row", + ); document.getElementById("status").textContent = "browser_ini_workflow_overview_smoke=ok"; console.log("browser_ini_workflow_overview_smoke=ok"); diff --git a/wasm-port/tests/sdk/node/verify_sdk_surface.mjs b/wasm-port/tests/sdk/node/verify_sdk_surface.mjs index 59abb0b..119a0fe 100644 --- a/wasm-port/tests/sdk/node/verify_sdk_surface.mjs +++ b/wasm-port/tests/sdk/node/verify_sdk_surface.mjs @@ -4,22 +4,41 @@ import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { + INI_PANEL_ENTRIES, analyzeIniRuntimeBoundaries, + createControlPageBrowserApiManifest, + createControlPageBrowserApiMethods, + createControlPageBrowserApiSchema, createIniPanelLaunchApiManifest, + createIniPanelLauncherLinkViewModel, createIniPanelShellApiSurfaceInventory, + createIniPanelShellControlPageContract, + createIniPanelShellIntegrationManifest, + createIniPanelShellIntegrationReadiness, + createIniPanelShellIntegrationSchema, + createIniPanelShellIntegrationWorkflowPlan, createIniPanelShellSessionReadinessWorkflowReport, createIniPanelShellViewModel, + createIniPanelShellWorkflowOverviewContract, createIniPanelShellWorkflowOverviewEmbeddingMountDomContract, createIniPanelShellWorkflowOverviewEmbeddingMountDomReadiness, createLinuxCncIniSdk, createLinuxCncInterpSdk, createMachineSessionSnapshotPayload, + createMachineSessionPersistenceDisplayViewModel, + createMachineSessionPersistenceRenderState, + createMachineSessionPersistenceSummary, createSessionSnapshot, defaultMachinePaths, gcodeFilenameFromProgramPath, gcodeProgramPath, + getIniPanelEntryByHref, + getIniPanelEntryManifest, getOpfsRoot, + getVisibleIniPanelEntries, + isSupportedIniPanelControlPageApiManifest, isSupportedIniPanelLaunchApiManifest, + isSupportedIniPanelShellIntegrationManifest, loadMachineSessionFromOpfs, loadMachineSessionSnapshot, loadMachineTextFiles, @@ -56,6 +75,13 @@ const requiredExports = [ ["planIniFileContextStaging", planIniFileContextStaging], ["planSimConfigStaging", planSimConfigStaging], ["analyzeIniRuntimeBoundaries", analyzeIniRuntimeBoundaries], + ["getIniPanelEntryManifest", getIniPanelEntryManifest], + ["getIniPanelEntryByHref", getIniPanelEntryByHref], + ["getVisibleIniPanelEntries", getVisibleIniPanelEntries], + ["createIniPanelLauncherLinkViewModel", createIniPanelLauncherLinkViewModel], + ["createMachineSessionPersistenceDisplayViewModel", createMachineSessionPersistenceDisplayViewModel], + ["createMachineSessionPersistenceRenderState", createMachineSessionPersistenceRenderState], + ["createMachineSessionPersistenceSummary", createMachineSessionPersistenceSummary], ["getOpfsRoot", getOpfsRoot], ["saveTextFile", saveTextFile], ["loadTextFile", loadTextFile], @@ -80,11 +106,22 @@ const requiredExports = [ ["loadMachineToolTableFromOpfs", loadMachineToolTableFromOpfs], ["loadMachineSessionFromOpfs", loadMachineSessionFromOpfs], ["readMachineSessionReadiness", readMachineSessionReadiness], + ["createControlPageBrowserApiSchema", createControlPageBrowserApiSchema], + ["createControlPageBrowserApiManifest", createControlPageBrowserApiManifest], + ["createControlPageBrowserApiMethods", createControlPageBrowserApiMethods], + ["createIniPanelShellControlPageContract", createIniPanelShellControlPageContract], + ["isSupportedIniPanelControlPageApiManifest", isSupportedIniPanelControlPageApiManifest], ["createIniPanelLaunchApiManifest", createIniPanelLaunchApiManifest], ["isSupportedIniPanelLaunchApiManifest", isSupportedIniPanelLaunchApiManifest], + ["createIniPanelShellIntegrationSchema", createIniPanelShellIntegrationSchema], + ["createIniPanelShellIntegrationManifest", createIniPanelShellIntegrationManifest], + ["createIniPanelShellIntegrationReadiness", createIniPanelShellIntegrationReadiness], + ["createIniPanelShellIntegrationWorkflowPlan", createIniPanelShellIntegrationWorkflowPlan], + ["isSupportedIniPanelShellIntegrationManifest", isSupportedIniPanelShellIntegrationManifest], ["createIniPanelShellViewModel", createIniPanelShellViewModel], ["createIniPanelShellApiSurfaceInventory", createIniPanelShellApiSurfaceInventory], ["createIniPanelShellSessionReadinessWorkflowReport", createIniPanelShellSessionReadinessWorkflowReport], + ["createIniPanelShellWorkflowOverviewContract", createIniPanelShellWorkflowOverviewContract], [ "createIniPanelShellWorkflowOverviewEmbeddingMountDomContract", createIniPanelShellWorkflowOverviewEmbeddingMountDomContract, @@ -116,12 +153,80 @@ assert.equal(sessionSnapshotPath("sdk-session"), "linuxcnc/sessions/sdk-session/ assert.equal(normalizeOpfsPath("linuxcnc/machines/sdk-mill/tool.tbl"), "linuxcnc/machines/sdk-mill/tool.tbl"); assert.equal(gcodeFilenameFromProgramPath("linuxcnc/gcode/demo.ngc"), "demo.ngc"); +assert.deepEqual(getIniPanelEntryManifest(), INI_PANEL_ENTRIES); +assert.deepEqual(getVisibleIniPanelEntries(), INI_PANEL_ENTRIES); +assert.deepEqual(getIniPanelEntryByHref("./control-page.html"), { + id: "control-view", + title: "Open read-only control view", + href: "./control-page.html", +}); +assert.deepEqual(createIniPanelLauncherLinkViewModel(), [ + { + entryId: "edit-run", + href: "./index.html", + label: "Open edit and run panel", + }, + { + entryId: "control-view", + href: "./control-page.html", + label: "Open read-only control view", + }, + { + entryId: "workflow-overview", + href: "./workflow-overview.html", + label: "Open shell handoff overview", + }, +]); +assert.equal( + createMachineSessionPersistenceSummary({ + state: { + badges: { opfs: "OPFS: ready" }, + fields: { sessionSnapshot: "sdk-session/snapshot.json", sessionIni: "/work/session-machine.ini" }, + sessionLoad: { iniWasmPath: "/work/session-machine.ini" }, + sessionReadiness: { phase: "ready", ready: true, missing: [] }, + }, + }).rows.find(({ id }) => id === "session-load")?.value, + "loaded: /work/session-machine.ini", +); +assert.equal( + createMachineSessionPersistenceRenderState( + createMachineSessionPersistenceDisplayViewModel(createMachineSessionPersistenceSummary()), + ).apiName, + "machine-session-persistence-render-state", +); + +const controlPageApiSchema = createControlPageBrowserApiSchema(); +const controlPageApiManifest = createControlPageBrowserApiManifest(); +assert.equal(controlPageApiSchema.apiName, "ini-panel-control-page"); +assert.equal(isSupportedIniPanelControlPageApiManifest(controlPageApiManifest), true); +assert.deepEqual(controlPageApiManifest.methods, createControlPageBrowserApiMethods()); +assert.equal( + createIniPanelShellControlPageContract().readonlyStatusApiManifest.apiName, + "machine-session-readonly-status", +); + const launchManifest = createIniPanelLaunchApiManifest(); assert.equal(isSupportedIniPanelLaunchApiManifest(launchManifest), true); assert.equal( launchManifest.methods.includes("mountWorkflowOverviewEmbeddingMountState"), true, ); +assert.deepEqual( + launchManifest.visibleEntries, + getVisibleIniPanelEntries().map(({ id, href, title }) => ({ id, href, title })), +); + +const shellIntegrationManifest = createIniPanelShellIntegrationManifest(); +assert.equal(createIniPanelShellIntegrationSchema().apiName, "ini-panel-shell-integration"); +assert.equal(isSupportedIniPanelShellIntegrationManifest(shellIntegrationManifest), true); +assert.equal(createIniPanelShellIntegrationReadiness(shellIntegrationManifest).ready, true); +assert.equal(createIniPanelShellIntegrationWorkflowPlan({ + manifest: shellIntegrationManifest, +}).ready, true); +assert.equal( + createIniPanelShellWorkflowOverviewContract().methodNames.includes("getWorkflowOverviewApiSurfaceInventory"), + true, +); const shellViewModel = createIniPanelShellViewModel(); assert.equal( @@ -140,6 +245,19 @@ assert.equal( createIniPanelShellApiSurfaceInventory().apiName, "ini-panel-shell-api-surface-inventory", ); +assert.equal(shellViewModel.shellApiSurfaceInventory.ready, true); +assert.equal( + shellViewModel.shellSessionHandoffWorkflowSummary.rows.find( + ({ id }) => id === "persistence-summary", + )?.value, + "waiting: 5 missing", +); +assert.equal( + shellViewModel.shellSessionHandoffWorkflowSummary.rows.find( + ({ id }) => id === "api-surface-inventory", + )?.value, + "ready: 6 rows", +); assert.equal( shellViewModel.mountWorkflowOverviewEmbeddingMountState, mountIniPanelShellWorkflowOverviewEmbeddingMountState, 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 c72e99f..788c8da 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 @@ -167,6 +167,9 @@ const requiredShellExports = [ "createIniPanelShellViewModel", "getIniPanelEntryByHref", "getVisibleIniPanelEntries", + "createMachineSessionPersistenceDisplayViewModel", + "createMachineSessionPersistenceRenderState", + "createMachineSessionPersistenceSummary", "isSupportedIniPanelControlPageApiManifest", "isSupportedIniPanelLaunchApiManifest", "isSupportedIniPanelShellIntegrationManifest", 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 d2fc723..82dc1b2 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 @@ -69,6 +69,7 @@ assert.deepEqual(manifest, { "mountShellSessionHandoff", "mountShellSessionHandoffFromControlPage", "readShellSessionHandoffStatusFromControlPage", + "getMachineSessionPersistenceSummary", "getShellSessionHandoffWorkflowSummary", "getShellSessionReadinessWorkflowReport", "getShellApiSurfaceInventory", @@ -227,6 +228,7 @@ assert.match(launchText, /\brenderShellSessionHandoffMountState\b/); assert.match(launchText, /\bmountShellSessionHandoff\b/); assert.match(launchText, /\bmountShellSessionHandoffFromControlPage\b/); assert.match(launchText, /\breadShellSessionHandoffStatusFromControlPage\b/); +assert.match(launchText, /\bgetMachineSessionPersistenceSummary\b/); assert.match(launchText, /\bgetShellSessionHandoffWorkflowSummary\b/); assert.match(launchText, /\bgetShellSessionReadinessWorkflowReport\b/); assert.match(launchText, /\bgetShellApiSurfaceInventory\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 fec00e2..af63b1e 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 @@ -179,7 +179,7 @@ assert.deepEqual(readiness, { counts: { pages: 3, launcherLinks: 3, - launchMethods: 57, + launchMethods: 58, controlPageMethods: 11, persistenceCapabilities: 3, }, @@ -242,7 +242,7 @@ assert.deepEqual(handoffSummary, { counts: { pages: 3, launcherLinks: 3, - launchMethods: 57, + launchMethods: 58, controlPageMethods: 11, persistenceCapabilities: 3, }, @@ -779,6 +779,7 @@ assert.deepEqual(createIniPanelShellWorkflowOverviewContract(), { "getWorkflowOverviewSummary", "getWorkflowOverviewSessionReadinessReport", "getWorkflowOverviewApiSurfaceInventory", + "getWorkflowOverviewPersistenceSummary", "getWorkflowOverviewDomContract", "getWorkflowOverviewDomReadiness", "renderWorkflowOverview", @@ -827,6 +828,7 @@ assert.deepEqual( [ "getWorkflowOverviewDisplayViewModel", "getWorkflowOverviewSummary", + "getWorkflowOverviewPersistenceSummary", "getWorkflowOverviewDomContract", "getWorkflowOverviewDomReadiness", "renderWorkflowOverview", @@ -1246,7 +1248,7 @@ assert.deepEqual(createIniPanelShellIntegrationReadiness(blockedManifest), { counts: { pages: 3, launcherLinks: 3, - launchMethods: 57, + launchMethods: 58, controlPageMethods: 11, persistenceCapabilities: 3, }, diff --git a/wasm-port/tests/ui/node/verify_ini_panel_state_summary.mjs b/wasm-port/tests/ui/node/verify_ini_panel_state_summary.mjs index 9dc441c..609e9a9 100644 --- a/wasm-port/tests/ui/node/verify_ini_panel_state_summary.mjs +++ b/wasm-port/tests/ui/node/verify_ini_panel_state_summary.mjs @@ -3,6 +3,9 @@ import assert from "node:assert/strict"; import { createIniPanelStateSummary, createMachineSessionControlView, + createMachineSessionPersistenceDisplayViewModel, + createMachineSessionPersistenceRenderState, + createMachineSessionPersistenceSummary, createMachineSessionStateSnapshot, createMachineSessionStateBundle, createMachineSessionStateReport, @@ -566,6 +569,145 @@ const expectedBundleReport = { }, }; +const readyPersistenceSummary = createMachineSessionPersistenceSummary({ + state: { + badges: { + ini: "INI WASM: ready", + interp: "Interpreter WASM: ready", + opfs: "OPFS: ready", + }, + machineFiles: { + iniOpfsPath: "linuxcnc/machines/xyzab-tdr/machine.ini", + parameterOpfsPath: "linuxcnc/machines/xyzab-tdr/linuxcnc.var", + toolTableOpfsPath: "linuxcnc/machines/xyzab-tdr/tool.tbl", + gcodeOpfsPath: "linuxcnc/gcode/ui-session.ngc", + }, + fields: { + sessionSnapshot: "ui-machine-session/ui-machine-session.json", + sessionIni: "/work/session-machine.ini", + sessionParameters: "/work/session-linuxcnc.var", + sessionToolTable: "/work/session-tool.tbl", + sessionGcode: "linuxcnc/gcode/ui-session.ngc", + }, + sessionLoad: { + iniWasmPath: "/work/session-machine.ini", + parameterWasmPath: "/work/session-linuxcnc.var", + toolTableWasmPath: "/work/session-tool.tbl", + }, + sessionReadiness: sessionReadinessFixture, + }, + readonlyStatus: { + phase: "ready", + ready: true, + }, + handoffWorkflowSummary: { + phase: "ready", + ready: true, + }, +}); + +assert.deepEqual( + readyPersistenceSummary, + { + apiName: "machine-session-persistence-summary", + summaryVersion: 1, + phase: "ready", + ready: true, + checks: { + opfs: true, + machineFiles: true, + sessionSnapshot: true, + sessionReadiness: true, + sessionLoad: true, + }, + missing: [], + counts: { + machineFiles: 4, + missing: 0, + }, + rows: [ + { + id: "opfs", + label: "OPFS", + value: "OPFS: ready", + }, + { + id: "machine-files", + label: "Machine files", + value: "ini, parameters, tool-table, g-code", + }, + { + id: "session-snapshot", + label: "Session snapshot", + value: "ui-machine-session/ui-machine-session.json", + }, + { + id: "session-readiness", + label: "Session readiness", + value: "ready", + }, + { + id: "session-load", + label: "Session load", + value: "loaded: /work/session-machine.ini", + }, + { + id: "readonly-status", + label: "Readonly status", + value: "ready", + }, + { + id: "handoff-readiness", + label: "Handoff readiness", + value: "ready", + }, + ], + }, +); + +assert.deepEqual( + createMachineSessionPersistenceSummary().missing, + ["opfs", "machineFiles", "sessionSnapshot", "sessionReadiness", "sessionLoad"], +); + +assert.deepEqual( + createMachineSessionPersistenceDisplayViewModel(readyPersistenceSummary), + { + phase: "ready", + title: "OPFS/session persistence", + statusText: "Ready", + detailText: "No missing persistence requirements", + rows: readyPersistenceSummary.rows, + }, +); + +assert.deepEqual( + createMachineSessionPersistenceRenderState( + createMachineSessionPersistenceDisplayViewModel(readyPersistenceSummary), + ), + { + apiName: "machine-session-persistence-render-state", + renderStateVersion: 1, + phase: "ready", + ready: true, + title: "OPFS/session persistence", + statusText: "Ready", + detailText: "No missing persistence requirements", + statusLine: "Ready: No missing persistence requirements", + rows: readyPersistenceSummary.rows, + dataset: { + persistencePhase: "ready", + persistenceReady: "true", + persistenceScope: "opfs-session", + }, + }, +); + +assert.equal( + createMachineSessionPersistenceRenderState().statusLine, + "Waiting: opfs, machineFiles, sessionSnapshot, sessionReadiness, sessionLoad", +); + assert.deepEqual( createMachineSessionWorkflowOverview(expectedBundleReport), expectedWorkflowOverview, 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 44f9c59..8a7cfae 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 @@ -67,6 +67,9 @@ import { createControlPageRefreshState, createControlPageSessionLoadState, createMachineSessionControlPageController, + createMachineSessionPersistenceDisplayViewModel, + createMachineSessionPersistenceRenderState, + createMachineSessionPersistenceSummary, getIniPanelEntryByHref, getIniPanelEntryManifest, getVisibleIniPanelEntries, @@ -104,6 +107,9 @@ assert.deepEqual(createControlPageSessionLoadState({ phase: "waiting-panel" }), }); assert.equal(getIniPanelEntryByHref("./index.html")?.id, "edit-run"); assert.equal(typeof createMachineSessionControlPageController, "function"); +assert.equal(typeof createMachineSessionPersistenceDisplayViewModel, "function"); +assert.equal(typeof createMachineSessionPersistenceRenderState, "function"); +assert.equal(typeof createMachineSessionPersistenceSummary, "function"); assert.equal(typeof createMachineSessionReadonlyStatus, "function"); assert.equal(typeof createMachineSessionReadonlyStatusApiManifest, "function"); assert.equal(typeof createControlPageBrowserApiManifest, "function"); @@ -667,6 +673,7 @@ assert.deepEqual(shellViewModel.shellWorkflowOverviewContract, { "getWorkflowOverviewSummary", "getWorkflowOverviewSessionReadinessReport", "getWorkflowOverviewApiSurfaceInventory", + "getWorkflowOverviewPersistenceSummary", "getWorkflowOverviewDomContract", "getWorkflowOverviewDomReadiness", "renderWorkflowOverview", @@ -1113,13 +1120,17 @@ assert.equal(workflowSummaryResult.apiName, "ini-panel-shell-session-handoff-wor assert.equal(workflowSummaryResult.summaryVersion, 1); assert.equal(workflowSummaryResult.phase, "blocked"); assert.equal(workflowSummaryResult.ready, false); +assert.deepEqual(workflowSummaryResult.apiSurfaceInventory, null); +assert.deepEqual(workflowSummaryResult.persistenceSummary, null); assert.deepEqual(workflowSummaryResult.sessionReadiness, { phase: "ready", ready: true, missing: [], }); assert.equal(workflowSummaryResult.rows.find(({ id }) => id === "session-readiness")?.value, "ready"); -assert.equal(workflowSummaryResult.rows.length, 6); +assert.equal(workflowSummaryResult.rows.find(({ id }) => id === "persistence-summary")?.value, "missing"); +assert.equal(workflowSummaryResult.rows.find(({ id }) => id === "api-surface-inventory")?.value, "missing"); +assert.equal(workflowSummaryResult.rows.length, 8); assert.equal(workflowSummaryResult.blockingReasons[0], "mount:blocked"); const sessionReadinessWorkflowReport = createIniPanelShellSessionReadinessWorkflowReport({ workflowSummary: workflowSummaryResult, @@ -1137,7 +1148,18 @@ assert.equal(shellViewModel.shellSessionReadinessWorkflowReport.rows.length, 3); assert.equal(shellViewModel.shellApiSurfaceInventory.apiName, "ini-panel-shell-api-surface-inventory"); assert.equal(shellViewModel.shellApiSurfaceInventory.ready, true); assert.equal(shellViewModel.shellApiSurfaceInventory.counts.sessionReadinessRows, 3); -assert.equal(shellViewModel.shellApiSurfaceInventory.rows.length, 5); +assert.equal(shellViewModel.shellApiSurfaceInventory.counts.persistenceSummaryRows, 7); +assert.equal(shellViewModel.shellApiSurfaceInventory.rows.length, 6); +assert.equal(shellViewModel.shellPersistenceSummary.ready, false); +assert.equal( + shellViewModel.shellSessionHandoffWorkflowSummary.rows.find(({ id }) => id === "persistence-summary")?.value, + "waiting: 5 missing", +); +assert.equal(shellViewModel.shellSessionHandoffWorkflowSummary.apiSurfaceInventory.ready, true); +assert.equal( + shellViewModel.shellSessionHandoffWorkflowSummary.rows.find(({ id }) => id === "api-surface-inventory")?.value, + "ready: 6 rows", +); const readyWorkflowSummaryResult = createIniPanelShellSessionHandoffWorkflowSummary({ mountWorkflow: { phase: "ready", @@ -1157,7 +1179,7 @@ assert.equal(workflowDisplayViewModel.phase, "blocked"); assert.equal(workflowDisplayViewModel.title, "Shell handoff workflow"); assert.equal(workflowDisplayViewModel.statusText, "Blocked"); assert.equal(workflowDisplayViewModel.detailText, "mount:blocked"); -assert.equal(workflowDisplayViewModel.rows.length, 6); +assert.equal(workflowDisplayViewModel.rows.length, 8); const readyWorkflowDisplayViewModel = createIniPanelShellSessionHandoffWorkflowDisplayViewModel( readyWorkflowSummaryResult, ); @@ -1172,7 +1194,7 @@ assert.equal(workflowRenderState.renderStateVersion, 1); assert.equal(workflowRenderState.phase, "blocked"); assert.equal(workflowRenderState.statusLine, "Blocked: mount:blocked"); assert.equal(workflowRenderState.dataset.handoffScope, "workflow"); -assert.equal(workflowRenderState.rows.length, 6); +assert.equal(workflowRenderState.rows.length, 8); const readyWorkflowRenderState = createIniPanelShellSessionHandoffWorkflowRenderState( readyWorkflowDisplayViewModel, ); @@ -1215,13 +1237,15 @@ assert.deepEqual( { rendered: true, statusLine: "Blocked: mount:blocked", - rowCount: 6, + rowCount: 8, rowIds: [ "mount-workflow", "control-page-mount-workflow", "readonly-status-workflow", "session-readiness", "readonly-status-missing-fields", + "persistence-summary", + "api-surface-inventory", "blocking-reasons", ], dataset: { @@ -1242,7 +1266,7 @@ assert.equal(workflowDomMountResult.apiName, "ini-panel-shell-session-handoff-wo assert.equal(workflowDomMountResult.workflowVersion, 1); assert.equal(workflowDomMountResult.phase, "blocked"); assert.equal(workflowDomMountResult.ready, false); -assert.equal(workflowDomMountResult.renderResult.rowCount, 6); +assert.equal(workflowDomMountResult.renderResult.rowCount, 8); assert.equal(workflowDomMountResult.renderResult.dataset.handoffScope, "workflow"); assert.deepEqual(createIniPanelShellWorkflowOverviewContract(), shellViewModel.shellWorkflowOverviewContract); assert.deepEqual(createIniPanelShellWorkflowOverviewReadiness(), { @@ -1259,6 +1283,7 @@ const workflowOverviewApi = { getWorkflowOverviewSummary: () => workflowSummaryResult, getWorkflowOverviewSessionReadinessReport: () => sessionReadinessWorkflowReport, getWorkflowOverviewApiSurfaceInventory: () => shellViewModel.shellApiSurfaceInventory, + getWorkflowOverviewPersistenceSummary: () => shellViewModel.shellPersistenceSummary, getWorkflowOverviewDomContract: createIniPanelShellSessionHandoffWorkflowDomContract, getWorkflowOverviewDomReadiness: createIniPanelShellSessionHandoffWorkflowDomReadiness, renderWorkflowOverview: renderIniPanelShellSessionHandoffWorkflowState, @@ -1291,7 +1316,7 @@ assert.equal(workflowOverviewEmbedding.apiName, "ini-panel-shell-workflow-overvi assert.equal(workflowOverviewEmbedding.embeddingVersion, 1); assert.equal(workflowOverviewEmbedding.ready, false); assert.equal(workflowOverviewEmbedding.renderState.statusLine, "Blocked: mount:blocked"); -assert.equal(workflowOverviewEmbedding.mountResult.renderResult.rowCount, 6); +assert.equal(workflowOverviewEmbedding.mountResult.renderResult.rowCount, 8); assert.equal(workflowOverviewEmbedding.error, null); const workflowOverviewEmbeddingReport = createIniPanelShellWorkflowOverviewEmbeddingReport(workflowOverviewEmbedding); assert.deepEqual(workflowOverviewEmbeddingReport, { @@ -1307,7 +1332,7 @@ assert.deepEqual(workflowOverviewEmbeddingReport, { contract: shellViewModel.shellWorkflowOverviewContract, missingMethods: [], mountPhase: "blocked", - renderRowCount: 6, + renderRowCount: 8, error: null, blockingReasons: ["embedding:blocked", "mount:blocked"], rows: [ @@ -1334,7 +1359,7 @@ assert.deepEqual(workflowOverviewEmbeddingReport, { { id: "render-row-count", label: "Render row count", - value: "6", + value: "8", }, { id: "error",