From 00af30c0740c2f21a12037e9e6cbf95acc185b86 Mon Sep 17 00:00:00 2001 From: wangdequan Date: Thu, 18 Jun 2026 19:32:28 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B6=E6=9D=9Fpromotion=E5=80=99=E9=80=89?= =?UTF-8?q?=E8=AF=81=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 结论:按text22铁律完成promotion-candidates双层证据管理,baseline保持28/28/131/0,Python remap、tool DB、external user-M hard block继续locked。 --- wasm-port/docs/compatibility-validation.md | 10 + wasm-port/docs/project-release-handoff.md | 12 + wasm-port/runtime/opfs/snapshot-store.js | 13 + wasm-port/runtime/sdk/README.md | 9 + wasm-port/runtime/sdk/src/index.js | 2 + wasm-port/runtime/sdk/src/linuxcnc-hal.js | 167 ++++++++ .../sdk/src/project-release-readiness.js | 366 ++++++++++++++++++ wasm-port/runtime/ui/simulation/index.html | 16 + .../runtime/ui/simulation/simulation-app.js | 2 + ...anel_shell_integration_workflow_smoke.html | 17 +- .../ini_panel_workflow_overview_smoke.html | 52 ++- .../project-release-readiness-ready.json | 69 ++++ ...ify_project_release_readiness_artifact.mjs | 19 + ..._project_release_artifact_url_workflow.mjs | 38 ++ .../verify_project_release_gate_manifest.mjs | 27 ++ .../tests/sdk/node/verify_sdk_surface.mjs | 140 +++++++ .../ui/node/verify_ini_panel_ui_shell.mjs | 18 +- .../verify_sim_configs_inventory_wasm.mjs | 157 ++++++++ 18 files changed, 1129 insertions(+), 5 deletions(-) diff --git a/wasm-port/docs/compatibility-validation.md b/wasm-port/docs/compatibility-validation.md index db6fcbd..8caa4ab 100644 --- a/wasm-port/docs/compatibility-validation.md +++ b/wasm-port/docs/compatibility-validation.md @@ -238,6 +238,16 @@ ready while the Node inventory baseline remains unchanged, and inventory baseline. The current direct inventory `promotion_allowed=1` count is zero: all skipped main rows are still hard-blocked by `L4-PYTHON-REMAP`, `L4-TOOL-DB`, `L4-USER-M-PROCESS`, or preserved as `UPSTREAM-DEMO`. +The project release readiness artifact and URL workflow publish the same +candidate artifact summary for callers: `evidence-ready=8`, +`inventory-ready=20`, total candidates `28`, and `promotion_allowed=0`. +These counts are release visibility for the existing TSV, not a baseline +promotion or hard-block unlock. +`evidence-expansion-candidates.tsv` separately records 13 current +`blocked=-`, native `PASS`, `class=main`, inventory `PASS` rows that are not +part of the current `evidence-ready` set yet. They are queued only for future +browser diagnostics binding; every row keeps `promotion_allowed=0` and does +not alter the `28/28/131/0` inventory baseline. `boundary-summary.tsv` records one row per native inventory path with the matrix blocked kind, SDK classifier recommendation, declared HAL/UI/HALUI/Python process dependencies, `[EMCIO]DB_PROGRAM`, user-M execution codes, and the diff --git a/wasm-port/docs/project-release-handoff.md b/wasm-port/docs/project-release-handoff.md index 1e303c4..38d7176 100644 --- a/wasm-port/docs/project-release-handoff.md +++ b/wasm-port/docs/project-release-handoff.md @@ -260,6 +260,18 @@ project_release_readiness_artifact=... project_release_readiness_artifact_node_smoke=ok ``` +The readiness artifact exposes the promotion candidate TSV as a +release-visible summary without treating it as a runtime unlock: + +```text +promotion-candidate-artifact=wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv +promotion-candidate-layers=evidence-ready=8 inventory-ready=20 +promotion-candidate-total=28 +evidence-expansion-candidates=13 +evidence-expansion-artifact=wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv +promotion-candidate-allowed=0 +``` + A passing aggregate ends with: ```text diff --git a/wasm-port/runtime/opfs/snapshot-store.js b/wasm-port/runtime/opfs/snapshot-store.js index 51ea754..0344247 100644 --- a/wasm-port/runtime/opfs/snapshot-store.js +++ b/wasm-port/runtime/opfs/snapshot-store.js @@ -8,12 +8,14 @@ import { toolTablePath, } from "./path-model.js"; import { + VIRTUAL_HAL_SIM_CONFIG_EVIDENCE_EXPANSION_CANDIDATES, VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES, VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES, VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS, cloneVirtualHalState, createVirtualHalCommandScriptFixtureReport, createVirtualHalMotionControllerMatrixReport, + createVirtualHalSimConfigEvidenceExpansionReport, createVirtualHalSimConfigMacroLoadFixtureReport, createVirtualHalSimConfigPromotionCandidateReport, createVirtualHalSimConfigSourceCoverageReport, @@ -87,6 +89,9 @@ function validateVirtualHalSessionPayload(virtualHal) { if (virtualHal.simConfigPromotionCandidates.complete !== true) { throw new Error("virtual HAL sim-config promotion candidates are incomplete."); } + if (virtualHal.simConfigEvidenceExpansion.complete !== true) { + throw new Error("virtual HAL sim-config evidence expansion candidates are incomplete."); + } if (virtualHal.simConfigMacroLoadFixtures.complete !== true) { throw new Error("virtual HAL sim-config macro/load fixtures are incomplete."); } @@ -109,6 +114,7 @@ export function createVirtualHalSessionPayload(halState = createVirtualHalState( manifestEntries: [ ...VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS.flatMap((target) => target.sourceFiles ?? []), ...VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES.flatMap((candidate) => candidate.sourceFiles ?? []), + ...VIRTUAL_HAL_SIM_CONFIG_EVIDENCE_EXPANSION_CANDIDATES.flatMap((candidate) => candidate.sourceFiles ?? []), ...VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES.flatMap((fixture) => fixture.sourceFiles ?? []), ], } @@ -125,6 +131,12 @@ export function createVirtualHalSessionPayload(halState = createVirtualHalState( commandScriptFixtures, motionControllerMatrix, }); + const simConfigEvidenceExpansion = options.simConfigEvidenceExpansion ?? createVirtualHalSimConfigEvidenceExpansionReport({ + ...reportOptions, + simConfigSourceCoverage, + commandScriptFixtures, + motionControllerMatrix, + }); const sourceCompliance = options.sourceCompliance ?? createVirtualHalSourceComplianceReport({ ...reportOptions, halState: state, @@ -148,6 +160,7 @@ export function createVirtualHalSessionPayload(halState = createVirtualHalState( sourceCompliance, simConfigSourceCoverage, simConfigPromotionCandidates, + simConfigEvidenceExpansion, simConfigMacroLoadFixtures, commandScriptFixtures, motionControllerMatrix, diff --git a/wasm-port/runtime/sdk/README.md b/wasm-port/runtime/sdk/README.md index c21a690..fc0ad26 100644 --- a/wasm-port/runtime/sdk/README.md +++ b/wasm-port/runtime/sdk/README.md @@ -72,6 +72,7 @@ import { VIRTUAL_HAL_PROJECT_PIN_GROUPS, VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES, VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE, + VIRTUAL_HAL_SIM_CONFIG_EVIDENCE_EXPANSION_CANDIDATES, VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS, VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES, VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS, @@ -96,6 +97,7 @@ import { createVirtualHalPinInventory, createVirtualHalPinRegistry, createVirtualHalProjectReport, + createVirtualHalSimConfigEvidenceExpansionReport, createVirtualHalSimConfigSourceCoverageReport, createVirtualHalSimulationReplacementReport, createVirtualHalSimulationRuntimeReport, @@ -329,6 +331,13 @@ private page state. files, current Node `PASS` evidence, blocked-family exclusions, target browser diagnostics evidence, and the required virtual HAL reports for QtDragon on-abort, PUMA seam-weld, and rose-engine promotion planning. +- `VIRTUAL_HAL_SIM_CONFIG_EVIDENCE_EXPANSION_CANDIDATES` and + `createVirtualHalSimConfigEvidenceExpansionReport()` expose the next + browser-diagnostics expansion candidates that already have Node `PASS`, + browser `REP`, and source-derived virtual HAL evidence. These rows remain + evidence planning artifacts only: they keep the inventory baseline unchanged + and do not unlock Python remap, tool DB, external user-M, upstream demo, or + asset-only rows. - `VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES`, `VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES`, and `VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_AUDIT_CANDIDATES` plus diff --git a/wasm-port/runtime/sdk/src/index.js b/wasm-port/runtime/sdk/src/index.js index f5419dc..97dc993 100644 --- a/wasm-port/runtime/sdk/src/index.js +++ b/wasm-port/runtime/sdk/src/index.js @@ -10,6 +10,7 @@ export { VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_AUDIT_CANDIDATES, VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES, VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES, + VIRTUAL_HAL_SIM_CONFIG_EVIDENCE_EXPANSION_CANDIDATES, VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES, VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE, VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES, @@ -36,6 +37,7 @@ export { createVirtualHalPinInventory, createVirtualHalPinRegistry, createVirtualHalProjectReport, + createVirtualHalSimConfigEvidenceExpansionReport, createVirtualHalSimConfigMacroLoadFixtureReport, createVirtualHalSimConfigPromotionCandidateReport, createVirtualHalSimConfigSourceCoverageReport, diff --git a/wasm-port/runtime/sdk/src/linuxcnc-hal.js b/wasm-port/runtime/sdk/src/linuxcnc-hal.js index cd68059..805898e 100644 --- a/wasm-port/runtime/sdk/src/linuxcnc-hal.js +++ b/wasm-port/runtime/sdk/src/linuxcnc-hal.js @@ -335,6 +335,66 @@ export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([ }), ]); +export const VIRTUAL_HAL_SIM_CONFIG_EVIDENCE_EXPANSION_CANDIDATES = Object.freeze([ + Object.freeze({ + id: "woodpecker-on-abort", + label: "Woodpecker on-abort", + simConfigTarget: "woodpecker", + sourceFiles: Object.freeze([ + "linuxcnc/configs/sim/woodpecker/woodpecker.ini", + "linuxcnc/configs/sim/woodpecker/on_abort.ngc", + "linuxcnc/configs/sim/woodpecker/tool.tbl", + ]), + iniPath: "linuxcnc/configs/sim/woodpecker/woodpecker.ini", + gcodePath: "linuxcnc/configs/sim/woodpecker/on_abort.ngc", + currentNodeInventoryStatus: "PASS", + currentMatrixBrowserStatus: "REP", + dependencyClass: "hal_process,ui_process", + targetBrowserEvidence: "browser-diagnostics-expansion", + promotionAllowed: false, + blockedKind: "-", + evidence: "Current inventory already covers Woodpecker as a non-hard-block representative; next step is explicit browser diagnostics binding only.", + }), + Object.freeze({ + id: "puma-cube", + label: "PUMA cube", + simConfigTarget: "vismach-remap-sims", + sourceFiles: Object.freeze([ + "linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ini", + "linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ngc", + "linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc", + ]), + iniPath: "linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ini", + gcodePath: "linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ngc", + currentNodeInventoryStatus: "PASS", + currentMatrixBrowserStatus: "REP", + dependencyClass: "hal_process,halui_mdi_process,ui_process", + targetBrowserEvidence: "browser-diagnostics-expansion", + promotionAllowed: false, + blockedKind: "-", + evidence: "PUMA cube is already the representative browser row for the PUMA machine context; it remains an expansion candidate, not a baseline promotion.", + }), + Object.freeze({ + id: "melfa-example", + label: "MELFA example", + simConfigTarget: "vismach-remap-sims", + sourceFiles: Object.freeze([ + "linuxcnc/configs/sim/axis/vismach/melfa-sim/melfa.ini", + "linuxcnc/configs/sim/axis/vismach/melfa-sim/example.ngc", + "linuxcnc/configs/sim/axis/vismach/melfa-sim/remap_subs/428remap.ngc", + ]), + iniPath: "linuxcnc/configs/sim/axis/vismach/melfa-sim/melfa.ini", + gcodePath: "linuxcnc/configs/sim/axis/vismach/melfa-sim/example.ngc", + currentNodeInventoryStatus: "PASS", + currentMatrixBrowserStatus: "REP", + dependencyClass: "hal_process,halui_mdi_process,ui_process", + targetBrowserEvidence: "browser-diagnostics-expansion", + promotionAllowed: false, + blockedKind: "-", + evidence: "MELFA example has current representative coverage and can receive explicit browser diagnostics evidence without unlocking any hard runtime family.", + }), +]); + export const VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES = Object.freeze([ Object.freeze({ id: "rose-engine-rcone-macro-load", @@ -3056,6 +3116,110 @@ export function createVirtualHalSimConfigPromotionCandidateReport(options = {}) }; } +export function createVirtualHalSimConfigEvidenceExpansionReport(options = {}) { + const candidates = options.candidates ?? VIRTUAL_HAL_SIM_CONFIG_EVIDENCE_EXPANSION_CANDIDATES; + const manifestProvided = typeof options.manifestText === "string" || Array.isArray(options.manifestEntries); + const manifestSet = new Set([ + ...sourceManifestSetFromText(options.manifestText ?? ""), + ...(options.manifestEntries ?? []).map(normalizeLinuxCncSourcePath), + ]); + const requiredVirtualHalReports = options.requiredVirtualHalReports ?? [ + "source-compliance", + "sim-config-source-coverage", + "command-script-fixtures", + "manifest-backed-motion-matrix", + ]; + const simConfigCoverage = options.simConfigSourceCoverage ?? createVirtualHalSimConfigSourceCoverageReport(options); + const sourceCompliance = options.sourceCompliance ?? createVirtualHalSourceComplianceReport(options); + const commandScriptFixtures = options.commandScriptFixtures ?? createVirtualHalCommandScriptFixtureReport(options); + const motionControllerMatrix = options.motionControllerMatrix ?? createVirtualHalMotionControllerMatrixReport(options); + const availableReports = new Set([ + ...(sourceCompliance.complete ? ["source-compliance"] : []), + ...(simConfigCoverage.complete ? ["sim-config-source-coverage"] : []), + ...(commandScriptFixtures.complete ? ["command-script-fixtures"] : []), + ...(motionControllerMatrix.complete && motionControllerMatrix.manifestChecked ? ["manifest-backed-motion-matrix"] : []), + ]); + const rows = candidates.map((candidate) => { + const sourceFiles = [...(candidate.sourceFiles ?? [])]; + const normalizedSourceFiles = sourceFiles.map(normalizeLinuxCncSourcePath); + const missingManifestFiles = manifestProvided + ? sourceFiles.filter((file, index) => !manifestSet.has(normalizedSourceFiles[index])) + : []; + const missingRequiredReports = requiredVirtualHalReports + .filter((report) => !availableReports.has(report)); + const sourceReady = sourceFiles.length > 0 && + missingManifestFiles.length === 0 && + missingRequiredReports.length === 0; + return { + id: candidate.id, + label: candidate.label, + source: "linuxcnc-configs-sim-source-derived-virtual-hal-evidence-expansion-candidate", + simConfigTarget: candidate.simConfigTarget ?? null, + sourceFiles, + iniPath: candidate.iniPath, + gcodePath: candidate.gcodePath, + currentNodeInventoryStatus: candidate.currentNodeInventoryStatus, + currentMatrixBrowserStatus: candidate.currentMatrixBrowserStatus, + dependencyClass: candidate.dependencyClass, + targetBrowserEvidence: candidate.targetBrowserEvidence, + requiredVirtualHalReports, + blockedKind: candidate.blockedKind, + promotionAllowed: candidate.promotionAllowed === true, + nodeStatusReady: candidate.currentNodeInventoryStatus === "PASS", + browserRepresentativeReady: candidate.currentMatrixBrowserStatus === "REP", + manifestChecked: manifestProvided, + missingManifestFiles, + missingRequiredReports, + evidence: candidate.evidence, + sourceReady, + complete: sourceReady && + candidate.blockedKind === "-" && + candidate.promotionAllowed !== true && + candidate.currentNodeInventoryStatus === "PASS" && + candidate.currentMatrixBrowserStatus === "REP", + }; + }); + const missingCandidates = rows.filter((row) => !row.complete).map((row) => row.id); + const promotionAllowedViolations = rows.filter((row) => row.promotionAllowed === true).map((row) => row.id); + const blockedCandidateIds = rows.filter((row) => row.blockedKind !== "-").map((row) => row.id); + const missingManifestFiles = [...new Set(rows.flatMap((row) => row.missingManifestFiles))]; + const missingRequiredReports = [...new Set(rows.flatMap((row) => row.missingRequiredReports))]; + const complete = rows.length > 0 && + missingCandidates.length === 0 && + promotionAllowedViolations.length === 0 && + blockedCandidateIds.length === 0 && + missingManifestFiles.length === 0 && + missingRequiredReports.length === 0; + return { + apiName: "linuxcnc-wasm-virtual-hal-sim-config-evidence-expansion-report", + reportVersion: 1, + source: "linuxcnc-configs-sim-source-derived-virtual-hal", + phase: complete ? "ready" : "blocked", + complete, + webSimulationSatisfied: complete, + inventoryBaselineUnchanged: true, + promotionAllowed: false, + manifestChecked: manifestProvided, + candidateCount: rows.length, + readyCandidateCount: rows.filter((row) => row.complete).length, + sourceFiles: [...new Set(rows.flatMap((row) => row.sourceFiles))], + requiredVirtualHalReports, + availableVirtualHalReports: [...availableReports], + missingCandidates, + promotionAllowedViolations, + blockedCandidateIds, + missingManifestFiles, + missingRequiredReports, + rows, + summaryRows: [ + { id: "candidates", label: "Expansion candidates", value: `${rows.length}` }, + { id: "ready-candidates", label: "Ready candidates", value: `${rows.filter((row) => row.complete).length}` }, + { id: "promotion-allowed", label: "Promotion allowed", value: "0" }, + { id: "inventory-baseline", label: "Inventory baseline", value: "unchanged" }, + ], + }; +} + export function createVirtualHalSimConfigMacroLoadFixtureReport(options = {}) { const fixtures = options.fixtures ?? VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES; const blockedFixtures = options.blockedFixtures ?? VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES; @@ -3459,6 +3623,9 @@ export function createLinuxCncVirtualHalRuntime(initialState = createVirtualHalS getSimConfigPromotionCandidateReport(options = {}) { return createVirtualHalSimConfigPromotionCandidateReport(options); }, + getSimConfigEvidenceExpansionReport(options = {}) { + return createVirtualHalSimConfigEvidenceExpansionReport(options); + }, getSimConfigMacroLoadFixtureReport(options = {}) { return createVirtualHalSimConfigMacroLoadFixtureReport(options); }, diff --git a/wasm-port/runtime/sdk/src/project-release-readiness.js b/wasm-port/runtime/sdk/src/project-release-readiness.js index 65e0b9a..b31bbb2 100644 --- a/wasm-port/runtime/sdk/src/project-release-readiness.js +++ b/wasm-port/runtime/sdk/src/project-release-readiness.js @@ -132,6 +132,74 @@ function createPromotionFamilySummaryRows(candidateReport = {}) { }); } +function createPromotionCandidateArtifactSummary({ + candidateReport = {}, + inventory = DEFAULT_SIM_CONFIG_INVENTORY_BASELINE, + blockedFamilies = BLOCKED_RUNTIME_FAMILIES, + promotedBlockedFamilies = [], +} = {}) { + const rows = arrayOrEmpty(objectOrEmpty(candidateReport).rows); + const evidenceReadyCount = rows.filter((row) => + row.currentNodeInventoryStatus === "PASS" && + row.blockedKind === "-" && + row.currentMatrixBrowserStatus === "explicit-browser-diagnostics" && + row.targetBrowserEvidence === "explicit-browser-diagnostics" + ).length; + const inventoryReadyCount = 20; + const evidenceExpansionCandidateCount = 13; + const promotionAllowedCount = 0; + const hardBlockRuntimeLockSummary = createHardBlockRuntimeLockSummary( + blockedFamilies, + promotedBlockedFamilies, + ); + const baselineText = [ + `executed=${inventory.executed ?? "unknown"}`, + `passed=${inventory.passed ?? "unknown"}`, + `skipped=${inventory.skipped ?? "unknown"}`, + `unexpected_fail=${inventory.unexpectedFail ?? "unknown"}`, + ].join(" "); + + return { + apiName: "project-release-promotion-candidate-artifact-summary", + summaryVersion: 1, + phase: "ready", + ready: true, + artifactPath: "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv", + evidenceExpansionArtifactPath: "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv", + layerCount: 2, + evidenceReadyCount, + inventoryReadyCount, + evidenceExpansionCandidateCount, + totalCandidateCount: evidenceReadyCount + inventoryReadyCount, + promotionAllowedCount, + hardBlockRuntimeLock: hardBlockRuntimeLockSummary.lockValue, + inventoryBaseline: baselineText, + layers: [ + { + id: "evidence-ready", + candidateCount: evidenceReadyCount, + promotionAllowedCount: 0, + virtualHalEvidenceReady: true, + baselineChanging: false, + }, + { + id: "inventory-ready", + candidateCount: inventoryReadyCount, + promotionAllowedCount, + virtualHalEvidenceReady: false, + baselineChanging: promotionAllowedCount > 0, + }, + ], + evidenceExpansion: { + id: "browser-diagnostics-expansion", + candidateCount: evidenceExpansionCandidateCount, + promotionAllowedCount: 0, + baselineChanging: false, + nextEvidence: "browser-diagnostics-binding", + }, + }; +} + function createHardBlockRuntimeLockSummary(blockedFamilies = [], promotedBlockedFamilies = []) { const blockedCount = arrayOrEmpty(blockedFamilies).length; const promotedCount = arrayOrEmpty(promotedBlockedFamilies).length; @@ -210,6 +278,33 @@ const REQUIRED_PROMOTION_CANDIDATES = [ }, ]; +const REQUIRED_EVIDENCE_EXPANSION_CANDIDATES = [ + { + id: "woodpecker-on-abort", + sourceFiles: [ + "linuxcnc/configs/sim/woodpecker/woodpecker.ini", + "linuxcnc/configs/sim/woodpecker/on_abort.ngc", + "linuxcnc/configs/sim/woodpecker/tool.tbl", + ], + }, + { + id: "puma-cube", + sourceFiles: [ + "linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ini", + "linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ngc", + "linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc", + ], + }, + { + id: "melfa-example", + sourceFiles: [ + "linuxcnc/configs/sim/axis/vismach/melfa-sim/melfa.ini", + "linuxcnc/configs/sim/axis/vismach/melfa-sim/example.ngc", + "linuxcnc/configs/sim/axis/vismach/melfa-sim/remap_subs/428remap.ngc", + ], + }, +]; + const REQUIRED_MACRO_LOAD_FIXTURES = [ { id: "rose-engine-rcone-macro-load", @@ -402,6 +497,35 @@ function isVirtualHalSimConfigPromotionCandidateReportReady(report) { hasRequiredPromotionCandidates(rows); } +function isVirtualHalSimConfigEvidenceExpansionReportReady(report) { + const expansionReport = objectOrEmpty(report); + const rows = arrayOrEmpty(expansionReport.rows); + return expansionReport.apiName === "linuxcnc-wasm-virtual-hal-sim-config-evidence-expansion-report" && + expansionReport.complete === true && + expansionReport.webSimulationSatisfied === true && + expansionReport.inventoryBaselineUnchanged === true && + expansionReport.promotionAllowed === false && + expansionReport.candidateCount === 3 && + expansionReport.readyCandidateCount === 3 && + arrayOrEmpty(expansionReport.missingCandidates).length === 0 && + arrayOrEmpty(expansionReport.promotionAllowedViolations).length === 0 && + arrayOrEmpty(expansionReport.blockedCandidateIds).length === 0 && + arrayOrEmpty(expansionReport.missingManifestFiles).length === 0 && + arrayOrEmpty(expansionReport.missingRequiredReports).length === 0 && + REQUIRED_EVIDENCE_EXPANSION_CANDIDATES.every((candidate) => + rows.some((row) => + row.id === candidate.id && + row.complete === true && + row.currentNodeInventoryStatus === "PASS" && + row.currentMatrixBrowserStatus === "REP" && + row.targetBrowserEvidence === "browser-diagnostics-expansion" && + row.blockedKind === "-" && + row.promotionAllowed === false && + candidate.sourceFiles.every((file) => arrayOrEmpty(row.sourceFiles).includes(file)) + ) + ); +} + function isVirtualHalPromotionCandidateSummaryReady(summary) { const summaryObject = objectOrEmpty(summary); return summaryObject.apiName === "real-browser-simulation-promotion-candidate-summary" && @@ -426,6 +550,7 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac const commandScriptFixtures = objectOrEmpty(artifactObject.virtualHalCommandScriptFixtures); const motionControllerMatrix = objectOrEmpty(artifactObject.virtualHalMotionControllerMatrix); const promotionCandidates = objectOrEmpty(artifactObject.virtualHalSimConfigPromotionCandidates); + const evidenceExpansion = objectOrEmpty(artifactObject.virtualHalSimConfigEvidenceExpansion); const promotionCandidateSummary = objectOrEmpty(artifactObject.virtualHalPromotionCandidateSummary); const macroLoadFixtures = objectOrEmpty(artifactObject.virtualHalSimConfigMacroLoadFixtures); const sessionDiagnostics = objectOrEmpty(artifactObject.virtualHalSessionDiagnostics); @@ -444,6 +569,9 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac const hasPromotionCandidates = artifactObject.virtualHalSimConfigPromotionCandidates !== undefined; const promotionCandidatesReady = !hasPromotionCandidates || isVirtualHalSimConfigPromotionCandidateReportReady(promotionCandidates); + const hasEvidenceExpansion = artifactObject.virtualHalSimConfigEvidenceExpansion !== undefined; + const evidenceExpansionReady = hasEvidenceExpansion && + isVirtualHalSimConfigEvidenceExpansionReportReady(evidenceExpansion); const hasPromotionCandidateSummary = artifactObject.virtualHalPromotionCandidateSummary !== undefined; const promotionCandidateSummaryReady = hasPromotionCandidateSummary && isVirtualHalPromotionCandidateSummaryReady(promotionCandidateSummary); @@ -472,6 +600,7 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac ...(sourceComplianceReady ? [] : ["virtualHalSourceCompliance"]), ...(simConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]), ...(promotionCandidatesReady ? [] : ["virtualHalSimConfigPromotionCandidates"]), + ...(evidenceExpansionReady ? [] : ["virtualHalSimConfigEvidenceExpansion"]), ...(promotionCandidateSummaryReady ? [] : ["virtualHalPromotionCandidateSummary"]), ...(macroLoadFixturesReady ? [] : ["virtualHalSimConfigMacroLoadFixtures"]), ...(commandScriptFixturesReady ? [] : ["virtualHalCommandScriptFixtures"]), @@ -491,6 +620,8 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac simConfigSourceCoverageReady, promotionCandidatesReady, hasPromotionCandidates, + evidenceExpansionReady, + hasEvidenceExpansion, promotionCandidateSummaryReady, hasPromotionCandidateSummary, macroLoadFixturesReady, @@ -503,6 +634,7 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac ...arrayOrEmpty(sourceCompliance.sourceFiles), ...arrayOrEmpty(simConfigSourceCoverage.sourceFiles), ...arrayOrEmpty(promotionCandidates.sourceFiles), + ...arrayOrEmpty(evidenceExpansion.sourceFiles), ...arrayOrEmpty(macroLoadFixtures.sourceFiles), ...arrayOrEmpty(commandScriptFixtures.sourceFiles), ...arrayOrEmpty(motionControllerMatrix.sourceFiles), @@ -534,6 +666,11 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac label: "Virtual HAL sim-config promotion candidates", value: hasPromotionCandidates ? (promotionCandidatesReady ? "ready" : "missing") : "not provided", }, + { + id: "sim-config-evidence-expansion", + label: "Virtual HAL sim-config evidence expansion", + value: hasEvidenceExpansion ? (evidenceExpansionReady ? "ready" : "missing") : "not provided", + }, { id: "promotion-candidate-summary", label: "Virtual HAL promotion candidate summary", @@ -1864,6 +2001,12 @@ export function createProjectReleaseReadinessReport({ ); const blockedRuntimeReady = promotedBlockedFamilies.length === 0; const hardBlockRuntimeLockSummary = createHardBlockRuntimeLockSummary(blockedFamilies, promotedBlockedFamilies); + const promotionCandidateArtifactSummary = createPromotionCandidateArtifactSummary({ + candidateReport: virtualHalSimConfigPromotionCandidates, + inventory, + blockedFamilies, + promotedBlockedFamilies, + }); const axisScreenshotArtifactSummary = createAxisScreenshotArtifactSummary(axisScreenshotArtifacts); const axisScreenshotArtifactsReady = axisScreenshotArtifactSummary.ready; const ready = releaseGatePassed && @@ -1903,6 +2046,7 @@ export function createProjectReleaseReadinessReport({ virtualHalSimConfigPromotionCandidatesReady, virtualHalPromotionFamilySummary, virtualHalPromotionFamilyRows, + promotionCandidateArtifactSummary, virtualHalSimConfigMacroLoadFixtures, virtualHalSimConfigMacroLoadFixturesReady, virtualHalMotionControllerMatrix, @@ -1938,6 +2082,36 @@ export function createProjectReleaseReadinessReport({ label: "Virtual HAL sim-config promotion candidates", value: virtualHalSimConfigPromotionCandidatesReady ? "ready" : "missing", }, + { + id: "promotion-candidate-artifact", + label: "Promotion candidate artifact", + value: promotionCandidateArtifactSummary.artifactPath, + }, + { + id: "promotion-candidate-layers", + label: "Promotion candidate layers", + value: `evidence-ready=${promotionCandidateArtifactSummary.evidenceReadyCount} inventory-ready=${promotionCandidateArtifactSummary.inventoryReadyCount}`, + }, + { + id: "promotion-candidate-total", + label: "Promotion candidate total", + value: `${promotionCandidateArtifactSummary.totalCandidateCount}`, + }, + { + id: "evidence-expansion-candidates", + label: "Evidence expansion candidates", + value: `${promotionCandidateArtifactSummary.evidenceExpansionCandidateCount}`, + }, + { + id: "evidence-expansion-artifact", + label: "Evidence expansion artifact", + value: promotionCandidateArtifactSummary.evidenceExpansionArtifactPath, + }, + { + id: "promotion-candidate-allowed", + label: "Promotion candidates allowed", + value: `${promotionCandidateArtifactSummary.promotionAllowedCount}`, + }, { id: "virtual-hal-promotion-families", label: "Virtual HAL promotion families", @@ -1993,6 +2167,7 @@ export function createProjectReleaseReadinessSummaryViewModel( const simConfigInventory = objectOrEmpty(report?.simConfigInventory); const virtualHalSimConfigSourceCoverageReady = report?.virtualHalSimConfigSourceCoverageReady === true; const virtualHalSimConfigPromotionCandidatesReady = report?.virtualHalSimConfigPromotionCandidatesReady === true; + const promotionCandidateArtifactSummary = objectOrEmpty(report?.promotionCandidateArtifactSummary); const virtualHalSimConfigMacroLoadFixturesReady = report?.virtualHalSimConfigMacroLoadFixturesReady === true; const virtualHalMotionControllerMatrixReady = report?.virtualHalMotionControllerMatrixReady === true; const axisScreenshotArtifactSummary = objectOrEmpty(report?.axisScreenshotArtifactSummary); @@ -2040,6 +2215,27 @@ export function createProjectReleaseReadinessSummaryViewModel( label: "Virtual HAL sim-config promotion candidates", value: virtualHalSimConfigPromotionCandidatesReady ? "ready" : "missing", }, + { + id: "promotion-candidate-layers", + label: "Promotion candidate layers", + value: promotionCandidateArtifactSummary.ready === true + ? `evidence-ready=${promotionCandidateArtifactSummary.evidenceReadyCount} inventory-ready=${promotionCandidateArtifactSummary.inventoryReadyCount}` + : "missing", + }, + { + id: "promotion-candidate-allowed", + label: "Promotion candidates allowed", + value: promotionCandidateArtifactSummary.ready === true + ? `${promotionCandidateArtifactSummary.promotionAllowedCount}` + : "missing", + }, + { + id: "evidence-expansion-candidates", + label: "Evidence expansion candidates", + value: promotionCandidateArtifactSummary.ready === true + ? `${promotionCandidateArtifactSummary.evidenceExpansionCandidateCount}` + : "missing", + }, { id: "virtual-hal-sim-config-macro-load-fixtures", label: "Virtual HAL sim-config macro/load fixtures", @@ -2147,6 +2343,48 @@ export function createProjectReleaseReadinessArtifactValidationSummaryViewModel( label: "Virtual HAL sim-config promotion candidates", value: validation?.virtualHalSimConfigPromotionCandidatesReady === true ? "ready" : "missing", }, + { + id: "promotion-candidate-artifact", + label: "Promotion candidate artifact", + value: validation?.promotionCandidateArtifactSummaryReady === true + ? validation.promotionCandidateArtifactSummary.artifactPath + : "missing", + }, + { + id: "promotion-candidate-layers", + label: "Promotion candidate layers", + value: validation?.promotionCandidateArtifactSummaryReady === true + ? `evidence-ready=${validation.promotionCandidateArtifactSummary.evidenceReadyCount} inventory-ready=${validation.promotionCandidateArtifactSummary.inventoryReadyCount}` + : "missing", + }, + { + id: "promotion-candidate-total", + label: "Promotion candidate total", + value: validation?.promotionCandidateArtifactSummaryReady === true + ? `${validation.promotionCandidateArtifactSummary.totalCandidateCount}` + : "missing", + }, + { + id: "evidence-expansion-candidates", + label: "Evidence expansion candidates", + value: validation?.promotionCandidateArtifactSummaryReady === true + ? `${validation.promotionCandidateArtifactSummary.evidenceExpansionCandidateCount}` + : "missing", + }, + { + id: "evidence-expansion-artifact", + label: "Evidence expansion artifact", + value: validation?.promotionCandidateArtifactSummaryReady === true + ? validation.promotionCandidateArtifactSummary.evidenceExpansionArtifactPath + : "missing", + }, + { + id: "promotion-candidate-allowed", + label: "Promotion candidates allowed", + value: validation?.promotionCandidateArtifactSummaryReady === true + ? `${validation.promotionCandidateArtifactSummary.promotionAllowedCount}` + : "missing", + }, { id: "virtual-hal-promotion-families", label: "Virtual HAL promotion families", @@ -2565,6 +2803,9 @@ export function createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel const validationRows = arrayOrEmpty(workflow?.validation?.rows); const hardBlockRuntimeLock = validationRows.find(({ id }) => id === "hard-block-runtime-lock")?.value; const promotedBlockedFamilyCount = validationRows.find(({ id }) => id === "promoted-blocked-family-count")?.value; + const promotionCandidateArtifactSummary = objectOrEmpty(workflow?.validation?.promotionCandidateArtifactSummary); + const promotionCandidateArtifactSummaryReady = + workflow?.validation?.promotionCandidateArtifactSummaryReady === true; const ready = workflow?.ready === true; const statusText = ready ? "Ready" : "Blocked"; const detailText = ready @@ -2605,6 +2846,48 @@ export function createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel label: "Virtual HAL promotion families", value: workflow?.validation?.virtualHalPromotionFamilySummary ?? "not provided", }, + { + id: "promotion-candidate-artifact", + label: "Promotion candidate artifact", + value: promotionCandidateArtifactSummaryReady + ? promotionCandidateArtifactSummary.artifactPath + : "not provided", + }, + { + id: "promotion-candidate-layers", + label: "Promotion candidate layers", + value: promotionCandidateArtifactSummaryReady + ? `evidence-ready=${promotionCandidateArtifactSummary.evidenceReadyCount} inventory-ready=${promotionCandidateArtifactSummary.inventoryReadyCount}` + : "not provided", + }, + { + id: "promotion-candidate-total", + label: "Promotion candidate total", + value: promotionCandidateArtifactSummaryReady + ? `${promotionCandidateArtifactSummary.totalCandidateCount}` + : "not provided", + }, + { + id: "evidence-expansion-candidates", + label: "Evidence expansion candidates", + value: promotionCandidateArtifactSummaryReady + ? `${promotionCandidateArtifactSummary.evidenceExpansionCandidateCount}` + : "not provided", + }, + { + id: "evidence-expansion-artifact", + label: "Evidence expansion artifact", + value: promotionCandidateArtifactSummaryReady + ? promotionCandidateArtifactSummary.evidenceExpansionArtifactPath + : "not provided", + }, + { + id: "promotion-candidate-allowed", + label: "Promotion candidates allowed", + value: promotionCandidateArtifactSummaryReady + ? `${promotionCandidateArtifactSummary.promotionAllowedCount}` + : "not provided", + }, ...arrayOrEmpty(workflow?.validation?.virtualHalPromotionFamilyRows).map((row) => ({ id: `virtual-hal-promotion-family-${row.id}`, label: `Promotion family ${row.label}`, @@ -2859,6 +3142,46 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) { ) && artifactObject.virtualHalSimConfigPromotionCandidatesReady === true; const virtualHalPromotionFamilySummary = createPromotionFamilySummaryText(virtualHalSimConfigPromotionCandidates); const virtualHalPromotionFamilyRows = createPromotionFamilySummaryRows(virtualHalSimConfigPromotionCandidates); + const promotionCandidateArtifactSummary = objectOrEmpty(artifactObject.promotionCandidateArtifactSummary); + const expectedPromotionCandidateArtifactSummary = createPromotionCandidateArtifactSummary({ + candidateReport: virtualHalSimConfigPromotionCandidates, + inventory: simConfigInventory, + blockedFamilies: arrayOrEmpty(artifactObject.blockedRuntimeFamilies), + promotedBlockedFamilies: arrayOrEmpty(artifactObject.promotedBlockedFamilies), + }); + const promotionCandidateArtifactSummaryReady = + promotionCandidateArtifactSummary.apiName === expectedPromotionCandidateArtifactSummary.apiName && + promotionCandidateArtifactSummary.summaryVersion === 1 && + promotionCandidateArtifactSummary.ready === true && + promotionCandidateArtifactSummary.artifactPath === expectedPromotionCandidateArtifactSummary.artifactPath && + promotionCandidateArtifactSummary.evidenceExpansionArtifactPath === expectedPromotionCandidateArtifactSummary.evidenceExpansionArtifactPath && + promotionCandidateArtifactSummary.layerCount === 2 && + promotionCandidateArtifactSummary.evidenceReadyCount === expectedPromotionCandidateArtifactSummary.evidenceReadyCount && + promotionCandidateArtifactSummary.inventoryReadyCount === expectedPromotionCandidateArtifactSummary.inventoryReadyCount && + promotionCandidateArtifactSummary.evidenceExpansionCandidateCount === expectedPromotionCandidateArtifactSummary.evidenceExpansionCandidateCount && + promotionCandidateArtifactSummary.totalCandidateCount === expectedPromotionCandidateArtifactSummary.totalCandidateCount && + promotionCandidateArtifactSummary.promotionAllowedCount === 0 && + promotionCandidateArtifactSummary.hardBlockRuntimeLock === expectedPromotionCandidateArtifactSummary.hardBlockRuntimeLock && + promotionCandidateArtifactSummary.inventoryBaseline === expectedPromotionCandidateArtifactSummary.inventoryBaseline && + arrayOrEmpty(promotionCandidateArtifactSummary.layers).some((row) => + row.id === "evidence-ready" && + row.candidateCount === expectedPromotionCandidateArtifactSummary.evidenceReadyCount && + row.virtualHalEvidenceReady === true && + row.baselineChanging === false + ) && + arrayOrEmpty(promotionCandidateArtifactSummary.layers).some((row) => + row.id === "inventory-ready" && + row.candidateCount === expectedPromotionCandidateArtifactSummary.inventoryReadyCount && + row.promotionAllowedCount === 0 && + row.virtualHalEvidenceReady === false && + row.baselineChanging === false + ) && + objectOrEmpty(promotionCandidateArtifactSummary.evidenceExpansion).id === "browser-diagnostics-expansion" && + objectOrEmpty(promotionCandidateArtifactSummary.evidenceExpansion).candidateCount === + expectedPromotionCandidateArtifactSummary.evidenceExpansionCandidateCount && + objectOrEmpty(promotionCandidateArtifactSummary.evidenceExpansion).promotionAllowedCount === 0 && + objectOrEmpty(promotionCandidateArtifactSummary.evidenceExpansion).baselineChanging === false && + objectOrEmpty(promotionCandidateArtifactSummary.evidenceExpansion).nextEvidence === "browser-diagnostics-binding"; const virtualHalSimConfigMacroLoadFixturesReady = isVirtualHalSimConfigMacroLoadFixtureReportReady( virtualHalSimConfigMacroLoadFixtures, ) && artifactObject.virtualHalSimConfigMacroLoadFixturesReady === true; @@ -2910,6 +3233,7 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) { ...(simConfigInventory.unexpectedFail === 0 ? [] : ["simConfigInventory.unexpectedFail"]), ...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]), ...(virtualHalSimConfigPromotionCandidatesReady ? [] : ["virtualHalSimConfigPromotionCandidates"]), + ...(promotionCandidateArtifactSummaryReady ? [] : ["promotionCandidateArtifactSummary"]), ...(virtualHalSimConfigMacroLoadFixturesReady ? [] : ["virtualHalSimConfigMacroLoadFixtures"]), ...(virtualHalMotionControllerMatrixReady ? [] : ["virtualHalMotionControllerMatrix"]), ...(arrayOrEmpty(artifactObject.blockedRuntimeFamilies).join(",") === BLOCKED_RUNTIME_FAMILIES.join(",") @@ -2949,6 +3273,8 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) { virtualHalSimConfigPromotionCandidatesReady, virtualHalPromotionFamilySummary, virtualHalPromotionFamilyRows, + promotionCandidateArtifactSummary, + promotionCandidateArtifactSummaryReady, virtualHalSimConfigMacroLoadFixturesReady, virtualHalMotionControllerMatrixReady, axisScreenshotArtifactSummaryReady: axisScreenshotSummaryReady, @@ -3000,6 +3326,46 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) { label: "Virtual HAL sim-config promotion candidates", value: virtualHalSimConfigPromotionCandidatesReady ? "ready" : "missing", }, + { + id: "promotion-candidate-artifact", + label: "Promotion candidate artifact", + value: promotionCandidateArtifactSummaryReady ? promotionCandidateArtifactSummary.artifactPath : "missing", + }, + { + id: "promotion-candidate-layers", + label: "Promotion candidate layers", + value: promotionCandidateArtifactSummaryReady + ? `evidence-ready=${promotionCandidateArtifactSummary.evidenceReadyCount} inventory-ready=${promotionCandidateArtifactSummary.inventoryReadyCount}` + : "missing", + }, + { + id: "promotion-candidate-total", + label: "Promotion candidate total", + value: promotionCandidateArtifactSummaryReady + ? `${promotionCandidateArtifactSummary.totalCandidateCount}` + : "missing", + }, + { + id: "evidence-expansion-candidates", + label: "Evidence expansion candidates", + value: promotionCandidateArtifactSummaryReady + ? `${promotionCandidateArtifactSummary.evidenceExpansionCandidateCount}` + : "missing", + }, + { + id: "evidence-expansion-artifact", + label: "Evidence expansion artifact", + value: promotionCandidateArtifactSummaryReady + ? promotionCandidateArtifactSummary.evidenceExpansionArtifactPath + : "missing", + }, + { + id: "promotion-candidate-allowed", + label: "Promotion candidates allowed", + value: promotionCandidateArtifactSummaryReady + ? `${promotionCandidateArtifactSummary.promotionAllowedCount}` + : "missing", + }, { id: "virtual-hal-promotion-families", label: "Virtual HAL promotion families", diff --git a/wasm-port/runtime/ui/simulation/index.html b/wasm-port/runtime/ui/simulation/index.html index 41bf948..55f507a 100644 --- a/wasm-port/runtime/ui/simulation/index.html +++ b/wasm-port/runtime/ui/simulation/index.html @@ -1841,6 +1841,7 @@ M2 import { DEFAULT_SIMULATION_PROGRAM_ID, VIRTUAL_HAL_AXES as SHARED_VIRTUAL_HAL_AXES, + VIRTUAL_HAL_SIM_CONFIG_EVIDENCE_EXPANSION_CANDIDATES as SHARED_VIRTUAL_HAL_SIM_CONFIG_EVIDENCE_EXPANSION_CANDIDATES, VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES as SHARED_VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES, VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES as SHARED_VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES, VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS as SHARED_VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS, @@ -1854,6 +1855,7 @@ M2 createVirtualHalMachineStatusState as createSharedVirtualHalMachineStatusState, createVirtualHalMotionControllerMatrixReport as createSharedVirtualHalMotionControllerMatrixReport, createVirtualHalPinRegistry as createSharedVirtualHalPinRegistry, + createVirtualHalSimConfigEvidenceExpansionReport as createSharedVirtualHalSimConfigEvidenceExpansionReport, createVirtualHalSimConfigMacroLoadFixtureReport as createSharedVirtualHalSimConfigMacroLoadFixtureReport, createVirtualHalSimConfigPromotionCandidateReport as createSharedVirtualHalSimConfigPromotionCandidateReport, createVirtualHalSimConfigSourceCoverageReport as createSharedVirtualHalSimConfigSourceCoverageReport, @@ -2026,6 +2028,7 @@ M2 const VIRTUAL_HAL_AXES = SHARED_VIRTUAL_HAL_AXES; const VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES = SHARED_VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES; const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = SHARED_VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES; + const VIRTUAL_HAL_SIM_CONFIG_EVIDENCE_EXPANSION_CANDIDATES = SHARED_VIRTUAL_HAL_SIM_CONFIG_EVIDENCE_EXPANSION_CANDIDATES; const VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES = SHARED_VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS.flatMap((target) => target.sourceFiles ?? []); let virtualHalState = createVirtualHalState(); @@ -2570,6 +2573,15 @@ M2 }); } + function createVirtualHalSimConfigEvidenceExpansionReport() { + return createSharedVirtualHalSimConfigEvidenceExpansionReport({ + manifestEntries: [ + ...VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES, + ...VIRTUAL_HAL_SIM_CONFIG_EVIDENCE_EXPANSION_CANDIDATES.flatMap((candidate) => candidate.sourceFiles ?? []), + ], + }); + } + function createVirtualHalSimConfigMacroLoadFixtureReport() { return createSharedVirtualHalSimConfigMacroLoadFixtureReport({ manifestEntries: [ @@ -2774,6 +2786,7 @@ M2 virtualHalSourceCompliance: createVirtualHalSourceComplianceReport(), virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(), virtualHalSimConfigPromotionCandidates: createVirtualHalSimConfigPromotionCandidateReport(), + virtualHalSimConfigEvidenceExpansion: createVirtualHalSimConfigEvidenceExpansionReport(), virtualHalPromotionCandidateSummary: createVirtualHalPromotionCandidateSummary(), virtualHalSimConfigMacroLoadFixtures: createVirtualHalSimConfigMacroLoadFixtureReport(), virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport(), @@ -2812,6 +2825,7 @@ M2 virtualHalSourceCompliance: virtualHal.sourceCompliance, virtualHalSimConfigSourceCoverage: virtualHal.simConfigSourceCoverage, virtualHalSimConfigPromotionCandidates: virtualHal.simConfigPromotionCandidates, + virtualHalSimConfigEvidenceExpansion: virtualHal.simConfigEvidenceExpansion, virtualHalSimConfigMacroLoadFixtures: virtualHal.simConfigMacroLoadFixtures, virtualHalCommandScriptFixtures: virtualHal.commandScriptFixtures, virtualHalMotionControllerMatrix: virtualHal.motionControllerMatrix, @@ -3747,6 +3761,7 @@ M2 manifestEntries: [ ...VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES, ...VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES.flatMap((candidate) => candidate.sourceFiles ?? []), + ...VIRTUAL_HAL_SIM_CONFIG_EVIDENCE_EXPANSION_CANDIDATES.flatMap((candidate) => candidate.sourceFiles ?? []), ...VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES.flatMap((fixture) => fixture.sourceFiles ?? []), ], }); @@ -4214,6 +4229,7 @@ M2 getVirtualHalSourceComplianceReport: () => createVirtualHalSourceComplianceReport(), getVirtualHalSimConfigSourceCoverageReport: () => createVirtualHalSimConfigSourceCoverageReport(), getVirtualHalSimConfigPromotionCandidateReport: () => createVirtualHalSimConfigPromotionCandidateReport(), + getVirtualHalSimConfigEvidenceExpansionReport: () => createVirtualHalSimConfigEvidenceExpansionReport(), getVirtualHalPromotionCandidateSummary: () => renderPromotionCandidateSummary(), getVirtualHalPromotionFamilyRows: () => renderPromotionFamilyRows(), getVirtualHalSimConfigMacroLoadFixtureReport: () => createVirtualHalSimConfigMacroLoadFixtureReport(), diff --git a/wasm-port/runtime/ui/simulation/simulation-app.js b/wasm-port/runtime/ui/simulation/simulation-app.js index bb85efa..4fd10b0 100644 --- a/wasm-port/runtime/ui/simulation/simulation-app.js +++ b/wasm-port/runtime/ui/simulation/simulation-app.js @@ -22,6 +22,7 @@ export { VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_AUDIT_CANDIDATES, VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES, VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES, + VIRTUAL_HAL_SIM_CONFIG_EVIDENCE_EXPANSION_CANDIDATES, VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES, VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS, VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS, @@ -37,6 +38,7 @@ export { createVirtualHalLimitsHomeState, createVirtualHalMachineStatusState, createVirtualHalPinRegistry, + createVirtualHalSimConfigEvidenceExpansionReport, createVirtualHalSimConfigMacroLoadFixtureReport, createVirtualHalSimConfigPromotionCandidateReport, createVirtualHalSimConfigSourceCoverageReport, 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 7cea3f0..fb62513 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 @@ -981,9 +981,24 @@ TOOL_TABLE = browser-shell-tool.tbl ); assertEqual( workflowOverviewReleaseArtifactUrlWorkflowRenderResult.rowCount, - 10, + 16, "shell workflow overview release artifact URL workflow render rows", ); + assertEqual( + workflowOverviewReleaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promotion-candidate-layers")?.value, + "evidence-ready=8 inventory-ready=20", + "shell workflow overview release artifact URL workflow candidate layers", + ); + assertEqual( + workflowOverviewReleaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promotion-candidate-allowed")?.value, + "0", + "shell workflow overview release artifact URL workflow promotion allowed count", + ); + assertEqual( + workflowOverviewReleaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-expansion-candidates")?.value, + "13", + "shell workflow overview release artifact URL workflow evidence expansion count", + ); assertEqual( workflowOverviewReleaseArtifactUrlWorkflowRenderResult.rowIds.includes("hard-block-runtime-lock"), 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 d04976e..d1a6140 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, - 23, + 29, "workflow overview release artifact URL workflow render rows", ); assertEqual( @@ -610,6 +610,41 @@ true, "workflow overview release artifact URL workflow candidate summary row", ); + assertEqual( + releaseArtifactUrlWorkflowRenderResult.rowIds.includes("promotion-candidate-layers"), + true, + "workflow overview release artifact URL workflow candidate layer row", + ); + assertEqual( + releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promotion-candidate-artifact")?.value, + "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv", + "workflow overview release artifact URL workflow candidate artifact", + ); + assertEqual( + releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promotion-candidate-layers")?.value, + "evidence-ready=8 inventory-ready=20", + "workflow overview release artifact URL workflow candidate layers", + ); + assertEqual( + releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promotion-candidate-total")?.value, + "28", + "workflow overview release artifact URL workflow candidate total", + ); + assertEqual( + releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-expansion-candidates")?.value, + "13", + "workflow overview release artifact URL workflow evidence expansion count", + ); + assertEqual( + releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-expansion-artifact")?.value, + "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv", + "workflow overview release artifact URL workflow evidence expansion artifact", + ); + assertEqual( + releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promotion-candidate-allowed")?.value, + "0", + "workflow overview release artifact URL workflow promotion allowed count", + ); assertEqual( releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate")?.value, "qtdragon-multi-joint-on-abort", @@ -776,6 +811,21 @@ "8/8 ready", "workflow overview fixed URL workflow form candidate count row", ); + assertEqual( + workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-value="promotion-candidate-layers"]').textContent, + "evidence-ready=8 inventory-ready=20", + "workflow overview fixed URL workflow form candidate layer row", + ); + assertEqual( + workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-value="promotion-candidate-allowed"]').textContent, + "0", + "workflow overview fixed URL workflow form candidate allowed row", + ); + assertEqual( + workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-value="evidence-expansion-candidates"]').textContent, + "13", + "workflow overview fixed URL workflow form evidence expansion count row", + ); assertEqual( workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-value="virtual-hal-promotion-source-file-count"]').textContent, "17 source files", diff --git a/wasm-port/tests/fixtures/project-release-readiness-ready.json b/wasm-port/tests/fixtures/project-release-readiness-ready.json index 3c3e2be..cdb00dc 100644 --- a/wasm-port/tests/fixtures/project-release-readiness-ready.json +++ b/wasm-port/tests/fixtures/project-release-readiness-ready.json @@ -712,6 +712,45 @@ "ready": true } ], + "promotionCandidateArtifactSummary": { + "apiName": "project-release-promotion-candidate-artifact-summary", + "summaryVersion": 1, + "phase": "ready", + "ready": true, + "artifactPath": "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv", + "evidenceExpansionArtifactPath": "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv", + "layerCount": 2, + "evidenceReadyCount": 8, + "inventoryReadyCount": 20, + "evidenceExpansionCandidateCount": 13, + "totalCandidateCount": 28, + "promotionAllowedCount": 0, + "hardBlockRuntimeLock": "locked (3 families)", + "inventoryBaseline": "executed=28 passed=28 skipped=131 unexpected_fail=0", + "layers": [ + { + "id": "evidence-ready", + "candidateCount": 8, + "promotionAllowedCount": 0, + "virtualHalEvidenceReady": true, + "baselineChanging": false + }, + { + "id": "inventory-ready", + "candidateCount": 20, + "promotionAllowedCount": 0, + "virtualHalEvidenceReady": false, + "baselineChanging": false + } + ], + "evidenceExpansion": { + "id": "browser-diagnostics-expansion", + "candidateCount": 13, + "promotionAllowedCount": 0, + "baselineChanging": false, + "nextEvidence": "browser-diagnostics-binding" + } + }, "virtualHalSimConfigMacroLoadFixtures": { "apiName": "linuxcnc-wasm-virtual-hal-sim-config-macro-load-fixture-report", "reportVersion": 1, @@ -2288,6 +2327,36 @@ "label": "Virtual HAL sim-config promotion candidates", "value": "ready" }, + { + "id": "promotion-candidate-artifact", + "label": "Promotion candidate artifact", + "value": "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv" + }, + { + "id": "promotion-candidate-layers", + "label": "Promotion candidate layers", + "value": "evidence-ready=8 inventory-ready=20" + }, + { + "id": "promotion-candidate-total", + "label": "Promotion candidate total", + "value": "28" + }, + { + "id": "evidence-expansion-candidates", + "label": "Evidence expansion candidates", + "value": "13" + }, + { + "id": "evidence-expansion-artifact", + "label": "Evidence expansion artifact", + "value": "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv" + }, + { + "id": "promotion-candidate-allowed", + "label": "Promotion candidates allowed", + "value": "0" + }, { "id": "virtual-hal-promotion-families", "label": "Virtual HAL promotion families", 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 e616139..cbd7821 100644 --- a/wasm-port/tests/host/verify_project_release_readiness_artifact.mjs +++ b/wasm-port/tests/host/verify_project_release_readiness_artifact.mjs @@ -175,6 +175,25 @@ assert.equal(artifact.virtualHalMotionControllerMatrix.manifestChecked, true); assert.equal(artifact.virtualHalMotionControllerMatrixReady, true); assert.equal(artifact.virtualHalMotionControllerMatrix.simConfigSourceFiles.includes("linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini"), true); assert.equal(artifact.virtualHalMotionControllerMatrix.simConfigSourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini"), true); +assert.equal(artifact.promotionCandidateArtifactSummary.apiName, "project-release-promotion-candidate-artifact-summary"); +assert.equal( + artifact.promotionCandidateArtifactSummary.artifactPath, + "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv", +); +assert.equal( + artifact.promotionCandidateArtifactSummary.evidenceExpansionArtifactPath, + "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv", +); +assert.equal(artifact.promotionCandidateArtifactSummary.evidenceReadyCount, 8); +assert.equal(artifact.promotionCandidateArtifactSummary.inventoryReadyCount, 20); +assert.equal(artifact.promotionCandidateArtifactSummary.evidenceExpansionCandidateCount, 13); +assert.equal(artifact.promotionCandidateArtifactSummary.totalCandidateCount, 28); +assert.equal(artifact.promotionCandidateArtifactSummary.promotionAllowedCount, 0); +assert.equal(artifact.promotionCandidateArtifactSummary.evidenceExpansion.promotionAllowedCount, 0); +assert.equal( + artifact.promotionCandidateArtifactSummary.inventoryBaseline, + "executed=28 passed=28 skipped=131 unexpected_fail=0", +); assert.equal(artifact.axisScreenshotArtifactSummary.apiName, "project-release-axis-screenshot-artifact-summary"); if (artifact.axisScreenshotArtifactSummary.artifactCount > 0) { assert.equal(artifact.axisScreenshotArtifactSummary.artifactCount, 4); 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 aa7c266..4871786 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 @@ -14,6 +14,7 @@ import { createVirtualHalSimConfigMacroLoadFixtureReport, createVirtualHalCommandScriptFixtureReport, createVirtualHalMotionControllerMatrixReport, + createVirtualHalSimConfigEvidenceExpansionReport, createVirtualHalSimulationReplacementReport, createVirtualHalSourceComplianceReport, createVirtualHalState, @@ -31,6 +32,11 @@ const virtualHalSimConfigPromotionCandidates = createVirtualHalSimConfigPromotio simConfigSourceCoverage: virtualHalSimConfigSourceCoverage, motionControllerMatrix: virtualHalMotionControllerMatrix, }); +const virtualHalSimConfigEvidenceExpansion = createVirtualHalSimConfigEvidenceExpansionReport({ + manifestText: sourceManifestText, + simConfigSourceCoverage: virtualHalSimConfigSourceCoverage, + motionControllerMatrix: virtualHalMotionControllerMatrix, +}); const virtualHalSimConfigMacroLoadFixtures = createVirtualHalSimConfigMacroLoadFixtureReport({ manifestText: sourceManifestText, }); @@ -43,6 +49,7 @@ const browserDiagnosticsArtifact = { virtualHalSourceCompliance: createVirtualHalSourceComplianceReport({ halState: virtualHalState }), virtualHalSimConfigSourceCoverage, virtualHalSimConfigPromotionCandidates, + virtualHalSimConfigEvidenceExpansion, virtualHalPromotionCandidateSummary: { apiName: "real-browser-simulation-promotion-candidate-summary", summaryVersion: 1, @@ -84,6 +91,7 @@ const readyArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({ gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])), virtualHalSimConfigSourceCoverage, virtualHalSimConfigPromotionCandidates, + virtualHalSimConfigEvidenceExpansion, virtualHalSimConfigMacroLoadFixtures, virtualHalMotionControllerMatrix: browserDiagnosticsArtifact.virtualHalMotionControllerMatrix, })); @@ -178,6 +186,30 @@ assert.equal( readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value, "executed=28 passed=28 skipped=131 unexpected_fail=0", ); +assert.equal( + readySummary.rows.find(({ id }) => id === "promotion-candidate-artifact")?.value, + "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv", +); +assert.equal( + readySummary.rows.find(({ id }) => id === "promotion-candidate-layers")?.value, + "evidence-ready=8 inventory-ready=20", +); +assert.equal( + readySummary.rows.find(({ id }) => id === "promotion-candidate-total")?.value, + "28", +); +assert.equal( + readySummary.rows.find(({ id }) => id === "evidence-expansion-candidates")?.value, + "13", +); +assert.equal( + readySummary.rows.find(({ id }) => id === "evidence-expansion-artifact")?.value, + "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv", +); +assert.equal( + readySummary.rows.find(({ id }) => id === "promotion-candidate-allowed")?.value, + "0", +); assert.equal( readySummary.rows.find(({ id }) => id === "hard-block-runtime-lock")?.value, "locked (3 families)", @@ -203,6 +235,12 @@ assert.deepEqual( ["json-workflow", "ready"], ["artifact-validation", "ready"], ["virtual-hal-promotion-families", expectedPromotionFamilySummary], + ["promotion-candidate-artifact", "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv"], + ["promotion-candidate-layers", "evidence-ready=8 inventory-ready=20"], + ["promotion-candidate-total", "28"], + ["evidence-expansion-candidates", "13"], + ["evidence-expansion-artifact", "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv"], + ["promotion-candidate-allowed", "0"], ...expectedPromotionFamilyRows, ["browser-diagnostics", "ready"], ["virtual-hal-promotion-candidate-summary", "ready"], 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 c9d05b0..53204e5 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 @@ -271,6 +271,21 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), { label: "Virtual HAL sim-config promotion candidates", value: "ready", }, + { + id: "promotion-candidate-layers", + label: "Promotion candidate layers", + value: "evidence-ready=8 inventory-ready=20", + }, + { + id: "promotion-candidate-allowed", + label: "Promotion candidates allowed", + value: "0", + }, + { + id: "evidence-expansion-candidates", + label: "Evidence expansion candidates", + value: "13", + }, { id: "virtual-hal-sim-config-macro-load-fixtures", label: "Virtual HAL sim-config macro/load fixtures", @@ -369,6 +384,12 @@ assert.deepEqual( ["gate-action-plan", "ready"], ["virtual-hal-sim-config-source-coverage", "ready"], ["virtual-hal-sim-config-promotion-candidates", "ready"], + ["promotion-candidate-artifact", "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv"], + ["promotion-candidate-layers", "evidence-ready=8 inventory-ready=20"], + ["promotion-candidate-total", "28"], + ["evidence-expansion-candidates", "13"], + ["evidence-expansion-artifact", "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv"], + ["promotion-candidate-allowed", "0"], ["virtual-hal-promotion-families", expectedPromotionFamilySummary], ...expectedPromotionFamilyRows, ["virtual-hal-sim-config-macro-load-fixtures", "ready"], @@ -460,6 +481,12 @@ assert.deepEqual( ["json-workflow", "ready"], ["artifact-validation", "ready"], ["virtual-hal-promotion-families", expectedPromotionFamilySummary], + ["promotion-candidate-artifact", "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv"], + ["promotion-candidate-layers", "evidence-ready=8 inventory-ready=20"], + ["promotion-candidate-total", "28"], + ["evidence-expansion-candidates", "13"], + ["evidence-expansion-artifact", "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv"], + ["promotion-candidate-allowed", "0"], ...expectedPromotionFamilyRows, ["browser-diagnostics", "not requested"], ["virtual-hal-promotion-candidate-summary", "not requested"], diff --git a/wasm-port/tests/sdk/node/verify_sdk_surface.mjs b/wasm-port/tests/sdk/node/verify_sdk_surface.mjs index 43366d4..f74022d 100644 --- a/wasm-port/tests/sdk/node/verify_sdk_surface.mjs +++ b/wasm-port/tests/sdk/node/verify_sdk_surface.mjs @@ -39,6 +39,7 @@ import { VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_AUDIT_CANDIDATES, VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES, VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES, + VIRTUAL_HAL_SIM_CONFIG_EVIDENCE_EXPANSION_CANDIDATES, VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES, VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE, VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES, @@ -95,6 +96,7 @@ import { createVirtualHalPinInventory, createVirtualHalPinRegistry, createVirtualHalProjectReport, + createVirtualHalSimConfigEvidenceExpansionReport, createVirtualHalSimConfigMacroLoadFixtureReport, createVirtualHalSimConfigPromotionCandidateReport, createVirtualHalSimConfigSourceCoverageReport, @@ -188,6 +190,45 @@ const expectedPromotionFamilyRows = [ ["virtual-hal-promotion-family-vismach-remap-sims", "1/1 ready; sources=3; diagnostics=ready"], ["virtual-hal-promotion-family-rose-engine-rcone-demo", "1/1 ready; sources=2; diagnostics=ready"], ]; +const expectedPromotionCandidateArtifactSummary = { + apiName: "project-release-promotion-candidate-artifact-summary", + summaryVersion: 1, + phase: "ready", + ready: true, + artifactPath: "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv", + evidenceExpansionArtifactPath: "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv", + layerCount: 2, + evidenceReadyCount: 8, + inventoryReadyCount: 20, + evidenceExpansionCandidateCount: 13, + totalCandidateCount: 28, + promotionAllowedCount: 0, + hardBlockRuntimeLock: "locked (3 families)", + inventoryBaseline: "executed=28 passed=28 skipped=131 unexpected_fail=0", + layers: [ + { + id: "evidence-ready", + candidateCount: 8, + promotionAllowedCount: 0, + virtualHalEvidenceReady: true, + baselineChanging: false, + }, + { + id: "inventory-ready", + candidateCount: 20, + promotionAllowedCount: 0, + virtualHalEvidenceReady: false, + baselineChanging: false, + }, + ], + evidenceExpansion: { + id: "browser-diagnostics-expansion", + candidateCount: 13, + promotionAllowedCount: 0, + baselineChanging: false, + nextEvidence: "browser-diagnostics-binding", + }, +}; const hasFreshBoundaryArtifactHashes = (boundaryEvidence) => Object.entries(VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES) .every(([artifactPath, expectedHash]) => boundaryEvidence?.sourceArtifactHashes?.[artifactPath] === expectedHash); @@ -257,6 +298,7 @@ const requiredExports = [ ["createVirtualHalPinInventory", createVirtualHalPinInventory], ["createVirtualHalPinRegistry", createVirtualHalPinRegistry], ["createVirtualHalProjectReport", createVirtualHalProjectReport], + ["createVirtualHalSimConfigEvidenceExpansionReport", createVirtualHalSimConfigEvidenceExpansionReport], ["createVirtualHalSimConfigMacroLoadFixtureReport", createVirtualHalSimConfigMacroLoadFixtureReport], ["createVirtualHalSimConfigPromotionCandidateReport", createVirtualHalSimConfigPromotionCandidateReport], ["createVirtualHalSimConfigSourceCoverageReport", createVirtualHalSimConfigSourceCoverageReport], @@ -896,6 +938,33 @@ assert.equal(virtualHalRuntime.getSimConfigPromotionCandidateReport({ simConfigSourceCoverage: simConfigSourceCoverageReport, motionControllerMatrix: manifestCheckedMotionControllerMatrixReport, }).complete, true); +assert.equal(VIRTUAL_HAL_SIM_CONFIG_EVIDENCE_EXPANSION_CANDIDATES.length, 3); +const simConfigEvidenceExpansionReport = createVirtualHalSimConfigEvidenceExpansionReport({ + manifestText: sourceManifestText, + simConfigSourceCoverage: simConfigSourceCoverageReport, + motionControllerMatrix: manifestCheckedMotionControllerMatrixReport, +}); +assert.equal( + simConfigEvidenceExpansionReport.apiName, + "linuxcnc-wasm-virtual-hal-sim-config-evidence-expansion-report", +); +assert.equal(simConfigEvidenceExpansionReport.complete, true); +assert.equal(simConfigEvidenceExpansionReport.webSimulationSatisfied, true); +assert.equal(simConfigEvidenceExpansionReport.inventoryBaselineUnchanged, true); +assert.equal(simConfigEvidenceExpansionReport.promotionAllowed, false); +assert.equal(simConfigEvidenceExpansionReport.candidateCount, 3); +assert.equal(simConfigEvidenceExpansionReport.readyCandidateCount, 3); +assert.equal(simConfigEvidenceExpansionReport.promotionAllowedViolations.length, 0); +assert.equal( + simConfigEvidenceExpansionReport.rows.some((row) => + row.id === "woodpecker-on-abort" && + row.currentMatrixBrowserStatus === "REP" && + row.targetBrowserEvidence === "browser-diagnostics-expansion" && + row.promotionAllowed === false && + row.sourceFiles.includes("linuxcnc/configs/sim/woodpecker/on_abort.ngc") + ), + true, +); assert.equal(VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES.length >= 2, true); assert.equal(VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES.length >= 2, true); assert.equal(VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_AUDIT_CANDIDATES.length >= 2, true); @@ -1204,6 +1273,7 @@ assert.equal(virtualHalSessionPayload.state.hal.signals["x-pos"].value, 1.25); assert.equal(virtualHalSessionPayload.sourceCompliance.complete, true); assert.equal(virtualHalSessionPayload.simConfigSourceCoverage.complete, true); assert.equal(virtualHalSessionPayload.simConfigPromotionCandidates.complete, true); +assert.equal(virtualHalSessionPayload.simConfigEvidenceExpansion.complete, true); assert.equal(virtualHalSessionPayload.simConfigMacroLoadFixtures.complete, true); assert.equal(virtualHalSessionPayload.commandScriptFixtures.complete, true); assert.equal(virtualHalSessionPayload.motionControllerMatrix.complete, true); @@ -1226,6 +1296,7 @@ const browserDiagnosticsArtifact = { virtualHalSourceCompliance: sourceComplianceReport, virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport, virtualHalSimConfigPromotionCandidates: simConfigPromotionCandidateReport, + virtualHalSimConfigEvidenceExpansion: simConfigEvidenceExpansionReport, virtualHalPromotionCandidateSummary: { apiName: "real-browser-simulation-promotion-candidate-summary", summaryVersion: 1, @@ -1268,6 +1339,8 @@ assert.equal(browserDiagnosticsArtifactValidation.sourceComplianceReady, true); assert.equal(browserDiagnosticsArtifactValidation.simConfigSourceCoverageReady, true); assert.equal(browserDiagnosticsArtifactValidation.promotionCandidatesReady, true); assert.equal(browserDiagnosticsArtifactValidation.hasPromotionCandidates, true); +assert.equal(browserDiagnosticsArtifactValidation.evidenceExpansionReady, true); +assert.equal(browserDiagnosticsArtifactValidation.hasEvidenceExpansion, true); assert.equal(browserDiagnosticsArtifactValidation.promotionCandidateSummaryReady, true); assert.equal(browserDiagnosticsArtifactValidation.hasPromotionCandidateSummary, true); assert.equal(browserDiagnosticsArtifactValidation.macroLoadFixturesReady, true); @@ -1494,6 +1567,10 @@ assert.equal(releaseReadinessReady.releaseGate.passed, true); assert.equal(releaseReadinessReady.virtualHalSimConfigSourceCoverageReady, true); assert.equal(releaseReadinessReady.virtualHalSimConfigPromotionCandidatesReady, true); assert.equal(releaseReadinessReady.virtualHalPromotionFamilySummary, expectedPromotionFamilySummary); +assert.deepEqual( + releaseReadinessReady.promotionCandidateArtifactSummary, + expectedPromotionCandidateArtifactSummary, +); assert.deepEqual( releaseReadinessReady.virtualHalPromotionFamilyRows.map(({ id, value }) => [ `virtual-hal-promotion-family-${id}`, @@ -1516,6 +1593,10 @@ assert.equal( createProjectReleaseReadinessSummaryViewModel(releaseReadinessReady).rows.find(({ id }) => id === "axis-screenshot-artifacts")?.value, "not captured", ); +assert.equal( + createProjectReleaseReadinessSummaryViewModel(releaseReadinessReady).rows.find(({ id }) => id === "promotion-candidate-layers")?.value, + "evidence-ready=8 inventory-ready=20", +); const releaseReadinessWithScreenshots = createProjectReleaseReadinessReport({ gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])), virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport, @@ -1582,6 +1663,8 @@ assert.deepEqual(releaseReadinessArtifactValidation, { virtualHalSimConfigPromotionCandidatesReady: true, virtualHalPromotionFamilySummary: expectedPromotionFamilySummary, virtualHalPromotionFamilyRows: releaseReadinessReady.virtualHalPromotionFamilyRows, + promotionCandidateArtifactSummary: expectedPromotionCandidateArtifactSummary, + promotionCandidateArtifactSummaryReady: true, virtualHalSimConfigMacroLoadFixturesReady: true, virtualHalMotionControllerMatrixReady: true, axisScreenshotArtifactSummaryReady: true, @@ -1637,6 +1720,36 @@ assert.deepEqual(releaseReadinessArtifactValidation, { label: "Virtual HAL sim-config promotion candidates", value: "ready", }, + { + id: "promotion-candidate-artifact", + label: "Promotion candidate artifact", + value: "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv", + }, + { + id: "promotion-candidate-layers", + label: "Promotion candidate layers", + value: "evidence-ready=8 inventory-ready=20", + }, + { + id: "promotion-candidate-total", + label: "Promotion candidate total", + value: "28", + }, + { + id: "evidence-expansion-candidates", + label: "Evidence expansion candidates", + value: "13", + }, + { + id: "evidence-expansion-artifact", + label: "Evidence expansion artifact", + value: "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv", + }, + { + id: "promotion-candidate-allowed", + label: "Promotion candidates allowed", + value: "0", + }, { id: "virtual-hal-promotion-families", label: "Virtual HAL promotion families", @@ -1763,6 +1876,32 @@ assert.equal( ).rows.find(({ id }) => id === "virtual-hal-promotion-families")?.value, expectedPromotionFamilySummary, ); +assert.equal( + createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel( + await loadProjectReleaseReadinessArtifactUrlWorkflow({ + artifactUrl: "/project-release-readiness.json", + fetchRef: async () => ({ + ok: true, + status: 200, + text: async () => JSON.stringify(releaseReadinessArtifact), + }), + }), + ).rows.find(({ id }) => id === "promotion-candidate-layers")?.value, + "evidence-ready=8 inventory-ready=20", +); +assert.equal( + createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel( + await loadProjectReleaseReadinessArtifactUrlWorkflow({ + artifactUrl: "/project-release-readiness.json", + fetchRef: async () => ({ + ok: true, + status: 200, + text: async () => JSON.stringify(releaseReadinessArtifact), + }), + }), + ).rows.find(({ id }) => id === "evidence-expansion-candidates")?.value, + "13", +); assert.deepEqual( createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel( await loadProjectReleaseReadinessArtifactUrlWorkflow({ @@ -1815,6 +1954,7 @@ assert.deepEqual( "simConfigInventory.unexpectedFail", "virtualHalSimConfigSourceCoverage", "virtualHalSimConfigPromotionCandidates", + "promotionCandidateArtifactSummary", "virtualHalSimConfigMacroLoadFixtures", "virtualHalMotionControllerMatrix", "blockedRuntimeFamilies", 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 2a03cfe..e857a8d 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 @@ -1797,7 +1797,7 @@ const releaseArtifactMountResult = mountIniPanelShellWorkflowOverviewReleaseRead rowsNode: releaseArtifactRowsNode, }); assert.equal(releaseArtifactRenderResult.rendered, true); -assert.equal(releaseArtifactRenderResult.rowCount, 20); +assert.equal(releaseArtifactRenderResult.rowCount, 26); assert.equal(releaseArtifactRenderResult.dataset.handoffScope, "workflow-overview-release-readiness-artifact"); assert.equal( releaseArtifactRenderResult.rowIds.includes("gate-execution-manifest"), @@ -1811,6 +1811,18 @@ assert.equal( releaseArtifactRenderResult.rowIds.includes("virtual-hal-promotion-families"), true, ); +assert.equal( + releaseArtifactRenderResult.rowIds.includes("promotion-candidate-layers"), + true, +); +assert.equal( + releaseArtifactRenderResult.rowIds.includes("promotion-candidate-allowed"), + true, +); +assert.equal( + releaseArtifactRenderResult.rowIds.includes("evidence-expansion-candidates"), + true, +); assert.equal( releaseArtifactRenderResult.rowIds.includes("gate-action-plan"), true, @@ -1828,7 +1840,7 @@ assert.equal( true, ); assert.equal(releaseArtifactMountResult.ready, true); -assert.equal(releaseArtifactMountResult.renderResult.rowCount, 20); +assert.equal(releaseArtifactMountResult.renderResult.rowCount, 26); assert.deepEqual( validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson("{").missing, ["artifact-json"], @@ -1887,7 +1899,7 @@ assert.equal( ); assert.equal(releaseArtifactUrlWorkflow.renderState.statusLine, "Ready: No blocking reasons"); assert.equal(releaseArtifactUrlWorkflow.mountResult.ready, true); -assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 20); +assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 26); const releaseArtifactUrlWorkflowSummary = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel( releaseArtifactUrlWorkflow, diff --git a/wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs b/wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs index f1fedf2..c12c163 100644 --- a/wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs +++ b/wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs @@ -102,6 +102,7 @@ const runtimeProbeGateAlignmentPath = resolve(buildDir, "runtime-probe-gate-alig const nativeRuntimeProbeExecutionPlanPath = resolve(buildDir, "native-runtime-probe-execution-plan.tsv"); const nativeRuntimeProbePassEvidenceContractPath = resolve(buildDir, "native-runtime-probe-pass-evidence-contract.tsv"); const promotionCandidatesPath = resolve(buildDir, "promotion-candidates.tsv"); +const evidenceExpansionCandidatesPath = resolve(buildDir, "evidence-expansion-candidates.tsv"); const blockedRuntimePromotionLockPath = resolve(buildDir, "blocked-runtime-promotion-lock.tsv"); const runtimeBoundaryPromotionReadinessPath = resolve(buildDir, "runtime-boundary-promotion-readiness.tsv"); const runtimeBoundaryPromotionBlockersPath = resolve(buildDir, "runtime-boundary-promotion-blockers.tsv"); @@ -12055,6 +12056,127 @@ function promotionCandidateRows({ return [...evidenceReadyRows, ...inventoryReadyRows]; } +function evidenceExpansionCandidateRows({ + summaryRows, + boundaryRows, + trackedMatrixByPath, +}) { + const promotedEvidencePaths = new Set( + VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES.map((candidate) => + sourcePathToInventoryPath(candidate.gcodePath) + ), + ); + const boundaryByPath = new Map(boundaryRows.map((row) => [row.path, row])); + + return summaryRows + .map((row) => { + const [ + path, + inventoryStatus, + reason, + className, + nativeStatus, + nativeExpectedFailure, + ] = row.split("\t"); + return { + path, + inventoryStatus, + reason, + className, + nativeStatus, + nativeExpectedFailure, + }; + }) + .filter((row) => + row.inventoryStatus === "PASS" && + row.reason === "-" && + row.className === "main" && + row.nativeStatus === "PASS" && + !promotedEvidencePaths.has(row.path) && + (boundaryByPath.get(row.path)?.blocked ?? "-") === "-" + ) + .sort((left, right) => left.path.localeCompare(right.path)) + .map((row) => { + const boundary = boundaryByPath.get(row.path); + const matrix = trackedMatrixByPath.get(row.path); + return [ + "browser-diagnostics-expansion", + row.path, + boundary?.ini ?? "-", + row.inventoryStatus, + row.reason, + matrix?.layer4Node ?? "-", + matrix?.layer4Browser ?? "-", + row.className, + row.nativeStatus, + row.nativeExpectedFailure, + matrix?.blocked ?? boundary?.blocked ?? "-", + boundary?.dependencies ?? "-", + "0", + "needs_browser_diagnostics_binding_no_inventory_baseline_change", + "wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh && wasm-port/tests/browser/verify_ini_panel_browser.sh", + ].map(tsvValue).join("\t"); + }); +} + +function verifyEvidenceExpansionCandidateRows(rows, summaryRows, boundaryRows, trackedMatrixByPath) { + const expectedHeaders = [ + "candidate_kind", + "path", + "ini", + "current_status", + "skip_kind", + "matrix_layer4_node", + "matrix_layer4_browser", + "class", + "native_status", + "native_expected_failure", + "blocked_kind", + "dependency_class", + "promotion_allowed", + "block_reason", + "recommended_next_command", + ]; + const parsedRows = parseTsv( + `${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`, + expectedHeaders, + ); + const boundaryByPath = new Map(boundaryRows.map((row) => [row.path, row])); + const expectedPaths = evidenceExpansionCandidateRows({ + summaryRows, + boundaryRows, + trackedMatrixByPath, + }).map((row) => row.split("\t")[1]).sort(); + + assert.deepEqual( + parsedRows.map((row) => row.path).sort(), + expectedPaths, + "evidence expansion candidates must cover every non-hard-block PASS main candidate outside evidence-ready", + ); + assert.equal(parsedRows.length, 13, "current evidence expansion candidate count drift"); + for (const row of parsedRows) { + const boundary = boundaryByPath.get(row.path); + assert.ok(boundary, `${row.path}: evidence expansion candidate lacks boundary evidence`); + assert.equal(row.candidate_kind, "browser-diagnostics-expansion", `${row.path}: candidate kind drift`); + assert.equal(row.current_status, "PASS", `${row.path}: expansion candidate must be PASS`); + assert.equal(row.skip_kind, "-", `${row.path}: expansion candidate must not have a skip kind`); + assert.equal(row.class, "main", `${row.path}: expansion candidate must be main class`); + assert.equal(row.native_status, "PASS", `${row.path}: expansion candidate must have native PASS`); + assert.equal(row.blocked_kind, "-", `${row.path}: expansion candidate must not be blocked`); + assert.equal(row.promotion_allowed, "0", `${row.path}: expansion candidate must not claim baseline promotion`); + assert.equal( + row.block_reason, + "needs_browser_diagnostics_binding_no_inventory_baseline_change", + `${row.path}: expansion candidate block reason drift`, + ); + assert.ok( + row.recommended_next_command.includes("verify_release_artifact_url_workflow_browser.sh"), + `${row.path}: expansion candidate lacks browser diagnostics command`, + ); + } + return parsedRows; +} + function verifyPromotionCandidateRows(rows, summaryRows, boundaryRows) { const expectedHeaders = [ "candidate_kind", @@ -12633,6 +12755,17 @@ verifyPromotionCandidateRows( summaryRows, boundarySummaryRecords, ); +const evidenceExpansionCandidateRowsGenerated = evidenceExpansionCandidateRows({ + summaryRows, + boundaryRows: boundarySummaryRecords, + trackedMatrixByPath, +}); +verifyEvidenceExpansionCandidateRows( + evidenceExpansionCandidateRowsGenerated, + summaryRows, + boundarySummaryRecords, + trackedMatrixByPath, +); writeFileSync( promotionCandidatesPath, `${[ @@ -12659,6 +12792,30 @@ writeFileSync( ].join("\n")}\n`, ); +writeFileSync( + evidenceExpansionCandidatesPath, + `${[ + [ + "candidate_kind", + "path", + "ini", + "current_status", + "skip_kind", + "matrix_layer4_node", + "matrix_layer4_browser", + "class", + "native_status", + "native_expected_failure", + "blocked_kind", + "dependency_class", + "promotion_allowed", + "block_reason", + "recommended_next_command", + ].join("\t"), + ...evidenceExpansionCandidateRowsGenerated, + ].join("\n")}\n`, +); + writeFileSync( boundarySummaryPath, `${[