From 19eabdf06e2db96dfafec98d0ce57db8a25996ac Mon Sep 17 00:00:00 2001 From: wangdequan Date: Tue, 16 Jun 2026 17:39:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20release=20gate=20execution?= =?UTF-8?q?=20summary=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- text15.txt | 56 ++++++ wasm-port/docs/project-release-handoff.md | 5 +- wasm-port/runtime/sdk/README.md | 19 +- wasm-port/runtime/sdk/src/index.js | 1 + .../sdk/src/project-release-readiness.js | 99 +++++++++++ ...anel_shell_integration_workflow_smoke.html | 7 +- .../ini_panel_workflow_overview_smoke.html | 9 +- .../verify_project_release_handoff_docs.mjs | 3 + .../project-release-readiness-ready.json | 163 ++++++++++++++++++ ...ify_project_release_readiness_artifact.mjs | 3 + .../verify_project_release_gate_manifest.mjs | 41 +++++ .../tests/sdk/node/verify_sdk_surface.mjs | 9 + .../ui/node/verify_ini_panel_ui_shell.mjs | 10 +- 13 files changed, 411 insertions(+), 14 deletions(-) diff --git a/text15.txt b/text15.txt index 27f7f43..d4dc1d5 100644 --- a/text15.txt +++ b/text15.txt @@ -416,3 +416,59 @@ project_release_gate=ok - 新增内容只验证 host OPFS/session persistence、WASM staging、SDK/browser workflow 和 release gate evidence; - blocked runtime families 仍不 promotion。 + +五十九、2026-06-16 继续执行记录:release gate execution summary view-model SDK API + +本批继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力” +推进,选择 release gate execution evidence 的 UI/CI 展示能力。 + +本批新增实质能力: + +- 新增 SDK API:`createProjectReleaseGateExecutionSummaryViewModel()`; +- 该 API 复用 `createProjectReleaseGateExecutionManifest()` 输出,生成稳定 dashboard/view-model: + - `statusLine`; + - `passedCount`、`unknownCount`、`gateCount`; + - `nextGateId`; + - `evidenceCounts`; + - summary rows; + - normalized per-gate `gateRows`。 +- `createProjectReleaseReadinessReport()` 现在嵌入 `gateExecutionSummaryViewModel`; +- `createProjectReleaseReadinessArtifactValidation()` 现在要求 artifact 包含 ready + `gateExecutionSummaryViewModel`,并暴露 `gateExecutionSummaryReady`; +- release artifact validation summary/render rows 增加 `gate-execution-summary` evidence 行; +- SDK README 与 project release handoff docs 记录该 summary view-model; +- ready fixture 与 host artifact writer/validator 同步新 artifact schema。 + +本批新增/更新可执行验证: + +```bash +wasm-port/tests/sdk/node/verify_project_release_gate_manifest.sh +wasm-port/tests/sdk/node/verify_sdk_surface.sh +wasm-port/tests/docs/node/verify_project_release_handoff_docs.sh +wasm-port/tests/ui/node/verify_ui_node_smokes.sh +SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh +SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh +wasm-port/tests/host/verify_project_release_gate.sh +``` + +关键通过输出: + +```text +project_release_gate_manifest_node_smoke=ok +sdk_surface_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +ui_node_smokes=ok +browser_release_artifact_url_workflow_smoke=ok +browser_ini_shell_integration_workflow_smoke=ok +browser_opfs_session_workflow_smoke=ok +project_release_readiness_artifact_node_smoke=ok +project_release_gate=ok +``` + +本批仍保持 LinuxCNC 语义边界: + +- 未修改 vendored LinuxCNC 源码; +- 未在 JS/browser 中实现 G-code、tool、parameter、planner、kinematics、remap 或 canonical motion + 语义; +- 新增内容只包装 release gate evidence 与 artifact validation display rows; +- blocked runtime families 仍不 promotion。 diff --git a/wasm-port/docs/project-release-handoff.md b/wasm-port/docs/project-release-handoff.md index 110d878..f3823d1 100644 --- a/wasm-port/docs/project-release-handoff.md +++ b/wasm-port/docs/project-release-handoff.md @@ -41,7 +41,10 @@ continuation records are in `../text15.txt`. `createProjectReleaseGateActionPlan()` to derive pending gate commands, the next command, display rows, and a shell script from that matrix, `createProjectReleaseGateExecutionManifest()` to package each gate command, - expected output, observed-output evidence, and passed/unknown status, and + expected output, observed-output evidence, and passed/unknown status, + `createProjectReleaseGateExecutionSummaryViewModel()` to expose dashboard + status text, evidence counts, next missing gate, and normalized per-gate rows, + and `createProjectReleaseReadinessReport()` for a machine-readable release readiness report covering the same manifest, execution manifest, result matrix, action plan, sim-config inventory baseline, and blocked runtime diff --git a/wasm-port/runtime/sdk/README.md b/wasm-port/runtime/sdk/README.md index 0b1f558..f7ad49a 100644 --- a/wasm-port/runtime/sdk/README.md +++ b/wasm-port/runtime/sdk/README.md @@ -55,6 +55,7 @@ import { createMachineSessionSnapshotPayload, createProjectReleaseGateActionPlan, createProjectReleaseGateExecutionManifest, + createProjectReleaseGateExecutionSummaryViewModel, createProjectReleaseGateManifest, createProjectReleaseGateResultMatrix, createProjectReleaseReadinessArtifactJsonWorkflow, @@ -214,12 +215,15 @@ with caller-provided explicit results and observed output evidence. Each row includes the command, expected output, matched observed output when present, evidence kind, and `passed`/`unknown` status so CI or browser dashboards can render release-gate execution state without scraping shell scripts. +`createProjectReleaseGateExecutionSummaryViewModel()` turns that execution +manifest into stable dashboard status text, evidence counts, next missing gate, +summary rows, and normalized per-gate rows. `createProjectReleaseReadinessReport()` returns a machine-readable release -readiness report that embeds the same manifest, result matrix, and action plan -beside the gate execution manifest, sim-config inventory baseline, and blocked -runtime families. It is evidence driven: the default report is `ready: false` -until the caller supplies observed gate output such as `project_release_gate=ok` -or explicit gate results. +readiness report that embeds the same manifest, result matrix, action plan, +gate execution manifest, gate execution summary view-model, sim-config +inventory baseline, and blocked runtime families. It is evidence driven: the +default report is `ready: false` until the caller supplies observed gate output +such as `project_release_gate=ok` or explicit gate results. `createProjectReleaseReadinessSummaryViewModel()` turns the report into stable status text, a status line, and display rows for dashboards without executing any gate. @@ -238,8 +242,9 @@ it with `createProjectReleaseReadinessArtifactValidation()` without copying the host test assertions. The validation result includes `expectedGateCount` and `expectedGateIds` so external callers can compare an artifact against the current manifest, plus `gateManifestReady`, `gateExecutionManifestReady`, -`gateResultMatrixReady`, and `gateActionPlanReady` so stale artifacts that omit -the embedded gate evidence chain fail the same executable gate. +`gateExecutionSummaryReady`, `gateResultMatrixReady`, and +`gateActionPlanReady` so stale artifacts that omit the embedded gate evidence +chain fail the same executable gate. `createProjectReleaseReadinessArtifactValidationSummaryViewModel()` turns that validation result into stable status text and display rows for dashboards or browser shells without reinterpreting the artifact fields. diff --git a/wasm-port/runtime/sdk/src/index.js b/wasm-port/runtime/sdk/src/index.js index ff5192e..d36340c 100644 --- a/wasm-port/runtime/sdk/src/index.js +++ b/wasm-port/runtime/sdk/src/index.js @@ -3,6 +3,7 @@ export { createLinuxCncInterpSdk } from "./linuxcnc-interp.js"; export { createProjectReleaseGateActionPlan, createProjectReleaseGateExecutionManifest, + createProjectReleaseGateExecutionSummaryViewModel, createProjectReleaseGateManifest, createProjectReleaseGateResultMatrix, createProjectReleaseReadinessArtifactJsonWorkflow, diff --git a/wasm-port/runtime/sdk/src/project-release-readiness.js b/wasm-port/runtime/sdk/src/project-release-readiness.js index 7f02486..b7736e9 100644 --- a/wasm-port/runtime/sdk/src/project-release-readiness.js +++ b/wasm-port/runtime/sdk/src/project-release-readiness.js @@ -176,6 +176,81 @@ export function createProjectReleaseGateExecutionManifest({ }; } +export function createProjectReleaseGateExecutionSummaryViewModel( + executionManifest = createProjectReleaseGateExecutionManifest(), +) { + const rows = arrayOrEmpty(executionManifest?.rows); + const ready = executionManifest?.ready === true; + const missingGateIds = arrayOrEmpty(executionManifest?.missingGateIds); + const statusText = ready ? "Ready" : "Waiting"; + const detailText = ready + ? "All release gate evidence present" + : (missingGateIds.length > 0 ? missingGateIds.join(", ") : "release gate evidence missing"); + const evidenceCounts = rows.reduce((counts, row) => ({ + ...counts, + [row.evidence ?? "missing"]: (counts[row.evidence ?? "missing"] ?? 0) + 1, + }), {}); + + return { + apiName: "project-release-gate-execution-summary-view-model", + viewModelVersion: 1, + phase: ready ? "ready" : "waiting", + ready, + title: "Project release gate execution", + statusText, + detailText, + statusLine: `${statusText}: ${detailText}`, + gateCount: executionManifest?.gateCount ?? rows.length, + passedCount: executionManifest?.passedCount ?? rows.filter(({ passed }) => passed === true).length, + unknownCount: executionManifest?.unknownCount ?? rows.filter(({ passed }) => passed !== true).length, + missingGateIds, + nextGateId: missingGateIds[0] ?? null, + evidenceCounts, + rows: [ + { + id: "gate-results", + label: "Release gate evidence", + value: `${executionManifest?.passedCount ?? 0}/${executionManifest?.gateCount ?? rows.length} passed`, + }, + { + id: "unknown-gates", + label: "Unknown gates", + value: `${executionManifest?.unknownCount ?? 0}`, + }, + { + id: "next-gate", + label: "Next gate", + value: missingGateIds[0] ?? "none", + }, + { + id: "expected-output-evidence", + label: "Expected-output evidence", + value: `${evidenceCounts["expected-output"] ?? 0}`, + }, + { + id: "explicit-result-evidence", + label: "Explicit-result evidence", + value: `${evidenceCounts["explicit-result"] ?? 0}`, + }, + { + id: "missing", + label: "Missing gate evidence", + value: missingGateIds.length > 0 ? missingGateIds.join(", ") : "none", + }, + ], + gateRows: rows.map((row) => ({ + id: row.id, + label: row.label, + command: row.command, + expectedOutput: row.expectedOutput ?? null, + observedOutput: row.observedOutput ?? null, + evidence: row.evidence ?? "missing", + status: row.status ?? (row.passed === true ? "passed" : "unknown"), + passed: row.passed === true, + })), + }; +} + export function createProjectReleaseGateResultMatrix({ gateResults = {}, observedOutputs = [], @@ -274,6 +349,9 @@ export function createProjectReleaseReadinessReport({ observedOutputs, manifest: gateManifest, }); + const gateExecutionSummaryViewModel = createProjectReleaseGateExecutionSummaryViewModel( + gateExecutionManifest, + ); const gateResultMatrix = createProjectReleaseGateResultMatrix({ gateResults, observedOutputs, @@ -318,6 +396,7 @@ export function createProjectReleaseReadinessReport({ promotedBlockedFamilies, gateManifest, gateExecutionManifest, + gateExecutionSummaryViewModel, gateResultMatrix, gateActionPlan, gates: gateRows, @@ -432,6 +511,11 @@ export function createProjectReleaseReadinessArtifactValidationSummaryViewModel( label: "Gate execution manifest", value: validation?.gateExecutionManifestReady === true ? "ready" : "missing", }, + { + id: "gate-execution-summary", + label: "Gate execution summary", + value: validation?.gateExecutionSummaryReady === true ? "ready" : "missing", + }, { id: "gate-result-matrix", label: "Gate result matrix", @@ -889,6 +973,7 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) { const simConfigInventory = objectOrEmpty(artifactObject.simConfigInventory); const gateManifest = objectOrEmpty(artifactObject.gateManifest); const gateExecutionManifest = objectOrEmpty(artifactObject.gateExecutionManifest); + const gateExecutionSummaryViewModel = objectOrEmpty(artifactObject.gateExecutionSummaryViewModel); const gateResultMatrix = objectOrEmpty(artifactObject.gateResultMatrix); const gateActionPlan = objectOrEmpty(artifactObject.gateActionPlan); const gates = arrayOrEmpty(artifactObject.gates); @@ -917,6 +1002,13 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) { && gateExecutionManifest.unknownCount === 0 && executionRows.length === REQUIRED_RELEASE_GATES.length && executionRows.every(({ command, status }) => typeof command === "string" && status === "passed"); + const executionSummaryReady = gateExecutionSummaryViewModel.apiName === "project-release-gate-execution-summary-view-model" + && gateExecutionSummaryViewModel.viewModelVersion === 1 + && gateExecutionSummaryViewModel.ready === true + && gateExecutionSummaryViewModel.passedCount === REQUIRED_RELEASE_GATES.length + && gateExecutionSummaryViewModel.unknownCount === 0 + && gateExecutionSummaryViewModel.nextGateId === null + && arrayOrEmpty(gateExecutionSummaryViewModel.gateRows).length === REQUIRED_RELEASE_GATES.length; const actionPlanReady = gateActionPlan.apiName === "project-release-gate-action-plan" && gateActionPlan.planVersion === 1 && gateActionPlan.ready === true @@ -942,6 +1034,7 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) { ...(arrayOrEmpty(artifactObject.promotedBlockedFamilies).length === 0 ? [] : ["promotedBlockedFamilies"]), ...(manifestReady ? [] : ["gateManifest"]), ...(executionManifestReady ? [] : ["gateExecutionManifest"]), + ...(executionSummaryReady ? [] : ["gateExecutionSummaryViewModel"]), ...(matrixReady ? [] : ["gateResultMatrix"]), ...(actionPlanReady ? [] : ["gateActionPlan"]), ...(gateCountReady ? [] : ["gates.length"]), @@ -964,6 +1057,7 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) { expectedGateIds, gateManifestReady: manifestReady, gateExecutionManifestReady: executionManifestReady, + gateExecutionSummaryReady: executionSummaryReady, gateResultMatrixReady: matrixReady, gateActionPlanReady: actionPlanReady, blockedRuntimeFamilies: arrayOrEmpty(artifactObject.blockedRuntimeFamilies), @@ -988,6 +1082,11 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) { label: "Gate execution manifest", value: executionManifestReady ? "ready" : "missing", }, + { + id: "gate-execution-summary", + label: "Gate execution summary", + value: executionSummaryReady ? "ready" : "missing", + }, { id: "gate-result-matrix", label: "Gate result matrix", 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 91d5bd3..7c40510 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 @@ -469,7 +469,7 @@ TOOL_TABLE = browser-shell-tool.tbl ); assertEqual( workflowOverviewReleaseArtifactRenderResult.rowCount, - 7, + 8, "shell workflow overview release artifact render rows", ); assertEqual( @@ -477,6 +477,11 @@ TOOL_TABLE = browser-shell-tool.tbl true, "shell workflow overview release artifact execution manifest row", ); + assertEqual( + workflowOverviewReleaseArtifactRenderResult.rowIds.includes("gate-execution-summary"), + true, + "shell workflow overview release artifact execution summary row", + ); assertEqual( workflowOverviewReleaseArtifactMountResult.ready, true, 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 eeae078..85c92f9 100644 --- a/wasm-port/tests/browser/ini_panel_workflow_overview_smoke.html +++ b/wasm-port/tests/browser/ini_panel_workflow_overview_smoke.html @@ -262,14 +262,19 @@ .getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel(releaseArtifactUrlWorkflow); assertEqual(releaseArtifactDomReadinessReady.ready, true, "workflow overview release artifact DOM readiness"); assertEqual(releaseArtifactRenderResult.rendered, true, "workflow overview release artifact render result"); - assertEqual(releaseArtifactRenderResult.rowCount, 7, "workflow overview release artifact render row count"); + assertEqual(releaseArtifactRenderResult.rowCount, 8, "workflow overview release artifact render row count"); assertEqual( releaseArtifactRenderResult.rowIds.includes("gate-execution-manifest"), true, "workflow overview release artifact execution manifest row", ); + assertEqual( + releaseArtifactRenderResult.rowIds.includes("gate-execution-summary"), + true, + "workflow overview release artifact execution summary row", + ); assertEqual(releaseArtifactMountResult.ready, true, "workflow overview release artifact mount readiness"); - assertEqual(releaseArtifactMountResult.renderResult.rowCount, 7, "workflow overview release artifact mount rows"); + assertEqual(releaseArtifactMountResult.renderResult.rowCount, 8, "workflow overview release artifact mount rows"); assertEqual(releaseArtifactUrlWorkflow.ready, true, "workflow overview release artifact URL workflow readiness"); assertEqual(releaseArtifactUrlWorkflow.httpStatus, 200, "workflow overview release artifact URL workflow status"); assertEqual( diff --git a/wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs b/wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs index 1c09506..00ad59d 100644 --- a/wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +++ b/wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs @@ -35,6 +35,7 @@ for (const phrase of [ "createProjectReleaseGateResultMatrix()", "createProjectReleaseGateActionPlan()", "createProjectReleaseGateExecutionManifest()", + "createProjectReleaseGateExecutionSummaryViewModel()", "createProjectReleaseReadinessReport()", "createProjectReleaseReadinessSummaryViewModel()", "parseProjectReleaseReadinessArtifactJson()", @@ -118,6 +119,7 @@ for (const phrase of [ "createProjectReleaseGateManifest()", "createProjectReleaseGateResultMatrix()", "createProjectReleaseGateActionPlan()", + "createProjectReleaseGateExecutionSummaryViewModel()", "createProjectReleaseReadinessReport()", "createProjectReleaseReadinessSummaryViewModel()", "parseProjectReleaseReadinessArtifactJson()", @@ -127,6 +129,7 @@ for (const phrase of [ "createProjectReleaseReadinessArtifactValidationSummaryViewModel()", "gateManifestReady", "gateExecutionManifestReady", + "gateExecutionSummaryReady", "gateResultMatrixReady", "gateActionPlanReady", "loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl()", diff --git a/wasm-port/tests/fixtures/project-release-readiness-ready.json b/wasm-port/tests/fixtures/project-release-readiness-ready.json index d501b35..00e0823 100644 --- a/wasm-port/tests/fixtures/project-release-readiness-ready.json +++ b/wasm-port/tests/fixtures/project-release-readiness-ready.json @@ -327,6 +327,169 @@ } ] }, + "gateExecutionSummaryViewModel": { + "apiName": "project-release-gate-execution-summary-view-model", + "viewModelVersion": 1, + "phase": "ready", + "ready": true, + "title": "Project release gate execution", + "statusText": "Ready", + "detailText": "All release gate evidence present", + "statusLine": "Ready: All release gate evidence present", + "gateCount": 11, + "passedCount": 11, + "unknownCount": 0, + "missingGateIds": [], + "nextGateId": null, + "evidenceCounts": { + "explicit-result": 1, + "expected-output": 10 + }, + "rows": [ + { + "id": "gate-results", + "label": "Release gate evidence", + "value": "11/11 passed" + }, + { + "id": "unknown-gates", + "label": "Unknown gates", + "value": "0" + }, + { + "id": "next-gate", + "label": "Next gate", + "value": "none" + }, + { + "id": "expected-output-evidence", + "label": "Expected-output evidence", + "value": "10" + }, + { + "id": "explicit-result-evidence", + "label": "Explicit-result evidence", + "value": "1" + }, + { + "id": "missing", + "label": "Missing gate evidence", + "value": "none" + } + ], + "gateRows": [ + { + "id": "diff-check", + "label": "Git diff check", + "command": "git diff --check", + "expectedOutput": null, + "observedOutput": null, + "evidence": "explicit-result", + "status": "passed", + "passed": true + }, + { + "id": "vendor-sync", + "label": "Vendor sync guard", + "command": "wasm-port/tools/verify_vendor_sync.sh", + "expectedOutput": "vendor sync up to date", + "observedOutput": "vendor sync up to date", + "evidence": "expected-output", + "status": "passed", + "passed": true + }, + { + "id": "standalone-cnc-semantics", + "label": "Standalone CNC semantics guard", + "command": "wasm-port/tools/verify_no_standalone_cnc_semantics.sh", + "expectedOutput": "standalone CNC semantics guard complete", + "observedOutput": "standalone CNC semantics guard complete", + "evidence": "expected-output", + "status": "passed", + "passed": true + }, + { + "id": "interp-wasm", + "label": "Interpreter WASM Node smoke", + "command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh", + "expectedOutput": "interp_wasm_node_smoke=ok", + "observedOutput": "interp_wasm_node_smoke=ok", + "evidence": "expected-output", + "status": "passed", + "passed": true + }, + { + "id": "sim-config-inventory", + "label": "Sim config inventory WASM Node smoke", + "command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh", + "expectedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0", + "observedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0", + "evidence": "expected-output", + "status": "passed", + "passed": true + }, + { + "id": "ini-panel-browser", + "label": "INI panel browser smoke", + "command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh", + "expectedOutput": "browser_ini_shell_integration_workflow_smoke=ok", + "observedOutput": "browser_ini_shell_integration_workflow_smoke=ok", + "evidence": "expected-output", + "status": "passed", + "passed": true + }, + { + "id": "opfs-session-browser", + "label": "OPFS session browser workflow smoke", + "command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh", + "expectedOutput": "browser_opfs_session_workflow_smoke=ok", + "observedOutput": "browser_opfs_session_workflow_smoke=ok", + "evidence": "expected-output", + "status": "passed", + "passed": true + }, + { + "id": "release-artifact-url-browser", + "label": "Release artifact URL browser workflow smoke", + "command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh", + "expectedOutput": "browser_release_artifact_url_workflow_smoke=ok", + "observedOutput": "browser_release_artifact_url_workflow_smoke=ok", + "evidence": "expected-output", + "status": "passed", + "passed": true + }, + { + "id": "ui-node-smokes", + "label": "UI Node smokes", + "command": "wasm-port/tests/ui/node/verify_ui_node_smokes.sh", + "expectedOutput": "ui_node_smokes=ok", + "observedOutput": "ui_node_smokes=ok", + "evidence": "expected-output", + "status": "passed", + "passed": true + }, + { + "id": "host-smokes", + "label": "Host aggregate smoke", + "command": "wasm-port/tests/host/verify_host_smokes.sh", + "expectedOutput": "host_wasm_opfs_browser_smokes=ok", + "observedOutput": "host_wasm_opfs_browser_smokes=ok", + "evidence": "expected-output", + "status": "passed", + "passed": true + }, + { + "id": "project-release-gate", + "label": "Project release gate", + "command": "wasm-port/tests/host/verify_project_release_gate.sh", + "expectedOutput": "project_release_gate=ok", + "observedOutput": "project_release_gate=ok", + "evidence": "expected-output", + "status": "passed", + "passed": true + } + ] + }, "gateResultMatrix": { "apiName": "project-release-gate-result-matrix", "matrixVersion": 1, diff --git a/wasm-port/tests/host/verify_project_release_readiness_artifact.mjs b/wasm-port/tests/host/verify_project_release_readiness_artifact.mjs index 66fb19f..d20e073 100644 --- a/wasm-port/tests/host/verify_project_release_readiness_artifact.mjs +++ b/wasm-port/tests/host/verify_project_release_readiness_artifact.mjs @@ -24,6 +24,7 @@ assert.equal(validation.gateCount, 11); assert.equal(validation.expectedGateCount, 11); assert.equal(validation.gateManifestReady, true); assert.equal(validation.gateExecutionManifestReady, true); +assert.equal(validation.gateExecutionSummaryReady, true); assert.equal(validation.gateResultMatrixReady, true); assert.equal(validation.gateActionPlanReady, true); assert.deepEqual(validation.expectedGateIds, [ @@ -42,6 +43,8 @@ assert.deepEqual(validation.expectedGateIds, [ assert.equal(artifact.gateManifest.apiName, "project-release-gate-manifest"); assert.equal(artifact.gateExecutionManifest.apiName, "project-release-gate-execution-manifest"); assert.equal(artifact.gateExecutionManifest.passedCount, 11); +assert.equal(artifact.gateExecutionSummaryViewModel.apiName, "project-release-gate-execution-summary-view-model"); +assert.equal(artifact.gateExecutionSummaryViewModel.rows[0].value, "11/11 passed"); assert.equal(artifact.gateResultMatrix.apiName, "project-release-gate-result-matrix"); assert.equal(artifact.gateActionPlan.apiName, "project-release-gate-action-plan"); assert.equal(artifact.gateActionPlan.pendingCount, 0); diff --git a/wasm-port/tests/sdk/node/verify_project_release_gate_manifest.mjs b/wasm-port/tests/sdk/node/verify_project_release_gate_manifest.mjs index fe45102..4ae9f31 100644 --- a/wasm-port/tests/sdk/node/verify_project_release_gate_manifest.mjs +++ b/wasm-port/tests/sdk/node/verify_project_release_gate_manifest.mjs @@ -3,6 +3,7 @@ import assert from "node:assert/strict"; import { createProjectReleaseGateActionPlan, createProjectReleaseGateExecutionManifest, + createProjectReleaseGateExecutionSummaryViewModel, createProjectReleaseGateManifest, createProjectReleaseGateResultMatrix, createProjectReleaseReadinessArtifactJsonWorkflow, @@ -126,6 +127,37 @@ assert.equal( partialExecutionManifest.rows.find(({ id }) => id === "standalone-cnc-semantics")?.observedOutput, null, ); +const partialExecutionSummary = createProjectReleaseGateExecutionSummaryViewModel(partialExecutionManifest); +assert.equal(partialExecutionSummary.apiName, "project-release-gate-execution-summary-view-model"); +assert.equal(partialExecutionSummary.viewModelVersion, 1); +assert.equal(partialExecutionSummary.phase, "waiting"); +assert.equal(partialExecutionSummary.ready, false); +assert.equal(partialExecutionSummary.statusLine, "Waiting: standalone-cnc-semantics, interp-wasm, sim-config-inventory, ini-panel-browser, opfs-session-browser, ui-node-smokes, host-smokes, project-release-gate"); +assert.equal(partialExecutionSummary.gateCount, 11); +assert.equal(partialExecutionSummary.passedCount, 3); +assert.equal(partialExecutionSummary.unknownCount, 8); +assert.equal(partialExecutionSummary.nextGateId, "standalone-cnc-semantics"); +assert.deepEqual(partialExecutionSummary.evidenceCounts, { + "explicit-result": 1, + "expected-output": 2, + missing: 8, +}); +assert.deepEqual( + partialExecutionSummary.rows.map(({ id, value }) => [id, value]), + [ + ["gate-results", "3/11 passed"], + ["unknown-gates", "8"], + ["next-gate", "standalone-cnc-semantics"], + ["expected-output-evidence", "2"], + ["explicit-result-evidence", "1"], + ["missing", "standalone-cnc-semantics, interp-wasm, sim-config-inventory, ini-panel-browser, opfs-session-browser, ui-node-smokes, host-smokes, project-release-gate"], + ], +); +assert.equal(partialExecutionSummary.gateRows.length, manifest.gateCount); +assert.equal( + partialExecutionSummary.gateRows.find(({ id }) => id === "vendor-sync")?.evidence, + "expected-output", +); const partialActionPlan = createProjectReleaseGateActionPlan({ gateResultMatrix: partialMatrix }); assert.equal(partialActionPlan.apiName, "project-release-gate-action-plan"); @@ -166,6 +198,10 @@ assert.deepEqual(report.gateManifest, manifest); assert.equal(report.gateExecutionManifest.apiName, "project-release-gate-execution-manifest"); assert.equal(report.gateExecutionManifest.ready, true); assert.equal(report.gateExecutionManifest.passedCount, manifest.gateCount); +assert.equal(report.gateExecutionSummaryViewModel.apiName, "project-release-gate-execution-summary-view-model"); +assert.equal(report.gateExecutionSummaryViewModel.statusLine, "Ready: All release gate evidence present"); +assert.equal(report.gateExecutionSummaryViewModel.rows[0].value, "11/11 passed"); +assert.equal(report.gateExecutionSummaryViewModel.nextGateId, null); assert.equal(report.gateResultMatrix.ready, true); assert.equal(report.gateResultMatrix.passedCount, manifest.gateCount); assert.equal(report.gateActionPlan.ready, true); @@ -256,6 +292,7 @@ assert.deepEqual( ["gate-count", "11/11"], ["gate-manifest", "ready"], ["gate-execution-manifest", "ready"], + ["gate-execution-summary", "ready"], ["gate-result-matrix", "ready"], ["gate-action-plan", "ready"], ["missing", "none"], @@ -383,6 +420,10 @@ assert.equal( staleArtifactValidationSummary.rows.find(({ id }) => id === "gate-execution-manifest")?.value, "missing", ); +assert.equal( + staleArtifactValidationSummary.rows.find(({ id }) => id === "gate-execution-summary")?.value, + "missing", +); assert.equal(staleArtifactValidationActionPlan.phase, "blocked"); assert.equal(staleArtifactValidationActionPlan.ready, false); assert.equal(staleArtifactValidationActionPlan.missingCount, staleArtifactValidation.missing.length); diff --git a/wasm-port/tests/sdk/node/verify_sdk_surface.mjs b/wasm-port/tests/sdk/node/verify_sdk_surface.mjs index 07bb2c5..4d53ef0 100644 --- a/wasm-port/tests/sdk/node/verify_sdk_surface.mjs +++ b/wasm-port/tests/sdk/node/verify_sdk_surface.mjs @@ -36,6 +36,7 @@ import { createMachineSessionPersistenceSummary, createProjectReleaseGateActionPlan, createProjectReleaseGateExecutionManifest, + createProjectReleaseGateExecutionSummaryViewModel, createProjectReleaseGateManifest, createProjectReleaseGateResultMatrix, createProjectReleaseReadinessArtifactJsonWorkflow, @@ -98,6 +99,7 @@ const requiredExports = [ ["createLinuxCncInterpSdk", createLinuxCncInterpSdk], ["createProjectReleaseGateActionPlan", createProjectReleaseGateActionPlan], ["createProjectReleaseGateExecutionManifest", createProjectReleaseGateExecutionManifest], + ["createProjectReleaseGateExecutionSummaryViewModel", createProjectReleaseGateExecutionSummaryViewModel], ["createProjectReleaseGateManifest", createProjectReleaseGateManifest], ["createProjectReleaseGateResultMatrix", createProjectReleaseGateResultMatrix], ["createProjectReleaseReadinessArtifactJsonWorkflow", createProjectReleaseReadinessArtifactJsonWorkflow], @@ -294,6 +296,7 @@ assert.deepEqual(releaseReadinessArtifactValidation, { expectedGateIds: createProjectReleaseGateManifest().gateIds, gateManifestReady: true, gateExecutionManifestReady: true, + gateExecutionSummaryReady: true, gateResultMatrixReady: true, gateActionPlanReady: true, blockedRuntimeFamilies: [ @@ -322,6 +325,11 @@ assert.deepEqual(releaseReadinessArtifactValidation, { label: "Gate execution manifest", value: "ready", }, + { + id: "gate-execution-summary", + label: "Gate execution summary", + value: "ready", + }, { id: "gate-result-matrix", label: "Gate result matrix", @@ -426,6 +434,7 @@ assert.deepEqual( "blockedRuntimeFamilies", "gateManifest", "gateExecutionManifest", + "gateExecutionSummaryViewModel", "gateResultMatrix", "gateActionPlan", "gates.length", 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 832c23a..64fc631 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 @@ -1366,18 +1366,22 @@ const releaseArtifactMountResult = mountIniPanelShellWorkflowOverviewReleaseRead rowsNode: releaseArtifactRowsNode, }); assert.equal(releaseArtifactRenderResult.rendered, true); -assert.equal(releaseArtifactRenderResult.rowCount, 7); +assert.equal(releaseArtifactRenderResult.rowCount, 8); assert.equal(releaseArtifactRenderResult.dataset.handoffScope, "workflow-overview-release-readiness-artifact"); assert.equal( releaseArtifactRenderResult.rowIds.includes("gate-execution-manifest"), true, ); +assert.equal( + releaseArtifactRenderResult.rowIds.includes("gate-execution-summary"), + true, +); assert.equal( releaseArtifactRenderResult.rowIds.includes("gate-action-plan"), true, ); assert.equal(releaseArtifactMountResult.ready, true); -assert.equal(releaseArtifactMountResult.renderResult.rowCount, 7); +assert.equal(releaseArtifactMountResult.renderResult.rowCount, 8); assert.deepEqual( validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson("{").missing, ["artifact-json"], @@ -1418,7 +1422,7 @@ assert.equal( ); assert.equal(releaseArtifactUrlWorkflow.renderState.statusLine, "Ready: No blocking reasons"); assert.equal(releaseArtifactUrlWorkflow.mountResult.ready, true); -assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 7); +assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 8); const releaseArtifactUrlWorkflowSummary = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel( releaseArtifactUrlWorkflow,