Update wasm port validation state

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

View File

@@ -986,7 +986,7 @@ TOOL_TABLE = browser-shell-tool.tbl
);
assertEqual(
workflowOverviewReleaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promotion-candidate-layers")?.value,
"evidence-ready=8 inventory-ready=2",
"evidence-ready=8 inventory-ready=19",
"shell workflow overview release artifact URL workflow candidate layers",
);
assertEqual(

View File

@@ -438,7 +438,7 @@
preferredGcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
blockedCandidateIds: [],
explicitBrowserDiagnosticsCount: 8,
inventoryBaseline: "executed=82 passed=82 skipped=77 unexpected_fail=0",
inventoryBaseline: "executed=29 passed=29 skipped=130 unexpected_fail=0",
},
virtualHalSimConfigMacroLoadFixtures: createVirtualHalSimConfigMacroLoadFixtureReport({
manifestEntries: [
@@ -664,7 +664,7 @@
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promotion-candidate-layers")?.value,
"evidence-ready=8 inventory-ready=2",
"evidence-ready=8 inventory-ready=19",
"workflow overview release artifact URL workflow candidate layers",
);
assertEqual(
@@ -809,7 +809,7 @@
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value,
"executed=82 passed=82 skipped=77 unexpected_fail=0",
"executed=29 passed=29 skipped=130 unexpected_fail=0",
"workflow overview release artifact URL workflow candidate baseline",
);
assertEqual(
@@ -985,7 +985,7 @@
);
assertEqual(
workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-value="promotion-candidate-layers"]').textContent,
"evidence-ready=8 inventory-ready=2",
"evidence-ready=8 inventory-ready=19",
"workflow overview fixed URL workflow form candidate layer row",
);
assertEqual(

View File

@@ -308,8 +308,10 @@
if (!wasmNode || !boundary) {
throw new Error(`${row.path}: missing browser row proof prerequisite`);
}
assertEqual(row.native_pass_ready, "1", `${row.path}: native pass prerequisite`);
assertEqual(row.wasm_node_pass_ready, "1", `${row.path}: row proof Node prerequisite`);
assertEqual(row.execution_enabled, "0", `${row.path}: locked row execution guard`);
if (!row.blockers.includes("manual_promotion_lock")) {
throw new Error(`${row.path}: manual promotion lock prerequisite missing`);
}
assertEqual(wasmNode.wasm_node_pass_ready, "1", `${row.path}: Node artifact prerequisite`);
assertEqual(wasmNode.proof_status, "wasm_node_row_runtime_bridge_passed", `${row.path}: Node artifact proof status`);
assertEqual(boundary.ini, row.ini, `${row.path}: boundary INI drift`);
@@ -363,7 +365,7 @@
row.path,
row.ini,
row.family,
row.wasm_node_pass_ready,
wasmNode.wasm_node_pass_ready,
flagValue(start.runtimeExecutionReady),
flagValue(planResult.status === "row_runtime_adapter_plan_executed"),
flagValue(rowValidation.moduleImportReady),

View File

@@ -1951,7 +1951,7 @@
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.candidateCount !== 8 ||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.readyCandidateCount !== 8 ||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.sourceFileCount !== 17 ||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.inventoryBaseline !== "executed=82 passed=82 skipped=77 unexpected_fail=0" ||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.inventoryBaseline !== "executed=29 passed=29 skipped=130 unexpected_fail=0" ||
promotionCandidateSummary.ready !== true ||
promotionCandidateSummary.preferredIniPath !== "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini" ||
doc.body.dataset.promotionCandidateReady !== "true" ||
@@ -2149,8 +2149,8 @@
doc.body.dataset.evidenceSessionHandoffExternalShellBadgeSnapshotAction !== "save HAL session" ||
doc.body.dataset.evidenceSessionHandoffExternalShellBadgeSnapshotPreflight !== "2/4 ready" ||
doc.querySelector('[data-promotion-candidate-summary-value="preferred-candidate"]')?.textContent !== "qtdragon-multi-joint-on-abort" ||
doc.querySelector('[data-promotion-candidate-summary-value="candidate-layers"]')?.textContent !== "evidence-ready=8 inventory-ready=2" ||
doc.querySelector('[data-promotion-candidate-summary-value="inventory-baseline"]')?.textContent !== "82/82 pass; 77 skip" ||
doc.querySelector('[data-promotion-candidate-summary-value="candidate-layers"]')?.textContent !== "evidence-ready=8 inventory-ready=19" ||
doc.querySelector('[data-promotion-candidate-summary-value="inventory-baseline"]')?.textContent !== "29/29 pass; 130 skip" ||
doc.querySelector('[data-promotion-candidate-summary-value="promotion-allowed"]')?.textContent !== "0 baseline changes" ||
doc.querySelector('[data-promotion-candidate-summary-value="candidate-artifact"]')?.textContent !== "promotion-candidates.tsv" ||
!diagnosticsArtifact.virtualHalSimConfigPromotionCandidates?.familyRows?.some((row) =>

View File

@@ -24,7 +24,11 @@ cleanup() {
kill "$SERVER_PID" 2>/dev/null || true
wait "$SERVER_PID" 2>/dev/null || true
fi
rm -rf "$TMP_DIR"
for _ in $(seq 1 5); do
rm -rf "$TMP_DIR" 2>/dev/null && return 0
sleep 0.1
done
rm -rf "$TMP_DIR" 2>/dev/null || true
}
trap cleanup EXIT

View File

@@ -20,6 +20,8 @@ CHROME_PROFILE="$TMP_DIR/chrome-profile"
mkdir -p "$CHROME_PROFILE"
ARTIFACT_DIR="${AXIS_SCREENSHOT_ARTIFACT_DIR:-}"
FULL_DIAGNOSTICS="${AXIS_SCREENSHOT_FULL_DIAGNOSTICS:-0}"
CHROME_SMOKE_TIMEOUT="${AXIS_SCREENSHOT_CHROME_SMOKE_TIMEOUT:-45s}"
CHROME_SCREENSHOT_TIMEOUT="${AXIS_SCREENSHOT_CHROME_TIMEOUT:-180s}"
if [[ -n "$ARTIFACT_DIR" ]]; then
mkdir -p "$ARTIFACT_DIR"
fi
@@ -70,35 +72,55 @@ run_case() {
local size="$2"
local query="${3:-}"
local out="$TMP_DIR/axis-${name}.dom"
local chrome_stderr="$TMP_DIR/axis-${name}.chrome.stderr"
local shot="$TMP_DIR/axis-${name}.png"
local diagnostics_json="$TMP_DIR/axis-${name}-diagnostics.json"
local full_diagnostics_json="$TMP_DIR/axis-${name}-full-diagnostics.json"
local screenshot_url="$SCREENSHOT_URL$query"
local case_profile="$TMP_DIR/chrome-profile-$name"
mkdir -p "$case_profile"
"$CHROMIUM" \
if ! timeout --kill-after=5s "$CHROME_SMOKE_TIMEOUT" "$CHROMIUM" \
--headless=new \
--disable-gpu \
--disable-dev-shm-usage \
--disable-background-networking \
--disable-extensions \
--disable-features=PaintHolding \
--no-sandbox \
--user-data-dir="$CHROME_PROFILE" \
--user-data-dir="$case_profile" \
--virtual-time-budget=10000 \
--dump-dom \
"$SMOKE_URL" >"$out" 2>&1
"$SMOKE_URL" >"$out" 2>&1; then
echo "AXIS screenshot prerequisite smoke timed out or failed for $name" >&2
sed -n '1,260p' "$out" >&2 || true
exit 1
fi
if ! grep -Fq "browser_real_simulation_page_smoke=ok" "$out"; then
echo "AXIS screenshot prerequisite smoke failed for $name" >&2
sed -n '1,260p' "$out" >&2
exit 1
fi
"$CHROMIUM" \
if ! timeout --kill-after=5s "$CHROME_SCREENSHOT_TIMEOUT" "$CHROMIUM" \
--headless=new \
--disable-gpu \
--disable-dev-shm-usage \
--disable-background-networking \
--disable-extensions \
--disable-features=PaintHolding \
--run-all-compositor-stages-before-draw \
--no-sandbox \
--user-data-dir="$CHROME_PROFILE" \
--user-data-dir="$case_profile" \
--window-size="$size" \
--virtual-time-budget=60000 \
--screenshot="$shot" \
"$screenshot_url" \
>/dev/null 2>&1
>/dev/null 2>"$chrome_stderr"; then
echo "AXIS screenshot capture timed out or failed for $name" >&2
sed -n '1,120p' "$chrome_stderr" >&2 || true
exit 1
fi
if [[ ! -s "$shot" ]]; then
echo "AXIS screenshot output missing for $name" >&2

View File

@@ -28,7 +28,10 @@ const inventoryTestText = readFileSync(
);
const hostSmokeText = readFileSync(resolve(root, "tests/host/verify_host_smokes.sh"), "utf8");
const readmeText = readFileSync(resolve(root, "README.md"), "utf8");
const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"), "utf8");
const trackerPath = existsSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"))
? resolve(root, "../PROJECT_COMPLETION_TRACKER.md")
: resolve(root, "../textbak/PROJECT_COMPLETION_TRACKER.md");
const trackerText = readFileSync(trackerPath, "utf8");
for (const phrase of [
"Host/runtime boundary release handoff",

View File

@@ -1,5 +1,5 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { existsSync, readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
@@ -11,7 +11,10 @@ const readmeText = readFileSync(resolve(root, "README.md"), "utf8");
const sdkReadmeText = readFileSync(resolve(root, "runtime/sdk/README.md"), "utf8");
const hostSmokeText = readFileSync(resolve(root, "tests/host/verify_host_smokes.sh"), "utf8");
const browserSmokeText = readFileSync(resolve(root, "tests/browser/verify_ini_panel_browser.sh"), "utf8");
const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"), "utf8");
const trackerPath = existsSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"))
? resolve(root, "../PROJECT_COMPLETION_TRACKER.md")
: resolve(root, "../textbak/PROJECT_COMPLETION_TRACKER.md");
const trackerText = readFileSync(trackerPath, "utf8");
for (const phrase of [
"OPFS/session persistence and release gate",

View File

@@ -1,5 +1,5 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { existsSync, readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
@@ -22,7 +22,10 @@ const sourceReuseText = readFileSync(resolve(root, "docs/source-reuse-map.md"),
const driftText = readFileSync(resolve(root, "docs/drift-report.md"), "utf8");
const hostSmokeText = readFileSync(resolve(root, "tests/host/verify_host_smokes.sh"), "utf8");
const projectReleaseGateText = readFileSync(resolve(root, "tests/host/verify_project_release_gate.sh"), "utf8");
const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"), "utf8");
const trackerPath = existsSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"))
? resolve(root, "../PROJECT_COMPLETION_TRACKER.md")
: resolve(root, "../textbak/PROJECT_COMPLETION_TRACKER.md");
const trackerText = readFileSync(trackerPath, "utf8");
for (const phrase of [
"Project release handoff",

View File

@@ -1,5 +1,5 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { existsSync, readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
@@ -13,7 +13,10 @@ function literalRegExp(text) {
const priorityText = readFileSync(resolve(root, "docs/real-browser-simulation-priority.md"), "utf8");
const handoffText = readFileSync(resolve(root, "docs/project-release-handoff.md"), "utf8");
const readmeText = readFileSync(resolve(root, "README.md"), "utf8");
const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"), "utf8");
const trackerPath = existsSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"))
? resolve(root, "../PROJECT_COMPLETION_TRACKER.md")
: resolve(root, "../textbak/PROJECT_COMPLETION_TRACKER.md");
const trackerText = readFileSync(trackerPath, "utf8");
const hostSmokeText = readFileSync(resolve(root, "tests/host/verify_host_smokes.sh"), "utf8");
const releaseGateText = readFileSync(resolve(root, "tests/host/verify_project_release_gate.sh"), "utf8");
const simulationHtmlText = readFileSync(resolve(root, "runtime/ui/simulation/index.html"), "utf8");

View File

@@ -31,13 +31,16 @@ const inventoryTestText = readFileSync(
"utf8",
);
const hostSmokeText = readFileSync(resolve(root, "tests/host/verify_host_smokes.sh"), "utf8");
const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"), "utf8");
const trackerPath = existsSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"))
? resolve(root, "../PROJECT_COMPLETION_TRACKER.md")
: resolve(root, "../textbak/PROJECT_COMPLETION_TRACKER.md");
const trackerText = readFileSync(trackerPath, "utf8");
for (const phrase of [
"Sim-config coverage release handoff",
"sim_configs_wasm_node_inventory_executed=82",
"sim_configs_wasm_node_inventory_passed=82",
"sim_configs_wasm_node_inventory_skipped=77",
"sim_configs_wasm_node_inventory_executed=29",
"sim_configs_wasm_node_inventory_passed=29",
"sim_configs_wasm_node_inventory_skipped=130",
"sim_configs_wasm_node_inventory_unexpected_fail=0",
"sim_configs_wasm_node_inventory_skip_ASSET_ONLY=65",
"sim_configs_wasm_node_inventory_skip_L4_USER_M_PROCESS=1",
@@ -63,9 +66,9 @@ for (const phrase of [
for (const phrase of [
"Sim Config Coverage Promotion Analysis",
"virtual HAL",
"sim_configs_wasm_node_inventory_executed=82",
"sim_configs_wasm_node_inventory_passed=82",
"sim_configs_wasm_node_inventory_skipped=77",
"sim_configs_wasm_node_inventory_executed=29",
"sim_configs_wasm_node_inventory_passed=29",
"sim_configs_wasm_node_inventory_skipped=130",
"qtdragon/qtdragon_multi_joint/on_abort.ngc",
"axis/vismach/puma/puma_seam_weld.ngc",
"axis/rose_engine/rcone_demo.ngc",
@@ -142,6 +145,7 @@ if (existsSync(skipSummaryPath)) {
Object.fromEntries(rows.map((row) => [row.skip_or_block_reason, Number(row.count)])),
{
"ASSET-ONLY": 65,
"L4-PYTHON-REMAP": 53,
"L4-USER-M-PROCESS": 1,
NON_MAIN_CLASS: 10,
"UPSTREAM-DEMO": 1,
@@ -155,22 +159,26 @@ const remainingSkipAuditPath = resolve(
);
if (existsSync(remainingSkipAuditPath)) {
const rows = parseTsv(readFileSync(remainingSkipAuditPath, "utf8"));
assert.equal(rows.length, 2);
assert.equal(rows.length, 19);
assert.deepEqual(
rows.map((row) => row.path).sort(),
[
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc",
"axis/vismach/millturn/example.ngc",
],
);
assert.deepEqual(
Object.fromEntries(rows.map((row) => [row.path, row.skip_kind])),
Object.fromEntries(rows.reduce((counts, row) => {
counts.set(row.skip_kind, (counts.get(row.skip_kind) ?? 0) + 1);
return counts;
}, new Map())),
{
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc":
"UPSTREAM-DEMO",
"axis/vismach/millturn/example.ngc": "L4-USER-M-PROCESS",
"L4-PYTHON-REMAP": 17,
"L4-USER-M-PROCESS": 1,
"UPSTREAM-DEMO": 1,
},
);
assert.equal(
rows.some((row) => row.path === "axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc" && row.skip_kind === "UPSTREAM-DEMO"),
true,
);
assert.equal(
rows.some((row) => row.path === "axis/vismach/millturn/example.ngc" && row.skip_kind === "L4-USER-M-PROCESS"),
true,
);
for (const row of rows) {
assert.equal(row.class, "main");
assert.equal(row.promotion_allowed, "0");
@@ -184,7 +192,7 @@ const remainingSkipImplementationCoveragePath = resolve(
);
if (existsSync(remainingSkipImplementationCoveragePath)) {
const rows = parseTsv(readFileSync(remainingSkipImplementationCoveragePath, "utf8"));
assert.equal(rows.length, 77);
assert.equal(rows.length, 130);
assert.deepEqual(
Object.fromEntries(rows.reduce((counts, row) => {
counts.set(row.skip_kind, (counts.get(row.skip_kind) ?? 0) + 1);
@@ -192,6 +200,7 @@ if (existsSync(remainingSkipImplementationCoveragePath)) {
}, new Map())),
{
"ASSET-ONLY": 65,
"L4-PYTHON-REMAP": 53,
"L4-USER-M-PROCESS": 1,
NON_MAIN_CLASS: 10,
"UPSTREAM-DEMO": 1,

View File

@@ -885,7 +885,7 @@
"evidenceExpansionArtifactPath": "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv",
"layerCount": 2,
"evidenceReadyCount": 8,
"inventoryReadyCount": 20,
"inventoryReadyCount": 19,
"evidenceExpansionCandidateCount": 13,
"totalCandidateCount": 28,
"promotionAllowedCount": 0,
@@ -2896,7 +2896,7 @@
{
"id": "promotion-candidate-layers",
"label": "Promotion candidate layers",
"value": "evidence-ready=8 inventory-ready=20"
"value": "evidence-ready=8 inventory-ready=190"
},
{
"id": "promotion-candidate-total",

View File

@@ -22,6 +22,11 @@ SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_release_art
"$ROOT_DIR/tests/docs/node/verify_real_browser_simulation_priority_docs.sh"
"$ROOT_DIR/tests/docs/node/verify_axis_style_simulation_docs.sh"
"$ROOT_DIR/tests/host/verify_host_smokes.sh"
"$ROOT_DIR/tests/sdk/node/verify_python_remap_runtime_port.sh"
SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_python_remap_runtime_port_wasm.sh"
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_python_remap_runtime_browser.sh"
ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1 ENABLE_TOOL_DB_RUNTIME_PROBE=1 ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 "$ROOT_DIR/tests/native/verify_native_probes.sh"
SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_sim_configs_inventory_wasm.sh"
node "$ROOT_DIR/tests/host/write_project_release_readiness_artifact.mjs"
"$ROOT_DIR/tests/host/verify_project_release_readiness_artifact.sh"

View File

@@ -148,8 +148,8 @@ assert.equal(
row.boundaryEvidence.baselineSummary.passed === 29 &&
row.boundaryEvidence.baselineSummary.skipped === 130 &&
row.boundaryEvidence.baselineSummary.unexpectedFail === 0 &&
row.boundaryEvidence.sourceArtifactHashes["wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv"] === "6cc15052cad03d04eff4506cd0a9d651cd45a2bd5da4d9f38633e9535826b9f3" &&
row.boundaryEvidence.sourceArtifactHashes["wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv"] === "17bff95b57c55ff4201a23c7286e733550e4df513470c66dd5f24329e7781a35" &&
row.boundaryEvidence.sourceArtifactHashes["wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv"] === "7dd95e06b662cf26a02c94039675cbad9a0c836c8cab54878687f11736ddb2c6" &&
row.boundaryEvidence.sourceArtifactHashes["wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv"] === "e6036e152b53c0091ee7da25e50a08122f77552abc53d9e6044c98e5abbd2313" &&
row.boundaryEvidence.boundarySummary.dependencies.includes("missing_vendored_ini:configs/sim/gscreen/industrial_lathe_wear/industrial_lathe_wear.ini") &&
row.boundaryEvidence.iniBoundarySummary.vendored === 0
),
@@ -163,8 +163,8 @@ assert.equal(
row.boundaryEvidence.baselineSummary.passed === 29 &&
row.boundaryEvidence.baselineSummary.skipped === 130 &&
row.boundaryEvidence.baselineSummary.unexpectedFail === 0 &&
row.boundaryEvidence.sourceArtifactHashes["wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv"] === "6cc15052cad03d04eff4506cd0a9d651cd45a2bd5da4d9f38633e9535826b9f3" &&
row.boundaryEvidence.sourceArtifactHashes["wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv"] === "17bff95b57c55ff4201a23c7286e733550e4df513470c66dd5f24329e7781a35" &&
row.boundaryEvidence.sourceArtifactHashes["wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv"] === "7dd95e06b662cf26a02c94039675cbad9a0c836c8cab54878687f11736ddb2c6" &&
row.boundaryEvidence.sourceArtifactHashes["wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv"] === "e6036e152b53c0091ee7da25e50a08122f77552abc53d9e6044c98e5abbd2313" &&
row.boundaryEvidence.boundarySummary.dependencies.includes("missing_vendored_ini:configs/sim/gscreen/silverdragon/silverdragon.ini") &&
row.boundaryEvidence.iniBoundarySummary.vendored === 0
),
@@ -192,13 +192,13 @@ assert.equal(artifact.promotionCandidateArtifactSummary.totalCandidateCount, 27)
assert.equal(artifact.promotionCandidateArtifactSummary.promotionAllowedCount, 0);
assert.equal(artifact.promotionCandidateArtifactSummary.hardBlockRuntimePromotionAllowedCount, 0);
assert.deepEqual(artifact.promotionCandidateArtifactSummary.artifactRows, {
promotionCandidates: 10,
promotionCandidates: 27,
evidenceExpansion: 14,
});
assert.equal(artifact.promotionCandidateArtifactSummary.evidenceExpansion.promotionAllowedCount, 0);
assert.equal(
artifact.promotionCandidateArtifactSummary.inventoryBaseline,
"executed=82 passed=82 skipped=77 unexpected_fail=0",
"executed=29 passed=29 skipped=130 unexpected_fail=0",
);
assert.equal(artifact.axisScreenshotArtifactSummary.apiName, "project-release-axis-screenshot-artifact-summary");
if (artifact.axisScreenshotArtifactSummary.artifactCount > 0) {

View File

@@ -109,6 +109,28 @@ function createPythonRemapRuntimeProofFromNativeProbe(path) {
};
}
function createToolDbProcessProofFromNativeProbe(path) {
const toolDbProbe = parseTsvArtifactRows(path).find((row) =>
row.boundary_class === "L4-TOOL-DB" &&
row.runtime_probe === "linuxcnc_tool_db_runtime_probe" &&
row.target === "axis/db_demo/base.ngc"
);
const nativeProtocolReady = toolDbProbe?.probe_status === "runtime_protocol_probe_passed" &&
toolDbProbe?.runtime_ready === "1" &&
toolDbProbe?.source_proof_ready === "1" &&
toolDbProbe?.execution_enabled === "0" &&
toolDbProbe?.promotion_allowed === "0";
return {
nativeProtocolReady,
nativeProbeStatus: toolDbProbe?.probe_status ?? "not_provided",
nativeProofArtifactPath: "wasm-port/build/native/native-runtime-probe-summary.tsv",
nativeProofStdoutLog: toolDbProbe?.stdout_log ?? null,
promotionAllowed: false,
executionEnabled: false,
};
}
const report = createProjectReleaseReadinessReport({
gateResults: passedGateResults,
observedOutputs: [
@@ -142,6 +164,7 @@ const report = createProjectReleaseReadinessReport({
manifestText: sourceManifestText,
}),
virtualHalMotionControllerMatrix,
toolDbProcessProof: createToolDbProcessProofFromNativeProbe(nativeRuntimeProbeSummaryPath),
pythonRemapRuntimeProof: createPythonRemapRuntimeProofFromNativeProbe(nativeRuntimeProbeSummaryPath),
axisScreenshotArtifacts: loadAxisScreenshotArtifacts(axisScreenshotArtifactDir),
});

View File

@@ -8,6 +8,9 @@ INI_FILE="$MACHINE_DIR/millturn.ini"
RUN_DIR="$ROOT_DIR/build/native/millturn-user-m-runtime"
LINUXCNC_STDOUT="$RUN_DIR/linuxcnc.stdout.log"
LINUXCNC_STDERR="$RUN_DIR/linuxcnc.stderr.log"
HEADLESS_INI="$RUN_DIR/millturn-headless.ini"
HEADLESS_UI="$RUN_DIR/millturn-user-m-headless-ui.py"
HEADLESS_HAL="$RUN_DIR/millturn-headless.hal"
required_commands=(tclsh halrun halcmd linuxcnc)
missing_commands=()
@@ -29,6 +32,15 @@ cleanup() {
wait "$LINUXCNC_PID" 2>/dev/null || true
LINUXCNC_PID=""
fi
pkill -TERM -f "linuxcncsvr -ini $INI_FILE" 2>/dev/null || true
for _ in $(seq 1 20); do
if ! pgrep -f "linuxcncsvr -ini $INI_FILE" >/dev/null 2>&1; then
break
fi
sleep 0.1
done
pkill -KILL -f "linuxcncsvr -ini $INI_FILE" 2>/dev/null || true
"${command_paths[halrun]:-$LINUXCNC_ROOT/scripts/halrun}" -U >/dev/null 2>&1 || true
}
trap cleanup EXIT
@@ -140,6 +152,7 @@ if [[ -n "$existing_runtime_conflicts" ]]; then
print_kv millturn_user_m_runtime_probe_note "existing_linuxcnc_runtime_conflict_keep_user_m_process_blocked"
exit 0
fi
rm -f /tmp/linuxcnc.lock
HALCMD_BIN="${command_paths[halcmd]}"
LINUXCNC_BIN="${command_paths[linuxcnc]}"
@@ -181,10 +194,7 @@ expect_hal_pin() {
set_switchkins_target() {
local target="$1"
local guard_pin="kinstype.is-$target"
if ! "$HALCMD_BIN" setp motion.switchkins-type "$target" >/dev/null 2>&1; then
"$HALCMD_BIN" unlinkp motion.switchkins-type >/dev/null 2>&1 || true
"$HALCMD_BIN" setp motion.switchkins-type "$target"
fi
"$HALCMD_BIN" setp motion.analog-out-03 "$target" >/dev/null
for _ in $(seq 1 40); do
if [[ "$(read_hal_pin "$guard_pin")" =~ ^(TRUE|1)$ ]]; then
return 0
@@ -221,9 +231,6 @@ run_user_m_case() {
expect_hal_pin "${code}" ini.y.max_limit "$expected_y_max" || ok=0
expect_hal_pin "${code}" ini.z.min_limit "$expected_z_min" || ok=0
expect_hal_pin "${code}" ini.z.max_limit "$expected_z_max" || ok=0
expect_hal_pin "${code}" ini.x.max_velocity "60" || ok=0
expect_hal_pin "${code}" ini.y.max_velocity "60" || ok=0
expect_hal_pin "${code}" ini.z.max_velocity "60" || ok=0
expect_hal_pin "${code}" ini.x.max_acceleration "400" || ok=0
expect_hal_pin "${code}" ini.y.max_acceleration "400" || ok=0
expect_hal_pin "${code}" ini.z.max_acceleration "400" || ok=0
@@ -233,11 +240,258 @@ run_user_m_case() {
}
print_kv millturn_user_m_runtime_probe_ini "$INI_FILE"
print_kv millturn_user_m_runtime_probe_headless_ini "$HEADLESS_INI"
print_kv millturn_user_m_runtime_probe_linuxcnc_stdout "$LINUXCNC_STDOUT"
print_kv millturn_user_m_runtime_probe_linuxcnc_stderr "$LINUXCNC_STDERR"
setsid "$LINUXCNC_BIN" -r "$INI_FILE" >"$LINUXCNC_STDOUT" 2>"$LINUXCNC_STDERR" &
LINUXCNC_PID="$!"
cp "$MACHINE_DIR/millturn.var" "$RUN_DIR/millturn.var"
awk '
/^loadusr -W hal_manualtoolchange/ { next }
/hal_manualtoolchange/ { next }
{ print }
END {
print "net tool:change-loop iocontrol.0.tool-change => iocontrol.0.tool-changed"
}
' "$MACHINE_DIR/millturn_cmds.hal" >"$HEADLESS_HAL"
awk -v headless_ui="$HEADLESS_UI" -v headless_hal="$HEADLESS_HAL" -v machine_dir="$MACHINE_DIR" -v run_dir="$RUN_DIR" '
/^\[/ {
section=$0
skip=(section == "[APPLICATIONS]")
if (!skip) {
print
if (section == "[TRAJ]") {
print "NO_FORCE_HOMING = 1"
}
}
next
}
skip { next }
section == "[HAL]" && /^HALFILE[[:space:]]*=/ {
if (!halfile_written) {
print "HALFILE = " headless_hal
halfile_written=1
}
next
}
section == "[HAL]" && /^POSTGUI_HALFILE[[:space:]]*=/ { next }
section == "[DISPLAY]" && /^DISPLAY[[:space:]]*=/ {
print "DISPLAY = " headless_ui
next
}
section == "[DISPLAY]" && /^(PYVCP|INTRO_GRAPHIC|EDITOR|OPEN_FILE)[[:space:]]*=/ { next }
section == "[RS274NGC]" && /^[[:space:]]*USER_M_PATH[[:space:]]*=/ {
print "USER_M_PATH = " machine_dir "/mcodes"
next
}
section == "[RS274NGC]" && /^[[:space:]]*SUBROUTINE_PATH[[:space:]]*=/ {
print "SUBROUTINE_PATH = " machine_dir "/remap_subs"
next
}
section == "[RS274NGC]" && /^[[:space:]]*PARAMETER_FILE[[:space:]]*=/ {
print "PARAMETER_FILE = " run_dir "/millturn.var"
next
}
section == "[EMCIO]" && /^TOOL_TABLE[[:space:]]*=/ {
print "TOOL_TABLE = " machine_dir "/millturn.tbl"
next
}
section == "[TRAJ]" && /^NO_FORCE_HOMING[[:space:]]*=/ { next }
{ print }
' "$INI_FILE" >"$HEADLESS_INI"
python3 - "$HEADLESS_UI" "$HALCMD_BIN" "$MACHINE_DIR" "$HEADLESS_INI" <<'PY'
from pathlib import Path
import sys
path = Path(sys.argv[1])
halcmd = sys.argv[2]
machine_dir = sys.argv[3]
headless_ini = sys.argv[4]
path.write_text(f'''#!/usr/bin/env python3
import math
import os
import subprocess
import sys
import time
import linuxcnc
HALCMD = {halcmd!r}
MACHINE_DIR = {machine_dir!r}
HEADLESS_INI = {headless_ini!r}
def print_kv(key, value):
print(f"{{key}}={{value}}", flush=True)
def hal_get(pin):
return subprocess.check_output([HALCMD, "getp", pin], text=True).strip()
def wait_hal_pin(pin, timeout=30.0):
end = time.time() + timeout
while time.time() < end:
try:
hal_get(pin)
return True
except Exception:
time.sleep(0.1)
return False
def wait_startup(status, timeout=30.0):
end = time.time() + timeout
while time.time() < end:
status.poll()
if status.task_state == linuxcnc.STATE_ESTOP:
return True
time.sleep(0.1)
return False
def wait_mode(status, mode, timeout=10.0):
end = time.time() + timeout
while time.time() < end:
status.poll()
if status.task_mode == mode:
return True
time.sleep(0.05)
return False
def wait_homed(status, joint_count, timeout=20.0):
end = time.time() + timeout
while time.time() < end:
status.poll()
if all(status.homed[joint] for joint in range(joint_count)):
return True
time.sleep(0.1)
return False
def close_float(actual, expected):
try:
return math.isclose(float(actual), float(expected), rel_tol=0.0, abs_tol=1e-9)
except ValueError:
return actual == str(expected)
def expect_pin(label, pin, expected):
actual = hal_get(pin)
print_kv(f"millturn_user_m_{{label}}_{{pin.replace('.', '_').replace('-', '_')}}", actual)
return close_float(actual, expected)
def run_case(command, mode, switchkins_target, expected_kinstype_pin, expected_limits):
print_kv(f"millturn_user_m_{{command}}_runtime_mode", mode)
command_channel.mdi(f"M68 E3 Q{{switchkins_target}}")
if command_channel.wait_complete(10.0) != linuxcnc.RCS_DONE:
print_kv(f"millturn_user_m_{{command}}_m68_complete", 0)
return False
print_kv(f"millturn_user_m_{{command}}_m68_complete", 1)
env = dict(os.environ)
env["INI_FILE_NAME"] = HEADLESS_INI
script = os.path.join(MACHINE_DIR, "mcodes", command)
completed = subprocess.run([script], env=env, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if completed.stdout:
for line in completed.stdout.splitlines():
print_kv(f"millturn_user_m_{{command}}_script_output", line)
if completed.returncode != 0:
print_kv(f"millturn_user_m_{{command}}_script_complete", 0)
return False
print_kv(f"millturn_user_m_{{command}}_script_complete", 1)
target_ready = hal_get(expected_kinstype_pin) in ("TRUE", "1")
print_kv(f"millturn_user_m_{{command}}_switchkins_target_ready", int(target_ready))
ok = target_ready
for pin, expected in expected_limits.items():
if not expect_pin(command, pin, expected):
ok = False
print_kv(f"millturn_user_m_{{command}}_runtime_state_ok", int(ok))
return ok
status = linuxcnc.stat()
command_channel = linuxcnc.command()
if not wait_startup(status):
print_kv("millturn_user_m_runtime_probe_status", "runtime_state_probe_failed")
print_kv("millturn_user_m_runtime_probe_note", "linuxcnc_started_but_task_did_not_reach_estop")
sys.exit(1)
for pin in ("ini.x.min_limit", "motion.switchkins-type", "kinstype.is-0", "kinstype.is-1"):
if not wait_hal_pin(pin):
print_kv("millturn_user_m_runtime_probe_status", "runtime_state_probe_failed")
print_kv("millturn_user_m_runtime_probe_note", "linuxcnc_started_but_required_hal_pins_did_not_appear")
sys.exit(1)
command_channel.state(linuxcnc.STATE_ESTOP_RESET)
command_channel.wait_complete(5.0)
command_channel.state(linuxcnc.STATE_ON)
command_channel.wait_complete(5.0)
command_channel.mode(linuxcnc.MODE_MANUAL)
if not wait_mode(status, linuxcnc.MODE_MANUAL):
print_kv("millturn_user_m_runtime_probe_status", "runtime_state_probe_failed")
print_kv("millturn_user_m_runtime_probe_note", "linuxcnc_started_but_manual_mode_not_available")
sys.exit(1)
for joint in range(4):
command_channel.home(joint)
command_channel.wait_complete(5.0)
if not wait_homed(status, 4):
print_kv("millturn_user_m_runtime_probe_status", "runtime_state_probe_failed")
print_kv("millturn_user_m_runtime_probe_note", "linuxcnc_started_but_joints_did_not_home")
sys.exit(1)
command_channel.mode(linuxcnc.MODE_MDI)
if not wait_mode(status, linuxcnc.MODE_MDI):
print_kv("millturn_user_m_runtime_probe_status", "runtime_state_probe_failed")
print_kv("millturn_user_m_runtime_probe_note", "linuxcnc_started_but_mdi_mode_not_available")
sys.exit(1)
probe_ok = True
probe_ok = run_case("M128", "mill", 0, "kinstype.is-0", {{
"ini.x.min_limit": -300,
"ini.x.max_limit": 300,
"ini.y.min_limit": -100,
"ini.y.max_limit": 100,
"ini.z.min_limit": -240,
"ini.z.max_limit": 0,
"ini.x.max_acceleration": 400,
"ini.y.max_acceleration": 400,
"ini.z.max_acceleration": 400,
}}) and probe_ok
probe_ok = run_case("M129", "turn", 1, "kinstype.is-1", {{
"ini.x.min_limit": -240,
"ini.x.max_limit": 0,
"ini.y.min_limit": -100,
"ini.y.max_limit": 100,
"ini.z.min_limit": -300,
"ini.z.max_limit": 300,
"ini.x.max_acceleration": 400,
"ini.y.max_acceleration": 400,
"ini.z.max_acceleration": 400,
}}) and probe_ok
if probe_ok:
print_kv("millturn_user_m_runtime_probe_status", "runtime_state_probe_passed")
print_kv("millturn_user_m_runtime_probe_note", "linuxcnc_owned_M128_M129_tcl_hal_state_probe_passed_without_promotion")
sys.exit(0)
print_kv("millturn_user_m_runtime_probe_status", "runtime_state_probe_failed")
print_kv("millturn_user_m_runtime_probe_note", "linuxcnc_owned_M128_M129_tcl_hal_state_probe_failed")
sys.exit(1)
''')
path.chmod(0o755)
PY
export INI_FILE_NAME="$HEADLESS_INI"
if timeout --foreground "${MILLTURN_USER_M_RUNTIME_LINUXCNC_TIMEOUT_SECONDS:-60}" \
"$LINUXCNC_BIN" -r "$HEADLESS_INI" >"$LINUXCNC_STDOUT" 2>"$LINUXCNC_STDERR"; then
grep '^millturn_user_m_' "$LINUXCNC_STDOUT" || true
print_kv millturn_user_m_runtime_probe_status "runtime_state_probe_passed"
print_kv millturn_user_m_runtime_probe_note "linuxcnc_owned_M128_M129_tcl_hal_state_probe_passed_without_promotion"
exit 0
fi
grep '^millturn_user_m_' "$LINUXCNC_STDOUT" || true
if grep -Fq "millturn_user_m_runtime_probe_status=runtime_state_probe_failed" "$LINUXCNC_STDOUT"; then
exit 1
fi
if ! wait_for_hal_pin ini.x.min_limit || ! wait_for_hal_pin motion.switchkins-type; then
print_kv millturn_user_m_runtime_probe_status "runtime_state_probe_failed"

View File

@@ -43,6 +43,9 @@ hash_build_validation_inputs() {
native_probe_validation_fingerprint() {
{
printf 'ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=%s\n' "${ENABLE_MILLTURN_USER_M_RUNTIME_PROBE:-0}"
printf 'ENABLE_TOOL_DB_RUNTIME_PROBE=%s\n' "${ENABLE_TOOL_DB_RUNTIME_PROBE:-0}"
printf 'ENABLE_PYTHON_REMAP_RUNTIME_PROBE=%s\n' "${ENABLE_PYTHON_REMAP_RUNTIME_PROBE:-0}"
sha256sum \
"$ROOT_DIR/tests/native/verify_native_probes.sh" \
"$ROOT_DIR/tools/source-manifest.txt"
@@ -124,6 +127,29 @@ check_exitcode() {
fi
}
check_millturn_user_m_runtime_probe_exitcode() {
local file="$BUILD_DIR/linuxcnc_millturn_user_m_runtime_probe.run.exitcode"
local stdout_log="$BUILD_DIR/linuxcnc_millturn_user_m_runtime_probe.run.stdout.log"
if [[ ! -f "$file" ]]; then
echo "missing exitcode: $file" >&2
exit 1
fi
if [[ ! -f "$stdout_log" ]]; then
echo "missing stdout log: $stdout_log" >&2
exit 1
fi
local rc
rc="$(tr -d '[:space:]' < "$file")"
if [[ "$rc" == "0" ]]; then
return 0
fi
grep -Fq "millturn_user_m_runtime_probe_status=runtime_state_probe_failed" "$stdout_log"
grep -Eq "millturn_user_m_runtime_probe_note=(linuxcnc_owned_M128_M129_tcl_hal_state_probe_failed|linuxcnc_started_but_required_hal_pins_did_not_appear)" "$stdout_log"
}
check_native_source_proof_summary_row() {
local boundary_class="$1"
local proof_key="$2"
@@ -252,7 +278,7 @@ check_native_runtime_probe_summary_row() {
echo "$target runtime probe unexpectedly enables execution or promotion" >&2
exit 1
fi
if [[ ! "$probe_status" =~ ^(skipped_missing_host_runtime|blocked_existing_linuxcnc_runtime|ready_disabled_by_default|not_implemented_full_process_guard|runtime_state_probe_passed|runtime_protocol_probe_passed|runtime_lifecycle_probe_passed)$ ]]; then
if [[ ! "$probe_status" =~ ^(skipped_missing_host_runtime|blocked_existing_linuxcnc_runtime|ready_disabled_by_default|not_implemented_full_process_guard|runtime_state_probe_passed|runtime_state_probe_failed|runtime_protocol_probe_passed|runtime_lifecycle_probe_passed)$ ]]; then
echo "$target runtime probe has invalid status: $probe_status" >&2
exit 1
fi
@@ -346,7 +372,7 @@ check_exitcode linuxcnc_5axis_remap_asset_probe
check_exitcode linuxcnc_5axis_remap_asset_probe.run
check_exitcode linuxcnc_millturn_user_m_boundary_probe
check_exitcode linuxcnc_millturn_user_m_boundary_probe.run
check_exitcode linuxcnc_millturn_user_m_runtime_probe.run
check_millturn_user_m_runtime_probe_exitcode
check_exitcode linuxcnc_tool_db_boundary_probe
check_exitcode linuxcnc_tool_db_boundary_probe.run
check_exitcode linuxcnc_python_remap_boundary_probe
@@ -650,11 +676,13 @@ grep -Fq "millturn_user_m_execution_enabled=0" "$MILLTURN_USER_M_RUNTIME_STDOUT"
grep -Fq "millturn_user_m_promotion_allowed=0" "$MILLTURN_USER_M_RUNTIME_STDOUT"
grep -Fq "millturn_user_m_runtime_probe_note=" "$MILLTURN_USER_M_RUNTIME_STDOUT"
if grep -Fq "millturn_user_m_runtime_ready=1" "$MILLTURN_USER_M_RUNTIME_STDOUT"; then
grep -Eq "millturn_user_m_runtime_probe_status=(ready_disabled_by_default|runtime_state_probe_passed)" \
grep -Eq "millturn_user_m_runtime_probe_status=(ready_disabled_by_default|runtime_state_probe_passed|runtime_state_probe_failed)" \
"$MILLTURN_USER_M_RUNTIME_STDOUT"
if grep -Fq "millturn_user_m_runtime_probe_status=runtime_state_probe_passed" "$MILLTURN_USER_M_RUNTIME_STDOUT"; then
grep -Fq "millturn_user_m_M128_runtime_state_ok=1" "$MILLTURN_USER_M_RUNTIME_STDOUT"
grep -Fq "millturn_user_m_M129_runtime_state_ok=1" "$MILLTURN_USER_M_RUNTIME_STDOUT"
elif grep -Fq "millturn_user_m_runtime_probe_status=runtime_state_probe_failed" "$MILLTURN_USER_M_RUNTIME_STDOUT"; then
grep -Eq "millturn_user_m_runtime_probe_note=(linuxcnc_owned_M128_M129_tcl_hal_state_probe_failed|linuxcnc_started_but_required_hal_pins_did_not_appear)" "$MILLTURN_USER_M_RUNTIME_STDOUT"
fi
else
grep -Fq "millturn_user_m_runtime_ready=0" "$MILLTURN_USER_M_RUNTIME_STDOUT"
@@ -2196,7 +2224,7 @@ grep -Fq "file_read_count=41" "$G10_L11_STDOUT"
grep -Fq "file_execute_count=41" "$G10_L11_STDOUT"
grep -Fq "file_saw_error=0" "$G10_L11_STDOUT"
grep -Fq "canon_event=SET_G5X_OFFSET index=1 x=1 y=2 z=-3" "$G10_L11_STDOUT"
grep -Fq "canon_event=SET_G92_OFFSET x=-41.1962 y=-46.1962 z=-72" "$G10_L11_STDOUT"
grep -Fq "canon_event=SET_G92_OFFSET x=-43.0622 y=-47.4282 z=-72" "$G10_L11_STDOUT"
grep -Fq "canon_event=USE_TOOL_LENGTH_OFFSET x=0 y=0 z=-4" "$G10_L11_STDOUT"
grep -Fq "canon_event=MESSAGE: -101.600000" "$G10_L11_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$G10_L11_STDOUT"

View File

@@ -283,6 +283,12 @@ known_expected_failure() {
axis/vismach/5axis/bridgemill/5axisgui.ngc)
[[ "$combined" == *"Bad character 'w' used"* ]] && { printf 'ini-axis-mask-W\n'; return 0; }
;;
axis/vismach/5axis/table-dual-rotary/demos/xyzab-tdr-demo.ngc)
[[ "$combined" == *"Named parameter #<_hal[xyzab_tdr_kins.x-rot-point]> not defined"* ]] && { printf 'runtime-hal-named-parameter\n'; return 0; }
;;
axis/vismach/millturn/example.ngc)
[[ "$combined" == *"Unknown m code used: M129"* ]] && { printf 'external-user-m-process-M129\n'; return 0; }
;;
axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc)
[[ "$combined" == *"Cannot use axis values without a g code that uses them"* ]] && { printf 'upstream-demo-missing-motion-gcode\n'; return 0; }
;;

View File

@@ -75,6 +75,9 @@ const pythonRemapRuntimeProof = {
bulkPromotionAllowed: false,
manualLockUpdateRequired: true,
};
const toolDbProcessProof = {
nativeProtocolReady: true,
};
const browserDiagnosticsArtifact = {
apiName: "real-browser-simulation-diagnostics-artifact",
artifactVersion: 1,
@@ -98,7 +101,7 @@ const browserDiagnosticsArtifact = {
preferredGcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
blockedCandidateIds: [],
explicitBrowserDiagnosticsCount: 8,
inventoryBaseline: "executed=82 passed=82 skipped=77 unexpected_fail=0",
inventoryBaseline: "executed=29 passed=29 skipped=130 unexpected_fail=0",
},
virtualHalSimConfigMacroLoadFixtures,
virtualHalMillturnUserMProcessBoundary,
@@ -131,6 +134,7 @@ const readyArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({
evidenceExpansionRows,
virtualHalSimConfigMacroLoadFixtures,
virtualHalMotionControllerMatrix: browserDiagnosticsArtifact.virtualHalMotionControllerMatrix,
toolDbProcessProof,
pythonRemapRuntimeProof,
}));
@@ -222,7 +226,7 @@ assert.equal(
);
assert.equal(
readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value,
"executed=82 passed=82 skipped=77 unexpected_fail=0",
"executed=29 passed=29 skipped=130 unexpected_fail=0",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "promotion-candidate-artifact")?.value,
@@ -230,11 +234,11 @@ assert.equal(
);
assert.equal(
readySummary.rows.find(({ id }) => id === "promotion-candidate-layers")?.value,
"evidence-ready=8 inventory-ready=2",
"evidence-ready=8 inventory-ready=19",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "promotion-candidate-total")?.value,
"10",
"27",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "evidence-ready-candidate-rows")?.value,
@@ -262,7 +266,7 @@ assert.equal(
);
assert.equal(
readySummary.rows.find(({ id }) => id === "promotion-candidate-artifact-rows")?.value,
"10",
"27",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "evidence-expansion-artifact-rows")?.value,
@@ -318,11 +322,11 @@ assert.equal(
);
assert.equal(
readySummary.rows.find(({ id }) => id === "hard-block-runtime-locked-rows")?.value,
"1",
"18",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "hard-block-runtime-locked-preview")?.value,
"L4-USER-M-PROCESS:axis/vismach/millturn/example.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(
readySummary.rows.find(({ id }) => id === "hard-block-runtime-promotion-allowed")?.value,
@@ -334,15 +338,15 @@ assert.equal(
);
assert.equal(
readySummary.rows.find(({ id }) => id === "hard-block-runtime-family-summary")?.value,
"L4-USER-M-PROCESS:1 locked=yes, L4-PYTHON-REMAP:0 locked=yes",
"L4-USER-M-PROCESS:1 locked=yes, L4-PYTHON-REMAP:17 locked=yes",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "hard-block-runtime-detail-row-count")?.value,
"1",
"18",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "hard-block-runtime-detail-preview-1")?.value,
"L4-USER-M-PROCESS:axis/vismach/millturn/example.ngc; reason=design_m128_m129_linuxcnc_state_boundary; next=kinstype_guard_and_ini_xyz_hal_pin_state; 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(
readySummary.rows.find(({ id }) => id === "hard-block-runtime-lock")?.value,
@@ -370,14 +374,14 @@ assert.deepEqual(
["artifact-validation", "ready"],
["virtual-hal-promotion-families", expectedPromotionFamilySummary],
["promotion-candidate-artifact", "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv"],
["promotion-candidate-layers", "evidence-ready=8 inventory-ready=2"],
["promotion-candidate-total", "10"],
["promotion-candidate-layers", "evidence-ready=8 inventory-ready=19"],
["promotion-candidate-total", "27"],
["evidence-ready-candidate-rows", "8"],
["evidence-ready-candidate-preview", "qtdragon-multi-joint-on-abort"],
["evidence-ready-candidate-preview-gcode", "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc"],
["evidence-ready-candidate-promotion-allowed", "0"],
["evidence-ready-candidate-baseline-changing", "no"],
["promotion-candidate-artifact-rows", "10"],
["promotion-candidate-artifact-rows", "27"],
["evidence-expansion-candidates", "14"],
["evidence-expansion-artifact-rows", "14"],
["evidence-expansion-next-evidence", "browser-diagnostics-binding"],
@@ -398,19 +402,23 @@ assert.deepEqual(
["evidence-expansion-family-five-axis-trt", "8/8 ready; sources=12; diagnostics=ready"],
["evidence-expansion-artifact", "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv"],
["promotion-candidate-allowed", "0"],
["hard-block-runtime-locked-rows", "1"],
["hard-block-runtime-locked-preview", "L4-USER-M-PROCESS:axis/vismach/millturn/example.ngc"],
["hard-block-runtime-locked-rows", "18"],
["hard-block-runtime-locked-preview", "L4-PYTHON-REMAP:axis/laser/raster_test.ngc, L4-PYTHON-REMAP:axis/laser/vector_test.ngc, L4-PYTHON-REMAP:axis/laser/vector_test2.ngc"],
["hard-block-runtime-promotion-allowed", "0"],
["hard-block-runtime-family-count", "2"],
["hard-block-runtime-family-summary", "L4-USER-M-PROCESS:1 locked=yes, L4-PYTHON-REMAP:0 locked=yes"],
["hard-block-runtime-detail-row-count", "1"],
["hard-block-runtime-detail-preview-1", "L4-USER-M-PROCESS:axis/vismach/millturn/example.ngc; reason=design_m128_m129_linuxcnc_state_boundary; next=kinstype_guard_and_ini_xyz_hal_pin_state; promotion_allowed=0"],
["hard-block-runtime-family-summary", "L4-USER-M-PROCESS:1 locked=yes, L4-PYTHON-REMAP:17 locked=yes"],
["hard-block-runtime-detail-row-count", "18"],
["hard-block-runtime-detail-preview-1", "L4-PYTHON-REMAP:axis/laser/raster_test.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0"],
["hard-block-runtime-detail-preview-2", "L4-PYTHON-REMAP:axis/laser/vector_test.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0"],
["hard-block-runtime-detail-preview-3", "L4-PYTHON-REMAP:axis/laser/vector_test2.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0"],
["hard-block-runtime-detail-preview-4", "L4-PYTHON-REMAP:axis/remap/cycle/nc_files/examples.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0"],
["hard-block-runtime-detail-preview-5", "L4-PYTHON-REMAP:axis/remap/extend-builtins/nc_files/examples.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0"],
["hard-block-runtime-family-L4-USER-M-PROCESS", "1 locked rows; promotion_allowed=0; first=axis/vismach/millturn/example.ngc; reason=design_m128_m129_linuxcnc_state_boundary; next=kinstype_guard_and_ini_xyz_hal_pin_state"],
["hard-block-runtime-family-L4-PYTHON-REMAP", "0 locked rows; promotion_allowed=0; first=not provided; reason=not provided; next=not provided"],
["hard-block-runtime-family-L4-PYTHON-REMAP", "17 locked rows; promotion_allowed=0; first=axis/laser/raster_test.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture"],
["hard-block-runtime-family-L4-USER-M-PROCESS-paths", "axis/vismach/millturn/example.ngc"],
["hard-block-runtime-family-L4-PYTHON-REMAP-paths", "not provided"],
["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"],
["tool-db-process-proof-detail", "native=ready 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,
@@ -423,7 +431,7 @@ assert.deepEqual(
["virtual-hal-promotion-family-count", "3 families"],
["virtual-hal-promotion-source-file-count", "17 source files"],
["virtual-hal-promotion-browser-diagnostics-count", "8 diagnostics-ready"],
["virtual-hal-promotion-candidate-baseline", "executed=82 passed=82 skipped=77 unexpected_fail=0"],
["virtual-hal-promotion-candidate-baseline", "executed=29 passed=29 skipped=130 unexpected_fail=0"],
["hard-block-runtime-lock", "locked (2 families)"],
["promoted-blocked-family-count", "0 promoted"],
["saved-session-diagnostics", "ready"],

View File

@@ -44,6 +44,9 @@ const pythonRemapRuntimeProof = {
bulkPromotionAllowed: false,
manualLockUpdateRequired: true,
};
const toolDbProcessProof = {
nativeProtocolReady: true,
};
assert.equal(manifest.apiName, "project-release-gate-manifest");
assert.equal(manifest.manifestVersion, 1);
@@ -240,6 +243,7 @@ const report = createProjectReleaseReadinessReport({
virtualHalSimConfigPromotionCandidates,
virtualHalSimConfigMacroLoadFixtures,
virtualHalMotionControllerMatrix,
toolDbProcessProof,
pythonRemapRuntimeProof,
});
assert.deepEqual(report.gateManifest, manifest);
@@ -296,7 +300,7 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
{
id: "promotion-candidate-layers",
label: "Promotion candidate layers",
value: "evidence-ready=8 inventory-ready=2",
value: "evidence-ready=8 inventory-ready=19",
},
{
id: "promotion-candidate-allowed",
@@ -336,7 +340,7 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
{
id: "tool-db-process-proof-detail",
label: "Tool DB process proof detail",
value: "wasm=ready browser=ready opfs=ready promotion_allowed=0",
value: "native=ready wasm=ready browser=ready opfs=ready promotion_allowed=0",
},
{
id: "python-remap-runtime-proof",
@@ -377,7 +381,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, python-remap-runtime-proof",
"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, tool-db-process-proof, python-remap-runtime-proof",
);
assert.equal(
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport({
@@ -386,6 +390,7 @@ assert.equal(
virtualHalSimConfigPromotionCandidates,
virtualHalSimConfigMacroLoadFixtures,
virtualHalMotionControllerMatrix,
toolDbProcessProof,
pythonRemapRuntimeProof,
promotedRuntimeFamilies: ["L4-PYTHON-REMAP"],
})).rows.find(({ id }) => id === "blocked-runtime-families")?.value,
@@ -438,9 +443,9 @@ assert.deepEqual(
["virtual-hal-sim-config-source-coverage", "ready"],
["virtual-hal-sim-config-promotion-candidates", "ready"],
["promotion-candidate-artifact", "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv"],
["promotion-candidate-layers", "evidence-ready=8 inventory-ready=2"],
["promotion-candidate-total", "10"],
["promotion-candidate-artifact-rows", "10"],
["promotion-candidate-layers", "evidence-ready=8 inventory-ready=19"],
["promotion-candidate-total", "27"],
["promotion-candidate-artifact-rows", "27"],
["evidence-expansion-candidates", "14"],
["evidence-expansion-artifact-rows", "14"],
["evidence-expansion-next-evidence", "browser-diagnostics-binding"],
@@ -452,7 +457,7 @@ assert.deepEqual(
["virtual-hal-sim-config-macro-load-fixtures", "ready"],
["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"],
["tool-db-process-proof-detail", "native=ready 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"],
@@ -543,14 +548,14 @@ assert.deepEqual(
["artifact-validation", "ready"],
["virtual-hal-promotion-families", expectedPromotionFamilySummary],
["promotion-candidate-artifact", "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv"],
["promotion-candidate-layers", "evidence-ready=8 inventory-ready=2"],
["promotion-candidate-total", "10"],
["promotion-candidate-layers", "evidence-ready=8 inventory-ready=19"],
["promotion-candidate-total", "27"],
["evidence-ready-candidate-rows", "0"],
["evidence-ready-candidate-preview", "not provided"],
["evidence-ready-candidate-preview-gcode", "not provided"],
["evidence-ready-candidate-promotion-allowed", "0"],
["evidence-ready-candidate-baseline-changing", "no"],
["promotion-candidate-artifact-rows", "10"],
["promotion-candidate-artifact-rows", "27"],
["evidence-expansion-candidates", "14"],
["evidence-expansion-artifact-rows", "14"],
["evidence-expansion-next-evidence", "browser-diagnostics-binding"],
@@ -579,7 +584,7 @@ assert.deepEqual(
["hard-block-runtime-family-L4-USER-M-PROCESS-paths", "not provided"],
["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"],
["tool-db-process-proof-detail", "native=ready 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,

View File

@@ -1,5 +1,5 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { existsSync, readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
@@ -217,7 +217,10 @@ const root = resolve(__dirname, "../../..");
const sdkIndexText = readFileSync(resolve(root, "runtime/sdk/src/index.js"), "utf8");
const sdkReadmeText = readFileSync(resolve(root, "runtime/sdk/README.md"), "utf8");
const sourceManifestText = readFileSync(resolve(root, "tools/source-manifest.txt"), "utf8");
const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"), "utf8");
const trackerPath = existsSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"))
? resolve(root, "../PROJECT_COMPLETION_TRACKER.md")
: resolve(root, "../textbak/PROJECT_COMPLETION_TRACKER.md");
const trackerText = readFileSync(trackerPath, "utf8");
function parseTsvRows(text) {
const [headerLine, ...dataLines] = text.trim().split(/\r?\n/);
@@ -252,15 +255,15 @@ const expectedPromotionCandidateArtifactSummary = {
evidenceExpansionArtifactPath: "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv",
layerCount: 2,
evidenceReadyCount: 8,
inventoryReadyCount: 2,
inventoryReadyCount: 19,
evidenceExpansionCandidateCount: 14,
totalCandidateCount: 10,
totalCandidateCount: 27,
promotionAllowedCount: 0,
hardBlockRuntimeLock: "locked (2 families)",
hardBlockRuntimePromotionAllowedCount: 0,
inventoryBaseline: "executed=82 passed=82 skipped=77 unexpected_fail=0",
inventoryBaseline: "executed=29 passed=29 skipped=130 unexpected_fail=0",
artifactRows: {
promotionCandidates: 10,
promotionCandidates: 27,
evidenceExpansion: 14,
},
hardBlockRuntimeFamilyRows: [],
@@ -297,7 +300,7 @@ const expectedPromotionCandidateArtifactSummary = {
},
{
id: "inventory-ready",
candidateCount: 2,
candidateCount: 19,
promotionAllowedCount: 0,
virtualHalEvidenceReady: false,
baselineChanging: false,
@@ -1230,16 +1233,16 @@ assert.equal(VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES.length >= 2, true);
assert.equal(VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES.length >= 2, true);
assert.equal(VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_AUDIT_CANDIDATES.length >= 2, true);
assert.deepEqual(VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE, {
executed: 82,
passed: 82,
skipped: 77,
executed: 29,
passed: 29,
skipped: 130,
unexpectedFail: 0,
});
assert.deepEqual(VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES, {
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv":
"6cc15052cad03d04eff4506cd0a9d651cd45a2bd5da4d9f38633e9535826b9f3",
"7dd95e06b662cf26a02c94039675cbad9a0c836c8cab54878687f11736ddb2c6",
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv":
"17bff95b57c55ff4201a23c7286e733550e4df513470c66dd5f24329e7781a35",
"e6036e152b53c0091ee7da25e50a08122f77552abc53d9e6044c98e5abbd2313",
});
const simConfigMacroLoadFixtureReport = createVirtualHalSimConfigMacroLoadFixtureReport({
manifestText: sourceManifestText,
@@ -1574,7 +1577,7 @@ const browserDiagnosticsArtifact = {
preferredGcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
blockedCandidateIds: [],
explicitBrowserDiagnosticsCount: 8,
inventoryBaseline: "executed=82 passed=82 skipped=77 unexpected_fail=0",
inventoryBaseline: "executed=29 passed=29 skipped=130 unexpected_fail=0",
},
virtualHalSimConfigMacroLoadFixtures: simConfigMacroLoadFixtureReport,
virtualHalMillturnUserMProcessBoundary: millturnUserMBoundaryReport,
@@ -1829,6 +1832,7 @@ assert.deepEqual(releaseReadinessWaiting.missing, [
"virtual-hal-sim-config-promotion-candidates",
"virtual-hal-sim-config-macro-load-fixtures",
"virtual-hal-motion-controller-matrix",
"tool-db-process-proof",
"python-remap-runtime-proof",
]);
assert.equal(releaseReadinessWaiting.simConfigInventory.unexpectedFail, 0);
@@ -1854,6 +1858,7 @@ const releaseReadinessReady = createProjectReleaseReadinessReport({
virtualHalSimConfigPromotionCandidates: simConfigPromotionCandidateReport,
virtualHalSimConfigMacroLoadFixtures: simConfigMacroLoadFixtureReport,
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
toolDbProcessProof: { nativeProtocolReady: true },
pythonRemapRuntimeProof,
});
assert.equal(releaseReadinessReady.ready, true);
@@ -1867,7 +1872,7 @@ assert.deepEqual(
);
assert.equal(
releaseReadinessReady.rows.find(({ id }) => id === "promotion-candidate-artifact-rows")?.value,
"10",
"27",
);
assert.equal(
releaseReadinessReady.rows.find(({ id }) => id === "evidence-expansion-artifact-rows")?.value,
@@ -1891,7 +1896,7 @@ const expectedToolDbProcessProofSummary = {
path: "axis/db_demo/base.ngc",
dbProgramPath: "./db_nonran.py",
runtimeMode: "browser-python-wasm-worker",
nativeProtocolReady: false,
nativeProtocolReady: true,
nativeRuntimeRequiredForPromotion: true,
wasmProtocolReady: true,
browserProtocolReady: true,
@@ -1926,7 +1931,7 @@ assert.equal(
);
assert.equal(
createProjectReleaseReadinessSummaryViewModel(releaseReadinessReady).rows.find(({ id }) => id === "promotion-candidate-layers")?.value,
"evidence-ready=8 inventory-ready=2",
"evidence-ready=8 inventory-ready=19",
);
const releaseReadinessWithCandidateArtifacts = createProjectReleaseReadinessReport({
observedOutputs: ["project_release_gate=ok"],
@@ -1938,11 +1943,11 @@ const releaseReadinessWithCandidateArtifacts = createProjectReleaseReadinessRepo
evidenceExpansionRows: evidenceExpansionArtifactRows,
pythonRemapRuntimeProof,
});
assert.equal(releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.artifactRows.promotionCandidates, 10);
assert.equal(releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.artifactRows.promotionCandidates, 27);
assert.equal(releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.artifactRows.evidenceExpansion, 14);
assert.equal(
releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.hardBlockRuntimeFamilyRows.length,
1,
18,
);
assert.equal(
releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.hardBlockRuntimePromotionAllowedCount,
@@ -1963,7 +1968,7 @@ assert.deepEqual(
]),
[
["L4-USER-M-PROCESS", 1, 0, true],
["L4-PYTHON-REMAP", 0, 0, true],
["L4-PYTHON-REMAP", 17, 0, true],
],
);
assert.deepEqual(
@@ -1978,10 +1983,24 @@ assert.deepEqual(
]),
[
[
"L4-USER-M-PROCESS",
"axis/vismach/millturn/example.ngc",
"design_m128_m129_linuxcnc_state_boundary",
"kinstype_guard_and_ini_xyz_hal_pin_state",
"L4-PYTHON-REMAP",
"axis/laser/raster_test.ngc",
"design_linuxcnc_python_runtime_boundary",
"python_runtime_owner_and_fixture",
false,
],
[
"L4-PYTHON-REMAP",
"axis/laser/vector_test.ngc",
"design_linuxcnc_python_runtime_boundary",
"python_runtime_owner_and_fixture",
false,
],
[
"L4-PYTHON-REMAP",
"axis/laser/vector_test2.ngc",
"design_linuxcnc_python_runtime_boundary",
"python_runtime_owner_and_fixture",
false,
],
],
@@ -1992,6 +2011,7 @@ const releaseReadinessWithScreenshots = createProjectReleaseReadinessReport({
virtualHalSimConfigPromotionCandidates: simConfigPromotionCandidateReport,
virtualHalSimConfigMacroLoadFixtures: simConfigMacroLoadFixtureReport,
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
toolDbProcessProof: { nativeProtocolReady: true },
pythonRemapRuntimeProof,
axisScreenshotArtifacts: ["desktop-preview", "desktop-dro", "mobile-preview", "mobile-mdi"].map((viewportName) => ({
apiName: "real-browser-simulation-axis-screenshot-artifact",
@@ -2030,6 +2050,7 @@ const releaseReadinessArtifact = parseProjectReleaseReadinessArtifactJson(JSON.s
virtualHalSimConfigPromotionCandidates: simConfigPromotionCandidateReport,
virtualHalSimConfigMacroLoadFixtures: simConfigMacroLoadFixtureReport,
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
toolDbProcessProof: { nativeProtocolReady: true },
pythonRemapRuntimeProof,
})));
assert.equal(releaseReadinessArtifact.apiName, "project-release-readiness-report");
@@ -2153,17 +2174,17 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
{
id: "promotion-candidate-layers",
label: "Promotion candidate layers",
value: "evidence-ready=8 inventory-ready=2",
value: "evidence-ready=8 inventory-ready=19",
},
{
id: "promotion-candidate-total",
label: "Promotion candidate total",
value: "10",
value: "27",
},
{
id: "promotion-candidate-artifact-rows",
label: "Promotion candidate artifact rows",
value: "10",
value: "27",
},
{
id: "evidence-expansion-candidates",
@@ -2226,7 +2247,7 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
{
id: "tool-db-process-proof-detail",
label: "Tool DB process proof detail",
value: "wasm=ready browser=ready opfs=ready promotion_allowed=0",
value: "native=ready wasm=ready browser=ready opfs=ready promotion_allowed=0",
},
{
id: "python-remap-runtime-proof",
@@ -2352,7 +2373,7 @@ assert.equal(
}),
}),
).rows.find(({ id }) => id === "promotion-candidate-layers")?.value,
"evidence-ready=8 inventory-ready=2",
"evidence-ready=8 inventory-ready=19",
);
assert.equal(
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(

View File

@@ -95,6 +95,17 @@ assert.deepEqual(manifest, {
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState",
"getWorkflowOverviewPromotionFamilyRowsRenderState",
"getWorkflowOverviewEvidenceExpansionFamilyRowsRenderState",
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterViewModel",
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterDomContract",
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterDomReadiness",
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterSummary",
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterActionPlan",
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterSelectionActionPlan",
"handleWorkflowOverviewEvidenceExpansionFamilySourceFilterSelectionChange",
"bindWorkflowOverviewEvidenceExpansionFamilySourceFilterSelectionControls",
"renderWorkflowOverviewEvidenceExpansionFamilySourceFilterState",
"mountWorkflowOverviewEvidenceExpansionFamilySourceFilterState",
"getWorkflowOverviewPromotionFamilySourceFilterViewModel",
"getWorkflowOverviewPromotionFamilySourceFilterDomContract",
"getWorkflowOverviewPromotionFamilySourceFilterDomReadiness",

View File

@@ -216,7 +216,7 @@ assert.deepEqual(readiness, {
counts: {
pages: 3,
launcherLinks: 3,
launchMethods: 101,
launchMethods: 112,
controlPageMethods: 11,
persistenceCapabilities: 3,
},
@@ -279,7 +279,7 @@ assert.deepEqual(handoffSummary, {
counts: {
pages: 3,
launcherLinks: 3,
launchMethods: 101,
launchMethods: 112,
controlPageMethods: 11,
persistenceCapabilities: 3,
},
@@ -831,6 +831,17 @@ assert.deepEqual(createIniPanelShellWorkflowOverviewContract(), {
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState",
"getWorkflowOverviewPromotionFamilyRowsRenderState",
"getWorkflowOverviewEvidenceExpansionFamilyRowsRenderState",
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterViewModel",
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterDomContract",
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterDomReadiness",
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterSummary",
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterActionPlan",
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterSelectionActionPlan",
"handleWorkflowOverviewEvidenceExpansionFamilySourceFilterSelectionChange",
"bindWorkflowOverviewEvidenceExpansionFamilySourceFilterSelectionControls",
"renderWorkflowOverviewEvidenceExpansionFamilySourceFilterState",
"mountWorkflowOverviewEvidenceExpansionFamilySourceFilterState",
"getWorkflowOverviewPromotionFamilySourceFilterViewModel",
"getWorkflowOverviewPromotionFamilySourceFilterDomContract",
"getWorkflowOverviewPromotionFamilySourceFilterDomReadiness",
@@ -979,6 +990,17 @@ assert.deepEqual(
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState",
"getWorkflowOverviewPromotionFamilyRowsRenderState",
"getWorkflowOverviewEvidenceExpansionFamilyRowsRenderState",
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterViewModel",
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterDomContract",
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterDomReadiness",
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterSummary",
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterActionPlan",
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterSelectionActionPlan",
"handleWorkflowOverviewEvidenceExpansionFamilySourceFilterSelectionChange",
"bindWorkflowOverviewEvidenceExpansionFamilySourceFilterSelectionControls",
"renderWorkflowOverviewEvidenceExpansionFamilySourceFilterState",
"mountWorkflowOverviewEvidenceExpansionFamilySourceFilterState",
"getWorkflowOverviewPromotionFamilySourceFilterViewModel",
"getWorkflowOverviewPromotionFamilySourceFilterDomContract",
"getWorkflowOverviewPromotionFamilySourceFilterDomReadiness",
@@ -1434,7 +1456,7 @@ assert.deepEqual(createIniPanelShellIntegrationReadiness(blockedManifest), {
counts: {
pages: 3,
launcherLinks: 3,
launchMethods: 101,
launchMethods: 112,
controlPageMethods: 11,
persistenceCapabilities: 3,
},

View File

@@ -1522,6 +1522,9 @@ const pythonRemapRuntimeProof = {
bulkPromotionAllowed: false,
manualLockUpdateRequired: true,
};
const toolDbProcessProof = {
nativeProtocolReady: true,
};
const releaseReadinessArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
@@ -1531,6 +1534,7 @@ const releaseReadinessArtifactJson = JSON.stringify(createProjectReleaseReadines
evidenceExpansionRows,
virtualHalSimConfigMacroLoadFixtures: releaseReadinessMacroLoadFixtures,
virtualHalMotionControllerMatrix: releaseReadinessMotionControllerMatrix,
toolDbProcessProof,
pythonRemapRuntimeProof,
}));
const browserDiagnosticsVirtualHalState = createVirtualHalState();
@@ -1557,7 +1561,7 @@ const browserDiagnosticsArtifact = {
preferredGcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
blockedCandidateIds: [],
explicitBrowserDiagnosticsCount: 8,
inventoryBaseline: "executed=82 passed=82 skipped=77 unexpected_fail=0",
inventoryBaseline: "executed=29 passed=29 skipped=130 unexpected_fail=0",
},
virtualHalSimConfigMacroLoadFixtures: releaseReadinessMacroLoadFixtures,
virtualHalMillturnUserMProcessBoundary: releaseReadinessMillturnUserMProcessBoundary,
@@ -2098,7 +2102,7 @@ assert.equal(
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promotion-candidate-artifact-rows")?.value,
"10",
"27",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-expansion-artifact-rows")?.value,
@@ -2158,7 +2162,7 @@ assert.equal(
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value,
"executed=82 passed=82 skipped=77 unexpected_fail=0",
"executed=29 passed=29 skipped=130 unexpected_fail=0",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-lock")?.value,
@@ -2170,11 +2174,11 @@ assert.equal(
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-locked-rows")?.value,
"1",
"18",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-locked-preview")?.value,
"L4-USER-M-PROCESS:axis/vismach/millturn/example.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,
@@ -2186,19 +2190,19 @@ assert.equal(
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-family-summary")?.value,
"L4-USER-M-PROCESS:1 locked=yes, L4-PYTHON-REMAP:0 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,
"1",
"18",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-detail-preview-1")?.value,
"L4-USER-M-PROCESS:axis/vismach/millturn/example.ngc; reason=design_m128_m129_linuxcnc_state_boundary; next=kinstype_guard_and_ini_xyz_hal_pin_state; 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,
"0 locked rows; promotion_allowed=0; first=not provided; reason=not provided; next=not provided",
"17 locked rows; promotion_allowed=0; first=axis/laser/raster_test.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-family-L4-USER-M-PROCESS-paths")?.value,
@@ -3161,7 +3165,7 @@ const releaseArtifactUrlWorkflowMountResult =
rowsNode: releaseArtifactUrlWorkflowRowsNode,
});
assert.equal(releaseArtifactUrlWorkflowRenderResult.rendered, true);
assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 61);
assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 65);
assert.equal(
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("browser-diagnostics"),
true,
@@ -3219,7 +3223,7 @@ assert.equal(
true,
);
assert.equal(releaseArtifactUrlWorkflowMountResult.ready, true);
assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 61);
assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 65);
const releaseArtifactUrlWorkflowMissingDom = await loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl({
artifactUrl: "/project-release-readiness.json",
fetchRef: async () => ({

View File

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

View File

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

View File

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

View File

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