# 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 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 OPFS host-boundary validation command is: ```bash wasm-port/tests/opfs/node/verify_file_service.sh ``` 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 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_native_probes.sh` Checks probe exit codes, source-probe coverage, harness stdout, canonical fixture events, and expected error behavior. 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 writes selected G-code fixtures 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. 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 and `.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 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 the default OPFS parameter-file and tool-table mappings used when a machine session is copied into the interpreter WASM filesystem. 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 uses real browser OPFS storage plus the interpreter SDK to restore and save a LinuxCNC parameter file through vendored `Interp::restore_parameters()` and `Interp::save_parameters()`, to load/save LinuxCNC tool tables through vendored `tooldata_common.cc`, and 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. The aggregate host smoke script builds the INI and interpreter-core 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_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_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 initial interpreter-core WASM module can be built from vendored LinuxCNC interpreter source, loaded through the interpreter JS SDK, run the first fixture group through `Interp::execute()` and selected file fixtures through `Interp::open()`/`read()`/`execute()`, match the native canonical event plus required state readback fixtures, and run parameter-file restore/save through vendored LinuxCNC `Interp::restore_parameters()` and `Interp::save_parameters()`. | | `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, and canonical-event display paths 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 through vendored LinuxCNC `Interp::execute()` plus `Interp::open()`/`read()`/`execute()` via the exported C ABI, including OPFS-backed parameter-file restore/save and tool-table load/save through vendored LinuxCNC source. | | `tests/host/verify_host_smokes.sh` | Runs the current host-side Node, WASM interpreter-core, 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 - named and numbered parameters - O-word subroutines - 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 ## 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`, 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_semantics` through the INI-aware file execution ABI, `file_open_reset`, `percent_file_finish`, and `oword_subroutine`. `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. 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. The Node WASM interpreter smoke also covers LinuxCNC parameter-file restore/save behavior through the exported C ABI, including out-of-order file rejection, missing-file success, required numeric parameter writeback, removal of named-parameter-only lines from saved output, and the `.bak` backup produced by vendored `save_parameters()`. 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. 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_semantics` through the INI-aware program ABI Node WASM file-path coverage currently includes: - `minimal_linear` - `arc_semantics` - `length_units` - `modal_incremental` - `plane_selection` - `coordinate_offsets` - `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` - `position_params` through the dedicated `canon_file/` expectation - `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_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` - `coordinate_offsets` - `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` - `position_params` through the dedicated `canon_file/` expectation - `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, probing, spindle-orient, file-open reset, file-finish, tool-reload, tool select/change/length-offset, canned-cycle, state-tag motion, tool-table setup, and O-word subroutine fixtures, plus threading/rigid tap, NURBS dispatch boundaries, and the comparable canonical runtime edge and program-end cleanup calls. Fixtures that depend on standalone-only runtime adapters, HAL/INI/tool-change 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` depends on standalone INI/HAL adapter resolution. `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 canonical events with upstream `rs274`; M61 current-pocket host-state behavior remains covered by the standalone fixture expectation, and `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.