diff --git a/wasm-port/docs/sim-config-coverage-promotion-analysis.md b/wasm-port/docs/sim-config-coverage-promotion-analysis.md index 038a48b..d6f6e76 100644 --- a/wasm-port/docs/sim-config-coverage-promotion-analysis.md +++ b/wasm-port/docs/sim-config-coverage-promotion-analysis.md @@ -611,6 +611,24 @@ event, and verifies that the source filter DOM re-renders with 2 rose-engine source rows. This keeps the browser integration path explicit without changing promotion status or CNC semantics. +## 2026-06-18 Release URL Promotion Summary Counts + +The release artifact URL workflow summary now exposes the promotion candidate +summary counts as first-class rows: + +```text +virtual-hal-promotion-family-count=3 families +virtual-hal-promotion-source-file-count=17 source files +virtual-hal-promotion-browser-diagnostics-count=8 diagnostics-ready +``` + +These rows are read from the existing browser diagnostics +`virtualHalPromotionCandidateSummary`. They make the current evidence-ready +promotion set easier to inspect from SDK, ini-panel, and browser workflow +surfaces. They do not add virtual HAL runtime capability, do not alter +`promotion-candidates.tsv`, and do not change the inventory baseline or any +hard-block lock. + ## Boundary Statement This analysis relies on the improved virtual HAL only for Web simulation diff --git a/wasm-port/runtime/sdk/src/project-release-readiness.js b/wasm-port/runtime/sdk/src/project-release-readiness.js index 1324f6d..a8f99e8 100644 --- a/wasm-port/runtime/sdk/src/project-release-readiness.js +++ b/wasm-port/runtime/sdk/src/project-release-readiness.js @@ -2611,6 +2611,27 @@ export function createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel ? `${promotionCandidateSummary.readyCandidateCount}/${promotionCandidateSummary.candidateCount} ready` : "not provided", }, + { + id: "virtual-hal-promotion-family-count", + label: "Promotion family count", + value: Number.isFinite(promotionCandidateSummary.familyCount) + ? `${promotionCandidateSummary.familyCount} families` + : "not provided", + }, + { + id: "virtual-hal-promotion-source-file-count", + label: "Promotion source files", + value: Number.isFinite(promotionCandidateSummary.sourceFileCount) + ? `${promotionCandidateSummary.sourceFileCount} source files` + : "not provided", + }, + { + id: "virtual-hal-promotion-browser-diagnostics-count", + label: "Promotion browser diagnostics", + value: Number.isFinite(promotionCandidateSummary.explicitBrowserDiagnosticsCount) + ? `${promotionCandidateSummary.explicitBrowserDiagnosticsCount} diagnostics-ready` + : "not provided", + }, { id: "virtual-hal-promotion-candidate-baseline", label: "Candidate inventory baseline", diff --git a/wasm-port/runtime/ui/ini-panel/ui-shell.js b/wasm-port/runtime/ui/ini-panel/ui-shell.js index 651dcdc..5142491 100644 --- a/wasm-port/runtime/ui/ini-panel/ui-shell.js +++ b/wasm-port/runtime/ui/ini-panel/ui-shell.js @@ -2634,6 +2634,27 @@ export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWo ? `${promotionCandidateSummary.readyCandidateCount}/${promotionCandidateSummary.candidateCount} ready` : "not provided", }, + { + id: "virtual-hal-promotion-family-count", + label: "Promotion family count", + value: Number.isFinite(promotionCandidateSummary.familyCount) + ? `${promotionCandidateSummary.familyCount} families` + : "not provided", + }, + { + id: "virtual-hal-promotion-source-file-count", + label: "Promotion source files", + value: Number.isFinite(promotionCandidateSummary.sourceFileCount) + ? `${promotionCandidateSummary.sourceFileCount} source files` + : "not provided", + }, + { + id: "virtual-hal-promotion-browser-diagnostics-count", + label: "Promotion browser diagnostics", + value: Number.isFinite(promotionCandidateSummary.explicitBrowserDiagnosticsCount) + ? `${promotionCandidateSummary.explicitBrowserDiagnosticsCount} diagnostics-ready` + : "not provided", + }, { id: "virtual-hal-promotion-candidate-baseline", label: "Candidate inventory baseline", 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 74e1f57..51b3cc4 100644 --- a/wasm-port/tests/browser/ini_panel_workflow_overview_smoke.html +++ b/wasm-port/tests/browser/ini_panel_workflow_overview_smoke.html @@ -597,7 +597,7 @@ ); assertEqual( releaseArtifactUrlWorkflowRenderResult.rowCount, - 18, + 21, "workflow overview release artifact URL workflow render rows", ); assertEqual( @@ -630,6 +630,21 @@ "8/8 ready", "workflow overview release artifact URL workflow candidate count", ); + assertEqual( + releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-family-count")?.value, + "3 families", + "workflow overview release artifact URL workflow family count", + ); + assertEqual( + releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-source-file-count")?.value, + "17 source files", + "workflow overview release artifact URL workflow source file count", + ); + assertEqual( + releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-browser-diagnostics-count")?.value, + "8 diagnostics-ready", + "workflow overview release artifact URL workflow browser diagnostics count", + ); assertEqual( releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value, "executed=28 passed=28 skipped=131 unexpected_fail=0", @@ -751,6 +766,11 @@ "8/8 ready", "workflow overview fixed URL workflow form candidate count row", ); + assertEqual( + workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-value="virtual-hal-promotion-source-file-count"]').textContent, + "17 source files", + "workflow overview fixed URL workflow form source file count row", + ); assertEqual( workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-action-plan-value="next-command"]').textContent, "none", diff --git a/wasm-port/tests/sdk/node/verify_project_release_artifact_url_workflow.mjs b/wasm-port/tests/sdk/node/verify_project_release_artifact_url_workflow.mjs index 65a6207..7314e4c 100644 --- a/wasm-port/tests/sdk/node/verify_project_release_artifact_url_workflow.mjs +++ b/wasm-port/tests/sdk/node/verify_project_release_artifact_url_workflow.mjs @@ -162,13 +162,25 @@ assert.equal( readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-count")?.value, "8/8 ready", ); +assert.equal( + readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-family-count")?.value, + "3 families", +); +assert.equal( + readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-source-file-count")?.value, + "17 source files", +); +assert.equal( + readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-browser-diagnostics-count")?.value, + "8 diagnostics-ready", +); assert.equal( readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value, "executed=28 passed=28 skipped=131 unexpected_fail=0", ); assert.deepEqual( readySummary.rows - .filter(({ id }) => id.startsWith("virtual-hal-promotion-family-")) + .filter(({ id }) => expectedPromotionFamilyRows.some(([familyId]) => familyId === id)) .map(({ id, value }) => [id, value]), expectedPromotionFamilyRows, ); @@ -190,6 +202,9 @@ assert.deepEqual( ["virtual-hal-promotion-candidate-ini", "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini"], ["virtual-hal-promotion-candidate-gcode", "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc"], ["virtual-hal-promotion-candidate-count", "8/8 ready"], + ["virtual-hal-promotion-family-count", "3 families"], + ["virtual-hal-promotion-source-file-count", "17 source files"], + ["virtual-hal-promotion-browser-diagnostics-count", "8 diagnostics-ready"], ["virtual-hal-promotion-candidate-baseline", "executed=28 passed=28 skipped=131 unexpected_fail=0"], ["saved-session-diagnostics", "ready"], ["next-command", "none"], 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 1d3edc7..1157280 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 @@ -457,6 +457,9 @@ assert.deepEqual( ["virtual-hal-promotion-candidate-ini", "not provided"], ["virtual-hal-promotion-candidate-gcode", "not provided"], ["virtual-hal-promotion-candidate-count", "not provided"], + ["virtual-hal-promotion-family-count", "not provided"], + ["virtual-hal-promotion-source-file-count", "not provided"], + ["virtual-hal-promotion-browser-diagnostics-count", "not provided"], ["virtual-hal-promotion-candidate-baseline", "not provided"], ["saved-session-diagnostics", "not requested"], ["next-command", "none"], diff --git a/wasm-port/tests/sdk/node/verify_sdk_surface.mjs b/wasm-port/tests/sdk/node/verify_sdk_surface.mjs index 59458ef..901d7d4 100644 --- a/wasm-port/tests/sdk/node/verify_sdk_surface.mjs +++ b/wasm-port/tests/sdk/node/verify_sdk_surface.mjs @@ -1680,7 +1680,7 @@ assert.deepEqual( createProjectReleaseReadinessArtifactValidationSummaryViewModel( releaseReadinessArtifactValidation, ).rows - .filter(({ id }) => id.startsWith("virtual-hal-promotion-family-")) + .filter(({ id }) => expectedPromotionFamilyRows.some(([familyId]) => familyId === id)) .map(({ id, value }) => [id, value]), expectedPromotionFamilyRows, ); @@ -1751,7 +1751,7 @@ assert.deepEqual( }), }), ).rows - .filter(({ id }) => id.startsWith("virtual-hal-promotion-family-")) + .filter(({ id }) => expectedPromotionFamilyRows.some(([familyId]) => familyId === id)) .map(({ id, value }) => [id, value]), expectedPromotionFamilyRows, ); 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 0629ad7..5e6a5bc 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 @@ -1939,6 +1939,18 @@ assert.equal( releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-count")?.value, "8/8 ready", ); +assert.equal( + releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-family-count")?.value, + "3 families", +); +assert.equal( + releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-source-file-count")?.value, + "17 source files", +); +assert.equal( + releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-browser-diagnostics-count")?.value, + "8 diagnostics-ready", +); assert.equal( releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value, "executed=28 passed=28 skipped=131 unexpected_fail=0", @@ -2058,7 +2070,7 @@ assert.equal( ); assert.deepEqual( releaseArtifactUrlWorkflowSummary.rows - .filter(({ id }) => id.startsWith("virtual-hal-promotion-family-")) + .filter(({ id }) => expectedPromotionFamilyRows.some(([familyId]) => familyId === id)) .map(({ id, value }) => [id, value]), expectedPromotionFamilyRows, ); @@ -2565,7 +2577,7 @@ const releaseArtifactUrlWorkflowMountResult = rowsNode: releaseArtifactUrlWorkflowRowsNode, }); assert.equal(releaseArtifactUrlWorkflowRenderResult.rendered, true); -assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 22); +assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 25); assert.equal( releaseArtifactUrlWorkflowRenderResult.rowIds.includes("browser-diagnostics"), true, @@ -2583,7 +2595,7 @@ assert.equal( true, ); assert.equal(releaseArtifactUrlWorkflowMountResult.ready, true); -assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 22); +assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 25); const releaseArtifactUrlWorkflowMissingDom = await loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl({ artifactUrl: "/project-release-readiness.json", fetchRef: async () => ({