先提交到云仓库

结论:已提交当前 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");
}