# LinuxCNC WASM Porting Steps

## Goal

Build a separate WASM-based CNC simulation program that:

- uses LinuxCNC source as the semantic source of truth,
- does not modify the original `linuxcnc/` source tree,
- is managed independently from native LinuxCNC,
- provides HTML + JavaScript frontend plus OPFS persistence,
- matches LinuxCNC software behavior as closely as practical except for realtime hardware driving.

## Non-Negotiable Constraints

1. `linuxcnc/` is upstream and read-only for the port effort.
2. Any source adaptation needed for WASM happens on copied or generated files under `wasm-port/`.
3. No direct edits inside `linuxcnc/src`, `linuxcnc/lib`, `linuxcnc/tests`, or `linuxcnc/web` are part of the port workflow.
4. LinuxCNC GUI code is reference-only. The migrated UI is rebuilt in HTML + JavaScript.
5. LinuxCNC compute logic should be reused before any reimplementation is considered.

## Workspace Structure

Create and keep these directories under `wasm-port/`:

- `docs/`
  Planning, architecture, and validation documents.
- `vendor/linuxcnc/`
  Copied source files selected for the port.
- `patches/`
  Patch files against the vendored copies.
- `tools/`
  Scripts that extract files from `../linuxcnc`, apply patches, and verify drift.
- `runtime/core/`
  WASM-targeted C/C++ code and wrappers.
- `runtime/sdk/`
  JavaScript/TypeScript wrapper over the WASM module.
- `runtime/ui/`
  HTML + JavaScript frontend.
- `runtime/opfs/`
  Browser file adapter.
- `tests/native/`
  Native extracted-core regression tests.
- `tests/browser/`
  Browser/WASM regression and smoke tests.

## Ported Program Directory Structure

The migrated program itself should be managed as a standalone product under
`wasm-port/`, with LinuxCNC acting as an upstream source provider.

The recommended final structure is:

```text
wasm-port/
├── docs/
├── vendor/
│   └── linuxcnc/
│       └── src/
│           ├── emc/
│           │   ├── ini/
│           │   ├── kinematics/
│           │   ├── rs274ngc/
│           │   └── tp/
│           ├── hal/
│           │   └── components/
│           └── libnml/
│               └── posemath/
├── patches/
├── tools/
├── runtime/
│   ├── core/
│   │   ├── include/
│   │   ├── shims/
│   │   ├── adapters/
│   │   ├── canon/
│   │   ├── session/
│   │   ├── simulation/
│   │   ├── linuxcnc_wrap/
│   │   └── c_api/
│   ├── sdk/
│   │   └── src/
│   ├── ui/
│   │   ├── public/
│   │   └── src/
│   │       ├── panels/
│   │       ├── preview/
│   │       ├── state/
│   │       ├── machine/
│   │       └── files/
│   └── opfs/
├── tests/
│   ├── native/
│   ├── wasm/
│   └── fixtures/
├── build/
│   ├── native/
│   └── wasm/
└── dist/
    ├── sdk/
    └── web/
```

### Directory Responsibilities

- `vendor/linuxcnc/`
  Holds extracted upstream LinuxCNC source files. These are copied or
  generated from `../linuxcnc` and are the only place where source-level
  port patches are applied.
- `patches/`
  Stores every patch applied to vendored LinuxCNC files. Patches are tracked
  here instead of being mixed into the original source tree.
- `tools/`
  Holds extraction, sync, verification, manifest generation, and
  compatibility-check scripts.
- `runtime/core/`
  The standalone simulation runtime. This is where vendored LinuxCNC source
  is wrapped, adapted, and exposed to the rest of the ported program.
- `runtime/core/include/`
  Public internal headers for the standalone runtime.
- `runtime/core/shims/`
  Small compatibility headers and implementation stubs required to compile
  vendored LinuxCNC code outside the native runtime.
- `runtime/core/adapters/`
  Host boundary adapters such as file IO, logging, simulation HAL, and
  runtime-state providers.
- `runtime/core/canon/`
  Canonical motion event collection and serialization.
- `runtime/core/session/`
  Per-simulation session ownership for interpreter state, planner state,
  machine state, and controller state.
- `runtime/core/simulation/`
  Higher-level simulation orchestration built on top of reused LinuxCNC
  compute modules.
- `runtime/core/linuxcnc_wrap/`
  Thin wrappers around vendored LinuxCNC entry points. Prefer wrappers here
  over editing vendored source directly.
- `runtime/core/c_api/`
  Stable C ABI exported to the WASM layer.
- `runtime/sdk/`
  JavaScript/TypeScript SDK that calls the WASM module and hides memory
  management and ABI details from the frontend.
- `runtime/ui/`
  The actual CNC simulation web application built with HTML + JavaScript.
- `runtime/ui/src/panels/`
  Operator panels, machine configuration panels, parameter editors, and
  controller-status panels.
- `runtime/ui/src/preview/`
  2D/3D path preview, 5-axis visualization, joint/world overlays, and
  playback views.
- `runtime/ui/src/state/`
  Browser-side state management for session lifecycle and UI coordination.
- `runtime/ui/src/machine/`
  Machine-model-specific UI logic.
- `runtime/ui/src/files/`
  File import/export and project/session handling UI logic.
- `runtime/opfs/`
  OPFS-backed file services, snapshot persistence, and path mapping.
- `tests/native/`
  Native extracted-core regression tests run before WASM build validation.
- `tests/wasm/`
  WASM tests for Node and browser environments.
- `tests/fixtures/`
  Shared INI, G-code, parameter, tool-table, and machine fixtures.
- `build/`
  Intermediate build output. This is disposable.
- `dist/`
  Deliverable artifacts such as generated web bundles and SDK packages.

### Source Ownership Rule

The ported program's own source code is expected to live under:

- `runtime/core/`
- `runtime/sdk/`
- `runtime/ui/`
- `runtime/opfs/`
- `tests/`
- `tools/`
- `docs/`

Vendored LinuxCNC code must live under:

- `vendor/linuxcnc/`

The original upstream tree must remain outside the ported program's source
ownership boundary:

- `../linuxcnc/`

### Patch Placement Rule

If a LinuxCNC source file needs adaptation:

1. extract it into `vendor/linuxcnc/`;
2. patch the vendored copy only;
3. record the patch in `patches/`;
4. document why the patch exists in `docs/` or in the patch header.

Do not place LinuxCNC source patches under `runtime/`.

### Build Graph Rule

The standalone build should flow like this:

1. `tools/` extracts upstream files into `vendor/`
2. `patches/` are applied to vendored copies
3. `runtime/core/` compiles vendored files plus wrappers
4. `runtime/sdk/` consumes the generated WASM module
5. `runtime/ui/` consumes the SDK
6. `runtime/opfs/` provides browser persistence services
7. `tests/` validate native and browser behavior

This ensures the ported program remains independently buildable while still
tracking LinuxCNC as the semantic source of truth.

## Phase 0: Freeze Upstream Reference

Purpose:
Make the LinuxCNC source baseline explicit before extraction starts.

Steps:

1. Record the exact upstream commit from `linuxcnc/.git`.
2. Record local build assumptions:
   - compiler version
   - emscripten version
   - python version
   - node version
3. Record the first supported LinuxCNC fixture set:
   - one 3-axis machine
   - one non-trivial kinematics case
   - one 5-axis machine
4. Record the first G-code feature set:
   - linear motion
   - arc motion
   - canned cycles
   - offsets and coordinate systems
   - subroutines
   - numeric and named variables
   - representative 5-axis programs

Outputs:

- upstream revision note
- supported feature baseline
- supported machine baseline

## Phase 1: Build the Source Reuse Map

Purpose:
Identify exactly which LinuxCNC source files are needed.

Steps:

1. Map each required capability to LinuxCNC files:
   - G-code interpreter:
     `../linuxcnc/src/emc/rs274ngc/*`
   - parameter tables and named variables:
     `../linuxcnc/src/emc/rs274ngc/interp_*`
   - INI parsing:
     `../linuxcnc/src/emc/ini/inifile.*`
   - planner:
     `../linuxcnc/src/emc/tp/*`
   - kinematics:
     `../linuxcnc/src/emc/kinematics/*`
     and selected files from `../linuxcnc/src/hal/components/*.comp`
   - posemath:
     `../linuxcnc/src/libnml/posemath/*`
2. For each file, classify it:
   - copy unchanged
   - copy plus shim
   - copy plus light patch
   - not included in phase 1
3. For each file, record dependencies:
   - HAL
   - RTAPI
   - NML
   - native file IO
   - Python
   - GUI
4. Save the mapping as a table under `wasm-port/docs/`.

Outputs:

- file-level reuse matrix
- dependency matrix

## Phase 2: Build the Extraction Pipeline

Purpose:
Keep LinuxCNC source untouched while making port-specific copies available.

Steps:

1. Write extraction scripts in `wasm-port/tools/`.
2. Copy selected upstream files into `wasm-port/vendor/linuxcnc/`.
3. Preserve relative structure where useful, for example:
   - `vendor/linuxcnc/src/emc/rs274ngc/...`
   - `vendor/linuxcnc/src/emc/ini/...`
4. Store every modification as:
   - a patch in `wasm-port/patches/`, or
   - a thin wrapper outside the vendored file
5. Add a verification script that compares upstream file hashes against the vendored source list.

Rules:

- if upstream changes, re-run extraction;
- reapply patches only in the standalone workspace;
- never patch `../linuxcnc` directly.

Outputs:

- reproducible extraction pipeline
- vendored source tree

## Phase 3: Build the Native Extracted Core

Purpose:
Prove the reusable LinuxCNC compute code works outside the full runtime.

Steps:

1. Create `wasm-port/runtime/core/` as the portable core layer.
2. Add wrapper translation units instead of editing vendored files where possible.
3. Start with these subsystems in order:
   - INI parser
   - numeric parameter table
   - named parameter logic
   - interpreter core
   - planner
   - kinematics
4. Build a native CLI harness first, before any WASM build.
5. Replace host edges with abstractions:
   - file provider
   - HAL adapter
   - runtime state provider
   - logging adapter

Outputs:

- portable native core
- native harness executable

Current verified progress:

- `tools/build_native_probes.sh` builds the INI parser probe, interpreter
  state probe, named-parameter harness, RS274 compile probe, and the minimal
  interpreter harness from the standalone `wasm-port/` workspace.
- `tests/native/verify_native_probes.sh` validates that all native probe
  exit codes are zero and that the minimal interpreter harness emits a
  `STRAIGHT_TRAVERSE` canonical event for `G0 X1.0 Y2.0`, plus
  `SET_FEED_RATE` and `STRAIGHT_FEED` canonical events for
  `G1 X3.0 Y4.0 F120.0`.
- The same validation is now fixture-driven through
  `tests/fixtures/gcode/minimal_linear.ngc` and
  `tests/fixtures/canon/minimal_linear.events`.
- `tests/fixtures/gcode/modal_incremental.ngc` and
  `tests/fixtures/canon/modal_incremental.events` add `G90`/`G91`
  distance-mode switching plus modal `G1` carry-forward coverage. The
  temporary wrapper behavior is based on LinuxCNC
  `interp_convert.cc::convert_distance_mode()` and
  `interp_convert.cc::convert_straight()`.
- `tests/fixtures/gcode/position_params.ngc` and
  `tests/fixtures/canon/position_params.events` cover Z-axis motion plus
  current-position parameter visibility for `#5420`, `#5421`, and `#5422`.
  The parameter source basis is LinuxCNC
  `interp_parameter_def.hh` and `interp_namedparams.cc`.
- `tests/fixtures/gcode/canned_cycles.ngc` and
  `tests/fixtures/canon/canned_cycles.events` cover LinuxCNC canned-cycle
  conversion through vendored `interp_cycles.cc`, including `G81`, `G82`,
  `G83`, `G80` cancellation, dwell, peck drilling, and incremental `L`
  repeats.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
  coordinate-system canonical boundary calls emitted by vendored LinuxCNC
  conversion code: `SET_G5X_OFFSET`, `SET_G92_OFFSET`, `SET_XY_ROTATION`,
  `CANON_UPDATE_END_POINT`, `USE_LENGTH_UNITS`, and `SELECT_PLANE`.
  `tests/fixtures/gcode/coordinate_offsets.ngc` and
  `tests/fixtures/canon/coordinate_offsets.events` pin `G55`, active
  `G10 L2`, `G92`, `G92.1`, and `G54` behavior from
  `interp_convert.cc::convert_coordinate_system()`,
  `interp_convert.cc::convert_setup()`, and
  `interp_convert.cc::convert_axis_offsets()`.
- `tests/fixtures/gcode/length_units.ngc` and
  `tests/fixtures/canon/length_units.events` pin `G20`/`G21` unit switching
  and the `USE_LENGTH_UNITS` canonical boundary emitted by vendored
  `interp_convert.cc::convert_length_units()`.
- `tests/fixtures/gcode/plane_selection.ngc` and
  `tests/fixtures/canon/plane_selection.events` pin `G17`/`G18`/`G19` plane
  switching and the `SELECT_PLANE` canonical boundary emitted by vendored
  `interp_convert.cc::convert_set_plane()`.
- `tests/fixtures/gcode_errors/cutter_comp_plane_change.ngc` and
  `tests/fixtures/canon_errors/cutter_comp_plane_change.expected` pin the
  LinuxCNC error path for attempting a plane change after `G41.1` enables
  cutter radius compensation. The source basis is vendored
  `interp_convert.cc::convert_cutter_compensation_on()` and
  `interp_convert.cc::convert_set_plane()`.
- `runtime/core/linuxcnc_wrap/linuxcnc_runtime_state_stubs.cpp` now initializes
  `setup::cutter_comp_firstmove` to the same initial value as vendored
  `rs274ngc_pre.cc`, allowing the first cutter-compensated straight move to
  follow LinuxCNC `interp_convert.cc::convert_straight_comp1()` instead of the
  subsequent-move path. `tests/fixtures/gcode/cutter_comp_motion.ngc` and
  `tests/fixtures/canon/cutter_comp_motion.events` pin positive `G41.1`/`G40`
  compensated straight-feed output through vendored `interp_convert.cc` and
  `interp_queue.cc`.
- `tests/fixtures/gcode/g53_machine_coordinates.ngc` and
  `tests/fixtures/canon/g53_machine_coordinates.events` pin `G53` machine
  coordinate straight-traverse behavior through vendored
  `interp_find.cc::find_ends()` and `interp_convert.cc::convert_straight()`.
  `tests/fixtures/gcode_errors/g53_incremental.ngc` and
  `tests/fixtures/canon_errors/g53_incremental.expected` pin the LinuxCNC
  rejection path for `G53` while incremental distance mode is active.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
  feed/control canonical boundary calls emitted by vendored LinuxCNC
  conversion and queue code: `SET_TRAVERSE_RATE`, `SET_FEED_REFERENCE`,
  `SET_FEED_MODE`, `SET_MOTION_CONTROL_MODE`, and
  `SET_NAIVECAM_TOLERANCE`. `tests/fixtures/gcode/feed_control_modes.ngc`
  and `tests/fixtures/canon/feed_control_modes.events` pin `G93`, `G94`,
  `G95`, `G61`, `G61.1`, and `G64 P/Q` behavior from
  `interp_convert.cc::convert_feed_mode()`,
  `interp_convert.cc::convert_control_mode()`, and `interp_queue.cc`.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
  probe canonical boundary calls emitted by vendored
  `interp_convert.cc::convert_probe()`: `TURN_PROBE_ON`,
  `STRAIGHT_PROBE`, and `TURN_PROBE_OFF`.
  `tests/fixtures/gcode/probe_semantics.ngc` and
  `tests/fixtures/canon/probe_semantics.events` pin `G38.2`, `G38.3`,
  `G38.4`, and `G38.5` probe type handling.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
  speed/feed synchronization and rigid-tap canonical boundary calls emitted by
  vendored `interp_convert.cc::convert_straight()`: `START_SPEED_FEED_SYNCH`,
  `STOP_SPEED_FEED_SYNCH`, and `RIGID_TAP`.
  `tests/fixtures/gcode/threading_sync.ngc` and
  `tests/fixtures/canon/threading_sync.events` pin `G33` spindle-synchronized
  straight feed and `G33.1` rigid tap behavior.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
  NURBS G5 canonical boundary calls emitted by vendored
  `interp_convert.cc::convert_nurbs()`: `NURBS_G5_FEED`.
  `tests/fixtures/gcode/nurbs_g5_semantics.ngc` and
  `tests/fixtures/canon/nurbs_g5_semantics.events` pin `G5.2` control-point
  collection and `G5.3` NURBS feed emission.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
  NURBS G6 canonical boundary calls emitted by vendored
  `interp_convert.cc::convert_nurbs()`: `NURBS_G6_FEED`.
  `tests/fixtures/gcode/nurbs_g6_semantics.ngc` and
  `tests/fixtures/canon/nurbs_g6_semantics.events` pin `G6.2` order,
  interpolation mode, control-point, weight, and K segment handling.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
  spindle-orient canonical boundary calls emitted by vendored LinuxCNC
  conversion and queue code: `ORIENT_SPINDLE` and
  `WAIT_SPINDLE_ORIENT_COMPLETE`. `tests/fixtures/gcode/spindle_orient.ngc`
  and `tests/fixtures/canon/spindle_orient.events` pin `M19 R/P/Q` behavior
  from `interp_convert.cc::convert_m()` and `interp_queue.cc`.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
  tool-table canonical boundary calls emitted by vendored
  `interp_convert.cc::convert_setup_tool()`: `SET_TOOL_TABLE_ENTRY`.
  `tests/fixtures/gcode/tool_table_setup.ngc` and
  `tests/fixtures/canon/tool_table_setup.events` pin `G10 L1` tool offset,
  diameter, front/back angle, and orientation behavior.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
  the tool-data reload boundary call emitted by vendored
  `interp_convert.cc::convert_modal_0()`: `RELOAD_TOOLDATA`.
  `tests/fixtures/gcode/tool_reload.ngc` and
  `tests/fixtures/canon/tool_reload.events` pin `G10 L0` dispatch without
  adding standalone tool-table reload semantics.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
  interpreter state-tag boundary calls emitted by vendored LinuxCNC
  `interp_write.cc::write_state_tag()` and
  `interp_convert.cc::update_tag()`: `UPDATE_TAG`.
  `tests/fixtures/gcode/state_tag_motion.ngc` and
  `tests/fixtures/canon/state_tag_motion.events` pin straight-motion state tag
  emission without deriving modal state in standalone code.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
  rotary indexer lock/unlock boundary calls emitted by vendored
  `interp_convert.cc::issue_straight_index()`: `UNLOCK_ROTARY` and
  `LOCK_ROTARY`. A dedicated `linuxcnc_indexer_harness` pins the LinuxCNC
  single-axis `G0 A...` indexer path, including the surrounding motion-control
  boundary calls, without adding standalone rotary-indexing semantics.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
  the canonical flush boundary emitted by vendored LinuxCNC file-reading
  code: `FINISH`. `tests/fixtures/gcode/percent_file_finish.ngc` and
  `tests/fixtures/canon/percent_file_finish.events` pin `%`-delimited file
  handling from `rs274ngc_pre.cc::open()` and `interp_read.cc::read_text()`.
  The native verification script treats this fixture as file-mode-only
  because `%` is a LinuxCNC program-file delimiter, not a valid MDI command.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
  the reset/queue-drop boundary emitted by vendored
  `rs274ngc_pre.cc::reset()`: `ON_RESET`.
  `tests/fixtures/gcode/file_open_reset.ngc` and
  `tests/fixtures/canon/file_open_reset.events` pin the LinuxCNC file-open
  reset path without adding standalone queue-management semantics.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
  the interpreter initialization boundary emitted by vendored
  `rs274ngc_pre.cc::init()`: `INIT_CANON`. A dedicated
  `linuxcnc_interp_init_harness` calls vendored `Interp::init()` and pins the
  LinuxCNC initialization sequence: `INIT_CANON`, `USE_LENGTH_UNITS`,
  `SET_G5X_OFFSET`, `SET_G92_OFFSET`, `SET_XY_ROTATION`, and
  `SET_FEED_REFERENCE`, without changing the minimal G-code fixture harness
  startup path.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
  comment logging canonical boundary calls emitted by vendored
  `interp_convert.cc::convert_comment()`: `LOGOPEN`, `LOG`, `LOGAPPEND`,
  and `LOGCLOSE`. `tests/fixtures/gcode/comment_logging.ngc` and
  `tests/fixtures/canon/comment_logging.events` pin LinuxCNC comment logging
  dispatch without adding standalone comment semantics.
- `tools/verify_native_linuxcnc_fixture_baseline.sh` now runs an expanded
  side-by-side baseline against upstream `../linuxcnc/bin/rs274`. It normalizes
  native LinuxCNC canonical output for simple motion, offsets, feed-control,
  comment/logging, numbered-parameter, spindle-orient, file-finish, and
  tool-reload fixtures. It now also includes file-open reset and O-word
  subroutine fixture coverage, threading/rigid tap coverage, NURBS dispatch
  boundary coverage, comparable canonical runtime edge calls, and the
  program-end cleanup calls emitted after `M2`. Native output is filtered to
  the event classes each standalone fixture explicitly expects, while upstream
  `rs274` output gaps such as `WAIT`, hidden NURBS control-point detail, and
  standalone modal-state assertions remain covered by the standalone harness
  rather than being treated as side-by-side evidence. The comparison does not
  introduce a project-authored CNC semantics oracle.
- `tests/fixtures/gcode_errors/g1_zero_feed.ngc` and
  `tests/fixtures/canon_errors/g1_zero_feed.expected` pin the negative
  `G1` zero-feed case. The source basis is LinuxCNC
  `interp_convert.cc::convert_straight()` and
  `rs274ngc_return.hh::NCE_CANNOT_DO_G1_WITH_ZERO_FEED_RATE`.
- This proves the current extracted interpreter slice can parse and execute a
  small fixture set through a standalone canonical event sink.
- Important direction change: the minimal wrapper behavior is a migration
  probe, not the program body. Do not expand it into a separate CNC
  implementation. The next work must retire hand-written wrapper semantics and
  move execution through vendored LinuxCNC interpreter source such as
  `interp_convert.cc`, `interp_read.cc`, `interp_check.cc`, and
  `interp_execute.cc`.
- `tools/build_native_probes.sh` now includes
  `linuxcnc_interp_convert_source_probe`, which directly compiles vendored
  `interp_convert.cc`. The required `emcStatus` shim is limited to the native
  status boundary used by `tag_arc()` for machine units; it is not a
  project-authored CNC behavior implementation.
- `tools/build_native_probes.sh` now also includes direct source compile
  probes for vendored `interp_read.cc`, `interp_check.cc`, and
  `interp_execute.cc`. These probes keep the next interpreter-core migration
  work focused on LinuxCNC source entry points and expose missing standalone
  runtime shims before wrapper behavior is expanded.
- The direct source compile probes now cover the remaining vendored
  `rs274ngc` interpreter core files used by the standalone harness:
  `modal_state.cc`, `interp_array.cc`, `interp_internal.cc`,
  `interp_arc.cc`, `interp_inverse.cc`, `interp_cycles.cc`,
  `interp_g7x.cc`, `interp_queue.cc`, `interp_find.cc`,
  `interp_namedparams.cc`, `interp_write.cc`, `interp_o_word.cc`, and
  `rs274ngc_pre.cc`. `rs274ngc_pre.cc` is compiled with the existing
  standalone `UNIT_TEST`/`LINUXCNC_STANDALONE_USE_RS274_PRE_STATE` boundary
  that isolates Python runtime integration from the browser simulation core.
- `tools/build_native_probes.sh` now includes direct source compile probes
  for the vendored LinuxCNC trajectory-planner and posemath files used by
  `linuxcnc_tp_api_probe`: `tp.c`, `tc.c`, `tcq.c`, `spherical_arc.c`,
  `blendmath.c`, `sp_scurve.c`, `ruckig_wrapper.c`, the selected
  `cruckig/*.c` sources, `emcpose.c`, `posemath.cc`, `_posemath.c`, and
  `sincos.c`. These probes use the same `TP_FLAGS` as the standalone TP
  harness, including the existing `-fpermissive` boundary required by
  upstream enum conversions in `tp.c`.
- `tools/verify_vendor_sync.sh` now validates the LinuxCNC source extraction
  boundary before native probes run. It rejects duplicate manifest entries,
  missing or extra vendored files, and byte-level drift between each manifest
  file under `vendor/linuxcnc/` and the matching file under `../linuxcnc/`.
  `tools/source-manifest.txt` was also de-duplicated so the manifest is a
  single authoritative extraction list.
- Direct source compile probes now also cover vendored `interp_base.cc` and
  `gomath.c`. `interp_base.cc` uses a standalone `EMC2_HOME` compile-time
  path boundary for LinuxCNC's dynamic interpreter lookup, and `gomath.c` is
  compiled as C with `gcc` so its LinuxCNC C linkage is preserved. The
  standalone `rtapi.h` shim was made C/C++ compatible for this C-source
  boundary without changing vendored LinuxCNC files.
- `inifile.cc` now has its own direct source compile probe in addition to the
  existing INI parser harness. With this probe, every `.c` and `.cc` file
  listed in `tools/source-manifest.txt` has a native source-level compile
  check under the standalone build boundary.
- `tools/build_native_probes.sh` now emits `build/native/source-probes.tsv`
  while compiling vendored `*_source_probe` targets. The native verification
  script compares that map against every `.c` and `.cc` entry in
  `tools/source-manifest.txt`, so future LinuxCNC source additions fail
  validation unless they also get an explicit standalone source compile probe.
- `tools/verify_no_standalone_cnc_semantics.sh` now guards against
  reintroducing a standalone `Interp::convert_g()` definition outside
  `vendor/linuxcnc/`. This keeps G-code group conversion on the vendored
  LinuxCNC `interp_convert.cc` path instead of allowing the wrapper layer to
  grow another project-authored conversion implementation.
- `docs/scope-and-baseline.md` records the current upstream LinuxCNC commit,
  local tool versions, and fixture baseline. `tools/verify_upstream_baseline.sh`
  now runs before vendor sync validation so extraction drift is checked against
  the intended upstream HEAD, not an accidental checkout change.
- `docs/source-reuse-map.md` records the current vendored LinuxCNC source
  reuse matrix and dependency matrix, tying each extracted group to its
  standalone runtime boundary and validation path.
- `docs/compatibility-validation.md` records the current validation chain,
  source coverage requirements, native harnesses, fixture coverage, and
  validation boundaries.
- `docs/drift-report.md` records current no-drift enforcement, allowed
  standalone runtime boundaries, known gaps, and the current drift conclusion.

## Phase 4: Port INI Parsing Without Editing Upstream

Purpose:
Move LinuxCNC config parsing into the standalone runtime.

Steps:

1. Vendor:
   - `../linuxcnc/src/emc/ini/inifile.cc`
   - `../linuxcnc/src/emc/ini/inifile.hh`
   - `../linuxcnc/src/emc/ini/inifile.h`
2. Add shim headers under `wasm-port/runtime/core/shims/` for small dependencies only.
3. Replace file loading through:
   - wrapper-level adapter, preferred
   - minimal vendored patch only if unavoidable
4. Preserve:
   - `#INCLUDE`
   - relative includes
   - recursion checks
   - line continuation
   - duplicate section merge behavior
   - typed query behavior
5. Add tests for INI semantics under `tests/native/`.

Involved LinuxCNC source:

- `src/emc/ini/inifile.cc`
- `src/emc/ini/inifile.hh`
- `src/emc/ini/inifile.h`

## Phase 5: Port Parameter Tables and Variable Files

Purpose:
Preserve LinuxCNC numeric parameter behavior exactly enough for simulation.

Steps:

1. Vendor:
   - `../linuxcnc/src/emc/rs274ngc/interp_parameter_def.hh`
   - `../linuxcnc/src/emc/rs274ngc/interp_array.cc`
   - `../linuxcnc/src/emc/rs274ngc/interp_internal.hh`
   - `../linuxcnc/src/emc/rs274ngc/rs274ngc_pre.cc`
2. Extract:
   - `setup.parameters[]`
   - `required_parameters[]`
   - `readonly_parameters[]`
   - parameter file load/save logic
3. Replace native file writes with a standalone file service abstraction.
4. Keep LinuxCNC’s parameter text format in phase 1.
5. Add regression tests for:
   - missing file
   - out-of-order parameters
   - zero-fill behavior
   - required parameter persistence
   - read-only protection

Involved LinuxCNC source:

- `src/emc/rs274ngc/interp_parameter_def.hh`
- `src/emc/rs274ngc/interp_array.cc`
- `src/emc/rs274ngc/interp_internal.hh`
- `src/emc/rs274ngc/rs274ngc_pre.cc`

## Phase 6: Port Named Parameters and Interpreter State

Purpose:
Preserve LinuxCNC variable semantics and controller-visible state.

Steps:

1. Vendor:
   - `../linuxcnc/src/emc/rs274ngc/interp_namedparams.cc`
   - `../linuxcnc/src/emc/rs274ngc/interp_internal.hh`
   - `../linuxcnc/src/emc/rs274ngc/interp_fwd.hh`
   - selected interpreter files needed by setup/state handling
2. Preserve:
   - `context.named_params`
   - `PA_READONLY`
   - `PA_GLOBAL`
   - `PA_USE_LOOKUP`
   - `PA_FROM_INI`
   - built-in named parameters from `init_named_parameters()`
3. Preserve lookup order:
   - local
   - global
   - `_ini[...]`
   - `_hal[...]`
   - optional Python providers later
4. Export state outward rather than redesigning it in JS.
5. Add regression cases for:
   - local/global scoping
   - built-in state variables
   - `_ini[...]`
   - `_hal[...]`
   - read-only errors

Involved LinuxCNC source:

- `src/emc/rs274ngc/interp_namedparams.cc`
- `src/emc/rs274ngc/interp_internal.hh`
- `src/emc/rs274ngc/interp_fwd.hh`
- `src/emc/rs274ngc/interpmodule.cc`

## Phase 7: Replace HAL Runtime With a Simulation HAL Adapter

Purpose:
Keep LinuxCNC interpreter-visible HAL behavior without migrating HAL itself.

Steps:

1. Do not vendor the whole HAL runtime as a target runtime dependency.
2. Use LinuxCNC HAL source as reference only for interface semantics:
   - `../linuxcnc/src/hal/hal.h`
   - `../linuxcnc/src/hal/halmodule.cc`
   - `../linuxcnc/src/emc/ini/inihal.cc`
3. Define a standalone HAL adapter interface:
   - lookup by name
   - typed numeric value
   - connection/existence status
4. Make `_hal[...]` reads resolve through this adapter.
5. Seed the adapter from simulation config and runtime state.
6. Add tests for:
   - existing names
   - missing names
   - disconnected signals
   - type conversion

Involved LinuxCNC source:

- `src/hal/hal.h`
- `src/hal/halmodule.cc`
- `src/emc/rs274ngc/interp_namedparams.cc`
- `src/emc/ini/inihal.cc`

## Phase 8: Port the Interpreter Core

Purpose:
Build the standalone G-code execution engine without touching upstream files.

Steps:

1. Vendor selected files from `../linuxcnc/src/emc/rs274ngc/`.
2. Keep original source as intact as possible in `vendor/`.
3. Add wrappers or minimal patches only in the standalone area.
4. Replace these external edges:
   - file open/read
   - world sync
   - HAL reads
   - logging
   - runtime callbacks
5. Preserve:
   - modal state
   - subroutines
   - offsets
   - tool handling
   - parameter interactions
   - error semantics
6. Add a canonical event sink interface in standalone code.

Involved LinuxCNC source:

- `src/emc/rs274ngc/interp_execute.cc`
- `src/emc/rs274ngc/interp_read.cc`
- `src/emc/rs274ngc/interp_check.cc`
- `src/emc/rs274ngc/interp_convert.cc`
- `src/emc/rs274ngc/interp_cycles.cc`
- `src/emc/rs274ngc/interp_find.cc`
- `src/emc/rs274ngc/interp_write.cc`
- `src/emc/rs274ngc/interp_o_word.cc`
- `src/emc/rs274ngc/rs274ngc_pre.cc`
- `src/emc/rs274ngc/rs274ngc_interp.hh`

## Phase 9: Port Planner and Kinematics

Purpose:
Preserve LinuxCNC motion planning and 5-axis simulation behavior.

Steps:

1. Vendor selected planner files from `../linuxcnc/src/emc/tp/`.
2. Vendor selected kinematics files from:
   - `../linuxcnc/src/emc/kinematics/`
   - selected `.comp` sources mirrored into standalone code where needed
3. Replace loadable-module assumptions with a registry in the standalone runtime.
4. Keep original math and state logic intact as far as practical.
5. Add tests for:
   - planner outputs
   - forward/inverse kinematics
   - 5-axis world/joint transforms
   - LinuxCNC five-axis `M428`/`M429`/`M430` remap assets and switchkins
     machine configuration links

Current five-axis switchkins status:

1. Vendored LinuxCNC `5axiskins`, `xyzac-trt-kins`, and `xyzbc-trt-kins`
   sources are compiled and probed natively.
2. Vendored LinuxCNC sample machine assets now cover bridge-mill,
   table-dual-rotary, and table-rotary-tilting INI/HAL/tool/demo/remap
   files.
3. `M428`, `M429`, and `M430` remain LinuxCNC `REMAP` entries backed by
   vendored LinuxCNC NGC subroutines; do not replace them with standalone
   M-code handlers.
4. `linuxcnc_remap_parse_harness` now links vendored `interp_remap.cc` and
   validates the LinuxCNC `Interp::parse_remap()` path for `xyzac-trt`,
   `xyzbc-trt`, `xyzab-tdr`, and bridge-mill NGC remap descriptors, matching
   each vendored machine's LinuxCNC-defined `M428`/`M429`/`M430` set.
5. `linuxcnc_remap_hal_sync_harness` validates vendored LinuxCNC `M68` and
   `M66` synchronization through the standalone HAL adapter boundary.
6. `linuxcnc_5axis_remap_execute_harness` executes the `xyzac-trt` and
   `xyzbc-trt` switchkins `M429 -> M428 -> M430 -> M429` paths plus the
   `xyzab-tdr` two-remap `M429 -> M428 -> M429` path and the bridge-mill
   `M429 -> M428 -> M430 -> M429` path where M428 selects default
   bridge-mill kinematics, loads vendored machine tool tables through
   LinuxCNC `tooldata_load()`, and 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`, and `xyzab-tdr-demo.ngc`, plus bridge-mill
   `5axisgui.ngc` through LinuxCNC remap/file execution paths.
7. The WASM interpreter core now links vendored `interp_remap.cc` plus the
   Python-only runtime edge stub, and the Node WASM smoke 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 SDK C ABI without JavaScript M-code or kinematics semantics.
8. Browser smoke and the INI panel UI now expose the same validated 5-axis
   remap execution path by copying vendored LinuxCNC sample-machine files into
   the interpreter WASM filesystem and calling the SDK C ABI.
9. LinuxCNC upstream `tests/remap/duplicate-o-word` and
   `tests/remap/m30-interaction` are now vendored unchanged and validated
   through the generic remap file execution path. Native, Node WASM, and
   browser interpreter smokes load the vendored INI, program, and remap
   subroutines, then call vendored `Interp::parse_remap()`, `open()`,
   `read()`, and `execute()`; standalone code only supplies the INI/file path
   boundary.

Next work:

1. Continue broadening source-backed remap coverage through LinuxCNC upstream
   tests that can run through NGC REMAP/O-word/file execution without Python
   semantics.
2. Evaluate additional vendored LinuxCNC five-axis sample machines and demos
   after the TRT boat/impeller, TDR demo, and bridge-mill demo paths are
   stable in native, Node WASM, browser, and UI remap paths.

Involved LinuxCNC source:

- `src/emc/tp/tp.c`
- `src/emc/tp/tc.c`
- `src/emc/tp/tcq.c`
- `src/emc/tp/blendmath.c`
- `src/emc/tp/sp_scurve.c`
- `src/emc/tp/ruckig_wrapper.c`
- `src/emc/kinematics/*.c`
- `src/hal/components/xyzab_tdr_kins.comp`
- `src/hal/components/xyzacb_trsrn.comp`
- `src/hal/components/xyzbca_trsrn.comp`
- `configs/sim/axis/vismach/5axis/bridgemill/*`
- `configs/sim/axis/vismach/5axis/table-dual-rotary/*`
- `configs/sim/axis/vismach/5axis/table-rotary-tilting/*`

## Phase 10: Build the Standalone WASM Program

Purpose:
Keep the migrated program completely outside native LinuxCNC management.

Steps:

1. Build the standalone native core first.
2. Add a standalone WASM export layer under:
   - `wasm-port/runtime/core/`
   - `wasm-port/runtime/sdk/`
3. Use `vendor/` sources plus standalone wrappers as build input.
4. Do not compile from `../linuxcnc` directly in the final WASM product build.
5. Emit:
   - `wasm` module
   - JS loader
   - standalone SDK

Outputs:

- independently managed WASM simulation core

## Phase 11: Build the Frontend and OPFS Layer

Purpose:
Keep the standalone port program separate from LinuxCNC GUI code and storage.

Steps:

1. Build frontend files under:
   - `wasm-port/runtime/ui/`
   - `wasm-port/runtime/opfs/`
2. Implement:
   - HTML shell
   - JavaScript control panel
   - OPFS persistence
   - file import/export
   - machine/controller state views
3. Keep LinuxCNC GUI code reference-only.
4. Ensure the frontend depends only on the standalone SDK, not on native LinuxCNC binaries.

Outputs:

- standalone browser program

## Phase 12: Validate Drift Against Upstream

Purpose:
Prove the standalone port still tracks LinuxCNC behavior.

Steps:

1. Use native LinuxCNC tests and fixtures as semantic baselines.
2. Compare:
   - LinuxCNC native behavior
   - standalone native extracted core
   - standalone WASM behavior
3. Maintain a drift report.
4. Re-run extraction if upstream source changes.
5. Keep patches small and traceable.

Outputs:

- drift report
- compatibility regression suite

## Management Rules For Daily Development

Use these rules continuously:

1. Never edit files under `linuxcnc/` as part of the port.
2. Any needed modification to upstream logic must be applied to vendored copies only.
3. Any upstream sync must be script-driven and repeatable.
4. Every shim must be documented.
5. Every local patch against vendored source must be stored as a patch file or clearly isolated wrapper.

## Recommended Document Set

Keep these documents under `wasm-port/docs/`:

- `scope-and-baseline.md`
- `source-reuse-map.md`
- `state-porting-strategy.md`
- `wasm-build-strategy.md`
- `frontend-architecture.md`
- `opfs-file-model.md`
- `compatibility-validation.md`
- `drift-report.md`

## Immediate Next Step

Continue from the current verified extracted-core baseline without adding
project-authored CNC semantics:

1. Keep all `Interp::...` interpreter member behavior on vendored LinuxCNC
   source. Standalone code may only provide documented runtime-edge stubs such
   as the current Python/remap boundary.
2. Expand WASM interpreter coverage by routing more existing native fixture
   paths through `runtime/core/linuxcnc_wrap/linuxcnc_interp_wasm.cpp`, using
   the same vendored interpreter source set as the native harness.
3. Promote remaining standalone-only fixture expectations to native LinuxCNC
   baselines where possible, especially adapter-heavy paths such as INI/HAL
   named parameters, tool-change host state, and richer machine session state.
4. Move browser-facing work through SDK and OPFS adapters only after the core
   behavior is validated against native LinuxCNC or vendored-source harnesses.
5. Before adding any CNC feature, update `tools/source-manifest.txt`, extract
   the LinuxCNC source file, add a source probe or harness, and document the
   reuse boundary in `docs/source-reuse-map.md`.
