接入虚拟HAL仿真配置promotion证据

This commit is contained in:
2026-06-18 04:47:30 +08:00
parent abf858641f
commit 0bc6896b36
24 changed files with 2879 additions and 108 deletions

View File

@@ -8,14 +8,19 @@
<pre id="status">running</pre>
<script type="module">
import {
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
createVirtualHalCommandScriptFixtureReport,
createVirtualHalMotionControllerMatrixReport,
createVirtualHalSimConfigPromotionCandidateReport,
createVirtualHalSimConfigSourceCoverageReport,
createVirtualHalSimulationReplacementReport,
createVirtualHalSourceComplianceReport,
createVirtualHalState,
} from "../../runtime/sdk/src/index.js";
const VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES =
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS.flatMap((target) => target.sourceFiles ?? []);
function assertEqual(actual, expected, label) {
if (actual !== expected) {
throw new Error(`${label}: expected ${expected}, got ${actual}`);
@@ -60,6 +65,11 @@
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness ||
!workflowApi?.renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState ||
!workflowApi?.mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState ||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState ||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomContract ||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomReadiness ||
!workflowApi?.renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState ||
!workflowApi?.mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState ||
!workflowApi?.runWorkflowOverviewReleaseReadinessArtifactUrlForm ||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel ||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan ||
@@ -348,16 +358,49 @@
renderState: releaseArtifactJsonWorkflowRenderState,
});
const diagnosticsVirtualHalState = createVirtualHalState();
const diagnosticsArtifactJson = JSON.stringify({
const diagnosticsArtifact = {
apiName: "real-browser-simulation-diagnostics-artifact",
artifactVersion: 1,
virtualHal: diagnosticsVirtualHalState,
virtualHalSimulationReplacement: createVirtualHalSimulationReplacementReport(diagnosticsVirtualHalState),
virtualHalSourceCompliance: createVirtualHalSourceComplianceReport({ halState: diagnosticsVirtualHalState }),
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
virtualHalSimConfigPromotionCandidates: createVirtualHalSimConfigPromotionCandidateReport({
manifestEntries: [
...VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/qtdragon_xyza.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/qtdragon_hd_z_compensation.ini",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/on_abort.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/puma_seam_weld.ngc",
"linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
"linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
],
motionControllerMatrix: createVirtualHalMotionControllerMatrixReport({
manifestEntries: VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
}),
}),
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport({ halState: diagnosticsVirtualHalState }),
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport(),
});
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport({
manifestEntries: VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
}),
};
const diagnosticsSessionNestedArtifact = { ...diagnosticsArtifact };
diagnosticsArtifact.virtualHalSessionDiagnostics = {
apiName: "real-browser-simulation-virtual-hal-session-diagnostics",
artifactVersion: 1,
ready: true,
phase: "release-ready",
sessionId: "workflow-overview-virtual-hal-session",
machineId: "workflow-overview-machine",
createdAt: "2026-06-17T00:00:00.000Z",
metadata: { source: "workflow-overview-url-fixture" },
files: {},
virtualHalRevision: diagnosticsVirtualHalState.revision,
validation: workflowApi.validateBrowserDiagnosticsArtifact?.(diagnosticsSessionNestedArtifact) ?? { ready: true },
diagnosticsArtifact: diagnosticsSessionNestedArtifact,
};
const diagnosticsArtifactJson = JSON.stringify(diagnosticsArtifact);
const releaseArtifactUrlWorkflow = await workflowApi.loadWorkflowOverviewReleaseReadinessArtifactUrl({
artifactUrl: "../../tests/fixtures/project-release-readiness-ready.json",
diagnosticsUrl: "../../tests/fixtures/real-browser-simulation-diagnostics-ready.json",
@@ -489,6 +532,8 @@
assertEqual(releaseArtifactUrlWorkflow.httpStatus, 200, "workflow overview release artifact URL workflow status");
assertEqual(releaseArtifactUrlWorkflow.diagnosticsHttpStatus, 200, "workflow overview diagnostics artifact URL workflow status");
assertEqual(releaseArtifactUrlWorkflow.diagnosticsValidation.ready, true, "workflow overview diagnostics artifact validation");
assertEqual(releaseArtifactUrlWorkflow.diagnosticsValidation.hasSessionDiagnostics, true, "workflow overview diagnostics saved-session presence");
assertEqual(releaseArtifactUrlWorkflow.diagnosticsValidation.sessionDiagnosticsReady, true, "workflow overview diagnostics saved-session validation");
assertEqual(
releaseArtifactUrlWorkflow.validationSummaryViewModel.statusLine,
"Ready: Artifact evidence complete",
@@ -511,7 +556,7 @@
);
assertEqual(
releaseArtifactUrlWorkflowRenderResult.rowCount,
11,
12,
"workflow overview release artifact URL workflow render rows",
);
assertEqual(
@@ -519,6 +564,11 @@
true,
"workflow overview release artifact URL workflow diagnostics row",
);
assertEqual(
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("saved-session-diagnostics"),
true,
"workflow overview release artifact URL workflow saved-session diagnostics row",
);
assertEqual(
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("mount-result"),
true,
@@ -544,6 +594,27 @@
true,
"workflow overview fixed URL workflow form mount readiness",
);
assertEqual(
releaseArtifactUrlFormResult.actionPlanMountResult.ready,
true,
"workflow overview fixed URL workflow action-plan mount readiness",
);
assertEqual(
releaseArtifactUrlFormResult.actionPlanRenderState.rowIds?.includes?.("next-command") ??
releaseArtifactUrlFormResult.actionPlanRenderState.rows.some(({ id }) => id === "next-command"),
true,
"workflow overview fixed URL workflow action-plan next-command row",
);
assertEqual(
releaseArtifactUrlFormResult.gateExecutionSummaryMountResult.ready,
true,
"workflow overview fixed URL workflow gate execution summary mount readiness",
);
assertEqual(
releaseArtifactUrlFormResult.gateExecutionSummaryRenderState.rows.some(({ id }) => id === "gate-ui-node-smokes"),
true,
"workflow overview fixed URL workflow gate execution summary row",
);
assertEqual(
workflowDoc.body.dataset.releaseArtifactUrlWorkflowReady,
"true",
@@ -554,6 +625,16 @@
"ready",
"workflow overview fixed URL workflow form browser diagnostics row",
);
assertEqual(
workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-action-plan-value="next-command"]').textContent,
"none",
"workflow overview fixed URL workflow action-plan DOM next command",
);
assertEqual(
workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-gate-execution-summary-value="gate-ui-node-smokes"]')?.textContent.includes("passed: observed output matched"),
true,
"workflow overview fixed URL workflow gate execution summary DOM row",
);
assertEqual(
releaseArtifactJsonWorkflow.statusLine,
"Ready: Artifact JSON workflow complete",

View File

@@ -243,6 +243,7 @@
!api?.getVirtualHalSimulationReplacementReport ||
!api?.getVirtualHalSourceComplianceReport ||
!api?.getVirtualHalSimConfigSourceCoverageReport ||
!api?.getVirtualHalSimConfigPromotionCandidateReport ||
!api?.getVirtualHalCommandScriptFixtureReport ||
!api?.getVirtualHalMotionControllerMatrixReport ||
!api?.getVirtualRealtimeHalRuntimeReport
@@ -418,6 +419,13 @@
!diagnosticsArtifact.virtualHalSourceCompliance?.sourceFiles?.includes("linuxcnc/src/hal/utils/halcmd_commands.cc") ||
diagnosticsArtifact.virtualHalSimConfigSourceCoverage?.complete !== true ||
!diagnosticsArtifact.virtualHalSimConfigSourceCoverage?.sourceFiles?.includes("linuxcnc/configs/sim/axis/foam/axis_foam.ini") ||
diagnosticsArtifact.virtualHalSimConfigPromotionCandidates?.complete !== true ||
!diagnosticsArtifact.virtualHalSimConfigPromotionCandidates?.rows?.some((row) =>
row.id === "qtdragon-multi-joint-on-abort" &&
row.complete === true &&
row.currentNodeInventoryStatus === "PASS" &&
row.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini")
) ||
diagnosticsArtifact.virtualHalCommandScriptFixtures?.complete !== true ||
!diagnosticsArtifact.virtualHalCommandScriptFixtures?.coveredActions?.includes("loadusr") ||
diagnosticsArtifact.virtualHalMotionControllerMatrix?.complete !== true ||
@@ -820,6 +828,8 @@
motionControllerMatrix.apiName !== "linuxcnc-wasm-virtual-hal-motion-controller-matrix-report" ||
motionControllerMatrix.complete !== true ||
motionControllerMatrix.webSimulationSatisfied !== true ||
motionControllerMatrix.manifestChecked !== true ||
motionControllerMatrix.missingManifestFiles.length !== 0 ||
!motionControllerMatrix.requiredPins.includes("motion.distance-to-go") ||
!motionControllerMatrix.requiredPins.includes("motion.in-position") ||
!motionControllerMatrix.sourceFiles.includes("linuxcnc/src/emc/motion/axis.c") ||
@@ -829,11 +839,13 @@
!motionControllerMatrix.simConfigSourceFiles.includes("linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini") ||
!motionControllerMatrix.simConfigSourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini") ||
!motionControllerMatrix.simConfigSourceFiles.includes("linuxcnc/configs/sim/axis/vismach/puma/puma.ini") ||
!motionControllerMatrix.rows.every(({ manifestChecked, missingManifestFiles }) => manifestChecked === true && missingManifestFiles.length === 0) ||
!motionControllerMatrix.rows.some(({ id, inPosition }) => id === "xyz-multi-axis-in-position" && inPosition === true)
) {
throw new Error(`AXIS-style virtual motion controller matrix did not pass: ${JSON.stringify(motionControllerMatrix)}`);
}
const virtualHalReplacement = api.getVirtualHalSimulationReplacementReport();
const promotionCandidateReport = api.getVirtualHalSimConfigPromotionCandidateReport();
if (
virtualHalReplacement.apiName !== "linuxcnc-wasm-virtual-hal-simulation-replacement-report" ||
virtualHalReplacement.ready !== true ||
@@ -851,6 +863,12 @@
!api.getVirtualHalSourceComplianceReport().sourceFiles.includes("linuxcnc/src/emc/motion/motion.c") ||
api.getVirtualHalSimConfigSourceCoverageReport().complete !== true ||
!api.getVirtualHalSimConfigSourceCoverageReport().sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc") ||
promotionCandidateReport.complete !== true ||
promotionCandidateReport.inventoryBaselineUnchanged !== true ||
!promotionCandidateReport.rows.some(({ id, targetBrowserEvidence }) =>
id === "qtdragon-multi-joint-on-abort" &&
targetBrowserEvidence === "explicit-browser-diagnostics"
) ||
api.getVirtualRealtimeHalRuntimeReport().ready !== true ||
api.getVirtualHalSimulationRuntimeReport().replacesHostRuntimeForSimulation !== true
) {

View File

@@ -22,6 +22,10 @@ function parseTsv(text) {
const docText = readFileSync(resolve(root, "docs/sim-configs-coverage-handoff.md"), "utf8");
const matrixText = readFileSync(resolve(root, "docs/sim-configs-coverage-matrix.md"), "utf8");
const completionPlanText = readFileSync(resolve(root, "docs/sim-configs-completion-plan.md"), "utf8");
const promotionAnalysisText = readFileSync(
resolve(root, "docs/sim-config-coverage-promotion-analysis.md"),
"utf8",
);
const inventoryTestText = readFileSync(
resolve(root, "tests/wasm/node/verify_sim_configs_inventory_wasm.mjs"),
"utf8",
@@ -52,6 +56,25 @@ for (const phrase of [
assert.match(docText, literalRegExp(phrase));
}
for (const phrase of [
"Sim Config Coverage Promotion Analysis",
"virtual HAL",
"sim_configs_wasm_node_inventory_executed=28",
"sim_configs_wasm_node_inventory_passed=28",
"sim_configs_wasm_node_inventory_skipped=131",
"qtdragon/qtdragon_multi_joint/on_abort.ngc",
"axis/vismach/puma/puma_seam_weld.ngc",
"axis/rose_engine/rcone_demo.ngc",
"axis/external_offsets/queuebuster.ngc",
"L4-PYTHON-REMAP",
"L4-TOOL-DB",
"L4-USER-M-PROCESS",
"UPSTREAM-DEMO",
"verify_no_standalone_cnc_semantics.sh",
]) {
assert.match(promotionAnalysisText, literalRegExp(phrase));
}
for (const reason of [
"ASSET-ONLY",
"L4-PYTHON-REMAP",
@@ -63,6 +86,7 @@ for (const reason of [
assert.match(matrixText, literalRegExp(reason));
assert.match(completionPlanText, literalRegExp(reason));
assert.match(docText, literalRegExp(reason));
assert.match(promotionAnalysisText, literalRegExp(reason));
}
assert.match(inventoryTestText, /sim_configs_wasm_node_inventory_skip_/);

View File

@@ -15,6 +15,595 @@
"skipped": 131,
"unexpectedFail": 0
},
"virtualHalSimConfigSourceCoverage": {
"apiName": "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report",
"coverageVersion": 1,
"source": "linuxcnc-configs-sim-source-derived-virtual-hal",
"phase": "source-covered",
"complete": true,
"webSimulationSatisfied": true,
"manifestChecked": true,
"targetCount": 7,
"sourceFiles": [
"linuxcnc/configs/sim/axis/foam/axis_foam.ini",
"linuxcnc/configs/sim/axis/foam/foam.ngc",
"linuxcnc/configs/sim/axis/geometry/xyzc.ini",
"linuxcnc/configs/sim/axis/geometry/xyzc.ngc",
"linuxcnc/configs/sim/axis/geometry/M110",
"linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/M111",
"linuxcnc/configs/sim/axis/external_offsets/dyn_demo.ngc",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc",
"linuxcnc/configs/sim/axis/gladevcp/gladevcp_panel.ini",
"linuxcnc/configs/sim/axis/gladevcp/probe.ngc",
"linuxcnc/configs/sim/axis/gladevcp/sim.tbl",
"linuxcnc/configs/sim/woodpecker/woodpecker.ini",
"linuxcnc/configs/sim/woodpecker/on_abort.ngc",
"linuxcnc/configs/sim/woodpecker/tool.tbl",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/qtdragon_inch.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/qtdragon_hd_vertical.ini",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/qtdragon_mpg.ini",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/on_abort.ngc",
"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",
"linuxcnc/configs/sim/axis/vismach/puma/puma.ini",
"linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc"
],
"coveredCapabilities": [
"axis-joint-position-feedback",
"hal-pin-signal-param-store",
"halcmd-simulation-replacement",
"interpreter-hal-bridge-snapshot",
"motion-controller-simulation-replacement",
"realtime-hal-simulation-replacement",
"servo-period-motion-step",
"spindle-coolant-tool-status",
"userspace-load-command-stubs"
],
"requiredCapabilities": [
"realtime-hal-simulation-replacement",
"halcmd-simulation-replacement",
"motion-controller-simulation-replacement",
"hal-pin-signal-param-store",
"userspace-load-command-stubs",
"servo-period-motion-step",
"axis-joint-position-feedback",
"spindle-coolant-tool-status",
"interpreter-hal-bridge-snapshot"
],
"missingCapabilities": [],
"missingTargets": [],
"rows": [
{
"id": "axis-foam",
"label": "AXIS foam sim",
"source": "linuxcnc/configs/sim",
"sourceFiles": [
"linuxcnc/configs/sim/axis/foam/axis_foam.ini",
"linuxcnc/configs/sim/axis/foam/foam.ngc"
],
"capabilities": [
"axis-joint-position-feedback",
"hal-pin-signal-param-store"
],
"evidence": "Vendored LinuxCNC AXIS foam sim config covers axis motion pins and simple program execution.",
"sourceDerived": true,
"manifestChecked": true,
"missingManifestFiles": []
},
{
"id": "axis-geometry-user-m",
"label": "AXIS geometry user-M sim",
"source": "linuxcnc/configs/sim",
"sourceFiles": [
"linuxcnc/configs/sim/axis/geometry/xyzc.ini",
"linuxcnc/configs/sim/axis/geometry/xyzc.ngc",
"linuxcnc/configs/sim/axis/geometry/M110"
],
"capabilities": [
"userspace-load-command-stubs",
"halcmd-simulation-replacement"
],
"evidence": "Vendored LinuxCNC geometry sim config declares USER_M_PATH behavior represented as a simulation boundary.",
"sourceDerived": true,
"manifestChecked": true,
"missingManifestFiles": []
},
{
"id": "external-offsets",
"label": "External offsets sim",
"source": "linuxcnc/configs/sim",
"sourceFiles": [
"linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/M111",
"linuxcnc/configs/sim/axis/external_offsets/dyn_demo.ngc",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc"
],
"capabilities": [
"motion-controller-simulation-replacement",
"servo-period-motion-step"
],
"evidence": "Vendored LinuxCNC external-offset configs exercise motion/HAL pins and deterministic user-M boundary accounting.",
"sourceDerived": true,
"manifestChecked": true,
"missingManifestFiles": []
},
{
"id": "gladevcp-probe",
"label": "GladeVCP probe sim",
"source": "linuxcnc/configs/sim",
"sourceFiles": [
"linuxcnc/configs/sim/axis/gladevcp/gladevcp_panel.ini",
"linuxcnc/configs/sim/axis/gladevcp/probe.ngc",
"linuxcnc/configs/sim/axis/gladevcp/sim.tbl"
],
"capabilities": [
"interpreter-hal-bridge-snapshot",
"hal-pin-signal-param-store"
],
"evidence": "Vendored LinuxCNC probe sim config covers probe input and tool-table staging without browser-owned CNC semantics.",
"sourceDerived": true,
"manifestChecked": true,
"missingManifestFiles": []
},
{
"id": "woodpecker",
"label": "Woodpecker sim",
"source": "linuxcnc/configs/sim",
"sourceFiles": [
"linuxcnc/configs/sim/woodpecker/woodpecker.ini",
"linuxcnc/configs/sim/woodpecker/on_abort.ngc",
"linuxcnc/configs/sim/woodpecker/tool.tbl"
],
"capabilities": [
"halcmd-simulation-replacement",
"spindle-coolant-tool-status"
],
"evidence": "Vendored LinuxCNC woodpecker sim config supplies deterministic on-abort and tool-table workflows.",
"sourceDerived": true,
"manifestChecked": true,
"missingManifestFiles": []
},
{
"id": "qtdragon-on-abort",
"label": "QtDragon on-abort sims",
"source": "linuxcnc/configs/sim",
"sourceFiles": [
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/qtdragon_inch.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/qtdragon_hd_vertical.ini",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/qtdragon_mpg.ini",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/on_abort.ngc"
],
"capabilities": [
"realtime-hal-simulation-replacement",
"axis-joint-position-feedback"
],
"evidence": "Vendored LinuxCNC QtDragon sim configs provide multi-joint and on-abort machine/session coverage for Web simulation.",
"sourceDerived": true,
"manifestChecked": true,
"missingManifestFiles": []
},
{
"id": "vismach-remap-sims",
"label": "Vismach remap sims",
"source": "linuxcnc/configs/sim",
"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",
"linuxcnc/configs/sim/axis/vismach/puma/puma.ini",
"linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc"
],
"capabilities": [
"interpreter-hal-bridge-snapshot",
"motion-controller-simulation-replacement"
],
"evidence": "Vendored LinuxCNC vismach remap configs remain LinuxCNC-owned remap/kinematics assets staged through the WASM boundary.",
"sourceDerived": true,
"manifestChecked": true,
"missingManifestFiles": []
}
],
"summaryRows": [
{
"id": "targets",
"label": "LinuxCNC sim config targets",
"value": "7"
},
{
"id": "source-files",
"label": "Source files",
"value": "30"
},
{
"id": "capabilities",
"label": "Covered capabilities",
"value": "covered"
},
{
"id": "manifest",
"label": "Manifest check",
"value": "passed"
}
]
},
"virtualHalSimConfigSourceCoverageReady": true,
"virtualHalMotionControllerMatrix": {
"apiName": "linuxcnc-wasm-virtual-hal-motion-controller-matrix-report",
"matrixVersion": 1,
"source": "linuxcnc-motion-source-derived-virtual-hal",
"phase": "ready",
"complete": true,
"webSimulationSatisfied": true,
"sourceFiles": [
"linuxcnc/src/emc/motion/motion.c",
"linuxcnc/src/emc/motion/axis.c",
"wasm-port/vendor/linuxcnc/src/emc/motion/motion.h",
"linuxcnc/src/emc/motion/homing.c"
],
"simConfigSourceFiles": [
"linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/M111",
"linuxcnc/configs/sim/axis/external_offsets/dyn_demo.ngc",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/qtdragon_inch.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/qtdragon_hd_vertical.ini",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/qtdragon_mpg.ini",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/on_abort.ngc",
"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",
"linuxcnc/configs/sim/axis/vismach/puma/puma.ini",
"linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc"
],
"simConfigTargets": [
"external-offsets",
"qtdragon-on-abort",
"vismach-remap-sims"
],
"manifestChecked": true,
"requiredPins": [
"motion.distance-to-go",
"motion.in-position",
"axis.x.pos-cmd",
"joint.0.pos-fb",
"axis.y.pos-cmd",
"axis.z.pos-cmd",
"halui.axis.x.pos-feedback",
"joint.1.pos-fb",
"joint.2.pos-fb",
"motion.current-vel",
"motion.requested-vel",
"axis.a.pos-cmd",
"axis.b.pos-cmd",
"axis.c.pos-cmd",
"joint.3.pos-fb",
"joint.4.pos-fb",
"joint.5.pos-fb"
],
"fixtureCount": 3,
"missingFixtures": [],
"missingSimConfigTargets": [],
"missingManifestFiles": [],
"rows": [
{
"id": "x-single-axis-servo-dtg",
"label": "Single-axis servo period target stepping",
"source": "linuxcnc-motion-source-derived-virtual-motion-controller",
"sourceFiles": [
"linuxcnc/src/emc/motion/motion.c",
"linuxcnc/src/emc/motion/axis.c",
"wasm-port/vendor/linuxcnc/src/emc/motion/motion.h"
],
"simConfigTargets": [
"external-offsets"
],
"simConfigEvidenceRows": [
{
"id": "external-offsets",
"label": "External offsets sim",
"sourceFiles": [
"linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/M111",
"linuxcnc/configs/sim/axis/external_offsets/dyn_demo.ngc",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc"
],
"capabilities": [
"motion-controller-simulation-replacement",
"servo-period-motion-step"
],
"evidence": "Vendored LinuxCNC external-offset configs exercise motion/HAL pins and deterministic user-M boundary accounting."
}
],
"simConfigSourceFiles": [
"linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/M111",
"linuxcnc/configs/sim/axis/external_offsets/dyn_demo.ngc",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc"
],
"missingSimConfigTargets": [],
"manifestChecked": true,
"missingManifestFiles": [],
"complete": true,
"target": {
"x": 1
},
"initialPosition": {
"x": 0,
"y": 0,
"z": 0
},
"maxVelocity": 10,
"dt": 0.05,
"cycleCount": 1,
"inPosition": false,
"distanceToGo": 0.5,
"currentVel": 10,
"requestedVel": 10,
"servoPeriod": 0.05,
"missingPins": [],
"inPositionMatched": true,
"distanceToGoMatched": true,
"movingMatched": true,
"axisFeedbackRows": [
{
"axis": "x",
"expected": 0.5,
"axisPosition": 0.5,
"jointFeedback": 0.5,
"haluiFeedback": 0.5,
"matched": true
}
],
"frameCount": 1
},
{
"id": "xyz-multi-axis-in-position",
"label": "Multi-axis target reaches in-position",
"source": "linuxcnc-motion-source-derived-virtual-motion-controller",
"sourceFiles": [
"linuxcnc/src/emc/motion/motion.c",
"linuxcnc/src/emc/motion/axis.c",
"linuxcnc/src/emc/motion/homing.c",
"wasm-port/vendor/linuxcnc/src/emc/motion/motion.h"
],
"simConfigTargets": [
"qtdragon-on-abort"
],
"simConfigEvidenceRows": [
{
"id": "qtdragon-on-abort",
"label": "QtDragon on-abort sims",
"sourceFiles": [
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/qtdragon_inch.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/qtdragon_hd_vertical.ini",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/qtdragon_mpg.ini",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/on_abort.ngc"
],
"capabilities": [
"realtime-hal-simulation-replacement",
"axis-joint-position-feedback"
],
"evidence": "Vendored LinuxCNC QtDragon sim configs provide multi-joint and on-abort machine/session coverage for Web simulation."
}
],
"simConfigSourceFiles": [
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/qtdragon_inch.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/qtdragon_hd_vertical.ini",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/qtdragon_mpg.ini",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/on_abort.ngc"
],
"missingSimConfigTargets": [],
"manifestChecked": true,
"missingManifestFiles": [],
"complete": true,
"target": {
"x": 1,
"y": -0.5,
"z": 0.25
},
"initialPosition": {
"x": 0,
"y": 0,
"z": 0
},
"maxVelocity": 10,
"dt": 0.1,
"cycleCount": 1,
"inPosition": true,
"distanceToGo": 0,
"currentVel": 17.5,
"requestedVel": 10,
"servoPeriod": 0.1,
"missingPins": [],
"inPositionMatched": true,
"distanceToGoMatched": true,
"movingMatched": true,
"axisFeedbackRows": [
{
"axis": "x",
"expected": 1,
"axisPosition": 1,
"jointFeedback": 1,
"haluiFeedback": 1,
"matched": true
},
{
"axis": "y",
"expected": -0.5,
"axisPosition": -0.5,
"jointFeedback": -0.5,
"haluiFeedback": -0.5,
"matched": true
},
{
"axis": "z",
"expected": 0.25,
"axisPosition": 0.25,
"jointFeedback": 0.25,
"haluiFeedback": 0.25,
"matched": true
}
],
"frameCount": 1
},
{
"id": "abc-rotary-feedback",
"label": "Rotary axis feedback follows target",
"source": "linuxcnc-motion-source-derived-virtual-motion-controller",
"sourceFiles": [
"linuxcnc/src/emc/motion/motion.c",
"linuxcnc/src/emc/motion/axis.c",
"wasm-port/vendor/linuxcnc/src/emc/motion/motion.h"
],
"simConfigTargets": [
"vismach-remap-sims"
],
"simConfigEvidenceRows": [
{
"id": "vismach-remap-sims",
"label": "Vismach remap sims",
"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",
"linuxcnc/configs/sim/axis/vismach/puma/puma.ini",
"linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc"
],
"capabilities": [
"interpreter-hal-bridge-snapshot",
"motion-controller-simulation-replacement"
],
"evidence": "Vendored LinuxCNC vismach remap configs remain LinuxCNC-owned remap/kinematics assets staged through the WASM boundary."
}
],
"simConfigSourceFiles": [
"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",
"linuxcnc/configs/sim/axis/vismach/puma/puma.ini",
"linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc"
],
"missingSimConfigTargets": [],
"manifestChecked": true,
"missingManifestFiles": [],
"complete": true,
"target": {
"a": 0.4,
"b": -0.2,
"c": 0.1
},
"initialPosition": {
"a": 0,
"b": 0,
"c": 0
},
"maxVelocity": 8,
"dt": 0.1,
"cycleCount": 1,
"inPosition": true,
"distanceToGo": 0,
"currentVel": 7,
"requestedVel": 8,
"servoPeriod": 0.1,
"missingPins": [],
"inPositionMatched": true,
"distanceToGoMatched": true,
"movingMatched": true,
"axisFeedbackRows": [
{
"axis": "a",
"expected": 0.4,
"axisPosition": 0.4,
"jointFeedback": 0.4,
"haluiFeedback": 0.4,
"matched": true
},
{
"axis": "b",
"expected": -0.2,
"axisPosition": -0.2,
"jointFeedback": -0.2,
"haluiFeedback": -0.2,
"matched": true
},
{
"axis": "c",
"expected": 0.1,
"axisPosition": 0.1,
"jointFeedback": 0.1,
"haluiFeedback": 0.1,
"matched": true
}
],
"frameCount": 1
}
],
"summaryRows": [
{
"id": "fixtures",
"label": "Motion matrix fixtures",
"value": "3"
},
{
"id": "source",
"label": "LinuxCNC motion source",
"value": "source-derived"
},
{
"id": "sim-config-source",
"label": "LinuxCNC sim config source",
"value": "source-derived"
},
{
"id": "manifest",
"label": "Sim config manifest check",
"value": "passed"
},
{
"id": "servo-period",
"label": "Servo period stepping",
"value": "covered"
},
{
"id": "axis-joint-feedback",
"label": "Axis/joint feedback",
"value": "covered"
}
]
},
"virtualHalMotionControllerMatrixReady": true,
"blockedRuntimeFamilies": [
"L4-USER-M-PROCESS",
"L4-TOOL-DB",
@@ -22,6 +611,29 @@
],
"promotedRuntimeFamilies": [],
"promotedBlockedFamilies": [],
"axisScreenshotArtifactSummary": {
"apiName": "project-release-axis-screenshot-artifact-summary",
"summaryVersion": 1,
"phase": "ready",
"ready": true,
"expectedViewports": [
"desktop-preview",
"desktop-dro",
"mobile-preview",
"mobile-mdi"
],
"artifactCount": 0,
"viewportNames": [],
"missingViewports": [
"desktop-preview",
"desktop-dro",
"mobile-preview",
"mobile-mdi"
],
"undersizedViewports": [],
"totalScreenshotBytes": 0,
"artifacts": []
},
"gateManifest": {
"apiName": "project-release-gate-manifest",
"manifestVersion": 1,
@@ -224,7 +836,7 @@
"command": "git diff --check",
"expectedOutput": null,
"observedOutput": null,
"observedOutputCount": 11,
"observedOutputCount": 0,
"status": "passed",
"passed": true,
"evidence": "explicit-result"
@@ -234,121 +846,121 @@
"label": "Vendor sync guard",
"command": "wasm-port/tools/verify_vendor_sync.sh",
"expectedOutput": "vendor sync up to date",
"observedOutput": "vendor sync up to date",
"observedOutputCount": 11,
"observedOutput": null,
"observedOutputCount": 0,
"status": "passed",
"passed": true,
"evidence": "expected-output"
"evidence": "explicit-result"
},
{
"id": "standalone-cnc-semantics",
"label": "Standalone CNC semantics guard",
"command": "wasm-port/tools/verify_no_standalone_cnc_semantics.sh",
"expectedOutput": "standalone CNC semantics guard complete",
"observedOutput": "standalone CNC semantics guard complete",
"observedOutputCount": 11,
"observedOutput": null,
"observedOutputCount": 0,
"status": "passed",
"passed": true,
"evidence": "expected-output"
"evidence": "explicit-result"
},
{
"id": "interp-wasm",
"label": "Interpreter WASM Node smoke",
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh",
"expectedOutput": "interp_wasm_node_smoke=ok",
"observedOutput": "interp_wasm_node_smoke=ok",
"observedOutputCount": 11,
"observedOutput": null,
"observedOutputCount": 0,
"status": "passed",
"passed": true,
"evidence": "expected-output"
"evidence": "explicit-result"
},
{
"id": "sim-config-inventory",
"label": "Sim config inventory WASM Node smoke",
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh",
"expectedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0",
"observedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0",
"observedOutputCount": 11,
"observedOutput": null,
"observedOutputCount": 0,
"status": "passed",
"passed": true,
"evidence": "expected-output"
"evidence": "explicit-result"
},
{
"id": "ini-panel-browser",
"label": "INI panel browser smoke",
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh",
"expectedOutput": "browser_ini_shell_integration_workflow_smoke=ok",
"observedOutput": "browser_ini_shell_integration_workflow_smoke=ok",
"observedOutputCount": 11,
"observedOutput": null,
"observedOutputCount": 0,
"status": "passed",
"passed": true,
"evidence": "expected-output"
"evidence": "explicit-result"
},
{
"id": "opfs-session-browser",
"label": "OPFS session browser workflow smoke",
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh",
"expectedOutput": "browser_opfs_session_workflow_smoke=ok",
"observedOutput": "browser_opfs_session_workflow_smoke=ok",
"observedOutputCount": 11,
"observedOutput": null,
"observedOutputCount": 0,
"status": "passed",
"passed": true,
"evidence": "expected-output"
"evidence": "explicit-result"
},
{
"id": "release-artifact-url-browser",
"label": "Release artifact URL browser workflow smoke",
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh",
"expectedOutput": "browser_release_artifact_url_workflow_smoke=ok",
"observedOutput": "browser_release_artifact_url_workflow_smoke=ok",
"observedOutputCount": 11,
"observedOutput": null,
"observedOutputCount": 0,
"status": "passed",
"passed": true,
"evidence": "expected-output"
"evidence": "explicit-result"
},
{
"id": "project-batch-acceptance",
"label": "Project batch acceptance workflow smoke",
"command": "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
"expectedOutput": "project_batch_acceptance_workflow_node_smoke=ok",
"observedOutput": "project_batch_acceptance_workflow_node_smoke=ok",
"observedOutputCount": 11,
"observedOutput": null,
"observedOutputCount": 0,
"status": "passed",
"passed": true,
"evidence": "expected-output"
"evidence": "explicit-result"
},
{
"id": "ui-node-smokes",
"label": "UI Node smokes",
"command": "wasm-port/tests/ui/node/verify_ui_node_smokes.sh",
"expectedOutput": "ui_node_smokes=ok",
"observedOutput": "ui_node_smokes=ok",
"observedOutputCount": 11,
"observedOutput": null,
"observedOutputCount": 0,
"status": "passed",
"passed": true,
"evidence": "expected-output"
"evidence": "explicit-result"
},
{
"id": "host-smokes",
"label": "Host aggregate smoke",
"command": "wasm-port/tests/host/verify_host_smokes.sh",
"expectedOutput": "host_wasm_opfs_browser_smokes=ok",
"observedOutput": "host_wasm_opfs_browser_smokes=ok",
"observedOutputCount": 11,
"observedOutput": null,
"observedOutputCount": 0,
"status": "passed",
"passed": true,
"evidence": "expected-output"
"evidence": "explicit-result"
},
{
"id": "project-release-gate",
"label": "Project release gate",
"command": "wasm-port/tests/host/verify_project_release_gate.sh",
"expectedOutput": "project_release_gate=ok",
"observedOutput": "project_release_gate=ok",
"observedOutputCount": 11,
"observedOutput": null,
"observedOutputCount": 0,
"status": "passed",
"passed": true,
"evidence": "expected-output"
"evidence": "explicit-result"
}
]
},
@@ -367,8 +979,7 @@
"missingGateIds": [],
"nextGateId": null,
"evidenceCounts": {
"explicit-result": 1,
"expected-output": 11
"explicit-result": 12
},
"rows": [
{
@@ -389,12 +1000,12 @@
{
"id": "expected-output-evidence",
"label": "Expected-output evidence",
"value": "11"
"value": "0"
},
{
"id": "explicit-result-evidence",
"label": "Explicit-result evidence",
"value": "1"
"value": "12"
},
{
"id": "missing",
@@ -418,8 +1029,8 @@
"label": "Vendor sync guard",
"command": "wasm-port/tools/verify_vendor_sync.sh",
"expectedOutput": "vendor sync up to date",
"observedOutput": "vendor sync up to date",
"evidence": "expected-output",
"observedOutput": null,
"evidence": "explicit-result",
"status": "passed",
"passed": true
},
@@ -428,8 +1039,8 @@
"label": "Standalone CNC semantics guard",
"command": "wasm-port/tools/verify_no_standalone_cnc_semantics.sh",
"expectedOutput": "standalone CNC semantics guard complete",
"observedOutput": "standalone CNC semantics guard complete",
"evidence": "expected-output",
"observedOutput": null,
"evidence": "explicit-result",
"status": "passed",
"passed": true
},
@@ -438,8 +1049,8 @@
"label": "Interpreter WASM Node smoke",
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh",
"expectedOutput": "interp_wasm_node_smoke=ok",
"observedOutput": "interp_wasm_node_smoke=ok",
"evidence": "expected-output",
"observedOutput": null,
"evidence": "explicit-result",
"status": "passed",
"passed": true
},
@@ -448,8 +1059,8 @@
"label": "Sim config inventory WASM Node smoke",
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh",
"expectedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0",
"observedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0",
"evidence": "expected-output",
"observedOutput": null,
"evidence": "explicit-result",
"status": "passed",
"passed": true
},
@@ -458,8 +1069,8 @@
"label": "INI panel browser smoke",
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh",
"expectedOutput": "browser_ini_shell_integration_workflow_smoke=ok",
"observedOutput": "browser_ini_shell_integration_workflow_smoke=ok",
"evidence": "expected-output",
"observedOutput": null,
"evidence": "explicit-result",
"status": "passed",
"passed": true
},
@@ -468,8 +1079,8 @@
"label": "OPFS session browser workflow smoke",
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh",
"expectedOutput": "browser_opfs_session_workflow_smoke=ok",
"observedOutput": "browser_opfs_session_workflow_smoke=ok",
"evidence": "expected-output",
"observedOutput": null,
"evidence": "explicit-result",
"status": "passed",
"passed": true
},
@@ -478,8 +1089,8 @@
"label": "Release artifact URL browser workflow smoke",
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh",
"expectedOutput": "browser_release_artifact_url_workflow_smoke=ok",
"observedOutput": "browser_release_artifact_url_workflow_smoke=ok",
"evidence": "expected-output",
"observedOutput": null,
"evidence": "explicit-result",
"status": "passed",
"passed": true
},
@@ -488,8 +1099,8 @@
"label": "Project batch acceptance workflow smoke",
"command": "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
"expectedOutput": "project_batch_acceptance_workflow_node_smoke=ok",
"observedOutput": "project_batch_acceptance_workflow_node_smoke=ok",
"evidence": "expected-output",
"observedOutput": null,
"evidence": "explicit-result",
"status": "passed",
"passed": true
},
@@ -498,8 +1109,8 @@
"label": "UI Node smokes",
"command": "wasm-port/tests/ui/node/verify_ui_node_smokes.sh",
"expectedOutput": "ui_node_smokes=ok",
"observedOutput": "ui_node_smokes=ok",
"evidence": "expected-output",
"observedOutput": null,
"evidence": "explicit-result",
"status": "passed",
"passed": true
},
@@ -508,8 +1119,8 @@
"label": "Host aggregate smoke",
"command": "wasm-port/tests/host/verify_host_smokes.sh",
"expectedOutput": "host_wasm_opfs_browser_smokes=ok",
"observedOutput": "host_wasm_opfs_browser_smokes=ok",
"evidence": "expected-output",
"observedOutput": null,
"evidence": "explicit-result",
"status": "passed",
"passed": true
},
@@ -518,8 +1129,8 @@
"label": "Project release gate",
"command": "wasm-port/tests/host/verify_project_release_gate.sh",
"expectedOutput": "project_release_gate=ok",
"observedOutput": "project_release_gate=ok",
"evidence": "expected-output",
"observedOutput": null,
"evidence": "explicit-result",
"status": "passed",
"passed": true
}
@@ -864,10 +1475,25 @@
"label": "Sim config inventory",
"value": "unexpected_fail=0"
},
{
"id": "virtual-hal-sim-config-source-coverage",
"label": "Virtual HAL sim-config source coverage",
"value": "ready"
},
{
"id": "virtual-hal-motion-controller-matrix",
"label": "Virtual HAL motion controller matrix",
"value": "ready"
},
{
"id": "blocked-runtime-families",
"label": "Blocked runtime families",
"value": "L4-USER-M-PROCESS, L4-TOOL-DB, L4-PYTHON-REMAP"
},
{
"id": "axis-screenshot-artifacts",
"label": "AXIS screenshot artifacts",
"value": "not captured"
}
]
}

View File

@@ -28,6 +28,7 @@ assert.equal(validation.gateExecutionSummaryReady, true);
assert.equal(validation.gateResultMatrixReady, true);
assert.equal(validation.gateActionPlanReady, true);
assert.equal(validation.virtualHalSimConfigSourceCoverageReady, true);
assert.equal(validation.virtualHalMotionControllerMatrixReady, true);
assert.equal(validation.axisScreenshotArtifactSummaryReady, true);
assert.ok(validation.axisScreenshotArtifactCount === 0 || validation.axisScreenshotArtifactCount === 4);
assert.deepEqual(validation.expectedGateIds, [
@@ -59,6 +60,12 @@ assert.equal(artifact.virtualHalSimConfigSourceCoverage.webSimulationSatisfied,
assert.equal(artifact.virtualHalSimConfigSourceCoverageReady, true);
assert.equal(artifact.virtualHalSimConfigSourceCoverage.sourceFiles.includes("linuxcnc/configs/sim/axis/foam/axis_foam.ini"), true);
assert.equal(artifact.virtualHalSimConfigSourceCoverage.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc"), true);
assert.equal(artifact.virtualHalMotionControllerMatrix.apiName, "linuxcnc-wasm-virtual-hal-motion-controller-matrix-report");
assert.equal(artifact.virtualHalMotionControllerMatrix.complete, true);
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.axisScreenshotArtifactSummary.apiName, "project-release-axis-screenshot-artifact-summary");
if (artifact.axisScreenshotArtifactSummary.artifactCount > 0) {
assert.equal(artifact.axisScreenshotArtifactSummary.artifactCount, 4);

View File

@@ -4,6 +4,7 @@ import { fileURLToPath } from "node:url";
import {
createProjectReleaseReadinessReport,
createVirtualHalMotionControllerMatrixReport,
createVirtualHalSimConfigSourceCoverageReport,
} from "../../runtime/sdk/src/index.js";
@@ -67,6 +68,9 @@ const report = createProjectReleaseReadinessReport({
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport({
manifestText: sourceManifestText,
}),
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport({
manifestText: sourceManifestText,
}),
axisScreenshotArtifacts: loadAxisScreenshotArtifacts(axisScreenshotArtifactDir),
});

View File

@@ -37,14 +37,19 @@ assert.equal(jsonWorkflow.report.capabilityMatrix.ready, true);
assert.deepEqual(jsonWorkflow.report.capabilityMatrix.capabilityTypes, ["workflow", "api", "gate"]);
assert.equal(jsonWorkflow.report.workflow.ready, true);
assert.equal(jsonWorkflow.report.workflow.virtualHalSimConfigSourceCoverageReady, true);
assert.equal(jsonWorkflow.report.workflow.virtualHalMotionControllerMatrixReady, true);
assert.equal(jsonWorkflow.report.virtualHalSimConfigSourceCoverage.apiName, "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report");
assert.equal(jsonWorkflow.report.virtualHalSimConfigSourceCoverage.complete, true);
assert.equal(jsonWorkflow.report.virtualHalSimConfigSourceCoverage.webSimulationSatisfied, true);
assert.equal(jsonWorkflow.report.virtualHalSimConfigSourceCoverage.sourceFiles.includes("linuxcnc/configs/sim/axis/foam/axis_foam.ini"), true);
assert.equal(jsonWorkflow.report.virtualHalMotionControllerMatrix.apiName, "linuxcnc-wasm-virtual-hal-motion-controller-matrix-report");
assert.equal(jsonWorkflow.report.virtualHalMotionControllerMatrix.complete, true);
assert.equal(jsonWorkflow.report.virtualHalMotionControllerMatrix.manifestChecked, true);
assert.equal(jsonWorkflow.report.virtualHalMotionControllerMatrix.simConfigSourceFiles.includes("linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini"), true);
assert.equal(jsonWorkflow.report.summaryViewModel.statusLine, "Ready: Batch acceptance evidence complete");
assert.equal(jsonWorkflow.report.actionPlan.nextCommand, null);
assert.equal(jsonWorkflow.report.checklist.passedCount, 4);
assert.equal(jsonWorkflow.report.rows.find(({ id }) => id === "checklist")?.value, "4/4 passed");
assert.equal(jsonWorkflow.report.checklist.passedCount, 5);
assert.equal(jsonWorkflow.report.rows.find(({ id }) => id === "checklist")?.value, "5/5 passed");
assert.equal(validationSummary.apiName, "project-batch-acceptance-report-validation-summary-view-model");
assert.equal(validationSummary.statusLine, "Ready: Batch acceptance report evidence complete");
assert.equal(validationSummary.rows.find(({ id }) => id === "validation")?.value, "ready");

View File

@@ -122,6 +122,7 @@ assert.deepEqual(
["capabilities", "api, gate"],
["project-batch-acceptance", "passed"],
["virtual-hal-sim-config-source-coverage", "not required"],
["virtual-hal-motion-controller-matrix", "not required"],
["missing", "none"],
],
);
@@ -149,7 +150,7 @@ assert.equal(readyChecklist.checklistVersion, 1);
assert.equal(readyChecklist.phase, "ready");
assert.equal(readyChecklist.ready, true);
assert.equal(readyChecklist.batchId, "batch-acceptance-workflow");
assert.equal(readyChecklist.passedCount, 4);
assert.equal(readyChecklist.passedCount, 5);
assert.equal(readyChecklist.blockedCount, 0);
assert.deepEqual(
readyChecklist.rows.map(({ id, value }) => [id, value]),
@@ -157,6 +158,7 @@ assert.deepEqual(
["accepted-capability", "pass: api, gate"],
["batch-acceptance-gate", "pass: passed"],
["virtual-hal-sim-config-source-coverage", "pass: not required"],
["virtual-hal-motion-controller-matrix", "pass: not required"],
["next-action", "pass: none"],
],
);
@@ -176,13 +178,13 @@ assert.equal(readyReport.capabilityMatrix.apiName, "project-batch-acceptance-cap
assert.equal(readyReport.workflow.apiName, "project-batch-acceptance-workflow");
assert.equal(readyReport.summaryViewModel.statusLine, "Ready: Batch acceptance evidence complete");
assert.equal(readyReport.actionPlan.nextCommand, null);
assert.equal(readyReport.checklist.passedCount, 4);
assert.equal(readyReport.checklist.passedCount, 5);
assert.deepEqual(
readyReport.rows.map(({ id, value }) => [id, value]),
[
["summary", "Ready: Batch acceptance evidence complete"],
["capabilities", "api, gate"],
["checklist", "4/4 passed"],
["checklist", "5/5 passed"],
["next-command", "none"],
],
);
@@ -208,6 +210,7 @@ assert.deepEqual(
["workflow", "ready"],
["checklist", "ready"],
["virtual-hal-sim-config-source-coverage", "not required"],
["virtual-hal-motion-controller-matrix", "not required"],
["missing", "none"],
],
);
@@ -351,7 +354,7 @@ const blockedChecklist = createProjectBatchAcceptanceChecklist({
});
assert.equal(blockedChecklist.phase, "blocked");
assert.equal(blockedChecklist.ready, false);
assert.equal(blockedChecklist.passedCount, 1);
assert.equal(blockedChecklist.passedCount, 2);
assert.equal(blockedChecklist.blockedCount, 3);
assert.deepEqual(
blockedChecklist.items.map(({ id, status }) => [id, status]),
@@ -359,6 +362,7 @@ assert.deepEqual(
["accepted-capability", "blocked"],
["batch-acceptance-gate", "blocked"],
["virtual-hal-sim-config-source-coverage", "pass"],
["virtual-hal-motion-controller-matrix", "pass"],
["next-action", "blocked"],
],
);

View File

@@ -1,4 +1,7 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import {
createProjectReleaseBrowserDiagnosticsArtifactValidation,
@@ -7,6 +10,7 @@ import {
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
createProjectReleaseReadinessReport,
createVirtualHalSimConfigSourceCoverageReport,
createVirtualHalSimConfigPromotionCandidateReport,
createVirtualHalCommandScriptFixtureReport,
createVirtualHalMotionControllerMatrixReport,
createVirtualHalSimulationReplacementReport,
@@ -15,8 +19,17 @@ import {
loadProjectReleaseReadinessArtifactUrlWorkflow,
} from "../../../runtime/sdk/src/index.js";
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = resolve(__dirname, "../../..");
const sourceManifestText = readFileSync(resolve(root, "tools/source-manifest.txt"), "utf8");
const manifest = createProjectReleaseGateManifest();
const virtualHalSimConfigSourceCoverage = createVirtualHalSimConfigSourceCoverageReport();
const virtualHalMotionControllerMatrix = createVirtualHalMotionControllerMatrixReport({ manifestText: sourceManifestText });
const virtualHalSimConfigPromotionCandidates = createVirtualHalSimConfigPromotionCandidateReport({
manifestText: sourceManifestText,
simConfigSourceCoverage: virtualHalSimConfigSourceCoverage,
motionControllerMatrix: virtualHalMotionControllerMatrix,
});
const virtualHalState = createVirtualHalState();
const browserDiagnosticsArtifact = {
apiName: "real-browser-simulation-diagnostics-artifact",
@@ -25,13 +38,31 @@ const browserDiagnosticsArtifact = {
virtualHalSimulationReplacement: createVirtualHalSimulationReplacementReport(virtualHalState),
virtualHalSourceCompliance: createVirtualHalSourceComplianceReport({ halState: virtualHalState }),
virtualHalSimConfigSourceCoverage,
virtualHalSimConfigPromotionCandidates,
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport({ halState: virtualHalState }),
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport(),
virtualHalMotionControllerMatrix,
};
const browserDiagnosticsSessionNestedArtifact = { ...browserDiagnosticsArtifact };
const savedSessionDiagnosticsArtifact = {
apiName: "real-browser-simulation-virtual-hal-session-diagnostics",
artifactVersion: 1,
ready: true,
phase: "release-ready",
sessionId: "release-url-workflow-virtual-hal-session",
machineId: "release-url-workflow-machine",
createdAt: "2026-06-17T00:00:00.000Z",
metadata: { source: "project-release-artifact-url-workflow-fixture" },
files: {},
virtualHalRevision: virtualHalState.revision,
validation: createProjectReleaseBrowserDiagnosticsArtifactValidation(browserDiagnosticsSessionNestedArtifact),
diagnosticsArtifact: browserDiagnosticsSessionNestedArtifact,
};
browserDiagnosticsArtifact.virtualHalSessionDiagnostics = savedSessionDiagnosticsArtifact;
assert.equal(createProjectReleaseBrowserDiagnosticsArtifactValidation(browserDiagnosticsArtifact).ready, true);
const readyArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({
gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])),
virtualHalSimConfigSourceCoverage,
virtualHalMotionControllerMatrix: browserDiagnosticsArtifact.virtualHalMotionControllerMatrix,
}));
let fetchedUrl = null;
@@ -67,6 +98,10 @@ assert.equal(readyWorkflow.fetchError, null);
assert.equal(readyWorkflow.jsonWorkflow.ready, true);
assert.equal(readyWorkflow.validation.ready, true);
assert.equal(readyWorkflow.diagnosticsValidation.ready, true);
assert.equal(readyWorkflow.diagnosticsValidation.hasPromotionCandidates, true);
assert.equal(readyWorkflow.diagnosticsValidation.promotionCandidatesReady, true);
assert.equal(readyWorkflow.diagnosticsValidation.hasSessionDiagnostics, true);
assert.equal(readyWorkflow.diagnosticsValidation.sessionDiagnosticsReady, true);
assert.equal(readyWorkflow.diagnosticsFetched, true);
assert.equal(readyWorkflow.diagnosticsHttpStatus, 200);
assert.equal(readyWorkflow.actionPlan.ready, true);
@@ -80,6 +115,7 @@ assert.deepEqual(
["json-workflow", "ready"],
["artifact-validation", "ready"],
["browser-diagnostics", "ready"],
["saved-session-diagnostics", "ready"],
["next-command", "none"],
["missing", "none"],
],
@@ -100,6 +136,97 @@ assert.deepEqual(readyActionPlan, {
rows: [],
});
const liveOnlyDiagnosticsArtifact = { ...browserDiagnosticsSessionNestedArtifact };
const liveOnlyDiagnosticsWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
artifactUrl: "/project-release-readiness.json",
diagnosticsUrl: "/real-browser-simulation-live-only-diagnostics.json",
requireSavedSessionDiagnostics: true,
fetchRef: async (url) => ({
ok: true,
status: 200,
text: async () => url === "/real-browser-simulation-live-only-diagnostics.json"
? JSON.stringify(liveOnlyDiagnosticsArtifact)
: readyArtifactJson,
}),
});
assert.equal(liveOnlyDiagnosticsWorkflow.ready, false);
assert.equal(liveOnlyDiagnosticsWorkflow.diagnosticsValidation.ready, true);
assert.equal(liveOnlyDiagnosticsWorkflow.diagnosticsValidation.hasSessionDiagnostics, false);
assert.equal(liveOnlyDiagnosticsWorkflow.missing.includes("diagnostics.virtualHalSessionDiagnostics"), true);
assert.equal(
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(liveOnlyDiagnosticsWorkflow)
.rows.find(({ id }) => id === "saved-session-diagnostics")?.value,
"not provided",
);
const incompleteSourceSessionDiagnosticsArtifact = {
...browserDiagnosticsArtifact,
virtualHalSessionDiagnostics: {
...savedSessionDiagnosticsArtifact,
diagnosticsArtifact: {
...browserDiagnosticsSessionNestedArtifact,
virtualHalSourceCompliance: {
...browserDiagnosticsSessionNestedArtifact.virtualHalSourceCompliance,
complete: false,
},
},
},
};
incompleteSourceSessionDiagnosticsArtifact.virtualHalSessionDiagnostics.validation =
createProjectReleaseBrowserDiagnosticsArtifactValidation(
incompleteSourceSessionDiagnosticsArtifact.virtualHalSessionDiagnostics.diagnosticsArtifact,
);
const incompleteSourceDiagnosticsWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
artifactUrl: "/project-release-readiness.json",
diagnosticsUrl: "/real-browser-simulation-incomplete-source-diagnostics.json",
requireSavedSessionDiagnostics: true,
fetchRef: async (url) => ({
ok: true,
status: 200,
text: async () => url === "/real-browser-simulation-incomplete-source-diagnostics.json"
? JSON.stringify(incompleteSourceSessionDiagnosticsArtifact)
: readyArtifactJson,
}),
});
assert.equal(incompleteSourceDiagnosticsWorkflow.ready, false);
assert.equal(incompleteSourceDiagnosticsWorkflow.diagnosticsValidation.sessionDiagnosticsReady, false);
assert.equal(incompleteSourceDiagnosticsWorkflow.missing.includes("diagnostics.virtualHalSessionDiagnostics"), true);
assert.equal(
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(incompleteSourceDiagnosticsWorkflow)
.rows.find(({ id }) => id === "saved-session-diagnostics")?.value,
"blocked",
);
const missingManifestSessionDiagnosticsArtifact = {
...browserDiagnosticsArtifact,
virtualHalSessionDiagnostics: {
...savedSessionDiagnosticsArtifact,
diagnosticsArtifact: {
...browserDiagnosticsSessionNestedArtifact,
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport(),
},
},
};
missingManifestSessionDiagnosticsArtifact.virtualHalSessionDiagnostics.validation =
createProjectReleaseBrowserDiagnosticsArtifactValidation(
missingManifestSessionDiagnosticsArtifact.virtualHalSessionDiagnostics.diagnosticsArtifact,
);
const missingManifestDiagnosticsWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
artifactUrl: "/project-release-readiness.json",
diagnosticsUrl: "/real-browser-simulation-missing-manifest-diagnostics.json",
requireSavedSessionDiagnostics: true,
fetchRef: async (url) => ({
ok: true,
status: 200,
text: async () => url === "/real-browser-simulation-missing-manifest-diagnostics.json"
? JSON.stringify(missingManifestSessionDiagnosticsArtifact)
: readyArtifactJson,
}),
});
assert.equal(missingManifestDiagnosticsWorkflow.ready, false);
assert.equal(missingManifestDiagnosticsWorkflow.diagnosticsValidation.sessionDiagnosticsReady, false);
assert.equal(missingManifestDiagnosticsWorkflow.missing.includes("diagnostics.virtualHalSessionDiagnostics"), true);
const missingInputWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
artifactUrl: "",
fetchRef: null,

View File

@@ -1,6 +1,8 @@
import assert from "node:assert/strict";
import {
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
createVirtualHalMotionControllerMatrixReport,
createVirtualHalSimConfigSourceCoverageReport,
createProjectReleaseGateActionPlan,
createProjectReleaseGateExecutionManifest,
@@ -19,6 +21,9 @@ import {
const manifest = createProjectReleaseGateManifest();
const virtualHalSimConfigSourceCoverage = createVirtualHalSimConfigSourceCoverageReport();
const virtualHalMotionControllerMatrix = createVirtualHalMotionControllerMatrixReport({
manifestEntries: VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS.flatMap((target) => target.sourceFiles ?? []),
});
assert.equal(manifest.apiName, "project-release-gate-manifest");
assert.equal(manifest.manifestVersion, 1);
@@ -204,6 +209,7 @@ assert.match(partialActionPlan.shellScript, /wasm-port\/tests\/host\/verify_proj
const report = createProjectReleaseReadinessReport({
gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])),
virtualHalSimConfigSourceCoverage,
virtualHalMotionControllerMatrix,
});
assert.deepEqual(report.gateManifest, manifest);
assert.equal(report.gateExecutionManifest.apiName, "project-release-gate-execution-manifest");
@@ -251,6 +257,11 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
label: "Virtual HAL sim-config source coverage",
value: "ready",
},
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
value: "ready",
},
{
id: "blocked-runtime-families",
label: "Blocked runtime families",
@@ -270,12 +281,13 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
});
assert.equal(
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport()).statusLine,
"Waiting: project-release-gate, virtual-hal-sim-config-source-coverage",
"Waiting: project-release-gate, virtual-hal-sim-config-source-coverage, virtual-hal-motion-controller-matrix",
);
assert.equal(
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport({
gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])),
virtualHalSimConfigSourceCoverage,
virtualHalMotionControllerMatrix,
promotedRuntimeFamilies: ["L4-TOOL-DB"],
})).rows.find(({ id }) => id === "blocked-runtime-families")?.value,
"promoted: L4-TOOL-DB",
@@ -318,6 +330,7 @@ assert.deepEqual(
["gate-result-matrix", "ready"],
["gate-action-plan", "ready"],
["virtual-hal-sim-config-source-coverage", "ready"],
["virtual-hal-motion-controller-matrix", "ready"],
["missing", "none"],
],
);
@@ -405,6 +418,7 @@ assert.deepEqual(
["json-workflow", "ready"],
["artifact-validation", "ready"],
["browser-diagnostics", "not requested"],
["saved-session-diagnostics", "not requested"],
["next-command", "none"],
["missing", "none"],
],

View File

@@ -36,6 +36,7 @@ import {
VIRTUAL_HAL_COVERAGE_STATES,
VIRTUAL_HAL_MOTION_CONTROLLER_MATRIX_FIXTURES,
VIRTUAL_HAL_PROJECT_PIN_GROUPS,
VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES,
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES,
VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS,
@@ -89,6 +90,7 @@ import {
createVirtualHalPinInventory,
createVirtualHalPinRegistry,
createVirtualHalProjectReport,
createVirtualHalSimConfigPromotionCandidateReport,
createVirtualHalSimConfigSourceCoverageReport,
createVirtualHalSimulationReplacementReport,
createVirtualHalState,
@@ -239,6 +241,7 @@ const requiredExports = [
["createVirtualHalPinInventory", createVirtualHalPinInventory],
["createVirtualHalPinRegistry", createVirtualHalPinRegistry],
["createVirtualHalProjectReport", createVirtualHalProjectReport],
["createVirtualHalSimConfigPromotionCandidateReport", createVirtualHalSimConfigPromotionCandidateReport],
["createVirtualHalSimConfigSourceCoverageReport", createVirtualHalSimConfigSourceCoverageReport],
["createVirtualHalSimulationReplacementReport", createVirtualHalSimulationReplacementReport],
["createVirtualHalSimulationRuntimeReport", createVirtualHalSimulationRuntimeReport],
@@ -536,6 +539,10 @@ assert.match(sdkIndexText, /\bcreateVirtualHalSimulationReplacementReport\b/);
assert.match(sdkReadmeText, /\bcreateVirtualHalSimulationReplacementReport\b/);
assert.match(sdkIndexText, /\bcreateVirtualHalSimConfigSourceCoverageReport\b/);
assert.match(sdkReadmeText, /\bcreateVirtualHalSimConfigSourceCoverageReport\b/);
assert.match(sdkIndexText, /\bVIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES\b/);
assert.match(sdkReadmeText, /\bVIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES\b/);
assert.match(sdkIndexText, /\bcreateVirtualHalSimConfigPromotionCandidateReport\b/);
assert.match(sdkReadmeText, /\bcreateVirtualHalSimConfigPromotionCandidateReport\b/);
assert.match(sdkIndexText, /\bcreateVirtualHalSourceComplianceReport\b/);
assert.match(sdkReadmeText, /\bcreateVirtualHalSourceComplianceReport\b/);
assert.match(sdkIndexText, /\bcreateVirtualHalSystemCoverageReport\b/);
@@ -668,10 +675,23 @@ assert.equal(motionControllerMatrixReport.simConfigSourceFiles.includes("linuxcn
assert.equal(motionControllerMatrixReport.simConfigSourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini"), true);
assert.equal(motionControllerMatrixReport.simConfigSourceFiles.includes("linuxcnc/configs/sim/axis/vismach/puma/puma.ini"), true);
assert.equal(motionControllerMatrixReport.missingSimConfigTargets.length, 0);
assert.equal(motionControllerMatrixReport.manifestChecked, false);
assert.equal(motionControllerMatrixReport.missingManifestFiles.length, 0);
assert.equal(motionControllerMatrixReport.rows.some(({ id, inPosition }) => id === "xyz-multi-axis-in-position" && inPosition === true), true);
assert.equal(motionControllerMatrixReport.rows.some(({ id, simConfigTargets }) => id === "x-single-axis-servo-dtg" && simConfigTargets.includes("external-offsets")), true);
assert.equal(motionControllerMatrixReport.rows.every(({ axisFeedbackRows }) => axisFeedbackRows.every(({ matched }) => matched)), true);
assert.equal(virtualHalRuntime.getMotionControllerMatrixReport().complete, true);
const manifestCheckedMotionControllerMatrixReport = createVirtualHalMotionControllerMatrixReport({
manifestText: sourceManifestText,
});
assert.equal(manifestCheckedMotionControllerMatrixReport.complete, true);
assert.equal(manifestCheckedMotionControllerMatrixReport.manifestChecked, true);
assert.equal(manifestCheckedMotionControllerMatrixReport.missingManifestFiles.length, 0);
assert.equal(manifestCheckedMotionControllerMatrixReport.rows.every(({ manifestChecked }) => manifestChecked === true), true);
assert.equal(
manifestCheckedMotionControllerMatrixReport.rows.every(({ missingManifestFiles }) => missingManifestFiles.length === 0),
true,
);
const steppedVirtualHal = stepVirtualHalMotion(virtualHalRuntime.getState(), {
target: { x: 3 },
maxVelocity: 10,
@@ -729,6 +749,41 @@ assert.equal(simConfigSourceCoverageReport.sourceFiles.includes("linuxcnc/config
assert.equal(simConfigSourceCoverageReport.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc"), true);
assert.equal(simConfigSourceCoverageReport.rows.every(({ sourceDerived }) => sourceDerived), true);
assert.equal(virtualHalRuntime.getSimConfigSourceCoverageReport({ manifestText: sourceManifestText }).complete, true);
assert.equal(VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES.length >= 8, true);
const simConfigPromotionCandidateReport = createVirtualHalSimConfigPromotionCandidateReport({
manifestText: sourceManifestText,
simConfigSourceCoverage: simConfigSourceCoverageReport,
motionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
});
assert.equal(
simConfigPromotionCandidateReport.apiName,
"linuxcnc-wasm-virtual-hal-sim-config-promotion-candidate-report",
);
assert.equal(simConfigPromotionCandidateReport.complete, true);
assert.equal(simConfigPromotionCandidateReport.webSimulationSatisfied, true);
assert.equal(simConfigPromotionCandidateReport.inventoryBaselineUnchanged, true);
assert.equal(simConfigPromotionCandidateReport.manifestChecked, true);
assert.equal(simConfigPromotionCandidateReport.missingCandidates.length, 0);
assert.equal(simConfigPromotionCandidateReport.blockedCandidateIds.length, 0);
assert.equal(simConfigPromotionCandidateReport.missingManifestFiles.length, 0);
assert.equal(simConfigPromotionCandidateReport.missingRequiredReports.length, 0);
assert.equal(
simConfigPromotionCandidateReport.rows.some((row) =>
row.id === "qtdragon-multi-joint-on-abort" &&
row.complete === true &&
row.currentNodeInventoryStatus === "PASS" &&
row.targetBrowserEvidence === "explicit-browser-diagnostics" &&
row.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini") &&
row.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc")
),
true,
);
assert.equal(simConfigPromotionCandidateReport.rows.every(({ blockedKind }) => blockedKind === "-"), true);
assert.equal(virtualHalRuntime.getSimConfigPromotionCandidateReport({
manifestText: sourceManifestText,
simConfigSourceCoverage: simConfigSourceCoverageReport,
motionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
}).complete, true);
const sourceComplianceReport = createVirtualHalSourceComplianceReport({ halState: virtualHalRuntime.getState() });
assert.equal(sourceComplianceReport.apiName, "linuxcnc-wasm-virtual-hal-source-compliance-report");
assert.equal(sourceComplianceReport.complete, true);
@@ -768,25 +823,47 @@ assert.equal(
restoreVirtualHalStateFromSessionSnapshot(virtualHalSessionSnapshot).hal.nets["x-pos"].includes("axis.x.pos-cmd"),
true,
);
const browserDiagnosticsArtifactValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation({
const browserDiagnosticsArtifact = {
apiName: "real-browser-simulation-diagnostics-artifact",
virtualHal: virtualHalRuntime.getState(),
virtualHalSimulationReplacement: replacementReport,
virtualHalSourceCompliance: sourceComplianceReport,
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
virtualHalSimConfigPromotionCandidates: simConfigPromotionCandidateReport,
virtualHalCommandScriptFixtures: commandScriptFixtureReport,
virtualHalMotionControllerMatrix: motionControllerMatrixReport,
});
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
};
const browserDiagnosticsSessionNestedArtifact = { ...browserDiagnosticsArtifact };
browserDiagnosticsArtifact.virtualHalSessionDiagnostics = {
apiName: "real-browser-simulation-virtual-hal-session-diagnostics",
artifactVersion: 1,
ready: true,
phase: "release-ready",
sessionId: virtualHalSessionSnapshot.sessionId,
machineId: virtualHalSessionSnapshot.payload.machineId,
createdAt: virtualHalSessionSnapshot.createdAt,
metadata: { ...(virtualHalSessionSnapshot.metadata ?? {}) },
files: { ...(virtualHalSessionSnapshot.payload.files ?? {}) },
virtualHalRevision: virtualHalSessionPayload.state.revision,
validation: createProjectReleaseBrowserDiagnosticsArtifactValidation(browserDiagnosticsArtifact),
diagnosticsArtifact: browserDiagnosticsSessionNestedArtifact,
};
const browserDiagnosticsArtifactValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation(browserDiagnosticsArtifact);
assert.equal(browserDiagnosticsArtifactValidation.apiName, "project-release-browser-diagnostics-artifact-validation");
assert.equal(browserDiagnosticsArtifactValidation.ready, true);
assert.equal(browserDiagnosticsArtifactValidation.sourceComplianceReady, true);
assert.equal(browserDiagnosticsArtifactValidation.simConfigSourceCoverageReady, true);
assert.equal(browserDiagnosticsArtifactValidation.promotionCandidatesReady, true);
assert.equal(browserDiagnosticsArtifactValidation.hasPromotionCandidates, true);
assert.equal(browserDiagnosticsArtifactValidation.commandScriptFixturesReady, true);
assert.equal(browserDiagnosticsArtifactValidation.motionControllerMatrixReady, true);
assert.equal(browserDiagnosticsArtifactValidation.hasSessionDiagnostics, true);
assert.equal(browserDiagnosticsArtifactValidation.sessionDiagnosticsReady, true);
assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/src/hal/utils/halcmd_commands.cc"), true);
assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/src/emc/motion/axis.c"), true);
assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini"), true);
assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini"), true);
assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc"), true);
assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/configs/sim/axis/vismach/puma/puma.ini"), true);
const runtimeProjectReport = virtualHalRuntime.getProjectReport({ availableFunctions: VIRTUAL_HAL_WASM_BRIDGE_FUNCTIONS });
assert.equal(runtimeProjectReport.simulationRuntime.replacesHostRuntimeForSimulation, true);
@@ -857,16 +934,20 @@ assert.equal(
capabilities: batchAcceptanceWorkflow.capabilities,
observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"],
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
}).rows.find(({ id }) => id === "checklist")?.value,
"4/4 passed",
"5/5 passed",
);
const batchAcceptanceReport = createProjectBatchAcceptanceReport({
batchId: "sdk-surface-batch",
capabilities: batchAcceptanceWorkflow.capabilities,
observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"],
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
});
assert.equal(batchAcceptanceReport.virtualHalSimConfigSourceCoverage.complete, true);
assert.equal(batchAcceptanceReport.virtualHalMotionControllerMatrix.complete, true);
assert.equal(batchAcceptanceReport.workflow.virtualHalMotionControllerMatrixReady, true);
assert.equal(createProjectBatchAcceptanceReportValidation(batchAcceptanceReport).ready, true);
assert.equal(
createProjectBatchAcceptanceReportValidationSummaryViewModel(
@@ -920,7 +1001,11 @@ assert.equal(
);
assert.equal(releaseReadinessWaiting.apiName, "project-release-readiness-report");
assert.equal(releaseReadinessWaiting.ready, false);
assert.deepEqual(releaseReadinessWaiting.missing, ["project-release-gate", "virtual-hal-sim-config-source-coverage"]);
assert.deepEqual(releaseReadinessWaiting.missing, [
"project-release-gate",
"virtual-hal-sim-config-source-coverage",
"virtual-hal-motion-controller-matrix",
]);
assert.equal(releaseReadinessWaiting.simConfigInventory.unexpectedFail, 0);
assert.deepEqual(releaseReadinessWaiting.promotedBlockedFamilies, []);
assert.equal(releaseReadinessWaiting.gateActionPlan.apiName, "project-release-gate-action-plan");
@@ -933,10 +1018,12 @@ assert.equal(
const releaseReadinessReady = createProjectReleaseReadinessReport({
observedOutputs: ["project_release_gate=ok"],
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
});
assert.equal(releaseReadinessReady.ready, true);
assert.equal(releaseReadinessReady.releaseGate.passed, true);
assert.equal(releaseReadinessReady.virtualHalSimConfigSourceCoverageReady, true);
assert.equal(releaseReadinessReady.virtualHalMotionControllerMatrixReady, true);
assert.deepEqual(releaseReadinessReady.gateManifest, createProjectReleaseGateManifest());
assert.equal(releaseReadinessReady.gateResultMatrix.rows.length, createProjectReleaseGateManifest().gateCount);
assert.equal(createProjectReleaseGateActionPlan({
@@ -953,6 +1040,7 @@ assert.equal(
const releaseReadinessWithScreenshots = createProjectReleaseReadinessReport({
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
axisScreenshotArtifacts: ["desktop-preview", "desktop-dro", "mobile-preview", "mobile-mdi"].map((viewportName) => ({
apiName: "real-browser-simulation-axis-screenshot-artifact",
artifactVersion: 1,
@@ -987,6 +1075,7 @@ assert.equal(
const releaseReadinessArtifact = parseProjectReleaseReadinessArtifactJson(JSON.stringify(createProjectReleaseReadinessReport({
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
})));
assert.equal(releaseReadinessArtifact.apiName, "project-release-readiness-report");
const releaseReadinessArtifactValidation = createProjectReleaseReadinessArtifactValidation(releaseReadinessArtifact);
@@ -1007,6 +1096,7 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
gateResultMatrixReady: true,
gateActionPlanReady: true,
virtualHalSimConfigSourceCoverageReady: true,
virtualHalMotionControllerMatrixReady: true,
axisScreenshotArtifactSummaryReady: true,
axisScreenshotArtifactCount: 0,
blockedRuntimeFamilies: [
@@ -1055,6 +1145,11 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
label: "Virtual HAL sim-config source coverage",
value: "ready",
},
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
value: "ready",
},
{
id: "axis-screenshot-artifacts",
label: "AXIS screenshot artifacts",
@@ -1152,6 +1247,7 @@ assert.deepEqual(
"simConfigInventory.skipped",
"simConfigInventory.unexpectedFail",
"virtualHalSimConfigSourceCoverage",
"virtualHalMotionControllerMatrix",
"blockedRuntimeFamilies",
"gateManifest",
"gateExecutionManifest",

View File

@@ -4,6 +4,7 @@ import { fileURLToPath } from "node:url";
import {
createProjectBatchAcceptanceReport,
createVirtualHalMotionControllerMatrixReport,
createVirtualHalSimConfigSourceCoverageReport,
} from "../../../runtime/sdk/src/index.js";
@@ -29,6 +30,13 @@ const report = createProjectBatchAcceptanceReport({
evidence: "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report",
command: "wasm-port/tests/sdk/node/verify_sdk_surface.sh",
},
{
id: "virtual-hal-motion-controller-matrix",
type: "api",
label: "Virtual HAL motion controller matrix",
evidence: "linuxcnc-wasm-virtual-hal-motion-controller-matrix-report",
command: "wasm-port/tests/sdk/node/verify_sdk_surface.sh",
},
{
id: "project-batch-acceptance-workflow-gate",
type: "gate",
@@ -41,6 +49,9 @@ const report = createProjectBatchAcceptanceReport({
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport({
manifestText: sourceManifestText,
}),
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport({
manifestText: sourceManifestText,
}),
});
mkdirSync(dirname(outputPath), { recursive: true });

View File

@@ -1,18 +1,24 @@
import assert from "node:assert/strict";
import {
createProjectReleaseBrowserDiagnosticsArtifactValidation,
createProjectReleaseGateManifest,
createProjectReleaseReadinessReport,
} from "../../../runtime/sdk/src/project-release-readiness.js";
import {
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
createVirtualHalCommandScriptFixtureReport,
createVirtualHalMotionControllerMatrixReport,
createVirtualHalSimConfigPromotionCandidateReport,
createVirtualHalSimConfigSourceCoverageReport,
createVirtualHalSimulationReplacementReport,
createVirtualHalSourceComplianceReport,
createVirtualHalState,
} from "../../../runtime/sdk/src/linuxcnc-hal.js";
const VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES =
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS.flatMap((target) => target.sourceFiles ?? []);
import {
INI_PANEL_ENTRIES,
controlPageStatusText,
@@ -1356,21 +1362,54 @@ assert.equal(workflowDomMountResult.ready, false);
assert.equal(workflowDomMountResult.renderResult.rowCount, 8);
assert.equal(workflowDomMountResult.renderResult.dataset.handoffScope, "workflow");
assert.deepEqual(createIniPanelShellWorkflowOverviewContract(), shellViewModel.shellWorkflowOverviewContract);
const releaseReadinessMotionControllerMatrix = createVirtualHalMotionControllerMatrixReport({
manifestEntries: VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
});
const releaseReadinessArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
virtualHalMotionControllerMatrix: releaseReadinessMotionControllerMatrix,
}));
const browserDiagnosticsVirtualHalState = createVirtualHalState();
const browserDiagnosticsArtifactJson = JSON.stringify({
const browserDiagnosticsArtifact = {
apiName: "real-browser-simulation-diagnostics-artifact",
artifactVersion: 1,
virtualHal: browserDiagnosticsVirtualHalState,
virtualHalSimulationReplacement: createVirtualHalSimulationReplacementReport(browserDiagnosticsVirtualHalState),
virtualHalSourceCompliance: createVirtualHalSourceComplianceReport({ halState: browserDiagnosticsVirtualHalState }),
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
virtualHalSimConfigPromotionCandidates: createVirtualHalSimConfigPromotionCandidateReport({
manifestEntries: [
...VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/qtdragon_xyza.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/qtdragon_hd_z_compensation.ini",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/on_abort.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/puma_seam_weld.ngc",
"linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
"linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
],
motionControllerMatrix: releaseReadinessMotionControllerMatrix,
}),
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport({ halState: browserDiagnosticsVirtualHalState }),
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport(),
});
virtualHalMotionControllerMatrix: releaseReadinessMotionControllerMatrix,
};
const browserDiagnosticsSessionNestedArtifact = { ...browserDiagnosticsArtifact };
browserDiagnosticsArtifact.virtualHalSessionDiagnostics = {
apiName: "real-browser-simulation-virtual-hal-session-diagnostics",
artifactVersion: 1,
ready: true,
phase: "release-ready",
sessionId: "ini-panel-ui-shell-virtual-hal-session",
machineId: "ini-panel-ui-shell-machine",
createdAt: "2026-06-17T00:00:00.000Z",
metadata: { source: "ini-panel-ui-shell-url-fixture" },
files: {},
virtualHalRevision: browserDiagnosticsVirtualHalState.revision,
validation: createProjectReleaseBrowserDiagnosticsArtifactValidation(browserDiagnosticsSessionNestedArtifact),
diagnosticsArtifact: browserDiagnosticsSessionNestedArtifact,
};
const browserDiagnosticsArtifactJson = JSON.stringify(browserDiagnosticsArtifact);
const releaseArtifactValidation = validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson(
releaseReadinessArtifactJson,
);
@@ -1675,7 +1714,7 @@ const releaseArtifactMountResult = mountIniPanelShellWorkflowOverviewReleaseRead
rowsNode: releaseArtifactRowsNode,
});
assert.equal(releaseArtifactRenderResult.rendered, true);
assert.equal(releaseArtifactRenderResult.rowCount, 10);
assert.equal(releaseArtifactRenderResult.rowCount, 11);
assert.equal(releaseArtifactRenderResult.dataset.handoffScope, "workflow-overview-release-readiness-artifact");
assert.equal(
releaseArtifactRenderResult.rowIds.includes("gate-execution-manifest"),
@@ -1697,8 +1736,12 @@ assert.equal(
releaseArtifactRenderResult.rowIds.includes("virtual-hal-sim-config-source-coverage"),
true,
);
assert.equal(
releaseArtifactRenderResult.rowIds.includes("virtual-hal-motion-controller-matrix"),
true,
);
assert.equal(releaseArtifactMountResult.ready, true);
assert.equal(releaseArtifactMountResult.renderResult.rowCount, 10);
assert.equal(releaseArtifactMountResult.renderResult.rowCount, 11);
assert.deepEqual(
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson("{").missing,
["artifact-json"],
@@ -1745,13 +1788,17 @@ assert.equal(releaseArtifactUrlWorkflow.httpStatus, 200);
assert.equal(releaseArtifactUrlWorkflow.diagnosticsHttpStatus, 200);
assert.equal(releaseArtifactUrlWorkflow.validation.ready, true);
assert.equal(releaseArtifactUrlWorkflow.diagnosticsValidation.ready, true);
assert.equal(releaseArtifactUrlWorkflow.diagnosticsValidation.hasPromotionCandidates, true);
assert.equal(releaseArtifactUrlWorkflow.diagnosticsValidation.promotionCandidatesReady, true);
assert.equal(releaseArtifactUrlWorkflow.diagnosticsValidation.hasSessionDiagnostics, true);
assert.equal(releaseArtifactUrlWorkflow.diagnosticsValidation.sessionDiagnosticsReady, true);
assert.equal(
releaseArtifactUrlWorkflow.validationSummaryViewModel.statusLine,
"Ready: Artifact evidence complete",
);
assert.equal(releaseArtifactUrlWorkflow.renderState.statusLine, "Ready: No blocking reasons");
assert.equal(releaseArtifactUrlWorkflow.mountResult.ready, true);
assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 10);
assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 11);
const releaseArtifactUrlWorkflowSummary =
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
releaseArtifactUrlWorkflow,
@@ -1776,6 +1823,10 @@ assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "browser-diagnostics")?.value,
"ready",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "saved-session-diagnostics")?.value,
"ready",
);
assert.equal(releaseArtifactUrlWorkflow.actionPlan.ready, true);
assert.equal(releaseArtifactUrlWorkflow.actionPlan.nextCommand, null);
assert.equal(
@@ -1841,17 +1892,21 @@ const releaseArtifactUrlWorkflowMountResult =
rowsNode: releaseArtifactUrlWorkflowRowsNode,
});
assert.equal(releaseArtifactUrlWorkflowRenderResult.rendered, true);
assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 11);
assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 12);
assert.equal(
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("browser-diagnostics"),
true,
);
assert.equal(
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("saved-session-diagnostics"),
true,
);
assert.equal(
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("mount-result"),
true,
);
assert.equal(releaseArtifactUrlWorkflowMountResult.ready, true);
assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 11);
assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 12);
const releaseArtifactUrlWorkflowMissingDom = await loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl({
artifactUrl: "/project-release-readiness.json",
fetchRef: async () => ({