diff --git a/wasm-port/runtime/ui/ini-panel/ui-shell.js b/wasm-port/runtime/ui/ini-panel/ui-shell.js index 68de1ff..62d6218 100644 --- a/wasm-port/runtime/ui/ini-panel/ui-shell.js +++ b/wasm-port/runtime/ui/ini-panel/ui-shell.js @@ -3990,6 +3990,20 @@ export function createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownVi sourceKind: classifyPromotionFamilySource(sourceFile), manifestBacked: candidate?.manifestChecked === true, })); + const evidenceRows = candidateRows.map((row, index) => ({ + id: `evidence-${index + 1}`, + label: row.id ?? `Evidence ${index + 1}`, + value: row.gcodePath ?? "not provided", + familyId: row.simConfigTarget ?? "not provided", + sourceFileCount: Array.isArray(row.sourceFiles) ? row.sourceFiles.length : 0, + manifestBacked: row.manifestChecked === true, + status: row.complete === true && + row.currentNodeInventoryStatus === "PASS" && + row.blockedKind === "-" && + row.targetBrowserEvidence === "explicit-browser-diagnostics" + ? "evidence-ready" + : "blocked", + })); return { apiName: "ini-panel-shell-workflow-overview-promotion-candidate-drilldown-view-model", viewModelVersion: 1, @@ -4005,7 +4019,9 @@ export function createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownVi candidate, rows, sourceRows, + evidenceRows, sourceFileCount: sourceRows.length, + evidenceRowCount: evidenceRows.length, requiredReports, blockedFamilyExcluded, nodeInventoryReady, @@ -4029,11 +4045,19 @@ export function createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownRe ...row, label: `${row.label} (${row.sourceKind})`, })), - ].map(({ id, label, value, sourceKind, manifestBacked }) => ({ + ...(Array.isArray(viewModel?.evidenceRows) ? viewModel.evidenceRows : []).map((row) => ({ + ...row, + label: `${row.label} (${row.familyId})`, + evidenceReady: row.status === "evidence-ready", + })), + ].map(({ id, label, value, sourceKind, manifestBacked, familyId, sourceFileCount, evidenceReady }) => ({ id, label, value: value == null ? "" : String(value), ...(sourceKind ? { sourceKind } : {}), + ...(familyId ? { familyId } : {}), + ...(sourceFileCount != null ? { sourceFileCount: Number(sourceFileCount) } : {}), + ...(evidenceReady != null ? { evidenceReady: evidenceReady === true } : {}), ...(manifestBacked != null ? { manifestBacked: manifestBacked === true } : {}), })); const ready = viewModel?.ready === true; @@ -4050,6 +4074,7 @@ export function createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownRe rows, rowCount: rows.length, sourceFileCount: viewModel?.sourceFileCount ?? 0, + evidenceRowCount: viewModel?.evidenceRowCount ?? 0, dataset: viewModel?.dataset ?? { handoffPhase: ready ? "ready" : "blocked", handoffReady: ready ? "true" : "false", @@ -4154,6 +4179,12 @@ export function renderIniPanelShellWorkflowOverviewPromotionCandidateDrilldownSt term.dataset[`${effectiveRowDatasetPrefix}SourceKind`] = row.sourceKind; term.dataset[`${effectiveRowDatasetPrefix}ManifestBacked`] = row.manifestBacked ? "true" : "false"; } + if (row.familyId) { + term.dataset[`${effectiveRowDatasetPrefix}FamilyId`] = row.familyId; + term.dataset[`${effectiveRowDatasetPrefix}EvidenceReady`] = row.evidenceReady ? "true" : "false"; + term.dataset[`${effectiveRowDatasetPrefix}SourceFileCount`] = String(row.sourceFileCount ?? 0); + term.dataset[`${effectiveRowDatasetPrefix}ManifestBacked`] = row.manifestBacked ? "true" : "false"; + } term.textContent = row.label; const detail = documentRef.createElement("dd"); detail.dataset[`${effectiveRowDatasetPrefix}Value`] = row.id; @@ -4162,6 +4193,12 @@ export function renderIniPanelShellWorkflowOverviewPromotionCandidateDrilldownSt detail.dataset[`${effectiveRowDatasetPrefix}SourceKind`] = row.sourceKind; detail.dataset[`${effectiveRowDatasetPrefix}ManifestBacked`] = row.manifestBacked ? "true" : "false"; } + if (row.familyId) { + detail.dataset[`${effectiveRowDatasetPrefix}FamilyId`] = row.familyId; + detail.dataset[`${effectiveRowDatasetPrefix}EvidenceReady`] = row.evidenceReady ? "true" : "false"; + detail.dataset[`${effectiveRowDatasetPrefix}SourceFileCount`] = String(row.sourceFileCount ?? 0); + detail.dataset[`${effectiveRowDatasetPrefix}ManifestBacked`] = row.manifestBacked ? "true" : "false"; + } detail.textContent = row.value; rowsNode.append(term, detail); } @@ -4171,6 +4208,7 @@ export function renderIniPanelShellWorkflowOverviewPromotionCandidateDrilldownSt rowCount: rows.length, rowIds: rows.map(({ id }) => id), sourceFileCount: rows.filter(({ sourceKind }) => Boolean(sourceKind)).length, + evidenceRowCount: rows.filter(({ familyId }) => Boolean(familyId)).length, dataset: { handoffPhase: mountNode.dataset.handoffPhase, handoffReady: mountNode.dataset.handoffReady, 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 e857a8d..50ff912 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 @@ -10,6 +10,7 @@ import { createVirtualHalCommandScriptFixtureReport, createVirtualHalMotionControllerMatrixReport, createVirtualHalSimConfigMacroLoadFixtureReport, + createVirtualHalSimConfigEvidenceExpansionReport, createVirtualHalSimConfigPromotionCandidateReport, createVirtualHalSimConfigSourceCoverageReport, createVirtualHalSimulationReplacementReport, @@ -1431,6 +1432,13 @@ const releaseReadinessPromotionCandidates = createVirtualHalSimConfigPromotionCa ], motionControllerMatrix: releaseReadinessMotionControllerMatrix, }); +const releaseReadinessEvidenceExpansion = createVirtualHalSimConfigEvidenceExpansionReport({ + manifestEntries: [ + ...VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES, + "linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ini", + ], + motionControllerMatrix: releaseReadinessMotionControllerMatrix, +}); const releaseReadinessMacroLoadFixtures = createVirtualHalSimConfigMacroLoadFixtureReport({ manifestEntries: [ ...VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES, @@ -1445,6 +1453,7 @@ const releaseReadinessArtifactJson = JSON.stringify(createProjectReleaseReadines gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])), virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(), virtualHalSimConfigPromotionCandidates: releaseReadinessPromotionCandidates, + virtualHalSimConfigEvidenceExpansion: releaseReadinessEvidenceExpansion, virtualHalSimConfigMacroLoadFixtures: releaseReadinessMacroLoadFixtures, virtualHalMotionControllerMatrix: releaseReadinessMotionControllerMatrix, })); @@ -1457,6 +1466,7 @@ const browserDiagnosticsArtifact = { virtualHalSourceCompliance: createVirtualHalSourceComplianceReport({ halState: browserDiagnosticsVirtualHalState }), virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(), virtualHalSimConfigPromotionCandidates: releaseReadinessPromotionCandidates, + virtualHalSimConfigEvidenceExpansion: releaseReadinessEvidenceExpansion, virtualHalPromotionCandidateSummary: { apiName: "real-browser-simulation-promotion-candidate-summary", summaryVersion: 1, @@ -2013,6 +2023,22 @@ assert.equal( "passed", ); assert.equal(promotionCandidateDrilldownViewModel.sourceFileCount, 2); +assert.equal(promotionCandidateDrilldownViewModel.evidenceRowCount, 8); +assert.equal( + promotionCandidateDrilldownViewModel.evidenceRows.every(({ status, manifestBacked }) => + status === "evidence-ready" && manifestBacked === true + ), + true, +); +assert.equal( + promotionCandidateDrilldownViewModel.evidenceRows.some(({ label, value, familyId, sourceFileCount }) => + label === "puma-seam-weld" && + value === "linuxcnc/configs/sim/axis/vismach/puma/puma_seam_weld.ngc" && + familyId === "vismach-remap-sims" && + sourceFileCount === 3 + ), + true, +); assert.deepEqual( promotionCandidateDrilldownViewModel.sourceRows.map(({ value, sourceKind, manifestBacked }) => [ value, @@ -2025,8 +2051,9 @@ assert.deepEqual( ], ); assert.equal(promotionCandidateDrilldownRenderState.ready, true); -assert.equal(promotionCandidateDrilldownRenderState.rowCount, 11); +assert.equal(promotionCandidateDrilldownRenderState.rowCount, 19); assert.equal(promotionCandidateDrilldownRenderState.sourceFileCount, 2); +assert.equal(promotionCandidateDrilldownRenderState.evidenceRowCount, 8); assert.equal( createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomContract().selectors.mount, "[data-workflow-overview-promotion-candidate-drilldown]", @@ -2066,7 +2093,8 @@ const promotionCandidateDrilldownMountResult = rowsNode: promotionCandidateDrilldownRowsNode, }); assert.equal(promotionCandidateDrilldownRenderResult.rendered, true); -assert.equal(promotionCandidateDrilldownRenderResult.rowCount, 11); +assert.equal(promotionCandidateDrilldownRenderResult.rowCount, 19); +assert.equal(promotionCandidateDrilldownRenderResult.evidenceRowCount, 8); assert.deepEqual(promotionCandidateDrilldownRenderResult.dataset, { handoffPhase: "ready", handoffReady: "true", @@ -2082,8 +2110,16 @@ assert.equal(promotionCandidateDrilldownRowsNode.children.some((node) => node.dataset?.workflowOverviewPromotionCandidateDrilldownManifestBacked === "true" && node.textContent === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc" ), true); +assert.equal(promotionCandidateDrilldownRowsNode.children.some((node) => + node.dataset?.workflowOverviewPromotionCandidateDrilldownValue === "evidence-7" && + node.dataset?.workflowOverviewPromotionCandidateDrilldownFamilyId === "vismach-remap-sims" && + node.dataset?.workflowOverviewPromotionCandidateDrilldownEvidenceReady === "true" && + node.dataset?.workflowOverviewPromotionCandidateDrilldownSourceFileCount === "3" && + node.textContent === "linuxcnc/configs/sim/axis/vismach/puma/puma_seam_weld.ngc" +), true); assert.equal(promotionCandidateDrilldownMountResult.ready, true); assert.equal(promotionCandidateDrilldownMountResult.renderResult.sourceFileCount, 2); +assert.equal(promotionCandidateDrilldownMountResult.renderResult.evidenceRowCount, 8); assert.equal( releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-families")?.value, expectedPromotionFamilySummary,