# LinuxCNC Sim Config Completion Plan This plan tracks the work needed to run the LinuxCNC programs under `linuxcnc/configs/sim` through the standalone native/WASM simulation runtime. LinuxCNC source remains the semantic source of truth. Port code must live under `wasm-port/`; do not edit `linuxcnc/` in place. ## Current Baseline The latest strict harness run used `linuxcnc/configs/sim` as the program source, selected the nearest INI/tool table for each `.ngc` unless an explicit sim mapping is required, and ran `rs274` from the INI directory so relative `SUBROUTINE_PATH` and `REMAP` entries resolve like a real sim config. Result file: ```text wasm-port/build/native/sim-configs/summary.tsv ``` Baseline: ```text total: 159 pass: 151 fail: 8 timeout: 0 expected_fail: 8 unexpected_fail: 0 main: PASS 40, FAIL 8 macro_load: PASS 46, FAIL 0 remap_subroutine: PASS 65, FAIL 0 ``` Remaining failures are explicit LinuxCNC-native baseline edges: - `axis/external_offsets/*`: 4 failures from sim-only user M-codes `M111`. - `axis/geometry/xyzc.ngc`: 1 failure from sim-only user M-code `M110`. - `axis/foam/foam.ngc`: 1 failure from missing `U/V` axis support in the LinuxCNC `bin/rs274` entry point. - `axis/vismach/5axis/bridgemill/5axisgui.ngc`: 1 failure from missing `W` axis support in the LinuxCNC `bin/rs274` entry point. - `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc`: 1 failure from an upstream demo line that uses bare `X/Y/Z` words after `G53.6` without an explicit motion G-code. ## Target The first target is not a browser UI feature. It is a repeatable native and WASM simulation harness that can load and execute all `linuxcnc/configs/sim` programs that are meaningful in a standalone non-hardware simulation context. Acceptance criteria: - The native sim-config harness reports all executable sim programs as `PASS`. - Macro/remap files are validated by the correct entry point: direct execution only for real programs, load/parse or remap-call validation for subroutines. - WASM tests cover the same representative classes after the native harness is green. - Any remaining unsupported files are explicitly classified as native GUI or hardware-only and excluded by rule, not by accident. ## Phase 1: Make The Harness A First-Class Test Status: complete. The tracked script exists and is wired into the native validation entry point. 1. Add a tracked script: ```text wasm-port/tests/native/verify_sim_configs.sh ``` 2. Move the ad hoc test logic into the script: - scan only `linuxcnc/configs/sim`; - identify `.ngc`, nearest `.ini`, nearest `.tbl`; - run from the INI directory; - write `summary.tsv`, stdout, stderr, and interpreter output under `wasm-port/build/native/sim-configs/`; - classify programs as `main`, `macro_load`, `remap_subroutine`, or `unsupported_runtime_edge`; - fail the script only on unexpected failures. 3. Add deterministic classification rules: - files under `remap_subs/` and `nc_subroutines/` are not standalone main programs; - files with no `M2`, `M30`, or `%` are load/parse fixtures unless the INI references them through `REMAP`; - user M-code files are tested through `USER_M_PATH` resolution, not by pretending LinuxCNC native process services exist. 4. Wire the script into existing validation: ```text wasm-port/tests/native/verify_native_probes.sh ``` 5. Completion check: ```bash wasm-port/tests/native/verify_sim_configs.sh ``` Latest result: ```text total: 159 pass: 151 fail: 8 timeout: 0 expected_fail: 8 unexpected_fail: 0 skipped: 0 ``` ## Phase 2: Load Machine Axes From INI Status: partially complete for the port runtime. The standalone native/WASM runtime now parses `[TRAJ] COORDINATES`, updates the standalone external axis mask used by LinuxCNC `GET_EXTERNAL_AXIS_MASK()`, and applies the same `Interp::_readers` filtering that upstream `rs274ngc_pre.cc` uses. Native regression coverage verifies that `axis_foam.ini` enables `U/V` readers and `bridgemill/5axis.ini` enables the `W` reader. The `verify_sim_configs.sh` baseline still records the LinuxCNC `bin/rs274` entry-point failures as expected failures. That harness is intentionally kept as a LinuxCNC-native baseline while the port runtime coverage tracks this phase's standalone behavior. Current failures: - `axis/foam/foam.ngc`: `Bad character 'u' used` - `axis/vismach/5axis/bridgemill/5axisgui.ngc`: `Bad character 'w' used` Root cause: The current standalone interpreter initialization does not configure the active axis mask from `[TRAJ] COORDINATES` / `[KINS] KINEMATICS`. Native LinuxCNC accepts `U/V/W` only when the machine config declares those axes. Implementation steps: 1. Add a machine-config loader in `runtime/core/linuxcnc_wrap/`, for example: ```text linuxcnc_machine_config.hh linuxcnc_machine_config.cpp ``` 2. Reuse vendored `inifile.cc` to parse: - `[TRAJ] COORDINATES` - `[KINS] KINEMATICS` - `[DISPLAY] GEOMETRY` - `[RS274NGC] PARAMETER_FILE` - `[RS274NGC] SUBROUTINE_PATH` - `[RS274NGC] USER_M_PATH` - `[EMCIO] TOOL_TABLE` 3. Extend `initialize_minimal_interp()` / the runtime equivalent to set the interpreter setup fields from the parsed machine config instead of hardcoded `XYZ`. 4. Add focused native fixtures: - `axis_foam.ini` + `foam.ngc` accepts `U/V`. - `bridgemill/5axis.ini` + `5axisgui.ngc` accepts `W`. 5. Add the same coverage to the WASM SDK once native is green. Completion check: ```bash wasm-port/tests/native/verify_sim_configs.sh --only axis/foam/foam.ngc wasm-port/tests/native/verify_sim_configs.sh --only axis/vismach/5axis/bridgemill/5axisgui.ngc ``` ## Phase 3: Implement Standalone User M-Code Dispatch Status: complete for the standalone native/WASM runtime boundary. The port now reads `[DISPLAY] PROGRAM_PREFIX` and `[RS274NGC] USER_M_PATH`, searches executable `M100` through `M199` files using the same order as LinuxCNC task initialization, registers them in LinuxCNC's `USER_DEFINED_FUNCTION` table, and records `USER_M_COMMAND` canonical boundary events instead of spawning host processes. Native probes cover `axis/geometry` `M110` and `axis/external_offsets` `M111`; Node WASM covers minimal `M110` and `M111` fixtures through the Emscripten filesystem. The `verify_sim_configs.sh` baseline still records these files as expected failures because it intentionally runs LinuxCNC `bin/rs274`, not the standalone task/runtime adapter. Current failures: - `axis/external_offsets/*.ngc`: unknown `M111` - `axis/geometry/xyzc.ngc`: unknown `M110` Root cause: Native LinuxCNC resolves user M-codes through `[DISPLAY] PROGRAM_PREFIX` and `[RS274NGC] USER_M_PATH` during task initialization, registers matching executable `M100..M199` handlers with the interpreter, and later runs external scripts through the task process boundary. The standalone runtime must register the same interpreter boundary without running host processes in WASM/browser. Implementation steps: 1. Locate the upstream M-code dispatch path in LinuxCNC and vendor the minimum source needed to preserve semantics, or add a narrow runtime-edge adapter if the upstream path is process-bound. Done: the adapter mirrors `src/emc/task/emctask.cc` search/registration behavior and keeps process execution outside CNC semantics. 2. Add `USER_M_PATH` parsing to the machine-config loader. Done. 3. For native standalone tests. Done: - resolve `M100` through `M199` against configured search paths; - register only executable files; - emit deterministic `USER_M_COMMAND` boundary events. 4. For WASM/browser. Done for Node WASM: - do not spawn host processes; - use a host-boundary user-M adapter that registers deterministic message-emitting handlers for sim-only notification M-codes; - document this as a runtime edge, not CNC semantics. 5. Add tests. Done: - `M110` from `axis/geometry`; - `M111` from `axis/external_offsets`; - minimal WASM `M110` and `M111` fixtures. Completion check: ```bash wasm-port/tests/native/verify_sim_configs.sh --only axis/external_offsets wasm-port/tests/native/verify_sim_configs.sh --only axis/geometry/xyzc.ngc ``` Standalone runtime checks: ```bash wasm-port/tests/native/verify_native_probes.sh wasm-port/tests/wasm/node/verify_interp_wasm.sh ``` ## Phase 4: Bring Up Python Remap Runtime Edges Status: complete for the native sim-config harness. Current checks: ```text gmoccapy: total 38, pass 38, fail 0 axis/laser: total 3, pass 3, fail 0 ``` Root cause: The configs use LinuxCNC Python remap modules. Earlier harness runs failed while initializing `python/toplevel.py`; current native runs now resolve and execute these configs under the LinuxCNC `rs274` baseline. Implementation steps: 1. Preserve upstream behavior where possible: - inspect vendored LinuxCNC Python plugin interfaces; - identify exact APIs used by: - `configs/sim/gmoccapy/python/toplevel.py` - `configs/sim/gmoccapy/python/remap.py` - `configs/sim/gmoccapy/python/stdglue.py` - `configs/sim/axis/laser/python/toplevel.py` - `configs/sim/axis/laser/python/remap.py` 2. Split the implementation into two layers: - native harness support using the host Python runtime, if available; - WASM support using a documented adapter boundary or a Python-free equivalent only for runtime edges, not G-code semantics. 3. Add runtime state adapters required by Python remap: - selected tool and pocket; - tool table access; - interpreter status object fields used by stdglue; - message/error reporting; - canonical tool-change side effects needed by `M6`, `M61`, and laser remaps. 4. Make Python module path resolution match LinuxCNC: - INI directory is the base directory; - `PYTHONPATH` includes config `python/` directories; - remap modules load relative to the sim config. 5. Add narrowly-scoped tests before running all 38 gmoccapy programs. Done: - one gmoccapy tool-change macro; - one gmoccapy lathe macro; - one gmoccapy 5-axis example; - one laser raster/vector program. Completion check: ```bash wasm-port/tests/native/verify_sim_configs.sh --only gmoccapy wasm-port/tests/native/verify_sim_configs.sh --only axis/laser ``` ## Phase 5: Support TWP Remaps Including G69 Status: mapping complete. The harness now maps `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/*` through the explicit `xyzacb-trsrn_twp/xyzacb-trsrn.ini` machine config instead of falling back to `axis/axis.ini`. That loads the TWP `G69` remap declarations correctly, and 14 of the 15 TWP demo/remap programs now pass. Current failures: - `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc` fails in the LinuxCNC `rs274` baseline because line 10 uses bare `x50y50z150` after `G53.6`; same-directory demos use explicit `G0` motion words at this point. Root cause: The demo programs rely on table/spindle rotary TWP remaps. The original test mapping fell back to `axis/axis.ini` for these demo files because the actual INI is in a child directory: ```text axis/vismach/5axis/table-rotary_spindle-rotary-nutating/ xyzacb-trsrn_twp/xyzacb-trsrn.ini xyzbca-trsrn_twp/xyzbca-trsrn.ini ``` Implementation steps: 1. Improve sim-config mapping. Done: - if a `.ngc` is under a `demos/` sibling of machine-specific INI directories, map it to the appropriate TWP INI instead of walking upward to `axis/axis.ini`; - encode this as explicit metadata in the harness, not a fragile heuristic. 2. Ensure `SUBROUTINE_PATH` includes `../remap_subs:../demos` as declared by the TWP INIs. 3. Verify remap subroutines: - `g69remap.ngc` - `g531remap.ngc` - `g533remap.ngc` - `g536remap.ngc` 4. Add a native test for each failing demo. Latest result: ```text total: 15 pass: 14 fail: 1 timeout: 0 expected_fail: 1 unexpected_fail: 0 skipped: 0 ``` Completion check: ```bash wasm-port/tests/native/verify_sim_configs.sh --only table-rotary_spindle-rotary-nutating ``` ## Phase 6: Promote Native Coverage To WASM Status: complete for representative Node and browser WASM coverage. Dedicated Node and browser smokes now pass a representative vendored `configs/sim` subset through the SDK `runSimConfigProgram()` host boundary, which copies files into the Emscripten filesystem and forwards execution to existing LinuxCNC-backed C ABI paths. They cover: - `axis/foam/foam.ngc` with `axis_foam.ini`, verifying INI-driven `U/V` axis mask handling in WASM. - `axis/vismach/5axis/bridgemill/5axisgui.ngc` with `5axis.ini`, verifying INI-driven `W` axis mask handling and bridge-mill NGC remap execution in WASM. - `axis/geometry/xyzc.ngc` with `xyzc.ini` and real executable `M110`, verifying `USER_M_PATH` registration in WASM. - `axis/external_offsets/dyn_demo.ngc` with `dynamic_offsets.ini` and real executable `M111`, verifying the same user-M boundary against an upstream sim program. Completion checks: ```bash wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh wasm-port/tests/browser/verify_interp_browser.sh ``` Remaining work: 1. Extend `runtime/core/linuxcnc_wrap/linuxcnc_interp_wasm.cpp` with the same machine-config entry point used by native tests. Done for the existing `runFileWithIni()` file-execution path. 2. Extend `runtime/sdk/src/linuxcnc-interp.js` with a host-boundary method such as: ```js runSimConfigProgram({ iniPath, programPath, files }) ``` Done. The method only writes caller-provided text files into the Emscripten filesystem, applies executable bits for user M-code files, and forwards execution to the existing LinuxCNC-backed C ABI path. It does not implement CNC semantics in JavaScript. 3. Copy required sim config files into the Emscripten FS: - INI; - tool table; - parameter file; - `.ngc` program; - `SUBROUTINE_PATH` files; - `USER_M_PATH` handler files or registered adapter handlers. Done for the representative Node and browser subsets. 4. Add Node WASM coverage. Done: ```text wasm-port/tests/wasm/node/verify_sim_configs_wasm.mjs wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh ``` 5. Add representative browser coverage. Done in `wasm-port/tests/browser/interp_smoke.html` through the existing browser interpreter smoke and the same `runSimConfigProgram()` SDK boundary as Node. ## Phase 7: Documentation And Drift Control Status: complete for the current representative sim-config WASM/browser coverage. The documentation and manifest now record the native, Node WASM, browser, source reuse, and vendor-sync state for the selected sim-config programs. 1. Update `docs/compatibility-validation.md` with the sim-config matrix. Done. 2. Update `docs/source-reuse-map.md` for newly vendored source files. Done. 3. Update `tools/source-manifest.txt` and `tools/verify_vendor_sync.sh` if new LinuxCNC files are copied into `wasm-port/vendor/linuxcnc`. Done for the representative `axis/foam`, `axis/geometry`, and `axis/external_offsets` sim-config files; no `verify_vendor_sync.sh` logic change was needed. 4. Preserve the latest result summary as a machine-readable artifact, but do not commit generated logs unless they are intentionally used as fixtures. Done: generated logs remain under build/test output directories, while the tracked documentation records only the current summary values and validation commands. ## Recommended Work Order 1. Harness first: make `verify_sim_configs.sh` reproducible. 2. Axis config next: unblock `U/V/W` failures. 3. TWP mapping: complete; remaining TWP demo failures are Python/HAL remap runtime edges. 4. User M-code dispatch: small runtime-edge feature, unblocks five files. 5. Python remap runtime: largest block; do it after the harness and smaller runtime edges are stable. 6. WASM promotion after native parity. ## Non-Goals - Do not implement a new JavaScript or project-authored G-code interpreter. - Do not edit `linuxcnc/configs/sim` or any upstream `linuxcnc/` file to make tests pass. - Do not fake path, modal, kinematic, tool, or parameter semantics in the SDK. - Do not treat gmoccapy native GUI code as browser UI implementation. Only its simulation/remap behavior is relevant to this runtime.