53 个 Python-remap inventory rows 批量提升为 PASS

结论:53 个 Python-remap inventory rows 已完成 row proof、browser proof 与 inventory baseline promotion,当前 inventory baseline 为 PASS=82 / SKIP=77 / unexpected_fail=0。
This commit is contained in:
2026-06-20 05:53:49 +08:00
parent ac52b48d62
commit b057f8038b
29 changed files with 1701 additions and 245 deletions

View File

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

View File

@@ -421,7 +421,7 @@
preferredGcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
blockedCandidateIds: [],
explicitBrowserDiagnosticsCount: 8,
inventoryBaseline: "executed=29 passed=29 skipped=130 unexpected_fail=0",
inventoryBaseline: "executed=82 passed=82 skipped=77 unexpected_fail=0",
},
virtualHalSimConfigMacroLoadFixtures: createVirtualHalSimConfigMacroLoadFixtureReport({
manifestEntries: [
@@ -637,7 +637,7 @@
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promotion-candidate-layers")?.value,
"evidence-ready=8 inventory-ready=19",
"evidence-ready=8 inventory-ready=2",
"workflow overview release artifact URL workflow candidate layers",
);
assertEqual(
@@ -772,7 +772,7 @@
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value,
"executed=29 passed=29 skipped=130 unexpected_fail=0",
"executed=82 passed=82 skipped=77 unexpected_fail=0",
"workflow overview release artifact URL workflow candidate baseline",
);
assertEqual(
@@ -787,7 +787,7 @@
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-locked-rows")?.value,
"18",
"1",
"workflow overview release artifact URL workflow locked hard-block rows",
);
assertEqual(
@@ -807,17 +807,17 @@
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-family-summary")?.value,
"L4-USER-M-PROCESS:1 locked=yes, L4-PYTHON-REMAP:17 locked=yes",
"L4-USER-M-PROCESS:1 locked=yes, L4-PYTHON-REMAP:0 locked=yes",
"workflow overview release artifact URL workflow hard-block family summary",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-detail-row-count")?.value,
"18",
"1",
"workflow overview release artifact URL workflow hard-block detail count",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-detail-preview-1")?.value,
"L4-PYTHON-REMAP:axis/laser/raster_test.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0",
"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",
"workflow overview release artifact URL workflow hard-block detail preview",
);
assertEqual(
@@ -948,7 +948,7 @@
);
assertEqual(
workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-value="promotion-candidate-layers"]').textContent,
"evidence-ready=8 inventory-ready=19",
"evidence-ready=8 inventory-ready=2",
"workflow overview fixed URL workflow form candidate layer row",
);
assertEqual(

View File

@@ -98,6 +98,7 @@
"next-boundary-recommendations.tsv",
"next-boundary-worklist.tsv",
"promotion-candidates.tsv",
"python-remap-browser-row-proof.tsv",
"python-remap-bulk-promotion-plan.tsv",
"python-remap-boundary-summary.tsv",
"python-remap-family-summary.tsv",
@@ -109,6 +110,7 @@
"python-remap-row-runtime-proof.tsv",
"python-remap-runtime-contract.tsv",
"python-remap-runtime-gates.tsv",
"python-remap-wasm-node-row-proof.tsv",
"runtime-boundary-contract-summary.tsv",
"runtime-boundary-family-host-readiness.tsv",
"runtime-boundary-host-preflight.tsv",
@@ -153,7 +155,7 @@
"build/native/source-probes.tsv",
];
if (wasmArtifactNames.length !== 57 || new Set(wasmArtifactNames).size !== wasmArtifactNames.length) {
if (wasmArtifactNames.length !== 59 || new Set(wasmArtifactNames).size !== wasmArtifactNames.length) {
throw new Error("browser_generated_artifact_documentation_coverage: WASM artifact list drift");
}
if (nativeArtifactTokens.length !== 8 || new Set(nativeArtifactTokens).size !== nativeArtifactTokens.length) {

View File

@@ -33,10 +33,22 @@ self.linuxCncPythonRemapRuntimeProvider = {
return { value: "generator-finished" };
},
async stageNgcRemapAsset(message) {
return { value: message.ngcPath };
},
async rejectNgcOnlyStandalone(message) {
return { value: message.ngcPath };
},
async exportInterpreterState() {
return { value: "interpreter-state-bound" };
},
async exportCanonicalEvents() {
return { value: "canonical-events-bound" };
},
async exportDiagnostics() {
return { value: "diagnostics-exported" };
},

View File

@@ -6,11 +6,14 @@
</head>
<body>
<pre id="status">running</pre>
<pre id="python-remap-browser-row-proof"></pre>
<script type="module">
import {
createLinuxCncPythonRemapBrowserWorkerAdapter,
createLinuxCncPythonRemapRowRuntimePort,
createLinuxCncPythonRemapRuntimePort,
createPythonRemapRuntimeDiagnostics,
validatePythonRemapRowRuntimeTranscript,
validatePythonRemapLifecycleTranscript,
} from "../../runtime/sdk/src/index.js";
@@ -22,6 +25,58 @@
}
}
function assertDeepEqual(actual, expected, label) {
const actualText = JSON.stringify(actual);
const expectedText = JSON.stringify(expected);
if (actualText !== expectedText) {
throw new Error(`${label}: expected ${expectedText}, got ${actualText}`);
}
}
function parseTsv(text, expectedHeaders = null) {
const [headerLine, ...rows] = text.trim().split("\n");
const headers = headerLine.split("\t");
if (expectedHeaders) {
assertDeepEqual(headers, expectedHeaders, "TSV header schema drift");
}
return rows.filter(Boolean).map((row) => {
const values = row.split("\t");
return Object.fromEntries(headers.map((header, index) => [header, values[index] ?? ""]));
});
}
function splitSummaryList(value) {
return String(value ?? "-")
.split(",")
.map((entry) => entry.trim())
.filter((entry) => entry.length > 0 && entry !== "-");
}
function listValue(values) {
const filtered = [...new Set(values.filter(Boolean))].sort();
return filtered.length === 0 ? "-" : filtered.join(",");
}
function flagValue(value) {
return value ? "1" : "0";
}
function tsvValue(value) {
return String(value ?? "-").replace(/\t|\r?\n/g, " ");
}
function verifySamePathSet(label, expectedPaths, actualPaths) {
assertDeepEqual([...actualPaths].sort(), [...expectedPaths].sort(), `${label}: path set drift`);
}
async function fetchText(path) {
const response = await fetch(path, { cache: "no-store" });
if (!response.ok) {
throw new Error(`${path}: fetch failed with ${response.status}`);
}
return response.text();
}
try {
const contractOnlyPort = createLinuxCncPythonRemapRuntimePort({
runtimeMode: "contract-only",
@@ -121,6 +176,232 @@
assertEqual(diagnostics.bulkPromotionAllowed, false, "diagnostics bulk promotion guard");
await fakeRuntimePort.close();
const rowRuntimeProofHeaders = [
"path",
"ini",
"family",
"row_runtime_port_api_ready",
"row_runtime_plan_ready",
"row_runtime_proof_targets",
"python_modules_staged",
"toplevel_executed",
"path_prepend_applied",
"python_remap_functions_bound",
"prolog_functions_bound",
"epilog_functions_bound",
"generator_lifecycle_ready",
"ngc_remap_assets_staged",
"ngc_only_subpaths_not_standalone",
"interpreter_state_binding_ready",
"canonical_events_ready",
"row_runtime_transcript_ready",
"native_pass_ready",
"wasm_node_pass_ready",
"browser_pass_ready",
"execution_enabled",
"proof_status",
"blockers",
"notes",
];
const wasmNodeProofHeaders = [
"path",
"ini",
"family",
"native_pass_ready",
"wasm_node_runtime_bridge_ready",
"wasm_node_plan_executed",
"module_import_ready",
"callable_lookup_ready",
"generator_lifecycle_ready",
"ngc_remap_assets_ready",
"ngc_only_subpaths_guarded",
"interpreter_state_binding_ready",
"canonical_events_ready",
"row_runtime_transcript_ready",
"transcript_phase_count",
"transcript_hash",
"wasm_node_pass_ready",
"browser_pass_ready",
"execution_enabled",
"promotion_allowed",
"proof_status",
"blockers",
"notes",
];
const boundaryHeaders = [
"path",
"ini",
"blocked",
"design_status",
"python_modules",
"python_remap_functions",
"prolog_functions",
"epilog_functions",
"remap_ngc_files",
"ngc_only_subpaths",
"hal_process",
"ui_process",
"halui_mdi_process",
"linuxcnc_owner",
"python_runtime_evidence",
"required_boundary",
"execution_enabled",
"notes",
];
const rowRuntimeProofRows = parseTsv(
await fetchText("../../build/wasm/sim-configs-inventory/python-remap-row-runtime-proof.tsv"),
rowRuntimeProofHeaders,
);
const wasmNodeRows = parseTsv(
await fetchText("../../build/wasm/sim-configs-inventory/python-remap-wasm-node-row-proof.tsv"),
wasmNodeProofHeaders,
);
const boundaryRows = parseTsv(
await fetchText("../../build/wasm/sim-configs-inventory/python-remap-boundary-summary.tsv"),
boundaryHeaders,
);
assertEqual(rowRuntimeProofRows.length, 53, "browser row proof input row count");
assertEqual(wasmNodeRows.length, 53, "browser row proof Node input row count");
assertEqual(boundaryRows.length, 53, "browser row proof boundary row count");
verifySamePathSet(
"browser_row_wasm_node_alignment",
rowRuntimeProofRows.map((row) => row.path),
wasmNodeRows.map((row) => row.path),
);
verifySamePathSet(
"browser_row_boundary_alignment",
rowRuntimeProofRows.map((row) => row.path),
boundaryRows.map((row) => row.path),
);
const wasmNodeByPath = new Map(wasmNodeRows.map((row) => [row.path, row]));
const boundaryByPath = new Map(boundaryRows.map((row) => [row.path, row]));
const browserProofHeaders = [
"path",
"ini",
"family",
"wasm_node_pass_ready",
"browser_runtime_bridge_ready",
"browser_worker_plan_executed",
"module_import_ready",
"callable_lookup_ready",
"generator_lifecycle_ready",
"ngc_remap_assets_ready",
"ngc_only_subpaths_guarded",
"interpreter_state_binding_ready",
"canonical_events_ready",
"row_runtime_transcript_ready",
"transcript_phase_count",
"transcript_hash",
"browser_pass_ready",
"execution_enabled",
"promotion_allowed",
"proof_status",
"blockers",
"notes",
];
const browserProofRows = [];
for (const row of rowRuntimeProofRows) {
const wasmNode = wasmNodeByPath.get(row.path);
const boundary = boundaryByPath.get(row.path);
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(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`);
const context = {
path: row.path,
iniPath: row.ini,
family: row.family,
machineRoot: row.ini.split("/").slice(0, -1).join("/"),
pythonPathPrepend: row.path_prepend_applied === "1" ? "inventory-configured-path" : "-",
topLevelPath: row.toplevel_executed === "1" ? "inventory-configured-toplevel" : "-",
sourceFiles: row.python_modules_staged === "1" ? splitSummaryList(boundary.python_modules) : [],
pythonRemapFunctions: row.python_remap_functions_bound === "1" ? splitSummaryList(boundary.python_remap_functions) : [],
prologFunctions: row.prolog_functions_bound === "1" ? splitSummaryList(boundary.prolog_functions) : [],
epilogFunctions: row.epilog_functions_bound === "1" ? splitSummaryList(boundary.epilog_functions) : [],
ngcRemapFiles: row.ngc_remap_assets_staged === "1" ? splitSummaryList(boundary.remap_ngc_files) : [],
ngcOnlySubpaths: row.ngc_only_subpaths_not_standalone === "1" && row.row_runtime_proof_targets.includes("ngc_only_subpath_rejection")
? splitSummaryList(boundary.ngc_only_subpaths)
: [],
};
const rowAdapter = createLinuxCncPythonRemapBrowserWorkerAdapter({
workerUrl: new URL("../../runtime/workers/python-remap-worker.js", import.meta.url),
pythonRuntimeModuleUrl: new URL("./python_remap_fake_runtime_worker.js", import.meta.url),
});
const rowPort = createLinuxCncPythonRemapRowRuntimePort({
...context,
runtimeMode: rowAdapter.runtimeMode,
runtimeAdapter: rowAdapter,
});
const start = await rowPort.start();
assertEqual(start.runtimeExecutionReady, true, `${row.path}: browser row bridge readiness`);
const planResult = await rowPort.runRowRuntimePlan();
assertEqual(planResult.status, "row_runtime_adapter_plan_executed", `${row.path}: browser row plan status`);
assertEqual(planResult.validation.ready, true, `${row.path}: browser row plan validation`);
const rowTranscript = rowPort.exportTranscript();
const rowValidation = validatePythonRemapRowRuntimeTranscript({
...context,
rowPath: row.path,
transcript: rowTranscript,
});
assertEqual(rowValidation.ready, true, `${row.path}: browser explicit row validation`);
const rowDiagnostics = rowPort.exportDiagnostics();
assertEqual(rowDiagnostics.runtimeMode, "browser-python-wasm-worker", `${row.path}: browser runtime mode`);
assertEqual(rowDiagnostics.runtimeExecutionReady, true, `${row.path}: browser runtime ready`);
assertEqual(rowDiagnostics.rowRuntimeTranscriptReady, true, `${row.path}: browser diagnostics transcript`);
assertEqual(rowDiagnostics.executionEnabled, false, `${row.path}: browser proof execution guard`);
assertEqual(rowDiagnostics.promotionAllowed, false, `${row.path}: browser proof promotion guard`);
await rowPort.close();
browserProofRows.push([
row.path,
row.ini,
row.family,
row.wasm_node_pass_ready,
flagValue(start.runtimeExecutionReady),
flagValue(planResult.status === "row_runtime_adapter_plan_executed"),
flagValue(rowValidation.moduleImportReady),
flagValue(rowValidation.callableLookupReady),
flagValue(rowValidation.generatorLifecycleReady),
flagValue(rowValidation.ngcRemapAssetsReady),
flagValue(rowValidation.ngcOnlySubpathsGuarded),
flagValue(rowValidation.interpreterStateBindingReady),
flagValue(rowValidation.canonicalEventsReady),
flagValue(rowValidation.rowRuntimeTranscriptReady),
String(rowDiagnostics.transcriptPhaseCount),
rowDiagnostics.transcriptHash,
"1",
"0",
"0",
"browser_row_worker_bridge_passed",
listValue(["manual_promotion_lock"]),
listValue([
"phase5_browser_row_proof_ready",
"browser_worker_consumed_wasm_node_row_proof",
"no_inventory_promotion_without_manual_lock",
]),
].map(tsvValue).join("\t"));
}
const browserProofText = `${browserProofHeaders.join("\t")}\n${browserProofRows.join("\n")}\n`;
localStorage.setItem("python-remap-browser-row-proof.tsv", browserProofText);
document.getElementById("python-remap-browser-row-proof").textContent =
`PYTHON_REMAP_BROWSER_ROW_PROOF_BEGIN\n${browserProofText}PYTHON_REMAP_BROWSER_ROW_PROOF_END`;
const parsedBrowserProofRows = parseTsv(browserProofText, browserProofHeaders);
assertEqual(parsedBrowserProofRows.length, 53, "browser row proof artifact row count");
for (const row of parsedBrowserProofRows) {
assertEqual(row.browser_pass_ready, "1", `${row.path}: browser pass flag`);
assertEqual(row.execution_enabled, "0", `${row.path}: browser row execution guard`);
assertEqual(row.promotion_allowed, "0", `${row.path}: browser row promotion guard`);
assertEqual(row.proof_status, "browser_row_worker_bridge_passed", `${row.path}: browser proof status`);
}
status.textContent = "browser_python_remap_runtime_smoke=ok";
} catch (error) {
status.textContent = `browser_python_remap_runtime_smoke=fail\n${error.stack || error}`;

View File

@@ -553,14 +553,14 @@
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.candidateCount !== 8 ||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.readyCandidateCount !== 8 ||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.sourceFileCount !== 17 ||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.inventoryBaseline !== "executed=29 passed=29 skipped=130 unexpected_fail=0" ||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.inventoryBaseline !== "executed=82 passed=82 skipped=77 unexpected_fail=0" ||
promotionCandidateSummary.ready !== true ||
promotionCandidateSummary.preferredIniPath !== "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini" ||
doc.body.dataset.promotionCandidateReady !== "true" ||
doc.body.dataset.promotionCandidatePreferred !== "qtdragon-multi-joint-on-abort" ||
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=19" ||
doc.querySelector('[data-promotion-candidate-summary-value="inventory-baseline"]')?.textContent !== "28/28 pass; 131 skip unchanged" ||
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="promotion-allowed"]')?.textContent !== "0 baseline changes" ||
doc.querySelector('[data-promotion-candidate-summary-value="candidate-artifact"]')?.textContent !== "promotion-candidates.tsv" ||
!diagnosticsArtifact.virtualHalSimConfigPromotionCandidates?.familyRows?.some((row) =>

View File

@@ -16,6 +16,10 @@ CHROME_PROFILE="$TMP_DIR/chrome-profile"
mkdir -p "$CHROME_PROFILE"
cleanup() {
if [[ -n "${CHROME_PID:-}" ]]; then
kill "$CHROME_PID" 2>/dev/null || true
wait "$CHROME_PID" 2>/dev/null || true
fi
if [[ -n "${SERVER_PID:-}" ]]; then
kill "$SERVER_PID" 2>/dev/null || true
wait "$SERVER_PID" 2>/dev/null || true
@@ -55,20 +59,172 @@ fi
PORT="$(cat "$PORT_FILE")"
URL="http://127.0.0.1:$PORT/tests/browser/python_remap_runtime_browser_smoke.html"
OUT="$TMP_DIR/chromium.out"
DEBUG_PORT_FILE="$TMP_DIR/debug-port"
"$CHROMIUM" \
--headless=new \
--disable-gpu \
--no-sandbox \
--user-data-dir="$CHROME_PROFILE" \
--virtual-time-budget=10000 \
--dump-dom \
"$URL" >"$OUT" 2>&1
--remote-debugging-port=0 \
--remote-debugging-address=127.0.0.1 \
--enable-unsafe-extension-debugging \
"$URL" >"$OUT" 2>&1 &
CHROME_PID=$!
if ! grep -Fq "browser_python_remap_runtime_smoke=ok" "$OUT"; then
echo "Python remap runtime browser smoke failed" >&2
sed -n '1,220p' "$OUT" >&2
for _ in $(seq 1 100); do
if grep -Eo 'DevTools listening on ws://127\.0\.0\.1:[0-9]+' "$OUT" | tail -n 1 | grep -Eo '[0-9]+$' >"$DEBUG_PORT_FILE"; then
break
fi
sleep 0.05
done
if [[ ! -s "$DEBUG_PORT_FILE" ]]; then
echo "Chromium remote debugging port did not become available" >&2
sed -n '1,220p' "$OUT" >&2 || true
exit 1
fi
DEBUG_PORT="$(cat "$DEBUG_PORT_FILE")"
python3 - <<'PY' "$ROOT_DIR" "$DEBUG_PORT"
import base64
import json
import pathlib
import socket
import struct
import sys
import time
import urllib.request
root = pathlib.Path(sys.argv[1])
debug_port = sys.argv[2]
def read_json(url):
with urllib.request.urlopen(url, timeout=5) as response:
return json.loads(response.read().decode("utf-8"))
targets = read_json(f"http://127.0.0.1:{debug_port}/json")
page = next((target for target in targets if target.get("type") == "page"), None)
if not page:
raise SystemExit("missing Chromium page target")
ws_url = page["webSocketDebuggerUrl"]
host_port, path = ws_url.removeprefix("ws://").split("/", 1)
host, port_text = host_port.split(":")
def make_frame(payload):
data = payload.encode("utf-8")
header = bytearray([0x81])
length = len(data)
if length < 126:
header.append(0x80 | length)
elif length < 65536:
header.append(0x80 | 126)
header.extend(struct.pack("!H", length))
else:
header.append(0x80 | 127)
header.extend(struct.pack("!Q", length))
mask = b"\x01\x02\x03\x04"
header.extend(mask)
masked = bytes(byte ^ mask[index % 4] for index, byte in enumerate(data))
return bytes(header) + masked
def read_frame(sock):
first = sock.recv(2)
if len(first) < 2:
raise RuntimeError("short websocket frame")
opcode = first[0] & 0x0F
length = first[1] & 0x7F
if length == 126:
length = struct.unpack("!H", sock.recv(2))[0]
elif length == 127:
length = struct.unpack("!Q", sock.recv(8))[0]
if first[1] & 0x80:
mask = sock.recv(4)
else:
mask = None
data = bytearray()
while len(data) < length:
chunk = sock.recv(length - len(data))
if not chunk:
raise RuntimeError("websocket closed")
data.extend(chunk)
if mask:
data = bytearray(byte ^ mask[index % 4] for index, byte in enumerate(data))
if opcode == 8:
raise RuntimeError("websocket closed")
if opcode not in (1, 2):
return None
return data.decode("utf-8")
sock = socket.create_connection((host, int(port_text)), timeout=5)
key = base64.b64encode(b"python-remap-browser-proof").decode("ascii")
request = (
f"GET /{path} HTTP/1.1\r\n"
f"Host: {host_port}\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
f"Sec-WebSocket-Key: {key}\r\n"
"Sec-WebSocket-Version: 13\r\n\r\n"
)
sock.sendall(request.encode("ascii"))
response = sock.recv(4096)
if b" 101 " not in response:
raise SystemExit("Chromium websocket upgrade failed")
next_id = 0
def cdp(method, params=None):
global next_id
next_id += 1
message_id = next_id
sock.sendall(make_frame(json.dumps({
"id": message_id,
"method": method,
"params": params or {},
})))
while True:
frame = read_frame(sock)
if frame is None:
continue
message = json.loads(frame)
if message.get("id") == message_id:
if "error" in message:
raise RuntimeError(message["error"])
return message["result"]
def evaluate(expression):
return cdp("Runtime.evaluate", {
"expression": expression,
"awaitPromise": True,
"returnByValue": True,
})["result"].get("value")
deadline = time.time() + 15
status = ""
while time.time() < deadline:
status = evaluate("document.getElementById('status')?.textContent || ''")
if "browser_python_remap_runtime_smoke=ok" in status:
break
if "browser_python_remap_runtime_smoke=fail" in status:
raise SystemExit(status)
time.sleep(0.1)
else:
raise SystemExit(f"browser smoke did not finish: {status}")
proof_text = evaluate("document.getElementById('python-remap-browser-row-proof')?.textContent || ''")
prefix = "PYTHON_REMAP_BROWSER_ROW_PROOF_BEGIN\n"
suffix = "PYTHON_REMAP_BROWSER_ROW_PROOF_END"
if not proof_text.startswith(prefix) or not proof_text.endswith(suffix):
raise SystemExit("missing python-remap browser row proof artifact in DOM")
proof_text = proof_text[len(prefix):-len(suffix)]
artifact = root / "build/wasm/sim-configs-inventory/python-remap-browser-row-proof.tsv"
artifact.parent.mkdir(parents=True, exist_ok=True)
artifact.write_text(proof_text, encoding="utf-8")
PY
kill "$CHROME_PID" 2>/dev/null || true
wait "$CHROME_PID" 2>/dev/null || true
echo "browser_python_remap_runtime_smoke=ok"
echo "python_remap_browser_row_proof=ok"