解锁L4-TOOL-DB主机运行环境

结论:已识别RIP版LinuxCNC主机运行时,Tool DB native protocol probe可通过,inventory baseline保持28/28/131/0且promotion继续锁定。
This commit is contained in:
2026-06-19 06:28:38 +08:00
parent f101932bf1
commit d9ac3770a7
18 changed files with 3041 additions and 184 deletions

View File

@@ -612,7 +612,7 @@
);
assertEqual(
releaseArtifactUrlWorkflowRenderResult.rowCount,
29,
53,
"workflow overview release artifact URL workflow render rows",
);
assertEqual(
@@ -650,11 +650,46 @@
"13",
"workflow overview release artifact URL workflow evidence expansion count",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promotion-candidate-artifact-rows")?.value,
"28",
"workflow overview release artifact URL workflow promotion candidate artifact rows",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-expansion-artifact-rows")?.value,
"13",
"workflow overview release artifact URL workflow evidence expansion artifact rows",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-expansion-next-evidence")?.value,
"browser-diagnostics-binding",
"workflow overview release artifact URL workflow evidence expansion next evidence",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-ready-candidate-rows")?.value,
"8",
"workflow overview release artifact URL workflow evidence-ready candidate rows",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-ready-candidate-preview")?.value,
"qtdragon-multi-joint-on-abort",
"workflow overview release artifact URL workflow evidence-ready candidate preview",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-ready-candidate-preview-gcode")?.value,
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
"workflow overview release artifact URL workflow evidence-ready candidate preview gcode",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-ready-candidate-promotion-allowed")?.value,
"0",
"workflow overview release artifact URL workflow evidence-ready promotion allowed",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-ready-candidate-baseline-changing")?.value,
"no",
"workflow overview release artifact URL workflow evidence-ready baseline changing",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-expansion-baseline-changing")?.value,
"no",
@@ -750,6 +785,51 @@
"0 promoted",
"workflow overview release artifact URL workflow promoted blocked count",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-locked-rows")?.value,
"19",
"workflow overview release artifact URL workflow locked hard-block rows",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-locked-preview")?.value,
"L4-TOOL-DB:axis/db_demo/base.ngc, L4-PYTHON-REMAP:axis/laser/raster_test.ngc, L4-PYTHON-REMAP:axis/laser/vector_test.ngc",
"workflow overview release artifact URL workflow locked hard-block preview",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-promotion-allowed")?.value,
"0",
"workflow overview release artifact URL workflow hard-block promotion allowed count",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-family-count")?.value,
"3",
"workflow overview release artifact URL workflow hard-block family count",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-family-summary")?.value,
"L4-USER-M-PROCESS:1 locked=yes, L4-TOOL-DB:1 locked=yes, L4-PYTHON-REMAP:17 locked=yes",
"workflow overview release artifact URL workflow hard-block family summary",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-detail-row-count")?.value,
"19",
"workflow overview release artifact URL workflow hard-block detail count",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-detail-preview-1")?.value,
"L4-TOOL-DB:axis/db_demo/base.ngc; reason=design_tooldata_db_protocol_boundary; next=db_program_v2_1_handshake_getall_load_unload_or_put; promotion_allowed=0",
"workflow overview release artifact URL workflow hard-block detail preview",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-family-L4-USER-M-PROCESS")?.value,
"1 locked rows; promotion_allowed=0; first=axis/vismach/millturn/example.ngc; reason=design_m128_m129_linuxcnc_state_boundary; next=kinstype_guard_and_ini_xyz_hal_pin_state",
"workflow overview release artifact URL workflow user-M family row",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-family-L4-USER-M-PROCESS-paths")?.value,
"axis/vismach/millturn/example.ngc",
"workflow overview release artifact URL workflow user-M family paths",
);
assertEqual(
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("saved-session-diagnostics"),
true,
@@ -876,6 +956,21 @@
"0",
"workflow overview fixed URL workflow form candidate allowed row",
);
assertEqual(
workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-value="evidence-ready-candidate-rows"]').textContent,
"8",
"workflow overview fixed URL workflow form evidence-ready candidate rows",
);
assertEqual(
workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-value="evidence-ready-candidate-preview"]').textContent,
"qtdragon-multi-joint-on-abort",
"workflow overview fixed URL workflow form evidence-ready candidate preview",
);
assertEqual(
workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-value="evidence-ready-candidate-promotion-allowed"]').textContent,
"0",
"workflow overview fixed URL workflow form evidence-ready promotion allowed",
);
assertEqual(
workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-value="evidence-expansion-candidates"]').textContent,
"13",
@@ -943,6 +1038,27 @@
16,
"workflow overview promotion candidate drilldown evidence-ready DOM nodes",
);
assertEqual(
workflowDoc.querySelector(
'[data-workflow-overview-promotion-candidate-drilldown-value="candidate-layer"]',
)?.textContent,
"evidence-ready",
"workflow overview promotion candidate drilldown candidate layer row",
);
assertEqual(
workflowDoc.querySelector(
'[data-workflow-overview-promotion-candidate-drilldown-value="inventory-promotion-allowed"]',
)?.textContent,
"0",
"workflow overview promotion candidate drilldown promotion allowed row",
);
assertEqual(
workflowDoc.querySelector(
'[data-workflow-overview-promotion-candidate-drilldown-value="inventory-baseline-changing"]',
)?.textContent,
"no",
"workflow overview promotion candidate drilldown baseline changing row",
);
assertEqual(
workflowDoc.querySelector(
'[data-workflow-overview-promotion-candidate-drilldown-value="evidence-7"]',
@@ -1020,6 +1136,13 @@
4,
"workflow overview source filter changed source DOM nodes",
);
assertEqual(
workflowDoc.querySelectorAll(
'[data-workflow-overview-promotion-family-source-filter-type="candidate-row"]',
).length,
2,
"workflow overview source filter changed candidate DOM nodes",
);
assertEqual(
workflowDoc.querySelector(
'[data-workflow-overview-promotion-family-source-filter-value="linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc"]',
@@ -1027,6 +1150,20 @@
"linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
"workflow overview source filter changed rose engine source row",
);
assertEqual(
workflowDoc.querySelector(
'[data-workflow-overview-promotion-family-source-filter-type="candidate-row"][data-workflow-overview-promotion-family-source-filter-value="linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc"]',
)?.dataset.workflowOverviewPromotionFamilySourceFilterCandidateStatus,
"evidence-ready",
"workflow overview source filter changed rose engine candidate status",
);
assertEqual(
workflowDoc.querySelector(
'[data-workflow-overview-promotion-family-source-filter-type="candidate-row"][data-workflow-overview-promotion-family-source-filter-value="linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc"]',
)?.dataset.workflowOverviewPromotionFamilySourceFilterPromotionAllowed,
"0",
"workflow overview source filter changed rose engine promotion allowed",
);
assertEqual(
releaseArtifactJsonWorkflow.statusLine,
"Ready: Artifact JSON workflow complete",

View File

@@ -656,6 +656,170 @@
]
},
"virtualHalSimConfigPromotionCandidatesReady": true,
"virtualHalSimConfigEvidenceExpansion": {
"apiName": "linuxcnc-wasm-virtual-hal-sim-config-evidence-expansion-report",
"reportVersion": 1,
"source": "linuxcnc-configs-sim-source-derived-virtual-hal",
"phase": "ready",
"complete": true,
"webSimulationSatisfied": true,
"inventoryBaselineUnchanged": true,
"promotionAllowed": false,
"manifestChecked": true,
"candidateCount": 3,
"readyCandidateCount": 3,
"sourceFiles": [
"linuxcnc/configs/sim/woodpecker/woodpecker.ini",
"linuxcnc/configs/sim/woodpecker/on_abort.ngc",
"linuxcnc/configs/sim/woodpecker/tool.tbl",
"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",
"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"
],
"requiredVirtualHalReports": [
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix"
],
"availableVirtualHalReports": [
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix"
],
"missingCandidates": [],
"promotionAllowedViolations": [],
"blockedCandidateIds": [],
"missingManifestFiles": [],
"missingRequiredReports": [],
"rows": [
{
"id": "woodpecker-on-abort",
"label": "Woodpecker on-abort",
"source": "linuxcnc-configs-sim-source-derived-virtual-hal-evidence-expansion-candidate",
"simConfigTarget": "woodpecker",
"sourceFiles": [
"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",
"requiredVirtualHalReports": [
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix"
],
"blockedKind": "-",
"promotionAllowed": false,
"nodeStatusReady": true,
"browserRepresentativeReady": true,
"manifestChecked": true,
"missingManifestFiles": [],
"missingRequiredReports": [],
"evidence": "Current inventory already covers Woodpecker as a non-hard-block representative; next step is explicit browser diagnostics binding only.",
"sourceReady": true,
"complete": true
},
{
"id": "puma-cube",
"label": "PUMA cube",
"source": "linuxcnc-configs-sim-source-derived-virtual-hal-evidence-expansion-candidate",
"simConfigTarget": "vismach-remap-sims",
"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"
],
"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",
"requiredVirtualHalReports": [
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix"
],
"blockedKind": "-",
"promotionAllowed": false,
"nodeStatusReady": true,
"browserRepresentativeReady": true,
"manifestChecked": true,
"missingManifestFiles": [],
"missingRequiredReports": [],
"evidence": "PUMA cube is already the representative browser row for the PUMA machine context; it remains an expansion candidate, not a baseline promotion.",
"sourceReady": true,
"complete": true
},
{
"id": "melfa-example",
"label": "MELFA example",
"source": "linuxcnc-configs-sim-source-derived-virtual-hal-evidence-expansion-candidate",
"simConfigTarget": "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"
],
"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",
"requiredVirtualHalReports": [
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix"
],
"blockedKind": "-",
"promotionAllowed": false,
"nodeStatusReady": true,
"browserRepresentativeReady": true,
"manifestChecked": true,
"missingManifestFiles": [],
"missingRequiredReports": [],
"evidence": "MELFA example has current representative coverage and can receive explicit browser diagnostics evidence without unlocking any hard runtime family.",
"sourceReady": true,
"complete": true
}
],
"summaryRows": [
{
"id": "candidates",
"label": "Expansion candidates",
"value": "3"
},
{
"id": "ready-candidates",
"label": "Ready candidates",
"value": "3"
},
{
"id": "promotion-allowed",
"label": "Promotion allowed",
"value": "0"
},
{
"id": "inventory-baseline",
"label": "Inventory baseline",
"value": "unchanged"
}
]
},
"virtualHalPromotionFamilySummary": "qtdragon-on-abort: 6/6 ready; vismach-remap-sims: 1/1 ready; rose-engine-rcone-demo: 1/1 ready",
"virtualHalPromotionFamilyRows": [
{
@@ -726,7 +890,393 @@
"totalCandidateCount": 28,
"promotionAllowedCount": 0,
"hardBlockRuntimeLock": "locked (3 families)",
"hardBlockRuntimePromotionAllowedCount": 0,
"inventoryBaseline": "executed=28 passed=28 skipped=131 unexpected_fail=0",
"artifactRows": {
"promotionCandidates": 28,
"evidenceExpansion": 13
},
"hardBlockRuntimeFamilyRows": [
{
"candidateId": "axis/db_demo/base.ngc",
"path": "axis/db_demo/base.ngc",
"skipKind": "L4-TOOL-DB",
"promotionAllowed": false,
"blockReason": "design_tooldata_db_protocol_boundary",
"recommendedNextCommand": "db_program_v2_1_handshake_getall_load_unload_or_put",
"detailId": "hard-block-runtime-detail-1"
},
{
"candidateId": "axis/laser/raster_test.ngc",
"path": "axis/laser/raster_test.ngc",
"skipKind": "L4-PYTHON-REMAP",
"promotionAllowed": false,
"blockReason": "design_linuxcnc_python_runtime_boundary",
"recommendedNextCommand": "python_runtime_owner_and_fixture",
"detailId": "hard-block-runtime-detail-2"
},
{
"candidateId": "axis/laser/vector_test.ngc",
"path": "axis/laser/vector_test.ngc",
"skipKind": "L4-PYTHON-REMAP",
"promotionAllowed": false,
"blockReason": "design_linuxcnc_python_runtime_boundary",
"recommendedNextCommand": "python_runtime_owner_and_fixture",
"detailId": "hard-block-runtime-detail-3"
},
{
"candidateId": "axis/laser/vector_test2.ngc",
"path": "axis/laser/vector_test2.ngc",
"skipKind": "L4-PYTHON-REMAP",
"promotionAllowed": false,
"blockReason": "design_linuxcnc_python_runtime_boundary",
"recommendedNextCommand": "python_runtime_owner_and_fixture",
"detailId": "hard-block-runtime-detail-4"
},
{
"candidateId": "axis/remap/cycle/nc_files/examples.ngc",
"path": "axis/remap/cycle/nc_files/examples.ngc",
"skipKind": "L4-PYTHON-REMAP",
"promotionAllowed": false,
"blockReason": "design_linuxcnc_python_runtime_boundary",
"recommendedNextCommand": "python_runtime_owner_and_fixture",
"detailId": "hard-block-runtime-detail-5"
},
{
"candidateId": "axis/remap/extend-builtins/nc_files/examples.ngc",
"path": "axis/remap/extend-builtins/nc_files/examples.ngc",
"skipKind": "L4-PYTHON-REMAP",
"promotionAllowed": false,
"blockReason": "design_linuxcnc_python_runtime_boundary",
"recommendedNextCommand": "python_runtime_owner_and_fixture",
"detailId": "hard-block-runtime-detail-6"
},
{
"candidateId": "axis/remap/getting-started/nc_files/examples.ngc",
"path": "axis/remap/getting-started/nc_files/examples.ngc",
"skipKind": "L4-PYTHON-REMAP",
"promotionAllowed": false,
"blockReason": "design_linuxcnc_python_runtime_boundary",
"recommendedNextCommand": "python_runtime_owner_and_fixture",
"detailId": "hard-block-runtime-detail-7"
},
{
"candidateId": "axis/remap/manual-toolchange-with-tool-length-switch/nc_files/tcdemo.ngc",
"path": "axis/remap/manual-toolchange-with-tool-length-switch/nc_files/tcdemo.ngc",
"skipKind": "L4-PYTHON-REMAP",
"promotionAllowed": false,
"blockReason": "design_linuxcnc_python_runtime_boundary",
"recommendedNextCommand": "python_runtime_owner_and_fixture",
"detailId": "hard-block-runtime-detail-8"
},
{
"candidateId": "axis/remap/rack-toolchange/nc_files/tcdemo.ngc",
"path": "axis/remap/rack-toolchange/nc_files/tcdemo.ngc",
"skipKind": "L4-PYTHON-REMAP",
"promotionAllowed": false,
"blockReason": "design_linuxcnc_python_runtime_boundary",
"recommendedNextCommand": "python_runtime_owner_and_fixture",
"detailId": "hard-block-runtime-detail-9"
},
{
"candidateId": "axis/remap/stop-lookahead/nc_files/examples.ngc",
"path": "axis/remap/stop-lookahead/nc_files/examples.ngc",
"skipKind": "L4-PYTHON-REMAP",
"promotionAllowed": false,
"blockReason": "design_linuxcnc_python_runtime_boundary",
"recommendedNextCommand": "python_runtime_owner_and_fixture",
"detailId": "hard-block-runtime-detail-10"
},
{
"candidateId": "axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/circular_pocket.ngc",
"path": "axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/circular_pocket.ngc",
"skipKind": "L4-PYTHON-REMAP",
"promotionAllowed": false,
"blockReason": "design_linuxcnc_python_runtime_boundary",
"recommendedNextCommand": "python_runtime_owner_and_fixture",
"detailId": "hard-block-runtime-detail-11"
},
{
"candidateId": "axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_back_and_forth.ngc",
"path": "axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_back_and_forth.ngc",
"skipKind": "L4-PYTHON-REMAP",
"promotionAllowed": false,
"blockReason": "design_linuxcnc_python_runtime_boundary",
"recommendedNextCommand": "python_runtime_owner_and_fixture",
"detailId": "hard-block-runtime-detail-12"
},
{
"candidateId": "axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition.ngc",
"path": "axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition.ngc",
"skipKind": "L4-PYTHON-REMAP",
"promotionAllowed": false,
"blockReason": "design_linuxcnc_python_runtime_boundary",
"recommendedNextCommand": "python_runtime_owner_and_fixture",
"detailId": "hard-block-runtime-detail-13"
},
{
"candidateId": "axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/simple_example.ngc",
"path": "axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/simple_example.ngc",
"skipKind": "L4-PYTHON-REMAP",
"promotionAllowed": false,
"blockReason": "design_linuxcnc_python_runtime_boundary",
"recommendedNextCommand": "python_runtime_owner_and_fixture",
"detailId": "hard-block-runtime-detail-14"
},
{
"candidateId": "axis/vismach/millturn/example.ngc",
"path": "axis/vismach/millturn/example.ngc",
"skipKind": "L4-USER-M-PROCESS",
"promotionAllowed": false,
"blockReason": "design_m128_m129_linuxcnc_state_boundary",
"recommendedNextCommand": "kinstype_guard_and_ini_xyz_hal_pin_state",
"detailId": "hard-block-runtime-detail-15"
},
{
"candidateId": "axis/vismach/VMC_toolchange/toolchange.ngc",
"path": "axis/vismach/VMC_toolchange/toolchange.ngc",
"skipKind": "L4-PYTHON-REMAP",
"promotionAllowed": false,
"blockReason": "design_linuxcnc_python_runtime_boundary",
"recommendedNextCommand": "python_runtime_owner_and_fixture",
"detailId": "hard-block-runtime-detail-16"
},
{
"candidateId": "gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/boat-xyzac.ngc",
"path": "gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/boat-xyzac.ngc",
"skipKind": "L4-PYTHON-REMAP",
"promotionAllowed": false,
"blockReason": "design_linuxcnc_python_runtime_boundary",
"recommendedNextCommand": "python_runtime_owner_and_fixture",
"detailId": "hard-block-runtime-detail-17"
},
{
"candidateId": "gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/boat-xyzbc.ngc",
"path": "gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/boat-xyzbc.ngc",
"skipKind": "L4-PYTHON-REMAP",
"promotionAllowed": false,
"blockReason": "design_linuxcnc_python_runtime_boundary",
"recommendedNextCommand": "python_runtime_owner_and_fixture",
"detailId": "hard-block-runtime-detail-18"
},
{
"candidateId": "gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/impeller-7bl-xyzac.ngc",
"path": "gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/impeller-7bl-xyzac.ngc",
"skipKind": "L4-PYTHON-REMAP",
"promotionAllowed": false,
"blockReason": "design_linuxcnc_python_runtime_boundary",
"recommendedNextCommand": "python_runtime_owner_and_fixture",
"detailId": "hard-block-runtime-detail-19"
}
],
"hardBlockRuntimeFamilyDetailRows": [
{
"id": "hard-block-runtime-detail-1",
"family": "L4-TOOL-DB",
"path": "axis/db_demo/base.ngc",
"blockReason": "design_tooldata_db_protocol_boundary",
"nextProof": "db_program_v2_1_handshake_getall_load_unload_or_put",
"promotionAllowed": false
},
{
"id": "hard-block-runtime-detail-2",
"family": "L4-PYTHON-REMAP",
"path": "axis/laser/raster_test.ngc",
"blockReason": "design_linuxcnc_python_runtime_boundary",
"nextProof": "python_runtime_owner_and_fixture",
"promotionAllowed": false
},
{
"id": "hard-block-runtime-detail-3",
"family": "L4-PYTHON-REMAP",
"path": "axis/laser/vector_test.ngc",
"blockReason": "design_linuxcnc_python_runtime_boundary",
"nextProof": "python_runtime_owner_and_fixture",
"promotionAllowed": false
},
{
"id": "hard-block-runtime-detail-4",
"family": "L4-PYTHON-REMAP",
"path": "axis/laser/vector_test2.ngc",
"blockReason": "design_linuxcnc_python_runtime_boundary",
"nextProof": "python_runtime_owner_and_fixture",
"promotionAllowed": false
},
{
"id": "hard-block-runtime-detail-5",
"family": "L4-PYTHON-REMAP",
"path": "axis/remap/cycle/nc_files/examples.ngc",
"blockReason": "design_linuxcnc_python_runtime_boundary",
"nextProof": "python_runtime_owner_and_fixture",
"promotionAllowed": false
},
{
"id": "hard-block-runtime-detail-6",
"family": "L4-PYTHON-REMAP",
"path": "axis/remap/extend-builtins/nc_files/examples.ngc",
"blockReason": "design_linuxcnc_python_runtime_boundary",
"nextProof": "python_runtime_owner_and_fixture",
"promotionAllowed": false
},
{
"id": "hard-block-runtime-detail-7",
"family": "L4-PYTHON-REMAP",
"path": "axis/remap/getting-started/nc_files/examples.ngc",
"blockReason": "design_linuxcnc_python_runtime_boundary",
"nextProof": "python_runtime_owner_and_fixture",
"promotionAllowed": false
},
{
"id": "hard-block-runtime-detail-8",
"family": "L4-PYTHON-REMAP",
"path": "axis/remap/manual-toolchange-with-tool-length-switch/nc_files/tcdemo.ngc",
"blockReason": "design_linuxcnc_python_runtime_boundary",
"nextProof": "python_runtime_owner_and_fixture",
"promotionAllowed": false
},
{
"id": "hard-block-runtime-detail-9",
"family": "L4-PYTHON-REMAP",
"path": "axis/remap/rack-toolchange/nc_files/tcdemo.ngc",
"blockReason": "design_linuxcnc_python_runtime_boundary",
"nextProof": "python_runtime_owner_and_fixture",
"promotionAllowed": false
},
{
"id": "hard-block-runtime-detail-10",
"family": "L4-PYTHON-REMAP",
"path": "axis/remap/stop-lookahead/nc_files/examples.ngc",
"blockReason": "design_linuxcnc_python_runtime_boundary",
"nextProof": "python_runtime_owner_and_fixture",
"promotionAllowed": false
},
{
"id": "hard-block-runtime-detail-11",
"family": "L4-PYTHON-REMAP",
"path": "axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/circular_pocket.ngc",
"blockReason": "design_linuxcnc_python_runtime_boundary",
"nextProof": "python_runtime_owner_and_fixture",
"promotionAllowed": false
},
{
"id": "hard-block-runtime-detail-12",
"family": "L4-PYTHON-REMAP",
"path": "axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_back_and_forth.ngc",
"blockReason": "design_linuxcnc_python_runtime_boundary",
"nextProof": "python_runtime_owner_and_fixture",
"promotionAllowed": false
},
{
"id": "hard-block-runtime-detail-13",
"family": "L4-PYTHON-REMAP",
"path": "axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition.ngc",
"blockReason": "design_linuxcnc_python_runtime_boundary",
"nextProof": "python_runtime_owner_and_fixture",
"promotionAllowed": false
},
{
"id": "hard-block-runtime-detail-14",
"family": "L4-PYTHON-REMAP",
"path": "axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/simple_example.ngc",
"blockReason": "design_linuxcnc_python_runtime_boundary",
"nextProof": "python_runtime_owner_and_fixture",
"promotionAllowed": false
},
{
"id": "hard-block-runtime-detail-15",
"family": "L4-USER-M-PROCESS",
"path": "axis/vismach/millturn/example.ngc",
"blockReason": "design_m128_m129_linuxcnc_state_boundary",
"nextProof": "kinstype_guard_and_ini_xyz_hal_pin_state",
"promotionAllowed": false
},
{
"id": "hard-block-runtime-detail-16",
"family": "L4-PYTHON-REMAP",
"path": "axis/vismach/VMC_toolchange/toolchange.ngc",
"blockReason": "design_linuxcnc_python_runtime_boundary",
"nextProof": "python_runtime_owner_and_fixture",
"promotionAllowed": false
},
{
"id": "hard-block-runtime-detail-17",
"family": "L4-PYTHON-REMAP",
"path": "gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/boat-xyzac.ngc",
"blockReason": "design_linuxcnc_python_runtime_boundary",
"nextProof": "python_runtime_owner_and_fixture",
"promotionAllowed": false
},
{
"id": "hard-block-runtime-detail-18",
"family": "L4-PYTHON-REMAP",
"path": "gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/boat-xyzbc.ngc",
"blockReason": "design_linuxcnc_python_runtime_boundary",
"nextProof": "python_runtime_owner_and_fixture",
"promotionAllowed": false
},
{
"id": "hard-block-runtime-detail-19",
"family": "L4-PYTHON-REMAP",
"path": "gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/impeller-7bl-xyzac.ngc",
"blockReason": "design_linuxcnc_python_runtime_boundary",
"nextProof": "python_runtime_owner_and_fixture",
"promotionAllowed": false
}
],
"hardBlockRuntimeFamilySummaryRows": [
{
"family": "L4-USER-M-PROCESS",
"candidateCount": 1,
"promotionAllowedCount": 0,
"locked": true,
"lockedPaths": [
"axis/vismach/millturn/example.ngc"
],
"firstPath": "axis/vismach/millturn/example.ngc",
"firstBlockReason": "design_m128_m129_linuxcnc_state_boundary",
"nextProof": "kinstype_guard_and_ini_xyz_hal_pin_state"
},
{
"family": "L4-TOOL-DB",
"candidateCount": 1,
"promotionAllowedCount": 0,
"locked": true,
"lockedPaths": [
"axis/db_demo/base.ngc"
],
"firstPath": "axis/db_demo/base.ngc",
"firstBlockReason": "design_tooldata_db_protocol_boundary",
"nextProof": "db_program_v2_1_handshake_getall_load_unload_or_put"
},
{
"family": "L4-PYTHON-REMAP",
"candidateCount": 17,
"promotionAllowedCount": 0,
"locked": true,
"lockedPaths": [
"axis/laser/raster_test.ngc",
"axis/laser/vector_test.ngc",
"axis/laser/vector_test2.ngc",
"axis/remap/cycle/nc_files/examples.ngc",
"axis/remap/extend-builtins/nc_files/examples.ngc",
"axis/remap/getting-started/nc_files/examples.ngc",
"axis/remap/manual-toolchange-with-tool-length-switch/nc_files/tcdemo.ngc",
"axis/remap/rack-toolchange/nc_files/tcdemo.ngc",
"axis/remap/stop-lookahead/nc_files/examples.ngc",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/circular_pocket.ngc",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_back_and_forth.ngc",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition.ngc",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/simple_example.ngc",
"axis/vismach/VMC_toolchange/toolchange.ngc",
"gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/boat-xyzac.ngc",
"gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/boat-xyzbc.ngc",
"gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/impeller-7bl-xyzac.ngc"
],
"firstPath": "axis/laser/raster_test.ngc",
"firstBlockReason": "design_linuxcnc_python_runtime_boundary",
"nextProof": "python_runtime_owner_and_fixture"
}
],
"layers": [
{
"id": "evidence-ready",
@@ -746,6 +1296,7 @@
"evidenceExpansion": {
"id": "browser-diagnostics-expansion",
"candidateCount": 13,
"artifactRowCount": 13,
"promotionAllowedCount": 0,
"baselineChanging": false,
"nextEvidence": "browser-diagnostics-binding"
@@ -2332,6 +2883,16 @@
"label": "Promotion candidate artifact",
"value": "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv"
},
{
"id": "promotion-candidate-artifact-rows",
"label": "Promotion candidate artifact rows",
"value": "28"
},
{
"id": "evidence-expansion-artifact-rows",
"label": "Evidence expansion artifact rows",
"value": "13"
},
{
"id": "promotion-candidate-layers",
"label": "Promotion candidate layers",
@@ -2347,6 +2908,16 @@
"label": "Evidence expansion candidates",
"value": "13"
},
{
"id": "evidence-expansion-next-evidence",
"label": "Evidence expansion next evidence",
"value": "browser-diagnostics-binding"
},
{
"id": "evidence-expansion-baseline-changing",
"label": "Evidence expansion baseline changing",
"value": "no"
},
{
"id": "evidence-expansion-artifact",
"label": "Evidence expansion artifact",

View File

@@ -189,6 +189,11 @@ 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.hardBlockRuntimePromotionAllowedCount, 0);
assert.deepEqual(artifact.promotionCandidateArtifactSummary.artifactRows, {
promotionCandidates: 28,
evidenceExpansion: 13,
});
assert.equal(artifact.promotionCandidateArtifactSummary.evidenceExpansion.promotionAllowedCount, 0);
assert.equal(
artifact.promotionCandidateArtifactSummary.inventoryBaseline,

View File

@@ -4,6 +4,7 @@ import { fileURLToPath } from "node:url";
import {
createProjectReleaseReadinessReport,
createVirtualHalSimConfigEvidenceExpansionReport,
createVirtualHalMotionControllerMatrixReport,
createVirtualHalSimConfigMacroLoadFixtureReport,
createVirtualHalSimConfigPromotionCandidateReport,
@@ -17,6 +18,8 @@ const sourceManifestText = readFileSync(resolve(root, "tools/source-manifest.txt
const axisScreenshotArtifactDir = process.env.AXIS_SCREENSHOT_ARTIFACT_DIR
? resolve(process.env.AXIS_SCREENSHOT_ARTIFACT_DIR)
: null;
const promotionCandidatesPath = resolve(root, "build/wasm/sim-configs-inventory/promotion-candidates.tsv");
const evidenceExpansionCandidatesPath = resolve(root, "build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv");
const passedGateResults = {
"diff-check": true,
@@ -59,6 +62,23 @@ function loadAxisScreenshotArtifacts(directory) {
});
}
function parseTsvArtifactRows(path) {
if (!existsSync(path)) {
return [];
}
const [headerLine, ...dataLines] = readFileSync(path, "utf8").trim().split(/\r?\n/);
if (!headerLine) {
return [];
}
const headers = headerLine.split("\t");
return dataLines
.filter((line) => line.trim().length > 0)
.map((line) => {
const values = line.split("\t");
return Object.fromEntries(headers.map((header, index) => [header, values[index] ?? ""]));
});
}
const report = createProjectReleaseReadinessReport({
gateResults: passedGateResults,
observedOutputs: [
@@ -80,6 +100,13 @@ const report = createProjectReleaseReadinessReport({
simConfigSourceCoverage: virtualHalSimConfigSourceCoverage,
motionControllerMatrix: virtualHalMotionControllerMatrix,
}),
virtualHalSimConfigEvidenceExpansion: createVirtualHalSimConfigEvidenceExpansionReport({
manifestText: sourceManifestText,
simConfigSourceCoverage: virtualHalSimConfigSourceCoverage,
motionControllerMatrix: virtualHalMotionControllerMatrix,
}),
promotionCandidateRows: parseTsvArtifactRows(promotionCandidatesPath),
evidenceExpansionRows: parseTsvArtifactRows(evidenceExpansionCandidatesPath),
virtualHalSimConfigMacroLoadFixtures: createVirtualHalSimConfigMacroLoadFixtureReport({
manifestText: sourceManifestText,
}),

17
wasm-port/tests/native/probe_millturn_user_m_runtime.sh Normal file → Executable file
View File

@@ -22,7 +22,22 @@ cleanup() {
trap cleanup EXIT
command_path() {
command -v "$1" 2>/dev/null || true
local command_name="$1"
local path
path="$(command -v "$command_name" 2>/dev/null || true)"
if [[ -n "$path" ]]; then
printf '%s\n' "$path"
return
fi
case "$command_name" in
linuxcnc|halrun)
[[ -x "$LINUXCNC_ROOT/scripts/$command_name" ]] && printf '%s\n' "$LINUXCNC_ROOT/scripts/$command_name"
;;
halcmd)
[[ -x "$LINUXCNC_ROOT/bin/$command_name" ]] && printf '%s\n' "$LINUXCNC_ROOT/bin/$command_name"
;;
esac
}
csv_join() {

View File

@@ -14,7 +14,19 @@ required_commands=(python3 linuxcnc)
missing_requirements=()
command_path() {
command -v "$1" 2>/dev/null || true
local command_name="$1"
local path
path="$(command -v "$command_name" 2>/dev/null || true)"
if [[ -n "$path" ]]; then
printf '%s\n' "$path"
return
fi
case "$command_name" in
linuxcnc)
[[ -x "$LINUXCNC_ROOT/scripts/linuxcnc" ]] && printf '%s\n' "$LINUXCNC_ROOT/scripts/linuxcnc"
;;
esac
}
csv_join() {

61
wasm-port/tests/native/probe_tool_db_runtime.sh Normal file → Executable file
View File

@@ -14,7 +14,22 @@ required_commands=(python3 linuxcnc milltask halcmd)
missing_requirements=()
command_path() {
command -v "$1" 2>/dev/null || true
local command_name="$1"
local path
path="$(command -v "$command_name" 2>/dev/null || true)"
if [[ -n "$path" ]]; then
printf '%s\n' "$path"
return
fi
case "$command_name" in
linuxcnc)
[[ -x "$LINUXCNC_ROOT/scripts/linuxcnc" ]] && printf '%s\n' "$LINUXCNC_ROOT/scripts/linuxcnc"
;;
milltask|halcmd)
[[ -x "$LINUXCNC_ROOT/bin/$command_name" ]] && printf '%s\n' "$LINUXCNC_ROOT/bin/$command_name"
;;
esac
}
csv_join() {
@@ -108,7 +123,11 @@ import time
db_program, db_demo_dir, python_path, db_savefile, stdout_log, stderr_log = sys.argv[1:]
env = os.environ.copy()
linuxcnc_root = os.path.dirname(os.path.dirname(python_path))
env["PYTHONPATH"] = python_path + (os.pathsep + env["PYTHONPATH"] if env.get("PYTHONPATH") else "")
env["LD_LIBRARY_PATH"] = os.path.join(linuxcnc_root, "lib") + (
os.pathsep + env["LD_LIBRARY_PATH"] if env.get("LD_LIBRARY_PATH") else ""
)
stdout_file = open(stdout_log, "w", encoding="utf-8")
stderr_file = open(stderr_log, "w", encoding="utf-8")
@@ -119,31 +138,41 @@ proc = subprocess.Popen(
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=stderr_file,
text=True,
bufsize=1,
bufsize=0,
)
stdout_buffer = b""
line_queue = []
def emit(name, value):
print(f"{name}={value}")
def read_line(timeout=5.0):
global stdout_buffer
if line_queue:
return line_queue.pop(0)
deadline = time.monotonic() + timeout
while time.monotonic() < deadline:
ready, _, _ = select.select([proc.stdout], [], [], 0.1)
if ready:
line = proc.stdout.readline()
if line == "":
chunk = os.read(proc.stdout.fileno(), 4096)
if chunk == b"":
raise RuntimeError("db_program_stdout_closed")
line = line.rstrip("\n")
stdout_file.write(line + "\n")
stdout_file.flush()
return line
stdout_buffer += chunk
while b"\n" in stdout_buffer:
raw_line, stdout_buffer = stdout_buffer.split(b"\n", 1)
line = raw_line.decode("utf-8", errors="replace").rstrip("\r")
stdout_file.write(line + "\n")
stdout_file.flush()
line_queue.append(line)
if line_queue:
return line_queue.pop(0)
if proc.poll() is not None:
raise RuntimeError(f"db_program_exited_{proc.returncode}")
raise RuntimeError("timeout_waiting_for_db_program_reply")
def send(command):
proc.stdin.write(command + "\n")
proc.stdin.write((command + "\n").encode("utf-8"))
proc.stdin.flush()
def read_until_fini(command, timeout=5.0):
@@ -157,6 +186,10 @@ def read_until_fini(command, timeout=5.0):
return lines
raise RuntimeError(f"timeout_waiting_for_fini_{command.split()[0]}")
def read_single_reply(command, timeout=5.0):
send(command)
return read_line(timeout)
def require(condition, message):
if not condition:
raise RuntimeError(message)
@@ -187,14 +220,14 @@ try:
load_reply = read_until_fini("l t14 p0")
emit("tool_db_load_spindle_fini", int(load_reply[-1].startswith("FINI")))
t14_loaded = read_until_fini("t 14")
require(any(line.startswith("T14 ") and "P0" in line for line in t14_loaded), "load_spindle_state_drift")
t14_loaded = read_single_reply("t 14")
require(t14_loaded.startswith("T14 ") and "P0" in t14_loaded, "load_spindle_state_drift")
emit("tool_db_load_spindle_state_ok", 1)
unload_reply = read_until_fini("u t0 p0")
emit("tool_db_unload_spindle_fini", int(unload_reply[-1].startswith("FINI")))
t14_unloaded = read_until_fini("t 14")
require(any(line.startswith("T14 ") and "P114" in line for line in t14_unloaded), "unload_spindle_state_drift")
t14_unloaded = read_single_reply("t 14")
require(t14_unloaded.startswith("T14 ") and "P114" in t14_unloaded, "unload_spindle_state_drift")
emit("tool_db_unload_spindle_state_ok", 1)
require(os.path.exists(db_savefile), "db_savefile_missing")

View File

@@ -24,6 +24,23 @@ import {
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = resolve(__dirname, "../../..");
const sourceManifestText = readFileSync(resolve(root, "tools/source-manifest.txt"), "utf8");
function parseTsvRows(text) {
const [headerLine, ...dataLines] = text.trim().split(/\r?\n/);
const headers = headerLine.split("\t");
return dataLines
.filter((line) => line.trim().length > 0)
.map((line) => {
const values = line.split("\t");
return Object.fromEntries(headers.map((header, index) => [header, values[index] ?? ""]));
});
}
const promotionCandidateRows = parseTsvRows(
readFileSync(resolve(root, "build/wasm/sim-configs-inventory/promotion-candidates.tsv"), "utf8"),
);
const evidenceExpansionRows = parseTsvRows(
readFileSync(resolve(root, "build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv"), "utf8"),
);
const manifest = createProjectReleaseGateManifest();
const virtualHalSimConfigSourceCoverage = createVirtualHalSimConfigSourceCoverageReport();
const virtualHalMotionControllerMatrix = createVirtualHalMotionControllerMatrixReport({ manifestText: sourceManifestText });
@@ -92,6 +109,8 @@ const readyArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({
virtualHalSimConfigSourceCoverage,
virtualHalSimConfigPromotionCandidates,
virtualHalSimConfigEvidenceExpansion,
promotionCandidateRows,
evidenceExpansionRows,
virtualHalSimConfigMacroLoadFixtures,
virtualHalMotionControllerMatrix: browserDiagnosticsArtifact.virtualHalMotionControllerMatrix,
}));
@@ -198,10 +217,38 @@ assert.equal(
readySummary.rows.find(({ id }) => id === "promotion-candidate-total")?.value,
"28",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "evidence-ready-candidate-rows")?.value,
"8",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "evidence-ready-candidate-preview")?.value,
"qtdragon-multi-joint-on-abort",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "evidence-ready-candidate-preview-gcode")?.value,
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "evidence-ready-candidate-promotion-allowed")?.value,
"0",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "evidence-ready-candidate-baseline-changing")?.value,
"no",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "evidence-expansion-candidates")?.value,
"13",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "promotion-candidate-artifact-rows")?.value,
"28",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "evidence-expansion-artifact-rows")?.value,
"13",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "evidence-expansion-next-evidence")?.value,
"browser-diagnostics-binding",
@@ -242,6 +289,42 @@ assert.equal(
readySummary.rows.find(({ id }) => id === "promotion-candidate-allowed")?.value,
"0",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "hard-block-runtime-locked-rows")?.value,
"19",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "hard-block-runtime-locked-preview")?.value,
"L4-TOOL-DB:axis/db_demo/base.ngc, L4-PYTHON-REMAP:axis/laser/raster_test.ngc, L4-PYTHON-REMAP:axis/laser/vector_test.ngc",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "hard-block-runtime-promotion-allowed")?.value,
"0",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "hard-block-runtime-family-count")?.value,
"3",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "hard-block-runtime-family-summary")?.value,
"L4-USER-M-PROCESS:1 locked=yes, L4-TOOL-DB:1 locked=yes, L4-PYTHON-REMAP:17 locked=yes",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "hard-block-runtime-detail-row-count")?.value,
"19",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "hard-block-runtime-detail-preview-1")?.value,
"L4-TOOL-DB:axis/db_demo/base.ngc; reason=design_tooldata_db_protocol_boundary; next=db_program_v2_1_handshake_getall_load_unload_or_put; promotion_allowed=0",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "hard-block-runtime-family-L4-TOOL-DB")?.value,
"1 locked rows; promotion_allowed=0; first=axis/db_demo/base.ngc; reason=design_tooldata_db_protocol_boundary; next=db_program_v2_1_handshake_getall_load_unload_or_put",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "hard-block-runtime-family-L4-TOOL-DB-paths")?.value,
"axis/db_demo/base.ngc",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "hard-block-runtime-lock")?.value,
"locked (3 families)",
@@ -270,7 +353,14 @@ assert.deepEqual(
["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-ready-candidate-rows", "8"],
["evidence-ready-candidate-preview", "qtdragon-multi-joint-on-abort"],
["evidence-ready-candidate-preview-gcode", "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc"],
["evidence-ready-candidate-promotion-allowed", "0"],
["evidence-ready-candidate-baseline-changing", "no"],
["promotion-candidate-artifact-rows", "28"],
["evidence-expansion-candidates", "13"],
["evidence-expansion-artifact-rows", "13"],
["evidence-expansion-next-evidence", "browser-diagnostics-binding"],
["evidence-expansion-baseline-changing", "no"],
["evidence-expansion-preferred-candidate", "woodpecker-on-abort"],
@@ -281,6 +371,23 @@ assert.deepEqual(
["evidence-expansion-source-count-list", "3, 3, 3"],
["evidence-expansion-artifact", "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv"],
["promotion-candidate-allowed", "0"],
["hard-block-runtime-locked-rows", "19"],
["hard-block-runtime-locked-preview", "L4-TOOL-DB:axis/db_demo/base.ngc, L4-PYTHON-REMAP:axis/laser/raster_test.ngc, L4-PYTHON-REMAP:axis/laser/vector_test.ngc"],
["hard-block-runtime-promotion-allowed", "0"],
["hard-block-runtime-family-count", "3"],
["hard-block-runtime-family-summary", "L4-USER-M-PROCESS:1 locked=yes, L4-TOOL-DB:1 locked=yes, L4-PYTHON-REMAP:17 locked=yes"],
["hard-block-runtime-detail-row-count", "19"],
["hard-block-runtime-detail-preview-1", "L4-TOOL-DB:axis/db_demo/base.ngc; reason=design_tooldata_db_protocol_boundary; next=db_program_v2_1_handshake_getall_load_unload_or_put; promotion_allowed=0"],
["hard-block-runtime-detail-preview-2", "L4-PYTHON-REMAP:axis/laser/raster_test.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0"],
["hard-block-runtime-detail-preview-3", "L4-PYTHON-REMAP:axis/laser/vector_test.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0"],
["hard-block-runtime-detail-preview-4", "L4-PYTHON-REMAP:axis/laser/vector_test2.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0"],
["hard-block-runtime-detail-preview-5", "L4-PYTHON-REMAP:axis/remap/cycle/nc_files/examples.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0"],
["hard-block-runtime-family-L4-USER-M-PROCESS", "1 locked rows; promotion_allowed=0; first=axis/vismach/millturn/example.ngc; reason=design_m128_m129_linuxcnc_state_boundary; next=kinstype_guard_and_ini_xyz_hal_pin_state"],
["hard-block-runtime-family-L4-TOOL-DB", "1 locked rows; promotion_allowed=0; first=axis/db_demo/base.ngc; reason=design_tooldata_db_protocol_boundary; next=db_program_v2_1_handshake_getall_load_unload_or_put"],
["hard-block-runtime-family-L4-PYTHON-REMAP", "17 locked rows; promotion_allowed=0; first=axis/laser/raster_test.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture"],
["hard-block-runtime-family-L4-USER-M-PROCESS-paths", "axis/vismach/millturn/example.ngc"],
["hard-block-runtime-family-L4-TOOL-DB-paths", "axis/db_demo/base.ngc"],
["hard-block-runtime-family-L4-PYTHON-REMAP-paths", "axis/laser/raster_test.ngc, axis/laser/vector_test.ngc, axis/laser/vector_test2.ngc, axis/remap/cycle/nc_files/examples.ngc, axis/remap/extend-builtins/nc_files/examples.ngc, axis/remap/getting-started/nc_files/examples.ngc, axis/remap/manual-toolchange-with-tool-length-switch/nc_files/tcdemo.ngc, axis/remap/rack-toolchange/nc_files/tcdemo.ngc, axis/remap/stop-lookahead/nc_files/examples.ngc, axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/circular_pocket.ngc, axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_back_and_forth.ngc, axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition.ngc, axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/simple_example.ngc, axis/vismach/VMC_toolchange/toolchange.ngc, gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/boat-xyzac.ngc, gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/boat-xyzbc.ngc, gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/impeller-7bl-xyzac.ngc"],
...expectedPromotionFamilyRows,
["browser-diagnostics", "ready"],
["virtual-hal-promotion-candidate-summary", "ready"],

View File

@@ -397,7 +397,9 @@ assert.deepEqual(
["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"],
["promotion-candidate-artifact-rows", "28"],
["evidence-expansion-candidates", "13"],
["evidence-expansion-artifact-rows", "13"],
["evidence-expansion-next-evidence", "browser-diagnostics-binding"],
["evidence-expansion-baseline-changing", "no"],
["evidence-expansion-artifact", "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv"],
@@ -496,7 +498,14 @@ assert.deepEqual(
["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-ready-candidate-rows", "0"],
["evidence-ready-candidate-preview", "not provided"],
["evidence-ready-candidate-preview-gcode", "not provided"],
["evidence-ready-candidate-promotion-allowed", "0"],
["evidence-ready-candidate-baseline-changing", "no"],
["promotion-candidate-artifact-rows", "28"],
["evidence-expansion-candidates", "13"],
["evidence-expansion-artifact-rows", "13"],
["evidence-expansion-next-evidence", "browser-diagnostics-binding"],
["evidence-expansion-baseline-changing", "no"],
["evidence-expansion-preferred-candidate", "not provided"],
@@ -507,6 +516,18 @@ assert.deepEqual(
["evidence-expansion-source-count-list", "not provided"],
["evidence-expansion-artifact", "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv"],
["promotion-candidate-allowed", "0"],
["hard-block-runtime-locked-rows", "0"],
["hard-block-runtime-locked-preview", "not provided"],
["hard-block-runtime-promotion-allowed", "0"],
["hard-block-runtime-family-count", "3"],
["hard-block-runtime-family-summary", "L4-USER-M-PROCESS:0 locked=yes, L4-TOOL-DB:0 locked=yes, L4-PYTHON-REMAP:0 locked=yes"],
["hard-block-runtime-detail-row-count", "0"],
["hard-block-runtime-family-L4-USER-M-PROCESS", "0 locked rows; promotion_allowed=0; first=not provided; reason=not provided; next=not provided"],
["hard-block-runtime-family-L4-TOOL-DB", "0 locked rows; promotion_allowed=0; first=not provided; reason=not provided; next=not provided"],
["hard-block-runtime-family-L4-PYTHON-REMAP", "0 locked rows; promotion_allowed=0; first=not provided; reason=not provided; next=not provided"],
["hard-block-runtime-family-L4-USER-M-PROCESS-paths", "not provided"],
["hard-block-runtime-family-L4-TOOL-DB-paths", "not provided"],
["hard-block-runtime-family-L4-PYTHON-REMAP-paths", "not provided"],
...expectedPromotionFamilyRows,
["browser-diagnostics", "not requested"],
["virtual-hal-promotion-candidate-summary", "not requested"],

View File

@@ -183,6 +183,24 @@ const sdkIndexText = readFileSync(resolve(root, "runtime/sdk/src/index.js"), "ut
const sdkReadmeText = readFileSync(resolve(root, "runtime/sdk/README.md"), "utf8");
const sourceManifestText = readFileSync(resolve(root, "tools/source-manifest.txt"), "utf8");
const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"), "utf8");
function parseTsvRows(text) {
const [headerLine, ...dataLines] = text.trim().split(/\r?\n/);
const headers = headerLine.split("\t");
return dataLines
.filter((line) => line.trim().length > 0)
.map((line) => {
const values = line.split("\t");
return Object.fromEntries(headers.map((header, index) => [header, values[index] ?? ""]));
});
}
const promotionCandidateArtifactRows = parseTsvRows(
readFileSync(resolve(root, "build/wasm/sim-configs-inventory/promotion-candidates.tsv"), "utf8"),
);
const evidenceExpansionArtifactRows = parseTsvRows(
readFileSync(resolve(root, "build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv"), "utf8"),
);
const expectedPromotionFamilySummary =
"qtdragon-on-abort: 6/6 ready; vismach-remap-sims: 1/1 ready; rose-engine-rcone-demo: 1/1 ready";
const expectedPromotionFamilyRows = [
@@ -204,7 +222,46 @@ const expectedPromotionCandidateArtifactSummary = {
totalCandidateCount: 28,
promotionAllowedCount: 0,
hardBlockRuntimeLock: "locked (3 families)",
hardBlockRuntimePromotionAllowedCount: 0,
inventoryBaseline: "executed=28 passed=28 skipped=131 unexpected_fail=0",
artifactRows: {
promotionCandidates: 28,
evidenceExpansion: 13,
},
hardBlockRuntimeFamilyRows: [],
hardBlockRuntimeFamilyDetailRows: [],
hardBlockRuntimeFamilySummaryRows: [
{
family: "L4-USER-M-PROCESS",
candidateCount: 0,
promotionAllowedCount: 0,
locked: true,
lockedPaths: [],
firstPath: null,
firstBlockReason: null,
nextProof: null,
},
{
family: "L4-TOOL-DB",
candidateCount: 0,
promotionAllowedCount: 0,
locked: true,
lockedPaths: [],
firstPath: null,
firstBlockReason: null,
nextProof: null,
},
{
family: "L4-PYTHON-REMAP",
candidateCount: 0,
promotionAllowedCount: 0,
locked: true,
lockedPaths: [],
firstPath: null,
firstBlockReason: null,
nextProof: null,
},
],
layers: [
{
id: "evidence-ready",
@@ -224,6 +281,7 @@ const expectedPromotionCandidateArtifactSummary = {
evidenceExpansion: {
id: "browser-diagnostics-expansion",
candidateCount: 13,
artifactRowCount: 13,
promotionAllowedCount: 0,
baselineChanging: false,
nextEvidence: "browser-diagnostics-binding",
@@ -1571,6 +1629,14 @@ assert.deepEqual(
releaseReadinessReady.promotionCandidateArtifactSummary,
expectedPromotionCandidateArtifactSummary,
);
assert.equal(
releaseReadinessReady.rows.find(({ id }) => id === "promotion-candidate-artifact-rows")?.value,
"28",
);
assert.equal(
releaseReadinessReady.rows.find(({ id }) => id === "evidence-expansion-artifact-rows")?.value,
"13",
);
assert.deepEqual(
releaseReadinessReady.virtualHalPromotionFamilyRows.map(({ id, value }) => [
`virtual-hal-promotion-family-${id}`,
@@ -1597,6 +1663,78 @@ assert.equal(
createProjectReleaseReadinessSummaryViewModel(releaseReadinessReady).rows.find(({ id }) => id === "promotion-candidate-layers")?.value,
"evidence-ready=8 inventory-ready=20",
);
const releaseReadinessWithCandidateArtifacts = createProjectReleaseReadinessReport({
observedOutputs: ["project_release_gate=ok"],
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
virtualHalSimConfigPromotionCandidates: simConfigPromotionCandidateReport,
virtualHalSimConfigMacroLoadFixtures: simConfigMacroLoadFixtureReport,
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
promotionCandidateRows: promotionCandidateArtifactRows,
evidenceExpansionRows: evidenceExpansionArtifactRows,
});
assert.equal(releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.artifactRows.promotionCandidates, 28);
assert.equal(releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.artifactRows.evidenceExpansion, 13);
assert.equal(
releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.hardBlockRuntimeFamilyRows.length,
19,
);
assert.equal(
releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.hardBlockRuntimePromotionAllowedCount,
0,
);
assert.equal(
releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.hardBlockRuntimeFamilyRows
.some((row) => row.skipKind === "L4-TOOL-DB" && row.promotionAllowed === false),
true,
);
assert.deepEqual(
releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.hardBlockRuntimeFamilySummaryRows
.map(({ family, candidateCount, promotionAllowedCount, locked }) => [
family,
candidateCount,
promotionAllowedCount,
locked,
]),
[
["L4-USER-M-PROCESS", 1, 0, true],
["L4-TOOL-DB", 1, 0, true],
["L4-PYTHON-REMAP", 17, 0, true],
],
);
assert.deepEqual(
releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.hardBlockRuntimeFamilyDetailRows
.slice(0, 3)
.map(({ family, path, blockReason, nextProof, promotionAllowed }) => [
family,
path,
blockReason,
nextProof,
promotionAllowed,
]),
[
[
"L4-TOOL-DB",
"axis/db_demo/base.ngc",
"design_tooldata_db_protocol_boundary",
"db_program_v2_1_handshake_getall_load_unload_or_put",
false,
],
[
"L4-PYTHON-REMAP",
"axis/laser/raster_test.ngc",
"design_linuxcnc_python_runtime_boundary",
"python_runtime_owner_and_fixture",
false,
],
[
"L4-PYTHON-REMAP",
"axis/laser/vector_test.ngc",
"design_linuxcnc_python_runtime_boundary",
"python_runtime_owner_and_fixture",
false,
],
],
);
const releaseReadinessWithScreenshots = createProjectReleaseReadinessReport({
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
@@ -1735,11 +1873,21 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
label: "Promotion candidate total",
value: "28",
},
{
id: "promotion-candidate-artifact-rows",
label: "Promotion candidate artifact rows",
value: "28",
},
{
id: "evidence-expansion-candidates",
label: "Evidence expansion candidates",
value: "13",
},
{
id: "evidence-expansion-artifact-rows",
label: "Evidence expansion artifact rows",
value: "13",
},
{
id: "evidence-expansion-next-evidence",
label: "Evidence expansion next evidence",
@@ -1899,6 +2047,22 @@ assert.equal(
).rows.find(({ id }) => id === "promotion-candidate-layers")?.value,
"evidence-ready=8 inventory-ready=20",
);
assert.equal(
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
await loadProjectReleaseReadinessArtifactUrlWorkflow({
artifactUrl: "/project-release-readiness.json",
diagnosticsUrl: "/browser-diagnostics.json",
fetchRef: async (url) => ({
ok: true,
status: 200,
text: async () => url === "/browser-diagnostics.json"
? JSON.stringify(browserDiagnosticsArtifact)
: JSON.stringify(releaseReadinessArtifact),
}),
}),
).rows.find(({ id }) => id === "evidence-ready-candidate-rows")?.value,
"8",
);
assert.equal(
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
await loadProjectReleaseReadinessArtifactUrlWorkflow({

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,
@@ -18,9 +21,6 @@ import {
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,
@@ -161,6 +161,30 @@ import {
renderMachineSessionControlView,
} from "../../../runtime/ui/ini-panel/ui-shell.js";
const VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES =
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS.flatMap((target) => target.sourceFiles ?? []);
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = resolve(__dirname, "../../..");
function parseTsvRows(text) {
const [headerLine, ...dataLines] = text.trim().split(/\r?\n/);
const headers = headerLine.split("\t");
return dataLines
.filter((line) => line.trim().length > 0)
.map((line) => {
const values = line.split("\t");
return Object.fromEntries(headers.map((header, index) => [header, values[index] ?? ""]));
});
}
const promotionCandidateRows = parseTsvRows(
readFileSync(resolve(root, "build/wasm/sim-configs-inventory/promotion-candidates.tsv"), "utf8"),
);
const evidenceExpansionRows = parseTsvRows(
readFileSync(resolve(root, "build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv"), "utf8"),
);
assert.deepEqual(getIniPanelEntryManifest(), INI_PANEL_ENTRIES);
assert.deepEqual(getVisibleIniPanelEntries(), INI_PANEL_ENTRIES);
assert.equal(controlPageStatusText({ lastAction: "x", runStatus: "ok", error: null }), "x | ok | -");
@@ -1454,6 +1478,8 @@ const releaseReadinessArtifactJson = JSON.stringify(createProjectReleaseReadines
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
virtualHalSimConfigPromotionCandidates: releaseReadinessPromotionCandidates,
virtualHalSimConfigEvidenceExpansion: releaseReadinessEvidenceExpansion,
promotionCandidateRows,
evidenceExpansionRows,
virtualHalSimConfigMacroLoadFixtures: releaseReadinessMacroLoadFixtures,
virtualHalMotionControllerMatrix: releaseReadinessMotionControllerMatrix,
}));
@@ -1807,7 +1833,7 @@ const releaseArtifactMountResult = mountIniPanelShellWorkflowOverviewReleaseRead
rowsNode: releaseArtifactRowsNode,
});
assert.equal(releaseArtifactRenderResult.rendered, true);
assert.equal(releaseArtifactRenderResult.rowCount, 28);
assert.equal(releaseArtifactRenderResult.rowCount, 30);
assert.equal(releaseArtifactRenderResult.dataset.handoffScope, "workflow-overview-release-readiness-artifact");
assert.equal(
releaseArtifactRenderResult.rowIds.includes("gate-execution-manifest"),
@@ -1858,7 +1884,7 @@ assert.equal(
true,
);
assert.equal(releaseArtifactMountResult.ready, true);
assert.equal(releaseArtifactMountResult.renderResult.rowCount, 28);
assert.equal(releaseArtifactMountResult.renderResult.rowCount, 30);
assert.deepEqual(
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson("{").missing,
["artifact-json"],
@@ -1917,7 +1943,7 @@ assert.equal(
);
assert.equal(releaseArtifactUrlWorkflow.renderState.statusLine, "Ready: No blocking reasons");
assert.equal(releaseArtifactUrlWorkflow.mountResult.ready, true);
assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 28);
assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 30);
const releaseArtifactUrlWorkflowSummary =
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
releaseArtifactUrlWorkflow,
@@ -1981,10 +2007,38 @@ assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-browser-diagnostics-count")?.value,
"8 diagnostics-ready",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-ready-candidate-rows")?.value,
"8",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-ready-candidate-preview")?.value,
"qtdragon-multi-joint-on-abort",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-ready-candidate-preview-gcode")?.value,
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-ready-candidate-promotion-allowed")?.value,
"0",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-ready-candidate-baseline-changing")?.value,
"no",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-expansion-next-evidence")?.value,
"browser-diagnostics-binding",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promotion-candidate-artifact-rows")?.value,
"28",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-expansion-artifact-rows")?.value,
"13",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-expansion-baseline-changing")?.value,
"no",
@@ -2025,6 +2079,42 @@ assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promoted-blocked-family-count")?.value,
"0 promoted",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-locked-rows")?.value,
"19",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-locked-preview")?.value,
"L4-TOOL-DB:axis/db_demo/base.ngc, L4-PYTHON-REMAP:axis/laser/raster_test.ngc, L4-PYTHON-REMAP:axis/laser/vector_test.ngc",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-promotion-allowed")?.value,
"0",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-family-count")?.value,
"3",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-family-summary")?.value,
"L4-USER-M-PROCESS:1 locked=yes, L4-TOOL-DB:1 locked=yes, L4-PYTHON-REMAP:17 locked=yes",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-detail-row-count")?.value,
"19",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-detail-preview-1")?.value,
"L4-TOOL-DB:axis/db_demo/base.ngc; reason=design_tooldata_db_protocol_boundary; next=db_program_v2_1_handshake_getall_load_unload_or_put; promotion_allowed=0",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-family-L4-PYTHON-REMAP")?.value,
"17 locked rows; promotion_allowed=0; first=axis/laser/raster_test.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-family-L4-USER-M-PROCESS-paths")?.value,
"axis/vismach/millturn/example.ngc",
);
const promotionCandidateDrilldownViewModel =
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownViewModel(
releaseArtifactUrlWorkflowSummary,
@@ -2062,6 +2152,18 @@ assert.equal(
promotionCandidateDrilldownViewModel.rows.find(({ id }) => id === "blocked-family-exclusion")?.value,
"passed",
);
assert.equal(
promotionCandidateDrilldownViewModel.rows.find(({ id }) => id === "candidate-layer")?.value,
"evidence-ready",
);
assert.equal(
promotionCandidateDrilldownViewModel.rows.find(({ id }) => id === "inventory-promotion-allowed")?.value,
"0",
);
assert.equal(
promotionCandidateDrilldownViewModel.rows.find(({ id }) => id === "inventory-baseline-changing")?.value,
"no",
);
assert.equal(promotionCandidateDrilldownViewModel.sourceFileCount, 2);
assert.equal(promotionCandidateDrilldownViewModel.evidenceRowCount, 8);
assert.equal(
@@ -2091,7 +2193,7 @@ assert.deepEqual(
],
);
assert.equal(promotionCandidateDrilldownRenderState.ready, true);
assert.equal(promotionCandidateDrilldownRenderState.rowCount, 19);
assert.equal(promotionCandidateDrilldownRenderState.rowCount, 22);
assert.equal(promotionCandidateDrilldownRenderState.sourceFileCount, 2);
assert.equal(promotionCandidateDrilldownRenderState.evidenceRowCount, 8);
assert.equal(
@@ -2133,7 +2235,7 @@ const promotionCandidateDrilldownMountResult =
rowsNode: promotionCandidateDrilldownRowsNode,
});
assert.equal(promotionCandidateDrilldownRenderResult.rendered, true);
assert.equal(promotionCandidateDrilldownRenderResult.rowCount, 19);
assert.equal(promotionCandidateDrilldownRenderResult.rowCount, 22);
assert.equal(promotionCandidateDrilldownRenderResult.evidenceRowCount, 8);
assert.deepEqual(promotionCandidateDrilldownRenderResult.dataset, {
handoffPhase: "ready",
@@ -2145,6 +2247,18 @@ assert.equal(promotionCandidateDrilldownRowsNode.children.some((node) =>
node.dataset?.workflowOverviewPromotionCandidateDrilldownValue === "blocked-family-exclusion" &&
node.textContent === "passed"
), true);
assert.equal(promotionCandidateDrilldownRowsNode.children.some((node) =>
node.dataset?.workflowOverviewPromotionCandidateDrilldownValue === "candidate-layer" &&
node.textContent === "evidence-ready"
), true);
assert.equal(promotionCandidateDrilldownRowsNode.children.some((node) =>
node.dataset?.workflowOverviewPromotionCandidateDrilldownValue === "inventory-promotion-allowed" &&
node.textContent === "0"
), true);
assert.equal(promotionCandidateDrilldownRowsNode.children.some((node) =>
node.dataset?.workflowOverviewPromotionCandidateDrilldownValue === "inventory-baseline-changing" &&
node.textContent === "no"
), true);
assert.equal(promotionCandidateDrilldownRowsNode.children.some((node) =>
node.dataset?.workflowOverviewPromotionCandidateDrilldownSourceKind === "gcode" &&
node.dataset?.workflowOverviewPromotionCandidateDrilldownManifestBacked === "true" &&
@@ -2196,6 +2310,16 @@ assert.equal(promotionFamilyRowsRenderState.statusLine, "Ready: 3 promotion fami
assert.equal(promotionFamilyRowsRenderState.rows.length, 3);
assert.equal(promotionFamilyRowsRenderState.sourceFileCount, 17);
assert.equal(promotionFamilyRowsRenderState.sourceRows.length, 17);
assert.equal(promotionFamilyRowsRenderState.candidateCount, 8);
assert.equal(promotionFamilyRowsRenderState.candidateRows.length, 8);
assert.equal(
promotionFamilyRowsRenderState.candidateRows.every(({ candidateStatus, promotionAllowed, baselineChanging }) =>
candidateStatus === "evidence-ready" &&
promotionAllowed === false &&
baselineChanging === false
),
true,
);
assert.equal(
promotionFamilyRowsRenderState.rows.find(({ id }) => id === "virtual-hal-promotion-family-qtdragon-on-abort")
?.sourceKindSummary,
@@ -2215,6 +2339,8 @@ assert.equal(promotionFamilySourceFilterViewModel.ready, true);
assert.equal(promotionFamilySourceFilterViewModel.statusLine, "Ready: 17/17 source files selected");
assert.equal(promotionFamilySourceFilterViewModel.filteredSourceFileCount, 17);
assert.equal(promotionFamilySourceFilterViewModel.totalSourceFileCount, 17);
assert.equal(promotionFamilySourceFilterViewModel.filteredCandidateCount, 8);
assert.equal(promotionFamilySourceFilterViewModel.totalCandidateCount, 8);
assert.deepEqual(
promotionFamilySourceFilterViewModel.familyFilters.map(({ id, count, selected }) => [id, count, selected]),
[
@@ -2249,6 +2375,7 @@ assert.equal(promotionFamilyQtDragonGcodeFilterViewModel.selectedSourceKind, "gc
assert.equal(promotionFamilyQtDragonGcodeFilterViewModel.statusLine, "Ready: 6/17 source files selected");
assert.equal(promotionFamilyQtDragonGcodeFilterViewModel.scopedSourceFileCount, 12);
assert.equal(promotionFamilyQtDragonGcodeFilterViewModel.filteredSourceFileCount, 6);
assert.equal(promotionFamilyQtDragonGcodeFilterViewModel.filteredCandidateCount, 6);
assert.equal(promotionFamilyQtDragonGcodeFilterViewModel.familyRows.length, 1);
assert.equal(
promotionFamilyQtDragonGcodeFilterViewModel.familyRows[0]?.sourceKindSummary,
@@ -2325,7 +2452,8 @@ assert.equal(promotionFamilySourceFilterRenderResult.statusLine, "Ready: 6/17 so
assert.equal(promotionFamilySourceFilterRenderResult.controlCount, 2);
assert.equal(promotionFamilySourceFilterRenderResult.filterRowCount, 7);
assert.equal(promotionFamilySourceFilterRenderResult.sourceFileCount, 6);
assert.equal(promotionFamilySourceFilterRenderResult.rowCount, 13);
assert.equal(promotionFamilySourceFilterRenderResult.candidateCount, 6);
assert.equal(promotionFamilySourceFilterRenderResult.rowCount, 19);
assert.deepEqual(promotionFamilySourceFilterRenderResult.dataset, {
handoffPhase: "ready",
handoffReady: "true",
@@ -2385,8 +2513,20 @@ assert.equal(
),
true,
);
assert.equal(
promotionFamilySourceFilterRowsNode.children.some((node) =>
node.dataset?.workflowOverviewPromotionFamilySourceFilterType === "candidate-row" &&
node.dataset?.workflowOverviewPromotionFamilySourceFilterFamily ===
"virtual-hal-promotion-family-qtdragon-on-abort" &&
node.dataset?.workflowOverviewPromotionFamilySourceFilterCandidateStatus === "evidence-ready" &&
node.dataset?.workflowOverviewPromotionFamilySourceFilterPromotionAllowed === "0" &&
node.dataset?.workflowOverviewPromotionFamilySourceFilterBaselineChanging === "no" &&
node.textContent === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc"
),
true,
);
assert.equal(promotionFamilySourceFilterMountResult.ready, true);
assert.equal(promotionFamilySourceFilterMountResult.renderResult.rowCount, 13);
assert.equal(promotionFamilySourceFilterMountResult.renderResult.rowCount, 19);
const promotionFamilySourceFilterSelectionActionPlan =
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan(
promotionFamilyRowsRenderState,
@@ -2436,6 +2576,8 @@ assert.equal(promotionFamilySourceFilterSelectionChange.selectedSourceKind, "all
assert.equal(promotionFamilySourceFilterSelectionChange.actionPlan.changed, true);
assert.equal(promotionFamilySourceFilterSelectionChange.viewModel.filteredSourceFileCount, 2);
assert.equal(promotionFamilySourceFilterSelectionChange.renderResult.sourceFileCount, 2);
assert.equal(promotionFamilySourceFilterSelectionChange.viewModel.filteredCandidateCount, 1);
assert.equal(promotionFamilySourceFilterSelectionChange.renderResult.candidateCount, 1);
assert.equal(promotionFamilySourceFilterSelectionChange.renderResult.dataset.selectedSourceKind, "all");
assert.equal(
promotionFamilySourceFilterRowsNode.children.some((node) =>
@@ -2673,7 +2815,7 @@ const releaseArtifactUrlWorkflowMountResult =
rowsNode: releaseArtifactUrlWorkflowRowsNode,
});
assert.equal(releaseArtifactUrlWorkflowRenderResult.rendered, true);
assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 35);
assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 59);
assert.equal(
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("browser-diagnostics"),
true,
@@ -2723,7 +2865,7 @@ assert.equal(
true,
);
assert.equal(releaseArtifactUrlWorkflowMountResult.ready, true);
assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 35);
assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 59);
const releaseArtifactUrlWorkflowMissingDom = await loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl({
artifactUrl: "/project-release-readiness.json",
fetchRef: async () => ({

View File

@@ -2108,6 +2108,26 @@ function linuxCncSourceExecutable(relPath) {
}
}
function linuxCncHostCommandPath(command) {
const pathCommand = commandPath(command);
if (pathCommand !== "") {
return pathCommand;
}
const ripScriptCommands = new Set(["linuxcnc", "halrun"]);
const ripRelativePath = ripScriptCommands.has(command)
? `scripts/${command}`
: `bin/${command}`;
const ripPath = resolve(workspaceRoot, "linuxcnc", ripRelativePath);
try {
if ((statSync(ripPath).mode & 0o111) !== 0) {
return ripPath;
}
} catch {
return "";
}
return "";
}
function toolDbProcessNativeRuntimeReadinessRows(transactionRows) {
const first = transactionRows[0];
if (!first) {
@@ -2126,22 +2146,22 @@ function toolDbProcessNativeRuntimeReadinessRows(transactionRows) {
kind: "host_command",
requirement: "linuxcnc",
purpose: "provide_linuxcnc_python_runtime_and_task_context",
available: commandPath("linuxcnc") !== "",
evidence: commandPath("linuxcnc"),
available: linuxCncHostCommandPath("linuxcnc") !== "",
evidence: linuxCncHostCommandPath("linuxcnc"),
},
{
kind: "host_command",
requirement: "milltask",
purpose: "own_db_program_startup_and_tooldata_protocol",
available: commandPath("milltask") !== "",
evidence: commandPath("milltask"),
available: linuxCncHostCommandPath("milltask") !== "",
evidence: linuxCncHostCommandPath("milltask"),
},
{
kind: "host_command",
requirement: "halcmd",
purpose: "provide_linuxcnc_runtime_environment_for_db_program_sync",
available: commandPath("halcmd") !== "",
evidence: commandPath("halcmd"),
available: linuxCncHostCommandPath("halcmd") !== "",
evidence: linuxCncHostCommandPath("halcmd"),
},
{
kind: "source_config_file",
@@ -2326,9 +2346,20 @@ function toolDbProcessNativeRuntimeProbeGateRows({
pythonModulesReady &&
protocolContractReady
);
const gateStatus = runtimeReady && sourceProofReady
? "ready_to_implement_protocol_probe"
: "blocked_missing_host_runtime";
const nativeRuntimeProbeRows = existsSync(nativeRuntimeProbeSummarySourcePath)
? parseTsv(readFileSync(nativeRuntimeProbeSummarySourcePath, "utf8"))
: [];
const nativeToolDbProbe = nativeRuntimeProbeRows.find((row) =>
row.boundary_class === "L4-TOOL-DB" &&
row.runtime_probe === "linuxcnc_tool_db_runtime_probe",
);
const protocolProbePassed = nativeToolDbProbe?.probe_status === "runtime_protocol_probe_passed";
const gateStatus = protocolProbePassed
? "native_protocol_probe_passed_waiting_for_node_browser_proof"
: runtimeReady && sourceProofReady
? "ready_to_implement_protocol_probe"
: "blocked_missing_host_runtime";
const proofStatus = protocolProbePassed ? "native_protocol_probe_passed" : "pending";
return [[
first.path,
@@ -2351,12 +2382,14 @@ function toolDbProcessNativeRuntimeProbeGateRows({
flagValue(sourceProofReady),
"native_db_process_protocol_probe_required",
gateStatus,
"pending",
proofStatus,
"0",
"0",
runtimeReady
? "host_and_source_ready_write_native_db_protocol_probe_python3_and_tbl_fallback_not_sufficient"
: "python3_and_tool_table_fallback_not_sufficient_keep_tool_db_blocked",
protocolProbePassed
? "native_protocol_probe_passed_keep_tool_db_locked_until_node_browser_proof"
: runtimeReady
? "host_and_source_ready_write_native_db_protocol_probe_python3_and_tbl_fallback_not_sufficient"
: "python3_and_tool_table_fallback_not_sufficient_keep_tool_db_blocked",
].map(tsvValue).join("\t")];
}
@@ -2437,13 +2470,26 @@ function verifyToolDbProcessNativeRuntimeProbeGateRows({
assert.equal(row.runtime_ready, flagValue(allRuntimeReady), "tool DB runtime readiness drift");
assert.equal(row.source_proof_ready, "1", "tool DB source proof must be ready before runtime probe implementation");
assert.equal(row.required_native_proof, "native_db_process_protocol_probe_required", "tool DB native proof drift");
assert.equal(row.proof_status, "pending", "tool DB probe gate must remain pending");
assert.ok(
["pending", "native_protocol_probe_passed"].includes(row.proof_status),
"tool DB probe gate proof status drift",
);
assert.equal(row.execution_enabled, "0", "tool DB probe gate must not enable execution");
assert.equal(row.promotion_allowed, "0", "tool DB probe gate must not allow promotion");
if (allRuntimeReady) {
assert.equal(row.missing_requirements, "-", "ready tool DB runtime has missing requirements");
assert.equal(row.gate_status, "ready_to_implement_protocol_probe", "ready tool DB gate status drift");
assert.ok(
[
"ready_to_implement_protocol_probe",
"native_protocol_probe_passed_waiting_for_node_browser_proof",
].includes(row.gate_status),
"ready tool DB gate status drift",
);
if (row.gate_status === "native_protocol_probe_passed_waiting_for_node_browser_proof") {
assert.equal(row.proof_status, "native_protocol_probe_passed", "passed tool DB proof status drift");
assert.ok(row.notes.includes("node_browser_proof"), "passed tool DB gate must keep node/browser proof lock");
}
} else {
assert.deepEqual(
splitSummaryList(row.missing_requirements).sort(),
@@ -4168,8 +4214,8 @@ function pythonRemapNativeRuntimeReadinessRows(contractRows) {
kind: "host_command",
requirement: "linuxcnc",
purpose: "provide_linuxcnc_task_interpreter_python_context",
available: commandPath("linuxcnc") !== "",
evidence: commandPath("linuxcnc"),
available: linuxCncHostCommandPath("linuxcnc") !== "",
evidence: linuxCncHostCommandPath("linuxcnc"),
},
{
kind: "linuxcnc_source_file",
@@ -5563,7 +5609,7 @@ function boundaryPhaseCompletionSummaryRows({
)
);
const runtimeBoundaryHostUnblockPlanOk = (
runtimeBoundaryHostUnblockPlanRows.length === 4 &&
runtimeBoundaryHostUnblockPlanRows.length === runtimeBoundaryHostRequirementSummaryRows.filter((row) => row.missing_blocks_runtime === "1").length &&
runtimeBoundaryHostUnblockPlanRows.every((row) =>
row.requirement_kind === "host_command" &&
row.available === "0" &&
@@ -5581,14 +5627,21 @@ function boundaryPhaseCompletionSummaryRows({
const runtimeBoundaryFamilyHostReadinessOk = (
runtimeBoundaryFamilyHostReadinessRows.length === 3 &&
runtimeBoundaryFamilyHostReadinessRows.every((row) =>
/^[1-9][0-9]*$/.test(row.missing_requirement_count) &&
row.missing_host_requirements !== "-" &&
/^[0-9]+$/.test(row.missing_requirement_count) &&
/^[1-9][0-9]*$/.test(row.available_requirement_count) &&
row.available_requirements !== "-" &&
/^ENABLE_[A-Z0-9_]+=1$/.test(row.opt_in_env) &&
row.execution_command.includes(row.opt_in_env) &&
row.preflight_status === "blocked_missing_host_runtime" &&
row.family_host_status === "blocked_missing_host_requirements" &&
(
(row.missing_requirement_count === "0" &&
row.missing_host_requirements === "-" &&
row.preflight_status === "ready_to_run_opt_in_native_probe" &&
row.family_host_status === "host_ready_to_run_opt_in_probe") ||
(row.missing_requirement_count !== "0" &&
row.missing_host_requirements !== "-" &&
row.preflight_status === "blocked_missing_host_runtime" &&
row.family_host_status === "blocked_missing_host_requirements")
) &&
row.execution_enabled === "0" &&
row.promotion_allowed === "0",
)
@@ -5596,17 +5649,17 @@ function boundaryPhaseCompletionSummaryRows({
const runtimeBoundaryHostReadinessRollupOk = (
runtimeBoundaryHostReadinessRollupRows.length === 1 &&
runtimeBoundaryHostReadinessRollupRows.every((row) =>
row.scope === "blocked_runtime_host_readiness" &&
row.scope === "blocked_runtime_host_readiness" &&
row.family_count === "3" &&
row.host_ready_family_count === "0" &&
row.host_blocked_family_count === "3" &&
row.blocked_families !== "-" &&
row.missing_host_requirements !== "-" &&
row.missing_host_requirement_count === "4" &&
row.ready_opt_in_command_count === "0" &&
row.ready_opt_in_commands === "-" &&
row.blocked_opt_in_commands !== "-" &&
row.host_readiness_status === "host_blocked_for_all_opt_in_native_probes" &&
/^[0-9]+$/.test(row.host_ready_family_count) &&
/^[0-9]+$/.test(row.host_blocked_family_count) &&
/^[0-9]+$/.test(row.missing_host_requirement_count) &&
/^[0-9]+$/.test(row.ready_opt_in_command_count) &&
[
"host_ready_for_all_opt_in_native_probes",
"host_partially_ready_for_opt_in_native_probes",
"host_blocked_for_all_opt_in_native_probes",
].includes(row.host_readiness_status) &&
row.execution_enabled === "0" &&
row.promotion_allowed === "0",
)
@@ -5616,10 +5669,16 @@ function boundaryPhaseCompletionSummaryRows({
runtimeBoundaryOptInProbeDispatchPlanRows.every((row) =>
/^ENABLE_[A-Z0-9_]+=1$/.test(row.opt_in_env) &&
row.execution_command.includes(row.opt_in_env) &&
row.family_host_status === "blocked_missing_host_requirements" &&
row.missing_host_requirements !== "-" &&
row.host_action === "skip_missing_host_requirements" &&
row.dispatch_allowed === "0" &&
(
(row.family_host_status === "host_ready_to_run_opt_in_probe" &&
row.missing_host_requirements === "-" &&
row.host_action === "ready_for_manual_opt_in_native_probe" &&
row.dispatch_allowed === "1") ||
(row.family_host_status === "blocked_missing_host_requirements" &&
row.missing_host_requirements !== "-" &&
row.host_action === "skip_missing_host_requirements" &&
row.dispatch_allowed === "0")
) &&
row.execution_enabled === "0" &&
row.promotion_allowed === "0",
)
@@ -5627,12 +5686,16 @@ function boundaryPhaseCompletionSummaryRows({
const runtimeBoundaryOptInProbeDispatchRollupOk = (
runtimeBoundaryOptInProbeDispatchRollupRows.length === 1 &&
runtimeBoundaryOptInProbeDispatchRollupRows.every((row) =>
row.scope === "blocked_runtime_opt_in_probe_dispatch" &&
row.scope === "blocked_runtime_opt_in_probe_dispatch" &&
row.probe_count === "3" &&
row.dispatch_allowed_count === "0" &&
row.dispatch_blocked_count === "3" &&
row.dispatch_actions === "skip_missing_host_requirements" &&
row.dispatch_status === "dispatch_blocked_for_all_opt_in_native_probes" &&
/^[0-9]+$/.test(row.dispatch_allowed_count) &&
/^[0-9]+$/.test(row.dispatch_blocked_count) &&
row.dispatch_actions !== "-" &&
[
"dispatch_allowed_for_all_opt_in_native_probes",
"dispatch_partially_allowed_for_opt_in_native_probes",
"dispatch_blocked_for_all_opt_in_native_probes",
].includes(row.dispatch_status) &&
row.execution_enabled === "0" &&
row.promotion_allowed === "0",
)
@@ -5640,13 +5703,19 @@ function boundaryPhaseCompletionSummaryRows({
const runtimeBoundaryOptInProbeSkipEvidenceContractOk = (
runtimeBoundaryOptInProbeSkipEvidenceContractRows.length === 3 &&
runtimeBoundaryOptInProbeSkipEvidenceContractRows.every((row) =>
row.host_action === "skip_missing_host_requirements" &&
row.skip_reason === "missing_host_requirements" &&
row.missing_host_requirements !== "-" &&
row.current_probe_status === "skipped_missing_host_runtime" &&
(
(row.host_action === "ready_for_manual_opt_in_native_probe" &&
row.skip_reason === "-" &&
row.missing_host_requirements === "-" &&
row.skip_evidence_status === "skip_contract_not_applicable_dispatch_allowed") ||
(row.host_action === "skip_missing_host_requirements" &&
row.skip_reason === "missing_host_requirements" &&
row.missing_host_requirements !== "-" &&
row.current_probe_status === "skipped_missing_host_runtime" &&
row.skip_evidence_status === "skip_valid_until_host_requirements_available")
) &&
row.evidence_required_now === "0" &&
row.observed_evidence_ready === "0" &&
row.skip_evidence_status === "skip_valid_until_host_requirements_available" &&
row.execution_enabled === "0" &&
row.promotion_allowed === "0",
)
@@ -5654,12 +5723,12 @@ function boundaryPhaseCompletionSummaryRows({
const runtimeBoundaryOptInProbeSkipEvidenceRollupOk = (
runtimeBoundaryOptInProbeSkipEvidenceRollupRows.length === 1 &&
runtimeBoundaryOptInProbeSkipEvidenceRollupRows.every((row) =>
row.scope === "blocked_runtime_opt_in_probe_skip_evidence" &&
row.scope === "blocked_runtime_opt_in_probe_skip_evidence" &&
row.probe_count === "3" &&
row.skip_count === "3" &&
/^[0-9]+$/.test(row.skip_count) &&
row.evidence_required_now_count === "0" &&
row.observed_evidence_ready_count === "0" &&
row.skip_evidence_statuses === "skip_valid_until_host_requirements_available" &&
row.skip_evidence_statuses !== "-" &&
row.evidence_rollup_status === "no_native_pass_evidence_accepted_while_host_blocked" &&
row.execution_enabled === "0" &&
row.promotion_allowed === "0",
@@ -5668,11 +5737,10 @@ function boundaryPhaseCompletionSummaryRows({
const runtimeBoundaryNativeEvidenceAcceptanceGateOk = (
runtimeBoundaryNativeEvidenceAcceptanceGateRows.length === 3 &&
runtimeBoundaryNativeEvidenceAcceptanceGateRows.every((row) =>
row.current_probe_status === "skipped_missing_host_runtime" &&
row.native_evidence_status === "pending_until_native_pass" &&
row.native_evidence_status === "pending_until_native_pass" &&
row.evidence_required_now === "0" &&
row.observed_evidence_ready === "0" &&
row.native_evidence_gate === "blocked_until_host_requirements_available" &&
["blocked_until_host_requirements_available", "blocked_until_native_pass_evidence"].includes(row.native_evidence_gate) &&
row.evidence_acceptance_allowed === "0" &&
row.promotion_ready === "0" &&
row.execution_enabled === "0" &&
@@ -5698,7 +5766,7 @@ function boundaryPhaseCompletionSummaryRows({
classOrderMatches(runtimeBoundaryOptInProbeSkipEvidenceContractRows) &&
classOrderMatches(runtimeBoundaryNativeEvidenceAcceptanceGateRows) &&
runtimeBoundaryOptInProbeDispatchPlanRows.every((row) =>
row.dispatch_allowed === "0" &&
["0", "1"].includes(row.dispatch_allowed) &&
row.execution_enabled === "0" &&
row.promotion_allowed === "0",
) &&
@@ -5708,8 +5776,7 @@ function boundaryPhaseCompletionSummaryRows({
row.promotion_allowed === "0",
) &&
runtimeBoundaryPromotionBlockerRows.every((row) =>
row.blocker_keys.includes("host_runtime_requirements_missing") &&
row.blocker_keys.includes("promotion_lock_active") &&
row.blocker_keys.includes("promotion_lock_active") &&
row.blocker_keys.includes("manual_lock_update_required"),
)
);
@@ -6085,7 +6152,11 @@ function verifyBoundaryPhaseCompletionSummaryRows(rows) {
);
for (const row of parsedRows) {
assert.equal(row.passed, "1", `${row.criterion}: completion criterion failed`);
assert.ok(/^[1-9][0-9]*$/.test(row.count), `${row.criterion}: invalid criterion count`);
if (row.criterion === "runtime_boundary_host_unblock_plan") {
assert.ok(/^[0-9]+$/.test(row.count), `${row.criterion}: invalid criterion count`);
} else {
assert.ok(/^[1-9][0-9]*$/.test(row.count), `${row.criterion}: invalid criterion count`);
}
assert.notEqual(row.evidence, "-", `${row.criterion}: missing criterion evidence`);
}
const rowByCriterion = new Map(parsedRows.map((row) => [row.criterion, row]));
@@ -7233,9 +7304,9 @@ function verifyRuntimeBoundaryHostRequirementSummaryRows(rows, preflightRows) {
const byRequirement = new Map(parsedRows.map((row) => [row.requirement, row]));
for (const required of [
["linuxcnc", "0", "3", "L4-PYTHON-REMAP,L4-TOOL-DB,L4-USER-M-PROCESS"],
["halcmd", "0", "2", "L4-TOOL-DB,L4-USER-M-PROCESS"],
["milltask", "0", "1", "L4-TOOL-DB"],
["linuxcnc", "1", "3", "-"],
["halcmd", "1", "2", "-"],
["milltask", "1", "1", "-"],
["python3", "1", "2", "-"],
["tclsh", "1", "1", "-"],
]) {
@@ -7439,45 +7510,22 @@ function verifyRuntimeBoundaryFamilyHostReadinessRows(rows, preflightRows) {
expectedHeaders,
);
const preflightByClass = new Map(preflightRows.map((row) => [row.boundary_class, row]));
const expected = new Map([
[
"L4-USER-M-PROCESS",
{
missingCount: "3",
missing: "halrun,halcmd,linuxcnc",
available: "tclsh",
},
],
[
"L4-TOOL-DB",
{
missingCount: "3",
missing: "linuxcnc,milltask,halcmd",
available: "python3,axis/db_demo/db_nonran.py,linuxcnc.so,tooldb.py",
},
],
[
"L4-PYTHON-REMAP",
{
missingCount: "1",
missing: "linuxcnc",
available: "python3,src/emc/rs274ngc/interp_python.cc,src/emc/pythonplugin/python_plugin.cc,axis/remap/stop-lookahead/python/remap.py,axis/remap/stop-lookahead/python/toplevel.py",
},
],
]);
const expectedClasses = [
"L4-USER-M-PROCESS",
"L4-TOOL-DB",
"L4-PYTHON-REMAP",
];
assert.equal(parsedRows.length, expected.size, "family host readiness row count drift");
assert.equal(parsedRows.length, expectedClasses.length, "family host readiness row count drift");
assert.deepEqual(
parsedRows.map((row) => row.boundary_class),
[...expected.keys()],
expectedClasses,
"family host readiness order drift",
);
for (const row of parsedRows) {
const preflight = preflightByClass.get(row.boundary_class);
const expectedRow = expected.get(row.boundary_class);
assert.ok(preflight, `${row.boundary_class}: family host readiness lacks preflight row`);
assert.ok(expectedRow, `${row.boundary_class}: unexpected family host readiness row`);
assert.equal(row.boundary_kind, preflight.boundary_kind, `${row.boundary_class}: family host readiness kind drift`);
assert.equal(row.target, preflight.target, `${row.boundary_class}: family host readiness target drift`);
assert.equal(row.blocked, preflight.blocked, `${row.boundary_class}: family host readiness blocked drift`);
@@ -7486,11 +7534,20 @@ function verifyRuntimeBoundaryFamilyHostReadinessRows(rows, preflightRows) {
assert.equal(row.execution_command, preflight.execution_command, `${row.boundary_class}: family host readiness command drift`);
assert.equal(row.current_probe_status, preflight.current_probe_status, `${row.boundary_class}: family host readiness probe status drift`);
assert.equal(row.preflight_status, preflight.preflight_status, `${row.boundary_class}: family host readiness preflight status drift`);
assert.equal(row.missing_requirement_count, expectedRow.missingCount, `${row.boundary_class}: family host readiness missing count drift`);
assert.equal(row.missing_host_requirements, expectedRow.missing, `${row.boundary_class}: family host readiness missing requirements drift`);
assert.equal(row.available_requirements, expectedRow.available, `${row.boundary_class}: family host readiness available requirements drift`);
assert.equal(row.available_requirement_count, String(splitSummaryList(expectedRow.available).length), `${row.boundary_class}: family host readiness available count drift`);
assert.equal(row.family_host_status, "blocked_missing_host_requirements", `${row.boundary_class}: family host readiness should remain blocked on this host`);
assert.equal(row.missing_host_requirements, preflight.missing_requirements, `${row.boundary_class}: family host readiness missing requirements drift`);
assert.equal(row.missing_requirement_count, String(splitSummaryList(preflight.missing_requirements).length), `${row.boundary_class}: family host readiness missing count drift`);
assert.equal(
row.available_requirement_count,
String(splitSummaryList(row.available_requirements).length),
`${row.boundary_class}: family host readiness available count drift`,
);
if (preflight.runtime_ready === "1") {
assert.equal(row.missing_host_requirements, "-", `${row.boundary_class}: ready family must not list missing requirements`);
assert.equal(row.family_host_status, "host_ready_to_run_opt_in_probe", `${row.boundary_class}: ready family host status drift`);
} else {
assert.notEqual(row.missing_host_requirements, "-", `${row.boundary_class}: blocked family must list missing requirements`);
assert.equal(row.family_host_status, "blocked_missing_host_requirements", `${row.boundary_class}: blocked family host status drift`);
}
assert.equal(row.execution_enabled, "0", `${row.boundary_class}: family host readiness must not enable execution`);
assert.equal(row.promotion_allowed, "0", `${row.boundary_class}: family host readiness must not allow promotion`);
assert.ok(row.notes.includes("no_automatic_promotion"), `${row.boundary_class}: family host readiness note drift`);
@@ -7571,22 +7628,33 @@ function verifyRuntimeBoundaryHostReadinessRollupRows({
.filter((requirement) => requirement.missing_blocks_runtime === "1")
.map((requirement) => requirement.requirement),
);
const readyFamilies = familyHostReadinessRows.filter((family) =>
family.family_host_status === "host_ready_to_run_opt_in_probe",
);
const blockedFamilies = familyHostReadinessRows.filter((family) =>
family.family_host_status !== "host_ready_to_run_opt_in_probe",
);
assert.equal(parsedRows.length, 1, "host readiness rollup row count drift");
assert.equal(row.scope, "blocked_runtime_host_readiness", "host readiness rollup scope drift");
assert.equal(row.family_count, String(familyHostReadinessRows.length), "host readiness rollup family count drift");
assert.equal(row.host_ready_family_count, "0", "host readiness rollup must have no ready families on this host");
assert.equal(row.host_blocked_family_count, String(familyHostReadinessRows.length), "host readiness rollup blocked family count drift");
assert.equal(row.blocked_families, "L4-PYTHON-REMAP,L4-TOOL-DB,L4-USER-M-PROCESS", "host readiness rollup blocked families drift");
assert.equal(row.host_ready_family_count, String(readyFamilies.length), "host readiness rollup ready family count drift");
assert.equal(row.host_blocked_family_count, String(blockedFamilies.length), "host readiness rollup blocked family count drift");
assert.equal(row.blocked_families, listValue(uniqueSorted(blockedFamilies.map((family) => family.boundary_class))), "host readiness rollup blocked families drift");
assert.equal(row.missing_host_requirements, listValue(expectedMissingRequirements), "host readiness rollup missing requirements drift");
assert.equal(row.missing_host_requirement_count, String(expectedMissingRequirements.length), "host readiness rollup missing requirement count drift");
assert.equal(row.ready_opt_in_command_count, "0", "host readiness rollup must not report ready opt-in commands");
assert.equal(row.ready_opt_in_commands, "-", "host readiness rollup must not list ready opt-in commands");
assert.notEqual(row.blocked_opt_in_commands, "-", "host readiness rollup must list blocked opt-in commands");
for (const family of familyHostReadinessRows) {
assert.equal(row.ready_opt_in_command_count, String(readyFamilies.length), "host readiness rollup ready command count drift");
assert.equal(row.ready_opt_in_commands, listValue(uniqueSorted(readyFamilies.map((family) => family.execution_command))), "host readiness rollup ready commands drift");
assert.equal(row.blocked_opt_in_commands, listValue(uniqueSorted(blockedFamilies.map((family) => family.execution_command))), "host readiness rollup blocked commands drift");
for (const family of blockedFamilies) {
assert.ok(row.blocked_opt_in_commands.includes(family.execution_command), `${family.boundary_class}: host readiness rollup missing blocked command`);
}
assert.equal(row.host_readiness_status, "host_blocked_for_all_opt_in_native_probes", "host readiness rollup status drift");
const expectedHostReadinessStatus = readyFamilies.length === familyHostReadinessRows.length
? "host_ready_for_all_opt_in_native_probes"
: readyFamilies.length > 0
? "host_partially_ready_for_opt_in_native_probes"
: "host_blocked_for_all_opt_in_native_probes";
assert.equal(row.host_readiness_status, expectedHostReadinessStatus, "host readiness rollup status drift");
assert.equal(row.execution_enabled, "0", "host readiness rollup must not enable execution");
assert.equal(row.promotion_allowed, "0", "host readiness rollup must not allow promotion");
assert.ok(row.notes.includes("no_automatic_promotion"), "host readiness rollup note drift");
@@ -7685,14 +7753,19 @@ function verifyRuntimeBoundaryOptInProbeDispatchPlanRows({
assert.equal(row.missing_host_requirements, readiness.missing_host_requirements, `${row.boundary_class}: dispatch missing requirement drift`);
assert.equal(row.host_readiness_status, rollup.host_readiness_status, `${row.boundary_class}: dispatch rollup status drift`);
assert.ok(row.execution_command.includes(row.opt_in_env), `${row.boundary_class}: dispatch command must include opt-in env`);
assert.equal(row.family_host_status, "blocked_missing_host_requirements", `${row.boundary_class}: current host must remain blocked`);
assert.notEqual(row.missing_host_requirements, "-", `${row.boundary_class}: blocked dispatch must list missing requirements`);
assert.equal(row.host_readiness_status, "host_blocked_for_all_opt_in_native_probes", `${row.boundary_class}: dispatch host readiness status drift`);
assert.equal(row.host_action, "skip_missing_host_requirements", `${row.boundary_class}: current host dispatch action drift`);
assert.equal(row.dispatch_allowed, "0", `${row.boundary_class}: current host dispatch must not be allowed`);
if (readiness.family_host_status === "host_ready_to_run_opt_in_probe") {
assert.equal(row.missing_host_requirements, "-", `${row.boundary_class}: ready dispatch must not list missing requirements`);
assert.equal(row.host_action, "ready_for_manual_opt_in_native_probe", `${row.boundary_class}: ready dispatch action drift`);
assert.equal(row.dispatch_allowed, "1", `${row.boundary_class}: ready dispatch should be allowed manually`);
assert.ok(row.next_action.includes("manually_run_opt_in_probe"), `${row.boundary_class}: ready dispatch next action drift`);
} else {
assert.notEqual(row.missing_host_requirements, "-", `${row.boundary_class}: blocked dispatch must list missing requirements`);
assert.equal(row.host_action, "skip_missing_host_requirements", `${row.boundary_class}: blocked dispatch action drift`);
assert.equal(row.dispatch_allowed, "0", `${row.boundary_class}: blocked dispatch must not be allowed`);
assert.ok(row.next_action.includes(row.missing_host_requirements), `${row.boundary_class}: blocked dispatch next action must name missing requirements`);
}
assert.equal(row.execution_enabled, "0", `${row.boundary_class}: dispatch plan must not enable execution`);
assert.equal(row.promotion_allowed, "0", `${row.boundary_class}: dispatch plan must not allow promotion`);
assert.ok(row.next_action.includes(row.missing_host_requirements), `${row.boundary_class}: dispatch next action must name missing requirements`);
assert.ok(row.notes.includes("no_automatic_promotion"), `${row.boundary_class}: dispatch plan note drift`);
}
@@ -7752,6 +7825,7 @@ function verifyRuntimeBoundaryOptInProbeDispatchRollupRows({
expectedHeaders,
);
const row = parsedRows[0];
const dispatchAllowedRows = dispatchPlanRows.filter((plan) => plan.dispatch_allowed === "1");
const dispatchBlockedRows = dispatchPlanRows.filter((plan) => plan.dispatch_allowed !== "1");
const expectedMissingRequirements = uniqueSorted(dispatchBlockedRows.flatMap((plan) =>
splitSummaryList(plan.missing_host_requirements),
@@ -7760,17 +7834,22 @@ function verifyRuntimeBoundaryOptInProbeDispatchRollupRows({
assert.equal(parsedRows.length, 1, "opt-in probe dispatch rollup row count drift");
assert.equal(row.scope, "blocked_runtime_opt_in_probe_dispatch", "opt-in probe dispatch rollup scope drift");
assert.equal(row.probe_count, String(dispatchPlanRows.length), "opt-in probe dispatch rollup probe count drift");
assert.equal(row.dispatch_allowed_count, "0", "opt-in probe dispatch rollup must not allow probes on this host");
assert.equal(row.dispatch_blocked_count, String(dispatchPlanRows.length), "opt-in probe dispatch rollup blocked count drift");
assert.equal(row.blocked_families, "L4-PYTHON-REMAP,L4-TOOL-DB,L4-USER-M-PROCESS", "opt-in probe dispatch rollup blocked families drift");
assert.equal(row.dispatch_actions, "skip_missing_host_requirements", "opt-in probe dispatch rollup action drift");
assert.equal(row.dispatch_allowed_commands, "-", "opt-in probe dispatch rollup must not list allowed commands");
assert.notEqual(row.dispatch_blocked_commands, "-", "opt-in probe dispatch rollup must list blocked commands");
for (const plan of dispatchPlanRows) {
assert.equal(row.dispatch_allowed_count, String(dispatchAllowedRows.length), "opt-in probe dispatch rollup allowed count drift");
assert.equal(row.dispatch_blocked_count, String(dispatchBlockedRows.length), "opt-in probe dispatch rollup blocked count drift");
assert.equal(row.blocked_families, listValue(uniqueSorted(dispatchBlockedRows.map((plan) => plan.boundary_class))), "opt-in probe dispatch rollup blocked families drift");
assert.equal(row.dispatch_actions, listValue(uniqueSorted(dispatchPlanRows.map((plan) => plan.host_action))), "opt-in probe dispatch rollup action drift");
assert.equal(row.dispatch_allowed_commands, listValue(uniqueSorted(dispatchAllowedRows.map((plan) => plan.execution_command))), "opt-in probe dispatch rollup allowed commands drift");
assert.equal(row.dispatch_blocked_commands, listValue(uniqueSorted(dispatchBlockedRows.map((plan) => plan.execution_command))), "opt-in probe dispatch rollup blocked commands drift");
for (const plan of dispatchBlockedRows) {
assert.ok(row.dispatch_blocked_commands.includes(plan.execution_command), `${plan.boundary_class}: opt-in probe dispatch rollup missing blocked command`);
}
assert.equal(row.missing_host_requirements, listValue(expectedMissingRequirements), "opt-in probe dispatch rollup missing requirement drift");
assert.equal(row.dispatch_status, "dispatch_blocked_for_all_opt_in_native_probes", "opt-in probe dispatch rollup status drift");
const expectedDispatchStatus = dispatchAllowedRows.length === dispatchPlanRows.length
? "dispatch_allowed_for_all_opt_in_native_probes"
: dispatchAllowedRows.length > 0
? "dispatch_partially_allowed_for_opt_in_native_probes"
: "dispatch_blocked_for_all_opt_in_native_probes";
assert.equal(row.dispatch_status, expectedDispatchStatus, "opt-in probe dispatch rollup status drift");
assert.equal(row.execution_enabled, "0", "opt-in probe dispatch rollup must not enable execution");
assert.equal(row.promotion_allowed, "0", "opt-in probe dispatch rollup must not allow promotion");
assert.ok(row.notes.includes("no_automatic_promotion"), "opt-in probe dispatch rollup note drift");
@@ -7869,12 +7948,8 @@ function verifyRuntimeBoundaryOptInProbeSkipEvidenceContractRows({
assert.equal(row.runtime_probe, evidence.runtime_probe, `${row.boundary_class}: skip evidence runtime probe drift`);
assert.equal(row.opt_in_env, dispatch.opt_in_env, `${row.boundary_class}: skip evidence opt-in drift`);
assert.equal(row.execution_command, dispatch.execution_command, `${row.boundary_class}: skip evidence command drift`);
assert.equal(row.host_action, "skip_missing_host_requirements", `${row.boundary_class}: current host skip action drift`);
assert.equal(row.host_action, dispatch.host_action, `${row.boundary_class}: skip evidence host action drift`);
assert.equal(row.skip_reason, "missing_host_requirements", `${row.boundary_class}: skip evidence reason drift`);
assert.equal(row.missing_host_requirements, dispatch.missing_host_requirements, `${row.boundary_class}: skip evidence missing requirements drift`);
assert.notEqual(row.missing_host_requirements, "-", `${row.boundary_class}: skip evidence must list missing host requirements`);
assert.equal(row.current_probe_status, "skipped_missing_host_runtime", `${row.boundary_class}: skip evidence current probe status drift`);
assert.equal(row.current_probe_status, evidence.current_probe_status, `${row.boundary_class}: skip evidence probe status must match pass evidence contract`);
assert.equal(row.evidence_required_now, "0", `${row.boundary_class}: skipped probe must not require evidence now`);
assert.equal(row.evidence_required_now, evidence.evidence_required_now, `${row.boundary_class}: skip evidence required-now drift`);
@@ -7882,7 +7957,18 @@ function verifyRuntimeBoundaryOptInProbeSkipEvidenceContractRows({
assert.equal(row.observed_evidence_ready, evidence.observed_evidence_ready, `${row.boundary_class}: skip evidence readiness drift`);
assert.equal(row.native_evidence_status, "pending_until_native_pass", `${row.boundary_class}: skip evidence status drift`);
assert.equal(row.native_evidence_status, evidence.evidence_status, `${row.boundary_class}: skip evidence native status drift`);
assert.equal(row.skip_evidence_status, "skip_valid_until_host_requirements_available", `${row.boundary_class}: skip evidence contract status drift`);
if (dispatch.dispatch_allowed === "1") {
assert.equal(row.host_action, "ready_for_manual_opt_in_native_probe", `${row.boundary_class}: ready skip evidence action drift`);
assert.equal(row.skip_reason, "-", `${row.boundary_class}: ready dispatch must not have skip reason`);
assert.equal(row.missing_host_requirements, "-", `${row.boundary_class}: ready dispatch must not list missing host requirements`);
assert.equal(row.skip_evidence_status, "skip_contract_not_applicable_dispatch_allowed", `${row.boundary_class}: ready skip evidence status drift`);
} else {
assert.equal(row.host_action, "skip_missing_host_requirements", `${row.boundary_class}: blocked skip evidence action drift`);
assert.equal(row.skip_reason, "missing_host_requirements", `${row.boundary_class}: blocked skip evidence reason drift`);
assert.notEqual(row.missing_host_requirements, "-", `${row.boundary_class}: blocked skip evidence must list missing host requirements`);
assert.equal(row.current_probe_status, "skipped_missing_host_runtime", `${row.boundary_class}: blocked skip evidence current probe status drift`);
assert.equal(row.skip_evidence_status, "skip_valid_until_host_requirements_available", `${row.boundary_class}: blocked skip evidence status drift`);
}
assert.equal(row.execution_enabled, "0", `${row.boundary_class}: skip evidence contract must not enable execution`);
assert.equal(row.promotion_allowed, "0", `${row.boundary_class}: skip evidence contract must not allow promotion`);
assert.ok(row.notes.includes("no_automatic_promotion"), `${row.boundary_class}: skip evidence contract note drift`);
@@ -7947,6 +8033,7 @@ function verifyRuntimeBoundaryOptInProbeSkipEvidenceRollupRows({
expectedHeaders,
);
const row = parsedRows[0];
const skippedRows = skipEvidenceContractRows.filter((contract) => contract.skip_reason !== "-");
const expectedMissingRequirements = uniqueSorted(skipEvidenceContractRows.flatMap((contract) =>
splitSummaryList(contract.missing_host_requirements),
));
@@ -7954,15 +8041,15 @@ function verifyRuntimeBoundaryOptInProbeSkipEvidenceRollupRows({
assert.equal(parsedRows.length, 1, "opt-in probe skip evidence rollup row count drift");
assert.equal(row.scope, "blocked_runtime_opt_in_probe_skip_evidence", "opt-in probe skip evidence rollup scope drift");
assert.equal(row.probe_count, String(skipEvidenceContractRows.length), "opt-in probe skip evidence rollup probe count drift");
assert.equal(row.skip_count, String(skipEvidenceContractRows.length), "opt-in probe skip evidence rollup skip count drift");
assert.equal(row.skip_count, String(skippedRows.length), "opt-in probe skip evidence rollup skip count drift");
assert.equal(row.evidence_required_now_count, "0", "opt-in probe skip evidence rollup must not require evidence now");
assert.equal(row.observed_evidence_ready_count, "0", "opt-in probe skip evidence rollup must not report evidence ready");
assert.equal(row.skipped_families, "L4-PYTHON-REMAP,L4-TOOL-DB,L4-USER-M-PROCESS", "opt-in probe skip evidence rollup family drift");
assert.equal(row.skip_reasons, "missing_host_requirements", "opt-in probe skip evidence rollup reason drift");
assert.equal(row.skipped_families, listValue(uniqueSorted(skippedRows.map((contract) => contract.boundary_class))), "opt-in probe skip evidence rollup family drift");
assert.equal(row.skip_reasons, listValue(uniqueSorted(skippedRows.map((contract) => contract.skip_reason))), "opt-in probe skip evidence rollup reason drift");
assert.equal(row.missing_host_requirements, listValue(expectedMissingRequirements), "opt-in probe skip evidence rollup missing requirements drift");
assert.equal(row.current_probe_statuses, "skipped_missing_host_runtime", "opt-in probe skip evidence rollup probe status drift");
assert.equal(row.current_probe_statuses, listValue(uniqueSorted(skipEvidenceContractRows.map((contract) => contract.current_probe_status))), "opt-in probe skip evidence rollup probe status drift");
assert.equal(row.native_evidence_statuses, "pending_until_native_pass", "opt-in probe skip evidence rollup native evidence status drift");
assert.equal(row.skip_evidence_statuses, "skip_valid_until_host_requirements_available", "opt-in probe skip evidence rollup skip evidence status drift");
assert.equal(row.skip_evidence_statuses, listValue(uniqueSorted(skipEvidenceContractRows.map((contract) => contract.skip_evidence_status))), "opt-in probe skip evidence rollup skip evidence status drift");
assert.equal(row.evidence_rollup_status, "no_native_pass_evidence_accepted_while_host_blocked", "opt-in probe skip evidence rollup status drift");
assert.equal(row.execution_enabled, "0", "opt-in probe skip evidence rollup must not enable execution");
assert.equal(row.promotion_allowed, "0", "opt-in probe skip evidence rollup must not allow promotion");
@@ -8075,7 +8162,6 @@ function verifyRuntimeBoundaryNativeEvidenceAcceptanceGateRows({
assert.equal(row.target, skip.target, `${row.boundary_class}: native evidence gate target drift`);
assert.equal(row.blocked, skip.blocked, `${row.boundary_class}: native evidence gate blocked drift`);
assert.equal(row.runtime_probe, skip.runtime_probe, `${row.boundary_class}: native evidence gate runtime probe drift`);
assert.equal(row.current_probe_status, "skipped_missing_host_runtime", `${row.boundary_class}: native evidence gate probe status drift`);
assert.equal(row.current_probe_status, skip.current_probe_status, `${row.boundary_class}: native evidence gate skip status drift`);
assert.equal(row.native_evidence_status, "pending_until_native_pass", `${row.boundary_class}: native evidence gate evidence status drift`);
assert.equal(row.native_evidence_status, skip.native_evidence_status, `${row.boundary_class}: native evidence gate native evidence status drift`);
@@ -8083,16 +8169,21 @@ function verifyRuntimeBoundaryNativeEvidenceAcceptanceGateRows({
assert.equal(row.evidence_required_now, skip.evidence_required_now, `${row.boundary_class}: native evidence gate required-now drift`);
assert.equal(row.observed_evidence_ready, "0", `${row.boundary_class}: native evidence gate must not observe evidence ready`);
assert.equal(row.observed_evidence_ready, skip.observed_evidence_ready, `${row.boundary_class}: native evidence gate readiness drift`);
assert.equal(row.skip_evidence_status, "skip_valid_until_host_requirements_available", `${row.boundary_class}: native evidence gate skip status drift`);
assert.equal(row.skip_evidence_status, skip.skip_evidence_status, `${row.boundary_class}: native evidence gate skip status drift`);
assert.equal(row.promotion_ready, "0", `${row.boundary_class}: native evidence gate must not be promotion-ready`);
assert.equal(row.promotion_ready, readiness.promotion_ready, `${row.boundary_class}: native evidence gate promotion readiness drift`);
assert.equal(row.node_gate_status, "blocked_until_native_pass_evidence", `${row.boundary_class}: native evidence gate Node status drift`);
assert.equal(row.node_gate_status, postNativeGate.node_gate_status, `${row.boundary_class}: native evidence gate post-native Node status drift`);
assert.equal(row.browser_gate_status, "blocked_until_node_gate_complete", `${row.boundary_class}: native evidence gate browser status drift`);
assert.equal(row.browser_gate_status, postNativeGate.browser_gate_status, `${row.boundary_class}: native evidence gate post-native browser status drift`);
assert.equal(row.native_evidence_gate, "blocked_until_host_requirements_available", `${row.boundary_class}: native evidence gate status drift`);
if (skip.skip_reason === "missing_host_requirements") {
assert.equal(row.native_evidence_gate, "blocked_until_host_requirements_available", `${row.boundary_class}: native evidence gate status drift`);
assert.ok(row.next_action.includes(skip.missing_host_requirements), `${row.boundary_class}: native evidence gate next action must name missing requirements`);
} else {
assert.equal(row.native_evidence_gate, "blocked_until_native_pass_evidence", `${row.boundary_class}: native evidence gate status drift`);
assert.equal(row.next_action, "run_opt_in_native_runtime_probe_and_collect_evidence", `${row.boundary_class}: native evidence gate next action drift`);
}
assert.equal(row.evidence_acceptance_allowed, "0", `${row.boundary_class}: native evidence gate must not allow evidence acceptance`);
assert.ok(row.next_action.includes(skip.missing_host_requirements), `${row.boundary_class}: native evidence gate next action must name missing requirements`);
assert.equal(row.execution_enabled, "0", `${row.boundary_class}: native evidence gate must not enable execution`);
assert.equal(row.promotion_allowed, "0", `${row.boundary_class}: native evidence gate must not allow promotion`);
assert.ok(row.notes.includes("no_automatic_promotion"), `${row.boundary_class}: native evidence gate note drift`);
@@ -8223,12 +8314,16 @@ function verifyBlockedRuntimeOptInGateConsistency({
assert.equal(skipEvidence.missing_host_requirements, execution.missing_requirements, `${boundaryClass}: skip-evidence missing requirements drift`);
assert.equal(blocker.missing_runtime_requirements, execution.missing_requirements, `${boundaryClass}: blocker missing requirements drift`);
assert.equal(evidenceGate.evidence_acceptance_allowed, "0", `${boundaryClass}: evidence acceptance must remain blocked`);
assert.equal(dispatch.dispatch_allowed, "0", `${boundaryClass}: dispatch must remain blocked on this host`);
assert.equal(dispatch.dispatch_allowed, execution.runtime_ready, `${boundaryClass}: dispatch readiness drift`);
assert.equal(readiness.promotion_lock_active, "1", `${boundaryClass}: readiness must keep promotion lock active`);
assert.equal(postNative.promotion_lock_active, "1", `${boundaryClass}: post-native gate must keep promotion lock active`);
assert.ok(blocker.blocker_keys.includes("promotion_lock_active"), `${boundaryClass}: blocker lacks promotion lock key`);
assert.ok(blocker.blocker_keys.includes("manual_lock_update_required"), `${boundaryClass}: blocker lacks manual lock key`);
assert.ok(blocker.blocker_keys.includes("host_runtime_requirements_missing"), `${boundaryClass}: blocker lacks host runtime key`);
if (execution.runtime_ready === "0") {
assert.ok(blocker.blocker_keys.includes("host_runtime_requirements_missing"), `${boundaryClass}: blocker lacks host runtime key`);
} else {
assert.equal(blocker.blocker_keys.includes("host_runtime_requirements_missing"), false, `${boundaryClass}: ready runtime must not keep host runtime blocker`);
}
assert.ok(evidenceGate.native_evidence_gate.includes("blocked"), `${boundaryClass}: evidence gate must be blocked`);
}
}
@@ -8303,23 +8398,40 @@ function verifyBlockedRuntimeProbeExecutionConsistency({
assert.equal(dispatch.missing_host_requirements, execution.missing_requirements, `${execution.boundary_class}: dispatch missing requirement drift`);
assert.equal(skip.missing_host_requirements, execution.missing_requirements, `${execution.boundary_class}: skip evidence missing requirement drift`);
assert.equal(execution.runtime_ready, "0", `${execution.boundary_class}: current host must not be runtime-ready`);
assert.equal(execution.source_proof_ready, "1", `${execution.boundary_class}: source proof should remain ready before opt-in runtime`);
assert.equal(execution.current_probe_status, "skipped_missing_host_runtime", `${execution.boundary_class}: probe should remain skipped on this host`);
assert.equal(execution.plan_status, "blocked_missing_host_runtime", `${execution.boundary_class}: execution plan must remain host-blocked`);
assert.equal(preflight.preflight_status, "blocked_missing_host_runtime", `${execution.boundary_class}: preflight must remain host-blocked`);
assert.equal(dispatch.host_readiness_status, "host_blocked_for_all_opt_in_native_probes", `${execution.boundary_class}: dispatch host readiness drift`);
assert.equal(dispatch.host_action, "skip_missing_host_requirements", `${execution.boundary_class}: dispatch action must skip`);
assert.equal(dispatch.dispatch_allowed, "0", `${execution.boundary_class}: dispatch must not be allowed`);
assert.equal(skip.host_action, "skip_missing_host_requirements", `${execution.boundary_class}: skip evidence host action drift`);
assert.equal(skip.skip_reason, "missing_host_requirements", `${execution.boundary_class}: skip reason drift`);
assert.ok(["0", "1"].includes(execution.runtime_ready), `${execution.boundary_class}: invalid runtime readiness`);
if (execution.runtime_ready === "1") {
assert.equal(execution.current_probe_status, "ready_disabled_by_default", `${execution.boundary_class}: ready probe must remain opt-in`);
assert.equal(execution.plan_status, "ready_to_run_opt_in_probe", `${execution.boundary_class}: execution plan must remain opt-in`);
assert.equal(execution.missing_requirements, "-", `${execution.boundary_class}: ready execution plan must not list missing requirements`);
} else {
assert.equal(execution.current_probe_status, "skipped_missing_host_runtime", `${execution.boundary_class}: probe should remain skipped on missing host`);
assert.equal(execution.plan_status, "blocked_missing_host_runtime", `${execution.boundary_class}: execution plan must remain host-blocked`);
assert.notEqual(execution.missing_requirements, "-", `${execution.boundary_class}: blocked execution plan must list missing requirements`);
}
if (execution.runtime_ready === "1") {
assert.equal(preflight.preflight_status, "ready_to_run_opt_in_native_probe", `${execution.boundary_class}: ready preflight status drift`);
assert.equal(dispatch.host_action, "ready_for_manual_opt_in_native_probe", `${execution.boundary_class}: ready dispatch action drift`);
assert.equal(dispatch.dispatch_allowed, "1", `${execution.boundary_class}: ready dispatch must be manually allowed`);
assert.equal(skip.host_action, "ready_for_manual_opt_in_native_probe", `${execution.boundary_class}: ready skip evidence host action drift`);
assert.equal(skip.skip_reason, "-", `${execution.boundary_class}: ready skip reason drift`);
} else {
assert.equal(preflight.preflight_status, "blocked_missing_host_runtime", `${execution.boundary_class}: preflight must remain host-blocked`);
assert.equal(dispatch.host_action, "skip_missing_host_requirements", `${execution.boundary_class}: dispatch action must skip`);
assert.equal(dispatch.dispatch_allowed, "0", `${execution.boundary_class}: dispatch must not be allowed`);
assert.equal(skip.host_action, "skip_missing_host_requirements", `${execution.boundary_class}: skip evidence host action drift`);
assert.equal(skip.skip_reason, "missing_host_requirements", `${execution.boundary_class}: skip reason drift`);
}
assert.equal(pass.evidence_required_now, "0", `${execution.boundary_class}: pass evidence must not be required yet`);
assert.equal(skip.evidence_required_now, "0", `${execution.boundary_class}: skip evidence must not be required yet`);
assert.equal(pass.observed_evidence_ready, "0", `${execution.boundary_class}: pass evidence must not be ready yet`);
assert.equal(skip.observed_evidence_ready, "0", `${execution.boundary_class}: skip evidence must not be ready yet`);
assert.equal(pass.evidence_status, "pending_until_native_pass", `${execution.boundary_class}: pass evidence status drift`);
assert.equal(skip.native_evidence_status, "pending_until_native_pass", `${execution.boundary_class}: skip evidence native status drift`);
assert.equal(skip.skip_evidence_status, "skip_valid_until_host_requirements_available", `${execution.boundary_class}: skip evidence contract status drift`);
assert.ok(
["skip_valid_until_host_requirements_available", "skip_contract_not_applicable_dispatch_allowed"].includes(skip.skip_evidence_status),
`${execution.boundary_class}: skip evidence contract status drift`,
);
for (const row of [execution, pass, preflight, dispatch, skip]) {
assert.equal(row.execution_enabled, "0", `${execution.boundary_class}: probe execution row must not enable execution`);
@@ -8362,12 +8474,26 @@ function verifyBlockedRuntimeRollupConsistency({
assert.equal(dispatch.missing_host_requirements, skipEvidence.missing_host_requirements, "blocked runtime rollup dispatch/skip missing requirement drift");
assert.equal(host.ready_opt_in_commands, dispatch.dispatch_allowed_commands, "blocked runtime rollup allowed command drift");
assert.equal(host.blocked_opt_in_commands, dispatch.dispatch_blocked_commands, "blocked runtime rollup blocked command drift");
assert.equal(host.host_readiness_status, "host_blocked_for_all_opt_in_native_probes", "blocked runtime rollup host status drift");
assert.equal(dispatch.dispatch_status, "dispatch_blocked_for_all_opt_in_native_probes", "blocked runtime rollup dispatch status drift");
assert.ok(
[
"host_ready_for_all_opt_in_native_probes",
"host_partially_ready_for_opt_in_native_probes",
"host_blocked_for_all_opt_in_native_probes",
].includes(host.host_readiness_status),
"blocked runtime rollup host status drift",
);
assert.ok(
[
"dispatch_allowed_for_all_opt_in_native_probes",
"dispatch_partially_allowed_for_opt_in_native_probes",
"dispatch_blocked_for_all_opt_in_native_probes",
].includes(dispatch.dispatch_status),
"blocked runtime rollup dispatch status drift",
);
assert.equal(skipEvidence.evidence_rollup_status, "no_native_pass_evidence_accepted_while_host_blocked", "blocked runtime rollup evidence status drift");
assert.equal(skipEvidence.current_probe_statuses, "skipped_missing_host_runtime", "blocked runtime rollup probe status drift");
assert.notEqual(skipEvidence.current_probe_statuses, "-", "blocked runtime rollup probe status drift");
assert.equal(skipEvidence.native_evidence_statuses, "pending_until_native_pass", "blocked runtime rollup native evidence status drift");
assert.equal(skipEvidence.skip_evidence_statuses, "skip_valid_until_host_requirements_available", "blocked runtime rollup skip evidence status drift");
assert.notEqual(skipEvidence.skip_evidence_statuses, "-", "blocked runtime rollup skip evidence status drift");
for (const [label, row] of [
["host readiness", host],
@@ -8589,7 +8715,10 @@ function verifyBlockedRuntimePromotionGateConsistency({
assert.equal(postNative.node_gate_status, "blocked_until_native_pass_evidence", `${boundaryClass}: post-native Node gate must wait for native evidence`);
assert.equal(postNative.browser_gate_status, "blocked_until_node_gate_complete", `${boundaryClass}: post-native browser gate must wait for Node gate`);
assert.equal(postNative.gate_status, "blocked_before_native_pass", `${boundaryClass}: post-native gate status drift`);
assert.equal(evidenceGate.native_evidence_gate, "blocked_until_host_requirements_available", `${boundaryClass}: native evidence gate must remain host-blocked`);
assert.ok(
["blocked_until_host_requirements_available", "blocked_until_native_pass_evidence"].includes(evidenceGate.native_evidence_gate),
`${boundaryClass}: native evidence gate must remain blocked`,
);
assert.equal(evidenceGate.evidence_acceptance_allowed, "0", `${boundaryClass}: native evidence acceptance must remain blocked`);
for (const row of [readiness, blocker, postNative, evidenceGate, passEvidence, lock]) {
@@ -10816,7 +10945,12 @@ function userMProcessNativeRuntimeReadinessRows(runtimeStatePlanRows) {
["host_command", "linuxcnc", "run_linuxcnc_owned_task_hal_environment"],
];
const commandAvailability = new Map(
requirements.map(([, requirement]) => [requirement, commandPath(requirement)]),
requirements.map(([, requirement]) => [
requirement,
["halrun", "halcmd", "linuxcnc"].includes(requirement)
? linuxCncHostCommandPath(requirement)
: commandPath(requirement),
]),
);
const allAvailable = requirements.every(([, requirement]) =>
commandAvailability.get(requirement),
@@ -12231,6 +12365,15 @@ function verifyPromotionCandidateRows(rows, summaryRows, boundaryRows) {
0,
"current baseline has no direct skipped-main inventory promotion candidate",
);
for (const lockedKind of ["L4-PYTHON-REMAP", "L4-TOOL-DB", "L4-USER-M-PROCESS", "UPSTREAM-DEMO"]) {
const lockedRows = parsedInventoryReady.filter((row) => row.skip_kind === lockedKind);
assert.ok(lockedRows.length > 0, `${lockedKind}: promotion-candidates missing locked inventory rows`);
assert.equal(
lockedRows.filter((row) => row.promotion_allowed !== "0").length,
0,
`${lockedKind}: locked inventory rows must not allow promotion`,
);
}
for (const row of parsedEvidenceReady) {
assert.equal(row.current_status, "PASS", `${row.path}: evidence-ready row must be a current inventory PASS`);
assert.equal(row.skip_kind, "-", `${row.path}: evidence-ready row must not have a skip kind`);