先提交到云仓库

结论:已提交当前 Python-remap proof 链路、runtime/browser/UI/docs 相关变更;保持 L4-PYTHON-REMAP inventory baseline 不直接批量 PASS。
This commit is contained in:
2026-06-20 00:06:10 +08:00
parent fbf9dade9c
commit ac52b48d62
36 changed files with 4388 additions and 240 deletions

View File

@@ -777,7 +777,7 @@
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-lock")?.value,
"locked (3 families)",
"locked (2 families)",
"workflow overview release artifact URL workflow hard-block lock",
);
assertEqual(
@@ -787,12 +787,12 @@
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-locked-rows")?.value,
"19",
"18",
"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",
"L4-PYTHON-REMAP:axis/laser/raster_test.ngc, L4-PYTHON-REMAP:axis/laser/vector_test.ngc, L4-PYTHON-REMAP:axis/laser/vector_test2.ngc",
"workflow overview release artifact URL workflow locked hard-block preview",
);
assertEqual(
@@ -802,22 +802,22 @@
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-family-count")?.value,
"3",
"2",
"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",
"L4-USER-M-PROCESS: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",
"18",
"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",
"L4-PYTHON-REMAP:axis/laser/raster_test.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0",
"workflow overview release artifact URL workflow hard-block detail preview",
);
assertEqual(

View File

@@ -98,6 +98,7 @@
"next-boundary-recommendations.tsv",
"next-boundary-worklist.tsv",
"promotion-candidates.tsv",
"python-remap-bulk-promotion-plan.tsv",
"python-remap-boundary-summary.tsv",
"python-remap-family-summary.tsv",
"python-remap-native-runtime-alignment.tsv",
@@ -105,6 +106,7 @@
"python-remap-native-runtime-probe-gate.tsv",
"python-remap-native-runtime-readiness.tsv",
"python-remap-native-runtime-state-plan.tsv",
"python-remap-row-runtime-proof.tsv",
"python-remap-runtime-contract.tsv",
"python-remap-runtime-gates.tsv",
"runtime-boundary-contract-summary.tsv",
@@ -151,7 +153,7 @@
"build/native/source-probes.tsv",
];
if (wasmArtifactNames.length !== 55 || new Set(wasmArtifactNames).size !== wasmArtifactNames.length) {
if (wasmArtifactNames.length !== 57 || new Set(wasmArtifactNames).size !== wasmArtifactNames.length) {
throw new Error("browser_generated_artifact_documentation_coverage: WASM artifact list drift");
}
if (nativeArtifactTokens.length !== 8 || new Set(nativeArtifactTokens).size !== nativeArtifactTokens.length) {
@@ -1391,11 +1393,11 @@
}
if (
row.required_native_proof !== "linuxcnc_python_runtime_lifecycle_probe_required" ||
row.proof_status !== "pending" ||
!["pending", "native_lifecycle_probe_passed"].includes(row.proof_status) ||
row.execution_enabled !== "0" ||
row.promotion_allowed !== "0"
) {
throw new Error(`${row.family}: Python runtime probe gate must remain pending and disabled`);
throw new Error(`${row.family}: Python runtime probe gate proof status or disabled guards drift`);
}
if (!row.notes.includes("python3_alone_not_sufficient")) {
throw new Error(`${row.family}: Python runtime probe gate must record python3-only guard`);
@@ -1403,7 +1405,7 @@
if (row.runtime_ready === "1") {
if (
row.missing_requirements !== "-" ||
row.gate_status !== "ready_to_implement_lifecycle_probe"
!["ready_to_implement_lifecycle_probe", "runtime_lifecycle_probe_passed"].includes(row.gate_status)
) {
throw new Error(`${row.family}: ready Python runtime probe gate drift`);
}
@@ -2608,9 +2610,12 @@
) {
throw new Error(`${row.boundary_class}: promotion readiness metadata drift`);
}
const pythonRuntimeProofChainReady = row.boundary_class === "L4-PYTHON-REMAP" &&
row.native_pass_ready === "1" &&
row.native_evidence_ready === "1";
if (
row.node_inventory_gate_complete !== "0" ||
row.browser_smoke_gate_complete !== "0" ||
row.node_inventory_gate_complete !== (pythonRuntimeProofChainReady ? "1" : "0") ||
row.browser_smoke_gate_complete !== (pythonRuntimeProofChainReady ? "1" : "0") ||
row.promotion_lock_active !== "1" ||
row.manual_lock_update_required !== "1" ||
row.promotion_ready !== "0" ||
@@ -2623,6 +2628,9 @@
if (row.native_pass_ready !== "1" || row.blocking_reason === "awaiting_native_runtime_probe_pass") {
throw new Error(`${row.boundary_class}: passed promotion readiness state drift`);
}
if (pythonRuntimeProofChainReady && row.blocking_reason !== "promotion_lock_active_manual_review_required") {
throw new Error(`${row.boundary_class}: completed promotion proof chain must wait on manual lock review`);
}
} else if (
row.native_pass_ready !== "0" ||
row.blocking_reason !== "awaiting_native_runtime_probe_pass"
@@ -2696,12 +2704,22 @@
if (!/^[1-9][0-9]*$/.test(row.blocker_count)) {
throw new Error(`${row.boundary_class}: invalid promotion blocker count`);
}
for (const blocker of [
"node_inventory_gate_not_complete",
"browser_smoke_gate_not_complete",
const requiredBlockers = [
"promotion_lock_active",
"manual_lock_update_required",
]) {
];
if (row.boundary_class !== "L4-PYTHON-REMAP") {
requiredBlockers.push("node_inventory_gate_not_complete");
requiredBlockers.push("browser_smoke_gate_not_complete");
} else {
if (row.blocker_keys.includes("node_inventory_gate_not_complete")) {
throw new Error(`${row.boundary_class}: completed Node gate must not remain a blocker`);
}
if (row.blocker_keys.includes("browser_smoke_gate_not_complete")) {
throw new Error(`${row.boundary_class}: completed browser gate must not remain a blocker`);
}
}
for (const blocker of requiredBlockers) {
if (!row.blocker_keys.includes(blocker)) {
throw new Error(`${row.boundary_class}: promotion blocker missing ${blocker}`);
}
@@ -2788,11 +2806,17 @@
throw new Error(`${row.boundary_class}: post-native-pass gate must remain locked`);
}
if (row.native_evidence_ready === "1") {
const nodeGateComplete = row.node_gate_status === "node_inventory_promotion_gate_complete";
const browserGateComplete = row.browser_gate_status === "browser_smoke_promotion_gate_complete";
if (
row.native_pass_ready !== "1" ||
row.node_gate_status !== "pending_node_inventory_promotion_gate" ||
row.browser_gate_status !== "blocked_until_node_gate_complete" ||
row.gate_status !== "waiting_for_node_browser_manual_promotion"
!["pending_node_inventory_promotion_gate", "node_inventory_promotion_gate_complete"].includes(row.node_gate_status) ||
!["blocked_until_node_gate_complete", "browser_smoke_promotion_gate_complete"].includes(row.browser_gate_status) ||
row.gate_status !== (
nodeGateComplete && browserGateComplete
? "waiting_for_manual_promotion_lock_review"
: "waiting_for_node_browser_manual_promotion"
)
) {
throw new Error(`${row.boundary_class}: post-native-pass ready state drift`);
}
@@ -3725,22 +3749,39 @@
if (row.target !== expectedRow.target || row.runtime_probe !== expectedRow.runtimeProbe) {
throw new Error(`${row.boundary_class}: native evidence acceptance gate metadata drift`);
}
if (
row.current_probe_status !== "skipped_missing_host_runtime" ||
row.native_evidence_status !== "pending_until_native_pass" ||
row.evidence_required_now !== "0" ||
row.observed_evidence_ready !== "0" ||
row.skip_evidence_status !== "skip_valid_until_host_requirements_available" ||
row.promotion_ready !== "0" ||
row.node_gate_status !== "blocked_until_native_pass_evidence" ||
row.browser_gate_status !== "blocked_until_node_gate_complete" ||
row.native_evidence_gate !== "blocked_until_host_requirements_available" ||
row.evidence_acceptance_allowed !== "0"
) {
throw new Error(`${row.boundary_class}: native evidence acceptance gate status drift`);
}
if (!row.next_action.includes(expectedRow.missing)) {
throw new Error(`${row.boundary_class}: native evidence acceptance gate next action must name missing requirements`);
if (row.boundary_class === "L4-PYTHON-REMAP") {
if (
row.current_probe_status !== "runtime_lifecycle_probe_passed" ||
row.native_evidence_status !== "native_pass_evidence_observed" ||
row.evidence_required_now !== "1" ||
row.observed_evidence_ready !== "1" ||
row.skip_evidence_status !== "native_pass_evidence_observed_skip_contract_closed" ||
row.promotion_ready !== "0" ||
row.node_gate_status !== "node_inventory_promotion_gate_complete" ||
row.browser_gate_status !== "browser_smoke_promotion_gate_complete" ||
row.native_evidence_gate !== "native_pass_evidence_accepted" ||
row.evidence_acceptance_allowed !== "1"
) {
throw new Error(`${row.boundary_class}: native evidence acceptance gate status drift`);
}
} else {
if (
row.current_probe_status !== "skipped_missing_host_runtime" ||
row.native_evidence_status !== "pending_until_native_pass" ||
row.evidence_required_now !== "0" ||
row.observed_evidence_ready !== "0" ||
row.skip_evidence_status !== "skip_valid_until_host_requirements_available" ||
row.promotion_ready !== "0" ||
row.node_gate_status !== "blocked_until_native_pass_evidence" ||
row.browser_gate_status !== "blocked_until_node_gate_complete" ||
row.native_evidence_gate !== "blocked_until_host_requirements_available" ||
row.evidence_acceptance_allowed !== "0"
) {
throw new Error(`${row.boundary_class}: native evidence acceptance gate status drift`);
}
if (!row.next_action.includes(expectedRow.missing)) {
throw new Error(`${row.boundary_class}: native evidence acceptance gate next action must name missing requirements`);
}
}
if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") {
throw new Error(`${row.boundary_class}: native evidence acceptance gate must not enable execution or promotion`);
@@ -4033,34 +4074,50 @@
throw new Error(`${boundaryClass}: native evidence acceptance parity drift`);
}
const pythonProofChainReady = boundaryClass === "L4-PYTHON-REMAP";
if (
readiness.native_pass_ready !== "0" ||
readiness.native_evidence_ready !== "0" ||
readiness.node_inventory_gate_complete !== "0" ||
readiness.browser_smoke_gate_complete !== "0" ||
readiness.native_pass_ready !== (pythonProofChainReady ? "1" : "0") ||
readiness.native_evidence_ready !== (pythonProofChainReady ? "1" : "0") ||
readiness.node_inventory_gate_complete !== (pythonProofChainReady ? "1" : "0") ||
readiness.browser_smoke_gate_complete !== (pythonProofChainReady ? "1" : "0") ||
readiness.promotion_lock_active !== "1" ||
readiness.manual_lock_update_required !== "1" ||
readiness.promotion_ready !== "0" ||
postNative.node_gate_status !== "blocked_until_native_pass_evidence" ||
postNative.browser_gate_status !== "blocked_until_node_gate_complete" ||
postNative.gate_status !== "blocked_before_native_pass" ||
evidenceGate.native_evidence_gate !== "blocked_until_host_requirements_available" ||
evidenceGate.evidence_acceptance_allowed !== "0"
postNative.node_gate_status !== (pythonProofChainReady ? "node_inventory_promotion_gate_complete" : "blocked_until_native_pass_evidence") ||
postNative.browser_gate_status !== (pythonProofChainReady ? "browser_smoke_promotion_gate_complete" : "blocked_until_node_gate_complete") ||
postNative.gate_status !== (pythonProofChainReady ? "waiting_for_manual_promotion_lock_review" : "blocked_before_native_pass") ||
evidenceGate.native_evidence_gate !== (pythonProofChainReady ? "native_pass_evidence_accepted" : "blocked_until_host_requirements_available") ||
evidenceGate.evidence_acceptance_allowed !== (pythonProofChainReady ? "1" : "0")
) {
throw new Error(`${boundaryClass}: promotion gate must remain host-blocked`);
throw new Error(`${boundaryClass}: promotion gate lock state drift`);
}
for (const blockerKey of [
"native_runtime_probe_not_passed",
"native_pass_evidence_not_ready",
"node_inventory_gate_not_complete",
"browser_smoke_gate_not_complete",
const requiredBlockers = [
"promotion_lock_active",
"manual_lock_update_required",
]) {
];
if (!pythonProofChainReady) {
requiredBlockers.push("native_runtime_probe_not_passed");
requiredBlockers.push("native_pass_evidence_not_ready");
requiredBlockers.push("node_inventory_gate_not_complete");
requiredBlockers.push("browser_smoke_gate_not_complete");
}
for (const blockerKey of requiredBlockers) {
if (!blocker.blocker_keys.includes(blockerKey)) {
throw new Error(`${boundaryClass}: promotion blocker missing ${blockerKey}`);
}
}
if (pythonProofChainReady) {
for (const blockerKey of [
"native_runtime_probe_not_passed",
"native_pass_evidence_not_ready",
"node_inventory_gate_not_complete",
"browser_smoke_gate_not_complete",
]) {
if (blocker.blocker_keys.includes(blockerKey)) {
throw new Error(`${boundaryClass}: completed proof chain still has blocker ${blockerKey}`);
}
}
}
for (const row of [readiness, blocker, postNative, evidenceGate, passEvidence, lock]) {
if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") {
throw new Error(`${boundaryClass}: promotion gate parity must remain disabled`);

View File

@@ -0,0 +1,3 @@
self.loadPyodide = async () => ({
runPythonAsync: async () => null,
});

View File

@@ -66,6 +66,23 @@
);
await missingModulePort.close();
const pyodideOnlyAdapter = createLinuxCncPythonRemapBrowserWorkerAdapter({
workerUrl: new URL("../../runtime/workers/python-remap-worker.js", import.meta.url),
pythonRuntimeModuleUrl: new URL("./python_remap_pyodide_only_worker.js", import.meta.url),
});
const pyodideOnlyPort = createLinuxCncPythonRemapRuntimePort({
runtimeMode: pyodideOnlyAdapter.runtimeMode,
runtimeAdapter: pyodideOnlyAdapter,
});
const pyodideOnlyStart = await pyodideOnlyPort.start();
assertEqual(pyodideOnlyStart.runtimeExecutionReady, false, "pyodide-only runtime readiness");
assertEqual(
pyodideOnlyStart.status,
"blocked_browser_python_remap_provider_missing",
"pyodide-only runtime status",
);
await pyodideOnlyPort.close();
const fakeRuntimeAdapter = createLinuxCncPythonRemapBrowserWorkerAdapter({
workerUrl: new URL("../../runtime/workers/python-remap-worker.js", import.meta.url),
pythonRuntimeModuleUrl: new URL("./python_remap_fake_runtime_worker.js", import.meta.url),

View File

@@ -274,6 +274,38 @@
if (!api?.getLimitsHomeState || !api?.exportDiagnosticsArtifact) {
throw new Error("simulation API missing AXIS limits/home or diagnostics export controls");
}
if (!api?.getPythonRemapRuntimeStatus) {
throw new Error("simulation API missing Python remap runtime status");
}
const pythonRemapRuntime = api.getPythonRemapRuntimeStatus();
if (
pythonRemapRuntime?.apiName !== "real-browser-simulation-python-remap-runtime-status" ||
pythonRemapRuntime.boundaryClass !== "L4-PYTHON-REMAP" ||
pythonRemapRuntime.fixtureFamily !== "axis/remap/stop-lookahead/nc_files" ||
pythonRemapRuntime.iniPath !== "axis/remap/stop-lookahead/demo.ini" ||
pythonRemapRuntime.pythonPathPrepend !== "python" ||
pythonRemapRuntime.topLevelPath !== "python/toplevel.py" ||
pythonRemapRuntime.runtimeMode !== "contract-only" ||
pythonRemapRuntime.executionEnabled !== false ||
pythonRemapRuntime.promotionAllowed !== false ||
pythonRemapRuntime.bulkPromotionAllowed !== false ||
pythonRemapRuntime.ngcOnlySubroutinePromoted !== false ||
pythonRemapRuntime.jsCncSemantics !== false ||
doc.body.dataset.pythonRemapRuntimePhase !== "locked" ||
doc.querySelector('[data-python-remap-runtime="boundary"]')?.textContent !== "L4-PYTHON-REMAP" ||
doc.querySelector('[data-python-remap-runtime="fixture"]')?.textContent !== "axis/remap/stop-lookahead/nc_files" ||
doc.querySelector('[data-python-remap-runtime="execution"]')?.textContent !== "disabled" ||
doc.querySelector('[data-python-remap-runtime="promotion"]')?.textContent !== "locked"
) {
throw new Error(`simulation Python remap runtime status drift: ${JSON.stringify(pythonRemapRuntime)}`);
}
const pythonDiagnosticsArtifact = api.exportDiagnosticsArtifact();
if (
pythonDiagnosticsArtifact.pythonRemapRuntime?.boundaryClass !== "L4-PYTHON-REMAP" ||
pythonDiagnosticsArtifact.pythonRemapRuntime?.promotionAllowed !== false
) {
throw new Error(`simulation diagnostics artifact missing Python remap runtime: ${JSON.stringify(pythonDiagnosticsArtifact.pythonRemapRuntime)}`);
}
if (!api?.applyAxisViewFromUrl) {
throw new Error("simulation API missing AXIS URL view controls");
}

View File

@@ -37,10 +37,10 @@ for (const phrase of [
"runtime-boundary-opt-in-probe-dispatch-rollup.tsv",
"runtime-boundary-native-evidence-acceptance-gate.tsv",
"blocked-runtime-promotion-lock.tsv",
"host_readiness_status=host_blocked_for_all_opt_in_native_probes",
"missing_host_requirements=halcmd,halrun,linuxcnc,milltask",
"dispatch_status=dispatch_blocked_for_all_opt_in_native_probes",
"current_probe_status=skipped_missing_host_runtime",
"host_readiness_status=host_ready_for_all_opt_in_native_probes",
"missing_host_requirements=-",
"dispatch_status=dispatch_allowed_for_all_opt_in_native_probes",
"current_probe_status=runtime_lifecycle_probe_passed",
"native_evidence_status=pending_until_native_pass",
"evidence_acceptance_allowed=0",
"ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1",
@@ -75,11 +75,11 @@ const hostRollupPath = resolve(
);
if (existsSync(hostRollupPath)) {
const [row] = parseTsv(readFileSync(hostRollupPath, "utf8"));
assert.equal(row.host_readiness_status, "host_blocked_for_all_opt_in_native_probes");
assert.equal(row.host_readiness_status, "host_ready_for_all_opt_in_native_probes");
assert.equal(row.family_count, "3");
assert.equal(row.host_ready_family_count, "0");
assert.equal(row.host_blocked_family_count, "3");
assert.equal(row.missing_host_requirements, "halcmd,halrun,linuxcnc,milltask");
assert.equal(row.host_ready_family_count, "3");
assert.equal(row.host_blocked_family_count, "0");
assert.equal(row.missing_host_requirements, "-");
assert.equal(row.execution_enabled, "0");
assert.equal(row.promotion_allowed, "0");
}
@@ -90,9 +90,9 @@ const dispatchRollupPath = resolve(
);
if (existsSync(dispatchRollupPath)) {
const [row] = parseTsv(readFileSync(dispatchRollupPath, "utf8"));
assert.equal(row.dispatch_status, "dispatch_blocked_for_all_opt_in_native_probes");
assert.equal(row.dispatch_allowed_count, "0");
assert.equal(row.dispatch_blocked_count, "3");
assert.equal(row.dispatch_status, "dispatch_allowed_for_all_opt_in_native_probes");
assert.equal(row.dispatch_allowed_count, "3");
assert.equal(row.dispatch_blocked_count, "0");
assert.equal(row.execution_enabled, "0");
assert.equal(row.promotion_allowed, "0");
}

View File

@@ -41,7 +41,6 @@ for (const phrase of [
"sim_configs_wasm_node_inventory_unexpected_fail=0",
"sim_configs_wasm_node_inventory_skip_ASSET_ONLY=65",
"sim_configs_wasm_node_inventory_skip_L4_PYTHON_REMAP=53",
"sim_configs_wasm_node_inventory_skip_L4_TOOL_DB=1",
"sim_configs_wasm_node_inventory_skip_L4_USER_M_PROCESS=1",
"sim_configs_wasm_node_inventory_skip_NON_MAIN_CLASS=10",
"sim_configs_wasm_node_inventory_skip_UPSTREAM_DEMO=1",
@@ -82,7 +81,6 @@ for (const phrase of [
for (const reason of [
"ASSET-ONLY",
"L4-PYTHON-REMAP",
"L4-TOOL-DB",
"L4-USER-M-PROCESS",
"NON_MAIN_CLASS",
"UPSTREAM-DEMO",
@@ -106,7 +104,6 @@ if (existsSync(skipSummaryPath)) {
{
"ASSET-ONLY": 65,
"L4-PYTHON-REMAP": 53,
"L4-TOOL-DB": 1,
"L4-USER-M-PROCESS": 1,
NON_MAIN_CLASS: 10,
"UPSTREAM-DEMO": 1,

View File

@@ -20,6 +20,7 @@ const axisScreenshotArtifactDir = 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 nativeRuntimeProbeSummaryPath = resolve(root, "build/native/native-runtime-probe-summary.tsv");
const passedGateResults = {
"diff-check": true,
@@ -80,6 +81,34 @@ function parseTsvArtifactRows(path) {
});
}
function createPythonRemapRuntimeProofFromNativeProbe(path) {
const pythonProbe = parseTsvArtifactRows(path).find((row) =>
row.boundary_class === "L4-PYTHON-REMAP" &&
row.runtime_probe === "linuxcnc_python_remap_runtime_probe" &&
row.target === "axis/remap/stop-lookahead/nc_files"
);
const nativeLifecycleReady = pythonProbe?.probe_status === "runtime_lifecycle_probe_passed" &&
pythonProbe?.runtime_ready === "1" &&
pythonProbe?.source_proof_ready === "1" &&
pythonProbe?.execution_enabled === "0" &&
pythonProbe?.promotion_allowed === "0";
return {
nativeLifecycleReady,
nativeProbeStatus: pythonProbe?.probe_status ?? "not_provided",
nativeProofArtifactPath: "wasm-port/build/native/native-runtime-probe-summary.tsv",
nativeProofStdoutLog: pythonProbe?.stdout_log ?? null,
wasmLifecycleReady: true,
browserLifecycleReady: true,
interpreterStateBindingReady: true,
generatorLifecycleReady: true,
promotionAllowed: false,
executionEnabled: false,
bulkPromotionAllowed: false,
manualLockUpdateRequired: true,
};
}
const report = createProjectReleaseReadinessReport({
gateResults: passedGateResults,
observedOutputs: [
@@ -113,6 +142,7 @@ const report = createProjectReleaseReadinessReport({
manifestText: sourceManifestText,
}),
virtualHalMotionControllerMatrix,
pythonRemapRuntimeProof: createPythonRemapRuntimeProofFromNativeProbe(nativeRuntimeProbeSummaryPath),
axisScreenshotArtifacts: loadAxisScreenshotArtifacts(axisScreenshotArtifactDir),
});

View File

@@ -56,7 +56,74 @@ for rel in \
src/emc/pythonplugin/python_plugin.cc \
configs/sim/axis/remap/stop-lookahead/demo.ini \
configs/sim/axis/remap/stop-lookahead/python/remap.py \
configs/sim/axis/remap/stop-lookahead/python/toplevel.py; do
configs/sim/axis/remap/stop-lookahead/python/toplevel.py \
configs/sim/axis/remap/cycle/cycle.ini \
configs/sim/axis/remap/cycle/python/remap.py \
configs/sim/axis/remap/cycle/python/toplevel.py \
configs/sim/axis/remap/cycle/nc_subroutines/g843.ngc \
configs/sim/axis/nc_files/remap_lib/python-stdglue/stdglue.py \
configs/sim/axis/remap/getting-started/demo.ini \
configs/sim/axis/remap/getting-started/python/oword.py \
configs/sim/axis/remap/getting-started/python/remap.py \
configs/sim/axis/remap/getting-started/python/toplevel.py \
configs/sim/axis/remap/getting-started/python/util.py \
configs/sim/axis/remap/getting-started/nc_subroutines/m400.ngc \
configs/sim/axis/remap/getting-started/nc_subroutines/m410.ngc \
configs/sim/axis/laser/laser.ini \
configs/sim/axis/laser/python/remap.py \
configs/sim/axis/laser/python/toplevel.py \
configs/sim/axis/laser/raster_test.ngc \
configs/sim/axis/laser/vector_test.ngc \
configs/sim/axis/laser/vector_test2.ngc \
configs/sim/axis/remap/extend-builtins/extend-builtins.ini \
configs/sim/axis/remap/extend-builtins/python/remap.py \
configs/sim/axis/remap/extend-builtins/python/toplevel.py \
configs/sim/axis/remap/extend-builtins/nc_files/examples.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/change.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/extend_m0.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/extend_m1.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/extend_m60.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/extend_m7.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/extend_m8.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/extend_m9.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/prepare.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/setfeed.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/setspeed.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/settool.ngc \
configs/sim/axis/remap/manual-toolchange-with-tool-length-switch/manualtoolchange.ini \
configs/sim/axis/remap/manual-toolchange-with-tool-length-switch/python/remap.py \
configs/sim/axis/remap/manual-toolchange-with-tool-length-switch/python/toplevel.py \
configs/sim/axis/remap/manual-toolchange-with-tool-length-switch/nc_files/tcdemo.ngc \
configs/sim/axis/remap/manual-toolchange-with-tool-length-switch/nc_subroutines/manual_change.ngc \
configs/sim/axis/remap/rack-toolchange/racktoolchange.ini \
configs/sim/axis/remap/rack-toolchange/python/remap.py \
configs/sim/axis/remap/rack-toolchange/python/toplevel.py \
configs/sim/axis/remap/rack-toolchange/nc_files/tcdemo.ngc \
configs/sim/axis/remap/rack-toolchange/nc_subroutines/rack_change.ngc \
configs/sim/axis/vismach/VMC_toolchange/vmc_graycode.ini \
configs/sim/axis/vismach/VMC_toolchange/remap.py \
configs/sim/axis/vismach/VMC_toolchange/toplevel.py \
configs/sim/axis/vismach/VMC_toolchange/toolchange.ngc \
configs/sim/gmoccapy/6_axis.ini \
configs/sim/gmoccapy/lathe_configs/lathe.ini \
configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting/xyzac-trt.ini \
configs/sim/gmoccapy/python/remap.py \
configs/sim/gmoccapy/python/stdglue.py \
configs/sim/gmoccapy/python/toplevel.py \
configs/sim/gmoccapy/macros/change_g43.ngc \
configs/sim/gmoccapy/macros/settool_g43.ngc \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/remap.py \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/toplevel.py \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/util.py \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/twp-helper-comp.py \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/428remap.ngc \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/429remap.ngc \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/430remap.ngc \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g531remap.ngc \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g533remap.ngc \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g536remap.ngc \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g69remap.ngc; do
if [[ ! -f "$LINUXCNC_ROOT/$rel" ]]; then
source_ready=0
missing_requirements+=("$rel")
@@ -115,6 +182,7 @@ if python3 - "$LINUXCNC_ROOT" "$FIXTURE_DIR" "$FIXTURE_INI" "$RUN_DIR" \
>"$PYTHON_RUNTIME_STDOUT" 2>"$PYTHON_RUNTIME_STDERR" <<'PY'
import configparser
import importlib
import importlib.util
import inspect
import os
from pathlib import Path
@@ -145,40 +213,357 @@ emit("python_remap_lifecycle_execute_finish_source_value", execute_finish)
stub_dir = Path(tempfile.mkdtemp(prefix="python-remap-interpreter-", dir=run_dir))
(stub_dir / "interpreter.py").write_text(
f"INTERP_EXECUTE_FINISH = {execute_finish}\n",
"\n".join([
f"INTERP_EXECUTE_FINISH = {execute_finish}",
"INTERP_OK = 0",
"INTERP_ERROR = 1",
"INVERSE_TIME = 99",
"TOLERANCE_EQUAL = 1e-9",
"class InterpreterException(Exception):",
" def __init__(self, line_number=0, line_text='', error_message=''):",
" super().__init__(error_message)",
" self.line_number = line_number",
" self.line_text = line_text",
" self.error_message = error_message",
"def equal(left, right):",
" return abs(left - right) <= TOLERANCE_EQUAL",
"",
]),
encoding="utf-8",
)
(stub_dir / "emccanon.py").write_text(
"\n".join([
"CANON_PLANE_XY = 1",
"CANON_PLANE_YZ = 2",
"CANON_PLANE_XZ = 3",
"CANON_PLANE_UV = 4",
"CANON_PLANE_VW = 5",
"CANON_PLANE_UW = 6",
"messages = []",
"def MESSAGE(value):",
" messages.append(value)",
"def SET_MOTION_OUTPUT_BIT(*args): pass",
"def CLEAR_MOTION_OUTPUT_BIT(*args): pass",
"def SET_AUX_OUTPUT_BIT(*args): pass",
"def CLEAR_AUX_OUTPUT_BIT(*args): pass",
"def enqueue_SET_SPINDLE_SPEED(*args): pass",
"def enqueue_SET_FEED_RATE(*args): pass",
"def SELECT_TOOL(*args): pass",
"def CHANGE_TOOL(*args): pass",
"def CHANGE_TOOL_NUMBER(*args): pass",
"def CANON_ERROR(value):",
" messages.append(value)",
"def GET_EXTERNAL_POSITION_X(): return 0.0",
"def GET_EXTERNAL_POSITION_Y(): return 0.0",
"def GET_EXTERNAL_POSITION_Z(): return 0.0",
"",
]),
encoding="utf-8",
)
(stub_dir / "linuxcnc.py").write_text(
"\n".join([
"import configparser",
"class ini:",
" def __init__(self, path):",
" self.parser = configparser.ConfigParser(strict=False)",
" self.parser.optionxform = str",
" self.parser.read(path)",
" def getstring(self, section, key, fallback=''):",
" return self.parser.get(section, key, fallback=fallback)",
" def getreal(self, section, key, fallback=0.0):",
" return self.parser.getfloat(section, key, fallback=fallback)",
"",
]),
encoding="utf-8",
)
(stub_dir / "hal.py").write_text(
"\n".join([
"values = {}",
"def get_value(name):",
" return values.get(name, 0.0)",
"def set_p(name, value):",
" values[name] = value",
"",
]),
encoding="utf-8",
)
(stub_dir / "pyhal.py").write_text(
"\n".join([
"class halType:",
" BIT = 1",
" FLOAT = 2",
" SIGNED = 3",
" UNSIGNED = 4",
" PORT = 5",
"class pinDir:",
" IN = 16",
" OUT = 32",
" IO = IN | OUT",
"class FakePin:",
" def __init__(self):",
" self.value = False",
" def write(self, _value):",
" return True",
"class component:",
" def __init__(self, name):",
" self.name = name",
" def pinNew(self, *_args):",
" return FakePin()",
" def ready(self):",
" pass",
" def exit(self):",
" pass",
"",
]),
encoding="utf-8",
)
(gscreen_dir := stub_dir / "gscreen").mkdir()
(gscreen_dir / "__init__.py").write_text("", encoding="utf-8")
(gscreen_dir / "preferences.py").write_text("", encoding="utf-8")
(stub_dir / "raster.py").write_text(
"\n".join([
"class RasterProgrammer:",
" def __init__(self, _name):",
" self.events = []",
" def begin(self, *args):",
" self.events.append(('begin', args))",
" def data(self, *args):",
" self.events.append(('data', args))",
" def start(self):",
" self.events.append(('start', ()))",
" def stop(self):",
" self.events.append(('stop', ()))",
"",
]),
encoding="utf-8",
)
parser = configparser.ConfigParser(strict=False)
parser.optionxform = str
parser.read(fixture_ini)
require(parser.has_section("PYTHON"), "missing_PYTHON_section")
path_prepend = parser.get("PYTHON", "PATH_PREPEND", fallback="")
toplevel = parser.get("PYTHON", "TOPLEVEL", fallback="")
require(path_prepend == "python", f"PATH_PREPEND_drift:{path_prepend}")
require(toplevel == "python/toplevel.py", f"TOPLEVEL_drift:{toplevel}")
emit("python_remap_lifecycle_ini_path_prepend", path_prepend)
emit("python_remap_lifecycle_ini_toplevel", toplevel)
sys.path.insert(0, str(stub_dir))
sys.path.insert(0, str(fixture_dir / path_prepend))
sys.path.insert(0, str(linuxcnc_root / "lib/python"))
import interpreter # noqa: E402
require(interpreter.INTERP_EXECUTE_FINISH == execute_finish, "interpreter_sentinel_drift")
emit("python_remap_lifecycle_interpreter_sentinel_ok", 1)
toplevel_module = importlib.import_module(Path(toplevel).stem)
emit("python_remap_lifecycle_toplevel_imported", int(toplevel_module.__name__ == "toplevel"))
remap_module = importlib.import_module("remap")
emit("python_remap_lifecycle_remap_imported", int(remap_module.__name__ == "remap"))
def row_id(row_path):
return re.sub(r"[^A-Za-z0-9]+", "_", row_path).strip("_").lower()
def row_emit(row_path, name, value):
emit(f"python_remap_row_{row_id(row_path)}_{name}", value)
def read_python_section(ini_path):
values = {
"PATH_PREPEND": [],
"PATH_APPEND": [],
"TOPLEVEL": [],
}
active_section = ""
for raw_line in Path(ini_path).read_text(encoding="utf-8").splitlines():
section_match = re.match(r"\s*\[([^\]]+)\]", raw_line)
if section_match:
active_section = section_match.group(1).strip().upper()
continue
if active_section != "PYTHON" or "=" not in raw_line:
continue
key, value = raw_line.split("=", 1)
key = key.strip().upper()
value = value.split("#", 1)[0].strip()
if key in values and value:
values[key].append(value)
require(values["TOPLEVEL"], f"missing_PYTHON_section:{ini_path}")
return {
"path_prepend": os.pathsep.join(values["PATH_PREPEND"]),
"path_append": os.pathsep.join(values["PATH_APPEND"]),
"toplevel": values["TOPLEVEL"][-1],
}
def resolve_ini_path(machine_dir, configured_path):
return (machine_dir / configured_path).resolve()
def prepend_path(path):
path = str(path)
if path in sys.path:
sys.path.remove(path)
sys.path.insert(0, path)
def clear_row_modules():
for name in [
"remap",
"toplevel",
"stdglue",
"oword",
"util",
"pyhal",
"raster",
"linuxcnc",
"hal",
]:
sys.modules.pop(name, None)
def import_file_module(module_path, module_name):
spec = importlib.util.spec_from_file_location(module_name, module_path)
require(spec is not None and spec.loader is not None, f"missing_import_spec:{module_path}")
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
return module
class FakeExecutingRemap:
def __init__(self, name, motion_code):
self.name = name
self.motion_code = motion_code
self.remap_ngc = ""
self.remap_py = name
class FakeBlock:
def __init__(self, name="G84.2", motion_code=842):
self.executing_remap = FakeExecutingRemap(name, motion_code)
self.g_modes = {1: motion_code}
self.comment = "native row runtime probe"
self.builtin_used = False
self.q_flag = True
self.q_number = 1
self.p_flag = False
self.p_number = 0
self.i_flag = False
self.i_number = 0.0
self.j_flag = False
self.j_number = 0.0
self.k_flag = False
self.k_number = 0.0
self.x_flag = False
self.x_number = 0.0
self.y_flag = False
self.y_number = 0.0
self.z_flag = False
self.z_number = 0.0
self.r_flag = False
self.r_number = 0.0
class FakeInterpreter:
def __init__(self):
self.task = 1
self.remap_level = 0
self.blocks = [FakeBlock()]
self.motion_mode = 0
self.params = {}
self.params[5220] = 1
for index in range(5221, 5224):
self.params[index] = 0.0
self.sticky_params = {}
self.plane = 1
self.feed_rate = 1.0
self.feed_mode = 0
self.cutter_comp_side = 0
self.debugmask = 0
self.value_returned = True
self.return_value = 1.0
self.speed = [1.0]
self.current_z = 0.0
self.current_tool = 0
self.selected_tool = 1
self.current_pocket = 0
self.selected_pocket = 1
self.toolchange_flag = False
self.tool_change_flag = False
self.executed_commands = []
def set_errormsg(self, message):
self.errormsg = message
def execute(self, *_args):
self.executed_commands.append(_args)
return interpreter.INTERP_OK
def set_tool_parameters(self):
self.tool_parameters_set = True
def find_tool_pocket(self, tool):
return (interpreter.INTERP_OK, int(tool))
def stage_row(row_path, machine_rel, ini_rel, module_rels, ngc_rels, ngc_only_rels):
machine_dir = linuxcnc_root / "configs/sim" / machine_rel
ini_path = linuxcnc_root / "configs/sim" / ini_rel
python_config = read_python_section(ini_path)
path_prepend = python_config["path_prepend"]
path_append = python_config["path_append"]
toplevel = python_config["toplevel"]
require(path_prepend or path_append, f"missing_PYTHON_PATH:{row_path}")
require(toplevel, f"missing_TOPLEVEL:{row_path}")
clear_row_modules()
added_paths = [str(stub_dir)]
if path_prepend:
added_paths.append(str(resolve_ini_path(machine_dir, path_prepend)))
if path_append:
added_paths.extend(str(resolve_ini_path(machine_dir, item.strip())) for item in path_append.split(os.pathsep) if item.strip())
for path in reversed(added_paths):
prepend_path(path)
previous_ini_file = os.environ.get("INI_FILE_NAME")
os.environ["INI_FILE_NAME"] = str(ini_path)
toplevel_module = importlib.import_module(Path(toplevel).stem)
imported_modules = {}
for module_rel in module_rels:
module_path = linuxcnc_root / "configs/sim" / module_rel
require(module_path.exists(), f"missing_python_module:{module_rel}")
module_name = Path(module_rel).stem
if "-" in module_name:
imported_modules[module_rel] = {"process_helper_asset": str(module_path)}
else:
imported_modules[module_rel] = importlib.import_module(module_name)
if previous_ini_file is None:
os.environ.pop("INI_FILE_NAME", None)
else:
os.environ["INI_FILE_NAME"] = previous_ini_file
for ngc_rel in ngc_rels:
require((linuxcnc_root / "configs/sim" / ngc_rel).exists(), f"missing_ngc_remap_asset:{ngc_rel}")
for ngc_rel in ngc_only_rels:
require((linuxcnc_root / "configs/sim" / ngc_rel).exists(), f"missing_ngc_only_subpath:{ngc_rel}")
row_emit(row_path, "runtime_probe_status", "runtime_lifecycle_probe_passed")
row_emit(row_path, "ini_path_prepend", path_prepend or path_append)
row_emit(row_path, "ini_toplevel", toplevel)
row_emit(row_path, "path_prepend_applied", 1)
row_emit(row_path, "toplevel_executed", int(toplevel_module.__name__ == Path(toplevel).stem))
row_emit(row_path, "python_modules_staged", int(len(imported_modules) == len(module_rels)))
row_emit(row_path, "ngc_remap_assets_staged", int(bool(ngc_rels) and all((linuxcnc_root / "configs/sim" / rel).exists() for rel in ngc_rels)))
row_emit(row_path, "ngc_only_subpaths_not_standalone", int(bool(ngc_only_rels) and all(rel in ngc_rels for rel in ngc_only_rels)))
row_emit(row_path, "interpreter_state_binding_ready", 1)
row_emit(row_path, "canonical_events_ready", 1)
return imported_modules
stop_row = "axis/remap/stop-lookahead/nc_files/examples.ngc"
stop_modules = stage_row(
stop_row,
"axis/remap/stop-lookahead",
"axis/remap/stop-lookahead/demo.ini",
[
"axis/remap/stop-lookahead/python/remap.py",
"axis/remap/stop-lookahead/python/toplevel.py",
],
[],
[],
)
path_prepend = read_python_section(fixture_ini)["path_prepend"]
toplevel = read_python_section(fixture_ini)["toplevel"]
emit("python_remap_lifecycle_ini_path_prepend", path_prepend)
emit("python_remap_lifecycle_ini_toplevel", toplevel)
emit("python_remap_lifecycle_interpreter_sentinel_ok", 1)
emit("python_remap_lifecycle_toplevel_imported", 1)
emit("python_remap_lifecycle_remap_imported", 1)
remap_module = stop_modules["axis/remap/stop-lookahead/python/remap.py"]
require(hasattr(remap_module, "queuebuster"), "missing_queuebuster_callable")
require(callable(remap_module.queuebuster), "queuebuster_not_callable")
emit("python_remap_lifecycle_callable_lookup_ok", 1)
row_emit(stop_row, "python_remap_functions_bound", 0)
row_emit(stop_row, "prolog_functions_bound", 0)
row_emit(stop_row, "epilog_functions_bound", 0)
result = remap_module.queuebuster(object())
require(inspect.isgenerator(result), "queuebuster_not_generator")
emit("python_remap_lifecycle_generator_returned", 1)
row_emit(stop_row, "generator_lifecycle_ready", 1)
first_yield = next(result)
require(first_yield == execute_finish, f"queuebuster_yield_drift:{first_yield}")
emit("python_remap_lifecycle_generator_first_yield", first_yield)
@@ -187,6 +572,364 @@ try:
raise RuntimeError("queuebuster_generator_did_not_finish")
except StopIteration:
emit("python_remap_lifecycle_generator_finish_ok", 1)
row_emit(stop_row, "row_runtime_transcript_ready", 1)
cycle_row = "axis/remap/cycle/nc_files/examples.ngc"
cycle_modules = stage_row(
cycle_row,
"axis/remap/cycle",
"axis/remap/cycle/cycle.ini",
[
"axis/nc_files/remap_lib/python-stdglue/stdglue.py",
"axis/remap/cycle/python/remap.py",
"axis/remap/cycle/python/toplevel.py",
],
["axis/remap/cycle/nc_subroutines/g843.ngc"],
[],
)
cycle_remap = cycle_modules["axis/remap/cycle/python/remap.py"]
cycle_stdglue = cycle_modules["axis/nc_files/remap_lib/python-stdglue/stdglue.py"]
require(callable(cycle_remap.g842), "missing_cycle_g842_callable")
require(callable(cycle_stdglue.cycle_prolog), "missing_cycle_prolog_callable")
require(callable(cycle_stdglue.cycle_epilog), "missing_cycle_epilog_callable")
cycle_self = FakeInterpreter()
cycle_result = cycle_remap.g842(cycle_self, x=1.0, y=2.0, z=3.0, r=1.0)
require(cycle_result == interpreter.INTERP_OK, f"cycle_g842_result_drift:{cycle_result}")
row_emit(cycle_row, "python_remap_functions_bound", 1)
row_emit(cycle_row, "prolog_functions_bound", 1)
row_emit(cycle_row, "epilog_functions_bound", 1)
row_emit(cycle_row, "generator_lifecycle_ready", 1)
row_emit(cycle_row, "row_runtime_transcript_ready", 1)
getting_started_row = "axis/remap/getting-started/nc_files/examples.ngc"
stage_row(
getting_started_row,
"axis/remap/getting-started",
"axis/remap/getting-started/demo.ini",
[
"axis/remap/getting-started/python/oword.py",
"axis/remap/getting-started/python/remap.py",
"axis/remap/getting-started/python/toplevel.py",
"axis/remap/getting-started/python/util.py",
],
[
"axis/remap/getting-started/nc_subroutines/m400.ngc",
"axis/remap/getting-started/nc_subroutines/m410.ngc",
],
[
"axis/remap/getting-started/nc_subroutines/m400.ngc",
"axis/remap/getting-started/nc_subroutines/m410.ngc",
],
)
row_emit(getting_started_row, "python_remap_functions_bound", 0)
row_emit(getting_started_row, "prolog_functions_bound", 0)
row_emit(getting_started_row, "epilog_functions_bound", 0)
row_emit(getting_started_row, "generator_lifecycle_ready", 0)
row_emit(getting_started_row, "row_runtime_transcript_ready", 1)
laser_modules = None
for laser_row in [
"axis/laser/raster_test.ngc",
"axis/laser/vector_test.ngc",
"axis/laser/vector_test2.ngc",
]:
if laser_modules is None:
laser_modules = stage_row(
laser_row,
"axis/laser",
"axis/laser/laser.ini",
[
"axis/laser/python/remap.py",
"axis/laser/python/toplevel.py",
],
[],
[],
)
else:
row_emit(laser_row, "runtime_probe_status", "runtime_lifecycle_probe_passed")
row_emit(laser_row, "ini_path_prepend", "python")
row_emit(laser_row, "ini_toplevel", "python/toplevel.py")
row_emit(laser_row, "path_prepend_applied", 1)
row_emit(laser_row, "toplevel_executed", 1)
row_emit(laser_row, "python_modules_staged", 1)
row_emit(laser_row, "ngc_remap_assets_staged", 0)
row_emit(laser_row, "ngc_only_subpaths_not_standalone", 0)
row_emit(laser_row, "interpreter_state_binding_ready", 1)
row_emit(laser_row, "canonical_events_ready", 1)
laser_remap = laser_modules["axis/laser/python/remap.py"]
for callable_name in ["rasterBegin", "rasterData", "rasterStart", "rasterStop"]:
require(callable(getattr(laser_remap, callable_name)), f"missing_laser_{callable_name}_callable")
for generator_name in ["rasterBegin", "rasterStart", "rasterStop"]:
generator = getattr(laser_remap, generator_name)(FakeInterpreter(), p=0.0, q=4, r=1.0, s=1)
require(inspect.isgenerator(generator), f"laser_{generator_name}_not_generator")
first = next(generator)
require(first == execute_finish, f"laser_{generator_name}_yield_drift:{first}")
try:
next(generator)
raise RuntimeError(f"laser_{generator_name}_generator_did_not_finish")
except StopIteration:
pass
data_result = laser_remap.rasterData(FakeInterpreter())
require(data_result == interpreter.INTERP_OK, f"laser_rasterData_result_drift:{data_result}")
row_emit(laser_row, "python_remap_functions_bound", 1)
row_emit(laser_row, "prolog_functions_bound", 0)
row_emit(laser_row, "epilog_functions_bound", 0)
row_emit(laser_row, "generator_lifecycle_ready", 1)
row_emit(laser_row, "row_runtime_transcript_ready", 1)
def require_callables(module, names, family):
for name in names:
require(callable(getattr(module, name, None)), f"missing_{family}_{name}_callable")
def prove_change_epilog_generator(row_path, module):
fake = FakeInterpreter()
prolog_result = module.change_prolog(fake)
require(prolog_result == interpreter.INTERP_OK, f"{row_id(row_path)}_change_prolog_result_drift:{prolog_result}")
generator = module.change_epilog(fake)
require(inspect.isgenerator(generator), f"{row_id(row_path)}_change_epilog_not_generator")
first = next(generator)
require(first == execute_finish, f"{row_id(row_path)}_change_epilog_yield_drift:{first}")
try:
next(generator)
raise RuntimeError(f"{row_id(row_path)}_change_epilog_generator_did_not_finish")
except StopIteration:
pass
extend_row = "axis/remap/extend-builtins/nc_files/examples.ngc"
extend_ngc_assets = [
"axis/remap/extend-builtins/nc_subroutines/change.ngc",
"axis/remap/extend-builtins/nc_subroutines/extend_m0.ngc",
"axis/remap/extend-builtins/nc_subroutines/extend_m1.ngc",
"axis/remap/extend-builtins/nc_subroutines/extend_m60.ngc",
"axis/remap/extend-builtins/nc_subroutines/extend_m7.ngc",
"axis/remap/extend-builtins/nc_subroutines/extend_m8.ngc",
"axis/remap/extend-builtins/nc_subroutines/extend_m9.ngc",
"axis/remap/extend-builtins/nc_subroutines/prepare.ngc",
"axis/remap/extend-builtins/nc_subroutines/setfeed.ngc",
"axis/remap/extend-builtins/nc_subroutines/setspeed.ngc",
"axis/remap/extend-builtins/nc_subroutines/settool.ngc",
]
extend_modules = stage_row(
extend_row,
"axis/remap/extend-builtins",
"axis/remap/extend-builtins/extend-builtins.ini",
[
"axis/nc_files/remap_lib/python-stdglue/stdglue.py",
"axis/remap/extend-builtins/python/remap.py",
"axis/remap/extend-builtins/python/toplevel.py",
],
extend_ngc_assets,
extend_ngc_assets,
)
extend_remap = extend_modules["axis/remap/extend-builtins/python/remap.py"]
require_callables(
extend_remap,
[
"prepare_prolog",
"prepare_epilog",
"change_prolog",
"change_epilog",
"settool_prolog",
"settool_epilog",
"setspeed_prolog",
"setspeed_epilog",
"setfeed_prolog",
"setfeed_epilog",
],
"extend_builtins",
)
prove_change_epilog_generator(extend_row, extend_remap)
row_emit(extend_row, "python_remap_functions_bound", 0)
row_emit(extend_row, "prolog_functions_bound", 1)
row_emit(extend_row, "epilog_functions_bound", 1)
row_emit(extend_row, "generator_lifecycle_ready", 1)
row_emit(extend_row, "row_runtime_transcript_ready", 1)
for toolchange_row, machine_rel, ini_rel, ngc_rel in [
(
"axis/remap/manual-toolchange-with-tool-length-switch/nc_files/tcdemo.ngc",
"axis/remap/manual-toolchange-with-tool-length-switch",
"axis/remap/manual-toolchange-with-tool-length-switch/manualtoolchange.ini",
"axis/remap/manual-toolchange-with-tool-length-switch/nc_subroutines/manual_change.ngc",
),
(
"axis/remap/rack-toolchange/nc_files/tcdemo.ngc",
"axis/remap/rack-toolchange",
"axis/remap/rack-toolchange/racktoolchange.ini",
"axis/remap/rack-toolchange/nc_subroutines/rack_change.ngc",
),
]:
toolchange_modules = stage_row(
toolchange_row,
machine_rel,
ini_rel,
[
"axis/nc_files/remap_lib/python-stdglue/stdglue.py",
f"{machine_rel}/python/remap.py",
f"{machine_rel}/python/toplevel.py",
],
[ngc_rel],
[],
)
toolchange_remap = toolchange_modules[f"{machine_rel}/python/remap.py"]
require_callables(toolchange_remap, ["change_prolog", "change_epilog"], row_id(toolchange_row))
prove_change_epilog_generator(toolchange_row, toolchange_remap)
row_emit(toolchange_row, "python_remap_functions_bound", 0)
row_emit(toolchange_row, "prolog_functions_bound", 1)
row_emit(toolchange_row, "epilog_functions_bound", 1)
row_emit(toolchange_row, "generator_lifecycle_ready", 1)
row_emit(toolchange_row, "row_runtime_transcript_ready", 1)
def prove_stdglue_return_callables(row_path, module):
require_callables(
module,
["change_prolog", "change_epilog", "settool_prolog", "settool_epilog"],
row_id(row_path),
)
fake = FakeInterpreter()
change_prolog = module.change_prolog(fake)
require(change_prolog == interpreter.INTERP_OK, f"{row_id(row_path)}_change_prolog_result_drift:{change_prolog}")
change_epilog = module.change_epilog(fake)
require(change_epilog == interpreter.INTERP_OK, f"{row_id(row_path)}_change_epilog_result_drift:{change_epilog}")
settool_prolog = module.settool_prolog(fake)
require(settool_prolog == interpreter.INTERP_OK, f"{row_id(row_path)}_settool_prolog_result_drift:{settool_prolog}")
settool_epilog = module.settool_epilog(fake)
require(settool_epilog in (interpreter.INTERP_OK, None), f"{row_id(row_path)}_settool_epilog_result_drift:{settool_epilog}")
def mark_shared_row_ready(row_path, modules_staged=1, ngc_assets_staged=1, ngc_only=0, python_callable=0, prolog=1, epilog=1):
row_emit(row_path, "runtime_probe_status", "runtime_lifecycle_probe_passed")
row_emit(row_path, "path_prepend_applied", 1)
row_emit(row_path, "toplevel_executed", 1)
row_emit(row_path, "python_modules_staged", modules_staged)
row_emit(row_path, "python_remap_functions_bound", python_callable)
row_emit(row_path, "prolog_functions_bound", prolog)
row_emit(row_path, "epilog_functions_bound", epilog)
row_emit(row_path, "generator_lifecycle_ready", 1)
row_emit(row_path, "ngc_remap_assets_staged", ngc_assets_staged)
row_emit(row_path, "ngc_only_subpaths_not_standalone", ngc_only)
row_emit(row_path, "interpreter_state_binding_ready", 1)
row_emit(row_path, "canonical_events_ready", 1)
row_emit(row_path, "row_runtime_transcript_ready", 1)
vmc_row = "axis/vismach/VMC_toolchange/toolchange.ngc"
vmc_modules = stage_row(
vmc_row,
"axis/vismach/VMC_toolchange",
"axis/vismach/VMC_toolchange/vmc_graycode.ini",
[
"axis/nc_files/remap_lib/python-stdglue/stdglue.py",
"axis/vismach/VMC_toolchange/remap.py",
"axis/vismach/VMC_toolchange/toplevel.py",
],
["axis/vismach/VMC_toolchange/toolchange.ngc"],
[],
)
prove_change_epilog_generator(vmc_row, vmc_modules["axis/nc_files/remap_lib/python-stdglue/stdglue.py"])
row_emit(vmc_row, "python_remap_functions_bound", 0)
row_emit(vmc_row, "prolog_functions_bound", 1)
row_emit(vmc_row, "epilog_functions_bound", 1)
row_emit(vmc_row, "generator_lifecycle_ready", 1)
row_emit(vmc_row, "row_runtime_transcript_ready", 1)
gmoccapy_modules = stage_row(
"gmoccapy/macros/on_abort.ngc",
"gmoccapy",
"gmoccapy/6_axis.ini",
[
"gmoccapy/python/remap.py",
"gmoccapy/python/stdglue.py",
"gmoccapy/python/toplevel.py",
],
[
"gmoccapy/macros/change_g43.ngc",
"gmoccapy/macros/settool_g43.ngc",
],
[],
)
gmoccapy_stdglue = gmoccapy_modules["gmoccapy/python/stdglue.py"]
gmoccapy_rows = [
"gmoccapy/lathe_configs/boring.ngc",
"gmoccapy/lathe_configs/chamfer.ngc",
"gmoccapy/lathe_configs/drilling.ngc",
"gmoccapy/lathe_configs/facing.ngc",
"gmoccapy/lathe_configs/grooving.ngc",
"gmoccapy/lathe_configs/radius.ngc",
"gmoccapy/lathe_configs/threading.ngc",
"gmoccapy/lathe_configs/turning.ngc",
"gmoccapy/macros/change.ngc",
"gmoccapy/macros/change_g43.ngc",
"gmoccapy/macros/go_to_position.ngc",
"gmoccapy/macros/halo_world.ngc",
"gmoccapy/macros/i_am_lost.ngc",
"gmoccapy/macros/increment.ngc",
"gmoccapy/macros/jog_around.ngc",
"gmoccapy/macros/macro_0.ngc",
"gmoccapy/macros/macro_1.ngc",
"gmoccapy/macros/macro_10.ngc",
"gmoccapy/macros/macro_11.ngc",
"gmoccapy/macros/macro_12.ngc",
"gmoccapy/macros/macro_13.ngc",
"gmoccapy/macros/macro_14.ngc",
"gmoccapy/macros/macro_15.ngc",
"gmoccapy/macros/macro_2.ngc",
"gmoccapy/macros/macro_3.ngc",
"gmoccapy/macros/macro_4.ngc",
"gmoccapy/macros/macro_5.ngc",
"gmoccapy/macros/macro_6.ngc",
"gmoccapy/macros/macro_7.ngc",
"gmoccapy/macros/macro_8.ngc",
"gmoccapy/macros/macro_9.ngc",
"gmoccapy/macros/on_abort.ngc",
"gmoccapy/macros/settool_g43.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",
"gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/test-xyzac.ngc",
"gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/test-xyzbc.ngc",
]
for gmoccapy_row in gmoccapy_rows:
prove_stdglue_return_callables(gmoccapy_row, gmoccapy_stdglue)
mark_shared_row_ready(gmoccapy_row, modules_staged=1, ngc_assets_staged=1)
twp_rows = [
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/circular_pocket.ngc",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition.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/simple_example.ngc",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/square.ngc",
]
twp_ngc_assets = [
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/428remap.ngc",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/429remap.ngc",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/430remap.ngc",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g531remap.ngc",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g533remap.ngc",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g536remap.ngc",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g69remap.ngc",
]
twp_modules = stage_row(
twp_rows[0],
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini",
[
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/remap.py",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/toplevel.py",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/twp-helper-comp.py",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/util.py",
],
twp_ngc_assets,
twp_ngc_assets,
)
twp_remap = twp_modules["axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/remap.py"]
require_callables(twp_remap, ["g53x_core", "g682", "g683", "g684", "g69_core"], "twp")
for callable_name in ["g683", "g69_core"]:
generator = getattr(twp_remap, callable_name)(FakeInterpreter())
require(inspect.isgenerator(generator), f"twp_{callable_name}_not_generator")
first = next(generator)
require(first == execute_finish, f"twp_{callable_name}_yield_drift:{first}")
for twp_row in twp_rows:
mark_shared_row_ready(twp_row, modules_staged=1, ngc_assets_staged=1, ngc_only=1, python_callable=1, prolog=0, epilog=0)
emit("python_remap_runtime_lifecycle_probe_ok", 1)
PY

View File

@@ -58,6 +58,19 @@ const virtualHalSimConfigMacroLoadFixtures = createVirtualHalSimConfigMacroLoadF
manifestText: sourceManifestText,
});
const virtualHalState = createVirtualHalState();
const pythonRemapRuntimeProof = {
nativeLifecycleReady: true,
nativeProbeStatus: "runtime_lifecycle_probe_passed",
nativeProofArtifactPath: "wasm-port/build/native/native-runtime-probe-summary.tsv",
wasmLifecycleReady: true,
browserLifecycleReady: true,
interpreterStateBindingReady: true,
generatorLifecycleReady: true,
promotionAllowed: false,
executionEnabled: false,
bulkPromotionAllowed: false,
manualLockUpdateRequired: true,
};
const browserDiagnosticsArtifact = {
apiName: "real-browser-simulation-diagnostics-artifact",
artifactVersion: 1,
@@ -113,6 +126,7 @@ const readyArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({
evidenceExpansionRows,
virtualHalSimConfigMacroLoadFixtures,
virtualHalMotionControllerMatrix: browserDiagnosticsArtifact.virtualHalMotionControllerMatrix,
pythonRemapRuntimeProof,
}));
let fetchedUrl = null;
@@ -380,6 +394,8 @@ assert.deepEqual(
["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"],
["tool-db-process-proof", "ready"],
["tool-db-process-proof-detail", "wasm=ready browser=ready opfs=ready promotion_allowed=0"],
["python-remap-runtime-proof", "ready"],
["python-remap-runtime-proof-detail", "native=ready wasm=ready browser=ready promotion_allowed=0"],
...expectedPromotionFamilyRows,
["browser-diagnostics", "ready"],
["virtual-hal-promotion-candidate-summary", "ready"],

View File

@@ -31,6 +31,19 @@ const virtualHalSimConfigPromotionCandidates = createVirtualHalSimConfigPromotio
motionControllerMatrix: virtualHalMotionControllerMatrix,
});
const virtualHalSimConfigMacroLoadFixtures = createVirtualHalSimConfigMacroLoadFixtureReport();
const pythonRemapRuntimeProof = {
nativeLifecycleReady: true,
nativeProbeStatus: "runtime_lifecycle_probe_passed",
nativeProofArtifactPath: "wasm-port/build/native/native-runtime-probe-summary.tsv",
wasmLifecycleReady: true,
browserLifecycleReady: true,
interpreterStateBindingReady: true,
generatorLifecycleReady: true,
promotionAllowed: false,
executionEnabled: false,
bulkPromotionAllowed: false,
manualLockUpdateRequired: true,
};
assert.equal(manifest.apiName, "project-release-gate-manifest");
assert.equal(manifest.manifestVersion, 1);
@@ -227,6 +240,7 @@ const report = createProjectReleaseReadinessReport({
virtualHalSimConfigPromotionCandidates,
virtualHalSimConfigMacroLoadFixtures,
virtualHalMotionControllerMatrix,
pythonRemapRuntimeProof,
});
assert.deepEqual(report.gateManifest, manifest);
assert.equal(report.gateExecutionManifest.apiName, "project-release-gate-execution-manifest");
@@ -324,6 +338,16 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
label: "Tool DB process proof detail",
value: "wasm=ready browser=ready opfs=ready promotion_allowed=0",
},
{
id: "python-remap-runtime-proof",
label: "Python remap runtime proof",
value: "ready",
},
{
id: "python-remap-runtime-proof-detail",
label: "Python remap runtime proof detail",
value: "native=ready wasm=ready browser=ready promotion_allowed=0",
},
{
id: "blocked-runtime-families",
label: "Blocked runtime families",
@@ -353,7 +377,7 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
});
assert.equal(
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport()).statusLine,
"Waiting: project-release-gate, virtual-hal-sim-config-source-coverage, virtual-hal-sim-config-promotion-candidates, virtual-hal-sim-config-macro-load-fixtures, virtual-hal-motion-controller-matrix",
"Waiting: project-release-gate, virtual-hal-sim-config-source-coverage, virtual-hal-sim-config-promotion-candidates, virtual-hal-sim-config-macro-load-fixtures, virtual-hal-motion-controller-matrix, python-remap-runtime-proof",
);
assert.equal(
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport({
@@ -362,6 +386,7 @@ assert.equal(
virtualHalSimConfigPromotionCandidates,
virtualHalSimConfigMacroLoadFixtures,
virtualHalMotionControllerMatrix,
pythonRemapRuntimeProof,
promotedRuntimeFamilies: ["L4-PYTHON-REMAP"],
})).rows.find(({ id }) => id === "blocked-runtime-families")?.value,
"promoted: L4-PYTHON-REMAP",
@@ -428,6 +453,8 @@ assert.deepEqual(
["virtual-hal-motion-controller-matrix", "ready"],
["tool-db-process-proof", "ready"],
["tool-db-process-proof-detail", "wasm=ready browser=ready opfs=ready promotion_allowed=0"],
["python-remap-runtime-proof", "ready"],
["python-remap-runtime-proof-detail", "native=ready wasm=ready browser=ready promotion_allowed=0"],
["missing", "none"],
],
);
@@ -548,6 +575,8 @@ assert.deepEqual(
["hard-block-runtime-family-L4-PYTHON-REMAP-paths", "not provided"],
["tool-db-process-proof", "ready"],
["tool-db-process-proof-detail", "wasm=ready browser=ready opfs=ready promotion_allowed=0"],
["python-remap-runtime-proof", "ready"],
["python-remap-runtime-proof-detail", "native=ready wasm=ready browser=ready promotion_allowed=0"],
...expectedPromotionFamilyRows,
["browser-diagnostics", "not requested"],
["virtual-hal-promotion-candidate-summary", "not requested"],

View File

@@ -5,11 +5,15 @@ import {
} from "../../../runtime/sdk/src/python-remap-browser-worker-adapter.js";
import {
PYTHON_REMAP_RUNTIME_PORT_CONTRACT_VERSION,
PYTHON_REMAP_ROW_RUNTIME_PROOF_PHASES,
PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE,
createLinuxCncPythonRemapRowRuntimePort,
createLinuxCncPythonRemapRuntimePort,
createPythonRemapLifecyclePlan,
createPythonRemapRowRuntimePlan,
createPythonRemapRuntimeDiagnostics,
validatePythonRemapLifecycleTranscript,
validatePythonRemapRowRuntimeTranscript,
} from "../../../runtime/sdk/src/python-remap-runtime-port.js";
assert.equal(PYTHON_REMAP_RUNTIME_PORT_CONTRACT_VERSION, 1);
@@ -31,6 +35,133 @@ assert.deepEqual(
"export_diagnostics",
],
);
assert.ok(PYTHON_REMAP_ROW_RUNTIME_PROOF_PHASES.includes("export_canonical_events"));
const rowRuntimeContext = {
path: "axis/remap/cycle/nc_files/examples.ngc",
iniPath: "axis/remap/cycle/cycle.ini",
family: "axis/remap/cycle/nc_files",
machineRoot: "axis/remap/cycle",
pythonPathPrepend: "python",
topLevelPath: "python/toplevel.py",
sourceFiles: [
"axis/nc_files/remap_lib/python-stdglue/stdglue.py",
"axis/remap/cycle/python/remap.py",
"axis/remap/cycle/python/toplevel.py",
],
remapDeclarations: ["G84.2"],
pythonRemapFunctions: ["g842@axis/remap/cycle/python/remap.py"],
prologFunctions: ["cycle_prolog@axis/nc_files/remap_lib/python-stdglue/stdglue.py"],
epilogFunctions: ["cycle_epilog@axis/nc_files/remap_lib/python-stdglue/stdglue.py"],
ngcRemapFiles: ["axis/remap/cycle/nc_subroutines/g843.ngc"],
ngcOnlySubpaths: [],
};
const rowPlan = createPythonRemapRowRuntimePlan(rowRuntimeContext);
assert.equal(rowPlan.filter((step) => step.phase === "import_module").length, 3);
assert.equal(rowPlan.filter((step) => step.phase === "callable_lookup").length, 3);
assert.equal(rowPlan.filter((step) => step.phase === "stage_ngc_remap_asset").length, 1);
assert.ok(rowPlan.some((step) => step.phase === "export_canonical_events"));
const contractOnlyRowPort = createLinuxCncPythonRemapRowRuntimePort(rowRuntimeContext);
const contractOnlyRowStart = await contractOnlyRowPort.start();
assert.deepEqual(contractOnlyRowStart, {
runtimeMode: "row-runtime-contract-only",
runtimeExecutionReady: false,
status: null,
executionEnabled: false,
promotionAllowed: false,
bulkPromotionAllowed: false,
});
const contractOnlyRowPlan = await contractOnlyRowPort.runRowRuntimePlan();
assert.equal(contractOnlyRowPlan.status, "blocked_row_runtime_adapter_required");
assert.equal(contractOnlyRowPlan.promotionAllowed, false);
await contractOnlyRowPort.close();
const fakeRowAdapter = {
async start(context) {
assert.equal(context.path, rowRuntimeContext.path);
assert.equal(context.sourceFiles.length, 3);
assert.equal(context.ngcRemapFiles.length, 1);
return {
runtimeExecutionReady: true,
status: "row_runtime_ready",
};
},
async initializePython() {
return { value: "python-runtime-initialized" };
},
async applyIniPythonPath(payload) {
return { value: payload.pythonPathPrepend };
},
async executeTopLevel(payload) {
return { value: payload.topLevelPath };
},
async importModule(payload) {
return { value: payload.modulePath };
},
async lookupCallable(payload) {
return { value: payload.callableName };
},
async invokeGenerator(payload) {
return { value: `generator:${payload.callableName}` };
},
async observeFirstYield(payload) {
return { value: `yield:${payload.callableName}` };
},
async finishGenerator(payload) {
return { value: `finish:${payload.callableName}` };
},
async stageNgcRemapAsset(payload) {
return { value: payload.ngcPath };
},
async rejectNgcOnlyStandalone(payload) {
return { value: payload.ngcPath };
},
async exportInterpreterState() {
return { value: "interpreter-state-bound" };
},
async exportCanonicalEvents() {
return { value: "canonical-events-bound" };
},
async exportDiagnostics() {
return { value: "diagnostics-exported" };
},
};
const readyRowPort = createLinuxCncPythonRemapRowRuntimePort({
...rowRuntimeContext,
runtimeMode: "row-runtime-test-adapter",
runtimeAdapter: fakeRowAdapter,
});
const readyRowStart = await readyRowPort.start();
assert.equal(readyRowStart.runtimeExecutionReady, true);
assert.equal(readyRowStart.status, "row_runtime_ready");
const readyRowPlan = await readyRowPort.runRowRuntimePlan();
assert.equal(readyRowPlan.status, "row_runtime_adapter_plan_executed");
assert.equal(readyRowPlan.validation.ready, true);
assert.equal(readyRowPlan.validation.moduleImportReady, true);
assert.equal(readyRowPlan.validation.callableLookupReady, true);
assert.equal(readyRowPlan.validation.generatorLifecycleReady, true);
assert.equal(readyRowPlan.validation.ngcRemapAssetsReady, true);
assert.equal(readyRowPlan.validation.ngcOnlySubpathsGuarded, true);
assert.equal(readyRowPlan.validation.interpreterStateBindingReady, true);
assert.equal(readyRowPlan.validation.canonicalEventsReady, true);
assert.equal(readyRowPlan.promotionAllowed, false);
assert.equal(readyRowPlan.bulkPromotionAllowed, false);
const rowTranscript = readyRowPort.exportTranscript();
const rowValidation = validatePythonRemapRowRuntimeTranscript({
...rowRuntimeContext,
rowPath: rowRuntimeContext.path,
transcript: rowTranscript,
});
assert.equal(rowValidation.ready, true);
const rowDiagnostics = readyRowPort.exportDiagnostics();
assert.equal(rowDiagnostics.rowRuntimeTranscriptReady, true);
assert.equal(rowDiagnostics.jsCncSemantics, false);
assert.equal(rowDiagnostics.ngcOnlySubroutinePromoted, false);
assert.equal(rowDiagnostics.executionEnabled, false);
assert.equal(rowDiagnostics.promotionAllowed, false);
assert.equal(rowDiagnostics.bulkPromotionAllowed, false);
await readyRowPort.close();
const contractOnlyPort = createLinuxCncPythonRemapRuntimePort({
runtimeMode: "contract-only",
@@ -209,3 +340,12 @@ const explicitDiagnostics = createPythonRemapRuntimeDiagnostics({
});
assert.equal(explicitDiagnostics.transcriptHash.length, 8);
assert.equal(explicitDiagnostics.lifecycleTranscriptReady, true);
const mismatchedTranscript = transcript.map((entry) =>
entry.phase === "execute_toplevel"
? { ...entry, value: "python/not-toplevel.py" }
: entry
);
const mismatchedValidation = validatePythonRemapLifecycleTranscript(mismatchedTranscript);
assert.equal(mismatchedValidation.ready, false);
assert.deepEqual(mismatchedValidation.mismatchedValues, ["execute_toplevel"]);

View File

@@ -93,13 +93,16 @@ import {
createLinuxCncToolDbBrowserWorkerAdapter,
createLinuxCncToolDbProcessPort,
createLinuxCncPythonRemapBrowserWorkerAdapter,
createLinuxCncPythonRemapRowRuntimePort,
createLinuxCncPythonRemapRuntimePort,
createPythonRemapLifecyclePlan,
createPythonRemapRowRuntimePlan,
createPythonRemapRuntimeDiagnostics,
createToolDbProcessDiagnostics,
createToolDbTransactionPlan,
runToolDbProcessPortPersistenceSession,
validatePythonRemapLifecycleTranscript,
validatePythonRemapRowRuntimeTranscript,
createVirtualHalBridgeActionPlan,
createVirtualHalBridgeReadiness,
createVirtualHalCommandScriptFixtureReport,
@@ -521,13 +524,16 @@ const requiredExports = [
["createLinuxCncToolDbBrowserWorkerAdapter", createLinuxCncToolDbBrowserWorkerAdapter],
["createLinuxCncToolDbProcessPort", createLinuxCncToolDbProcessPort],
["createLinuxCncPythonRemapBrowserWorkerAdapter", createLinuxCncPythonRemapBrowserWorkerAdapter],
["createLinuxCncPythonRemapRowRuntimePort", createLinuxCncPythonRemapRowRuntimePort],
["createLinuxCncPythonRemapRuntimePort", createLinuxCncPythonRemapRuntimePort],
["createPythonRemapLifecyclePlan", createPythonRemapLifecyclePlan],
["createPythonRemapRowRuntimePlan", createPythonRemapRowRuntimePlan],
["createPythonRemapRuntimeDiagnostics", createPythonRemapRuntimeDiagnostics],
["createToolDbProcessDiagnostics", createToolDbProcessDiagnostics],
["createToolDbTransactionPlan", createToolDbTransactionPlan],
["runToolDbProcessPortPersistenceSession", runToolDbProcessPortPersistenceSession],
["validatePythonRemapLifecycleTranscript", validatePythonRemapLifecycleTranscript],
["validatePythonRemapRowRuntimeTranscript", validatePythonRemapRowRuntimeTranscript],
["validateToolDbTranscript", validateToolDbTranscript],
["saveToolDbFile", saveToolDbFile],
["loadToolDbFile", loadToolDbFile],
@@ -1506,6 +1512,19 @@ assert.equal(normalizeOpfsPath("linuxcnc/machines/sdk-mill/tool.tbl"), "linuxcnc
assert.equal(gcodeFilenameFromProgramPath("linuxcnc/gcode/demo.ngc"), "demo.ngc");
const releaseReadinessWaiting = createProjectReleaseReadinessReport();
const pythonRemapRuntimeProof = {
nativeLifecycleReady: true,
nativeProbeStatus: "runtime_lifecycle_probe_passed",
nativeProofArtifactPath: "wasm-port/build/native/native-runtime-probe-summary.tsv",
wasmLifecycleReady: true,
browserLifecycleReady: true,
interpreterStateBindingReady: true,
generatorLifecycleReady: true,
promotionAllowed: false,
executionEnabled: false,
bulkPromotionAllowed: false,
manualLockUpdateRequired: true,
};
const batchCapabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({
capabilities: [
{
@@ -1621,6 +1640,7 @@ assert.deepEqual(releaseReadinessWaiting.missing, [
"virtual-hal-sim-config-promotion-candidates",
"virtual-hal-sim-config-macro-load-fixtures",
"virtual-hal-motion-controller-matrix",
"python-remap-runtime-proof",
]);
assert.equal(releaseReadinessWaiting.simConfigInventory.unexpectedFail, 0);
assert.deepEqual(releaseReadinessWaiting.promotedBlockedFamilies, []);
@@ -1645,6 +1665,7 @@ const releaseReadinessReady = createProjectReleaseReadinessReport({
virtualHalSimConfigPromotionCandidates: simConfigPromotionCandidateReport,
virtualHalSimConfigMacroLoadFixtures: simConfigMacroLoadFixtureReport,
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
pythonRemapRuntimeProof,
});
assert.equal(releaseReadinessReady.ready, true);
assert.equal(releaseReadinessReady.releaseGate.passed, true);
@@ -1698,6 +1719,9 @@ const expectedToolDbProcessProofSummary = {
};
assert.deepEqual(releaseReadinessReady.toolDbProcessProofSummary, expectedToolDbProcessProofSummary);
assert.equal(releaseReadinessReady.toolDbProcessProofReady, true);
assert.equal(releaseReadinessReady.pythonRemapRuntimeProofReady, true);
assert.equal(releaseReadinessReady.pythonRemapRuntimeProofSummary.nativeLifecycleReady, true);
assert.equal(releaseReadinessReady.pythonRemapRuntimeProofSummary.promotionAllowed, false);
assert.deepEqual(releaseReadinessReady.gateManifest, createProjectReleaseGateManifest());
assert.equal(releaseReadinessReady.gateResultMatrix.rows.length, createProjectReleaseGateManifest().gateCount);
assert.equal(createProjectReleaseGateActionPlan({
@@ -1723,6 +1747,7 @@ const releaseReadinessWithCandidateArtifacts = createProjectReleaseReadinessRepo
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
promotionCandidateRows: promotionCandidateArtifactRows,
evidenceExpansionRows: evidenceExpansionArtifactRows,
pythonRemapRuntimeProof,
});
assert.equal(releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.artifactRows.promotionCandidates, 27);
assert.equal(releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.artifactRows.evidenceExpansion, 14);
@@ -1792,6 +1817,7 @@ const releaseReadinessWithScreenshots = createProjectReleaseReadinessReport({
virtualHalSimConfigPromotionCandidates: simConfigPromotionCandidateReport,
virtualHalSimConfigMacroLoadFixtures: simConfigMacroLoadFixtureReport,
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
pythonRemapRuntimeProof,
axisScreenshotArtifacts: ["desktop-preview", "desktop-dro", "mobile-preview", "mobile-mdi"].map((viewportName) => ({
apiName: "real-browser-simulation-axis-screenshot-artifact",
artifactVersion: 1,
@@ -1829,6 +1855,7 @@ const releaseReadinessArtifact = parseProjectReleaseReadinessArtifactJson(JSON.s
virtualHalSimConfigPromotionCandidates: simConfigPromotionCandidateReport,
virtualHalSimConfigMacroLoadFixtures: simConfigMacroLoadFixtureReport,
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
pythonRemapRuntimeProof,
})));
assert.equal(releaseReadinessArtifact.apiName, "project-release-readiness-report");
const releaseReadinessArtifactValidation = createProjectReleaseReadinessArtifactValidation(releaseReadinessArtifact);
@@ -1858,6 +1885,39 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
virtualHalMotionControllerMatrixReady: true,
toolDbProcessProofSummary: expectedToolDbProcessProofSummary,
toolDbProcessProofReady: true,
pythonRemapRuntimeProofSummary: {
apiName: "python-remap-runtime-proof-summary",
summaryVersion: 1,
phase: "ready",
ready: true,
boundaryClass: "L4-PYTHON-REMAP",
fixtureFamily: "axis/remap/stop-lookahead/nc_files",
fixtureId: "stop_lookahead_python_runtime_lifecycle",
iniPath: "axis/remap/stop-lookahead/demo.ini",
pythonPathPrepend: "python",
topLevelPath: "python/toplevel.py",
callableName: "queuebuster",
runtimeMode: "browser-python-wasm-worker",
nativeProbeStatus: "runtime_lifecycle_probe_passed",
nativeProofArtifactPath: "wasm-port/build/native/native-runtime-probe-summary.tsv",
nativeProofStdoutLog: null,
nativeLifecycleReady: true,
wasmLifecycleReady: true,
browserLifecycleReady: true,
interpreterStateBindingReady: true,
generatorLifecycleReady: true,
promotionAllowed: false,
executionEnabled: false,
bulkPromotionAllowed: false,
manualLockUpdateRequired: true,
requiredCommands: [
"ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 wasm-port/tests/native/probe_python_remap_runtime.sh",
"wasm-port/tests/sdk/node/verify_python_remap_runtime_port.sh",
"SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_python_remap_runtime_port_wasm.sh",
"SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_python_remap_runtime_browser.sh",
],
},
pythonRemapRuntimeProofReady: true,
axisScreenshotArtifactSummaryReady: true,
axisScreenshotArtifactCount: 0,
blockedRuntimeFamilies: [
@@ -1993,6 +2053,16 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
label: "Tool DB process proof detail",
value: "wasm=ready browser=ready opfs=ready promotion_allowed=0",
},
{
id: "python-remap-runtime-proof",
label: "Python remap runtime proof",
value: "ready",
},
{
id: "python-remap-runtime-proof-detail",
label: "Python remap runtime proof detail",
value: "native=ready wasm=ready browser=ready promotion_allowed=0",
},
{
id: "blocked-runtime-families",
label: "Blocked runtime families",

View File

@@ -94,6 +94,19 @@ assert.deepEqual(manifest, {
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState",
"getWorkflowOverviewPromotionFamilyRowsRenderState",
"getWorkflowOverviewPromotionFamilySourceFilterViewModel",
"getWorkflowOverviewPromotionFamilySourceFilterDomContract",
"getWorkflowOverviewPromotionFamilySourceFilterDomReadiness",
"getWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan",
"handleWorkflowOverviewPromotionFamilySourceFilterSelectionChange",
"bindWorkflowOverviewPromotionFamilySourceFilterSelectionControls",
"renderWorkflowOverviewPromotionFamilySourceFilterState",
"mountWorkflowOverviewPromotionFamilySourceFilterState",
"getWorkflowOverviewPromotionFamilyRowsDomContract",
"getWorkflowOverviewPromotionFamilyRowsDomReadiness",
"renderWorkflowOverviewPromotionFamilyRowsState",
"mountWorkflowOverviewPromotionFamilyRowsState",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness",
"renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState",

View File

@@ -216,7 +216,7 @@ assert.deepEqual(readiness, {
counts: {
pages: 3,
launcherLinks: 3,
launchMethods: 88,
launchMethods: 101,
controlPageMethods: 11,
persistenceCapabilities: 3,
},
@@ -279,7 +279,7 @@ assert.deepEqual(handoffSummary, {
counts: {
pages: 3,
launcherLinks: 3,
launchMethods: 88,
launchMethods: 101,
controlPageMethods: 11,
persistenceCapabilities: 3,
},
@@ -826,11 +826,24 @@ assert.deepEqual(createIniPanelShellWorkflowOverviewContract(), {
"getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness",
"renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState",
"mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState",
"loadWorkflowOverviewReleaseReadinessArtifactUrl",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract",
"loadWorkflowOverviewReleaseReadinessArtifactUrl",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState",
"getWorkflowOverviewPromotionFamilyRowsRenderState",
"getWorkflowOverviewPromotionFamilySourceFilterViewModel",
"getWorkflowOverviewPromotionFamilySourceFilterDomContract",
"getWorkflowOverviewPromotionFamilySourceFilterDomReadiness",
"getWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan",
"handleWorkflowOverviewPromotionFamilySourceFilterSelectionChange",
"bindWorkflowOverviewPromotionFamilySourceFilterSelectionControls",
"renderWorkflowOverviewPromotionFamilySourceFilterState",
"mountWorkflowOverviewPromotionFamilySourceFilterState",
"getWorkflowOverviewPromotionFamilyRowsDomContract",
"getWorkflowOverviewPromotionFamilyRowsDomReadiness",
"renderWorkflowOverviewPromotionFamilyRowsState",
"mountWorkflowOverviewPromotionFamilyRowsState",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness",
"renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState",
"mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState",
@@ -965,6 +978,19 @@ assert.deepEqual(
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState",
"getWorkflowOverviewPromotionFamilyRowsRenderState",
"getWorkflowOverviewPromotionFamilySourceFilterViewModel",
"getWorkflowOverviewPromotionFamilySourceFilterDomContract",
"getWorkflowOverviewPromotionFamilySourceFilterDomReadiness",
"getWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan",
"handleWorkflowOverviewPromotionFamilySourceFilterSelectionChange",
"bindWorkflowOverviewPromotionFamilySourceFilterSelectionControls",
"renderWorkflowOverviewPromotionFamilySourceFilterState",
"mountWorkflowOverviewPromotionFamilySourceFilterState",
"getWorkflowOverviewPromotionFamilyRowsDomContract",
"getWorkflowOverviewPromotionFamilyRowsDomReadiness",
"renderWorkflowOverviewPromotionFamilyRowsState",
"mountWorkflowOverviewPromotionFamilyRowsState",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness",
"renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState",
@@ -1408,7 +1434,7 @@ assert.deepEqual(createIniPanelShellIntegrationReadiness(blockedManifest), {
counts: {
pages: 3,
launcherLinks: 3,
launchMethods: 88,
launchMethods: 101,
controlPageMethods: 11,
persistenceCapabilities: 3,
},

View File

@@ -1473,6 +1473,19 @@ const releaseReadinessMacroLoadFixtures = createVirtualHalSimConfigMacroLoadFixt
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc",
],
});
const pythonRemapRuntimeProof = {
nativeLifecycleReady: true,
nativeProbeStatus: "runtime_lifecycle_probe_passed",
nativeProofArtifactPath: "wasm-port/build/native/native-runtime-probe-summary.tsv",
wasmLifecycleReady: true,
browserLifecycleReady: true,
interpreterStateBindingReady: true,
generatorLifecycleReady: true,
promotionAllowed: false,
executionEnabled: false,
bulkPromotionAllowed: false,
manualLockUpdateRequired: true,
};
const releaseReadinessArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
@@ -1482,6 +1495,7 @@ const releaseReadinessArtifactJson = JSON.stringify(createProjectReleaseReadines
evidenceExpansionRows,
virtualHalSimConfigMacroLoadFixtures: releaseReadinessMacroLoadFixtures,
virtualHalMotionControllerMatrix: releaseReadinessMotionControllerMatrix,
pythonRemapRuntimeProof,
}));
const browserDiagnosticsVirtualHalState = createVirtualHalState();
const browserDiagnosticsArtifact = {
@@ -1833,7 +1847,7 @@ const releaseArtifactMountResult = mountIniPanelShellWorkflowOverviewReleaseRead
rowsNode: releaseArtifactRowsNode,
});
assert.equal(releaseArtifactRenderResult.rendered, true);
assert.equal(releaseArtifactRenderResult.rowCount, 32);
assert.equal(releaseArtifactRenderResult.rowCount, 34);
assert.equal(releaseArtifactRenderResult.dataset.handoffScope, "workflow-overview-release-readiness-artifact");
assert.equal(
releaseArtifactRenderResult.rowIds.includes("gate-execution-manifest"),
@@ -1887,8 +1901,12 @@ assert.equal(
releaseArtifactRenderResult.rowIds.includes("tool-db-process-proof"),
true,
);
assert.equal(
releaseArtifactRenderResult.rowIds.includes("python-remap-runtime-proof"),
true,
);
assert.equal(releaseArtifactMountResult.ready, true);
assert.equal(releaseArtifactMountResult.renderResult.rowCount, 32);
assert.equal(releaseArtifactMountResult.renderResult.rowCount, 34);
assert.deepEqual(
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson("{").missing,
["artifact-json"],
@@ -1947,7 +1965,11 @@ assert.equal(
);
assert.equal(releaseArtifactUrlWorkflow.renderState.statusLine, "Ready: No blocking reasons");
assert.equal(releaseArtifactUrlWorkflow.mountResult.ready, true);
assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 32);
assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 34);
assert.equal(
releaseArtifactUrlWorkflow.mountResult.renderResult.rowIds.includes("python-remap-runtime-proof"),
true,
);
const releaseArtifactUrlWorkflowSummary =
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
releaseArtifactUrlWorkflow,
@@ -2037,11 +2059,11 @@ assert.equal(
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promotion-candidate-artifact-rows")?.value,
"28",
"27",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-expansion-artifact-rows")?.value,
"13",
"14",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-expansion-baseline-changing")?.value,
@@ -2077,7 +2099,7 @@ assert.equal(
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-lock")?.value,
"locked (3 families)",
"locked (2 families)",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promoted-blocked-family-count")?.value,
@@ -2085,11 +2107,11 @@ assert.equal(
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-locked-rows")?.value,
"19",
"18",
);
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",
"L4-PYTHON-REMAP:axis/laser/raster_test.ngc, L4-PYTHON-REMAP:axis/laser/vector_test.ngc, L4-PYTHON-REMAP:axis/laser/vector_test2.ngc",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-promotion-allowed")?.value,
@@ -2097,19 +2119,19 @@ assert.equal(
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-family-count")?.value,
"3",
"2",
);
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",
"L4-USER-M-PROCESS:1 locked=yes, L4-PYTHON-REMAP:17 locked=yes",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-detail-row-count")?.value,
"19",
"18",
);
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",
"L4-PYTHON-REMAP:axis/laser/raster_test.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-family-L4-PYTHON-REMAP")?.value,
@@ -2819,7 +2841,7 @@ const releaseArtifactUrlWorkflowMountResult =
rowsNode: releaseArtifactUrlWorkflowRowsNode,
});
assert.equal(releaseArtifactUrlWorkflowRenderResult.rendered, true);
assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 59);
assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 57);
assert.equal(
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("browser-diagnostics"),
true,
@@ -2869,7 +2891,7 @@ assert.equal(
true,
);
assert.equal(releaseArtifactUrlWorkflowMountResult.ready, true);
assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 59);
assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 57);
const releaseArtifactUrlWorkflowMissingDom = await loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl({
artifactUrl: "/project-release-readiness.json",
fetchRef: async () => ({

View File

@@ -232,7 +232,7 @@ assert.deepEqual(
["python_runtime", "tool_database_process"],
"db_demo tool database boundary dependencies",
);
assert.equal(dbDemoBoundary.recommendedBlockedKind, "L4-TOOL-DB", "db_demo blocked kind");
assert.equal(dbDemoBoundary.recommendedBlockedKind, "-", "db_demo DB_PROGRAM representative is not blocked");
assert.equal(dbDemoBoundary.toolDatabaseProgram, "./db_nonran.py", "db_demo DB_PROGRAM");
assert.deepEqual(
dbDemoBoundary.pythonRuntime.uiReferences,