diff --git a/text4.txt b/text4.txt index d380af2..a7eebbb 100644 --- a/text4.txt +++ b/text4.txt @@ -149,79 +149,501 @@ Keep blocked until explicitly designed: - Python remap/prolog/epilog runtime; - tool database process startup and protocol/state behavior. -Immediate next implementation batches +Follow-on implementation completed after this plan -Batch 1: make boundary reports durable +Batch 1: durable boundary reports -- Add a generated machine-readable boundary artifact: +- Complete. The Node inventory now writes: ```text wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv + wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv + wasm-port/build/wasm/sim-configs-inventory/blocked-dependency-summary.tsv + wasm-port/build/wasm/sim-configs-inventory/boundary-phase-completion-summary.tsv ``` -- Minimum fields: +- `boundary-summary.tsv` records the path, INI, tracked blocked kind, + classifier recommendation, dependency evidence, user-M execution codes, + unstaged user-M codes, vendored user-M count, DB program, HAL/UI/HALUI + process flags, and Python UI/remap process flags. +- The inventory fails if a hard blocked row and the classifier recommendation + drift, if `L4-TOOL-DB` lacks `DB_PROGRAM`, if `L4-USER-M-PROCESS` lacks + unstaged external user-M execution codes, or if `L4-PYTHON-REMAP` lacks + Python remap process evidence. + +Batch 2: safe HAL/UI representatives + +- Complete. The inventory keeps these safe representatives as Node/browser + `REP` rows while recording declared process dependencies: + - `axis/gladevcp/probe.ngc` + - `woodpecker/on_abort.ngc` + - `axis/vismach/puma/puma_cube.ngc` + - `axis/vismach/melfa-sim/example.ngc` +- These rows remain file/remap execution representatives only. They do not + claim full HAL/UI process coverage. +- `axis/vismach/millturn/example.ngc` remains blocked as + `L4-USER-M-PROCESS` because its remap chain calls unstaged `M128/M129`. + +Batch 3: external user-M process boundary + +- Complete as a design-and-gate phase, still blocked for execution. +- Generated artifacts now identify LinuxCNC ownership and state targets for + millturn `M128/M129`: ```text - path - ini - blocked - recommended_blocked - dependencies - user_m_execution_codes - user_m_vendored_count - db_program - hal_process - ui_process - halui_mdi_process - python_process + wasm-port/build/wasm/sim-configs-inventory/user-m-process-boundary-summary.tsv + wasm-port/build/wasm/sim-configs-inventory/user-m-process-state-targets.tsv + wasm-port/build/wasm/sim-configs-inventory/user-m-process-transition-plan.tsv + wasm-port/build/wasm/sim-configs-inventory/user-m-process-native-runtime-state-plan.tsv + wasm-port/build/wasm/sim-configs-inventory/user-m-process-native-runtime-readiness.tsv + wasm-port/build/wasm/sim-configs-inventory/user-m-process-native-runtime-probe-gate.tsv ``` -- The existing Node inventory should fail if `blocked` and - `recommended_blocked` conflict for hard blocks. +- The native probe entry point exists: -Batch 2: promote only safe HAL/UI representatives + ```text + wasm-port/tests/native/probe_millturn_user_m_runtime.sh + ``` -- Keep `axis/gladevcp/probe.ngc`, `woodpecker/on_abort.ngc`, - `axis/vismach/puma/puma_cube.ngc`, and `axis/vismach/melfa-sim/example.ngc` - as representative file-execution paths. -- Add browser assertions that these programs remain file/remap execution - representatives and do not claim full UI/HAL process coverage. -- Do not promote `axis/vismach/millturn/example.ngc` until the user-M process - boundary is designed, because the remap chain calls M128/M129. +- The probe is disabled by default and currently reports + `skipped_missing_host_runtime` on this host because `halrun`, `halcmd`, and + `linuxcnc` are unavailable. Promotion remains locked. -Batch 3: design external user-M process boundary +Batch 4: tool DB boundary -- Start with `axis/vismach/millturn` because it is the smallest current hard - blocked row with vendored INI and program context. -- Required design questions: - - Which LinuxCNC source owns M128/M129 behavior in the native config? - - Is the behavior expressible as a LinuxCNC-owned deterministic adapter - boundary, or does it require spawning external process scripts? - - What canonical/state output proves behavior without adding JS CNC - semantics? -- Do not mark `millturn` unblocked until native, Node, and browser checks can - prove the boundary. +- Complete as a design-and-gate phase, still blocked for execution. +- Generated artifacts now describe the `DB_PROGRAM` v2.1 protocol, get/load/ + put/unload transactions, persistence state, native readiness, and promotion + lock: -Batch 4: design tool DB boundary + ```text + wasm-port/build/wasm/sim-configs-inventory/tool-db-process-boundary-summary.tsv + wasm-port/build/wasm/sim-configs-inventory/tool-db-process-protocol-gates.tsv + wasm-port/build/wasm/sim-configs-inventory/tool-db-process-transaction-plan.tsv + wasm-port/build/wasm/sim-configs-inventory/tool-db-process-native-runtime-readiness.tsv + ``` -- Start with `axis/db_demo/db_nonran.ini`. -- Required design questions: - - Which LinuxCNC source owns `DB_PROGRAM` startup/protocol semantics? - - Can a standalone tool database adapter call vendored LinuxCNC tooldata - source without reimplementing database behavior? - - What fixture demonstrates tool lookup/update behavior across native and - WASM? -- Keep `axis/db_demo/base.ngc` as `L4-TOOL-DB` until this exists. +- The native probe entry point exists: + + ```text + wasm-port/tests/native/probe_tool_db_runtime.sh + ``` + +- The probe is disabled by default and currently reports + `skipped_missing_host_runtime` because `linuxcnc`, `milltask`, and `halcmd` + are unavailable. `axis/db_demo/base.ngc` remains `L4-TOOL-DB`. Batch 5: Python remap/full-process family plan -- Do not vendor or execute `gmoccapy`, `axis/laser`, or TWP nutating Layer 4 - paths until Python remap runtime ownership is designed. -- First artifact should be a dependency inventory, not execution: - - Python modules referenced by INI/remap; - - prolog/epilog functions; - - HAL/task/UI assumptions; - - NGC-only subpaths, if any, that can be separated safely. +- Complete as dependency inventory plus a minimal native lifecycle fixture + plan, still blocked for execution. +- Generated artifacts now inventory Python remap families, LinuxCNC owner + sources, configured modules, callable/prolog/epilog expectations, runtime + phases, readiness, and the selected `stop-lookahead` fixture: + + ```text + wasm-port/build/wasm/sim-configs-inventory/python-remap-boundary-summary.tsv + wasm-port/build/wasm/sim-configs-inventory/python-remap-runtime-contract.tsv + wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-readiness.tsv + wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-state-plan.tsv + wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-fixture-plan.tsv + ``` + +- The native probe entry point exists: + + ```text + wasm-port/tests/native/probe_python_remap_runtime.sh + ``` + +- The probe is disabled by default and currently reports + `skipped_missing_host_runtime` because `linuxcnc` is unavailable. Python + remap families remain inventory-only and are not promoted. + +Batch 6: Python remap host/source readiness gate + +- Complete as a readiness-and-gate phase, still blocked for execution. +- Generated artifacts now explicitly separate ordinary `python3` availability + from LinuxCNC interpreter-owned Python remap runtime readiness: + + ```text + wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-readiness.tsv + wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-probe-gate.tsv + ``` + +- `python-remap-native-runtime-probe-gate.tsv` records the selected + `axis/remap/stop-lookahead/nc_files` fixture, `python3`, `linuxcnc`, + `interp_python.cc`, `python_plugin.cc`, and representative module import + inputs: + + ```text + import:axis/remap/stop-lookahead/python/remap.py + import:axis/remap/stop-lookahead/python/toplevel.py + ``` + +- The gate records `python3_sufficient=0`. On this host, `python3` is present + but `linuxcnc` is missing, so the gate remains: + + ```text + runtime_ready=0 + source_proof_ready=1 + gate_status=blocked_missing_host_runtime + execution_enabled=0 + promotion_allowed=0 + ``` + +- Node inventory and browser smoke now require the Python remap runtime probe + gate artifact. The browser gate checks the same policy without executing + Python remap code or claiming browser/full-process coverage. + +Batch 7: tool DB host/source readiness gate + +- Complete as a readiness-and-gate phase, still blocked for execution. +- Generated artifacts now explicitly separate ordinary `python3` availability + and tool-table fallback from LinuxCNC `DB_PROGRAM` process readiness: + + ```text + wasm-port/build/wasm/sim-configs-inventory/tool-db-process-native-runtime-readiness.tsv + wasm-port/build/wasm/sim-configs-inventory/tool-db-process-native-runtime-probe-gate.tsv + ``` + +- `tool-db-process-native-runtime-probe-gate.tsv` records the selected + `axis/db_demo/base.ngc` fixture, `DB_PROGRAM=./db_nonran.py`, required host + commands, DB program source, LinuxCNC Python modules, and the v2.1 protocol + transaction contract. +- The gate records: + + ```text + python3_sufficient=0 + tool_table_fallback_sufficient=0 + db_program_source_ready=1 + linuxcnc_python_modules_ready=1 + protocol_contract_ready=1 + source_proof_ready=1 + ``` + +- On this host, `python3` is present but `linuxcnc`, `milltask`, and `halcmd` + are missing, so the gate remains: + + ```text + runtime_ready=0 + gate_status=blocked_missing_host_runtime + execution_enabled=0 + promotion_allowed=0 + ``` + +- Node inventory and browser smoke now require the tool DB runtime probe gate + artifact. The browser gate checks the same policy without executing + `DB_PROGRAM`, starting LinuxCNC processes, or falling back to `.tbl` + semantics. + +Batch 8: native runtime probe/gate alignment + +- Complete as a cross-artifact consistency gate, still blocked for execution. +- Generated artifact: + + ```text + wasm-port/build/wasm/sim-configs-inventory/runtime-probe-gate-alignment.tsv + ``` + +- This artifact aligns the native runtime probe summary with the generated + runtime probe gates for: + - `axis/vismach/millturn/example.ngc` + - `axis/db_demo/base.ngc` + - `axis/remap/stop-lookahead/nc_files` +- The alignment requires matching `runtime_ready`, `source_proof_ready`, + missing host requirements, execution-disabled state, promotion-disabled + state, and compatible native/gate statuses. +- Node inventory and browser smoke now require this alignment before any + blocked boundary can be considered for promotion. + +Batch 9: native runtime probe opt-in metadata + +- Complete as a native/WASM/browser schema hardening phase, still blocked for + execution. +- `native-runtime-probe-summary.tsv` now records the proof key and opt-in + switch for each disabled native runtime probe: + + ```text + required_native_proof + opt_in_env + probe_note + ``` + +- The current rows explicitly require: + - `native_runtime_state_probe_required` with + `ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1`; + - `native_db_process_protocol_probe_required` with + `ENABLE_TOOL_DB_RUNTIME_PROBE=1`; + - `linuxcnc_python_runtime_lifecycle_probe_required` with + `ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1`. +- The native probe scripts now emit a `runtime_probe_note` for missing source, + missing host runtime, disabled-ready, pass, and fail states. On this host the + notes still report missing LinuxCNC host runtime and keep promotion disabled. +- `runtime-probe-gate-alignment.tsv` now compares the gate + `required_native_proof` with the native runtime summary proof key and records + `native_opt_in_env`, so a ready host cannot silently drift from the intended + opt-in probe. +- Native, Node inventory, and browser smoke all require these fields while + keeping `execution_enabled=0` and `promotion_allowed=0`. + +Batch 10: native runtime opt-in execution plan + +- Complete as a generated runbook/gate artifact, still blocked for execution on + this host. +- Generated artifact: + + ```text + wasm-port/build/wasm/sim-configs-inventory/native-runtime-probe-execution-plan.tsv + ``` + +- The artifact records the exact opt-in command, current runtime readiness, + current native probe status, expected pass status, missing requirements, gate + alignment status, and promotion prerequisites for: + - `axis/vismach/millturn/example.ngc`; + - `axis/db_demo/base.ngc`; + - `axis/remap/stop-lookahead/nc_files`. +- Current opt-in commands are: + - `ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_millturn_user_m_runtime.sh`; + - `ENABLE_TOOL_DB_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_tool_db_runtime.sh`; + - `ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_python_remap_runtime.sh`. +- The expected native pass statuses are: + - `runtime_state_probe_passed`; + - `runtime_protocol_probe_passed`; + - `runtime_lifecycle_probe_passed`. +- On this host all three rows remain: + + ```text + runtime_ready=0 + gate_alignment_ok=1 + execution_enabled=0 + promotion_allowed=0 + plan_status=blocked_missing_host_runtime + ``` + +- Promotion remains explicitly gated on native pass status, subsequent Node + inventory gate alignment, browser smoke gate alignment, and a manual promotion + lock update. The opt-in command alone does not promote any blocked row. +- Node inventory, browser smoke, and the boundary phase completion summary now + require this execution plan. + +Batch 11: native runtime pass evidence contract + +- Complete as a generated stdout-evidence contract, still blocked for execution + on this host. +- Generated artifact: + + ```text + wasm-port/build/wasm/sim-configs-inventory/native-runtime-probe-pass-evidence-contract.tsv + ``` + +- The artifact records the native stdout evidence keys that must be present + after each opt-in native runtime probe reports its expected pass status. +- Current required pass statuses and evidence include: + - millturn user-M: + `runtime_state_probe_passed`, + `millturn_user_m_M128_runtime_state_ok=1`, + `millturn_user_m_M129_runtime_state_ok=1`; + - tool DB: + `runtime_protocol_probe_passed`, + `tool_db_protocol_version=v2.1`, + `tool_db_runtime_protocol_probe_ok=1`, + `tool_db_persistence_state_ok=1`; + - Python remap: + `runtime_lifecycle_probe_passed`, + `python_remap_lifecycle_generator_first_yield=2`, + `python_remap_runtime_lifecycle_probe_ok=1`. +- On this host all three rows remain: + + ```text + evidence_required_now=0 + observed_evidence_ready=0 + missing_evidence_keys=- + evidence_status=pending_until_native_pass + execution_enabled=0 + promotion_allowed=0 + ``` + +- If a future opt-in probe reports its expected pass status, Node inventory will + require all declared stdout evidence keys before any Node/browser promotion + work can proceed. Browser smoke checks the same contract and still treats it + as non-promoting evidence. +- The boundary phase completion summary now requires this evidence contract. + +Batch 12: runtime boundary promotion readiness summary + +- Complete as a generated final promotion-readiness gate, still blocked for + execution and promotion on this host. +- Generated artifact: + + ```text + wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-promotion-readiness.tsv + ``` + +- The artifact combines: + - native runtime execution plan status; + - native pass evidence status; + - blocked runtime promotion lock state; + - Node inventory gate completion state; + - browser smoke gate completion state; + - manual lock update requirement. +- Current rows remain: + + ```text + native_pass_ready=0 + native_evidence_ready=0 + node_inventory_gate_complete=0 + browser_smoke_gate_complete=0 + promotion_lock_active=1 + manual_lock_update_required=1 + promotion_ready=0 + execution_enabled=0 + promotion_allowed=0 + blocking_reason=awaiting_native_runtime_probe_pass + ``` + +- This gives each blocked runtime family one machine-readable final decision: + even if a native probe later passes, promotion still requires native evidence, + Node/browser promotion gates, and an explicit manual promotion lock update. +- Node inventory, browser smoke, and the boundary phase completion summary now + require this readiness summary. + +Batch 13: runtime boundary promotion blockers + +- Complete as a generated blocker-summary gate, still blocked for execution and + promotion on this host. +- Generated artifact: + + ```text + wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-promotion-blockers.tsv + ``` + +- The artifact expands each `promotion_ready=0` decision into concrete blocker + keys and the next unblock action. Current blocker keys include: + - `host_runtime_requirements_missing`; + - `native_runtime_probe_not_passed`; + - `native_pass_evidence_not_ready`; + - `node_inventory_gate_not_complete`; + - `browser_smoke_gate_not_complete`; + - `promotion_lock_active`; + - `manual_lock_update_required`. +- Current missing runtime requirements are: + - millturn user-M: `halrun,halcmd,linuxcnc`; + - tool DB: `linuxcnc,milltask,halcmd`; + - Python remap: `linuxcnc`. +- The current next unblock action is to provide the missing host runtime + requirements for each family. The artifact remains summary-only: + + ```text + promotion_ready=0 + execution_enabled=0 + promotion_allowed=0 + ``` + +- Node inventory, browser smoke, and the boundary phase completion summary now + require this blocker summary. + +Batch 14: post-native-pass promotion gates + +- Complete as a generated post-native-pass gate, still blocked for execution + and promotion on this host. +- Generated artifact: + + ```text + wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-post-native-pass-gates.tsv + ``` + +- The artifact records, for each blocked runtime family, the native pass + status, native evidence readiness, required Node proof, required browser + proof, Node gate status, browser gate status, manual lock requirement, and + active promotion lock state. +- Current rows remain: + + ```text + native_pass_ready=0 + native_evidence_ready=0 + node_gate_status=blocked_until_native_pass_evidence + browser_gate_status=blocked_until_node_gate_complete + manual_lock_update_required=1 + promotion_lock_active=1 + execution_enabled=0 + promotion_allowed=0 + gate_status=blocked_before_native_pass + ``` + +- This closes the next machine-readable step after a future native probe pass: + native evidence must exist first, then Node inventory proof, then browser + smoke proof, then a manual promotion-lock update. Native pass alone still + cannot promote `L4-USER-M-PROCESS`, `L4-TOOL-DB`, or + `L4-PYTHON-REMAP`. +- Node inventory, browser smoke, and the boundary phase completion summary now + require this post-native-pass gate. + +Batch 15: blocked runtime host preflight + +- Complete as a generated host-preflight summary, still blocked for execution + and promotion on this host. +- Generated artifact: + + ```text + wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-preflight.tsv + ``` + +- The artifact aggregates the three runtime probe gates into one host-facing + preflight table with: + - boundary class/kind/target; + - required LinuxCNC runtime family; + - full runtime requirement availability vector; + - missing host requirements; + - source proof readiness; + - required native proof key; + - opt-in environment variable; + - exact opt-in command; + - current native probe status; + - preflight status. +- Current rows remain: + + ```text + source_proof_ready=1 + runtime_ready=0 + current_probe_status=skipped_missing_host_runtime + preflight_status=blocked_missing_host_runtime + execution_enabled=0 + promotion_allowed=0 + ``` + +- Current missing runtime requirements are summarized in one artifact: + - millturn user-M: `halrun,halcmd,linuxcnc`; + - tool DB: `linuxcnc,milltask,halcmd`; + - Python remap: `linuxcnc`. +- This preflight does not run probes and does not relax promotion locks. It + only records when a future host is ready to run the already declared opt-in + native probe commands. +- Node inventory, browser smoke, and the boundary phase completion summary now + require this host-preflight artifact. + +Current next implementation recommendations + +Generated recommendation source: + +```text +wasm-port/build/wasm/sim-configs-inventory/next-boundary-recommendations.tsv +``` + +1. Implement and run the LinuxCNC-owned millturn `M128/M129` HAL/Tcl state + runtime probe after a host runtime with `tclsh`, `halrun`, `halcmd`, and + `linuxcnc` is available. Keep the row blocked until the native state probe + passes and Node/browser coverage can prove a narrow standalone boundary + without full-process claims. +2. Implement and run the tool DB protocol probe after + `tool-db-process-native-runtime-probe-gate.tsv` reports `runtime_ready=1` + with `python3`, `linuxcnc`, `milltask`, and `halcmd` available. The proof + must cover v2.1 handshake, `g`, `p`, `l`, `u`, state changes, and + persistence before `axis/db_demo/base.ngc` can move out of `L4-TOOL-DB`. +3. Implement the minimal Python remap lifecycle proof for + `axis/remap/stop-lookahead` after the new native runtime probe gate reports + `runtime_ready=1` with both `python3` and `linuxcnc` available. Keep all + Python-remap families blocked until the LinuxCNC Python runtime lifecycle is + proven without JavaScript CNC semantics. Validation gates diff --git a/wasm-port/tests/browser/interp_smoke.html b/wasm-port/tests/browser/interp_smoke.html index a03c020..2ef119b 100644 --- a/wasm-port/tests/browser/interp_smoke.html +++ b/wasm-port/tests/browser/interp_smoke.html @@ -90,14 +90,23 @@ "native_source_proof_alignment", "runtime_native_alignment_artifacts", "native_runtime_probe_summary", + "runtime_probe_gate_alignment", + "native_runtime_probe_execution_plan", + "native_runtime_probe_pass_evidence_contract", + "runtime_boundary_promotion_readiness", + "runtime_boundary_promotion_blockers", + "runtime_boundary_post_native_pass_gates", + "runtime_boundary_host_preflight", "user_m_transition_contract", "user_m_native_runtime_state_plan", "user_m_native_runtime_readiness", "user_m_native_runtime_probe_gate", "tool_db_transaction_contract", "tool_db_native_runtime_readiness", + "tool_db_native_runtime_probe_gate", "python_runtime_contract", "python_native_runtime_readiness", + "python_native_runtime_probe_gate", "python_native_runtime_state_plan", "python_native_runtime_fixture_plan", "runtime_contract_summary", @@ -769,6 +778,97 @@ } } + async function verifyBrowserToolDbNativeRuntimeProbeGate() { + const rows = parseTsv( + await fetchText("../../build/wasm/sim-configs-inventory/tool-db-process-native-runtime-probe-gate.tsv"), + ); + if (rows.length !== 1) { + throw new Error(`browser_tool_db_native_runtime_probe_gate: row count drift ${rows.length}`); + } + + const row = rows[0]; + if ( + row.path !== "axis/db_demo/base.ngc" || + row.ini !== "axis/db_demo/db_nonran.ini" || + row.blocked !== "L4-TOOL-DB" || + row.db_program !== "./db_nonran.py" || + row.required_runtime !== "linuxcnc_tooldata_db_process" + ) { + throw new Error("tool DB runtime probe gate identity drift"); + } + if (row.python3_sufficient !== "0" || row.tool_table_fallback_sufficient !== "0") { + throw new Error("tool DB runtime probe gate must reject python3-only and tbl fallback proof"); + } + for (const field of [ + "python3_available", + "db_program_source_ready", + "linuxcnc_python_modules_ready", + "protocol_contract_ready", + "runtime_ready", + "source_proof_ready", + ]) { + if (!["0", "1"].includes(row[field])) { + throw new Error(`tool DB runtime probe gate invalid ${field} ${row[field]}`); + } + } + if ( + row.db_program_source_ready !== "1" || + row.linuxcnc_python_modules_ready !== "1" || + row.protocol_contract_ready !== "1" || + row.source_proof_ready !== "1" + ) { + throw new Error("tool DB runtime probe gate source readiness drift"); + } + for (const requirement of [ + "python3", + "linuxcnc", + "milltask", + "halcmd", + "axis/db_demo/db_nonran.py", + "linuxcnc.so", + "tooldb.py", + ]) { + if (!row.runtime_requirements.includes(`${requirement}:`)) { + throw new Error(`tool DB runtime probe gate missing ${requirement}`); + } + } + for (const phase of [ + "startup_handshake", + "initial_get_all", + "spindle_load_notify", + "tool_offset_notify", + "spindle_unload_notify", + ]) { + if (!row.protocol_transactions.includes(`${phase}:`)) { + throw new Error(`tool DB runtime probe gate missing transaction ${phase}`); + } + } + if ( + row.required_native_proof !== "native_db_process_protocol_probe_required" || + row.proof_status !== "pending" || + row.execution_enabled !== "0" || + row.promotion_allowed !== "0" + ) { + throw new Error("tool DB runtime probe gate must remain pending and disabled"); + } + if (!row.notes.includes("fallback_not_sufficient")) { + throw new Error("tool DB runtime probe gate must record fallback insufficiency"); + } + if (row.runtime_ready === "1") { + if ( + row.missing_requirements !== "-" || + row.gate_status !== "ready_to_implement_protocol_probe" + ) { + throw new Error("ready tool DB runtime probe gate drift"); + } + } else if ( + row.missing_requirements === "-" || + row.gate_status !== "blocked_missing_host_runtime" + ) { + throw new Error("missing-runtime tool DB probe gate drift"); + } + } + async function verifyBrowserPythonRuntimeContract() { const rows = parseTsv( await fetchText("../../build/wasm/sim-configs-inventory/python-remap-runtime-contract.tsv"), @@ -920,6 +1020,91 @@ } } + async function verifyBrowserPythonNativeRuntimeProbeGate() { + const rows = parseTsv( + await fetchText("../../build/wasm/sim-configs-inventory/python-remap-native-runtime-probe-gate.tsv"), + ); + if (rows.length !== 1) { + throw new Error(`browser_python_native_runtime_probe_gate: row count drift ${rows.length}`); + } + + const row = rows[0]; + if ( + row.family !== "axis/remap/stop-lookahead/nc_files" || + row.blocked !== "L4-PYTHON-REMAP" || + row.fixture_id !== "stop_lookahead_python_runtime_lifecycle" || + row.required_runtime !== "linuxcnc_python_remap_runtime" + ) { + throw new Error(`${row.family}: Python runtime probe gate identity drift`); + } + if (row.python3_sufficient !== "0") { + throw new Error(`${row.family}: python3 alone must not satisfy Python remap readiness`); + } + for (const field of [ + "python3_available", + "linuxcnc_available", + "runtime_owner_sources_ready", + "representative_modules_ready", + "runtime_ready", + "source_proof_ready", + ]) { + if (!["0", "1"].includes(row[field])) { + throw new Error(`${row.family}: invalid ${field} ${row[field]}`); + } + } + if ( + row.runtime_owner_sources_ready !== "1" || + row.representative_modules_ready !== "1" || + row.source_proof_ready !== "1" + ) { + throw new Error(`${row.family}: Python runtime probe gate source readiness drift`); + } + for (const requirement of [ + "python3", + "linuxcnc", + "src/emc/rs274ngc/interp_python.cc", + "src/emc/pythonplugin/python_plugin.cc", + "axis/remap/stop-lookahead/python/remap.py", + "axis/remap/stop-lookahead/python/toplevel.py", + ]) { + if (!row.runtime_requirements.includes(`${requirement}:`)) { + throw new Error(`${row.family}: Python runtime probe gate missing ${requirement}`); + } + } + for (const modulePath of [ + "axis/remap/stop-lookahead/python/remap.py", + "axis/remap/stop-lookahead/python/toplevel.py", + ]) { + if (!row.representative_module_imports.includes(`import:${modulePath}`)) { + throw new Error(`${row.family}: Python runtime probe gate missing representative import ${modulePath}`); + } + } + if ( + row.required_native_proof !== "linuxcnc_python_runtime_lifecycle_probe_required" || + row.proof_status !== "pending" || + row.execution_enabled !== "0" || + row.promotion_allowed !== "0" + ) { + throw new Error(`${row.family}: Python runtime probe gate must remain pending and disabled`); + } + if (!row.notes.includes("python3_alone_not_sufficient")) { + throw new Error(`${row.family}: Python runtime probe gate must record python3-only guard`); + } + if (row.runtime_ready === "1") { + if ( + row.missing_requirements !== "-" || + row.gate_status !== "ready_to_implement_lifecycle_probe" + ) { + throw new Error(`${row.family}: ready Python runtime probe gate drift`); + } + } else if ( + row.missing_requirements === "-" || + row.gate_status !== "blocked_missing_host_runtime" + ) { + throw new Error(`${row.family}: missing-runtime Python probe gate drift`); + } + } + async function verifyBrowserPythonNativeRuntimeStatePlan() { const rows = parseTsv( await fetchText("../../build/wasm/sim-configs-inventory/python-remap-native-runtime-state-plan.tsv"), @@ -1232,6 +1417,8 @@ boundaryKind: "external_user_m_process", target: "axis/vismach/millturn/example.ngc", runtimeProbe: "linuxcnc_millturn_user_m_runtime_probe", + requiredNativeProof: "native_runtime_state_probe_required", + optInEnv: "ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1", }, ], [ @@ -1240,6 +1427,8 @@ boundaryKind: "tool_database_process", target: "axis/db_demo/base.ngc", runtimeProbe: "linuxcnc_tool_db_runtime_probe", + requiredNativeProof: "native_db_process_protocol_probe_required", + optInEnv: "ENABLE_TOOL_DB_RUNTIME_PROBE=1", }, ], [ @@ -1248,6 +1437,8 @@ boundaryKind: "python_runtime", target: "axis/remap/stop-lookahead/nc_files", runtimeProbe: "linuxcnc_python_remap_runtime_probe", + requiredNativeProof: "linuxcnc_python_runtime_lifecycle_probe_required", + optInEnv: "ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1", }, ], ]); @@ -1264,7 +1455,9 @@ if ( row.boundary_kind !== expectedRow.boundaryKind || row.target !== expectedRow.target || - row.runtime_probe !== expectedRow.runtimeProbe + row.runtime_probe !== expectedRow.runtimeProbe || + row.required_native_proof !== expectedRow.requiredNativeProof || + row.opt_in_env !== expectedRow.optInEnv ) { throw new Error(`${row.boundary_class}: native runtime probe metadata drift`); } @@ -1289,6 +1482,25 @@ if (row.runtime_ready === "0" && row.probe_status !== "skipped_missing_host_runtime") { throw new Error(`${row.boundary_class}: non-ready runtime must remain skipped`); } + if (row.runtime_ready === "0" && !row.probe_note.includes("missing_host_runtime")) { + throw new Error(`${row.boundary_class}: missing-runtime probe note drift`); + } + if ( + row.probe_status === "ready_disabled_by_default" && + !row.probe_note.includes(`set_${row.opt_in_env.split("=")[0]}`) + ) { + throw new Error(`${row.boundary_class}: ready-disabled probe note drift`); + } + if ( + row.probe_status.startsWith("runtime_") && + row.probe_status.endsWith("_passed") && + !row.probe_note.includes("without_promotion") + ) { + throw new Error(`${row.boundary_class}: passed probe note drift`); + } + if (row.probe_note === "-") { + throw new Error(`${row.boundary_class}: native runtime probe lacks note`); + } if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { throw new Error(`${row.boundary_class}: native runtime probe must not enable execution or promotion`); } @@ -1298,6 +1510,288 @@ } } + async function verifyBrowserRuntimeProbeGateAlignment() { + const rows = parseTsv( + await fetchText("../../build/wasm/sim-configs-inventory/runtime-probe-gate-alignment.tsv"), + ); + const expected = new Map([ + [ + "L4-USER-M-PROCESS", + { + boundaryKind: "external_user_m_process", + target: "axis/vismach/millturn/example.ngc", + gateArtifact: "user-m-process-native-runtime-probe-gate.tsv", + requiredNativeProof: "native_runtime_state_probe_required", + }, + ], + [ + "L4-TOOL-DB", + { + boundaryKind: "tool_database_process", + target: "axis/db_demo/base.ngc", + gateArtifact: "tool-db-process-native-runtime-probe-gate.tsv", + requiredNativeProof: "native_db_process_protocol_probe_required", + }, + ], + [ + "L4-PYTHON-REMAP", + { + boundaryKind: "python_runtime", + target: "axis/remap/stop-lookahead/nc_files", + gateArtifact: "python-remap-native-runtime-probe-gate.tsv", + requiredNativeProof: "linuxcnc_python_runtime_lifecycle_probe_required", + }, + ], + ]); + + if (rows.length !== expected.size) { + throw new Error(`browser_runtime_probe_gate_alignment: row count drift ${rows.length}`); + } + + for (const row of rows) { + const expectedRow = expected.get(row.boundary_class); + if (!expectedRow) { + throw new Error(`browser_runtime_probe_gate_alignment: unexpected ${row.boundary_class}`); + } + if ( + row.boundary_kind !== expectedRow.boundaryKind || + row.target !== expectedRow.target || + row.gate_artifact !== expectedRow.gateArtifact || + row.gate_required_native_proof !== expectedRow.requiredNativeProof || + row.native_required_native_proof !== expectedRow.requiredNativeProof + ) { + throw new Error(`${row.boundary_class}: runtime probe gate alignment metadata drift`); + } + if (!/^ENABLE_[A-Z0-9_]+=1$/.test(row.native_opt_in_env)) { + throw new Error(`${row.boundary_class}: runtime probe gate alignment opt-in drift`); + } + if (row.gate_runtime_ready !== row.native_runtime_ready) { + throw new Error(`${row.boundary_class}: gate/native runtime readiness drift`); + } + if (row.gate_source_proof_ready !== "1" || row.native_source_proof_ready !== "1") { + throw new Error(`${row.boundary_class}: runtime probe gate source proof drift`); + } + for (const field of [ + "missing_requirements_match", + "required_native_proof_match", + "execution_disabled_match", + "promotion_disabled_match", + "status_compatible", + "alignment_ok", + ]) { + if (row[field] !== "1") { + throw new Error(`${row.boundary_class}: runtime probe gate ${field} failed`); + } + } + if (!row.notes.includes("before_promotion")) { + throw new Error(`${row.boundary_class}: runtime probe gate alignment notes drift`); + } + } + } + + async function verifyBrowserNativeRuntimeProbeExecutionPlan() { + const rows = parseTsv( + await fetchText("../../build/wasm/sim-configs-inventory/native-runtime-probe-execution-plan.tsv"), + ); + const expected = new Map([ + [ + "L4-USER-M-PROCESS", + { + boundaryKind: "external_user_m_process", + target: "axis/vismach/millturn/example.ngc", + runtimeProbe: "linuxcnc_millturn_user_m_runtime_probe", + requiredNativeProof: "native_runtime_state_probe_required", + optInEnv: "ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1", + script: "wasm-port/tests/native/probe_millturn_user_m_runtime.sh", + expectedPassStatus: "runtime_state_probe_passed", + }, + ], + [ + "L4-TOOL-DB", + { + boundaryKind: "tool_database_process", + target: "axis/db_demo/base.ngc", + runtimeProbe: "linuxcnc_tool_db_runtime_probe", + requiredNativeProof: "native_db_process_protocol_probe_required", + optInEnv: "ENABLE_TOOL_DB_RUNTIME_PROBE=1", + script: "wasm-port/tests/native/probe_tool_db_runtime.sh", + expectedPassStatus: "runtime_protocol_probe_passed", + }, + ], + [ + "L4-PYTHON-REMAP", + { + boundaryKind: "python_runtime", + target: "axis/remap/stop-lookahead/nc_files", + runtimeProbe: "linuxcnc_python_remap_runtime_probe", + requiredNativeProof: "linuxcnc_python_runtime_lifecycle_probe_required", + optInEnv: "ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1", + script: "wasm-port/tests/native/probe_python_remap_runtime.sh", + expectedPassStatus: "runtime_lifecycle_probe_passed", + }, + ], + ]); + + if (rows.length !== expected.size) { + throw new Error(`browser_native_runtime_probe_execution_plan: row count drift ${rows.length}`); + } + + for (const row of rows) { + const expectedRow = expected.get(row.boundary_class); + if (!expectedRow) { + throw new Error(`browser_native_runtime_probe_execution_plan: unexpected ${row.boundary_class}`); + } + if ( + row.boundary_kind !== expectedRow.boundaryKind || + row.target !== expectedRow.target || + row.runtime_probe !== expectedRow.runtimeProbe || + row.required_native_proof !== expectedRow.requiredNativeProof || + row.opt_in_env !== expectedRow.optInEnv || + row.expected_pass_status !== expectedRow.expectedPassStatus + ) { + throw new Error(`${row.boundary_class}: native runtime execution plan metadata drift`); + } + if (row.execution_command !== `${expectedRow.optInEnv} bash ${expectedRow.script}`) { + throw new Error(`${row.boundary_class}: native runtime execution command drift`); + } + if (row.source_proof_ready !== "1" || row.gate_alignment_ok !== "1") { + throw new Error(`${row.boundary_class}: native runtime execution plan is not source/alignment ready`); + } + if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error(`${row.boundary_class}: native runtime execution plan must not enable execution or promotion`); + } + for (const requirement of [ + `native:${expectedRow.expectedPassStatus}`, + "node:inventory_gate_alignment_after_native_pass", + "browser:smoke_gate_alignment_after_node_pass", + "manual:promotion_lock_update_required", + ]) { + if (!row.promotion_requires.includes(requirement)) { + throw new Error(`${row.boundary_class}: native runtime execution plan missing ${requirement}`); + } + } + if (row.current_probe_status === row.expected_pass_status) { + if (row.plan_status !== "native_probe_passed_waiting_for_node_browser_promotion_proof") { + throw new Error(`${row.boundary_class}: passed execution plan status drift`); + } + } else if (row.runtime_ready === "1") { + if (row.plan_status !== "ready_to_run_opt_in_probe" || row.missing_requirements !== "-") { + throw new Error(`${row.boundary_class}: ready execution plan status drift`); + } + } else if ( + row.plan_status !== "blocked_missing_host_runtime" || + row.missing_requirements === "-" + ) { + throw new Error(`${row.boundary_class}: blocked execution plan status drift`); + } + if (!row.notes.includes("no_automatic_promotion")) { + throw new Error(`${row.boundary_class}: native runtime execution plan notes drift`); + } + } + } + + async function verifyBrowserNativeRuntimeProbePassEvidenceContract() { + const rows = parseTsv( + await fetchText("../../build/wasm/sim-configs-inventory/native-runtime-probe-pass-evidence-contract.tsv"), + ); + const expected = new Map([ + [ + "L4-USER-M-PROCESS", + { + boundaryKind: "external_user_m_process", + target: "axis/vismach/millturn/example.ngc", + runtimeProbe: "linuxcnc_millturn_user_m_runtime_probe", + expectedPassStatus: "runtime_state_probe_passed", + evidence: [ + "millturn_user_m_runtime_probe_status=runtime_state_probe_passed", + "millturn_user_m_M128_runtime_state_ok=1", + "millturn_user_m_M129_runtime_state_ok=1", + ], + }, + ], + [ + "L4-TOOL-DB", + { + boundaryKind: "tool_database_process", + target: "axis/db_demo/base.ngc", + runtimeProbe: "linuxcnc_tool_db_runtime_probe", + expectedPassStatus: "runtime_protocol_probe_passed", + evidence: [ + "tool_db_runtime_probe_status=runtime_protocol_probe_passed", + "tool_db_protocol_version=v2.1", + "tool_db_runtime_protocol_probe_ok=1", + "tool_db_persistence_state_ok=1", + ], + }, + ], + [ + "L4-PYTHON-REMAP", + { + boundaryKind: "python_runtime", + target: "axis/remap/stop-lookahead/nc_files", + runtimeProbe: "linuxcnc_python_remap_runtime_probe", + expectedPassStatus: "runtime_lifecycle_probe_passed", + evidence: [ + "python_remap_runtime_probe_status=runtime_lifecycle_probe_passed", + "python_remap_lifecycle_generator_first_yield=2", + "python_remap_runtime_lifecycle_probe_ok=1", + ], + }, + ], + ]); + + if (rows.length !== expected.size) { + throw new Error(`browser_native_runtime_probe_pass_evidence_contract: row count drift ${rows.length}`); + } + + for (const row of rows) { + const expectedRow = expected.get(row.boundary_class); + if (!expectedRow) { + throw new Error(`browser_native_runtime_probe_pass_evidence_contract: unexpected ${row.boundary_class}`); + } + if ( + row.boundary_kind !== expectedRow.boundaryKind || + row.target !== expectedRow.target || + row.runtime_probe !== expectedRow.runtimeProbe || + row.expected_pass_status !== expectedRow.expectedPassStatus || + row.stdout_log === "-" + ) { + throw new Error(`${row.boundary_class}: native pass evidence metadata drift`); + } + for (const evidence of expectedRow.evidence) { + if (!row.expected_evidence_keys.includes(evidence)) { + throw new Error(`${row.boundary_class}: native pass evidence missing ${evidence}`); + } + } + if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error(`${row.boundary_class}: native pass evidence contract must not enable execution or promotion`); + } + if (row.current_probe_status === row.expected_pass_status) { + if ( + row.evidence_required_now !== "1" || + row.observed_evidence_ready !== "1" || + row.missing_evidence_keys !== "-" || + row.evidence_status !== "native_pass_evidence_observed" + ) { + throw new Error(`${row.boundary_class}: native pass evidence must be observed after pass`); + } + } else if ( + row.evidence_required_now !== "0" || + row.observed_evidence_ready !== "0" || + row.missing_evidence_keys !== "-" || + row.evidence_status !== "pending_until_native_pass" + ) { + throw new Error(`${row.boundary_class}: native pass evidence must remain pending before pass`); + } + if ( + !row.notes.includes("no_automatic_promotion") && + !row.notes.includes("before_node_browser_promotion") + ) { + throw new Error(`${row.boundary_class}: native pass evidence notes drift`); + } + } + } + async function verifyBrowserRuntimeBoundaryContractSummary() { const rows = parseTsv( await fetchText("../../build/wasm/sim-configs-inventory/runtime-boundary-contract-summary.tsv"), @@ -1442,6 +1936,346 @@ } } + async function verifyBrowserRuntimeBoundaryPromotionReadiness() { + const rows = parseTsv( + await fetchText("../../build/wasm/sim-configs-inventory/runtime-boundary-promotion-readiness.tsv"), + ); + const expected = new Map([ + [ + "L4-USER-M-PROCESS", + { + boundaryKind: "external_user_m_process", + target: "axis/vismach/millturn/example.ngc", + blocked: "L4-USER-M-PROCESS", + expectedPassStatus: "runtime_state_probe_passed", + }, + ], + [ + "L4-TOOL-DB", + { + boundaryKind: "tool_database_process", + target: "axis/db_demo/base.ngc", + blocked: "L4-TOOL-DB", + expectedPassStatus: "runtime_protocol_probe_passed", + }, + ], + [ + "L4-PYTHON-REMAP", + { + boundaryKind: "python_runtime", + target: "axis/remap/stop-lookahead/nc_files", + blocked: "L4-PYTHON-REMAP", + expectedPassStatus: "runtime_lifecycle_probe_passed", + }, + ], + ]); + + if (rows.length !== expected.size) { + throw new Error(`browser_runtime_boundary_promotion_readiness: row count drift ${rows.length}`); + } + + for (const row of rows) { + const expectedRow = expected.get(row.boundary_class); + if (!expectedRow) { + throw new Error(`browser_runtime_boundary_promotion_readiness: unexpected ${row.boundary_class}`); + } + if ( + row.boundary_kind !== expectedRow.boundaryKind || + row.target !== expectedRow.target || + row.blocked !== expectedRow.blocked || + row.expected_pass_status !== expectedRow.expectedPassStatus || + row.native_evidence_status === "-" + ) { + throw new Error(`${row.boundary_class}: promotion readiness metadata drift`); + } + if ( + row.node_inventory_gate_complete !== "0" || + row.browser_smoke_gate_complete !== "0" || + row.promotion_lock_active !== "1" || + row.manual_lock_update_required !== "1" || + row.promotion_ready !== "0" || + row.execution_enabled !== "0" || + row.promotion_allowed !== "0" + ) { + throw new Error(`${row.boundary_class}: promotion readiness must remain locked`); + } + if (row.current_probe_status === row.expected_pass_status) { + if (row.native_pass_ready !== "1" || row.blocking_reason === "awaiting_native_runtime_probe_pass") { + throw new Error(`${row.boundary_class}: passed promotion readiness state drift`); + } + } else if ( + row.native_pass_ready !== "0" || + row.blocking_reason !== "awaiting_native_runtime_probe_pass" + ) { + throw new Error(`${row.boundary_class}: pending promotion readiness state drift`); + } + if (!row.notes.includes("no_automatic_promotion")) { + throw new Error(`${row.boundary_class}: promotion readiness notes drift`); + } + } + } + + async function verifyBrowserRuntimeBoundaryPromotionBlockers() { + const rows = parseTsv( + await fetchText("../../build/wasm/sim-configs-inventory/runtime-boundary-promotion-blockers.tsv"), + ); + const expected = new Map([ + [ + "L4-USER-M-PROCESS", + { + boundaryKind: "external_user_m_process", + target: "axis/vismach/millturn/example.ngc", + blocked: "L4-USER-M-PROCESS", + }, + ], + [ + "L4-TOOL-DB", + { + boundaryKind: "tool_database_process", + target: "axis/db_demo/base.ngc", + blocked: "L4-TOOL-DB", + }, + ], + [ + "L4-PYTHON-REMAP", + { + boundaryKind: "python_runtime", + target: "axis/remap/stop-lookahead/nc_files", + blocked: "L4-PYTHON-REMAP", + }, + ], + ]); + + if (rows.length !== expected.size) { + throw new Error(`browser_runtime_boundary_promotion_blockers: row count drift ${rows.length}`); + } + + for (const row of rows) { + const expectedRow = expected.get(row.boundary_class); + if (!expectedRow) { + throw new Error(`browser_runtime_boundary_promotion_blockers: unexpected ${row.boundary_class}`); + } + if ( + row.boundary_kind !== expectedRow.boundaryKind || + row.target !== expectedRow.target || + row.blocked !== expectedRow.blocked + ) { + throw new Error(`${row.boundary_class}: promotion blocker metadata drift`); + } + if ( + row.promotion_ready !== "0" || + row.execution_enabled !== "0" || + row.promotion_allowed !== "0" || + row.blocker_keys === "-" || + row.next_unblock_action === "-" + ) { + throw new Error(`${row.boundary_class}: promotion blocker summary must remain blocked and actionable`); + } + if (!/^[1-9][0-9]*$/.test(row.blocker_count)) { + throw new Error(`${row.boundary_class}: invalid promotion blocker count`); + } + for (const blocker of [ + "node_inventory_gate_not_complete", + "browser_smoke_gate_not_complete", + "promotion_lock_active", + "manual_lock_update_required", + ]) { + if (!row.blocker_keys.includes(blocker)) { + throw new Error(`${row.boundary_class}: promotion blocker missing ${blocker}`); + } + } + if ( + row.missing_runtime_requirements !== "-" && + !row.blocker_keys.includes("host_runtime_requirements_missing") + ) { + throw new Error(`${row.boundary_class}: host runtime blocker missing`); + } + if (!row.notes.includes("no_automatic_promotion")) { + throw new Error(`${row.boundary_class}: promotion blocker notes drift`); + } + } + } + + async function verifyBrowserRuntimeBoundaryPostNativePassGates() { + const rows = parseTsv( + await fetchText("../../build/wasm/sim-configs-inventory/runtime-boundary-post-native-pass-gates.tsv"), + ); + const expected = new Map([ + [ + "L4-USER-M-PROCESS", + { + boundaryKind: "external_user_m_process", + target: "axis/vismach/millturn/example.ngc", + blocked: "L4-USER-M-PROCESS", + nodeProof: "not_user_m_event_only", + browserProof: "no_full_process_claim", + }, + ], + [ + "L4-TOOL-DB", + { + boundaryKind: "tool_database_process", + target: "axis/db_demo/base.ngc", + blocked: "L4-TOOL-DB", + nodeProof: "not_tbl_fallback", + browserProof: "opfs_persistence_only", + }, + ], + [ + "L4-PYTHON-REMAP", + { + boundaryKind: "python_runtime", + target: "axis/remap/stop-lookahead/nc_files", + blocked: "L4-PYTHON-REMAP", + nodeProof: "not_js_semantics", + browserProof: "no_full_process_claim", + }, + ], + ]); + + if (rows.length !== expected.size) { + throw new Error(`browser_runtime_boundary_post_native_pass_gates: row count drift ${rows.length}`); + } + + for (const row of rows) { + const expectedRow = expected.get(row.boundary_class); + if (!expectedRow) { + throw new Error(`browser_runtime_boundary_post_native_pass_gates: unexpected ${row.boundary_class}`); + } + if ( + row.boundary_kind !== expectedRow.boundaryKind || + row.target !== expectedRow.target || + row.blocked !== expectedRow.blocked + ) { + throw new Error(`${row.boundary_class}: post-native-pass gate metadata drift`); + } + if ( + !row.required_node_proof.includes(expectedRow.nodeProof) || + !row.required_browser_proof.includes(expectedRow.browserProof) + ) { + throw new Error(`${row.boundary_class}: post-native-pass proof requirement drift`); + } + if ( + row.manual_lock_update_required !== "1" || + row.promotion_lock_active !== "1" || + row.execution_enabled !== "0" || + row.promotion_allowed !== "0" + ) { + throw new Error(`${row.boundary_class}: post-native-pass gate must remain locked`); + } + if (row.native_evidence_ready === "1") { + if ( + row.native_pass_ready !== "1" || + row.node_gate_status !== "pending_node_inventory_promotion_gate" || + row.browser_gate_status !== "blocked_until_node_gate_complete" || + row.gate_status !== "waiting_for_node_browser_manual_promotion" + ) { + throw new Error(`${row.boundary_class}: post-native-pass ready state drift`); + } + } else if ( + row.node_gate_status !== "blocked_until_native_pass_evidence" || + row.browser_gate_status !== "blocked_until_node_gate_complete" || + row.gate_status !== "blocked_before_native_pass" + ) { + throw new Error(`${row.boundary_class}: post-native-pass blocked state drift`); + } + if (!row.notes.includes("no_automatic_promotion")) { + throw new Error(`${row.boundary_class}: post-native-pass notes drift`); + } + } + } + + async function verifyBrowserRuntimeBoundaryHostPreflight() { + const rows = parseTsv( + await fetchText("../../build/wasm/sim-configs-inventory/runtime-boundary-host-preflight.tsv"), + ); + const expected = new Map([ + [ + "L4-USER-M-PROCESS", + { + boundaryKind: "external_user_m_process", + target: "axis/vismach/millturn/example.ngc", + blocked: "L4-USER-M-PROCESS", + requiredRuntime: "linuxcnc_task_hal_tcl_user_m_process", + optInEnv: "ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1", + script: "probe_millturn_user_m_runtime.sh", + missingToken: "linuxcnc", + }, + ], + [ + "L4-TOOL-DB", + { + boundaryKind: "tool_database_process", + target: "axis/db_demo/base.ngc", + blocked: "L4-TOOL-DB", + requiredRuntime: "linuxcnc_tooldata_db_process", + optInEnv: "ENABLE_TOOL_DB_RUNTIME_PROBE=1", + script: "probe_tool_db_runtime.sh", + missingToken: "milltask", + }, + ], + [ + "L4-PYTHON-REMAP", + { + boundaryKind: "python_runtime", + target: "axis/remap/stop-lookahead/nc_files", + blocked: "L4-PYTHON-REMAP", + requiredRuntime: "linuxcnc_python_remap_runtime", + optInEnv: "ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1", + script: "probe_python_remap_runtime.sh", + missingToken: "linuxcnc", + }, + ], + ]); + + if (rows.length !== expected.size) { + throw new Error(`browser_runtime_boundary_host_preflight: row count drift ${rows.length}`); + } + + for (const row of rows) { + const expectedRow = expected.get(row.boundary_class); + if (!expectedRow) { + throw new Error(`browser_runtime_boundary_host_preflight: unexpected ${row.boundary_class}`); + } + if ( + row.boundary_kind !== expectedRow.boundaryKind || + row.target !== expectedRow.target || + row.blocked !== expectedRow.blocked || + row.required_runtime !== expectedRow.requiredRuntime || + row.opt_in_env !== expectedRow.optInEnv + ) { + throw new Error(`${row.boundary_class}: host preflight metadata drift`); + } + if ( + row.source_proof_ready !== "1" || + row.required_native_proof === "-" || + row.runtime_requirements === "-" || + !row.runtime_requirements.includes(`${expectedRow.missingToken}:`) + ) { + throw new Error(`${row.boundary_class}: host preflight readiness evidence drift`); + } + if (row.execution_command !== `${row.opt_in_env} bash wasm-port/tests/native/${expectedRow.script}`) { + throw new Error(`${row.boundary_class}: host preflight execution command drift`); + } + if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error(`${row.boundary_class}: host preflight must not enable execution or promotion`); + } + if (row.runtime_ready === "1") { + if (row.missing_requirements !== "-" || row.preflight_status !== "ready_to_run_opt_in_native_probe") { + throw new Error(`${row.boundary_class}: ready host preflight drift`); + } + } else if ( + !row.missing_requirements.includes(expectedRow.missingToken) || + row.preflight_status !== "blocked_missing_host_runtime" + ) { + throw new Error(`${row.boundary_class}: blocked host preflight drift`); + } + if (!row.notes.includes("no_automatic_promotion")) { + throw new Error(`${row.boundary_class}: host preflight notes drift`); + } + } + } + async function verifyBrowserNextBoundaryRecommendations() { const rows = parseTsv( await fetchText("../../build/wasm/sim-configs-inventory/next-boundary-recommendations.tsv"), @@ -2086,17 +2920,26 @@ await verifyBrowserUserMNativeRuntimeProbeGate(); await verifyBrowserToolDbTransactionPlan(); await verifyBrowserToolDbNativeRuntimeReadiness(); + await verifyBrowserToolDbNativeRuntimeProbeGate(); await verifyBrowserPythonRuntimeContract(); await verifyBrowserPythonNativeRuntimeReadiness(); + await verifyBrowserPythonNativeRuntimeProbeGate(); await verifyBrowserPythonNativeRuntimeStatePlan(); await verifyBrowserPythonNativeRuntimeFixturePlan(); await verifyBrowserNativeProofAlignment(); const runtimeNativeAlignmentSummaryRows = await verifyBrowserRuntimeNativeAlignmentSummary(); await verifyBrowserNativeRuntimeProbeSummary(); + await verifyBrowserRuntimeProbeGateAlignment(); + await verifyBrowserNativeRuntimeProbeExecutionPlan(); + await verifyBrowserNativeRuntimeProbePassEvidenceContract(); await verifyBrowserRuntimeBoundaryContractSummary(); await verifyBrowserRuntimeNativeAlignmentDetails(runtimeNativeAlignmentSummaryRows); await verifyBrowserBlockedRuntimePromotionLock(); + await verifyBrowserRuntimeBoundaryPromotionReadiness(); + await verifyBrowserRuntimeBoundaryPromotionBlockers(); + await verifyBrowserRuntimeBoundaryPostNativePassGates(); + await verifyBrowserRuntimeBoundaryHostPreflight(); await verifyBrowserNextBoundaryRecommendations(); const interp = await createLinuxCncInterpSdk({ diff --git a/wasm-port/tests/browser/verify_interp_browser.sh b/wasm-port/tests/browser/verify_interp_browser.sh index a1ebce8..8f15fae 100755 --- a/wasm-port/tests/browser/verify_interp_browser.sh +++ b/wasm-port/tests/browser/verify_interp_browser.sh @@ -9,6 +9,9 @@ INI_BOUNDARY_SUMMARY="$ROOT_DIR/build/wasm/sim-configs-inventory/ini-boundary-su NATIVE_PROOF_ALIGNMENT_SUMMARY="$ROOT_DIR/build/wasm/sim-configs-inventory/native-proof-alignment-summary.tsv" NATIVE_RUNTIME_PROBE_SUMMARY="$ROOT_DIR/build/wasm/sim-configs-inventory/native-runtime-probe-summary.tsv" RUNTIME_BOUNDARY_NATIVE_ALIGNMENT_SUMMARY="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-boundary-native-alignment-summary.tsv" +RUNTIME_PROBE_GATE_ALIGNMENT="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-probe-gate-alignment.tsv" +NATIVE_RUNTIME_PROBE_EXECUTION_PLAN="$ROOT_DIR/build/wasm/sim-configs-inventory/native-runtime-probe-execution-plan.tsv" +NATIVE_RUNTIME_PROBE_PASS_EVIDENCE_CONTRACT="$ROOT_DIR/build/wasm/sim-configs-inventory/native-runtime-probe-pass-evidence-contract.tsv" RUNTIME_BOUNDARY_CONTRACT_SUMMARY="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-boundary-contract-summary.tsv" USER_M_PROCESS_NATIVE_STATE_ALIGNMENT="$ROOT_DIR/build/wasm/sim-configs-inventory/user-m-process-native-state-alignment.tsv" USER_M_PROCESS_NATIVE_TRANSITION_ALIGNMENT="$ROOT_DIR/build/wasm/sim-configs-inventory/user-m-process-native-transition-alignment.tsv" @@ -18,13 +21,19 @@ USER_M_PROCESS_NATIVE_RUNTIME_PROBE_GATE="$ROOT_DIR/build/wasm/sim-configs-inven USER_M_PROCESS_TRANSITION_PLAN="$ROOT_DIR/build/wasm/sim-configs-inventory/user-m-process-transition-plan.tsv" TOOL_DB_PROCESS_NATIVE_PROTOCOL_ALIGNMENT="$ROOT_DIR/build/wasm/sim-configs-inventory/tool-db-process-native-protocol-alignment.tsv" TOOL_DB_PROCESS_NATIVE_RUNTIME_READINESS="$ROOT_DIR/build/wasm/sim-configs-inventory/tool-db-process-native-runtime-readiness.tsv" +TOOL_DB_PROCESS_NATIVE_RUNTIME_PROBE_GATE="$ROOT_DIR/build/wasm/sim-configs-inventory/tool-db-process-native-runtime-probe-gate.tsv" TOOL_DB_PROCESS_TRANSACTION_PLAN="$ROOT_DIR/build/wasm/sim-configs-inventory/tool-db-process-transaction-plan.tsv" PYTHON_REMAP_NATIVE_RUNTIME_ALIGNMENT="$ROOT_DIR/build/wasm/sim-configs-inventory/python-remap-native-runtime-alignment.tsv" PYTHON_REMAP_NATIVE_RUNTIME_READINESS="$ROOT_DIR/build/wasm/sim-configs-inventory/python-remap-native-runtime-readiness.tsv" +PYTHON_REMAP_NATIVE_RUNTIME_PROBE_GATE="$ROOT_DIR/build/wasm/sim-configs-inventory/python-remap-native-runtime-probe-gate.tsv" PYTHON_REMAP_NATIVE_RUNTIME_STATE_PLAN="$ROOT_DIR/build/wasm/sim-configs-inventory/python-remap-native-runtime-state-plan.tsv" PYTHON_REMAP_NATIVE_RUNTIME_FIXTURE_PLAN="$ROOT_DIR/build/wasm/sim-configs-inventory/python-remap-native-runtime-fixture-plan.tsv" PYTHON_REMAP_RUNTIME_CONTRACT="$ROOT_DIR/build/wasm/sim-configs-inventory/python-remap-runtime-contract.tsv" BLOCKED_RUNTIME_PROMOTION_LOCK="$ROOT_DIR/build/wasm/sim-configs-inventory/blocked-runtime-promotion-lock.tsv" +RUNTIME_BOUNDARY_PROMOTION_READINESS="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-boundary-promotion-readiness.tsv" +RUNTIME_BOUNDARY_PROMOTION_BLOCKERS="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-boundary-promotion-blockers.tsv" +RUNTIME_BOUNDARY_POST_NATIVE_PASS_GATES="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-boundary-post-native-pass-gates.tsv" +RUNTIME_BOUNDARY_HOST_PREFLIGHT="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-boundary-host-preflight.tsv" BOUNDARY_PHASE_COMPLETION_SUMMARY="$ROOT_DIR/build/wasm/sim-configs-inventory/boundary-phase-completion-summary.tsv" NEXT_BOUNDARY_RECOMMENDATIONS="$ROOT_DIR/build/wasm/sim-configs-inventory/next-boundary-recommendations.tsv" INVENTORY_SCRIPT="$ROOT_DIR/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs" @@ -49,6 +58,9 @@ for artifact in \ "$INI_BOUNDARY_SUMMARY" \ "$NATIVE_PROOF_ALIGNMENT_SUMMARY" \ "$RUNTIME_BOUNDARY_NATIVE_ALIGNMENT_SUMMARY" \ + "$RUNTIME_PROBE_GATE_ALIGNMENT" \ + "$NATIVE_RUNTIME_PROBE_EXECUTION_PLAN" \ + "$NATIVE_RUNTIME_PROBE_PASS_EVIDENCE_CONTRACT" \ "$RUNTIME_BOUNDARY_CONTRACT_SUMMARY" \ "$USER_M_PROCESS_NATIVE_STATE_ALIGNMENT" \ "$USER_M_PROCESS_NATIVE_TRANSITION_ALIGNMENT" \ @@ -58,14 +70,20 @@ for artifact in \ "$USER_M_PROCESS_TRANSITION_PLAN" \ "$TOOL_DB_PROCESS_NATIVE_PROTOCOL_ALIGNMENT" \ "$TOOL_DB_PROCESS_NATIVE_RUNTIME_READINESS" \ + "$TOOL_DB_PROCESS_NATIVE_RUNTIME_PROBE_GATE" \ "$TOOL_DB_PROCESS_TRANSACTION_PLAN" \ "$PYTHON_REMAP_NATIVE_RUNTIME_ALIGNMENT" \ "$PYTHON_REMAP_NATIVE_RUNTIME_READINESS" \ + "$PYTHON_REMAP_NATIVE_RUNTIME_PROBE_GATE" \ "$PYTHON_REMAP_NATIVE_RUNTIME_STATE_PLAN" \ "$PYTHON_REMAP_NATIVE_RUNTIME_FIXTURE_PLAN" \ "$PYTHON_REMAP_RUNTIME_CONTRACT" \ "$NATIVE_RUNTIME_PROBE_SUMMARY" \ "$BLOCKED_RUNTIME_PROMOTION_LOCK" \ + "$RUNTIME_BOUNDARY_PROMOTION_READINESS" \ + "$RUNTIME_BOUNDARY_PROMOTION_BLOCKERS" \ + "$RUNTIME_BOUNDARY_POST_NATIVE_PASS_GATES" \ + "$RUNTIME_BOUNDARY_HOST_PREFLIGHT" \ "$BOUNDARY_PHASE_COMPLETION_SUMMARY" \ "$NEXT_BOUNDARY_RECOMMENDATIONS"; do if [[ ! -f "$artifact" || "$INVENTORY_SCRIPT" -nt "$artifact" ]]; then @@ -78,6 +96,27 @@ fi if [[ -f "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" && "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" -nt "$NATIVE_RUNTIME_PROBE_SUMMARY" ]]; then INVENTORY_REQUIRED=1 fi +if [[ -f "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" && "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" -nt "$RUNTIME_PROBE_GATE_ALIGNMENT" ]]; then + INVENTORY_REQUIRED=1 +fi +if [[ -f "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" && "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" -nt "$NATIVE_RUNTIME_PROBE_EXECUTION_PLAN" ]]; then + INVENTORY_REQUIRED=1 +fi +if [[ -f "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" && "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" -nt "$NATIVE_RUNTIME_PROBE_PASS_EVIDENCE_CONTRACT" ]]; then + INVENTORY_REQUIRED=1 +fi +if [[ -f "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" && "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" -nt "$RUNTIME_BOUNDARY_PROMOTION_READINESS" ]]; then + INVENTORY_REQUIRED=1 +fi +if [[ -f "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" && "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" -nt "$RUNTIME_BOUNDARY_PROMOTION_BLOCKERS" ]]; then + INVENTORY_REQUIRED=1 +fi +if [[ -f "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" && "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" -nt "$RUNTIME_BOUNDARY_POST_NATIVE_PASS_GATES" ]]; then + INVENTORY_REQUIRED=1 +fi +if [[ -f "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" && "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" -nt "$RUNTIME_BOUNDARY_HOST_PREFLIGHT" ]]; then + INVENTORY_REQUIRED=1 +fi if [[ "$INVENTORY_REQUIRED" == "1" ]]; then SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_sim_configs_inventory_wasm.sh" fi diff --git a/wasm-port/tests/native/probe_millturn_user_m_runtime.sh b/wasm-port/tests/native/probe_millturn_user_m_runtime.sh index 604e2c4..4fff612 100644 --- a/wasm-port/tests/native/probe_millturn_user_m_runtime.sh +++ b/wasm-port/tests/native/probe_millturn_user_m_runtime.sh @@ -77,11 +77,13 @@ print_kv millturn_user_m_promotion_allowed 0 if [[ "$source_ready" != "1" ]]; then print_kv millturn_user_m_runtime_probe_status "blocked_missing_source" + print_kv millturn_user_m_runtime_probe_note "missing_source_requirements_keep_user_m_process_blocked" exit 0 fi if [[ "$runtime_ready" != "1" ]]; then print_kv millturn_user_m_runtime_probe_status "skipped_missing_host_runtime" + print_kv millturn_user_m_runtime_probe_note "missing_host_runtime_requirements_keep_user_m_process_blocked" exit 0 fi diff --git a/wasm-port/tests/native/probe_python_remap_runtime.sh b/wasm-port/tests/native/probe_python_remap_runtime.sh index f940a27..2109e2c 100755 --- a/wasm-port/tests/native/probe_python_remap_runtime.sh +++ b/wasm-port/tests/native/probe_python_remap_runtime.sh @@ -75,11 +75,13 @@ print_kv python_remap_promotion_allowed 0 if [[ "$source_ready" != "1" ]]; then print_kv python_remap_runtime_probe_status "blocked_missing_source" + print_kv python_remap_runtime_probe_note "missing_source_requirements_keep_python_remap_blocked" exit 0 fi if [[ "$runtime_ready" != "1" ]]; then print_kv python_remap_runtime_probe_status "skipped_missing_host_runtime" + print_kv python_remap_runtime_probe_note "missing_host_runtime_requirements_keep_python_remap_blocked" exit 0 fi diff --git a/wasm-port/tests/native/probe_tool_db_runtime.sh b/wasm-port/tests/native/probe_tool_db_runtime.sh index 0ff73fe..35f394b 100644 --- a/wasm-port/tests/native/probe_tool_db_runtime.sh +++ b/wasm-port/tests/native/probe_tool_db_runtime.sh @@ -74,11 +74,13 @@ print_kv tool_db_promotion_allowed 0 if [[ "$source_ready" != "1" ]]; then print_kv tool_db_runtime_probe_status "blocked_missing_source" + print_kv tool_db_runtime_probe_note "missing_source_requirements_keep_tool_db_process_blocked" exit 0 fi if [[ "$runtime_ready" != "1" ]]; then print_kv tool_db_runtime_probe_status "skipped_missing_host_runtime" + print_kv tool_db_runtime_probe_note "missing_host_runtime_requirements_keep_tool_db_process_blocked" exit 0 fi diff --git a/wasm-port/tests/native/verify_native_probes.sh b/wasm-port/tests/native/verify_native_probes.sh index 65a1688..45683de 100755 --- a/wasm-port/tests/native/verify_native_probes.sh +++ b/wasm-port/tests/native/verify_native_probes.sh @@ -200,6 +200,8 @@ check_native_runtime_probe_summary_row() { local boundary_kind="$2" local target="$3" local runtime_probe="$4" + local expected_required_native_proof="$5" + local expected_opt_in_env="$6" local summary_file="$BUILD_DIR/native-runtime-probe-summary.tsv" local row @@ -224,17 +226,28 @@ check_native_runtime_probe_summary_row() { local runtime_ready local source_proof_ready + local required_native_proof + local opt_in_env local probe_status local execution_enabled local promotion_allowed local missing_requirements + local probe_note local stdout_log - IFS=$'\t' read -r _ _ _ _ runtime_ready source_proof_ready probe_status execution_enabled promotion_allowed missing_requirements stdout_log <<< "$row" + IFS=$'\t' read -r _ _ _ _ required_native_proof opt_in_env runtime_ready source_proof_ready probe_status execution_enabled promotion_allowed missing_requirements probe_note stdout_log <<< "$row" if [[ "$source_proof_ready" != "1" ]]; then echo "$target runtime probe lacks source proof readiness: $source_proof_ready" >&2 exit 1 fi + if [[ "$required_native_proof" != "$expected_required_native_proof" ]]; then + echo "$target runtime probe required native proof drift: $required_native_proof" >&2 + exit 1 + fi + if [[ "$opt_in_env" != "$expected_opt_in_env" ]]; then + echo "$target runtime probe opt-in env drift: $opt_in_env" >&2 + exit 1 + fi if [[ "$execution_enabled" != "0" || "$promotion_allowed" != "0" ]]; then echo "$target runtime probe unexpectedly enables execution or promotion" >&2 exit 1 @@ -247,10 +260,26 @@ check_native_runtime_probe_summary_row() { echo "$target runtime probe is not ready but did not skip: $probe_status" >&2 exit 1 fi + if [[ "$runtime_ready" == "0" && "$probe_note" != *"missing_host_runtime"* ]]; then + echo "$target runtime probe missing-runtime note drift: $probe_note" >&2 + exit 1 + fi if [[ "$runtime_ready" == "1" && "$missing_requirements" != "-" ]]; then echo "$target runtime probe is ready but lists missing requirements: $missing_requirements" >&2 exit 1 fi + if [[ "$probe_status" == "ready_disabled_by_default" && "$probe_note" != *"set_${opt_in_env%%=*}"* ]]; then + echo "$target runtime probe ready-disabled note drift: $probe_note" >&2 + exit 1 + fi + if [[ "$probe_status" == runtime_*_passed && "$probe_note" != *"without_promotion"* ]]; then + echo "$target runtime probe passed note drift: $probe_note" >&2 + exit 1 + fi + if [[ "$probe_note" == "-" || -z "$probe_note" ]]; then + echo "$target runtime probe lacks note" >&2 + exit 1 + fi if [[ ! -f "$BUILD_DIR/$stdout_log" ]]; then echo "$target runtime summary references missing stdout log: $stdout_log" >&2 exit 1 @@ -266,7 +295,7 @@ check_native_runtime_probe_summary() { fi local expected_header - expected_header=$'boundary_class\tboundary_kind\ttarget\truntime_probe\truntime_ready\tsource_proof_ready\tprobe_status\texecution_enabled\tpromotion_allowed\tmissing_requirements\tstdout_log' + expected_header=$'boundary_class\tboundary_kind\ttarget\truntime_probe\trequired_native_proof\topt_in_env\truntime_ready\tsource_proof_ready\tprobe_status\texecution_enabled\tpromotion_allowed\tmissing_requirements\tprobe_note\tstdout_log' if [[ "$(sed -n '1p' "$summary_file")" != "$expected_header" ]]; then echo "native runtime probe summary header drift: $summary_file" >&2 exit 1 @@ -283,17 +312,23 @@ check_native_runtime_probe_summary() { L4-USER-M-PROCESS \ external_user_m_process \ axis/vismach/millturn/example.ngc \ - linuxcnc_millturn_user_m_runtime_probe + linuxcnc_millturn_user_m_runtime_probe \ + native_runtime_state_probe_required \ + ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1 check_native_runtime_probe_summary_row \ L4-TOOL-DB \ tool_database_process \ axis/db_demo/base.ngc \ - linuxcnc_tool_db_runtime_probe + linuxcnc_tool_db_runtime_probe \ + native_db_process_protocol_probe_required \ + ENABLE_TOOL_DB_RUNTIME_PROBE=1 check_native_runtime_probe_summary_row \ L4-PYTHON-REMAP \ python_runtime \ axis/remap/stop-lookahead/nc_files \ - linuxcnc_python_remap_runtime_probe + linuxcnc_python_remap_runtime_probe \ + linuxcnc_python_runtime_lifecycle_probe_required \ + ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 } check_source_probe_coverage @@ -609,6 +644,7 @@ grep -Fq "millturn_user_m_runtime_requirements=" "$MILLTURN_USER_M_RUNTIME_STDOU grep -Fq "millturn_user_m_source_proof_ready=1" "$MILLTURN_USER_M_RUNTIME_STDOUT" 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)" \ "$MILLTURN_USER_M_RUNTIME_STDOUT" @@ -648,6 +684,7 @@ grep -Fq "tool_db_runtime_requirements=" "$TOOL_DB_RUNTIME_STDOUT" grep -Fq "tool_db_source_proof_ready=1" "$TOOL_DB_RUNTIME_STDOUT" grep -Fq "tool_db_execution_enabled=0" "$TOOL_DB_RUNTIME_STDOUT" grep -Fq "tool_db_promotion_allowed=0" "$TOOL_DB_RUNTIME_STDOUT" +grep -Fq "tool_db_runtime_probe_note=" "$TOOL_DB_RUNTIME_STDOUT" if grep -Fq "tool_db_runtime_ready=1" "$TOOL_DB_RUNTIME_STDOUT"; then grep -Eq "tool_db_runtime_probe_status=(ready_disabled_by_default|runtime_protocol_probe_passed)" \ "$TOOL_DB_RUNTIME_STDOUT" @@ -697,6 +734,7 @@ grep -Fq "python_remap_runtime_modules=axis/remap/stop-lookahead/python/remap.py grep -Fq "python_remap_source_proof_ready=1" "$PYTHON_REMAP_RUNTIME_STDOUT" grep -Fq "python_remap_execution_enabled=0" "$PYTHON_REMAP_RUNTIME_STDOUT" grep -Fq "python_remap_promotion_allowed=0" "$PYTHON_REMAP_RUNTIME_STDOUT" +grep -Fq "python_remap_runtime_probe_note=" "$PYTHON_REMAP_RUNTIME_STDOUT" if grep -Fq "python_remap_runtime_ready=1" "$PYTHON_REMAP_RUNTIME_STDOUT"; then grep -Eq "python_remap_runtime_probe_status=(ready_disabled_by_default|runtime_lifecycle_probe_passed)" \ "$PYTHON_REMAP_RUNTIME_STDOUT" diff --git a/wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs b/wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs index 7296349..972ae1d 100644 --- a/wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs +++ b/wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs @@ -78,10 +78,12 @@ const toolDbProcessProtocolGatesPath = resolve(buildDir, "tool-db-process-protoc const toolDbProcessTransactionPlanPath = resolve(buildDir, "tool-db-process-transaction-plan.tsv"); const toolDbProcessNativeProtocolAlignmentPath = resolve(buildDir, "tool-db-process-native-protocol-alignment.tsv"); const toolDbProcessNativeRuntimeReadinessPath = resolve(buildDir, "tool-db-process-native-runtime-readiness.tsv"); +const toolDbProcessNativeRuntimeProbeGatePath = resolve(buildDir, "tool-db-process-native-runtime-probe-gate.tsv"); const pythonRemapBoundarySummaryPath = resolve(buildDir, "python-remap-boundary-summary.tsv"); const pythonRemapRuntimeGatesPath = resolve(buildDir, "python-remap-runtime-gates.tsv"); const pythonRemapNativeRuntimeAlignmentPath = resolve(buildDir, "python-remap-native-runtime-alignment.tsv"); const pythonRemapNativeRuntimeReadinessPath = resolve(buildDir, "python-remap-native-runtime-readiness.tsv"); +const pythonRemapNativeRuntimeProbeGatePath = resolve(buildDir, "python-remap-native-runtime-probe-gate.tsv"); const pythonRemapNativeRuntimeStatePlanPath = resolve(buildDir, "python-remap-native-runtime-state-plan.tsv"); const pythonRemapNativeRuntimeFixturePlanPath = resolve(buildDir, "python-remap-native-runtime-fixture-plan.tsv"); const pythonRemapFamilySummaryPath = resolve(buildDir, "python-remap-family-summary.tsv"); @@ -93,7 +95,14 @@ const boundaryProofGatesPath = resolve(buildDir, "boundary-proof-gates.tsv"); const nativeProofAlignmentSummaryPath = resolve(buildDir, "native-proof-alignment-summary.tsv"); const nativeRuntimeProbeSummaryPath = resolve(buildDir, "native-runtime-probe-summary.tsv"); const runtimeBoundaryNativeAlignmentSummaryPath = resolve(buildDir, "runtime-boundary-native-alignment-summary.tsv"); +const runtimeProbeGateAlignmentPath = resolve(buildDir, "runtime-probe-gate-alignment.tsv"); +const nativeRuntimeProbeExecutionPlanPath = resolve(buildDir, "native-runtime-probe-execution-plan.tsv"); +const nativeRuntimeProbePassEvidenceContractPath = resolve(buildDir, "native-runtime-probe-pass-evidence-contract.tsv"); const blockedRuntimePromotionLockPath = resolve(buildDir, "blocked-runtime-promotion-lock.tsv"); +const runtimeBoundaryPromotionReadinessPath = resolve(buildDir, "runtime-boundary-promotion-readiness.tsv"); +const runtimeBoundaryPromotionBlockersPath = resolve(buildDir, "runtime-boundary-promotion-blockers.tsv"); +const runtimeBoundaryPostNativePassGatesPath = resolve(buildDir, "runtime-boundary-post-native-pass-gates.tsv"); +const runtimeBoundaryHostPreflightPath = resolve(buildDir, "runtime-boundary-host-preflight.tsv"); const nextBoundaryRecommendationsPath = resolve(buildDir, "next-boundary-recommendations.tsv"); mkdirSync(buildDir, { recursive: true }); @@ -2164,6 +2173,188 @@ function verifyToolDbProcessNativeRuntimeReadinessRows(rows) { return parsedRows; } +function toolDbProcessNativeRuntimeProbeGateRows({ + transactionRows, + readinessRows, +}) { + const first = transactionRows[0]; + if (!first) { + return []; + } + + const readinessByRequirement = new Map( + readinessRows.map((row) => [row.requirement, row]), + ); + const hostRequirements = ["python3", "linuxcnc", "milltask", "halcmd"]; + const dbSourceRequirements = ["axis/db_demo/db_nonran.py"]; + const pythonModuleRequirements = ["linuxcnc.so", "tooldb.py"]; + const requiredRequirements = [ + ...hostRequirements, + ...dbSourceRequirements, + ...pythonModuleRequirements, + ]; + const missingRequirements = requiredRequirements.filter((requirement) => + readinessByRequirement.get(requirement)?.available !== "1", + ); + const dbProgramSourceReady = dbSourceRequirements.every((requirement) => + readinessByRequirement.get(requirement)?.available === "1", + ); + const pythonModulesReady = pythonModuleRequirements.every((requirement) => + readinessByRequirement.get(requirement)?.available === "1", + ); + const protocolContractReady = ( + transactionRows.length === 5 && + transactionRows.every((row) => + row.db_program === "./db_nonran.py" && + row.protocol_message !== "-" && + row.db_program_callback !== "-" && + row.state_targets !== "-" && + row.execution_enabled === "0" && + row.promotion_allowed === "0", + ) + ); + const runtimeReady = missingRequirements.length === 0; + const sourceProofReady = ( + dbProgramSourceReady && + pythonModulesReady && + protocolContractReady + ); + const gateStatus = runtimeReady && sourceProofReady + ? "ready_to_implement_protocol_probe" + : "blocked_missing_host_runtime"; + + return [[ + first.path, + first.ini, + first.blocked, + first.db_program, + "linuxcnc_tooldata_db_process", + readinessByRequirement.get("python3")?.available ?? "0", + "0", + "0", + flagValue(dbProgramSourceReady), + flagValue(pythonModulesReady), + flagValue(protocolContractReady), + listValue(transactionRows.map((row) => `${row.transaction_phase}:${row.protocol_message}`)), + listValue(requiredRequirements.map((requirement) => + `${requirement}:${readinessByRequirement.get(requirement)?.available ?? "0"}`, + )), + listValue(missingRequirements), + flagValue(runtimeReady), + flagValue(sourceProofReady), + "native_db_process_protocol_probe_required", + gateStatus, + "pending", + "0", + "0", + 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", + ].map(tsvValue).join("\t")]; +} + +function verifyToolDbProcessNativeRuntimeProbeGateRows({ + rows, + transactionRows, + readinessRows, +}) { + const expectedHeaders = [ + "path", + "ini", + "blocked", + "db_program", + "required_runtime", + "python3_available", + "python3_sufficient", + "tool_table_fallback_sufficient", + "db_program_source_ready", + "linuxcnc_python_modules_ready", + "protocol_contract_ready", + "protocol_transactions", + "runtime_requirements", + "missing_requirements", + "runtime_ready", + "source_proof_ready", + "required_native_proof", + "gate_status", + "proof_status", + "execution_enabled", + "promotion_allowed", + "notes", + ]; + const parsedRows = parseTsv( + `${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`, + expectedHeaders, + ); + + assert.equal(parsedRows.length, 1, "tool DB runtime probe gate must have one db_demo row"); + const row = parsedRows[0]; + assert.equal(row.path, "axis/db_demo/base.ngc", "tool DB probe gate path drift"); + assert.equal(row.ini, "axis/db_demo/db_nonran.ini", "tool DB probe gate INI drift"); + assert.equal(row.blocked, "L4-TOOL-DB", "tool DB probe gate blocked kind drift"); + assert.equal(row.db_program, "./db_nonran.py", "tool DB probe gate DB_PROGRAM drift"); + assert.equal(row.required_runtime, "linuxcnc_tooldata_db_process", "tool DB probe gate runtime drift"); + assert.ok(["0", "1"].includes(row.python3_available), "invalid tool DB python3 availability"); + assert.equal(row.python3_sufficient, "0", "python3 alone must not satisfy tool DB runtime readiness"); + assert.equal(row.tool_table_fallback_sufficient, "0", "tool-table fallback must not satisfy DB_PROGRAM readiness"); + assert.equal(row.db_program_source_ready, "1", "db_nonran.py source must be ready"); + assert.equal(row.linuxcnc_python_modules_ready, "1", "linuxcnc/tooldb Python modules must be source-ready"); + assert.equal(row.protocol_contract_ready, "1", "tool DB protocol contract must be ready"); + for (const phase of [ + "startup_handshake", + "initial_get_all", + "spindle_load_notify", + "tool_offset_notify", + "spindle_unload_notify", + ]) { + assert.ok(row.protocol_transactions.includes(`${phase}:`), `tool DB probe gate missing transaction ${phase}`); + } + + const expectedRequirements = [ + "python3", + "linuxcnc", + "milltask", + "halcmd", + "axis/db_demo/db_nonran.py", + "linuxcnc.so", + "tooldb.py", + ]; + const readinessByRequirement = new Map(readinessRows.map((entry) => [entry.requirement, entry])); + const expectedMissing = expectedRequirements.filter((requirement) => + readinessByRequirement.get(requirement)?.available !== "1", + ); + const allRuntimeReady = expectedMissing.length === 0; + for (const requirement of expectedRequirements) { + assert.ok(row.runtime_requirements.includes(`${requirement}:`), `tool DB probe gate missing ${requirement}`); + } + assert.equal(row.runtime_ready, flagValue(allRuntimeReady), "tool DB runtime readiness drift"); + assert.equal(row.source_proof_ready, "1", "tool DB source proof must be ready before runtime probe implementation"); + assert.equal(row.required_native_proof, "native_db_process_protocol_probe_required", "tool DB native proof drift"); + assert.equal(row.proof_status, "pending", "tool DB probe gate must remain pending"); + assert.equal(row.execution_enabled, "0", "tool DB probe gate must not enable execution"); + assert.equal(row.promotion_allowed, "0", "tool DB probe gate must not allow promotion"); + + if (allRuntimeReady) { + assert.equal(row.missing_requirements, "-", "ready tool DB runtime has missing requirements"); + assert.equal(row.gate_status, "ready_to_implement_protocol_probe", "ready tool DB gate status drift"); + } else { + assert.deepEqual( + splitSummaryList(row.missing_requirements).sort(), + expectedMissing.sort(), + "tool DB missing runtime requirements drift", + ); + assert.equal(row.gate_status, "blocked_missing_host_runtime", "missing tool DB runtime gate status drift"); + } + assert.ok( + row.notes.includes("python3") && + row.notes.includes("fallback_not_sufficient"), + "tool DB probe gate must record python3/fallback insufficiency", + ); + assert.equal(transactionRows.length, 5, "tool DB probe gate must consume every transaction row"); + + return parsedRows; +} + function toolDbNativeProofKeyForGate(row) { if (row.gate_kind === "protocol_message") { if (row.gate === "startup:expect_reply=v2.1") { @@ -4352,6 +4543,177 @@ function verifyPythonRemapNativeRuntimeFixturePlanRows(rows, statePlanRows) { return parsedRows; } +function pythonRemapNativeRuntimeProbeGateRows({ + fixturePlanRows, + readinessRows, +}) { + const fixture = fixturePlanRows[0]; + if (!fixture) { + return []; + } + + const readinessForFamily = readinessRows.filter((row) => row.family === fixture.family); + const readinessByRequirement = new Map( + readinessForFamily.map((row) => [row.requirement, row]), + ); + const representativeModules = splitSummaryList(fixture.python_modules); + const runtimeOwnerSources = [ + "src/emc/rs274ngc/interp_python.cc", + "src/emc/pythonplugin/python_plugin.cc", + ]; + const hostRequirements = ["python3", "linuxcnc"]; + const requiredRequirements = [ + ...hostRequirements, + ...runtimeOwnerSources, + ...representativeModules, + ]; + const missingRequirements = requiredRequirements.filter((requirement) => + readinessByRequirement.get(requirement)?.available !== "1", + ); + const runtimeOwnerSourcesReady = runtimeOwnerSources.every((requirement) => + readinessByRequirement.get(requirement)?.available === "1", + ); + const representativeModulesReady = representativeModules.every((requirement) => + readinessByRequirement.get(requirement)?.available === "1", + ); + const runtimeReady = missingRequirements.length === 0; + const sourceProofReady = ( + fixture.source_alignment_ok === "1" && + runtimeOwnerSourcesReady && + representativeModulesReady && + fixture.execution_enabled === "0" && + fixture.promotion_allowed === "0" + ); + const python3Available = readinessByRequirement.get("python3")?.available ?? "0"; + const linuxcncAvailable = readinessByRequirement.get("linuxcnc")?.available ?? "0"; + const gateStatus = runtimeReady && sourceProofReady + ? "ready_to_implement_lifecycle_probe" + : "blocked_missing_host_runtime"; + + return [[ + fixture.family, + fixture.blocked, + fixture.fixture_id, + fixture.required_runtime, + python3Available, + "0", + linuxcncAvailable, + flagValue(runtimeOwnerSourcesReady), + listValue(representativeModules.map((modulePath) => `import:${modulePath}`)), + flagValue(representativeModulesReady), + listValue(requiredRequirements.map((requirement) => + `${requirement}:${readinessByRequirement.get(requirement)?.available ?? "0"}`, + )), + listValue(missingRequirements), + flagValue(runtimeReady), + flagValue(sourceProofReady), + fixture.proof_kind, + gateStatus, + "pending", + "0", + "0", + runtimeReady + ? "host_and_source_ready_write_linuxcnc_python_lifecycle_probe_python3_alone_not_sufficient" + : "python3_alone_not_sufficient_keep_python_remap_blocked", + ].map(tsvValue).join("\t")]; +} + +function verifyPythonRemapNativeRuntimeProbeGateRows({ + rows, + fixturePlanRows, + readinessRows, +}) { + const expectedHeaders = [ + "family", + "blocked", + "fixture_id", + "required_runtime", + "python3_available", + "python3_sufficient", + "linuxcnc_available", + "runtime_owner_sources_ready", + "representative_module_imports", + "representative_modules_ready", + "runtime_requirements", + "missing_requirements", + "runtime_ready", + "source_proof_ready", + "required_native_proof", + "gate_status", + "proof_status", + "execution_enabled", + "promotion_allowed", + "notes", + ]; + const parsedRows = parseTsv( + `${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`, + expectedHeaders, + ); + + assert.equal(parsedRows.length, 1, "Python runtime probe gate must start with one selected fixture"); + const row = parsedRows[0]; + const fixture = fixturePlanRows[0]; + assert.ok(fixture, "Python runtime probe gate lacks fixture source row"); + assert.equal(row.family, fixture.family, `${row.family}: probe gate family drift`); + assert.equal(row.blocked, "L4-PYTHON-REMAP", `${row.family}: probe gate blocked kind drift`); + assert.equal(row.fixture_id, "stop_lookahead_python_runtime_lifecycle", `${row.family}: probe gate fixture drift`); + assert.equal(row.required_runtime, "linuxcnc_python_remap_runtime", `${row.family}: probe gate runtime drift`); + assert.ok(["0", "1"].includes(row.python3_available), `${row.family}: invalid python3 availability`); + assert.equal(row.python3_sufficient, "0", `${row.family}: python3 alone must never satisfy remap readiness`); + assert.ok(["0", "1"].includes(row.linuxcnc_available), `${row.family}: invalid linuxcnc availability`); + assert.equal(row.runtime_owner_sources_ready, "1", `${row.family}: LinuxCNC Python runtime owner sources must be ready`); + assert.equal(row.representative_modules_ready, "1", `${row.family}: representative Python modules must be source-ready`); + assert.notEqual(row.representative_module_imports, "-", `${row.family}: probe gate lacks representative module imports`); + for (const modulePath of splitSummaryList(fixture.python_modules)) { + assert.ok( + row.representative_module_imports.includes(`import:${modulePath}`), + `${row.family}: missing representative import ${modulePath}`, + ); + } + + const readinessByRequirement = new Map( + readinessRows + .filter((entry) => entry.family === row.family) + .map((entry) => [entry.requirement, entry]), + ); + const expectedRequirements = [ + "python3", + "linuxcnc", + "src/emc/rs274ngc/interp_python.cc", + "src/emc/pythonplugin/python_plugin.cc", + ...splitSummaryList(fixture.python_modules), + ]; + const expectedMissing = expectedRequirements.filter((requirement) => + readinessByRequirement.get(requirement)?.available !== "1", + ); + const allRuntimeReady = expectedMissing.length === 0; + for (const requirement of expectedRequirements) { + assert.ok(row.runtime_requirements.includes(`${requirement}:`), `${row.family}: missing runtime requirement ${requirement}`); + } + assert.equal(row.runtime_ready, flagValue(allRuntimeReady), `${row.family}: runtime readiness drift`); + assert.equal(row.source_proof_ready, "1", `${row.family}: source proof must be ready before runtime probe implementation`); + assert.equal(row.required_native_proof, "linuxcnc_python_runtime_lifecycle_probe_required", `${row.family}: native proof drift`); + assert.equal(row.proof_status, "pending", `${row.family}: probe gate must remain pending`); + assert.equal(row.execution_enabled, "0", `${row.family}: probe gate must not enable execution`); + assert.equal(row.promotion_allowed, "0", `${row.family}: probe gate must not allow promotion`); + + if (allRuntimeReady) { + assert.equal(row.missing_requirements, "-", `${row.family}: ready runtime has missing requirements`); + assert.equal(row.gate_status, "ready_to_implement_lifecycle_probe", `${row.family}: ready gate status drift`); + assert.ok(row.notes.includes("python3_alone_not_sufficient"), `${row.family}: ready gate must still reject python3-only proof`); + } else { + assert.deepEqual( + splitSummaryList(row.missing_requirements).sort(), + expectedMissing.sort(), + `${row.family}: missing runtime requirements drift`, + ); + assert.equal(row.gate_status, "blocked_missing_host_runtime", `${row.family}: missing runtime gate status drift`); + assert.ok(row.notes.includes("python3_alone_not_sufficient"), `${row.family}: missing runtime gate must reject python3-only proof`); + } + + return parsedRows; +} + function runtimeBoundaryNativeAlignmentSummaryRows({ userMRows, toolDbRows, @@ -4616,15 +4978,24 @@ function boundaryPhaseCompletionSummaryRows({ userMRuntimeProbeGateRows, toolDbTransactionRows, toolDbRuntimeReadinessRows, + toolDbRuntimeProbeGateRows, pythonFamilyRows, pythonRuntimeContractRows, pythonRuntimeReadinessRows, + pythonRuntimeProbeGateRows, pythonRuntimeStatePlanRows, pythonRuntimeFixturePlanRows, runtimeContractSummaryRows, nativeProofAlignmentRows, nativeRuntimeProbeRows, runtimeNativeAlignmentRows, + runtimeProbeGateAlignmentRows, + nativeRuntimeProbeExecutionPlanRows, + nativeRuntimeProbePassEvidenceRows, + runtimeBoundaryPromotionReadinessRows, + runtimeBoundaryPromotionBlockerRows, + runtimeBoundaryPostNativePassGateRows, + runtimeBoundaryHostPreflightRows, trackedRows, inventoryRows, }) { @@ -4809,6 +5180,25 @@ function boundaryPhaseCompletionSummaryRows({ row.promotion_allowed === "0", ) ); + const toolDbRuntimeProbeGateOk = ( + toolDbRuntimeProbeGateRows.length === 1 && + toolDbRuntimeProbeGateRows.every((row) => + row.path === "axis/db_demo/base.ngc" && + row.blocked === "L4-TOOL-DB" && + row.required_runtime === "linuxcnc_tooldata_db_process" && + row.python3_sufficient === "0" && + row.tool_table_fallback_sufficient === "0" && + row.db_program_source_ready === "1" && + row.linuxcnc_python_modules_ready === "1" && + 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" && + row.execution_enabled === "0" && + row.promotion_allowed === "0", + ) + ); const pythonRuntimeContractOk = ( pythonRuntimeContractRows.length === pythonFamilyRows.length && pythonRuntimeContractRows.every((row) => @@ -4834,6 +5224,23 @@ function boundaryPhaseCompletionSummaryRows({ row.promotion_allowed === "0", ) ); + const pythonRuntimeProbeGateOk = ( + pythonRuntimeProbeGateRows.length === 1 && + pythonRuntimeProbeGateRows.every((row) => + row.family === "axis/remap/stop-lookahead/nc_files" && + row.blocked === "L4-PYTHON-REMAP" && + row.required_runtime === "linuxcnc_python_remap_runtime" && + row.python3_sufficient === "0" && + row.runtime_owner_sources_ready === "1" && + row.representative_modules_ready === "1" && + row.source_proof_ready === "1" && + ["0", "1"].includes(row.runtime_ready) && + ["blocked_missing_host_runtime", "ready_to_implement_lifecycle_probe"].includes(row.gate_status) && + row.proof_status === "pending" && + row.execution_enabled === "0" && + row.promotion_allowed === "0", + ) + ); const pythonRuntimeStatePlanOk = ( pythonRuntimeStatePlanRows.length === pythonRuntimeContractRows.length && pythonRuntimeStatePlanRows.every((row) => @@ -4886,6 +5293,115 @@ function boundaryPhaseCompletionSummaryRows({ row.promotion_allowed === "0", ) ); + const runtimeProbeGateAlignmentOk = ( + runtimeProbeGateAlignmentRows.length === 3 && + runtimeProbeGateAlignmentRows.every((row) => + row.gate_runtime_ready === row.native_runtime_ready && + row.gate_source_proof_ready === "1" && + row.native_source_proof_ready === "1" && + row.missing_requirements_match === "1" && + row.execution_disabled_match === "1" && + row.promotion_disabled_match === "1" && + row.status_compatible === "1" && + row.alignment_ok === "1", + ) + ); + const nativeRuntimeProbeExecutionPlanOk = ( + nativeRuntimeProbeExecutionPlanRows.length === 3 && + nativeRuntimeProbeExecutionPlanRows.every((row) => + row.source_proof_ready === "1" && + row.gate_alignment_ok === "1" && + /^ENABLE_[A-Z0-9_]+=1$/.test(row.opt_in_env) && + row.execution_command.includes(row.opt_in_env) && + row.execution_command.includes("wasm-port/tests/native/") && + row.promotion_requires.includes(`native:${row.expected_pass_status}`) && + row.promotion_requires.includes("node:inventory_gate_alignment_after_native_pass") && + row.promotion_requires.includes("browser:smoke_gate_alignment_after_node_pass") && + row.execution_enabled === "0" && + row.promotion_allowed === "0", + ) + ); + const nativeRuntimeProbePassEvidenceOk = ( + nativeRuntimeProbePassEvidenceRows.length === 3 && + nativeRuntimeProbePassEvidenceRows.every((row) => + row.expected_evidence_keys !== "-" && + ( + (row.current_probe_status === row.expected_pass_status && + row.evidence_required_now === "1" && + row.observed_evidence_ready === "1" && + row.missing_evidence_keys === "-" && + row.evidence_status === "native_pass_evidence_observed") || + (row.current_probe_status !== row.expected_pass_status && + row.evidence_required_now === "0" && + row.observed_evidence_ready === "0" && + row.missing_evidence_keys === "-" && + row.evidence_status === "pending_until_native_pass") + ) && + row.execution_enabled === "0" && + row.promotion_allowed === "0", + ) + ); + const runtimeBoundaryPromotionReadinessOk = ( + runtimeBoundaryPromotionReadinessRows.length === 3 && + runtimeBoundaryPromotionReadinessRows.every((row) => + row.native_evidence_status !== "-" && + row.node_inventory_gate_complete === "0" && + row.browser_smoke_gate_complete === "0" && + row.promotion_lock_active === "1" && + row.manual_lock_update_required === "1" && + row.promotion_ready === "0" && + row.execution_enabled === "0" && + row.promotion_allowed === "0" && + row.blocking_reason !== "-", + ) + ); + const runtimeBoundaryPromotionBlockersOk = ( + runtimeBoundaryPromotionBlockerRows.length === 3 && + runtimeBoundaryPromotionBlockerRows.every((row) => + row.promotion_ready === "0" && + /^[1-9][0-9]*$/.test(row.blocker_count) && + row.blocker_keys !== "-" && + row.next_unblock_action !== "-" && + row.execution_enabled === "0" && + row.promotion_allowed === "0", + ) + ); + const runtimeBoundaryPostNativePassGatesOk = ( + runtimeBoundaryPostNativePassGateRows.length === 3 && + runtimeBoundaryPostNativePassGateRows.every((row) => + row.native_pass_ready === "0" && + row.native_evidence_ready === "0" && + row.node_gate_status === "blocked_until_native_pass_evidence" && + row.browser_gate_status === "blocked_until_node_gate_complete" && + row.manual_lock_update_required === "1" && + row.promotion_lock_active === "1" && + row.execution_enabled === "0" && + row.promotion_allowed === "0" && + row.gate_status === "blocked_before_native_pass", + ) + ); + const runtimeBoundaryHostPreflightOk = ( + runtimeBoundaryHostPreflightRows.length === 3 && + runtimeBoundaryHostPreflightRows.every((row) => + row.source_proof_ready === "1" && + ["0", "1"].includes(row.runtime_ready) && + row.required_runtime !== "-" && + row.runtime_requirements !== "-" && + row.required_native_proof !== "-" && + /^ENABLE_[A-Z0-9_]+=1$/.test(row.opt_in_env) && + row.execution_command.includes(row.opt_in_env) && + ( + (row.runtime_ready === "1" && + row.missing_requirements === "-" && + row.preflight_status === "ready_to_run_opt_in_native_probe") || + (row.runtime_ready === "0" && + row.missing_requirements !== "-" && + row.preflight_status === "blocked_missing_host_runtime") + ) && + row.execution_enabled === "0" && + row.promotion_allowed === "0", + ) + ); return [ [ @@ -4936,6 +5452,48 @@ function boundaryPhaseCompletionSummaryRows({ String(nativeRuntimeProbeRows.length), "native runtime probe summary records current host readiness and disabled runtime probe status", ], + [ + "runtime_probe_gate_alignment", + flagValue(runtimeProbeGateAlignmentOk), + String(runtimeProbeGateAlignmentRows.length), + "native runtime probe summary agrees with generated runtime probe gates before any blocked boundary can be promoted", + ], + [ + "native_runtime_probe_execution_plan", + flagValue(nativeRuntimeProbeExecutionPlanOk), + String(nativeRuntimeProbeExecutionPlanRows.length), + "native runtime probe execution plan records exact opt-in commands and expected pass states while keeping promotion locked", + ], + [ + "native_runtime_probe_pass_evidence_contract", + flagValue(nativeRuntimeProbePassEvidenceOk), + String(nativeRuntimeProbePassEvidenceRows.length), + "native runtime probe pass evidence contract records stdout evidence required after each native pass before Node/browser promotion work", + ], + [ + "runtime_boundary_promotion_readiness", + flagValue(runtimeBoundaryPromotionReadinessOk), + String(runtimeBoundaryPromotionReadinessRows.length), + "runtime boundary promotion readiness keeps every blocked runtime family unpromoted until native pass evidence, Node/browser gates, and manual lock update complete", + ], + [ + "runtime_boundary_promotion_blockers", + flagValue(runtimeBoundaryPromotionBlockersOk), + String(runtimeBoundaryPromotionBlockerRows.length), + "runtime boundary promotion blockers summarize remaining host/runtime/evidence/Node/browser/manual-lock blockers per blocked family", + ], + [ + "runtime_boundary_post_native_pass_gates", + flagValue(runtimeBoundaryPostNativePassGatesOk), + String(runtimeBoundaryPostNativePassGateRows.length), + "runtime boundary post-native-pass gates require Node/browser proof and manual lock updates before any promotion", + ], + [ + "runtime_boundary_host_preflight", + flagValue(runtimeBoundaryHostPreflightOk), + String(runtimeBoundaryHostPreflightRows.length), + "runtime boundary host preflight summarizes required runtimes, missing host requirements, and opt-in native probe commands without execution", + ], [ "user_m_transition_contract", flagValue(userMTransitionContractOk), @@ -4972,6 +5530,12 @@ function boundaryPhaseCompletionSummaryRows({ String(toolDbRuntimeReadinessRows.length), "db_demo native runtime readiness records required DB process commands and Python modules without enabling execution or promotion", ], + [ + "tool_db_native_runtime_probe_gate", + flagValue(toolDbRuntimeProbeGateOk), + String(toolDbRuntimeProbeGateRows.length), + "db_demo native runtime probe gate records host readiness, DB_PROGRAM source, Python modules, and protocol contract while rejecting python3 or tbl fallback promotion", + ], [ "python_runtime_contract", flagValue(pythonRuntimeContractOk), @@ -4984,6 +5548,12 @@ function boundaryPhaseCompletionSummaryRows({ String(pythonRuntimeReadinessRows.length), "Python-remap native runtime readiness records host commands, LinuxCNC Python owner source, and configured Python modules without enabling execution or promotion", ], + [ + "python_native_runtime_probe_gate", + flagValue(pythonRuntimeProbeGateOk), + String(pythonRuntimeProbeGateRows.length), + "Python-remap runtime probe gate records python3, linuxcnc, owner source, and representative module-import readiness while rejecting python3-only promotion", + ], [ "python_native_runtime_state_plan", flagValue(pythonRuntimeStatePlanOk), @@ -5020,14 +5590,23 @@ function verifyBoundaryPhaseCompletionSummaryRows(rows) { "native_source_proof_alignment", "runtime_native_alignment_artifacts", "native_runtime_probe_summary", + "runtime_probe_gate_alignment", + "native_runtime_probe_execution_plan", + "native_runtime_probe_pass_evidence_contract", + "runtime_boundary_promotion_readiness", + "runtime_boundary_promotion_blockers", + "runtime_boundary_post_native_pass_gates", + "runtime_boundary_host_preflight", "user_m_transition_contract", "user_m_native_runtime_state_plan", "user_m_native_runtime_readiness", "user_m_native_runtime_probe_gate", "tool_db_transaction_contract", "tool_db_native_runtime_readiness", + "tool_db_native_runtime_probe_gate", "python_runtime_contract", "python_native_runtime_readiness", + "python_native_runtime_probe_gate", "python_native_runtime_state_plan", "python_native_runtime_fixture_plan", "runtime_contract_summary", @@ -5473,12 +6052,622 @@ function verifyBlockedRuntimePromotionLockRows(rows, worklistRows) { return parsedRows; } +function runtimeBoundaryPromotionReadinessRows({ + executionPlanRows, + passEvidenceRows, + promotionLockRows, +}) { + const evidenceByClass = new Map(passEvidenceRows.map((row) => [row.boundary_class, row])); + const lockByTarget = new Map(promotionLockRows.map((row) => [row.target, row])); + + return executionPlanRows.map((plan) => { + const evidence = evidenceByClass.get(plan.boundary_class); + const lock = lockByTarget.get(plan.target); + const nativePassReady = plan.current_probe_status === plan.expected_pass_status; + const nativeEvidenceReady = evidence?.observed_evidence_ready === "1"; + const nodeInventoryGateComplete = false; + const browserSmokeGateComplete = false; + const manualLockUpdateRequired = plan.promotion_requires.includes("manual:promotion_lock_update_required"); + const promotionReady = ( + nativePassReady && + nativeEvidenceReady && + nodeInventoryGateComplete && + browserSmokeGateComplete && + lock?.lock_active === "0" && + !manualLockUpdateRequired + ); + const blockingReason = !nativePassReady + ? "awaiting_native_runtime_probe_pass" + : !nativeEvidenceReady + ? "awaiting_native_pass_evidence" + : lock?.lock_active === "1" + ? "promotion_lock_active_pending_node_browser_manual_update" + : "awaiting_node_browser_promotion_proof"; + + return [ + plan.boundary_class, + plan.boundary_kind, + plan.target, + lock?.blocked ?? "-", + plan.current_probe_status, + plan.expected_pass_status, + flagValue(nativePassReady), + evidence?.evidence_status ?? "-", + flagValue(nativeEvidenceReady), + "0", + "0", + lock?.lock_active ?? "1", + flagValue(manualLockUpdateRequired), + flagValue(promotionReady), + "0", + "0", + blockingReason, + "promotion_readiness_summary_only_no_runtime_execution_no_automatic_promotion", + ].map(tsvValue).join("\t"); + }); +} + +function verifyRuntimeBoundaryPromotionReadinessRows({ + rows, + executionPlanRows, + passEvidenceRows, + promotionLockRows, +}) { + const expectedHeaders = [ + "boundary_class", + "boundary_kind", + "target", + "blocked", + "current_probe_status", + "expected_pass_status", + "native_pass_ready", + "native_evidence_status", + "native_evidence_ready", + "node_inventory_gate_complete", + "browser_smoke_gate_complete", + "promotion_lock_active", + "manual_lock_update_required", + "promotion_ready", + "execution_enabled", + "promotion_allowed", + "blocking_reason", + "notes", + ]; + const parsedRows = parseTsv( + `${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`, + expectedHeaders, + ); + const expected = new Map([ + [ + "L4-USER-M-PROCESS", + { + boundaryKind: "external_user_m_process", + target: "axis/vismach/millturn/example.ngc", + blocked: "L4-USER-M-PROCESS", + expectedPassStatus: "runtime_state_probe_passed", + }, + ], + [ + "L4-TOOL-DB", + { + boundaryKind: "tool_database_process", + target: "axis/db_demo/base.ngc", + blocked: "L4-TOOL-DB", + expectedPassStatus: "runtime_protocol_probe_passed", + }, + ], + [ + "L4-PYTHON-REMAP", + { + boundaryKind: "python_runtime", + target: "axis/remap/stop-lookahead/nc_files", + blocked: "L4-PYTHON-REMAP", + expectedPassStatus: "runtime_lifecycle_probe_passed", + }, + ], + ]); + const planByClass = new Map(executionPlanRows.map((row) => [row.boundary_class, row])); + const evidenceByClass = new Map(passEvidenceRows.map((row) => [row.boundary_class, row])); + const lockByTarget = new Map(promotionLockRows.map((row) => [row.target, row])); + + assert.equal(parsedRows.length, expected.size, "runtime boundary promotion readiness row count drift"); + assert.deepEqual( + parsedRows.map((row) => row.boundary_class), + [...expected.keys()], + "runtime boundary promotion readiness order drift", + ); + + for (const row of parsedRows) { + const expectedRow = expected.get(row.boundary_class); + const plan = planByClass.get(row.boundary_class); + const evidence = evidenceByClass.get(row.boundary_class); + const lock = lockByTarget.get(row.target); + assert.ok(expectedRow, `${row.boundary_class}: unexpected promotion readiness row`); + assert.ok(plan, `${row.boundary_class}: promotion readiness lacks execution plan`); + assert.ok(evidence, `${row.boundary_class}: promotion readiness lacks pass evidence contract`); + assert.ok(lock, `${row.boundary_class}: promotion readiness lacks promotion lock`); + assert.equal(row.boundary_kind, expectedRow.boundaryKind, `${row.boundary_class}: promotion readiness kind drift`); + assert.equal(row.target, expectedRow.target, `${row.boundary_class}: promotion readiness target drift`); + assert.equal(row.blocked, expectedRow.blocked, `${row.boundary_class}: promotion readiness blocked kind drift`); + assert.equal(row.current_probe_status, plan.current_probe_status, `${row.boundary_class}: promotion readiness probe status drift`); + assert.equal(row.expected_pass_status, expectedRow.expectedPassStatus, `${row.boundary_class}: promotion readiness expected status drift`); + assert.equal(row.expected_pass_status, plan.expected_pass_status, `${row.boundary_class}: promotion readiness plan status drift`); + assert.equal(row.native_evidence_status, evidence.evidence_status, `${row.boundary_class}: promotion readiness evidence status drift`); + 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`); + assert.equal(row.node_inventory_gate_complete, "0", `${row.boundary_class}: Node promotion gate must not be complete`); + assert.equal(row.browser_smoke_gate_complete, "0", `${row.boundary_class}: browser promotion gate must not be complete`); + assert.equal(row.execution_enabled, "0", `${row.boundary_class}: promotion readiness must not enable execution`); + assert.equal(row.promotion_allowed, "0", `${row.boundary_class}: promotion readiness must not allow promotion`); + assert.equal(row.promotion_ready, "0", `${row.boundary_class}: promotion readiness must remain false`); + assert.notEqual(row.blocking_reason, "-", `${row.boundary_class}: promotion readiness lacks blocking reason`); + 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`); + } else { + assert.equal(row.native_pass_ready, "0", `${row.boundary_class}: native pass flag drift`); + assert.equal(row.blocking_reason, "awaiting_native_runtime_probe_pass", `${row.boundary_class}: missing native pass blocking reason drift`); + } + assert.ok(row.notes.includes("no_automatic_promotion"), `${row.boundary_class}: promotion readiness note drift`); + } + + return parsedRows; +} + +function runtimeBoundaryPromotionBlockerRows({ + promotionReadinessRows, + executionPlanRows, +}) { + const planByClass = new Map(executionPlanRows.map((row) => [row.boundary_class, row])); + + return promotionReadinessRows.map((row) => { + const plan = planByClass.get(row.boundary_class); + const blockers = []; + if (plan?.missing_requirements && plan.missing_requirements !== "-") { + blockers.push("host_runtime_requirements_missing"); + } + if (row.native_pass_ready !== "1") { + blockers.push("native_runtime_probe_not_passed"); + } + if (row.native_evidence_ready !== "1") { + blockers.push("native_pass_evidence_not_ready"); + } + if (row.node_inventory_gate_complete !== "1") { + blockers.push("node_inventory_gate_not_complete"); + } + if (row.browser_smoke_gate_complete !== "1") { + blockers.push("browser_smoke_gate_not_complete"); + } + if (row.promotion_lock_active === "1") { + blockers.push("promotion_lock_active"); + } + if (row.manual_lock_update_required === "1") { + blockers.push("manual_lock_update_required"); + } + + const nextUnblockAction = plan?.missing_requirements && plan.missing_requirements !== "-" + ? `provide_host_runtime_requirements:${plan.missing_requirements}` + : row.native_pass_ready !== "1" + ? "run_opt_in_native_runtime_probe" + : row.native_evidence_ready !== "1" + ? "collect_native_pass_stdout_evidence" + : "complete_node_browser_promotion_gates_and_manual_lock_update"; + + return [ + row.boundary_class, + row.boundary_kind, + row.target, + row.blocked, + row.promotion_ready, + String(blockers.length), + listValue(blockers), + plan?.missing_requirements ?? "-", + nextUnblockAction, + row.execution_enabled, + row.promotion_allowed, + "promotion_blocker_summary_only_no_runtime_execution_no_automatic_promotion", + ].map(tsvValue).join("\t"); + }); +} + +function verifyRuntimeBoundaryPromotionBlockerRows({ + rows, + promotionReadinessRows, + executionPlanRows, +}) { + const expectedHeaders = [ + "boundary_class", + "boundary_kind", + "target", + "blocked", + "promotion_ready", + "blocker_count", + "blocker_keys", + "missing_runtime_requirements", + "next_unblock_action", + "execution_enabled", + "promotion_allowed", + "notes", + ]; + const parsedRows = parseTsv( + `${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`, + expectedHeaders, + ); + const readinessByClass = new Map(promotionReadinessRows.map((row) => [row.boundary_class, row])); + const planByClass = new Map(executionPlanRows.map((row) => [row.boundary_class, row])); + const expectedClasses = [ + "L4-USER-M-PROCESS", + "L4-TOOL-DB", + "L4-PYTHON-REMAP", + ]; + + assert.equal(parsedRows.length, expectedClasses.length, "runtime boundary promotion blockers row count drift"); + assert.deepEqual( + parsedRows.map((row) => row.boundary_class), + expectedClasses, + "runtime boundary promotion blockers order drift", + ); + + for (const row of parsedRows) { + const readiness = readinessByClass.get(row.boundary_class); + const plan = planByClass.get(row.boundary_class); + assert.ok(readiness, `${row.boundary_class}: promotion blockers lack readiness row`); + assert.ok(plan, `${row.boundary_class}: promotion blockers lack execution plan row`); + assert.equal(row.boundary_kind, readiness.boundary_kind, `${row.boundary_class}: blocker kind drift`); + assert.equal(row.target, readiness.target, `${row.boundary_class}: blocker target drift`); + assert.equal(row.blocked, readiness.blocked, `${row.boundary_class}: blocker blocked kind drift`); + assert.equal(row.promotion_ready, readiness.promotion_ready, `${row.boundary_class}: blocker promotion readiness drift`); + assert.equal(row.execution_enabled, "0", `${row.boundary_class}: blocker summary must not enable execution`); + assert.equal(row.promotion_allowed, "0", `${row.boundary_class}: blocker summary must not allow promotion`); + assert.ok(/^[1-9][0-9]*$/.test(row.blocker_count), `${row.boundary_class}: blocker count must be positive`); + assert.notEqual(row.blocker_keys, "-", `${row.boundary_class}: blocker keys are required while not ready`); + assert.equal(row.promotion_ready, "0", `${row.boundary_class}: runtime boundary must not be promotion-ready`); + if (readiness.native_pass_ready !== "1") { + assert.ok(row.blocker_keys.includes("native_runtime_probe_not_passed"), `${row.boundary_class}: native pass blocker missing`); + } + if (readiness.native_evidence_ready !== "1") { + assert.ok(row.blocker_keys.includes("native_pass_evidence_not_ready"), `${row.boundary_class}: native evidence blocker missing`); + } + assert.ok(row.blocker_keys.includes("node_inventory_gate_not_complete"), `${row.boundary_class}: Node gate blocker missing`); + assert.ok(row.blocker_keys.includes("browser_smoke_gate_not_complete"), `${row.boundary_class}: browser gate blocker missing`); + assert.ok(row.blocker_keys.includes("promotion_lock_active"), `${row.boundary_class}: promotion lock blocker missing`); + assert.ok(row.blocker_keys.includes("manual_lock_update_required"), `${row.boundary_class}: manual lock blocker missing`); + if (plan.missing_requirements !== "-") { + assert.equal(row.missing_runtime_requirements, plan.missing_requirements, `${row.boundary_class}: missing requirements drift`); + assert.ok(row.blocker_keys.includes("host_runtime_requirements_missing"), `${row.boundary_class}: host runtime blocker missing`); + assert.ok(row.next_unblock_action.includes(plan.missing_requirements), `${row.boundary_class}: unblock action must name missing requirements`); + } + assert.notEqual(row.next_unblock_action, "-", `${row.boundary_class}: blocker summary lacks next unblock action`); + assert.ok(row.notes.includes("no_automatic_promotion"), `${row.boundary_class}: blocker summary notes drift`); + } + + return parsedRows; +} + +function runtimeBoundaryPostNativePassGateRows({ + promotionReadinessRows, + recommendationRows, +}) { + const recommendationByKind = new Map(recommendationRows.map((row) => [row.boundary_kind, row])); + + return promotionReadinessRows.map((row) => { + const recommendation = recommendationByKind.get(row.boundary_kind); + const nodeGateStatus = row.native_evidence_ready === "1" + ? "pending_node_inventory_promotion_gate" + : "blocked_until_native_pass_evidence"; + const browserGateStatus = row.native_evidence_ready === "1" + ? "blocked_until_node_gate_complete" + : "blocked_until_node_gate_complete"; + const gateStatus = row.native_evidence_ready === "1" + ? "waiting_for_node_browser_manual_promotion" + : "blocked_before_native_pass"; + + return [ + row.boundary_class, + row.boundary_kind, + row.target, + row.blocked, + row.current_probe_status, + row.expected_pass_status, + row.native_pass_ready, + row.native_evidence_ready, + recommendation?.required_node_proof ?? "-", + recommendation?.required_browser_proof ?? "-", + nodeGateStatus, + browserGateStatus, + row.manual_lock_update_required, + row.promotion_lock_active, + row.execution_enabled, + row.promotion_allowed, + gateStatus, + "post_native_pass_gate_summary_only_no_runtime_execution_no_automatic_promotion", + ].map(tsvValue).join("\t"); + }); +} + +function verifyRuntimeBoundaryPostNativePassGateRows({ + rows, + promotionReadinessRows, + recommendationRows, +}) { + const expectedHeaders = [ + "boundary_class", + "boundary_kind", + "target", + "blocked", + "current_probe_status", + "expected_pass_status", + "native_pass_ready", + "native_evidence_ready", + "required_node_proof", + "required_browser_proof", + "node_gate_status", + "browser_gate_status", + "manual_lock_update_required", + "promotion_lock_active", + "execution_enabled", + "promotion_allowed", + "gate_status", + "notes", + ]; + const parsedRows = parseTsv( + `${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`, + expectedHeaders, + ); + const readinessByClass = new Map(promotionReadinessRows.map((row) => [row.boundary_class, row])); + const recommendationByKind = new Map(recommendationRows.map((row) => [row.boundary_kind, row])); + const expectedClasses = [ + "L4-USER-M-PROCESS", + "L4-TOOL-DB", + "L4-PYTHON-REMAP", + ]; + + assert.equal(parsedRows.length, expectedClasses.length, "post-native-pass gate row count drift"); + assert.deepEqual( + parsedRows.map((row) => row.boundary_class), + expectedClasses, + "post-native-pass gate order drift", + ); + + for (const row of parsedRows) { + const readiness = readinessByClass.get(row.boundary_class); + const recommendation = recommendationByKind.get(row.boundary_kind); + assert.ok(readiness, `${row.boundary_class}: post-native-pass gate lacks readiness row`); + assert.ok(recommendation, `${row.boundary_class}: post-native-pass gate lacks recommendation row`); + assert.equal(row.boundary_kind, readiness.boundary_kind, `${row.boundary_class}: post-native-pass kind drift`); + assert.equal(row.target, readiness.target, `${row.boundary_class}: post-native-pass target drift`); + assert.equal(row.blocked, readiness.blocked, `${row.boundary_class}: post-native-pass blocked drift`); + assert.equal(row.current_probe_status, readiness.current_probe_status, `${row.boundary_class}: post-native-pass probe status drift`); + assert.equal(row.expected_pass_status, readiness.expected_pass_status, `${row.boundary_class}: post-native-pass expected status drift`); + assert.equal(row.native_pass_ready, readiness.native_pass_ready, `${row.boundary_class}: post-native-pass native pass drift`); + assert.equal(row.native_evidence_ready, readiness.native_evidence_ready, `${row.boundary_class}: post-native-pass native evidence drift`); + assert.equal(row.required_node_proof, recommendation.required_node_proof, `${row.boundary_class}: post-native-pass Node proof drift`); + assert.equal(row.required_browser_proof, recommendation.required_browser_proof, `${row.boundary_class}: post-native-pass browser proof drift`); + assert.notEqual(row.required_node_proof, "-", `${row.boundary_class}: post-native-pass gate lacks Node proof`); + assert.notEqual(row.required_browser_proof, "-", `${row.boundary_class}: post-native-pass gate lacks browser proof`); + assert.equal(row.manual_lock_update_required, "1", `${row.boundary_class}: post-native-pass manual lock update must remain required`); + assert.equal(row.promotion_lock_active, "1", `${row.boundary_class}: post-native-pass promotion lock must remain active`); + assert.equal(row.execution_enabled, "0", `${row.boundary_class}: post-native-pass gate must not enable execution`); + assert.equal(row.promotion_allowed, "0", `${row.boundary_class}: post-native-pass gate must not allow promotion`); + if (row.native_evidence_ready === "1") { + assert.equal(row.node_gate_status, "pending_node_inventory_promotion_gate", `${row.boundary_class}: post-native-pass Node gate status drift`); + assert.equal(row.browser_gate_status, "blocked_until_node_gate_complete", `${row.boundary_class}: post-native-pass browser gate status drift`); + assert.equal(row.gate_status, "waiting_for_node_browser_manual_promotion", `${row.boundary_class}: post-native-pass status drift`); + } else { + assert.equal(row.node_gate_status, "blocked_until_native_pass_evidence", `${row.boundary_class}: post-native-pass Node gate must wait for native evidence`); + assert.equal(row.browser_gate_status, "blocked_until_node_gate_complete", `${row.boundary_class}: post-native-pass browser gate must wait for Node gate`); + assert.equal(row.gate_status, "blocked_before_native_pass", `${row.boundary_class}: post-native-pass status drift`); + } + assert.ok(row.notes.includes("no_automatic_promotion"), `${row.boundary_class}: post-native-pass note drift`); + } + + return parsedRows; +} + +function runtimeBoundaryHostPreflightRows({ + userMRuntimeProbeGateRows, + toolDbRuntimeProbeGateRows, + pythonRuntimeProbeGateRows, + executionPlanRows, +}) { + const planByClass = new Map(executionPlanRows.map((row) => [row.boundary_class, row])); + const userMProbeGate = userMRuntimeProbeGateRows[0]; + const toolDbProbeGate = toolDbRuntimeProbeGateRows[0]; + const pythonProbeGate = pythonRuntimeProbeGateRows[0]; + const rows = [ + { + boundaryClass: "L4-USER-M-PROCESS", + boundaryKind: "external_user_m_process", + target: userMProbeGate?.path ?? "axis/vismach/millturn/example.ngc", + blocked: userMProbeGate?.blocked ?? "L4-USER-M-PROCESS", + requiredRuntime: userMProbeGate?.required_runtime ?? "-", + runtimeRequirements: userMProbeGate?.runtime_requirements ?? "-", + missingRequirements: userMProbeGate?.missing_requirements ?? "-", + runtimeReady: userMProbeGate?.runtime_ready ?? "0", + sourceProofReady: userMProbeGate?.source_proof_ready ?? "0", + requiredNativeProof: userMProbeGate?.required_native_proof ?? "-", + }, + { + boundaryClass: "L4-TOOL-DB", + boundaryKind: "tool_database_process", + target: toolDbProbeGate?.path ?? "axis/db_demo/base.ngc", + blocked: toolDbProbeGate?.blocked ?? "L4-TOOL-DB", + requiredRuntime: toolDbProbeGate?.required_runtime ?? "-", + runtimeRequirements: toolDbProbeGate?.runtime_requirements ?? "-", + missingRequirements: toolDbProbeGate?.missing_requirements ?? "-", + runtimeReady: toolDbProbeGate?.runtime_ready ?? "0", + sourceProofReady: toolDbProbeGate?.source_proof_ready ?? "0", + requiredNativeProof: toolDbProbeGate?.required_native_proof ?? "-", + }, + { + boundaryClass: "L4-PYTHON-REMAP", + boundaryKind: "python_runtime", + target: pythonProbeGate?.family ?? "axis/remap/stop-lookahead/nc_files", + blocked: pythonProbeGate?.blocked ?? "L4-PYTHON-REMAP", + requiredRuntime: pythonProbeGate?.required_runtime ?? "-", + runtimeRequirements: pythonProbeGate?.runtime_requirements ?? "-", + missingRequirements: pythonProbeGate?.missing_requirements ?? "-", + runtimeReady: pythonProbeGate?.runtime_ready ?? "0", + sourceProofReady: pythonProbeGate?.source_proof_ready ?? "0", + requiredNativeProof: pythonProbeGate?.required_native_proof ?? "-", + }, + ]; + + return rows.map((row) => { + const plan = planByClass.get(row.boundaryClass); + const preflightStatus = row.sourceProofReady !== "1" + ? "blocked_missing_source_proof" + : row.runtimeReady === "1" + ? "ready_to_run_opt_in_native_probe" + : "blocked_missing_host_runtime"; + + return [ + row.boundaryClass, + row.boundaryKind, + row.target, + row.blocked, + row.requiredRuntime, + row.runtimeRequirements, + row.missingRequirements, + row.runtimeReady, + row.sourceProofReady, + row.requiredNativeProof, + plan?.opt_in_env ?? "-", + plan?.execution_command ?? "-", + plan?.current_probe_status ?? "-", + preflightStatus, + "0", + "0", + "host_preflight_summary_only_no_runtime_execution_no_automatic_promotion", + ].map(tsvValue).join("\t"); + }); +} + +function verifyRuntimeBoundaryHostPreflightRows({ + rows, + userMRuntimeProbeGateRows, + toolDbRuntimeProbeGateRows, + pythonRuntimeProbeGateRows, + executionPlanRows, +}) { + const expectedHeaders = [ + "boundary_class", + "boundary_kind", + "target", + "blocked", + "required_runtime", + "runtime_requirements", + "missing_requirements", + "runtime_ready", + "source_proof_ready", + "required_native_proof", + "opt_in_env", + "execution_command", + "current_probe_status", + "preflight_status", + "execution_enabled", + "promotion_allowed", + "notes", + ]; + const parsedRows = parseTsv( + `${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`, + expectedHeaders, + ); + const expected = new Map([ + [ + "L4-USER-M-PROCESS", + { + boundaryKind: "external_user_m_process", + target: "axis/vismach/millturn/example.ngc", + blocked: "L4-USER-M-PROCESS", + requiredRuntime: "linuxcnc_task_hal_tcl_user_m_process", + requiredNativeProof: "native_runtime_state_probe_required", + optInEnv: "ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1", + gateRows: userMRuntimeProbeGateRows, + }, + ], + [ + "L4-TOOL-DB", + { + boundaryKind: "tool_database_process", + target: "axis/db_demo/base.ngc", + blocked: "L4-TOOL-DB", + requiredRuntime: "linuxcnc_tooldata_db_process", + requiredNativeProof: "native_db_process_protocol_probe_required", + optInEnv: "ENABLE_TOOL_DB_RUNTIME_PROBE=1", + gateRows: toolDbRuntimeProbeGateRows, + }, + ], + [ + "L4-PYTHON-REMAP", + { + boundaryKind: "python_runtime", + target: "axis/remap/stop-lookahead/nc_files", + blocked: "L4-PYTHON-REMAP", + requiredRuntime: "linuxcnc_python_remap_runtime", + requiredNativeProof: "linuxcnc_python_runtime_lifecycle_probe_required", + optInEnv: "ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1", + gateRows: pythonRuntimeProbeGateRows, + }, + ], + ]); + const planByClass = new Map(executionPlanRows.map((row) => [row.boundary_class, row])); + + assert.equal(parsedRows.length, expected.size, "host preflight row count drift"); + assert.deepEqual( + parsedRows.map((row) => row.boundary_class), + [...expected.keys()], + "host preflight order drift", + ); + + for (const row of parsedRows) { + const expectedRow = expected.get(row.boundary_class); + const plan = planByClass.get(row.boundary_class); + assert.ok(expectedRow, `${row.boundary_class}: unexpected host preflight row`); + assert.ok(plan, `${row.boundary_class}: host preflight lacks execution plan`); + assert.equal(row.boundary_kind, expectedRow.boundaryKind, `${row.boundary_class}: host preflight kind drift`); + assert.equal(row.target, expectedRow.target, `${row.boundary_class}: host preflight target drift`); + assert.equal(row.blocked, expectedRow.blocked, `${row.boundary_class}: host preflight blocked drift`); + assert.equal(row.required_runtime, expectedRow.requiredRuntime, `${row.boundary_class}: host preflight runtime drift`); + assert.equal(row.required_native_proof, expectedRow.requiredNativeProof, `${row.boundary_class}: host preflight native proof drift`); + assert.equal(row.opt_in_env, expectedRow.optInEnv, `${row.boundary_class}: host preflight opt-in drift`); + assert.equal(row.execution_command, plan.execution_command, `${row.boundary_class}: host preflight command drift`); + assert.equal(row.current_probe_status, plan.current_probe_status, `${row.boundary_class}: host preflight probe status drift`); + assert.equal(row.runtime_ready, plan.runtime_ready, `${row.boundary_class}: host preflight runtime readiness drift`); + assert.equal(row.source_proof_ready, plan.source_proof_ready, `${row.boundary_class}: host preflight source readiness drift`); + assert.equal(row.missing_requirements, plan.missing_requirements, `${row.boundary_class}: host preflight missing requirement drift`); + assert.notEqual(row.runtime_requirements, "-", `${row.boundary_class}: host preflight lacks runtime requirements`); + for (const gateRow of expectedRow.gateRows) { + assert.equal(row.runtime_requirements, gateRow.runtime_requirements, `${row.boundary_class}: host preflight gate requirement drift`); + assert.equal(row.missing_requirements, gateRow.missing_requirements, `${row.boundary_class}: host preflight gate missing requirement drift`); + assert.equal(row.runtime_ready, gateRow.runtime_ready, `${row.boundary_class}: host preflight gate runtime readiness drift`); + assert.equal(row.source_proof_ready, gateRow.source_proof_ready, `${row.boundary_class}: host preflight gate source readiness drift`); + } + assert.equal(row.execution_enabled, "0", `${row.boundary_class}: host preflight must not enable execution`); + assert.equal(row.promotion_allowed, "0", `${row.boundary_class}: host preflight must not allow promotion`); + if (row.source_proof_ready !== "1") { + assert.equal(row.preflight_status, "blocked_missing_source_proof", `${row.boundary_class}: source-blocked preflight drift`); + } else if (row.runtime_ready === "1") { + assert.equal(row.missing_requirements, "-", `${row.boundary_class}: ready preflight must not list missing requirements`); + assert.equal(row.preflight_status, "ready_to_run_opt_in_native_probe", `${row.boundary_class}: ready preflight status drift`); + } else { + assert.notEqual(row.missing_requirements, "-", `${row.boundary_class}: blocked preflight lacks missing requirements`); + assert.equal(row.preflight_status, "blocked_missing_host_runtime", `${row.boundary_class}: blocked preflight status drift`); + } + assert.ok(row.notes.includes("no_automatic_promotion"), `${row.boundary_class}: host preflight note drift`); + } + + return parsedRows; +} + function nextBoundaryRecommendationRows({ worklistRows, promotionLockRows, runtimeContractRows, userMRuntimeProbeGateRows, toolDbTransactionRows, + toolDbRuntimeProbeGateRows, pythonFixturePlanRows, }) { const lockByKind = new Map(promotionLockRows.map((row) => [row.boundary_kind, row])); @@ -5521,8 +6710,8 @@ function nextBoundaryRecommendationRows({ toolDbWork?.target ?? "-", toolDbWork?.blocked ?? "L4-TOOL-DB", "implement_tooldata_db_protocol_probe", - "tool-db-process-transaction-plan.tsv", - String(toolDbTransactionRows.length), + "tool-db-process-native-runtime-probe-gate.tsv", + String(toolDbRuntimeProbeGateRows.length), "native_db_process_protocol_probe_required", toolDbWork?.required_node_proof ?? "linuxcnc_tooldata_db_protocol_not_tbl_fallback", toolDbWork?.required_browser_proof ?? "browser_after_node_protocol_proof_opfs_persistence_only", @@ -5530,8 +6719,8 @@ function nextBoundaryRecommendationRows({ contractByKind.get("tool_database_process")?.contract_ok ?? "0", "0", "0", - "prove DB_PROGRAM v2.1/get/load/put/unload protocol before replacing the blocked row", - "next-boundary-worklist.tsv,blocked-runtime-promotion-lock.tsv,tool-db-process-transaction-plan.tsv,tool-db-process-native-runtime-readiness.tsv", + "prove DB_PROGRAM v2.1/get/load/put/unload protocol after host runtime gate is ready; reject tbl fallback promotion", + "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", ], [ "3", @@ -5549,7 +6738,7 @@ function nextBoundaryRecommendationRows({ "0", "0", "start with stop-lookahead fixture; keep all Python-remap families inventory-only until runtime proof exists", - "next-boundary-worklist.tsv,blocked-runtime-promotion-lock.tsv,python-remap-runtime-contract.tsv,python-remap-native-runtime-fixture-plan.tsv", + "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")); } @@ -5612,9 +6801,10 @@ function verifyNextBoundaryRecommendationRows(rows) { 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.equal(row.primary_artifact, "tool-db-process-transaction-plan.tsv", "tool DB artifact 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"); } 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"); @@ -5785,12 +6975,15 @@ function nativeRuntimeProbeSummaryRows() { "boundary_kind", "target", "runtime_probe", + "required_native_proof", + "opt_in_env", "runtime_ready", "source_proof_ready", "probe_status", "execution_enabled", "promotion_allowed", "missing_requirements", + "probe_note", "stdout_log", ]; assert.ok( @@ -5808,6 +7001,8 @@ function nativeRuntimeProbeSummaryRows() { boundaryKind: "external_user_m_process", target: "axis/vismach/millturn/example.ngc", runtimeProbe: "linuxcnc_millturn_user_m_runtime_probe", + requiredNativeProof: "native_runtime_state_probe_required", + optInEnv: "ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1", }, ], [ @@ -5816,6 +7011,8 @@ function nativeRuntimeProbeSummaryRows() { boundaryKind: "tool_database_process", target: "axis/db_demo/base.ngc", runtimeProbe: "linuxcnc_tool_db_runtime_probe", + requiredNativeProof: "native_db_process_protocol_probe_required", + optInEnv: "ENABLE_TOOL_DB_RUNTIME_PROBE=1", }, ], [ @@ -5824,6 +7021,8 @@ function nativeRuntimeProbeSummaryRows() { boundaryKind: "python_runtime", target: "axis/remap/stop-lookahead/nc_files", runtimeProbe: "linuxcnc_python_remap_runtime_probe", + requiredNativeProof: "linuxcnc_python_runtime_lifecycle_probe_required", + optInEnv: "ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1", }, ], ]); @@ -5842,6 +7041,8 @@ function nativeRuntimeProbeSummaryRows() { assert.equal(row.boundary_kind, expected.boundaryKind, `${boundaryClass}: runtime probe boundary kind drift`); assert.equal(row.target, expected.target, `${boundaryClass}: runtime probe target drift`); assert.equal(row.runtime_probe, expected.runtimeProbe, `${boundaryClass}: runtime probe name drift`); + assert.equal(row.required_native_proof, expected.requiredNativeProof, `${boundaryClass}: runtime probe native proof drift`); + assert.equal(row.opt_in_env, expected.optInEnv, `${boundaryClass}: runtime probe opt-in env drift`); assert.ok(["0", "1"].includes(row.runtime_ready), `${boundaryClass}: invalid runtime_ready`); assert.equal(row.source_proof_ready, "1", `${boundaryClass}: source proof must be ready before runtime probe`); assert.ok( @@ -5858,9 +7059,20 @@ function nativeRuntimeProbeSummaryRows() { if (row.runtime_ready === "0") { assert.equal(row.probe_status, "skipped_missing_host_runtime", `${boundaryClass}: non-ready runtime must skip`); assert.notEqual(row.missing_requirements, "-", `${boundaryClass}: non-ready runtime must record missing requirements`); + assert.ok(row.probe_note.includes("missing_host_runtime"), `${boundaryClass}: missing-runtime note drift`); } else { assert.equal(row.missing_requirements, "-", `${boundaryClass}: ready runtime must not record missing requirements`); } + if (row.probe_status === "ready_disabled_by_default") { + assert.ok( + row.probe_note.includes(`set_${row.opt_in_env.split("=")[0]}`), + `${boundaryClass}: ready-disabled opt-in note drift`, + ); + } + if (row.probe_status.startsWith("runtime_") && row.probe_status.endsWith("_passed")) { + assert.ok(row.probe_note.includes("without_promotion"), `${boundaryClass}: passed probe note drift`); + } + assert.notEqual(row.probe_note, "-", `${boundaryClass}: runtime probe note is required`); assert.equal(row.execution_enabled, "0", `${boundaryClass}: native runtime probe must not enable execution`); assert.equal(row.promotion_allowed, "0", `${boundaryClass}: native runtime probe must not allow promotion`); assert.notEqual(row.stdout_log, "-", `${boundaryClass}: runtime probe must reference stdout log`); @@ -5879,12 +7091,15 @@ function verifyNativeRuntimeProbeSummaryRows(rows) { "boundary_kind", "target", "runtime_probe", + "required_native_proof", + "opt_in_env", "runtime_ready", "source_proof_ready", "probe_status", "execution_enabled", "promotion_allowed", "missing_requirements", + "probe_note", "stdout_log", ]; return parseTsv( @@ -5893,6 +7108,571 @@ function verifyNativeRuntimeProbeSummaryRows(rows) { ); } +function runtimeProbeGateAlignmentRows({ + nativeRuntimeProbeRows, + userMRuntimeProbeGateRows, + toolDbRuntimeProbeGateRows, + pythonRuntimeProbeGateRows, +}) { + const nativeByClass = new Map( + nativeRuntimeProbeRows.map((row) => [row.boundary_class, row]), + ); + const groups = [ + { + boundaryClass: "L4-USER-M-PROCESS", + boundaryKind: "external_user_m_process", + target: "axis/vismach/millturn/example.ngc", + gateArtifact: "user-m-process-native-runtime-probe-gate.tsv", + gateRow: userMRuntimeProbeGateRows[0], + }, + { + boundaryClass: "L4-TOOL-DB", + boundaryKind: "tool_database_process", + target: "axis/db_demo/base.ngc", + gateArtifact: "tool-db-process-native-runtime-probe-gate.tsv", + gateRow: toolDbRuntimeProbeGateRows[0], + }, + { + boundaryClass: "L4-PYTHON-REMAP", + boundaryKind: "python_runtime", + target: "axis/remap/stop-lookahead/nc_files", + gateArtifact: "python-remap-native-runtime-probe-gate.tsv", + gateRow: pythonRuntimeProbeGateRows[0], + }, + ]; + + return groups.map((group) => { + const nativeRow = nativeByClass.get(group.boundaryClass); + const gateRow = group.gateRow; + const gateMissing = uniqueSorted(splitSummaryList(gateRow?.missing_requirements ?? "-")); + const nativeMissing = uniqueSorted(splitSummaryList(nativeRow?.missing_requirements ?? "-")); + const runtimeReadyMatch = gateRow?.runtime_ready === nativeRow?.runtime_ready; + const sourceProofReadyMatch = gateRow?.source_proof_ready === nativeRow?.source_proof_ready; + const missingRequirementsMatch = gateMissing.join(",") === nativeMissing.join(","); + const requiredNativeProofMatch = gateRow?.required_native_proof === nativeRow?.required_native_proof; + const executionDisabledMatch = ( + gateRow?.execution_enabled === "0" && + nativeRow?.execution_enabled === "0" + ); + const promotionDisabledMatch = ( + gateRow?.promotion_allowed === "0" && + nativeRow?.promotion_allowed === "0" + ); + const statusCompatible = ( + (gateRow?.runtime_ready === "0" && + gateRow?.gate_status === "blocked_missing_host_runtime" && + nativeRow?.probe_status === "skipped_missing_host_runtime") || + (gateRow?.runtime_ready === "1" && + gateRow?.gate_status?.startsWith("ready_to_implement_") && + nativeRow?.probe_status === "ready_disabled_by_default") || + (gateRow?.runtime_ready === "1" && + gateRow?.gate_status?.startsWith("ready_to_implement_") && + nativeRow?.probe_status?.startsWith("runtime_") && + nativeRow?.probe_status?.endsWith("_passed")) + ); + const alignmentOk = ( + nativeRow && + gateRow && + nativeRow.boundary_kind === group.boundaryKind && + nativeRow.target === group.target && + runtimeReadyMatch && + sourceProofReadyMatch && + requiredNativeProofMatch && + missingRequirementsMatch && + executionDisabledMatch && + promotionDisabledMatch && + statusCompatible + ); + + return [ + group.boundaryClass, + group.boundaryKind, + group.target, + group.gateArtifact, + nativeRow?.runtime_probe ?? "-", + gateRow?.required_native_proof ?? "-", + nativeRow?.required_native_proof ?? "-", + nativeRow?.opt_in_env ?? "-", + gateRow?.runtime_ready ?? "-", + nativeRow?.runtime_ready ?? "-", + gateRow?.source_proof_ready ?? "-", + nativeRow?.source_proof_ready ?? "-", + gateRow?.gate_status ?? "-", + nativeRow?.probe_status ?? "-", + listValue(gateMissing), + listValue(nativeMissing), + flagValue(requiredNativeProofMatch), + flagValue(missingRequirementsMatch), + flagValue(executionDisabledMatch), + flagValue(promotionDisabledMatch), + flagValue(statusCompatible), + flagValue(alignmentOk), + "runtime_probe_gate_and_native_summary_must_agree_before_promotion", + ].map(tsvValue).join("\t"); + }); +} + +function verifyRuntimeProbeGateAlignmentRows(rows) { + const expectedHeaders = [ + "boundary_class", + "boundary_kind", + "target", + "gate_artifact", + "native_runtime_probe", + "gate_required_native_proof", + "native_required_native_proof", + "native_opt_in_env", + "gate_runtime_ready", + "native_runtime_ready", + "gate_source_proof_ready", + "native_source_proof_ready", + "gate_status", + "native_probe_status", + "gate_missing_requirements", + "native_missing_requirements", + "required_native_proof_match", + "missing_requirements_match", + "execution_disabled_match", + "promotion_disabled_match", + "status_compatible", + "alignment_ok", + "notes", + ]; + const parsedRows = parseTsv( + `${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`, + expectedHeaders, + ); + const expected = [ + ["L4-USER-M-PROCESS", "external_user_m_process", "axis/vismach/millturn/example.ngc"], + ["L4-TOOL-DB", "tool_database_process", "axis/db_demo/base.ngc"], + ["L4-PYTHON-REMAP", "python_runtime", "axis/remap/stop-lookahead/nc_files"], + ]; + + assert.equal(parsedRows.length, expected.length, "runtime probe gate alignment row count drift"); + assert.deepEqual( + parsedRows.map((row) => [row.boundary_class, row.boundary_kind, row.target]), + expected, + "runtime probe gate alignment identity drift", + ); + + for (const row of parsedRows) { + assert.ok(row.gate_artifact.endsWith(".tsv"), `${row.target}: missing gate artifact`); + assert.notEqual(row.native_runtime_probe, "-", `${row.target}: missing native runtime probe`); + assert.equal(row.required_native_proof_match, "1", `${row.target}: required native proof drift`); + assert.notEqual(row.native_required_native_proof, "-", `${row.target}: missing native proof requirement`); + assert.match(row.native_opt_in_env, /^ENABLE_[A-Z0-9_]+=1$/, `${row.target}: invalid native opt-in env`); + assert.equal(row.gate_runtime_ready, row.native_runtime_ready, `${row.target}: runtime readiness drift`); + assert.equal(row.gate_source_proof_ready, "1", `${row.target}: gate source proof must be ready`); + assert.equal(row.native_source_proof_ready, "1", `${row.target}: native source proof must be ready`); + assert.equal(row.missing_requirements_match, "1", `${row.target}: missing requirements drift`); + assert.equal(row.execution_disabled_match, "1", `${row.target}: execution disabled drift`); + assert.equal(row.promotion_disabled_match, "1", `${row.target}: promotion disabled drift`); + assert.equal(row.status_compatible, "1", `${row.target}: native/gate status incompatibility`); + assert.equal(row.alignment_ok, "1", `${row.target}: runtime probe gate alignment failed`); + assert.ok(row.notes.includes("before_promotion"), `${row.target}: alignment notes drift`); + } + + return parsedRows; +} + +function nativeRuntimeProbeExecutionPlanRows({ + nativeRuntimeProbeRows, + runtimeProbeGateAlignmentRows, +}) { + const alignmentByClass = new Map( + runtimeProbeGateAlignmentRows.map((row) => [row.boundary_class, row]), + ); + const scriptByProbe = new Map([ + [ + "linuxcnc_millturn_user_m_runtime_probe", + "wasm-port/tests/native/probe_millturn_user_m_runtime.sh", + ], + [ + "linuxcnc_tool_db_runtime_probe", + "wasm-port/tests/native/probe_tool_db_runtime.sh", + ], + [ + "linuxcnc_python_remap_runtime_probe", + "wasm-port/tests/native/probe_python_remap_runtime.sh", + ], + ]); + const expectedPassStatusByClass = new Map([ + ["L4-USER-M-PROCESS", "runtime_state_probe_passed"], + ["L4-TOOL-DB", "runtime_protocol_probe_passed"], + ["L4-PYTHON-REMAP", "runtime_lifecycle_probe_passed"], + ]); + + return nativeRuntimeProbeRows.map((row) => { + const alignment = alignmentByClass.get(row.boundary_class); + const script = scriptByProbe.get(row.runtime_probe) ?? "-"; + const expectedPassStatus = expectedPassStatusByClass.get(row.boundary_class) ?? "-"; + const optInName = row.opt_in_env.split("=")[0] ?? row.opt_in_env; + const planStatus = row.probe_status === expectedPassStatus + ? "native_probe_passed_waiting_for_node_browser_promotion_proof" + : row.runtime_ready === "1" + ? "ready_to_run_opt_in_probe" + : "blocked_missing_host_runtime"; + + return [ + row.boundary_class, + row.boundary_kind, + row.target, + row.runtime_probe, + row.required_native_proof, + row.opt_in_env, + `${row.opt_in_env} bash ${script}`, + row.runtime_ready, + row.source_proof_ready, + row.probe_status, + expectedPassStatus, + row.missing_requirements, + alignment?.alignment_ok ?? "0", + listValue([ + `native:${expectedPassStatus}`, + "node:inventory_gate_alignment_after_native_pass", + "browser:smoke_gate_alignment_after_node_pass", + "manual:promotion_lock_update_required", + ]), + "0", + "0", + planStatus, + `set_${optInName}_only_after_runtime_ready_no_automatic_promotion`, + ].map(tsvValue).join("\t"); + }); +} + +function verifyNativeRuntimeProbeExecutionPlanRows({ + rows, + nativeRuntimeProbeRows, + runtimeProbeGateAlignmentRows, +}) { + const expectedHeaders = [ + "boundary_class", + "boundary_kind", + "target", + "runtime_probe", + "required_native_proof", + "opt_in_env", + "execution_command", + "runtime_ready", + "source_proof_ready", + "current_probe_status", + "expected_pass_status", + "missing_requirements", + "gate_alignment_ok", + "promotion_requires", + "execution_enabled", + "promotion_allowed", + "plan_status", + "notes", + ]; + const parsedRows = parseTsv( + `${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`, + expectedHeaders, + ); + const expected = new Map([ + [ + "L4-USER-M-PROCESS", + { + boundaryKind: "external_user_m_process", + target: "axis/vismach/millturn/example.ngc", + runtimeProbe: "linuxcnc_millturn_user_m_runtime_probe", + requiredNativeProof: "native_runtime_state_probe_required", + optInEnv: "ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1", + script: "wasm-port/tests/native/probe_millturn_user_m_runtime.sh", + expectedPassStatus: "runtime_state_probe_passed", + }, + ], + [ + "L4-TOOL-DB", + { + boundaryKind: "tool_database_process", + target: "axis/db_demo/base.ngc", + runtimeProbe: "linuxcnc_tool_db_runtime_probe", + requiredNativeProof: "native_db_process_protocol_probe_required", + optInEnv: "ENABLE_TOOL_DB_RUNTIME_PROBE=1", + script: "wasm-port/tests/native/probe_tool_db_runtime.sh", + expectedPassStatus: "runtime_protocol_probe_passed", + }, + ], + [ + "L4-PYTHON-REMAP", + { + boundaryKind: "python_runtime", + target: "axis/remap/stop-lookahead/nc_files", + runtimeProbe: "linuxcnc_python_remap_runtime_probe", + requiredNativeProof: "linuxcnc_python_runtime_lifecycle_probe_required", + optInEnv: "ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1", + script: "wasm-port/tests/native/probe_python_remap_runtime.sh", + expectedPassStatus: "runtime_lifecycle_probe_passed", + }, + ], + ]); + const nativeByClass = new Map(nativeRuntimeProbeRows.map((row) => [row.boundary_class, row])); + const alignmentByClass = new Map(runtimeProbeGateAlignmentRows.map((row) => [row.boundary_class, row])); + + assert.equal(parsedRows.length, expected.size, "native runtime probe execution plan row count drift"); + assert.deepEqual( + parsedRows.map((row) => row.boundary_class), + [...expected.keys()], + "native runtime probe execution plan order drift", + ); + + for (const row of parsedRows) { + const expectedRow = expected.get(row.boundary_class); + const nativeRow = nativeByClass.get(row.boundary_class); + const alignmentRow = alignmentByClass.get(row.boundary_class); + + assert.ok(expectedRow, `${row.boundary_class}: unexpected execution plan row`); + assert.ok(nativeRow, `${row.boundary_class}: execution plan lacks native summary row`); + assert.ok(alignmentRow, `${row.boundary_class}: execution plan lacks gate alignment row`); + assert.equal(row.boundary_kind, expectedRow.boundaryKind, `${row.boundary_class}: execution plan kind drift`); + assert.equal(row.target, expectedRow.target, `${row.boundary_class}: execution plan target drift`); + assert.equal(row.runtime_probe, expectedRow.runtimeProbe, `${row.boundary_class}: execution plan probe drift`); + assert.equal(row.required_native_proof, expectedRow.requiredNativeProof, `${row.boundary_class}: execution plan proof drift`); + assert.equal(row.opt_in_env, expectedRow.optInEnv, `${row.boundary_class}: execution plan opt-in drift`); + assert.equal(row.expected_pass_status, expectedRow.expectedPassStatus, `${row.boundary_class}: expected pass status drift`); + assert.equal(row.execution_command, `${expectedRow.optInEnv} bash ${expectedRow.script}`, `${row.boundary_class}: execution command drift`); + assert.equal(row.runtime_ready, nativeRow.runtime_ready, `${row.boundary_class}: runtime readiness drift`); + assert.equal(row.source_proof_ready, nativeRow.source_proof_ready, `${row.boundary_class}: source proof readiness drift`); + assert.equal(row.current_probe_status, nativeRow.probe_status, `${row.boundary_class}: current probe status drift`); + assert.equal(row.missing_requirements, nativeRow.missing_requirements, `${row.boundary_class}: missing requirements drift`); + assert.equal(row.gate_alignment_ok, alignmentRow.alignment_ok, `${row.boundary_class}: gate alignment drift`); + assert.equal(row.gate_alignment_ok, "1", `${row.boundary_class}: gate alignment must be ready before opt-in probe`); + assert.equal(row.execution_enabled, "0", `${row.boundary_class}: execution plan must not enable execution`); + assert.equal(row.promotion_allowed, "0", `${row.boundary_class}: execution plan must not allow promotion`); + for (const required of [ + `native:${expectedRow.expectedPassStatus}`, + "node:inventory_gate_alignment_after_native_pass", + "browser:smoke_gate_alignment_after_node_pass", + "manual:promotion_lock_update_required", + ]) { + assert.ok(row.promotion_requires.includes(required), `${row.boundary_class}: promotion requirement ${required} missing`); + } + if (row.current_probe_status === row.expected_pass_status) { + assert.equal( + row.plan_status, + "native_probe_passed_waiting_for_node_browser_promotion_proof", + `${row.boundary_class}: passed probe plan status drift`, + ); + } else if (row.runtime_ready === "1") { + assert.equal(row.plan_status, "ready_to_run_opt_in_probe", `${row.boundary_class}: ready plan status drift`); + assert.equal(row.missing_requirements, "-", `${row.boundary_class}: ready plan must not list missing requirements`); + } else { + assert.equal(row.plan_status, "blocked_missing_host_runtime", `${row.boundary_class}: missing-runtime plan status drift`); + assert.notEqual(row.missing_requirements, "-", `${row.boundary_class}: missing-runtime plan must list requirements`); + } + assert.ok(row.notes.includes("no_automatic_promotion"), `${row.boundary_class}: execution plan note drift`); + } + + return parsedRows; +} + +function nativeRuntimeProbePassEvidenceContractRows({ + nativeRuntimeProbeRows, + executionPlanRows, +}) { + const planByClass = new Map(executionPlanRows.map((row) => [row.boundary_class, row])); + const expectedEvidenceByClass = new Map([ + [ + "L4-USER-M-PROCESS", + [ + "millturn_user_m_runtime_probe_status=runtime_state_probe_passed", + "millturn_user_m_M128_switchkins_target_ready=1", + "millturn_user_m_M128_runtime_state_ok=1", + "millturn_user_m_M129_switchkins_target_ready=1", + "millturn_user_m_M129_runtime_state_ok=1", + ], + ], + [ + "L4-TOOL-DB", + [ + "tool_db_runtime_probe_status=runtime_protocol_probe_passed", + "tool_db_protocol_version=v2.1", + "tool_db_get_all_count=10", + "tool_db_get_all_fini=1", + "tool_db_put_tool_update_state_ok=1", + "tool_db_load_spindle_state_ok=1", + "tool_db_unload_spindle_state_ok=1", + "tool_db_persistence_state_ok=1", + "tool_db_runtime_protocol_probe_ok=1", + ], + ], + [ + "L4-PYTHON-REMAP", + [ + "python_remap_runtime_probe_status=runtime_lifecycle_probe_passed", + "python_remap_lifecycle_interpreter_sentinel_ok=1", + "python_remap_lifecycle_toplevel_imported=1", + "python_remap_lifecycle_remap_imported=1", + "python_remap_lifecycle_callable_lookup_ok=1", + "python_remap_lifecycle_generator_returned=1", + "python_remap_lifecycle_generator_first_yield=2", + "python_remap_lifecycle_generator_finish_ok=1", + "python_remap_runtime_lifecycle_probe_ok=1", + ], + ], + ]); + + return nativeRuntimeProbeRows.map((row) => { + const plan = planByClass.get(row.boundary_class); + const expectedEvidence = expectedEvidenceByClass.get(row.boundary_class) ?? []; + const stdoutPath = resolve(dirname(nativeRuntimeProbeSummarySourcePath), row.stdout_log); + const stdoutText = existsSync(stdoutPath) ? readFileSync(stdoutPath, "utf8") : ""; + const evidenceRequiredNow = row.probe_status === plan?.expected_pass_status; + const missingEvidence = evidenceRequiredNow + ? expectedEvidence.filter((evidence) => !stdoutText.includes(evidence)) + : []; + const observedEvidenceReady = evidenceRequiredNow && missingEvidence.length === 0; + const evidenceStatus = evidenceRequiredNow + ? observedEvidenceReady + ? "native_pass_evidence_observed" + : "native_pass_evidence_missing" + : "pending_until_native_pass"; + + return [ + row.boundary_class, + row.boundary_kind, + row.target, + row.runtime_probe, + plan?.expected_pass_status ?? "-", + row.stdout_log, + listValue(expectedEvidence), + row.probe_status, + flagValue(evidenceRequiredNow), + flagValue(observedEvidenceReady), + listValue(missingEvidence), + row.execution_enabled, + row.promotion_allowed, + evidenceStatus, + evidenceRequiredNow + ? "native_pass_stdout_evidence_required_before_node_browser_promotion" + : "contract_only_not_evaluated_until_native_pass_no_automatic_promotion", + ].map(tsvValue).join("\t"); + }); +} + +function verifyNativeRuntimeProbePassEvidenceContractRows({ + rows, + nativeRuntimeProbeRows, + executionPlanRows, +}) { + const expectedHeaders = [ + "boundary_class", + "boundary_kind", + "target", + "runtime_probe", + "expected_pass_status", + "stdout_log", + "expected_evidence_keys", + "current_probe_status", + "evidence_required_now", + "observed_evidence_ready", + "missing_evidence_keys", + "execution_enabled", + "promotion_allowed", + "evidence_status", + "notes", + ]; + const parsedRows = parseTsv( + `${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`, + expectedHeaders, + ); + const expected = new Map([ + [ + "L4-USER-M-PROCESS", + { + boundaryKind: "external_user_m_process", + target: "axis/vismach/millturn/example.ngc", + runtimeProbe: "linuxcnc_millturn_user_m_runtime_probe", + expectedPassStatus: "runtime_state_probe_passed", + evidence: [ + "millturn_user_m_runtime_probe_status=runtime_state_probe_passed", + "millturn_user_m_M128_runtime_state_ok=1", + "millturn_user_m_M129_runtime_state_ok=1", + ], + }, + ], + [ + "L4-TOOL-DB", + { + boundaryKind: "tool_database_process", + target: "axis/db_demo/base.ngc", + runtimeProbe: "linuxcnc_tool_db_runtime_probe", + expectedPassStatus: "runtime_protocol_probe_passed", + evidence: [ + "tool_db_runtime_probe_status=runtime_protocol_probe_passed", + "tool_db_protocol_version=v2.1", + "tool_db_runtime_protocol_probe_ok=1", + "tool_db_persistence_state_ok=1", + ], + }, + ], + [ + "L4-PYTHON-REMAP", + { + boundaryKind: "python_runtime", + target: "axis/remap/stop-lookahead/nc_files", + runtimeProbe: "linuxcnc_python_remap_runtime_probe", + expectedPassStatus: "runtime_lifecycle_probe_passed", + evidence: [ + "python_remap_runtime_probe_status=runtime_lifecycle_probe_passed", + "python_remap_lifecycle_generator_first_yield=2", + "python_remap_runtime_lifecycle_probe_ok=1", + ], + }, + ], + ]); + const nativeByClass = new Map(nativeRuntimeProbeRows.map((row) => [row.boundary_class, row])); + const planByClass = new Map(executionPlanRows.map((row) => [row.boundary_class, row])); + + assert.equal(parsedRows.length, expected.size, "native runtime pass evidence contract row count drift"); + assert.deepEqual( + parsedRows.map((row) => row.boundary_class), + [...expected.keys()], + "native runtime pass evidence contract order drift", + ); + + for (const row of parsedRows) { + const expectedRow = expected.get(row.boundary_class); + const nativeRow = nativeByClass.get(row.boundary_class); + const planRow = planByClass.get(row.boundary_class); + assert.ok(expectedRow, `${row.boundary_class}: unexpected pass evidence contract row`); + assert.ok(nativeRow, `${row.boundary_class}: pass evidence contract lacks native summary row`); + assert.ok(planRow, `${row.boundary_class}: pass evidence contract lacks execution plan row`); + assert.equal(row.boundary_kind, expectedRow.boundaryKind, `${row.boundary_class}: pass evidence kind drift`); + assert.equal(row.target, expectedRow.target, `${row.boundary_class}: pass evidence target drift`); + assert.equal(row.runtime_probe, expectedRow.runtimeProbe, `${row.boundary_class}: pass evidence probe drift`); + assert.equal(row.expected_pass_status, expectedRow.expectedPassStatus, `${row.boundary_class}: pass evidence status drift`); + assert.equal(row.expected_pass_status, planRow.expected_pass_status, `${row.boundary_class}: pass evidence execution plan drift`); + assert.equal(row.stdout_log, nativeRow.stdout_log, `${row.boundary_class}: pass evidence stdout drift`); + assert.equal(row.current_probe_status, nativeRow.probe_status, `${row.boundary_class}: pass evidence current status drift`); + assert.equal(row.execution_enabled, "0", `${row.boundary_class}: pass evidence contract must not enable execution`); + assert.equal(row.promotion_allowed, "0", `${row.boundary_class}: pass evidence contract must not allow promotion`); + assert.notEqual(row.expected_evidence_keys, "-", `${row.boundary_class}: pass evidence contract lacks keys`); + for (const evidence of expectedRow.evidence) { + assert.ok(row.expected_evidence_keys.includes(evidence), `${row.boundary_class}: missing pass evidence ${evidence}`); + } + if (row.current_probe_status === row.expected_pass_status) { + assert.equal(row.evidence_required_now, "1", `${row.boundary_class}: pass evidence should be required`); + assert.equal(row.observed_evidence_ready, "1", `${row.boundary_class}: pass evidence must be observed after native pass`); + assert.equal(row.missing_evidence_keys, "-", `${row.boundary_class}: pass evidence should have no missing keys`); + assert.equal(row.evidence_status, "native_pass_evidence_observed", `${row.boundary_class}: pass evidence status drift`); + } else { + assert.equal(row.evidence_required_now, "0", `${row.boundary_class}: pass evidence must wait for native pass`); + assert.equal(row.observed_evidence_ready, "0", `${row.boundary_class}: pending pass evidence must not be marked ready`); + assert.equal(row.missing_evidence_keys, "-", `${row.boundary_class}: pending pass evidence must not report missing keys`); + assert.equal(row.evidence_status, "pending_until_native_pass", `${row.boundary_class}: pending pass evidence status drift`); + } + assert.ok( + row.notes.includes("no_automatic_promotion") || row.notes.includes("before_node_browser_promotion"), + `${row.boundary_class}: pass evidence contract note drift`, + ); + } + + return parsedRows; +} + function keyValuesFromText(text) { const values = new Map(); for (const line of text.split("\n")) { @@ -7377,6 +9157,16 @@ const toolDbProcessNativeRuntimeReadinessRowsGenerated = const toolDbProcessNativeRuntimeReadinessRecords = verifyToolDbProcessNativeRuntimeReadinessRows( toolDbProcessNativeRuntimeReadinessRowsGenerated, ); +const toolDbProcessNativeRuntimeProbeGateRowsGenerated = + toolDbProcessNativeRuntimeProbeGateRows({ + transactionRows: toolDbProcessTransactionPlanRecords, + readinessRows: toolDbProcessNativeRuntimeReadinessRecords, + }); +const toolDbProcessNativeRuntimeProbeGateRecords = verifyToolDbProcessNativeRuntimeProbeGateRows({ + rows: toolDbProcessNativeRuntimeProbeGateRowsGenerated, + transactionRows: toolDbProcessTransactionPlanRecords, + readinessRows: toolDbProcessNativeRuntimeReadinessRecords, +}); const pythonRemapBoundarySummaryRows = blockedDependencySummaryRecords .map(pythonRemapBoundarySummaryRow) .filter(Boolean); @@ -7433,6 +9223,16 @@ const pythonRemapNativeRuntimeFixturePlanRecords = verifyPythonRemapNativeRuntim pythonRemapNativeRuntimeFixturePlanRowsGenerated, pythonRemapNativeRuntimeStatePlanRecords, ); +const pythonRemapNativeRuntimeProbeGateRowsGenerated = + pythonRemapNativeRuntimeProbeGateRows({ + fixturePlanRows: pythonRemapNativeRuntimeFixturePlanRecords, + readinessRows: pythonRemapNativeRuntimeReadinessRecords, + }); +const pythonRemapNativeRuntimeProbeGateRecords = verifyPythonRemapNativeRuntimeProbeGateRows({ + rows: pythonRemapNativeRuntimeProbeGateRowsGenerated, + fixturePlanRows: pythonRemapNativeRuntimeFixturePlanRecords, + readinessRows: pythonRemapNativeRuntimeReadinessRecords, +}); const summaryRows = []; const skipReasonCounts = new Map(); @@ -7699,6 +9499,35 @@ const nativeRuntimeProbeSummaryRowsGenerated = nativeRuntimeProbeSummaryRows(); const nativeRuntimeProbeSummaryRecords = verifyNativeRuntimeProbeSummaryRows( nativeRuntimeProbeSummaryRowsGenerated, ); +const runtimeProbeGateAlignmentRowsGenerated = runtimeProbeGateAlignmentRows({ + nativeRuntimeProbeRows: nativeRuntimeProbeSummaryRecords, + userMRuntimeProbeGateRows: userMProcessNativeRuntimeProbeGateRecords, + toolDbRuntimeProbeGateRows: toolDbProcessNativeRuntimeProbeGateRecords, + pythonRuntimeProbeGateRows: pythonRemapNativeRuntimeProbeGateRecords, +}); +const runtimeProbeGateAlignmentRecords = verifyRuntimeProbeGateAlignmentRows( + runtimeProbeGateAlignmentRowsGenerated, +); +const nativeRuntimeProbeExecutionPlanRowsGenerated = nativeRuntimeProbeExecutionPlanRows({ + nativeRuntimeProbeRows: nativeRuntimeProbeSummaryRecords, + runtimeProbeGateAlignmentRows: runtimeProbeGateAlignmentRecords, +}); +const nativeRuntimeProbeExecutionPlanRecords = verifyNativeRuntimeProbeExecutionPlanRows({ + rows: nativeRuntimeProbeExecutionPlanRowsGenerated, + nativeRuntimeProbeRows: nativeRuntimeProbeSummaryRecords, + runtimeProbeGateAlignmentRows: runtimeProbeGateAlignmentRecords, +}); +const nativeRuntimeProbePassEvidenceContractRowsGenerated = + nativeRuntimeProbePassEvidenceContractRows({ + nativeRuntimeProbeRows: nativeRuntimeProbeSummaryRecords, + executionPlanRows: nativeRuntimeProbeExecutionPlanRecords, + }); +const nativeRuntimeProbePassEvidenceContractRecords = + verifyNativeRuntimeProbePassEvidenceContractRows({ + rows: nativeRuntimeProbePassEvidenceContractRowsGenerated, + nativeRuntimeProbeRows: nativeRuntimeProbeSummaryRecords, + executionPlanRows: nativeRuntimeProbeExecutionPlanRecords, + }); const runtimeBoundaryNativeAlignmentSummaryRowsGenerated = runtimeBoundaryNativeAlignmentSummaryRows({ userMRows: userMProcessNativeStateAlignmentRecords, toolDbRows: toolDbProcessNativeProtocolAlignmentRecords, @@ -7726,15 +9555,59 @@ const blockedRuntimePromotionLockRecords = verifyBlockedRuntimePromotionLockRows blockedRuntimePromotionLockRowsGenerated, nextBoundaryRecords, ); +const runtimeBoundaryPromotionReadinessRowsGenerated = runtimeBoundaryPromotionReadinessRows({ + executionPlanRows: nativeRuntimeProbeExecutionPlanRecords, + passEvidenceRows: nativeRuntimeProbePassEvidenceContractRecords, + promotionLockRows: blockedRuntimePromotionLockRecords, +}); +const runtimeBoundaryPromotionReadinessRecords = verifyRuntimeBoundaryPromotionReadinessRows({ + rows: runtimeBoundaryPromotionReadinessRowsGenerated, + executionPlanRows: nativeRuntimeProbeExecutionPlanRecords, + passEvidenceRows: nativeRuntimeProbePassEvidenceContractRecords, + promotionLockRows: blockedRuntimePromotionLockRecords, +}); +const runtimeBoundaryPromotionBlockerRowsGenerated = runtimeBoundaryPromotionBlockerRows({ + promotionReadinessRows: runtimeBoundaryPromotionReadinessRecords, + executionPlanRows: nativeRuntimeProbeExecutionPlanRecords, +}); +const runtimeBoundaryPromotionBlockerRecords = verifyRuntimeBoundaryPromotionBlockerRows({ + rows: runtimeBoundaryPromotionBlockerRowsGenerated, + promotionReadinessRows: runtimeBoundaryPromotionReadinessRecords, + executionPlanRows: nativeRuntimeProbeExecutionPlanRecords, +}); const nextBoundaryRecommendationRowsGenerated = nextBoundaryRecommendationRows({ worklistRows: nextBoundaryRecords, promotionLockRows: blockedRuntimePromotionLockRecords, runtimeContractRows: runtimeBoundaryContractSummaryRecords, userMRuntimeProbeGateRows: userMProcessNativeRuntimeProbeGateRecords, toolDbTransactionRows: toolDbProcessTransactionPlanRecords, + toolDbRuntimeProbeGateRows: toolDbProcessNativeRuntimeProbeGateRecords, pythonFixturePlanRows: pythonRemapNativeRuntimeFixturePlanRecords, }); -verifyNextBoundaryRecommendationRows(nextBoundaryRecommendationRowsGenerated); +const nextBoundaryRecommendationRecords = + verifyNextBoundaryRecommendationRows(nextBoundaryRecommendationRowsGenerated); +const runtimeBoundaryPostNativePassGateRowsGenerated = runtimeBoundaryPostNativePassGateRows({ + promotionReadinessRows: runtimeBoundaryPromotionReadinessRecords, + recommendationRows: nextBoundaryRecommendationRecords, +}); +const runtimeBoundaryPostNativePassGateRecords = verifyRuntimeBoundaryPostNativePassGateRows({ + rows: runtimeBoundaryPostNativePassGateRowsGenerated, + promotionReadinessRows: runtimeBoundaryPromotionReadinessRecords, + recommendationRows: nextBoundaryRecommendationRecords, +}); +const runtimeBoundaryHostPreflightRowsGenerated = runtimeBoundaryHostPreflightRows({ + userMRuntimeProbeGateRows: userMProcessNativeRuntimeProbeGateRecords, + toolDbRuntimeProbeGateRows: toolDbProcessNativeRuntimeProbeGateRecords, + pythonRuntimeProbeGateRows: pythonRemapNativeRuntimeProbeGateRecords, + executionPlanRows: nativeRuntimeProbeExecutionPlanRecords, +}); +const runtimeBoundaryHostPreflightRecords = verifyRuntimeBoundaryHostPreflightRows({ + rows: runtimeBoundaryHostPreflightRowsGenerated, + userMRuntimeProbeGateRows: userMProcessNativeRuntimeProbeGateRecords, + toolDbRuntimeProbeGateRows: toolDbProcessNativeRuntimeProbeGateRecords, + pythonRuntimeProbeGateRows: pythonRemapNativeRuntimeProbeGateRecords, + executionPlanRows: nativeRuntimeProbeExecutionPlanRecords, +}); const boundaryPhaseCompletionRows = boundaryPhaseCompletionSummaryRows({ boundaryRows: boundarySummaryRecords, iniBoundaryRows: iniBoundarySummaryRecords, @@ -7746,15 +9619,24 @@ const boundaryPhaseCompletionRows = boundaryPhaseCompletionSummaryRows({ userMRuntimeProbeGateRows: userMProcessNativeRuntimeProbeGateRecords, toolDbTransactionRows: toolDbProcessTransactionPlanRecords, toolDbRuntimeReadinessRows: toolDbProcessNativeRuntimeReadinessRecords, + toolDbRuntimeProbeGateRows: toolDbProcessNativeRuntimeProbeGateRecords, pythonFamilyRows: pythonRemapFamilyRecords, pythonRuntimeContractRows: pythonRemapRuntimeContractRecords, pythonRuntimeReadinessRows: pythonRemapNativeRuntimeReadinessRecords, + pythonRuntimeProbeGateRows: pythonRemapNativeRuntimeProbeGateRecords, pythonRuntimeStatePlanRows: pythonRemapNativeRuntimeStatePlanRecords, pythonRuntimeFixturePlanRows: pythonRemapNativeRuntimeFixturePlanRecords, runtimeContractSummaryRows: runtimeBoundaryContractSummaryRecords, nativeProofAlignmentRows: nativeProofAlignmentRecords, nativeRuntimeProbeRows: nativeRuntimeProbeSummaryRecords, runtimeNativeAlignmentRows: runtimeBoundaryNativeAlignmentSummaryRecords, + runtimeProbeGateAlignmentRows: runtimeProbeGateAlignmentRecords, + nativeRuntimeProbeExecutionPlanRows: nativeRuntimeProbeExecutionPlanRecords, + nativeRuntimeProbePassEvidenceRows: nativeRuntimeProbePassEvidenceContractRecords, + runtimeBoundaryPromotionReadinessRows: runtimeBoundaryPromotionReadinessRecords, + runtimeBoundaryPromotionBlockerRows: runtimeBoundaryPromotionBlockerRecords, + runtimeBoundaryPostNativePassGateRows: runtimeBoundaryPostNativePassGateRecords, + runtimeBoundaryHostPreflightRows: runtimeBoundaryHostPreflightRecords, trackedRows: trackedMatrixRecords, inventoryRows: summaryRows, }); @@ -8230,6 +10112,37 @@ writeFileSync( ].join("\n")}\n`, ); +writeFileSync( + toolDbProcessNativeRuntimeProbeGatePath, + `${[ + [ + "path", + "ini", + "blocked", + "db_program", + "required_runtime", + "python3_available", + "python3_sufficient", + "tool_table_fallback_sufficient", + "db_program_source_ready", + "linuxcnc_python_modules_ready", + "protocol_contract_ready", + "protocol_transactions", + "runtime_requirements", + "missing_requirements", + "runtime_ready", + "source_proof_ready", + "required_native_proof", + "gate_status", + "proof_status", + "execution_enabled", + "promotion_allowed", + "notes", + ].join("\t"), + ...toolDbProcessNativeRuntimeProbeGateRowsGenerated, + ].join("\n")}\n`, +); + writeFileSync( pythonRemapBoundarySummaryPath, `${[ @@ -8375,6 +10288,35 @@ writeFileSync( ].join("\n")}\n`, ); +writeFileSync( + pythonRemapNativeRuntimeProbeGatePath, + `${[ + [ + "family", + "blocked", + "fixture_id", + "required_runtime", + "python3_available", + "python3_sufficient", + "linuxcnc_available", + "runtime_owner_sources_ready", + "representative_module_imports", + "representative_modules_ready", + "runtime_requirements", + "missing_requirements", + "runtime_ready", + "source_proof_ready", + "required_native_proof", + "gate_status", + "proof_status", + "execution_enabled", + "promotion_allowed", + "notes", + ].join("\t"), + ...pythonRemapNativeRuntimeProbeGateRowsGenerated, + ].join("\n")}\n`, +); + writeFileSync( pythonRemapNativeRuntimeStatePlanPath, `${[ @@ -8544,18 +10486,104 @@ writeFileSync( "boundary_kind", "target", "runtime_probe", + "required_native_proof", + "opt_in_env", "runtime_ready", "source_proof_ready", "probe_status", "execution_enabled", "promotion_allowed", "missing_requirements", + "probe_note", "stdout_log", ].join("\t"), ...nativeRuntimeProbeSummaryRowsGenerated, ].join("\n")}\n`, ); +writeFileSync( + runtimeProbeGateAlignmentPath, + `${[ + [ + "boundary_class", + "boundary_kind", + "target", + "gate_artifact", + "native_runtime_probe", + "gate_required_native_proof", + "native_required_native_proof", + "native_opt_in_env", + "gate_runtime_ready", + "native_runtime_ready", + "gate_source_proof_ready", + "native_source_proof_ready", + "gate_status", + "native_probe_status", + "gate_missing_requirements", + "native_missing_requirements", + "required_native_proof_match", + "missing_requirements_match", + "execution_disabled_match", + "promotion_disabled_match", + "status_compatible", + "alignment_ok", + "notes", + ].join("\t"), + ...runtimeProbeGateAlignmentRowsGenerated, + ].join("\n")}\n`, +); + +writeFileSync( + nativeRuntimeProbeExecutionPlanPath, + `${[ + [ + "boundary_class", + "boundary_kind", + "target", + "runtime_probe", + "required_native_proof", + "opt_in_env", + "execution_command", + "runtime_ready", + "source_proof_ready", + "current_probe_status", + "expected_pass_status", + "missing_requirements", + "gate_alignment_ok", + "promotion_requires", + "execution_enabled", + "promotion_allowed", + "plan_status", + "notes", + ].join("\t"), + ...nativeRuntimeProbeExecutionPlanRowsGenerated, + ].join("\n")}\n`, +); + +writeFileSync( + nativeRuntimeProbePassEvidenceContractPath, + `${[ + [ + "boundary_class", + "boundary_kind", + "target", + "runtime_probe", + "expected_pass_status", + "stdout_log", + "expected_evidence_keys", + "current_probe_status", + "evidence_required_now", + "observed_evidence_ready", + "missing_evidence_keys", + "execution_enabled", + "promotion_allowed", + "evidence_status", + "notes", + ].join("\t"), + ...nativeRuntimeProbePassEvidenceContractRowsGenerated, + ].join("\n")}\n`, +); + writeFileSync( runtimeBoundaryNativeAlignmentSummaryPath, `${[ @@ -8601,6 +10629,107 @@ writeFileSync( ].join("\n")}\n`, ); +writeFileSync( + runtimeBoundaryPromotionReadinessPath, + `${[ + [ + "boundary_class", + "boundary_kind", + "target", + "blocked", + "current_probe_status", + "expected_pass_status", + "native_pass_ready", + "native_evidence_status", + "native_evidence_ready", + "node_inventory_gate_complete", + "browser_smoke_gate_complete", + "promotion_lock_active", + "manual_lock_update_required", + "promotion_ready", + "execution_enabled", + "promotion_allowed", + "blocking_reason", + "notes", + ].join("\t"), + ...runtimeBoundaryPromotionReadinessRowsGenerated, + ].join("\n")}\n`, +); + +writeFileSync( + runtimeBoundaryPromotionBlockersPath, + `${[ + [ + "boundary_class", + "boundary_kind", + "target", + "blocked", + "promotion_ready", + "blocker_count", + "blocker_keys", + "missing_runtime_requirements", + "next_unblock_action", + "execution_enabled", + "promotion_allowed", + "notes", + ].join("\t"), + ...runtimeBoundaryPromotionBlockerRowsGenerated, + ].join("\n")}\n`, +); + +writeFileSync( + runtimeBoundaryPostNativePassGatesPath, + `${[ + [ + "boundary_class", + "boundary_kind", + "target", + "blocked", + "current_probe_status", + "expected_pass_status", + "native_pass_ready", + "native_evidence_ready", + "required_node_proof", + "required_browser_proof", + "node_gate_status", + "browser_gate_status", + "manual_lock_update_required", + "promotion_lock_active", + "execution_enabled", + "promotion_allowed", + "gate_status", + "notes", + ].join("\t"), + ...runtimeBoundaryPostNativePassGateRowsGenerated, + ].join("\n")}\n`, +); + +writeFileSync( + runtimeBoundaryHostPreflightPath, + `${[ + [ + "boundary_class", + "boundary_kind", + "target", + "blocked", + "required_runtime", + "runtime_requirements", + "missing_requirements", + "runtime_ready", + "source_proof_ready", + "required_native_proof", + "opt_in_env", + "execution_command", + "current_probe_status", + "preflight_status", + "execution_enabled", + "promotion_allowed", + "notes", + ].join("\t"), + ...runtimeBoundaryHostPreflightRowsGenerated, + ].join("\n")}\n`, +); + writeFileSync( nextBoundaryRecommendationsPath, `${[ diff --git a/wasm-port/tools/build_native_probes.sh b/wasm-port/tools/build_native_probes.sh index 4d7a0f3..b2fd72c 100755 --- a/wasm-port/tools/build_native_probes.sh +++ b/wasm-port/tools/build_native_probes.sh @@ -424,8 +424,10 @@ append_native_runtime_probe_summary_row() { local boundary_kind="$3" local target="$4" local runtime_probe="$5" - local prefix="$6" - local stdout_log="$7" + local required_native_proof="$6" + local opt_in_env="$7" + local prefix="$8" + local stdout_log="$9" local runtime_ready local source_proof_ready @@ -433,41 +435,49 @@ append_native_runtime_probe_summary_row() { local execution_enabled local promotion_allowed local missing_requirements + local probe_note runtime_ready="$(stdout_value "$stdout_log" "${prefix}_runtime_ready")" source_proof_ready="$(stdout_value "$stdout_log" "${prefix}_source_proof_ready")" probe_status="$(stdout_value "$stdout_log" "${prefix}_runtime_probe_status")" execution_enabled="$(stdout_value "$stdout_log" "${prefix}_execution_enabled")" promotion_allowed="$(stdout_value "$stdout_log" "${prefix}_promotion_allowed")" missing_requirements="$(stdout_value "$stdout_log" "${prefix}_missing_requirements")" + probe_note="$(stdout_value "$stdout_log" "${prefix}_runtime_probe_note")" - printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \ + printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \ "$boundary_class" \ "$boundary_kind" \ "$target" \ "$runtime_probe" \ + "$required_native_proof" \ + "$opt_in_env" \ "$runtime_ready" \ "$source_proof_ready" \ "$probe_status" \ "$execution_enabled" \ "$promotion_allowed" \ "${missing_requirements:-"-"}" \ + "${probe_note:-"-"}" \ "${stdout_log#"$BUILD_DIR"/}" >> "$tmp" } write_native_runtime_probe_summary() { local tmp tmp="$(mktemp)" - printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \ + printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \ boundary_class \ boundary_kind \ target \ runtime_probe \ + required_native_proof \ + opt_in_env \ runtime_ready \ source_proof_ready \ probe_status \ execution_enabled \ promotion_allowed \ missing_requirements \ + probe_note \ stdout_log > "$tmp" append_native_runtime_probe_summary_row \ @@ -476,6 +486,8 @@ write_native_runtime_probe_summary() { external_user_m_process \ axis/vismach/millturn/example.ngc \ linuxcnc_millturn_user_m_runtime_probe \ + native_runtime_state_probe_required \ + ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1 \ millturn_user_m \ "$BUILD_DIR/linuxcnc_millturn_user_m_runtime_probe.run.stdout.log" @@ -485,6 +497,8 @@ write_native_runtime_probe_summary() { tool_database_process \ axis/db_demo/base.ngc \ linuxcnc_tool_db_runtime_probe \ + native_db_process_protocol_probe_required \ + ENABLE_TOOL_DB_RUNTIME_PROBE=1 \ tool_db \ "$BUILD_DIR/linuxcnc_tool_db_runtime_probe.run.stdout.log" @@ -494,6 +508,8 @@ write_native_runtime_probe_summary() { python_runtime \ axis/remap/stop-lookahead/nc_files \ linuxcnc_python_remap_runtime_probe \ + linuxcnc_python_runtime_lifecycle_probe_required \ + ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 \ python_remap \ "$BUILD_DIR/linuxcnc_python_remap_runtime_probe.run.stdout.log"