# Compatibility Validation

## Purpose

This document records how the standalone LinuxCNC WASM port currently proves
that migrated behavior remains tied to LinuxCNC source code and fixture
semantics.

The primary native validation command is:

```bash
wasm-port/tests/native/verify_native_probes.sh
```

The native LinuxCNC `nc_files` basic/example validation command is:

```bash
wasm-port/tests/native/verify_nc_files.sh
```

The current WASM smoke validation command is:

```bash
wasm-port/tests/wasm/node/verify_ini_wasm.sh
```

The current WASM interpreter-core smoke validation command is:

```bash
wasm-port/tests/wasm/node/verify_interp_wasm.sh
```

The current WASM sim-config smoke validation command is:

```bash
wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh
```

The current WASM sim-config inventory validation command is:

```bash
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
```

The current WASM `nc_files` smoke validation command is:

```bash
wasm-port/tests/wasm/node/verify_nc_files_wasm.sh
```

The current WASM trajectory-planner smoke validation command is:

```bash
wasm-port/tests/wasm/node/verify_tp_wasm.sh
```

The current OPFS host-boundary validation command is:

```bash
wasm-port/tests/opfs/node/verify_file_service.sh
```

The current UI helper Node smoke validation command is:

```bash
wasm-port/tests/ui/node/verify_ui_node_smokes.sh
```

UI helper docs:

- `wasm-port/docs/ui-run-summary-helpers.md`
- `wasm-port/docs/ui-session-summary-helpers.md`
- `wasm-port/docs/ui-machine-file-helpers.md`
- `wasm-port/docs/ui-panel-state-summary.md`

The current browser smoke validation command is:

```bash
wasm-port/tests/browser/verify_ini_panel_browser.sh
```

The current browser interpreter smoke validation command is:

```bash
wasm-port/tests/browser/verify_interp_browser.sh
```

The current aggregate host/WASM/browser smoke command is:

```bash
wasm-port/tests/host/verify_host_smokes.sh
```

## Validation Layers

Use these layers when deciding where a LinuxCNC asset belongs. Do not widen a
later layer until the narrower layer has either passed or recorded an explicit
expected boundary.

| Layer | Scope | Entry command | Current result | Expected boundary |
| --- | --- | --- | --- | --- |
| 1 | `linuxcnc/nc_files` basic G-code smoke | `wasm-port/tests/native/verify_nc_files.sh` | `total: 107`, `pass: 101`, `expected_fail: 6`, `unexpected_fail: 0` | LinuxCNC-native entry-point/context edges only: W-axis machine context, lathe tool/cutter-comp context, upstream O-word syntax edge, and probe runtime context. |
| 2 | `linuxcnc/configs/sim` native strict harness | `wasm-port/tests/native/verify_sim_configs.sh` | `total: 159`, `pass: 151`, `expected_fail: 8`, `unexpected_fail: 0` | Native `bin/rs274` cannot provide task/user-M process execution or some INI axis-mask/runtime context. The upstream `incremental_repetition_g533.ngc` demo remains an expected upstream demo edge and must not be made pass by changing G-code semantics. |
| 3 | Standalone native runtime probes | `wasm-port/tests/native/verify_native_probes.sh` | Passes with `native probes complete` after source sync, no-standalone-semantics, fixture baseline, sim-config, and `nc_files` checks | Runtime adapters may cover filesystem, HAL/user-M, remap, parameter, tool, kinematics, TP, and WASM/browser boundary behavior, but CNC semantics must still come from vendored LinuxCNC source. |
| 4 | WASM Node/browser representative smoke | `wasm-port/tests/host/verify_host_smokes.sh` plus targeted Node/browser commands below | Current targeted checks pass with `sim_configs_wasm_node_smoke=ok`, `nc_files_wasm_node_smoke=ok`, `browser_interp_smoke=ok`, `browser_ini_opfs_smoke=ok`, and aggregate `host_wasm_opfs_browser_smokes=ok` | JS/browser code may stage files, apply executable bits, persist OPFS text, and forward paths to C ABI calls. It must not implement G-code, remap, tool-table, parameter, planner, kinematics, or user-M semantics. |

Layer 4 is made of focused smoke commands so failures can be isolated before
running the aggregate host check:

```bash
wasm-port/tests/wasm/node/verify_ini_wasm.sh
wasm-port/tests/wasm/node/verify_interp_wasm.sh
wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
wasm-port/tests/wasm/node/verify_nc_files_wasm.sh
wasm-port/tests/wasm/node/verify_tp_wasm.sh
wasm-port/tests/opfs/node/verify_file_service.sh
wasm-port/tests/ui/node/verify_ui_node_smokes.sh
wasm-port/tests/browser/verify_ini_panel_browser.sh
wasm-port/tests/browser/verify_interp_browser.sh
wasm-port/tests/host/verify_host_smokes.sh
```

Layer 4 INI-context staging uses `planIniFileContextStaging()` to collect
INI-declared `[DISPLAY]OPEN_FILE`, `[EMCIO]TOOL_TABLE`,
`[RS274NGC]PARAMETER_FILE`, `[RS274NGC]SUBROUTINE_PATH`,
`[RS274NGC]USER_M_PATH`, and remap-NGC files from the vendored source
manifest without browser directory enumeration. `planSimConfigStaging()` is the
`configs/sim` wrapper around that generic planner. These planners are host
filesystem helpers only; execution still goes through vendored LinuxCNC
interpreter/remap/tool/parameter code.

Layer responsibilities are intentionally narrow:

| Layer | LinuxCNC-owned behavior | Host/WASM adapter allowance | Expected-failure policy |
| --- | --- | --- | --- |
| 1 | `nc_files` parsing and execution through upstream `rs274` | Generate only the native harness INI/tool-table context needed to classify upstream examples | Expected failures document missing machine/task context or known upstream example edges; they must not be made pass by changing G-code semantics. |
| 2 | `configs/sim` file execution through upstream `rs274` with sim INI context | Locate the corresponding INI/tool table and classify main programs, macros, and remap subroutines | Expected failures document upstream standalone `rs274` limits such as task/user-M process edges and the preserved `incremental_repetition_g533.ngc` demo edge. |
| 3 | Vendored LinuxCNC interpreter, remap, tool, parameter, kinematics, and planner code | Provide deterministic runtime shims for filesystem, HAL/user-M boundaries, machine status, and build/source-probe coverage | A standalone pass may cover a runtime edge that Layer 1/2 cannot provide, but it must still call vendored LinuxCNC source for CNC behavior. |
| 4 | Vendored LinuxCNC C ABI behavior from generated WASM modules | Stage files, apply executable bits, persist OPFS text, generate manifest-based staging plans, and forward paths/results between JS/browser and C ABI | A WASM/browser pass proves the host boundary can reproduce the staged LinuxCNC context; it does not replace native source validation or justify JS-owned CNC semantics. |

## Layer 2 Expected Failures

The current `wasm-port/tests/native/verify_sim_configs.sh` run reports
`total: 159`, `pass: 151`, `expected_fail: 8`, and `unexpected_fail: 0`.
The eight expected failures are listed in the table below.

The tracked per-program inventory for this layer now lives in
`wasm-port/docs/sim-configs-coverage-matrix.md`. That matrix maps all 159
current `summary.tsv` records to program class, native result, current
Layer 3/4 coverage, and the first-pass blocked or follow-up note used for
future `configs/sim` coverage work.

The native harness also emits machine-readable derived artifacts without
changing the `summary.tsv` schema:

```text
wasm-port/build/native/source-probes.tsv
wasm-port/build/native/native-source-proof-summary.tsv
wasm-port/build/native/native-runtime-probe-summary.tsv
wasm-port/build/native/nc-files/summary.tsv
wasm-port/build/native/sim-configs/summary.tsv
wasm-port/build/native/sim-configs/class-summary.tsv
wasm-port/build/native/sim-configs/path-matrix.tsv
wasm-port/build/native/sim-configs/skipped.tsv
```

`class-summary.tsv` aggregates class/status/expected-failure counts.
`path-matrix.tsv` records each path, class, native status, expected-failure
reason, INI, tool table, runtime family, and blocked kind for CI and Node
inventory reconciliation.
`source-probes.tsv` records the native source-probe entry points used by
`verify_native_probes.sh`. `native-source-proof-summary.tsv` records
LinuxCNC-owned source proof for user-M process, tool DB process, and Python
remap runtime blockers without enabling execution or promotion.
`native-runtime-probe-summary.tsv` records the guarded native opt-in runtime
probe status and missing host requirements. `nc-files/summary.tsv` records the
Layer 1 native `nc_files` baseline, and `sim-configs/skipped.tsv` records the
Layer 2 native sim-config skipped or expected-skip accounting.

The Node inventory layer writes its own machine-readable artifacts:

```text
wasm-port/build/wasm/sim-configs-inventory/summary.tsv
wasm-port/build/wasm/sim-configs-inventory/skip-summary.tsv
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/full-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-native-state-alignment.tsv
wasm-port/build/wasm/sim-configs-inventory/tool-db-process-protocol-gates.tsv
wasm-port/build/wasm/sim-configs-inventory/tool-db-process-native-protocol-alignment.tsv
wasm-port/build/wasm/sim-configs-inventory/tool-db-process-native-runtime-probe-gate.tsv
wasm-port/build/wasm/sim-configs-inventory/python-remap-boundary-summary.tsv
wasm-port/build/wasm/sim-configs-inventory/python-remap-runtime-gates.tsv
wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-alignment.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-probe-gate.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
wasm-port/build/wasm/sim-configs-inventory/python-remap-family-summary.tsv
wasm-port/build/wasm/sim-configs-inventory/boundary-phase-completion-summary.tsv
wasm-port/build/wasm/sim-configs-inventory/native-proof-alignment-summary.tsv
wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-native-alignment-summary.tsv
wasm-port/build/wasm/sim-configs-inventory/native-runtime-probe-execution-plan.tsv
wasm-port/build/wasm/sim-configs-inventory/blocked-runtime-promotion-lock.tsv
wasm-port/build/wasm/sim-configs-inventory/next-boundary-worklist.tsv
wasm-port/build/wasm/sim-configs-inventory/next-boundary-recommendations.tsv
wasm-port/build/wasm/sim-configs-inventory/boundary-proof-gates.tsv
wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-native-evidence-acceptance-gate.tsv
wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-promotion-blockers.tsv
wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-preflight.tsv
wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-requirement-summary.tsv
wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-unblock-plan.tsv
wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-family-host-readiness.tsv
wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv
wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-dispatch-plan.tsv
wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-dispatch-rollup.tsv
wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-skip-evidence-contract.tsv
wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv
```

The current skip/block summary is `ASSET-ONLY=65`, `L4-PYTHON-REMAP=53`,
`L4-TOOL-DB=1`, `L4-USER-M-PROCESS=1`, `NON_MAIN_CLASS=10`, and
`UPSTREAM-DEMO=1`.
`skip-summary.tsv` is checked against the skip reasons derived from
`path-matrix.tsv`, so both the per-row inventory status and the aggregate
skip counts fail on blocked-policy drift.
For eligible rows, missing vendored machine context is an inventory failure,
not an expected skip; blocked rows must be classified by runtime dependency
before the Node inventory filter runs.
`boundary-summary.tsv` records one row per native inventory path with the
matrix blocked kind, SDK classifier recommendation, declared HAL/UI/HALUI/Python
process dependencies, `[EMCIO]DB_PROGRAM`, user-M execution codes, and the
subset of execution user-M codes that are not staged by vendored
`USER_M_PATH` files. It also separates Python UI/DB process dependencies from
Python remap runtime dependencies. For vendored INI rows, the SDK classifier
report must be available and `L4-TOOL-DB` / `L4-USER-M-PROCESS` hard blocks
must match the classifier recommendation. `recommended_blocked=UNAVAILABLE` is
only allowed when the boundary row records a missing vendored INI. Non-vendored
Python-remap families remain unavailable until their dependency inventory batch
vendors the required INI context.
`ini-boundary-summary.tsv` aggregates those path-level reports by INI, giving
vendored sim-config INIs a direct `report_available=1` coverage check and
recording hard-block recommendation alignment at INI granularity.
The inventory also guards safe representative rows such as
`axis/gladevcp/probe.ngc`, `woodpecker/on_abort.ngc`,
`axis/vismach/puma/puma_cube.ngc`, and
`axis/vismach/melfa-sim/example.ngc`: they must keep their declared process
dependencies in `boundary-summary.tsv`, remain Node/browser representatives,
match the expected HAL/UI/HALUI/Python process flags, and avoid hard-block
promotion unless a real hard runtime dependency appears.
Deterministic `M110`/`M111` representatives are also guarded: the inventory
must see the execution-chain user-M code, a vendored user-M file, and no hard
`L4-USER-M-PROCESS` recommendation. `opa_demo.ngc` includes its vendored
`circles.ngc` subroutine text in the boundary analysis so the `M111` call is
accounted for even though it is reached through `SUBROUTINE_PATH`.
`blocked-dependency-summary.tsv` records the hard blocked rows without
promoting them: 53 Python-remap rows, one tool-database row, and one external
user-M process row. It reads the source `linuxcnc/configs/sim` INI files for
dependency accounting only and records Python modules, remap/prolog/epilog
function ownership, NGC remap subpaths, HAL/UI/HALUI process declarations,
`DB_PROGRAM`, tool database protocol evidence, external user-M execution
codes, user-M process script files, user-M process side-effect evidence, and
the LinuxCNC source/config files that own the blocked behavior. The user-M
evidence is source-derived from the `M128`/`M129` Tcl scripts and records their
Tcl/HAL runtime use, kinstype guard, and `ini.[xyz]` HAL pin updates. The tool
database evidence is source-derived from `taskclass.cc`, `tooldata_db.cc`, and
`axis/db_demo/db.py`, including the `v2.1` handshake, `g`/`FINI` get-all, and
`l`/`u`/`p` notification protocol.
The final Node inventory `summary.tsv` also guards every hard-blocked row:
`L4-TOOL-DB`, `L4-USER-M-PROCESS`, and `L4-PYTHON-REMAP` paths must remain
`SKIP` with their matching blocked reason, not only carry a matrix-level
blocked label.
The detailed boundary design for the current non-Python hard blocks is tracked
in `wasm-port/docs/full-process-boundary-design.md`. That document records the
LinuxCNC owner sets and proof required before `axis/vismach/millturn/example.ngc`
or `axis/db_demo/base.ngc` can move out of `L4-USER-M-PROCESS` or
`L4-TOOL-DB`. It is not an execution artifact and does not change the current
Layer 4 blocked counts.
`full-process-boundary-summary.tsv` is the machine-readable companion for that
design record. It has one designed-but-blocked row for `millturn` and one for
`db_demo`, records the LinuxCNC runtime owner evidence from the corresponding
blocked dependency row, records the required native/Node/browser proof, and
keeps `execution_enabled=0` until the corresponding LinuxCNC-owned runtime
boundary exists. The tracked matrix and inventory guard also require those two
rows to remain non-representative in Node/browser until that proof exists.
`runtime-boundary-host-preflight.tsv` is the host-facing preflight for the
blocked runtime probes. It records each blocked family, the required LinuxCNC
runtime commands, the opt-in environment variable, the exact native probe
command, the current probe status, and keeps `execution_enabled=0` and
`promotion_allowed=0`. It does not run probes or relax promotion locks.
`runtime-boundary-host-requirement-summary.tsv` normalizes those preflight
requirements into one row per host command or source/module prerequisite, so
missing `halcmd`, `halrun`, `linuxcnc`, and `milltask` can be audited by the
blocked families they affect. `runtime-boundary-host-unblock-plan.tsv`
filters that table to unavailable requirements and records the affected
opt-in commands and remaining missing requirements; it is a planning artifact
only.
`runtime-boundary-family-host-readiness.tsv` reduces the same host evidence to
one row per blocked runtime family, and
`runtime-boundary-host-readiness-rollup.tsv` gives the aggregate decision. On
this host the rollup remains `host_blocked_for_all_opt_in_native_probes` with
zero ready opt-in commands. These rows are non-executing and non-promoting.
`runtime-boundary-opt-in-probe-dispatch-plan.tsv` turns each family readiness
row into a per-probe dispatch action. `runtime-boundary-opt-in-probe-dispatch-rollup.tsv`
then gives the aggregate dispatch switch; currently every blocked runtime
probe is skipped for missing host requirements. Dispatch permission only means
the guarded native probe may be manually run on a ready host, not that the row
is promoted.
`runtime-boundary-opt-in-probe-skip-evidence-contract.tsv` records why each
non-dispatched probe is skipped and whether native pass evidence is required
yet. `runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv` gives the
aggregate evidence decision. While probes are skipped for missing host
requirements, no native pass evidence is accepted and every blocked family
remains `execution_enabled=0` and `promotion_allowed=0`.
`native-runtime-probe-execution-plan.tsv` records the exact opt-in command,
required native proof key, current runtime readiness, expected pass status,
missing requirements, and promotion prerequisites for the three guarded native
runtime probes. On this host all rows remain
`plan_status=blocked_missing_host_runtime`.
`runtime-boundary-native-evidence-acceptance-gate.tsv` decides whether native
pass evidence may be accepted for each blocked runtime family. It currently
blocks evidence acceptance until host requirements are available and the
native opt-in probe passes. `runtime-boundary-promotion-blockers.tsv` expands
each non-ready promotion decision into concrete blocker keys such as missing
host runtime, native probe not passed, Node/browser gates incomplete,
promotion lock active, and manual lock update required.
`next-boundary-recommendations.tsv` is the generated priority handoff for the
next runtime boundary work. It ranks millturn user-M, tool DB, and Python
runtime lifecycle work, while keeping `execution_enabled=0` and
`promotion_allowed=0`. The Node inventory verifies the exact per-family
`source_artifacts` set, duplicate-free source lists, and `primary_artifact`
traceability for each recommendation, and requires every referenced artifact
token to exist under the generated sim-config inventory artifact directory.
The Node inventory also runs a cross-artifact guard over the blocked runtime
opt-in target set. It requires recommendations, execution plan, host preflight,
dispatch plan, skip/evidence contract, native evidence acceptance gate,
promotion readiness, promotion blockers, and post-native-pass gates to agree
on the same target, proof keys, opt-in command, missing runtime requirements,
active promotion lock, and non-executing/non-promoting state.
The browser smoke mirrors the promotion and opt-in gate consistency checks by
cross-checking the promotion lock, readiness, blocker, post-native-pass, native
evidence acceptance, pass-evidence, recommendation, host-preflight, family
readiness, dispatch, and skip-evidence artifacts. This browser-side parity is
non-executing and keeps every blocked runtime family locked until native,
Node, browser, and manual promotion gates all agree.
It also cross-checks `runtime-boundary-host-requirement-summary.tsv`,
`runtime-boundary-host-unblock-plan.tsv`,
`runtime-boundary-family-host-readiness.tsv`, and
`runtime-boundary-host-preflight.tsv` so missing and available host
requirements, affected opt-in commands, and family probe commands cannot drift
between the host-readiness artifacts.
It also checks that `boundary-phase-completion-summary.tsv` counts match the
blocked-runtime and family-specific artifact rows the browser has already
loaded, so completion criteria cannot drift from the generated gate artifacts
seen by browser smoke.
It also cross-checks the user-M, tool DB, and Python family-specific
contract/readiness/probe artifacts against `runtime-boundary-contract-summary.tsv`
so browser validation sees the same runtime-family contract alignment as the
Node inventory, without running any blocked runtime process.
It also checks `next-boundary-recommendations.tsv` row counts against the
generated user-M probe-gate, tool DB probe-gate, and Python runtime-contract
source rows, so recommendation priority handoffs cannot drift from their
machine-readable source artifacts.
It also checks each recommendation's `primary_artifact` and `source_artifacts`
against the browser-visible generated artifact set, including duplicate
detection, primary-artifact traceability, and exact per-family source artifact
sets, while keeping execution and promotion disabled. The Node coverage gate
also confirms the browser recommendation source-artifact check uses the same
`wasmArtifactNames` returned by the artifact documentation coverage helper.
It also fetches the compatibility, matrix, and full-process boundary docs in
browser smoke and checks the documented artifact names/tokens for the generated
WASM inventory TSVs and native TSVs. The same browser check fetches those TSV
artifacts, requires a non-empty TSV header, and verifies the expected
artifact/token lists are duplicate-free with fixed counts, mirroring the Node
documentation coverage gate without executing native runtime probes. The native
`build/native/sim-configs/skipped.tsv` artifact is allowed to be empty when the
native strict sim-config baseline has no skipped rows.
The same Node inventory run also checks generated artifact documentation
coverage: every TSV emitted under
`build/wasm/sim-configs-inventory/` must be named in
`compatibility-validation.md`, `sim-configs-coverage-matrix.md`, or
`full-process-boundary-design.md`. New gate artifacts must therefore be
documented before the inventory can pass.
The Node coverage gate also requires the generated WASM inventory artifact list
to remain the exact duplicate-free 54-entry baseline and the native generated
TSV token list to remain the exact duplicate-free 8-token baseline. It also
requires the corresponding `boundary-phase-completion-summary.tsv`
documentation-coverage counts to remain `54` and `8`, respectively. It also
checks that the browser smoke source lists the same generated WASM and native
artifact tokens and preserves the same fixed-count, duplicate-free,
fetchability/header, documentation-missing, and completion count-parity guards
before browser-side artifact coverage can pass. It also checks that browser
smoke actually executes the artifact documentation coverage helper, fetches
each generated WASM artifact by artifact name, fetches each native TSV by
relative artifact token, calls the completion count-parity helper with the
generated completion rows, still fetches and joins the exact compatibility,
matrix, and full-process boundary review-document set for that coverage check
before checking for missing artifact references, requires the browser
missing-reference loops to iterate `wasmArtifactNames` and
`nativeArtifactTokens` against that joined `documentationText`, keeps
completion evidence reviewable for browser count parity, binds the
documentation-coverage completion counts to the returned `wasmArtifactNames`
and `nativeArtifactTokens` arrays, and preserves the native skipped-artifact
empty allowance for `build/native/sim-configs/skipped.tsv`.
Native generated TSVs under `build/native/` are checked the same way by their
relative artifact path. This catches native baseline/source-proof/runtime-probe
artifacts that are generated but not described in the compatibility, coverage,
or full-process boundary documentation.
It also checks native source proof consistency: the native source proof
alignment rows, native runtime probe summary, and runtime probe gate alignment
must cover the same blocked classes and agree that source proof is ready while
execution and promotion remain disabled.
`user-m-process-state-targets.tsv` expands the `millturn` M128/M129 process
boundary into per-pin proof targets: two user-M codes, three axes, and four
`ini.[xyz]` HAL pins per axis. Each row records the source Tcl file, remap
caller, kinstype guard, INI source section/field, expected value,
`proof_status=pending`, `execution_enabled=0`, and `promotion_allowed=0`.
`user-m-process-native-state-alignment.tsv` then aligns each generated pin
target with the native source probe stdout key/value pair, for example
`M128_X_AXIS_X.MIN_LIMIT_ok=1`. This catches drift between generated target
rows and the LinuxCNC-owned native source proof without executing Tcl/HAL or
allowing promotion.
`user-m-process-native-transition-alignment.tsv` performs the same source
alignment for the `M428/M429` transition plan. It checks the native probe
stdout for `motion.analog-out-03`, kinstype targets `0` and `1`,
`G59.1`/`G59.2`, `P7`/`P8`, and the `M428 -> M128` / `M429 -> M129` calls.
Those rows also remain `proof_status=pending`, `execution_enabled=0`, and
`promotion_allowed=0`.
`user-m-process-native-runtime-state-plan.tsv` is the runnable native probe
contract that follows those source-alignment tables. It names the required
LinuxCNC task/HAL/Tcl user-M process runtime, the required HAL/INI
environment, and the exact `ini.[xyz].*` state values that a future native
runtime probe must record after `M428` and `M429`. It remains a plan only with
`native_runtime_status=pending_native_hal_tcl_process_probe`,
`execution_enabled=0`, and `promotion_allowed=0`.
`user-m-process-native-runtime-readiness.tsv` is the host capability gate for
that future probe. It records availability of `tclsh`, `halrun`, `halcmd`, and
`linuxcnc`, captures PATH evidence for available commands, and leaves
`proof_status=pending`, `execution_enabled=0`, and `promotion_allowed=0`.
`user-m-process-native-runtime-probe-gate.tsv` combines that readiness with the
source/state proof for `M428/M128` and `M429/M129`. The native
`probe_millturn_user_m_runtime.sh` entry point is wired through
`build_native_probes.sh`; without the full LinuxCNC HAL/Tcl command set it
reports `skipped_missing_host_runtime`, and with the runtime present it remains
disabled by default. When explicitly enabled with
`ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1`, it starts the vendored `millturn.ini`,
runs the LinuxCNC-owned Tcl `M128`/`M129` scripts, verifies the expected
`ini.[xyz].*` HAL pin state, and reports `runtime_state_probe_passed` without
enabling promotion.
`tool-db-process-protocol-gates.tsv` expands the `db_demo` tool-database
boundary into pending protocol, callback, and state gates. It records the
LinuxCNC `v2.1`, `g`, `l`, `u`, and `p` protocol requirements, the demo DB
callbacks, DB mode state targets such as ignored `TOOL_TABLE`, `T10..T19`,
`tno+100` pockets, and OPFS/host persistence boundaries, while keeping
`proof_status=pending`, `execution_enabled=0`, and `promotion_allowed=0`.
`tool-db-process-native-protocol-alignment.tsv` aligns each generated DB gate
with the native source probe stdout proof keys, including the protocol
handshake, get-all, notifications, callback registration, and nonrandom state
targets. This is still dependency/proof accounting only: it does not spawn
`DB_PROGRAM`, emulate the tool database protocol in JavaScript, or fall back to
a `.tbl` file.
`tool-db-process-native-runtime-readiness.tsv` records the host prerequisites
for the guarded DB process protocol probe: `python3`, `linuxcnc`, `milltask`,
`halcmd`, the configured `db_nonran.py` executable, and LinuxCNC's Python
`linuxcnc.so` / `tooldb.py` modules. It captures availability evidence while
keeping `proof_status=pending`, `execution_enabled=0`, and
`promotion_allowed=0`. When explicitly enabled with
`ENABLE_TOOL_DB_RUNTIME_PROBE=1`, `probe_tool_db_runtime.sh` starts the
vendored `DB_PROGRAM`, drives the LinuxCNC `tooldb.py` `v2.1`/`g`/`p`/`l`/`u`
protocol, verifies nonrandom startup/update/load/unload state and persistence,
and reports `runtime_protocol_probe_passed` without enabling promotion.
`tool-db-process-native-runtime-probe-gate.tsv` combines the DB source proof
and host readiness into the execution gate for that guarded native probe. It
records `runtime_ready`, missing requirements, source proof readiness, and the
non-promoting gate status before any manual opt-in run is allowed.
`python-remap-boundary-summary.tsv` is the machine-readable companion for the
Python remap inventory batch. It has one inventory-only row for each
`L4-PYTHON-REMAP` path, records Python modules, remap/prolog/epilog functions,
NGC-only subpaths, HAL/UI/HALUI assumptions, LinuxCNC Python runtime owner
evidence from `interp_python.cc` and `python_plugin.cc`, and keeps
`execution_enabled=0` until a LinuxCNC-owned Python runtime boundary exists.
`python-remap-runtime-gates.tsv` expands those rows into Python module,
remap/prolog/epilog callable, NGC-only subpath, process-assumption, and
runtime-owner gates, all with `proof_status=pending`,
`execution_enabled=0`, and `promotion_allowed=0`.
`python-remap-native-runtime-alignment.tsv` aligns every generated Python gate
with native source proof: exact runtime owner gates use keys such as
`python_runtime_pycall_dispatch`, while dependency gates use representative
family inventory proof or the aggregate
`python_remap_native_source_inventory_proof`. It does not initialize Python,
import modules, execute callbacks, or promote browser/Node coverage.
`python-remap-native-runtime-readiness.tsv` records the native runtime
prerequisites for guarded probes by family: `python3`, `linuxcnc`,
LinuxCNC's `interp_python.cc` / `python_plugin.cc` owner source files, and the
configured Python modules. It is a readiness gate only and keeps every row
`proof_status=pending`, `execution_enabled=0`, and `promotion_allowed=0`.
`python-remap-native-runtime-probe-gate.tsv` combines the selected Python
runtime fixture, source proof, and host readiness into the execution gate for
the guarded native lifecycle probe. It remains non-executing and
non-promoting until `linuxcnc` is available and the dispatch gate permits the
manual opt-in probe.
`python-remap-native-runtime-state-plan.tsv` records the next native runtime
probe target set by family: LinuxCNC Python phases, configured modules,
callables, NGC-only subpaths, process assumptions, readiness counts, and
source-alignment artifacts. It remains a plan only and does not initialize
Python, import modules, execute callbacks, or permit promotion.
`python-remap-native-runtime-fixture-plan.tsv` selects the first minimal
Python runtime lifecycle fixture, `axis/remap/stop-lookahead/nc_files`, because
it exercises Python runtime phases and configured modules without Python
callable or NGC-only subpath complexity. It is still a fixture plan only:
`proof_status=pending`, `execution_enabled=0`, and `promotion_allowed=0`.
When explicitly enabled with `ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1`,
`probe_python_remap_runtime.sh` follows the fixture `demo.ini` Python path and
toplevel declarations, imports the vendored modules, verifies `queuebuster`
callable lookup, generator return, and first `INTERP_EXECUTE_FINISH` yield, and
reports `runtime_lifecycle_probe_passed` without promoting Python-remap
execution.
`python-remap-family-summary.tsv` aggregates the same blocked rows by runtime
family, preserving row and INI counts plus family-level Python module,
remap/prolog/epilog, NGC-only subpath, Python runtime owner evidence,
HAL/UI/HALUI, and `execution_enabled=0` evidence. It is dependency inventory
only and must not be used to promote Python-remap execution.
`boundary-phase-completion-summary.tsv` records the current boundary-phase
completion criteria as machine-readable checks: vendored INI boundary report
coverage, hard-block dependency evidence, safe HAL/UI representative coverage,
proof that blocked families remain skipped/non-representative with execution
disabled, and proof that blocked runtime families carry LinuxCNC-owned user-M,
tool DB, and Python runtime evidence fields. It also records native source
proof alignment when the native proof summary is available, plus the aggregate
native stdout alignment summary for user-M, tool DB, and Python runtime gates.
The same completion summary now includes documentation-coverage criteria for
generated WASM sim-config inventory TSV artifacts and native generated TSV
artifacts, so artifact generation and review documentation stay in lockstep.
It also exposes the blocked runtime worklist/recommendation consistency guard,
the blocked runtime promotion gate consistency guard, the blocked runtime host
requirement consistency guard, the blocked runtime host/dispatch/skip-evidence
rollup consistency guard, the blocked runtime opt-in cross-artifact consistency
guard, the native source proof/runtime probe consistency guard, and the runtime
family contract/alignment consistency guard as completion criteria, so review
can see those gates without reopening every individual TSV. The family
contract/alignment guard checks that the user-M, tool DB, and Python-specific
contract, readiness, probe-gate, and fixture/state-plan artifacts agree with
`runtime-boundary-contract-summary.tsv` and
`runtime-boundary-native-alignment-summary.tsv` while keeping execution and
promotion disabled.
The browser smoke consumes the same `boundary-phase-completion-summary.tsv`
criterion list and fails on criterion drift. This is a browser-side parity
check for generated gate artifacts only; it does not execute blocked native
runtime probes or allow promotion.
The browser smoke also checks that `runtime-boundary-contract-summary.tsv`
points at the same runtime alignment artifact, row count, boundary kind, and
disabled execution/promotion state reported by
`runtime-boundary-native-alignment-summary.tsv`.
It also cross-checks `native-proof-alignment-summary.tsv`,
`native-runtime-probe-summary.tsv`, and `runtime-probe-gate-alignment.tsv` so
browser validation sees the same source-proof readiness, runtime readiness,
required native proof, target, and disabled execution/promotion state as the
Node inventory.
The browser smoke also cross-checks `native-runtime-probe-execution-plan.tsv`,
`native-runtime-probe-pass-evidence-contract.tsv`,
`runtime-boundary-host-preflight.tsv`,
`runtime-boundary-opt-in-probe-dispatch-plan.tsv`, and
`runtime-boundary-opt-in-probe-skip-evidence-contract.tsv`, keeping the
current host-blocked probe status, missing requirements, opt-in command,
evidence status, and disabled execution/promotion state aligned.
It also checks the host requirement summary and unblock plan against family
host-readiness and preflight rows, including unavailable requirement
ownership, available prerequisite ownership, opt-in environment variables, and
execution command traceability.
It also cross-checks `runtime-boundary-host-readiness-rollup.tsv`,
`runtime-boundary-opt-in-probe-dispatch-rollup.tsv`, and
`runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv` for family/probe
counts, blocked families, missing requirements, blocked opt-in commands,
host/dispatch/evidence status, and disabled execution/promotion parity.
`native-proof-alignment-summary.tsv` aligns native source proof rows with the
generated worklist and native proof gates for user-M, tool DB, and Python
runtime blockers. It is proof-consumption accounting only and keeps execution
and promotion disabled.
`runtime-boundary-native-alignment-summary.tsv` summarizes the three detailed
native alignment artifacts. It requires every alignment row to have native
stdout evidence, `alignment_ok=1`, `proof_status=pending`,
`execution_enabled=0`, and `promotion_allowed=0`.
`blocked-runtime-promotion-lock.tsv` combines the next-boundary worklist,
native/Node/browser proof gates, and runtime native alignment summary into one
promotion lock per blocked runtime target. A lock row is active only while
every proof layer is still pending, execution and promotion remain disabled,
and the matching runtime alignment artifact is complete.
`next-boundary-worklist.tsv` records the next blocked runtime-boundary design
targets in priority order. It starts with the designed-but-disabled
`millturn` external user-M process and `db_demo` tool database process
boundaries, then lists Python runtime families from the inventory. Every row
keeps `execution_enabled=0` and `promotion_allowed=0`, records the LinuxCNC
owner set and runtime owner evidence, records required native, Node, and
browser proof, and names the next boundary-design action before promotion.
`boundary-proof-gates.tsv` expands that worklist into one pending native,
Node, and browser proof gate per target. It is a promotion guard only: current
rows keep `proof_status=pending`, `execution_enabled=0`, and
`promotion_allowed=0`; user-M gates must require HAL pin state rather than
event-only proof, tool-DB gates must require protocol proof rather than a
`.tbl` fallback, and Python gates must require a LinuxCNC-owned Python runtime
boundary rather than JavaScript semantics.
The browser interpreter smoke reads the browser-layer rows from this artifact
before running safe representatives, so `millturn`, `db_demo`, and Python
runtime families cannot be accidentally treated as browser/full-process
coverage while their proof gates remain pending.

Current `configs/sim` class taxonomy:

| Class | Meaning | Current policy |
| --- | --- | --- |
| `main` | A complete standalone-executable sim-config program. | Eligible for native inventory; promote to Layer 3/4 when the required runtime boundary exists. |
| `macro_load` | A macro or load/parse asset that is not the primary machine program entry point. | Keep in native inventory, but do not treat it as a browser main-program target by default. Add class-level load/parse representatives instead of widening browser execution blindly. |
| `remap_subroutine` | A remap or subroutine asset under `remap_subs/` or similar directories. | Validate through remap parse/execute paths, not by pretending it is a standalone browser main program. |

Current `configs/sim` blocked policy:

| Blocked kind | Meaning | Current examples |
| --- | --- | --- |
| `ASSET-ONLY` | The file is a macro/remap asset or subroutine and is not a standalone browser main-program target. | `configs/sim/*/remap_subs/*.ngc` entries in the matrix. |
| `L4-TOOL-DB` | Native LinuxCNC `rs274` coverage exists, but Node/browser inventory is blocked by LinuxCNC tool-database process boundaries such as `[EMCIO]DB_PROGRAM`. | `configs/sim/axis/db_demo/*`. |
| `L4-USER-M-PROCESS` | Native LinuxCNC `rs274` coverage exists, but Node/browser inventory is blocked because the config depends on external `USER_M_PATH` process execution rather than the deterministic `M110`/`M111` boundary already modeled by the standalone runtime. | `configs/sim/axis/vismach/millturn/*`. |
| `L4-PYTHON-REMAP` | Native LinuxCNC `rs274` coverage exists, but full Node/browser inventory is blocked until an intentional Python-remap runtime boundary is exposed for Layer 4. | `configs/sim/gmoccapy/*`, `configs/sim/axis/laser/*`, `configs/sim/axis/remap/*/nc_files/*.ngc`, `configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/*`, and `configs/sim/axis/vismach/VMC_toolchange/toolchange.ngc`. |
| `UPSTREAM-DEMO` | A preserved upstream demo edge that should remain an expected failure instead of being forced through standalone semantics. | `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc`. |

| Program | Layer 2 classification | Current Layer 3/4 coverage or boundary |
| --- | --- | --- |
| `axis/foam/foam.ngc` | `ini-axis-mask-UV`; native upstream `bin/rs274` rejects `U`/`V` words before the standalone runtime applies the INI machine axis mask. | Covered by `wasm-port/tests/native/verify_native_probes.sh`, `wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh`, and `wasm-port/tests/browser/verify_interp_browser.sh`, which run the vendored file with `axis_foam.ini` and verify `U/V` axis acceptance through LinuxCNC-backed file execution. |
| `axis/vismach/5axis/bridgemill/5axisgui.ngc` | `ini-axis-mask-W`; native upstream `bin/rs274` rejects the `W` word outside the bridge-mill runtime context. | Covered by `wasm-port/tests/native/verify_native_probes.sh`, `wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh`, and `wasm-port/tests/browser/verify_interp_browser.sh`, which stage `5axis.ini`, the tool table, and remap subroutines, then execute through the vendored LinuxCNC remap/file path. |
| `axis/geometry/xyzc.ngc` | `user-m-code-M110`; native upstream `bin/rs274` does not register or execute the sim-config `USER_M_PATH` handler. | Covered by `wasm-port/tests/native/verify_native_probes.sh`, `wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh`, and `wasm-port/tests/browser/verify_interp_browser.sh`; the smoke assertions require `canon_event=USER_M_COMMAND code=M110` and reject `Unknown m code used: M110`. |
| `axis/external_offsets/dyn_demo.ngc` | `user-m-code-M111`; native upstream `bin/rs274` does not register or execute the sim-config `USER_M_PATH` handler. | Covered by `wasm-port/tests/native/verify_native_probes.sh`, `wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh`, and `wasm-port/tests/browser/verify_interp_browser.sh`; the smoke assertions require `canon_event=USER_M_COMMAND code=M111` and reject `Unknown m code used: M111`. |
| `axis/external_offsets/eoffsets.ngc` | `user-m-code-M111`; same standalone task/user-M boundary as `dyn_demo.ngc`. | Covered by `wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh` and `wasm-port/tests/browser/verify_interp_browser.sh` with the vendored executable `M111` and `eoffset.tbl` staged from the manifest. |
| `axis/external_offsets/jwp_z.ngc` | `user-m-code-M111`; same standalone task/user-M boundary as `dyn_demo.ngc`. | Covered by `wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh` and `wasm-port/tests/browser/verify_interp_browser.sh` with the vendored executable `M111` and `eoffset.tbl` staged from the manifest. |
| `axis/external_offsets/opa_demo.ngc` | `user-m-code-M111`; same standalone task/user-M boundary as `dyn_demo.ngc`, plus a subroutine dependency. | Covered by `wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh` and `wasm-port/tests/browser/verify_interp_browser.sh`, which stage `M111`, `eoffset.tbl`, and the `SUBROUTINE_PATH` dependency `circles.ngc`. |
| `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc` | `upstream-demo-missing-motion-gcode`; upstream standalone `rs274` reports `Cannot use axis values without a g code that uses them`. | Preserved as an upstream demo expected failure. Do not make this pass by editing the G-code, changing JS interpreter behavior, or adding standalone semantics. |

## Validation Chain

The native validation script runs these checks in order:

1. `tools/verify_upstream_baseline.sh`
   Confirms `../linuxcnc` is at the recorded upstream commit in
   `tools/upstream-baseline.txt`.
2. `tools/verify_vendor_sync.sh`
   Confirms every manifest file is present in `vendor/linuxcnc/`, no extra
   vendored file exists, and every vendored file is byte-identical to upstream.
3. `tools/verify_no_standalone_cnc_semantics.sh`
   Confirms standalone code has not introduced project-owned `Interp::...`
   member definitions outside the documented Python/remap runtime-edge stubs.
4. `tools/verify_native_linuxcnc_fixture_baseline.sh`
   Runs a side-by-side fixture baseline through upstream
   `../linuxcnc/bin/rs274` and compares normalized canonical events for
   fixtures that do not require standalone-only runtime adapters.
5. `tools/build_native_probes.sh`
   Builds native source probes and standalone harnesses from vendored
   LinuxCNC source plus narrow runtime wrappers.
6. `tests/native/verify_sim_configs.sh`
   Runs LinuxCNC `configs/sim` `.ngc` programs through the upstream
   `../linuxcnc/bin/rs274` standalone entry point, using nearest or explicit
   sim INI/tool-table mappings and classifying main programs, macro-load
   checks, and remap subroutines separately.
7. `tests/native/verify_nc_files.sh`
   Runs the basic/example subset of upstream `linuxcnc/nc_files` through
   `../linuxcnc/bin/rs274`, with complete programs executed strictly and
   macro/library files wrapped only for load/parse validation.
8. `tests/native/verify_native_probes.sh`
   Checks probe exit codes, source-probe coverage, harness stdout, canonical
   fixture events, and expected error behavior.

Current `nc_files` basic-suite baseline:

```text
total: 107
pass: 101
fail: 6
timeout: 0
expected_fail: 6
unexpected_fail: 0
```

The six expected failures are LinuxCNC-native entry-point/context edges:
`cone.ngc` needs a W-axis 5-axis machine context; `g76.ngc` and
`lathe-g76.ngc` need lathe cutter-compensation/tool context;
`lathe_g70_71_demo.ngc` needs lathe profile/tool context; `nestedcall.ngc`
uses an upstream O-word `callsub` syntax edge; and `tool-length-probe.ngc`
needs probe runtime context.

The same runner also supports exploratory full-directory inventory:

```bash
wasm-port/tests/native/verify_nc_files.sh --all
```

Current full-directory `nc_files` inventory:

```text
total: 247
pass: 219
fail: 28
timeout: 0
expected_fail: 28
unexpected_fail: 0
```

The additional expected failures are probe/plasmac runtime-context files and
one NURBS sample using `G2.2`, which the current upstream `bin/rs274`
standalone entry point reports as `Unknown g code used`.

The WASM INI smoke script builds `runtime/ui/ini-panel/linuxcnc_ini.js` and
`linuxcnc_ini.wasm` from vendored LinuxCNC `inifile.cc`, then loads that
module through `runtime/sdk/src/index.js` in Node and verifies INI string and
boolean queries against a file written to the Emscripten filesystem. Boolean
conversion is validated through vendored LinuxCNC `iniFindBool()`, and
machine-session file-name lookup is validated through LinuxCNC string queries
for `[RS274NGC]PARAMETER_FILE` and `[EMCIO]TOOL_TABLE`.

The WASM interpreter-core smoke script builds
`build/wasm/core/linuxcnc_interp.js` and `linuxcnc_interp.wasm` from the same
vendored LinuxCNC interpreter source set used by the native minimal
interpreter harness. It loads the module in Node through
`runtime/sdk/src/index.js`, runs the first WASM interpreter fixture
group through `Interp::execute()`, and compares emitted canonical events plus
required LinuxCNC `_setup` state readback with the matching files in
`tests/fixtures/canon/`. It also calls vendored `Interp::init()` and
`Interp::synch()` through the WASM C ABI to validate the initialization
canonical boundary, metric/inch machine-unit status edge, and current/selected
tool slot status synchronization already covered by the native init harness.
It also calls the vendored single-axis rotary indexer path through
`Interp::execute()` to validate the `UNLOCK_ROTARY`/`LOCK_ROTARY` canonical
runtime boundary already covered by the native indexer harness.
It also calls vendored `Interp::init_named_parameters()` and
`Interp::find_named_param()` through the WASM C ABI to validate the native
named-parameter harness path for LinuxCNC built-in, INI-backed, HAL-backed,
and missing named-parameter lookup.
It also writes selected G-code fixtures and vendored `tests/interp/g10`
regressions into the Emscripten filesystem through the SDK and runs them
through LinuxCNC `Interp::open()`, `Interp::read()`, and `Interp::execute()`
to validate the file execution path, including G10 tool-table updates,
tool-length offset application timing, active G5X offset changes, G92
interaction, XY-rotation behavior, G52/G92 shared-offset transitions, and
G92 startup parameter-file persistence through vendored
`ini_load()`/`Interp::init()`/`restore_parameters()`, including
`DISABLE_G92_PERSISTENCE` clearing parameters 5210-5219. It also writes the vendored LinuxCNC `xyzac-trt` and
`xyzbc-trt` table-rotary-tilting sample machine INI, `remap_subs/*.ngc`, and
`xyzac_switchkins.ngc`/`xyzbc_switchkins.ngc`,
`xyzac_switchkins_test_1.ngc`, `xyzac_switchkins_test_2.ngc`,
`xyzac_switchkins_test_3.ngc`, `boat-xyzac.ngc`, `boat-xyzbc.ngc`, and
`impeller-7bl-xyzac.ngc` demo files into the Emscripten
filesystem. It also writes the vendored LinuxCNC `xyzab-tdr` table-dual-rotary
INI, tool table, `remap_subs/*.ngc`, and `xyzab-tdr-demo.ngc` into the
Emscripten filesystem. It also writes the vendored LinuxCNC bridge-mill
`5axis.ini`, tool table, `remap_subs/*.ngc`, and `5axisgui.ngc` into the
Emscripten filesystem. These checks validate the WASM C ABI/SDK path for
vendored LinuxCNC `REMAP` parsing, vendored LinuxCNC tool-table loading from
the machine INI, O-word remap execution, `M68`/`M66` HAL synchronization, and
file `open()`/`read()`/`execute()` completion without JavaScript M-code or
kinematics semantics. The same Node smoke writes the vendored LinuxCNC
`tests/remap/duplicate-o-word`, NGC-only `tests/remap/fail/args.0`,
`tests/remap/fail/args.1`, `tests/remap/fail/args.2`,
`tests/remap/fail/body-ngc`, `tests/remap/m30-interaction`,
`tests/remap/nested-remaps-oword`, `tests/remap/posargs.0`, and
`tests/remap/sequencing` INI, program, and remap subroutines into the
Emscripten filesystem and validates those
upstream NGC remap regressions through the generic `runRemapFile()` C ABI/SDK
path, which only reads LinuxCNC INI `SUBROUTINE_PATH`, `REMAP`, and
`OWORD_NARGS` entries and calls vendored `Interp::parse_remap()`, `open()`,
`read()`, and `execute()`. The NGC-only failure cases that match upstream
`rs274 -n 0` flow use the sibling `runRemapFileContinueOnError()` C ABI/SDK
path, which records LinuxCNC error text and continues the same vendored
`open()`/`read()`/`execute()` loop without implementing failure semantics in
JavaScript. The same Node smoke also writes the NGC-only LinuxCNC
`tests/remap/remap-io/test-ngc.ini` and `io_*.ngc` subroutines into the
Emscripten filesystem, then calls `runRemapIoMdiSequence()` to feed the
upstream test-driver MDI sequence into vendored `Interp::execute()`. The SDK
does not implement M62-M68, M66 input, or REMAP semantics; the standalone
boundary only pre-seeds deterministic external input values and captures
LinuxCNC canonical events. The same WASM interpreter smoke also writes minimal `M110` and
`M111` fixtures into the Emscripten filesystem, marks the `M1xx` files
executable, and validates that the standalone machine-config boundary mirrors
LinuxCNC task-layer `[DISPLAY]PROGRAM_PREFIX` plus `[RS274NGC]USER_M_PATH`
lookup by registering `USER_DEFINED_FUNCTION` entries and recording
deterministic `USER_M_COMMAND` events instead of spawning host processes. The
same Node smoke writes the vendored LinuxCNC
`tests/interp/do-while-break`, `tests/interp/oword-bug315`,
`tests/interp/oword-bug315-p2`, `tests/interp/exists`,
`tests/interp/return-value`, `tests/interp/subs-follow-main`,
`tests/interp/fractional-linenumbers`, `tests/interp/cam-nisley` with its
upstream `test.tbl`, `tests/interp/namedparam-bug424`,
selected `tests/interp/rotation` pure interpreter cases, `tests/interp/iniparam`,
`tests/interp/iniparam-failassign`,
`tests/interp/sub-call-from-sub`, `tests/interp/sequence-number`, and
`tests/interp/nested-sub-error`, `tests/interp/nested-sub-in-file-error`, and
`tests/interp/abort-hot-comment`
files into the Emscripten filesystem and validates those upstream interpreter
regressions through `runFile()` or
`runFileWithIni()`, which only reads LinuxCNC INI `SUBROUTINE_PATH` where
needed and calls vendored LinuxCNC `Interp::open()`, `read()`, and `execute()`.
The `iniparam` fixtures validate vendored LinuxCNC `_ini[...]` lookup, missing
INI-parameter error text, and read-only named-parameter assignment rejection
through the `INI_FILE_NAME` runtime edge. It checks LinuxCNC canonical messages,
canonical motion/events, and final interpreter state without JavaScript O-word,
subroutine lookup, INI-variable, or read-only-parameter semantics. The
same Node smoke writes
LinuxCNC-format parameter files into the Emscripten filesystem and validates
vendored `Interp::restore_parameters()` and `Interp::save_parameters()`,
including the saved parameter values, missing required numeric parameter
defaulting, and the `.bak` backup file boundary. It also writes the
negative G-code fixtures into the Emscripten filesystem and verifies their
LinuxCNC-produced error text through the `Interp::open()`/`read()`/`execute()`
file path.

The WASM trajectory-planner smoke script builds
`build/wasm/tp/linuxcnc_tp.js` and `linuxcnc_tp.wasm` from vendored LinuxCNC
TP, TC, TC queue, spherical-arc, blendmath, S-curve, Ruckig-wrapper, C Ruckig
support, emcpose, and posemath source files. It loads the module in Node and
calls the exported TP probe C ABI to validate the same LinuxCNC linear, arc,
and queued-line planner calls covered by the native `linuxcnc_tp_api_probe`.

The OPFS host-boundary script validates the JavaScript file-service adapter
with a Node mock of the browser File System Access handles. It covers nested
directory creation, text save/load, missing file behavior, invalid relative
paths, unavailable OPFS storage, and the host-side OPFS path model for INI,
tool table, parameter, G-code, preview-cache, and session-snapshot storage
targets. It also validates the host-side session snapshot JSON envelope and
round-trip store, including unsupported format/version, session-id mismatch,
non-object metadata/payload rejection, custom snapshot filenames, and invalid
snapshot filename rejection, plus pure-text machine file and G-code stores,
including G-code program filename rejection for traversal or nested paths,
without defining CNC machine-state or file-format semantics. It now also validates the
OPFS-to-WASM parameter-file bridge with a mock interpreter SDK to ensure the
host boundary copies text into and out of the WASM filesystem without defining
parameter semantics, and that INI-derived machine file names are still
rejected by the OPFS path model when they contain traversal or nested path
segments. The same Node smoke also validates that explicit session file-name
options take precedence over INI-derived parameter and tool-table file names,
and that missing INI file-name values fall back to the host path model defaults,
keeping host override policy outside CNC semantics.

The browser INI/OPFS smoke script serves `wasm-port/` over localhost and runs
Chromium headless against a test page that imports the JS SDK, loads the INI
WASM module, queries vendored LinuxCNC INI parsing through the SDK, including
machine-session parameter/tool-table file-name strings, and performs an OPFS
text-file, generic session snapshot, custom snapshot filename, invalid
snapshot filename/envelope, machine file, G-code text round trip, and G-code
filename path-model rejection. It also verifies that the INI panel UI exposes
LinuxCNC-backed `[RS274NGC]PARAMETER_FILE` and `[EMCIO]TOOL_TABLE` query
results, plus the default OPFS parameter-file and tool-table mappings used
when a machine session is copied into the interpreter WASM filesystem. It also
clicks the INI panel's 5-axis remap demo action and verifies that the UI
copies vendored LinuxCNC `xyzac-trt` machine/remap/demo files into the
interpreter WASM filesystem, calls the LinuxCNC-backed SDK remap execution
path for the vendored `impeller-7bl-xyzac.ngc` demo, and displays the
resulting `fiveaxis_*` status lines without JavaScript M-code or kinematics
semantics.

The browser interpreter smoke script serves `wasm-port/` over localhost and
runs Chromium headless against a test page that loads the interpreter-core
WASM module through `runtime/sdk/src/index.js`, writes no CNC
behavior in JavaScript, and verifies existing canonical fixtures through the
exported C ABI backed by vendored LinuxCNC `Interp::execute()` and
`Interp::open()`/`read()`/`execute()` paths, including the INI-aware
named-parameter file path and negative interpreter fixtures with expected
error text plus absent canonical motion output. It also validates vendored
`Interp::init()` and `Interp::synch()` through the same SDK/C ABI path,
including initialization canonical events, metric/inch machine units, and tool
slot readback, and checks the vendored rotary-indexer `G0 A...` execution path
for `UNLOCK_ROTARY`/`LOCK_ROTARY` boundary events. It also validates
`Interp::init_named_parameters()` and `Interp::find_named_param()` through the
same browser SDK/C ABI path for built-in, INI-backed, HAL-backed, and missing
named-parameter lookup. It also directly checks browser SDK parameter-file
restore/save through vendored `Interp::restore_parameters()` and
`Interp::save_parameters()`, including the LinuxCNC-saved text and `.bak`
backup, and directly checks browser SDK non-random/random tool-table
load/save through vendored `tooldata_common.cc`. The same browser smoke uses
real browser OPFS storage plus the interpreter SDK to restore/save LinuxCNC
parameter files, directly checks missing-file success plus out-of-order
parameter-file rejection through the same LinuxCNC `restore_parameters()` C
ABI, and checks the random-toolchanger `tooldata_save()` result before OPFS
text writeback,
to reject invalid INI-derived parameter/tool-table file names through the OPFS
path model after the names are parsed by the LinuxCNC-backed INI WASM SDK. It
also verifies that explicit session parameter/tool-table file-name options
override INI-derived names before OPFS text is copied into the LinuxCNC-backed
WASM filesystem, and that absent INI file-name values use the default OPFS
parameter/tool-table paths in a real browser session. It also writes vendored
LinuxCNC `xyzac-trt`/`xyzbc-trt` INI, remap subroutines, and switchkins demo
files, including `xyzac_switchkins_test_1.ngc`,
`xyzac_switchkins_test_2.ngc`, `xyzac_switchkins_test_3.ngc`, and the larger
`boat-xyzac.ngc`, `boat-xyzbc.ngc`, and `impeller-7bl-xyzac.ngc` demos, into
the browser WASM filesystem. It also
writes vendored LinuxCNC `xyzab-tdr` machine files and `xyzab-tdr-demo.ngc`
into the browser WASM filesystem. It also writes vendored LinuxCNC bridge-mill
machine files and `5axisgui.ngc` into the browser WASM filesystem. The browser
smoke verifies `runFiveAxisRemapFile()` for the table-rotary-tilting,
table-dual-rotary, and bridge-mill sample machines through the exported
LinuxCNC remap/tool-table/file execution path. It also verifies the vendored
LinuxCNC `configs/sim/axis/foam/foam.ngc`, `axis/geometry/xyzc.ngc`, and
`axis/external_offsets/dyn_demo.ngc` programs through `runSimConfigProgram()`,
which writes the browser WASM filesystem files and forwards to
`runFileWithIni()`. It also verifies the bridge-mill
`axis/vismach/5axis/bridgemill/5axisgui.ngc` program through
`runSimConfigProgram()` with `executionMode: "fiveAxisRemap"`, which forwards
to `runFiveAxisRemapFile()`. These paths use the real vendored executable
`M110` and `M111` files for `USER_M_PATH` registration without spawning host
processes. It
also verifies the vendored
LinuxCNC `tests/remap/duplicate-o-word`, NGC-only `tests/remap/fail/args.0`,
`tests/remap/fail/args.1`, `tests/remap/fail/args.2`,
`tests/remap/fail/body-ngc`, `tests/remap/m30-interaction`,
`tests/remap/nested-remaps-oword`, `tests/remap/posargs.0`, and
`tests/remap/sequencing` regressions through `runRemapFile()` or
`runRemapFileContinueOnError()`, using the browser WASM filesystem and
vendored LinuxCNC REMAP/O-word/file execution path without JavaScript remap
semantics. It also writes the NGC-only vendored LinuxCNC
`tests/remap/remap-io/test-ngc.ini` and `io_*.ngc` subroutines into the
browser WASM filesystem and validates `runRemapIoMdiSequence()` through
vendored LinuxCNC REMAP parsing and MDI execution without JavaScript M-code or
I/O semantics. It also writes vendored LinuxCNC `tests/interp/do-while-break`,
`tests/interp/oword-bug315`, `tests/interp/oword-bug315-p2`,
`tests/interp/exists`, `tests/interp/return-value`,
`tests/interp/subs-follow-main`, `tests/interp/fractional-linenumbers`,
`tests/interp/cam-nisley`, `tests/interp/namedparam-bug424`,
`tests/interp/inside-corners`,
`tests/interp/inverse-time-with-comp`, and selected `tests/interp/rotation`
`.ngc` files plus
`tests/interp/sub-call-from-sub` `test.ini`, `test.ngc`, and `subs/*.ngc`
files and `tests/interp/sequence-number` `test.ini`, `test.ngc`, and
`rm400.ngc` plus `tests/interp/nested-sub-error` `test.ini`, `test.ngc`, and
`subs/nested.ngc` and `tests/interp/nested-sub-in-file-error` `test.ini`,
`test.ngc`, and `subs/sequential.ngc`, plus `tests/interp/abort-hot-comment`
`test.ini` and `test.ngc`, plus `tests/interp/m19` `test.ini` and `test.ngc`,
plus `tests/interp/magic_comments/param_format_printing` `test.ngc`,
plus selected pure-interpreter `tests/interp/m98m99` cases covering
Fanuc-style `M98/M99`, missing-P-word, missing-subprogram, mixed
Fanuc/RS274NGC sub-style, and `DISABLE_FANUC_STYLE_SUB` INI-gated errors,
main-program O-word termination rules, parameter scope, loop counts including
`L0`, nested numbered subprograms, subprograms after main program text,
leading-zero O-word lookup, named and numbered main programs, and
expression-based O-sub/M98 calls,
into the browser WASM filesystem and
validates those upstream
interpreter regressions through `runFile()` or `runFileWithIni()`, without
JavaScript O-word, parameter, line-number, spindle-speed, spindle-orient,
magic-comment formatting, M98/M99, subroutine lookup, or branch semantics.

The aggregate host smoke script builds the INI, interpreter-core, and
trajectory-planner WASM artifacts once, then runs the Node WASM smokes, the
Node OPFS mock smoke, and the Chromium browser smokes.

## Source Coverage

Every `.c` and `.cc` entry in `tools/source-manifest.txt` must have a
corresponding `*_source_probe` entry in `build/native/source-probes.tsv`.
The validation fails if:

- a manifest source file lacks a source probe;
- a source probe references a file not listed in the manifest;
- a manifest file is duplicated;
- vendored files drift byte-for-byte from upstream LinuxCNC.

## Current Native Harnesses

| Harness | Purpose |
| --- | --- |
| `linuxcnc_ini_probe` | Validates vendored LinuxCNC INI parsing can be used standalone. |
| `linuxcnc_interp_state_probe` | Validates interpreter state constants and structs compile under the standalone boundary. |
| `linuxcnc_rs274_compile_probe` | Validates the upstream `tests/interp/compile` class remains a source/compile boundary rather than being misclassified as a runtime `.ngc` interpreter fixture. |
| `linuxcnc_emc_status_probe` | Validates the standalone `emcStatus` machine-units status boundary used by vendored interpreter conversion and initialization code. |
| `linuxcnc_namedparam_harness` | Validates LinuxCNC named parameter behavior, `_ini[...]`, and `_hal[...]` adapter resolution. |
| `linuxcnc_interp_minimal_harness` | Runs G-code fixtures through vendored LinuxCNC parser/execution/conversion code, captures canonical events, and validates feed-rate state readback across length-unit changes. |
| `linuxcnc_parameter_file_harness` | Validates LinuxCNC parameter file restore/save behavior and required/read-only parameter handling. |
| `linuxcnc_interp_init_harness` | Validates vendored LinuxCNC `Interp::init()` emits canonical initialization boundaries, reads metric/inch machine units, and synchronizes current/selected tool slots through standalone status adapters. |
| `linuxcnc_indexer_harness` | Validates vendored LinuxCNC single-axis rotary indexer dispatch emits lock/unlock and motion boundaries through the standalone event sink. |
| `linuxcnc_remap_hal_sync_harness` | Validates vendored LinuxCNC `M68`/`M66` execution can drive the standalone HAL adapter boundary used by 5-axis switchkins remap files, including `_hal[motion.switchkins-type]` readback. |
| `linuxcnc_5axis_remap_execute_harness` | Validates vendored LinuxCNC `REMAP` parsing plus NGC remap execution for the 5-axis `M429 -> M428 -> M430 -> M429` switchkins path in the `xyzac-trt` and `xyzbc-trt` sample machines, the two-remap `M429 -> M428 -> M429` path in the `xyzab-tdr` sample machine, and the bridge-mill `M429 -> M428 -> M430 -> M429` path where M428 selects the default bridge-mill kinematics. It loads vendored machine tool tables through LinuxCNC `tooldata_load()`, then runs vendored `xyzac_switchkins.ngc`, `xyzbc_switchkins.ngc`, `xyzac_switchkins_test_1.ngc`, `xyzac_switchkins_test_2.ngc`, `xyzac_switchkins_test_3.ngc`, `boat-xyzac.ngc`, `boat-xyzbc.ngc`, `impeller-7bl-xyzac.ngc`, `xyzab-tdr-demo.ngc`, and `5axisgui.ngc` through the LinuxCNC file `open/read/execute` path. |
| `linuxcnc_duplicate_oword_remap_harness` | Validates vendored LinuxCNC upstream `tests/remap/duplicate-o-word`, NGC-only `tests/remap/fail/args.0`, `tests/remap/fail/args.1`, `tests/remap/fail/args.2`, `tests/remap/fail/body-ngc`, `tests/remap/m30-interaction`, `tests/remap/nested-remaps-oword`, `tests/remap/posargs.0`, `tests/remap/sequencing`, and the NGC-only `tests/remap/remap-io/test-ngc.ini` branch through LinuxCNC `REMAP` parsing, O-word remap dispatch, `OWORD_NARGS`, error-text reporting, G/M remap sequencing, remapped M62-M68/M66 MDI execution, and file `open/read/execute`. The standalone harness only supplies INI/file path setup, deterministic external input values, and canonical-event capture; it can continue after LinuxCNC errors for upstream `rs274 -n 0` style tests and does not implement duplicate-label, O-word, M30, positional-argument, failure, sequencing, M62-M68, M66 input, or remap semantics. |
| `linuxcnc_tp_api_probe` | Validates vendored LinuxCNC trajectory planner calls for linear, arc, and queued motion paths. |
| `linuxcnc_kinematics_probe` | Validates vendored LinuxCNC `trivkins.c` plus `kins_util.c` initialize and perform identity forward/inverse mapping through the standalone HAL/RTAPI boundary. |
| `linuxcnc_5axis_kinematics_probe` | Validates vendored LinuxCNC `5axiskins.c` through `switchkins.c`, including 5-axis forward/inverse round-trip behavior and switching to identity kinematics. |
| `linuxcnc_xyzac_trt_kinematics_probe` | Validates vendored LinuxCNC XYZAC TRT kinematics through `switchkins.c`, including forward/inverse round-trip behavior and switching to identity kinematics. |
| `linuxcnc_xyzbc_trt_kinematics_probe` | Validates vendored LinuxCNC XYZBC TRT kinematics through `switchkins.c`, including forward/inverse round-trip behavior and switching to identity kinematics. |
| `linuxcnc_corexy_kinematics_probe` | Validates vendored LinuxCNC CoreXY forward/inverse behavior through the standalone HAL/RTAPI boundary. |
| `linuxcnc_rotate_kinematics_probe` | Validates vendored LinuxCNC rotated-axis forward/inverse behavior. |
| `linuxcnc_rose_kinematics_probe` | Validates vendored LinuxCNC rose kinematics forward/inverse behavior. |
| `linuxcnc_max_kinematics_probe` | Validates vendored LinuxCNC max kinematics forward/inverse behavior. |
| `linuxcnc_lineardelta_kinematics_probe` | Validates vendored LinuxCNC linear-delta inverse/forward pose round-trip behavior. |
| `linuxcnc_rotarydelta_kinematics_probe` | Validates vendored LinuxCNC rotary-delta inverse/forward pose round-trip behavior. |
| `linuxcnc_scorbot_kinematics_probe` | Validates vendored LinuxCNC Scorbot forward/inverse behavior and pose round-trip behavior. |
| `linuxcnc_tripod_kinematics_probe` | Validates vendored LinuxCNC tripod inverse/forward behavior, including below-platform flag behavior. |
| `linuxcnc_scara_kinematics_probe` | Validates vendored LinuxCNC SCARA forward/inverse behavior and switching to identity kinematics. |
| `linuxcnc_puma_kinematics_probe` | Validates vendored LinuxCNC PUMA forward/inverse behavior, pose round-trip behavior, and switching to identity kinematics. |
| `linuxcnc_genser_kinematics_probe` | Validates vendored LinuxCNC generic serial kinematics forward/inverse behavior and switching to identity kinematics. |
| `linuxcnc_genhex_kinematics_probe` | Validates vendored LinuxCNC generic hexapod inverse/forward behavior, including the switchkins iterative-forward warmup path. |
| `linuxcnc_pentakins_kinematics_probe` | Validates vendored LinuxCNC pentapod inverse/forward pose round-trip behavior. |

## Current WASM Harnesses

| Harness | Purpose |
| --- | --- |
| `tests/wasm/node/verify_ini_wasm.sh` | Validates the browser-facing INI WASM module can be built from vendored LinuxCNC `inifile.cc`, loaded through the JS SDK in Node, and queried through the exported C ABI, including LinuxCNC-backed boolean conversion and machine-session file-name string lookup. |
| `tests/wasm/node/verify_interp_wasm.sh` | Validates the interpreter-core WASM module can be built from vendored LinuxCNC interpreter/remap source, loaded through the interpreter JS SDK, run the first fixture group through `Interp::execute()` and selected file fixtures plus vendored upstream `tests/interp` regression files through `Interp::open()`/`read()`/`execute()`, match the native canonical event plus required state readback fixtures, emit `run_step` execution-status records with LinuxCNC line number, encoded source statement, return code, and interpreter axis positions for file execution, cover vendored `tests/interp/flowsnake` recursive O-word file execution, `tests/interp/g6164` path-control and naive-cam tolerance execution, `tests/interp/oword-unwind` continue-on-error stack unwind behavior through planner-staged INI context, selected `tests/interp/bad` file-error paths, and `tests/interp/g33.1` rigid-tap file execution, run vendored `xyzac-trt`/`xyzbc-trt` table-rotary-tilting and `xyzab-tdr` table-dual-rotary switchkins remap demo files through the WASM C ABI/SDK path, and run parameter-file restore/save through vendored LinuxCNC `Interp::restore_parameters()` and `Interp::save_parameters()`. |
| `tests/wasm/node/verify_sim_configs_wasm.sh` | Validates representative vendored LinuxCNC `configs/sim` programs in Node WASM through `planSimConfigStaging()` plus `runSimConfigProgram()`. The generic `planIniFileContextStaging()` planner and its sim-config wrapper use INI text and `tools/source-manifest.txt` to collect the program, INI, tool table, parameter file when vendored, `SUBROUTINE_PATH` files, `USER_M_PATH` files, and remap-NGC files before forwarding to `runFileWithIni()` or `runFiveAxisRemapFile()`. This covers INI-driven `U/V/W` axis mask handling, real `USER_M_PATH` registration for executable `M110`/`M111`, all four current external-offset M111 expected-failure programs, deterministic `woodpecker/on_abort.ngc` file execution, plain INI/tool-table execution through `axis/gladevcp/probe.ngc`, `SUBROUTINE_PATH` staging for `opa_demo.ngc -> circles.ngc`, bridge-mill remap-subroutine staging, vendored LinuxCNC bridge-mill and `melfa-sim` NGC remap execution, and PUMA machine-context execution without spawning host processes. The same smoke includes synthetic staging-plan assertions for generic `TOOL_TABLE`, `PARAMETER_FILE`, multi-directory `SUBROUTINE_PATH`, `USER_M_PATH`, and `REMAP ... ngc=...` file collection. |
| `tests/wasm/node/verify_sim_configs_inventory_wasm.sh` | Validates the first machine-readable Node inventory layer for `configs/sim`. It ensures native `build/native/sim-configs/summary.tsv`, generated `class-summary.tsv`, and generated `path-matrix.tsv` exist; verifies that generated `path-matrix.tsv` and tracked `docs/sim-configs-coverage-matrix.md` contain the same 159 paths as native `summary.tsv`; checks tracked matrix fields for class, native status, expected-failure reason, and blocked kind drift against generated `path-matrix.tsv`; and checks that generated `class-summary.tsv` matches class/status/expected-failure counts derived from `summary.tsv`. It then executes only entries that are currently vendored and have a defined standalone/WASM runtime path, and writes the sim-config inventory TSV artifacts. The boundary summary records one row per native inventory path with the matrix block, SDK classifier recommendation, declared HAL/UI/HALUI/Python process dependencies, `[EMCIO]DB_PROGRAM`, and execution-chain user-M codes; vendored INI rows must have an available classifier report, vendored hard blocks fail on classifier/matrix drift, safe process-declaring representatives must remain Node/browser `REP` rows with the expected process flag combinations rather than full-process promotions, and the designed full-process blocked rows must remain non-`REP` until their proof criteria are met. The generated inventory summary also requires all hard-blocked rows to remain `SKIP` with matching reasons, and `skip-summary.tsv` must match the matrix-derived skip counts. The blocked-dependency and boundary summaries record hard blocked Python-remap, tool-database, and external user-M rows from source `linuxcnc/configs/sim` INI files for dependency accounting only, including LinuxCNC source/config ownership fields, user-M script side effects, tool DB protocol evidence, Python runtime owner evidence, and completion criteria that all remain non-executing. Current gate: `executed=28`, `passed=28`, `skipped=131`, `unexpected_fail=0`; current skip/block counts are `ASSET_ONLY=65`, `L4_PYTHON_REMAP=53`, `L4_TOOL_DB=1`, `L4_USER_M_PROCESS=1`, `NON_MAIN_CLASS=10`, and `UPSTREAM_DEMO=1`. The executed set now includes the deterministic `woodpecker` plus `qtdragon` / `qtdragon_hd` / `qtvcp_screens` `on_abort.ngc` family, `axis/gladevcp/probe.ngc`, `axis/rose_engine/rcone_demo.ngc`, `axis/vismach/melfa-sim/example.ngc`, and the `axis/vismach/puma` sample programs after vendoring the required machine INI, tool-table, and remap-subroutine inputs. When an upstream INI declares a missing local tool table but the native harness resolved a valid fallback table, the Node inventory stages that native-selected table at the INI-declared path so WASM execution uses the same machine context. The skip reasons are explicit: `NON_MAIN_CLASS`, `L4-TOOL-DB`, `L4-USER-M-PROCESS`, `L4-PYTHON-REMAP`, and `UPSTREAM-DEMO`; an eligible row that lacks vendored machine context is reported as an inventory failure. This runner is an inventory source-of-truth for Node WASM and is intentionally narrower than a browser full inventory. |
| `tests/wasm/node/verify_nc_files_wasm.sh` | Validates representative vendored LinuxCNC `nc_files` examples in Node WASM by copying `3D_Chips.ngc`, `arcspiral.ngc`, `hole-circle.ngc`, `factorial.ngc`, and `m6demo.ngc` into the Emscripten filesystem and forwarding to the LinuxCNC-backed `Interp::open()`/`read()`/`execute()` path. `3D_Chips.ngc` is staged with a minimal INI-declared `tool.tbl` because the upstream program contains `T1 M6`; JavaScript only stages files and checks LinuxCNC output, including `run_step` status records, and does not implement G-code, O-word, tool-change, or M-code behavior. |
| `tests/wasm/node/verify_tp_wasm.sh` | Validates a standalone trajectory-planner WASM module can be built from vendored LinuxCNC TP/TC/Ruckig support source, loaded in Node, and run the same linear, arc, and queued-line planner probe paths covered by the native TP harness. |
| `tests/opfs/node/verify_file_service.sh` | Validates the host-owned OPFS text-file adapter, path model, session snapshot store including custom filenames and envelope/path rejection paths, machine file store, G-code text store including filename rejection paths, OPFS-to-WASM parameter/tool-table bridges, and grouped machine-session loading without moving file persistence, parameter semantics, or tool-table semantics into the WASM core. |
| `tests/browser/verify_ini_panel_browser.sh` | Validates the INI SDK, INI/interpreter WASM module loading, LinuxCNC-backed INI machine-session file-name string lookup, OPFS text-file round trip, generic session snapshot round trip plus custom filename and envelope/path rejection paths, machine file text round trip, G-code text round trip plus filename rejection paths, and the INI panel UI's machine-session load with default OPFS parameter/tool-table file mapping, G-code run, `run_step`-backed progress/line/statement/axis display, canonical-event display paths, and 5-axis remap demo action in a real browser runtime. |
| `tests/browser/verify_interp_browser.sh` | Validates the interpreter-core WASM module loads through the interpreter JS SDK in a real browser runtime and runs selected positive and negative canonical fixtures plus vendored upstream `tests/interp` regression files through vendored LinuxCNC `Interp::execute()` and `Interp::open()`/`read()`/`execute()` via the exported C ABI, including direct browser SDK and OPFS-backed parameter-file restore/save, non-random/random tool-table load/save through vendored LinuxCNC source, vendored `xyzac-trt`/`xyzbc-trt` table-rotary-tilting and `xyzab-tdr` table-dual-rotary switchkins remap demo execution, representative vendored `configs/sim` `foam`, `geometry`, `external_offsets`, `axis/gladevcp/probe.ngc`, deterministic `woodpecker/on_abort.ngc`, bridge-mill, `melfa-sim`, and `puma_cube.ngc` programs through `runSimConfigProgram()`, representative vendored `nc_files` examples through `runFile()` or `runFileWithIni()` when the upstream file requires INI/tool-table context, and a synthetic browser assertion that `planIniFileContextStaging()` can collect INI, program, tool-table, parameter, multi-directory subroutine, executable user-M, and remap-NGC files using only manifest text. |
| `tests/host/verify_host_smokes.sh` | Runs the current host-side Node, WASM interpreter-core, sim-config representative smoke, sim-config Node inventory, WASM trajectory-planner, OPFS, and browser smoke validation with shared WASM builds. |

## Fixture Coverage

Positive G-code fixtures currently cover:

- linear traverse/feed
- arc semantics
- modal absolute/incremental motion
- position parameters
- canned cycles
- coordinate offsets
- G53 machine-coordinate motion
- feed and motion control modes
- probing
- threading and rigid tap
- NURBS G5/G6
- spindle orient
- tool semantics
- tool table setup
- tool-data reload boundary
- interpreter state-tag boundary
- percent-delimited file `FINISH` boundary
- file-open `ON_RESET` boundary
- comment logging canonical calls
- local, INI-backed named parameters and numbered parameters
- O-word subroutines
- continue-on-error O-word unwind
- program-end modal reset
- canonical runtime edge calls

Negative fixtures currently cover:

- zero-feed `G1`
- arc radius mismatch
- zero-radius arc
- G53 incremental-mode rejection
- read-only named parameter writes
- read-only numbered parameter writes
- missing tool
- missing tool length offset

The negative fixture expectations are also checked against upstream
`rs274 -g`: the baseline requires LinuxCNC to reject each program, to emit the
expected error text, and to omit the canonical event lines marked as absent in
`tests/fixtures/canon_errors/`.

## Validation Boundaries

Current full-core validation is native-only. WASM/SDK validation covers the
INI parser smoke harness and an initial interpreter-core canonical event smoke
for `minimal_linear`, `arc_semantics`, `length_units`, `modal_incremental`,
`plane_selection`, `coordinate_offsets`, `g53_machine_coordinates`,
`feed_control_modes`, `position_params`, `probe_semantics`, `spindle_orient`,
`comment_logging`, `numbered_params`, `tool_semantics`, `tool_table_setup`,
`tool_reload`, `canned_cycles`, `cutter_comp_motion`, `threading_sync`,
`nurbs_g5_semantics`, `nurbs_g6_semantics`, `state_tag_motion`,
`canon_runtime_edges`, `program_end_modal_reset`, `namedparam_ini_semantics`,
and `namedparam_semantics` through both Node WASM and browser INI-aware
program ABI smoke coverage,
plus the `g1_zero_feed`, `arc_radius_mismatch`, `arc_zero_radius`,
`cutter_comp_plane_change`, `g53_incremental`, `namedparam_readonly`,
`numbered_param_readonly`, `tool_not_found`, and
`tool_length_offset_not_found` negative fixtures. The WASM interpreter file
path additionally covers the same canonical-event fixture group, plus
`namedparam_ini_semantics` and `namedparam_semantics` through the INI-aware
file execution ABI,
`file_open_reset`, `percent_file_finish`, `oword_subroutine`,
`g6164`, and `oword_unwind`.
`position_params` uses a dedicated file-path expectation under
`tests/fixtures/canon_file/` because LinuxCNC file execution advances the
post-execute position parameters differently than the line-by-line MDI smoke;
both expectations now also pin post-execute modal, override, spindle, mist,
and flood `_setup` state readback.
`coordinate_offsets` also uses a dedicated file-path expectation because
LinuxCNC file execution leaves the post-execute position parameters at the
file-path reset state while the line-by-line MDI path exposes the active G55
offset values; both paths pin the post-execute modal, override, spindle, mist,
and flood `_setup` state.
`tool_semantics` also pins the post-execute position-parameter and modal,
override, spindle, mist, and flood `_setup` state after T/M6/G43/G49/M61
tool behavior.
`feed_control_modes` pins the post-execute feed and motion-control modal state
plus the same position-parameter, override, spindle, mist, and flood `_setup`
state readback after G93/G94/G95 and G61/G61.1/G64 transitions.
The same Node WASM and browser file-path smokes also cover the negative fixture
group and check the LinuxCNC file-execution error text plus absent canonical
motion constraints where applicable.
`canon_runtime_edges` also pins the vendored interpreter's post-program
modal, override, spindle, mist, and flood `_setup` state readback after the
canonical runtime-edge calls complete.
The Node WASM and browser interpreter smokes also cover LinuxCNC parameter-file
restore/save behavior through the exported C ABI, including out-of-order file
rejection, missing-file success, missing required numeric parameter defaulting,
required numeric parameter writeback, removal of named-parameter-only lines
from saved output, and the `.bak` backup produced by vendored
`save_parameters()`, including direct browser SDK readback from the
Emscripten filesystem, browser OPFS readback of the backup text after the host
bridge writes it to persistent storage, plus INI-derived custom and explicit
host override parameter-file paths saved back to OPFS with their
LinuxCNC-produced backup text. It also covers LinuxCNC tool-table load/save
behavior through vendored `tooldata_common.cc`, including the non-random and
random-toolchanger `tooldata_init()` branches, with the SDK only copying text
into the Emscripten filesystem and calling the exported C ABI; the browser
smoke directly reads back both non-random and random saved tool-table text
from the Emscripten filesystem, checks the random-toolchanger
`tooldata_save()` C ABI result before OPFS persistence writes the saved table
text back, and verifies that INI-derived custom and explicit host override
tool-table OPFS paths can be saved and read back after the table is loaded
through vendored LinuxCNC. The same
Node WASM and browser interpreter smokes now validate the exported
`Interp::init()`/`Interp::synch()` probe for initialization canonical events,
metric/inch `emcStatus` machine-unit conversion, and current/selected tool
slot synchronization. They also validate the exported rotary-indexer probe for
vendored LinuxCNC `UNLOCK_ROTARY`/`LOCK_ROTARY` dispatch around a single-axis
`G0 A...` move, plus the exported named-parameter probe for direct LinuxCNC
`init_named_parameters()` and `find_named_param()` lookup of built-in,
INI-backed, HAL-backed, and missing named parameters.
OPFS validation covers the JavaScript host-boundary adapter, the INI browser
smoke harness, the INI panel UI's machine-session load and G-code run buttons,
the raw canonical-event display fed directly by LinuxCNC interpreter WASM
output, the Node parameter/tool-table bridges that copy OPFS text through the
SDK into vendored LinuxCNC file APIs, the Node machine-session bridge that
groups INI, parameter, and tool-table loading, the random-toolchanger flag
derived from vendored LinuxCNC INI boolean parsing, INI-derived
`[RS274NGC]PARAMETER_FILE` and `[EMCIO]TOOL_TABLE` file names mapped to OPFS
machine files, OPFS path-model rejection of invalid INI-derived file names,
explicit host session file-name overrides taking precedence over INI-derived
names, default host path fallback when INI file-name values are absent,
session snapshot custom filename handling plus envelope/path rejection
for unsupported format/version, wrong session id, and invalid snapshot
filenames, and a browser interpreter smoke that uses the same session bridge
with real LinuxCNC INI WASM parsing before saving OPFS-backed parameter and
tool-table text through vendored LinuxCNC file APIs, including default OPFS
path fallback when INI file-name values are absent.
Full browser coverage, full SDK coverage, and richer machine-state validation
remain future work.

## WASM/Browser Fixture Matrix

The executable fixture lists for Node WASM and browser interpreter smoke tests
are centralized in `tests/fixtures/interp-fixture-matrix.mjs`. Additions to the
matrix must continue to route execution through the SDK and exported C ABI
backed by vendored LinuxCNC interpreter code; the matrix is only a test
coverage list, not a CNC behavior implementation.

Node WASM `Interp::execute()` coverage currently includes:

- `minimal_linear`
- `arc_semantics`
- `length_units`
- `modal_incremental`
- `plane_selection`
- `coordinate_offsets`
- `g53_machine_coordinates`
- `feed_control_modes`
- `position_params`
- `probe_semantics`
- `spindle_orient`
- `comment_logging`
- `numbered_params`
- `tool_semantics`
- `tool_table_setup`
- `tool_reload`
- `canned_cycles`
- `cutter_comp_motion`
- `threading_sync`
- `nurbs_g5_semantics`
- `nurbs_g6_semantics`
- `state_tag_motion`
- `canon_runtime_edges`
- `program_end_modal_reset`
- `namedparam_ini_semantics` through the INI-aware program ABI
- `namedparam_semantics` through the INI-aware program ABI

Node WASM file-path coverage currently includes:

- `minimal_linear`
- `arc_semantics`
- `length_units`
- `modal_incremental`
- `plane_selection`
- `g53_machine_coordinates`
- `feed_control_modes`
- `probe_semantics`
- `spindle_orient`
- `comment_logging`
- `numbered_params`
- `tool_semantics`
- `tool_table_setup`
- `tool_reload`
- `canned_cycles`
- `cutter_comp_motion`
- `threading_sync`
- `nurbs_g5_semantics`
- `nurbs_g6_semantics`
- `state_tag_motion`
- `canon_runtime_edges`
- `program_end_modal_reset`
- `file_open_reset`
- `percent_file_finish`
- `oword_subroutine`
- `coordinate_offsets` through the dedicated `canon_file/` expectation
- `position_params` through the dedicated `canon_file/` expectation
- `namedparam_ini_semantics` through the INI-aware file ABI
- `namedparam_semantics` through the INI-aware file ABI

Node WASM file-path negative coverage currently includes every fixture under
`tests/fixtures/gcode_errors/`:

- `g1_zero_feed`
- `arc_radius_mismatch`
- `arc_zero_radius`
- `cutter_comp_plane_change`
- `g53_incremental`
- `namedparam_readonly`
- `numbered_param_readonly`
- `tool_length_offset_not_found`
- `tool_not_found`

Browser interpreter file-path negative coverage currently includes the same
negative fixture list through `Interp::open()`/`read()`/`execute()`:

- `g1_zero_feed`
- `arc_radius_mismatch`
- `arc_zero_radius`
- `cutter_comp_plane_change`
- `g53_incremental`
- `namedparam_readonly`
- `numbered_param_readonly`
- `tool_length_offset_not_found`
- `tool_not_found`

Browser interpreter `Interp::execute()` coverage currently includes:

- `minimal_linear`
- `arc_semantics`
- `length_units`
- `modal_incremental`
- `plane_selection`
- `coordinate_offsets`
- `g53_machine_coordinates`
- `feed_control_modes`
- `position_params`
- `canned_cycles`
- `numbered_params`
- `comment_logging`
- `tool_semantics`
- `tool_table_setup`
- `probe_semantics`
- `spindle_orient`
- `cutter_comp_motion`
- `threading_sync`
- `nurbs_g5_semantics`
- `nurbs_g6_semantics`
- `state_tag_motion`
- `canon_runtime_edges`
- `tool_reload`
- `program_end_modal_reset`
- `namedparam_ini_semantics` through the INI-aware program ABI
- `namedparam_semantics` through the INI-aware program ABI

Browser interpreter negative coverage currently includes every fixture under
`tests/fixtures/gcode_errors/`:

- `g1_zero_feed`
- `arc_radius_mismatch`
- `arc_zero_radius`
- `g53_incremental`
- `cutter_comp_plane_change`
- `namedparam_readonly`
- `numbered_param_readonly`
- `tool_length_offset_not_found`
- `tool_not_found`

Browser interpreter file-path coverage currently includes the same
`INTERP_FILE_FIXTURES` list as the Node WASM smoke:

- `minimal_linear`
- `arc_semantics`
- `length_units`
- `modal_incremental`
- `plane_selection`
- `g53_machine_coordinates`
- `feed_control_modes`
- `probe_semantics`
- `spindle_orient`
- `comment_logging`
- `numbered_params`
- `tool_semantics`
- `tool_table_setup`
- `tool_reload`
- `canned_cycles`
- `cutter_comp_motion`
- `threading_sync`
- `nurbs_g5_semantics`
- `nurbs_g6_semantics`
- `state_tag_motion`
- `canon_runtime_edges`
- `program_end_modal_reset`
- `file_open_reset`
- `percent_file_finish`
- `oword_subroutine`
- `coordinate_offsets` through the dedicated `canon_file/` expectation
- `position_params` through the dedicated `canon_file/` expectation
- `namedparam_ini_semantics` through the INI-aware file ABI
- `namedparam_semantics` through the INI-aware file ABI

All current positive G-code fixtures have browser interpreter smoke coverage
through either `Interp::execute()`, the file-path ABI, or the INI-aware
file-path ABI.

The current fixture expectations validate standalone behavior against both the
vendored LinuxCNC source path and an upstream `rs274` side-by-side baseline for
parser/conversion, arc geometry, offsets, feed-control and feed-state
readback, comment/logging,
numbered-parameter, local named-parameter plus `_ini[...]` lookup through
`rs274 -i`, probing, spindle-orient, file-open reset, file-finish,
tool-reload, tool select/change/length-offset, M61 current-tool-number update,
canned-cycle, state-tag motion, tool-table setup, and O-word subroutine
fixtures, plus vendored `tests/interp/flowsnake` recursive O-word toolpath,
`tests/interp/cam-nisley` storm-door latch cam execution with upstream
tool-table context and a bare-run missing-tool negative check,
`tests/interp/inside-corners` cutter-compensation concave/convex/tangent
geometry across XY and ZX planes,
`tests/interp/inverse-time-with-comp` inverse-time feed and cutter-compensation
interaction,
selected upstream `tests/interp/bad` file-error paths including canned-cycle
A-axis rejection, center-format arc radius mismatch rejection, and
selected upstream `tests/interp/good` center-format arc tolerance acceptance,
selected `tests/interp/g72-*` lathe facing canned-cycle iteration regressions,
selected `tests/interp/g71-*` lathe roughing/finish canned-cycle regressions,
`tests/interp/g76` lathe threading with upstream tool-table context,
cutter-compensation rejection, selected standalone `tests/ccomp`
cutter-compensation file execution with upstream tool tables, threading/rigid tap,
vendored `tests/interp/g33.1` rigid-tap file execution, NURBS dispatch
boundaries, and the
comparable canonical runtime edge and program-end cleanup calls. The
standalone vendored-source harness, Node WASM smoke, and browser smoke also
pin `coordinate_offsets` MDI/file-path, `feed_control_modes`, `run_step`
file-execution status output for line number, URI-encoded source statement,
return code, and current interpreter axis positions, the
`position_params` MDI/file-path, `tool_semantics`, and `canon_runtime_edges`
post-program modal, override, spindle, mist, and flood `_setup` state. Native
and WASM standalone checks also cover the user M-code registration boundary for
INI-declared `M110`/`M111` handlers while keeping process execution outside the
browser/WASM runtime. Node and browser WASM sim-config coverage now run the real
vendored `axis/geometry/xyzc.ngc` and the
`axis/external_offsets/dyn_demo.ngc`, `eoffsets.ngc`, `jwp_z.ngc`, and
`opa_demo.ngc` programs with their executable `M110`/`M111` files,
`opa_demo.ngc`'s `SUBROUTINE_PATH` dependency on `circles.ngc`,
`axis/foam/foam.ngc` with its `U/V` machine configuration and shared
`axis/sim.tbl` tool table staged beside the INI, and
bridge-mill `5axisgui.ngc` with its `W` machine coordinates. The same upstream baseline also
validates the current negative fixture error text and absent canonical-event
constraints. Fixtures that depend on standalone-only runtime
adapters, HAL state, upstream `rs274` output gaps such as
`WAIT` or hidden NURBS control-point detail, or richer machine session state
still need dedicated native LinuxCNC baselines.

Remaining positive fixtures that are not in the upstream `rs274` side-by-side
baseline are intentionally held out until they get a dedicated native LinuxCNC
baseline: `namedparam_semantics` still depends on the standalone HAL adapter,
while `namedparam_ini_semantics` now covers the local named-parameter and
LinuxCNC `_ini[...]` subset against upstream `rs274 -i`. `state_tag_motion`
now compares its motion events with upstream `rs274`, while `UPDATE_TAG`
events remain a standalone state-tag capture boundary. `tool_semantics` now
compares T/M6/G43/G49 and M61 canonical/current-pocket readback with upstream
`rs274 -t -i`; `Interp::synch()` current/selected tool slot reads are covered
by the native init harness.

The kinematics probes currently cover LinuxCNC identity/trivial kinematics, the
switchable `5axiskins` XYZBCW bridge-mill model, TRT `xyzac`/`xyzbc`
table-rotary models, CoreXY, rotated-axis, rose, max, linear-delta,
rotary-delta, Scorbot, tripod, SCARA, PUMA, generic serial, generic hexapod,
and pentapod models.

`linuxcnc_5axis_remap_asset_probe` pins the LinuxCNC sample-machine assets
that define five-axis switchkins `M428`, `M429`, and `M430`: bridge-mill,
dual-rotary, and table-rotary-tilting INI files, `remap_subs/*.ngc`, HAL
switchkins links, tool tables, and demo programs. It verifies that these
commands remain LinuxCNC `REMAP` entries backed by LinuxCNC NGC subroutines
using `M68`, `M66`, and `_hal[motion.switchkins-type]`; it does not implement
the remap execution path.

`linuxcnc_remap_parse_harness` links vendored `interp_remap.cc` for the
standalone remap descriptor path. It reads the vendored `xyzac-trt`,
`xyzbc-trt`, `xyzab-tdr`, and bridge-mill INI `REMAP` entries, resolves their
`remap_subs/*.ngc` files through LinuxCNC `find_ngc_file()`, and validates the
resulting `_setup.m_remapped` descriptors for each machine's LinuxCNC-defined
`M428`/`M429`/`M430` set. Python callbacks remain runtime boundaries. Native
and WASM validation now execute the NGC remap/file paths through vendored
LinuxCNC O-word dispatch and the standalone HAL adapter boundary.

`linuxcnc_duplicate_oword_remap_harness` vendors LinuxCNC's upstream
`tests/remap/duplicate-o-word` and `tests/remap/m30-interaction` files
unchanged and runs each `test.ngc` with its `test.ini` through vendored
`Interp::parse_remap()`, `open()`, `read()`, and `execute()`. Native, Node
WASM, and browser interpreter validation assert the same successful LinuxCNC
regression paths, including the remapped `M207`/`M208` messages and final
`FINISH()` event for duplicate O-word, and the `M400 M30` remap-level
interaction with `PROGRAM_END`.

`linuxcnc_interp_minimal_harness` now also runs selected vendored LinuxCNC
upstream `tests/interp/*/test.ngc` files through vendored `Interp::open()`,
`read()`, and `execute()`, using LinuxCNC INI `SUBROUTINE_PATH` for
`tests/interp/sub-call-from-sub`, `tests/interp/sequence-number`, and
`tests/interp/nested-sub-error`, `tests/interp/nested-sub-in-file-error`, and
`tests/interp/abort-hot-comment`, plus LinuxCNC `[RS274NGC]ORIENT_OFFSET` for
`tests/interp/m19`.
Native, Node WASM, and browser interpreter validation assert the LinuxCNC
canonical messages, canonical events, error texts, and interpreter state for
O-word loop/break/subroutine paths, dynamic O-word calls, `EXISTS[]`,
subroutine return values, subroutines after main programs, fractional line
numbers, named-parameter parsing, M19 spindle-orient offset and wait timeout
canonical events, magic-comment parameter formatting, valid external subroutine calls from another
external subroutine, external-subroutine `#<_line>` reporting,
nested-subroutine-definition rejection, and blocked forward seek to a later
numbered subroutine in the same external file, selected `tests/interp/rotation`
absolute-position `#<_abs_x>`, `#<_abs_y>`, and `#<_abs_z>` reporting under
G54, G92, XY rotation, and unit changes plus rotated-coordinate `G28` and
`G53` endpoint behavior, selected `tests/interp/m98m99`
Fanuc-style `M98/M99` subprogram calls, missing-P-word, missing-subprogram,
mixed Fanuc/RS274NGC sub-style, and `DISABLE_FANUC_STYLE_SUB` INI-gated
errors, main-program O-word termination rules, parameter-scope differences,
loop counts including `L0`, nested numbered subprograms, subprograms after main
program text, leading-zero O-word lookup, named/numbered main programs, and
O-expression calls, plus
`(ABORT,...)` hot-comment numbered, named, and INI-parameter
expansion, with skipped branch/subroutine messages and post-abort program end
asserted absent where applicable. The same native, Node WASM, and browser
interpreter validation also stages an INI-declared variable file to verify
LinuxCNC G92 startup persistence and the `DISABLE_G92_PERSISTENCE` startup
clear path through vendored interpreter initialization.
