Update wasm port validation state
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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),
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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; }
|
||||
;;
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -95,6 +95,17 @@ assert.deepEqual(manifest, {
|
||||
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState",
|
||||
"getWorkflowOverviewPromotionFamilyRowsRenderState",
|
||||
"getWorkflowOverviewEvidenceExpansionFamilyRowsRenderState",
|
||||
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterViewModel",
|
||||
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterDomContract",
|
||||
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterDomReadiness",
|
||||
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterSummary",
|
||||
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterActionPlan",
|
||||
"getWorkflowOverviewEvidenceExpansionFamilySourceFilterSelectionActionPlan",
|
||||
"handleWorkflowOverviewEvidenceExpansionFamilySourceFilterSelectionChange",
|
||||
"bindWorkflowOverviewEvidenceExpansionFamilySourceFilterSelectionControls",
|
||||
"renderWorkflowOverviewEvidenceExpansionFamilySourceFilterState",
|
||||
"mountWorkflowOverviewEvidenceExpansionFamilySourceFilterState",
|
||||
"getWorkflowOverviewPromotionFamilySourceFilterViewModel",
|
||||
"getWorkflowOverviewPromotionFamilySourceFilterDomContract",
|
||||
"getWorkflowOverviewPromotionFamilySourceFilterDomReadiness",
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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 () => ({
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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/);
|
||||
|
||||
Reference in New Issue
Block a user