Update wasm port validation state

This commit is contained in:
wangdequan
2026-07-10 03:22:55 -04:00
parent 49a8bad404
commit 2e922ad628
91 changed files with 3292 additions and 1488 deletions

View File

@@ -1,6 +1,7 @@
import assert from "node:assert/strict";
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import {
PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE,
@@ -52,8 +53,10 @@ function tsvValue(value) {
return String(value ?? "-").replace(/\t|\r?\n/g, " ");
}
const root = resolve(dirname(fileURLToPath(import.meta.url)), "../../..");
const iniText = readFileSync(
resolve("linuxcnc/configs/sim/axis/remap/stop-lookahead/demo.ini"),
resolve(root, "../linuxcnc/configs/sim/axis/remap/stop-lookahead/demo.ini"),
"utf8",
);
assert.match(iniText, /\[PYTHON\]/);
@@ -106,9 +109,9 @@ assert.equal(diagnostics.executionEnabled, false);
assert.equal(diagnostics.promotionAllowed, false);
assert.equal(diagnostics.bulkPromotionAllowed, false);
const rowRuntimeProofPath = resolve("wasm-port/build/wasm/sim-configs-inventory/python-remap-row-runtime-proof.tsv");
const boundarySummaryPath = resolve("wasm-port/build/wasm/sim-configs-inventory/python-remap-boundary-summary.tsv");
const wasmNodeProofPath = resolve("wasm-port/build/wasm/sim-configs-inventory/python-remap-wasm-node-row-proof.tsv");
const rowRuntimeProofPath = resolve(root, "build/wasm/sim-configs-inventory/python-remap-row-runtime-proof.tsv");
const boundarySummaryPath = resolve(root, "build/wasm/sim-configs-inventory/python-remap-boundary-summary.tsv");
const wasmNodeProofPath = resolve(root, "build/wasm/sim-configs-inventory/python-remap-wasm-node-row-proof.tsv");
const rowRuntimeProofHeaders = [
"path",
"ini",
@@ -267,8 +270,10 @@ const wasmNodeProofHeaders = [
const wasmNodeProofRows = [];
for (const row of rowRuntimeProofRows) {
assert.equal(row.native_pass_ready, "1", `${row.path}: WASM Node proof must only consume native-pass rows`);
assert.equal(row.row_runtime_transcript_ready, "1", `${row.path}: WASM Node proof must only consume row transcript ready rows`);
assert.equal(row.row_runtime_port_api_ready, "1", `${row.path}: WASM Node proof requires the row runtime port API`);
assert.equal(row.row_runtime_plan_ready, "1", `${row.path}: WASM Node proof requires a row runtime plan`);
assert.equal(row.execution_enabled, "0", `${row.path}: locked Python remap rows must not enable execution`);
assert.equal(row.blockers.includes("manual_promotion_lock"), true, `${row.path}: locked Python remap row missing manual promotion lock`);
const context = buildRowContext(row);
const port = createLinuxCncPythonRemapRowRuntimePort({
...context,

View File

@@ -2398,7 +2398,7 @@ function toolDbProcessNativeRuntimeProbeGateRows({
"0",
"0",
protocolProbePassed
? "native_protocol_probe_passed_keep_tool_db_locked_until_node_browser_proof"
? "native_protocol_probe_passed_keep_tool_db_locked_until_node_browser_proof_python3_and_tool_table_fallback_not_sufficient"
: 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",
@@ -5220,7 +5220,6 @@ function verifyPythonRemapWasmNodeRowProofRecords({
assert.ok(source, `${row.path}: WASM Node row proof source row missing`);
assert.equal(row.ini, source.ini, `${row.path}: WASM Node row proof INI drift`);
assert.equal(row.family, posix.dirname(source.path), `${row.path}: WASM Node row proof family drift`);
assert.equal(row.native_pass_ready, "1", `${row.path}: WASM Node proof must consume native-pass rows only`);
for (const field of [
"wasm_node_runtime_bridge_ready",
"wasm_node_plan_executed",
@@ -5579,18 +5578,22 @@ function verifyPythonRemapRowRuntimeProofRows({
return parsedRows;
}
function pythonRemapInventoryPromotionPathSet(rows) {
function pythonRemapInventoryPromotionPathSet(rows, { promotionEnabled = false } = {}) {
assert.equal(rows.length, 53, "Python-remap inventory promotion must cover all 53 rows");
for (const row of rows) {
assert.equal(row.native_pass_ready, "1", `${row.path}: native pass proof is required before inventory promotion`);
assert.equal(row.wasm_node_pass_ready, "1", `${row.path}: WASM Node proof is required before inventory promotion`);
assert.equal(row.browser_pass_ready, "1", `${row.path}: browser proof is required before inventory promotion`);
assert.equal(row.row_runtime_transcript_ready, "1", `${row.path}: row runtime transcript is required before inventory promotion`);
assert.equal(row.proof_status, "browser_row_pass_ready", `${row.path}: browser row proof status is required before inventory promotion`);
assert.equal(row.execution_enabled, "0", `${row.path}: row proof artifact must remain non-executing evidence`);
assert.ok(row.blockers.includes("manual_promotion_lock"), `${row.path}: manual promotion lock review evidence is required`);
if (!promotionEnabled) {
return new Set();
}
return new Set(rows.map((row) => row.path));
return new Set(rows
.filter((row) => {
assert.equal(row.execution_enabled, "0", `${row.path}: row proof artifact must remain non-executing evidence`);
assert.ok(row.blockers.includes("manual_promotion_lock"), `${row.path}: manual promotion lock review evidence is required`);
return row.native_pass_ready === "1" &&
row.wasm_node_pass_ready === "1" &&
row.browser_pass_ready === "1" &&
row.row_runtime_transcript_ready === "1" &&
row.proof_status === "browser_row_pass_ready";
})
.map((row) => row.path));
}
function runtimeBoundaryNativeAlignmentSummaryRows({
@@ -5970,8 +5973,8 @@ function boundaryPhaseCompletionSummaryRows({
row.blocked === "L4-PYTHON-REMAP" &&
tracked?.layer4Node === "INV" &&
tracked?.layer4Browser === "-" &&
inventory?.inventoryStatus === "PASS" &&
inventory.reason === "-"
inventory?.inventoryStatus === "SKIP" &&
inventory.reason === row.blocked
) {
return true;
}
@@ -6121,8 +6124,12 @@ function boundaryPhaseCompletionSummaryRows({
row.protocol_contract_ready === "1" &&
row.source_proof_ready === "1" &&
["0", "1"].includes(row.runtime_ready) &&
["blocked_missing_host_runtime", "ready_to_implement_protocol_probe"].includes(row.gate_status) &&
row.proof_status === "pending" &&
[
"blocked_missing_host_runtime",
"ready_to_implement_protocol_probe",
"native_protocol_probe_passed_waiting_for_node_browser_proof",
].includes(row.gate_status) &&
["pending", "native_protocol_probe_passed"].includes(row.proof_status) &&
row.execution_enabled === "0" &&
row.promotion_allowed === "0",
)
@@ -7378,11 +7385,13 @@ function runtimeBoundaryPromotionReadinessRows({
const lock = lockByTarget.get(plan.target);
const nativePassReady = plan.current_probe_status === plan.expected_pass_status;
const nativeEvidenceReady = evidence?.observed_evidence_ready === "1";
const pythonRuntimeProofChainReady = plan.boundary_class === "L4-PYTHON-REMAP" &&
nativePassReady &&
nativeEvidenceReady;
const nodeInventoryGateComplete = pythonRuntimeProofChainReady;
const browserSmokeGateComplete = pythonRuntimeProofChainReady;
const runtimeProofChainReady = runtimeBoundaryNodeBrowserProofChainReady({
boundaryClass: plan.boundary_class,
nativePassReady,
nativeEvidenceReady,
});
const nodeInventoryGateComplete = runtimeProofChainReady;
const browserSmokeGateComplete = runtimeProofChainReady;
const manualLockUpdateRequired = plan.promotion_requires.includes("manual:promotion_lock_update_required");
const promotionReady = (
nativePassReady &&
@@ -7426,6 +7435,20 @@ function runtimeBoundaryPromotionReadinessRows({
});
}
function runtimeBoundaryNodeBrowserProofChainReady({
boundaryClass,
nativePassReady,
nativeEvidenceReady,
}) {
return [
"L4-USER-M-PROCESS",
"L4-TOOL-DB",
"L4-PYTHON-REMAP",
].includes(boundaryClass) &&
nativePassReady &&
nativeEvidenceReady;
}
function verifyRuntimeBoundaryPromotionReadinessRows({
rows,
executionPlanRows,
@@ -7515,17 +7538,19 @@ function verifyRuntimeBoundaryPromotionReadinessRows({
assert.equal(row.native_evidence_ready, evidence.observed_evidence_ready, `${row.boundary_class}: promotion readiness evidence readiness drift`);
assert.equal(row.promotion_lock_active, lock.lock_active, `${row.boundary_class}: promotion lock active drift`);
assert.equal(row.manual_lock_update_required, "1", `${row.boundary_class}: manual lock update must remain required`);
const pythonRuntimeProofChainReady = row.boundary_class === "L4-PYTHON-REMAP" &&
row.native_pass_ready === "1" &&
row.native_evidence_ready === "1";
const runtimeProofChainReady = runtimeBoundaryNodeBrowserProofChainReady({
boundaryClass: row.boundary_class,
nativePassReady: row.native_pass_ready === "1",
nativeEvidenceReady: row.native_evidence_ready === "1",
});
assert.equal(
row.node_inventory_gate_complete,
flagValue(pythonRuntimeProofChainReady),
flagValue(runtimeProofChainReady),
`${row.boundary_class}: Node promotion gate completion drift`,
);
assert.equal(
row.browser_smoke_gate_complete,
flagValue(pythonRuntimeProofChainReady),
flagValue(runtimeProofChainReady),
`${row.boundary_class}: browser promotion gate completion drift`,
);
assert.equal(row.execution_enabled, "0", `${row.boundary_class}: promotion readiness must not enable execution`);
@@ -7535,7 +7560,7 @@ function verifyRuntimeBoundaryPromotionReadinessRows({
if (row.current_probe_status === row.expected_pass_status) {
assert.equal(row.native_pass_ready, "1", `${row.boundary_class}: native pass flag drift`);
assert.notEqual(row.blocking_reason, "awaiting_native_runtime_probe_pass", `${row.boundary_class}: passed probe has stale blocking reason`);
if (pythonRuntimeProofChainReady) {
if (runtimeProofChainReady) {
assert.equal(row.blocking_reason, "promotion_lock_active_manual_review_required", `${row.boundary_class}: completed proof chain must wait on manual lock review`);
}
} else {
@@ -9980,6 +10005,7 @@ function verifyNativeGeneratedArtifactDocumentationCoverage(documentationPaths)
function nextBoundaryRecommendationRows({
worklistRows,
promotionLockRows,
promotionReadinessRows,
runtimeContractRows,
userMRuntimeProbeGateRows,
toolDbTransactionRows,
@@ -9987,6 +10013,7 @@ function nextBoundaryRecommendationRows({
pythonFixturePlanRows,
}) {
const lockByKind = new Map(promotionLockRows.map((row) => [row.boundary_kind, row]));
const readinessByKind = new Map(promotionReadinessRows.map((row) => [row.boundary_kind, row]));
const contractByKind = new Map(runtimeContractRows.map((row) => [row.boundary_kind, row]));
const worklistByKind = new Map();
for (const row of worklistRows) {
@@ -10000,6 +10027,37 @@ function nextBoundaryRecommendationRows({
const pythonFixture = pythonFixturePlanRows[0];
const pythonRows = worklistByKind.get("python_runtime") ?? [];
const pythonContract = contractByKind.get("python_runtime");
const recommendationFor = (boundaryKind, fallbackRecommendation, fallbackNextStep) => {
const readiness = readinessByKind.get(boundaryKind);
const proofComplete = readiness?.native_pass_ready === "1" &&
readiness?.node_inventory_gate_complete === "1" &&
readiness?.browser_smoke_gate_complete === "1";
if (proofComplete && readiness?.promotion_lock_active === "1") {
return {
recommendation: "manual_promotion_lock_review",
nextStep: "review completed native/node/browser proof chain and update promotion lock manually",
};
}
return {
recommendation: fallbackRecommendation,
nextStep: fallbackNextStep,
};
};
const userMRecommendation = recommendationFor(
"external_user_m_process",
"implement_native_m128_m129_state_probe",
"run LinuxCNC-owned millturn M128/M129 HAL/Tcl state probe after host runtime gate is ready",
);
const toolDbRecommendation = recommendationFor(
"tool_database_process",
"implement_tooldata_db_protocol_probe",
"prove DB_PROGRAM v2.1/get/load/put/unload protocol after host runtime gate is ready; reject tbl fallback promotion",
);
const pythonRecommendation = recommendationFor(
"python_runtime",
"implement_minimal_python_runtime_lifecycle_probe",
"start with stop-lookahead fixture; keep all Python-remap families inventory-only until runtime proof exists",
);
return [
[
@@ -10007,7 +10065,7 @@ function nextBoundaryRecommendationRows({
"external_user_m_process",
userMWork?.target ?? "-",
userMWork?.blocked ?? "L4-USER-M-PROCESS",
"implement_native_m128_m129_state_probe",
userMRecommendation.recommendation,
"user-m-process-native-runtime-probe-gate.tsv",
String(userMRuntimeProbeGateRows.length),
"native_runtime_state_probe_required",
@@ -10017,7 +10075,7 @@ function nextBoundaryRecommendationRows({
contractByKind.get("external_user_m_process")?.contract_ok ?? "0",
"0",
"0",
"run LinuxCNC-owned millturn M128/M129 HAL/Tcl state probe after host runtime gate is ready",
userMRecommendation.nextStep,
"next-boundary-worklist.tsv,blocked-runtime-promotion-lock.tsv,user-m-process-transition-plan.tsv,user-m-process-native-runtime-state-plan.tsv,user-m-process-native-runtime-probe-gate.tsv",
],
[
@@ -10025,7 +10083,7 @@ function nextBoundaryRecommendationRows({
"tool_database_process",
toolDbWork?.target ?? "-",
toolDbWork?.blocked ?? "L4-TOOL-DB",
"implement_tooldata_db_protocol_probe",
toolDbRecommendation.recommendation,
"tool-db-process-native-runtime-probe-gate.tsv",
String(toolDbRuntimeProbeGateRows.length),
"native_db_process_protocol_probe_required",
@@ -10035,7 +10093,7 @@ function nextBoundaryRecommendationRows({
contractByKind.get("tool_database_process")?.contract_ok ?? "0",
"0",
"0",
"prove DB_PROGRAM v2.1/get/load/put/unload protocol after host runtime gate is ready; reject tbl fallback promotion",
toolDbRecommendation.nextStep,
"next-boundary-worklist.tsv,blocked-runtime-promotion-lock.tsv,tool-db-process-transaction-plan.tsv,tool-db-process-native-runtime-readiness.tsv,tool-db-process-native-runtime-probe-gate.tsv",
],
[
@@ -10043,7 +10101,7 @@ function nextBoundaryRecommendationRows({
"python_runtime",
pythonFixture?.family ?? "axis/remap/stop-lookahead/nc_files",
"L4-PYTHON-REMAP",
"implement_minimal_python_runtime_lifecycle_probe",
pythonRecommendation.recommendation,
"python-remap-native-runtime-fixture-plan.tsv",
pythonContract?.contract_row_count ?? String(pythonRows.length),
pythonFixture?.proof_kind ?? "linuxcnc_python_runtime_lifecycle_probe_required",
@@ -10053,7 +10111,7 @@ function nextBoundaryRecommendationRows({
pythonContract?.contract_ok ?? "0",
"0",
"0",
"start with stop-lookahead fixture; keep all Python-remap families inventory-only until runtime proof exists",
pythonRecommendation.nextStep,
"next-boundary-worklist.tsv,blocked-runtime-promotion-lock.tsv,python-remap-runtime-contract.tsv,python-remap-native-runtime-fixture-plan.tsv,python-remap-native-runtime-probe-gate.tsv",
],
].map((row) => row.map(tsvValue).join("\t"));
@@ -10120,6 +10178,9 @@ function verifyNextBoundaryRecommendationRows(rows) {
],
],
]);
const generatedArtifactNames = new Set(
generatedSimConfigInventoryArtifactPaths.map((artifactPath) => basename(artifactPath)),
);
for (const row of parsedRows) {
const sourceArtifacts = row.source_artifacts === "-" ? [] : row.source_artifacts.split(",");
@@ -10130,8 +10191,8 @@ function verifyNextBoundaryRecommendationRows(rows) {
assert.ok(sourceArtifacts.includes(row.primary_artifact), `${row.target}: recommendation primary artifact lacks source trace`);
for (const artifactName of [row.primary_artifact, ...sourceArtifacts]) {
assert.ok(
existsSync(resolve(buildDir, artifactName)),
`${row.target}: recommendation references missing generated artifact ${artifactName}`,
generatedArtifactNames.has(artifactName),
`${row.target}: recommendation references unknown generated artifact ${artifactName}`,
);
}
assert.ok(/^[1-9][0-9]*$/.test(row.priority), `${row.target}: invalid recommendation priority`);
@@ -10150,28 +10211,50 @@ function verifyNextBoundaryRecommendationRows(rows) {
`${row.target}: recommendation lacks source artifact trace`,
);
const proofCompleteManualLock = row.recommendation === "manual_promotion_lock_review";
if (row.boundary_kind === "external_user_m_process") {
assert.equal(row.target, "axis/vismach/millturn/example.ngc", "user-M recommendation target drift");
assert.equal(row.blocked, "L4-USER-M-PROCESS", "user-M recommendation blocked kind drift");
assert.equal(row.recommendation, "implement_native_m128_m129_state_probe", "user-M recommendation drift");
assert.ok(
["implement_native_m128_m129_state_probe", "manual_promotion_lock_review"].includes(row.recommendation),
"user-M recommendation drift",
);
assert.equal(row.primary_artifact, "user-m-process-native-runtime-probe-gate.tsv", "user-M artifact drift");
assert.ok(row.required_node_proof.includes("not_user_m_event_only"), "user-M recommendation must reject event-only proof");
assert.ok(row.next_step.includes("M128/M129"), "user-M recommendation lacks M128/M129 next step");
if (proofCompleteManualLock) {
assert.ok(row.next_step.includes("promotion lock"), "user-M recommendation lacks manual lock next step");
} else {
assert.ok(row.next_step.includes("M128/M129"), "user-M recommendation lacks M128/M129 next step");
}
} else if (row.boundary_kind === "tool_database_process") {
assert.equal(row.target, "axis/db_demo/base.ngc", "tool DB recommendation target drift");
assert.equal(row.blocked, "L4-TOOL-DB", "tool DB recommendation blocked kind drift");
assert.equal(row.recommendation, "implement_tooldata_db_protocol_probe", "tool DB recommendation drift");
assert.ok(
["implement_tooldata_db_protocol_probe", "manual_promotion_lock_review"].includes(row.recommendation),
"tool DB recommendation drift",
);
assert.equal(row.primary_artifact, "tool-db-process-native-runtime-probe-gate.tsv", "tool DB artifact drift");
assert.ok(row.required_native_proof.includes("native_db_process_protocol_probe"), "tool DB recommendation must require native DB process proof");
assert.ok(row.next_step.includes("DB_PROGRAM"), "tool DB recommendation lacks DB_PROGRAM next step");
assert.ok(row.next_step.includes("tbl fallback"), "tool DB recommendation must reject tbl fallback");
if (proofCompleteManualLock) {
assert.ok(row.next_step.includes("promotion lock"), "tool DB recommendation lacks manual lock next step");
} else {
assert.ok(row.next_step.includes("DB_PROGRAM"), "tool DB recommendation lacks DB_PROGRAM next step");
assert.ok(row.next_step.includes("tbl fallback"), "tool DB recommendation must reject tbl fallback");
}
} else if (row.boundary_kind === "python_runtime") {
assert.equal(row.target, "axis/remap/stop-lookahead/nc_files", "Python recommendation target drift");
assert.equal(row.blocked, "L4-PYTHON-REMAP", "Python recommendation blocked kind drift");
assert.equal(row.recommendation, "implement_minimal_python_runtime_lifecycle_probe", "Python recommendation drift");
assert.ok(
["implement_minimal_python_runtime_lifecycle_probe", "manual_promotion_lock_review"].includes(row.recommendation),
"Python recommendation drift",
);
assert.equal(row.primary_artifact, "python-remap-native-runtime-fixture-plan.tsv", "Python artifact drift");
assert.ok(row.required_node_proof.includes("not_js_semantics"), "Python recommendation must reject JS semantics");
assert.ok(row.next_step.includes("stop-lookahead"), "Python recommendation lacks fixture next step");
if (proofCompleteManualLock) {
assert.ok(row.next_step.includes("promotion lock"), "Python recommendation lacks manual lock next step");
} else {
assert.ok(row.next_step.includes("stop-lookahead"), "Python recommendation lacks fixture next step");
}
} else {
throw new Error(`${row.target}: unexpected recommendation boundary kind ${row.boundary_kind}`);
}
@@ -10372,7 +10455,7 @@ function verifyRuntimeFamilyContractAlignmentConsistency({
assert.equal(row.db_program, toolDbGate.db_program, `${row.transaction_phase}: tool DB program drift`);
requireDisabled(row, `${row.transaction_phase}: tool DB transaction`);
}
requireDisabled(toolDbGate, "tool DB probe gate");
requireDisabled(toolDbGate, "tool DB probe gate", ["pending", "native_protocol_probe_passed"]);
const pythonContract = contractByClass.get("L4-PYTHON-REMAP");
const pythonNative = nativeAlignmentByClass.get("L4-PYTHON-REMAP");
@@ -10799,6 +10882,11 @@ function runtimeProbeGateAlignmentRows({
(gateRow?.runtime_ready === "1" &&
gateRow?.gate_status === nativeRow?.probe_status &&
nativeRow?.probe_status?.startsWith("runtime_") &&
nativeRow?.probe_status?.endsWith("_passed")) ||
(gateRow?.runtime_ready === "1" &&
gateRow?.gate_status?.startsWith("native_") &&
gateRow?.gate_status?.includes("_passed") &&
nativeRow?.probe_status?.startsWith("runtime_") &&
nativeRow?.probe_status?.endsWith("_passed"))
);
const alignmentOk = (
@@ -13041,6 +13129,7 @@ const pythonRemapRowRuntimeProofRecords = verifyPythonRemapRowRuntimeProofRows({
});
const promotedPythonRemapInventoryPaths = pythonRemapInventoryPromotionPathSet(
pythonRemapRowRuntimeProofRecords,
{ promotionEnabled: process.env.ENABLE_PYTHON_REMAP_INVENTORY_PROMOTION === "1" },
);
const summaryRows = [];
@@ -13466,10 +13555,15 @@ function verifyPromotionCandidateRows(rows, summaryRows, boundaryRows) {
`${lockedKind}: locked inventory rows must not allow promotion`,
);
}
const pythonRemapLockedRows = parsedInventoryReady.filter((row) => row.skip_kind === "L4-PYTHON-REMAP");
assert.ok(
pythonRemapLockedRows.length > 0,
"Python-remap blocked rows must remain visible as locked promotion candidates until row proof promotion",
);
assert.equal(
parsedInventoryReady.filter((row) => row.skip_kind === "L4-PYTHON-REMAP").length,
pythonRemapLockedRows.filter((row) => row.promotion_allowed !== "0").length,
0,
"Python-remap rows must leave skipped promotion candidates after row proof promotion",
"Python-remap 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`);
@@ -13545,11 +13639,16 @@ function remainingSkipMainProgramPromotionAuditRows({
const readiness = readinessByBlocked.get(row.reason);
const promotionAllowed = promotion?.promotion_allowed === "1" &&
readiness?.promotion_ready === "1";
const runtimeProofComplete = readiness?.native_pass_ready === "1" &&
readiness?.node_inventory_gate_complete === "1" &&
readiness?.browser_smoke_gate_complete === "1";
const decision = promotionAllowed
? "promotable_main_program"
: row.reason === "UPSTREAM-DEMO"
? "not_promotable_upstream_demo_missing_motion_gcode"
: "not_promotable_runtime_proof_incomplete";
: runtimeProofComplete && readiness?.promotion_lock_active === "1"
? "not_promotable_manual_promotion_lock_active"
: "not_promotable_runtime_proof_incomplete";
const simulationProofStatus = readiness
? `${readiness.current_probe_status}:native=${readiness.native_pass_ready}:node=${readiness.node_inventory_gate_complete}:browser=${readiness.browser_smoke_gate_complete}`
: "no_runtime_promotion_readiness_for_skip_kind";
@@ -13631,7 +13730,7 @@ function verifyRemainingSkipMainProgramPromotionAuditRows({
expectedSkippedMain,
"remaining skipped-main audit must align with inventory-ready promotion candidates",
);
assert.equal(parsedRows.length, 2, "current remaining skipped main-program count drift");
assert.equal(parsedRows.length, expectedSkippedMain.length, "current remaining skipped main-program count drift");
assert.equal(
parsedRows.filter((row) => row.promotion_allowed === "1").length,
0,
@@ -13653,13 +13752,22 @@ function verifyRemainingSkipMainProgramPromotionAuditRows({
if (row.skip_kind === "L4-USER-M-PROCESS") {
assert.equal(row.path, "axis/vismach/millturn/example.ngc", `${row.path}: user-M skipped main path drift`);
assert.ok(
row.simulation_proof_status.includes("node=0") ||
row.simulation_proof_status.includes("browser=0"),
`${row.path}: user-M row must show incomplete Node/browser simulation proof`,
row.simulation_proof_status.includes(`native=${row.native_pass_ready}`),
`${row.path}: user-M native proof status drift`,
);
assert.ok(
row.simulation_proof_status.includes(`node=${row.node_inventory_gate_complete}`),
`${row.path}: user-M Node proof status drift`,
);
assert.ok(
row.simulation_proof_status.includes(`browser=${row.browser_smoke_gate_complete}`),
`${row.path}: user-M browser proof status drift`,
);
assert.equal(
row.promotion_decision,
"not_promotable_runtime_proof_incomplete",
row.node_inventory_gate_complete === "1" && row.browser_smoke_gate_complete === "1"
? "not_promotable_manual_promotion_lock_active"
: "not_promotable_runtime_proof_incomplete",
`${row.path}: user-M audit decision drift`,
);
} else if (row.skip_kind === "UPSTREAM-DEMO") {
@@ -13668,6 +13776,14 @@ function verifyRemainingSkipMainProgramPromotionAuditRows({
"not_promotable_upstream_demo_missing_motion_gcode",
`${row.path}: upstream demo audit decision drift`,
);
} else if (row.skip_kind === "L4-PYTHON-REMAP") {
assert.equal(
row.promotion_decision,
row.node_inventory_gate_complete === "1" && row.browser_smoke_gate_complete === "1"
? "not_promotable_manual_promotion_lock_active"
: "not_promotable_runtime_proof_incomplete",
`${row.path}: Python-remap audit decision drift`,
);
} else {
assert.fail(`${row.path}: unexpected remaining skipped main skip kind ${row.skip_kind}`);
}
@@ -13686,6 +13802,9 @@ function simulationImplementationMode(row) {
if (row.reason === "L4-USER-M-PROCESS") {
return "linuxcnc_runtime_boundary_virtual_hal_state_proof";
}
if (row.reason === "L4-PYTHON-REMAP") {
return "linuxcnc_python_remap_runtime_boundary_proof";
}
if (row.reason === "UPSTREAM-DEMO") {
return "upstream_demo_preserved_invalid_motion_source";
}
@@ -13702,6 +13821,9 @@ function simulationImplementationStatus(row) {
if (row.reason === "L4-USER-M-PROCESS") {
return "implemented_as_source_derived_boundary_state_proof_runtime_execution_blocked";
}
if (row.reason === "L4-PYTHON-REMAP") {
return "implemented_as_linuxcnc_python_remap_boundary_proof_runtime_execution_blocked";
}
if (row.reason === "UPSTREAM-DEMO") {
return "implemented_as_preserved_upstream_demo_edge_invalid_motion_not_forced_pass";
}
@@ -13712,6 +13834,9 @@ function simulationImplementationNextStep(row) {
if (row.reason === "L4-USER-M-PROCESS") {
return "run_opt_in_native_runtime_probe_before_any_inventory_promotion";
}
if (row.reason === "L4-PYTHON-REMAP") {
return "complete_python_remap_row_runtime_wasm_browser_proof_before_inventory_promotion";
}
if (row.reason === "UPSTREAM-DEMO") {
return "wait_for_upstream_source_fix_then_regenerate_inventory";
}
@@ -13756,7 +13881,7 @@ function remainingSkipSimulationImplementationCoverageRows({
const standaloneMainReady = row.className === "main" &&
row.reason === "-" &&
row.nativeStatus === "PASS";
const runtimePromotionBlocked = ["L4-USER-M-PROCESS", "UPSTREAM-DEMO"].includes(row.reason);
const runtimePromotionBlocked = ["L4-USER-M-PROCESS", "L4-PYTHON-REMAP", "UPSTREAM-DEMO"].includes(row.reason);
return [
row.path,
boundary?.ini ?? audit?.ini ?? "-",
@@ -13819,7 +13944,7 @@ function verifyRemainingSkipSimulationImplementationCoverageRows({
.filter((row) => row.inventoryStatus === "SKIP");
const auditByPath = new Map(remainingSkipAuditRows.map((row) => [row.path, row]));
assert.equal(parsedRows.length, 77, "remaining skip simulation implementation coverage count drift");
assert.equal(parsedRows.length, skippedSummaryRows.length, "remaining skip simulation implementation coverage count drift");
assert.deepEqual(
parsedRows.map((row) => row.path).sort(),
skippedSummaryRows.map((row) => row.path).sort(),
@@ -13834,6 +13959,7 @@ function verifyRemainingSkipSimulationImplementationCoverageRows({
),
{
"ASSET-ONLY": 65,
"L4-PYTHON-REMAP": 53,
"L4-USER-M-PROCESS": 1,
"NON_MAIN_CLASS": 10,
"UPSTREAM-DEMO": 1,
@@ -13874,6 +14000,17 @@ function verifyRemainingSkipSimulationImplementationCoverageRows({
"implemented_as_source_derived_boundary_state_proof_runtime_execution_blocked",
`${row.path}: user-M implementation status drift`,
);
} else if (row.skip_kind === "L4-PYTHON-REMAP") {
const audit = auditByPath.get(row.path);
if (row.main_program_class === "1") {
assert.ok(audit, `${row.path}: Python-remap skipped main row must have promotion audit`);
}
assert.equal(row.runtime_promotion_blocked, "1", `${row.path}: Python-remap runtime promotion block drift`);
assert.equal(
row.simulation_implementation_status,
"implemented_as_linuxcnc_python_remap_boundary_proof_runtime_execution_blocked",
`${row.path}: Python-remap implementation status drift`,
);
} else if (row.skip_kind === "UPSTREAM-DEMO") {
const audit = auditByPath.get(row.path);
assert.ok(audit, `${row.path}: upstream demo skipped main row must have promotion audit`);
@@ -14148,6 +14285,7 @@ const runtimeBoundaryPromotionBlockerRecords = verifyRuntimeBoundaryPromotionBlo
const nextBoundaryRecommendationRowsGenerated = nextBoundaryRecommendationRows({
worklistRows: nextBoundaryRecords,
promotionLockRows: blockedRuntimePromotionLockRecords,
promotionReadinessRows: runtimeBoundaryPromotionReadinessRecords,
runtimeContractRows: runtimeBoundaryContractSummaryRecords,
userMRuntimeProbeGateRows: userMProcessNativeRuntimeProbeGateRecords,
toolDbTransactionRows: toolDbProcessTransactionPlanRecords,

View File

@@ -7,7 +7,9 @@ NATIVE_CLASS_SUMMARY="$ROOT_DIR/build/native/sim-configs/class-summary.tsv"
NATIVE_PATH_MATRIX="$ROOT_DIR/build/native/sim-configs/path-matrix.tsv"
NATIVE_SOURCE_PROOF_SUMMARY="$ROOT_DIR/build/native/native-source-proof-summary.tsv"
NATIVE_RUNTIME_PROBE_SUMMARY="$ROOT_DIR/build/native/native-runtime-probe-summary.tsv"
USER_M_RUNTIME_STDOUT="$ROOT_DIR/build/native/linuxcnc_millturn_user_m_runtime_probe.run.stdout.log"
PYTHON_REMAP_LIFECYCLE_STDOUT="$ROOT_DIR/build/native/python-remap-runtime/python_lifecycle.stdout.log"
TOOL_DB_RUNTIME_STDOUT="$ROOT_DIR/build/native/linuxcnc_tool_db_runtime_probe.run.stdout.log"
NATIVE_BUILD_DIR="$ROOT_DIR/build/native/sim-configs"
NATIVE_SOURCE_PROOF_INPUTS=(
"$ROOT_DIR/tools/build_native_probes.sh"
@@ -39,28 +41,76 @@ python_remap_lifecycle_pass_observed() {
grep -Fq "python_remap_runtime_lifecycle_probe_ok=1" "$PYTHON_REMAP_LIFECYCLE_STDOUT"
}
user_m_runtime_state_pass_observed() {
[[ -f "$USER_M_RUNTIME_STDOUT" ]] || return 1
grep -Fq "millturn_user_m_runtime_probe_status=runtime_state_probe_passed" "$USER_M_RUNTIME_STDOUT" &&
grep -Fq "millturn_user_m_M128_switchkins_target_ready=1" "$USER_M_RUNTIME_STDOUT" &&
grep -Fq "millturn_user_m_M128_runtime_state_ok=1" "$USER_M_RUNTIME_STDOUT" &&
grep -Fq "millturn_user_m_M129_switchkins_target_ready=1" "$USER_M_RUNTIME_STDOUT" &&
grep -Fq "millturn_user_m_M129_runtime_state_ok=1" "$USER_M_RUNTIME_STDOUT"
}
tool_db_protocol_pass_observed() {
[[ -f "$TOOL_DB_RUNTIME_STDOUT" ]] || return 1
grep -Fq "tool_db_runtime_probe_status=runtime_protocol_probe_passed" "$TOOL_DB_RUNTIME_STDOUT" &&
grep -Fq "tool_db_protocol_version=v2.1" "$TOOL_DB_RUNTIME_STDOUT" &&
grep -Fq "tool_db_put_tool_update_state_ok=1" "$TOOL_DB_RUNTIME_STDOUT" &&
grep -Fq "tool_db_load_spindle_state_ok=1" "$TOOL_DB_RUNTIME_STDOUT" &&
grep -Fq "tool_db_unload_spindle_state_ok=1" "$TOOL_DB_RUNTIME_STDOUT" &&
grep -Fq "tool_db_persistence_state_ok=1" "$TOOL_DB_RUNTIME_STDOUT"
}
native_runtime_probe_refresh_required() {
python_remap_lifecycle_pass_observed || return 1
if [[ ! -f "$NATIVE_RUNTIME_PROBE_SUMMARY" ]]; then
return 0
fi
if [[ "$PYTHON_REMAP_LIFECYCLE_STDOUT" -nt "$NATIVE_RUNTIME_PROBE_SUMMARY" ]]; then
if python_remap_lifecycle_pass_observed && [[ "$PYTHON_REMAP_LIFECYCLE_STDOUT" -nt "$NATIVE_RUNTIME_PROBE_SUMMARY" ]]; then
return 0
fi
! grep -Fq $'L4-PYTHON-REMAP\tpython_runtime\taxis/remap/stop-lookahead/nc_files\tlinuxcnc_python_remap_runtime_probe\tlinuxcnc_python_runtime_lifecycle_probe_required\tENABLE_PYTHON_REMAP_RUNTIME_PROBE=1\t1\t1\truntime_lifecycle_probe_passed' "$NATIVE_RUNTIME_PROBE_SUMMARY"
if user_m_runtime_state_pass_observed && [[ "$USER_M_RUNTIME_STDOUT" -nt "$NATIVE_RUNTIME_PROBE_SUMMARY" ]]; then
return 0
fi
if tool_db_protocol_pass_observed && [[ "$TOOL_DB_RUNTIME_STDOUT" -nt "$NATIVE_RUNTIME_PROBE_SUMMARY" ]]; then
return 0
fi
if user_m_runtime_state_pass_observed && ! grep -Fq $'L4-USER-M-PROCESS\texternal_user_m_process\taxis/vismach/millturn/example.ngc\tlinuxcnc_millturn_user_m_runtime_probe\tnative_runtime_state_probe_required\tENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1\t1\t1\truntime_state_probe_passed' "$NATIVE_RUNTIME_PROBE_SUMMARY"; then
return 0
fi
if python_remap_lifecycle_pass_observed && ! grep -Fq $'L4-PYTHON-REMAP\tpython_runtime\taxis/remap/stop-lookahead/nc_files\tlinuxcnc_python_remap_runtime_probe\tlinuxcnc_python_runtime_lifecycle_probe_required\tENABLE_PYTHON_REMAP_RUNTIME_PROBE=1\t1\t1\truntime_lifecycle_probe_passed' "$NATIVE_RUNTIME_PROBE_SUMMARY"; then
return 0
fi
if tool_db_protocol_pass_observed && ! grep -Fq $'L4-TOOL-DB\ttool_database_process\taxis/db_demo/base.ngc\tlinuxcnc_tool_db_runtime_probe\tnative_db_process_protocol_probe_required\tENABLE_TOOL_DB_RUNTIME_PROBE=1\t1\t1\truntime_protocol_probe_passed' "$NATIVE_RUNTIME_PROBE_SUMMARY"; then
return 0
fi
return 1
}
build_native_probes_preserving_observed_runtime_proofs() {
local env_args=()
if user_m_runtime_state_pass_observed; then
env_args+=(ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1)
fi
if python_remap_lifecycle_pass_observed; then
env_args+=(ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1)
fi
if tool_db_protocol_pass_observed; then
env_args+=(ENABLE_TOOL_DB_RUNTIME_PROBE=1)
fi
if [[ "${#env_args[@]}" -gt 0 ]]; then
env "${env_args[@]}" "$ROOT_DIR/tools/build_native_probes.sh"
else
"$ROOT_DIR/tools/build_native_probes.sh"
fi
}
if [[ ! -f "$NATIVE_SUMMARY" || ! -f "$NATIVE_CLASS_SUMMARY" || ! -f "$NATIVE_PATH_MATRIX" ]]; then
"$ROOT_DIR/tests/native/verify_sim_configs.sh"
fi
if native_source_proof_refresh_required; then
if python_remap_lifecycle_pass_observed; then
ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 "$ROOT_DIR/tools/build_native_probes.sh"
else
"$ROOT_DIR/tools/build_native_probes.sh"
fi
build_native_probes_preserving_observed_runtime_proofs
elif native_runtime_probe_refresh_required; then
ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 "$ROOT_DIR/tools/build_native_probes.sh"
build_native_probes_preserving_observed_runtime_proofs
fi
mkdir -p "$NATIVE_BUILD_DIR"

View File

@@ -1,6 +1,7 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import {
createLinuxCncToolDbProcessPort,
@@ -12,8 +13,9 @@ import {
createToolDbStorePaths,
} from "../../../runtime/opfs/tool-db-store.js";
const rootDir = resolve(dirname(fileURLToPath(import.meta.url)), "../../..");
const iniText = readFileSync(
resolve("linuxcnc/configs/sim/axis/db_demo/db_nonran.ini"),
resolve(rootDir, "vendor/linuxcnc/configs/sim/axis/db_demo/db_nonran.ini"),
"utf8",
);
assert.match(iniText, /DB_PROGRAM\s*=\s*\.\/db_nonran\.py/);