# text16.txt - LinuxCNC WASM/browser port continuation

Created: 2026-06-16 CST

This file continues from `text15.txt`. Use it for the next execution batches.

## Current Repository State

- Branch: `master`
- Remote sync state at creation: `master...origin/master`
- Latest commit at creation:

```text
69ccf93 支持真实G代码程序执行
```

- Working tree at creation: clean
- Active project tracker still exists at `PROJECT_COMPLETION_TRACKER.md`
- Previous continuation file: `text15.txt`

## Current Priority

The first priority remains the real browser CNC simulation page:

```text
wasm-port/runtime/ui/simulation/index.html
```

Primary supporting docs:

```text
wasm-port/docs/real-browser-simulation-priority.md
wasm-port/docs/axis-style-simulation-implementation.md
PROJECT_COMPLETION_TRACKER.md
```

Do not switch back to dashboard-only metadata work unless it directly creates a
gate or unblocks the browser simulation page.

## Completed Since text15 Start

The browser simulation page now has:

- AXIS-style browser shell:
  - titlebar;
  - File / Machine / View / Help menubar;
  - toolbar;
  - Manual Control / MDI tabs;
  - Preview / DRO tabs;
  - G-code pane;
  - machine-state pane;
  - bottom status bar.
- LinuxCNC-backed WASM execution through `createLinuxCncInterpSdk()`.
- Built-in test program selector.
- Built-in program directory:

```text
wasm-port/runtime/ui/simulation/programs/
```

- Current built-in program modules:
  - `square-linear.js`;
  - `pocket-z.js`;
  - `incremental-loop.js`;
  - `arc-g2-g3.js`;
  - `drill-g81.js`.
- Program inventory export:

```text
wasm-port/runtime/ui/simulation/programs/index.js
```

- Reset / Step Back / Play / Step Forward / Finish playback over LinuxCNC
  canonical motion events.
- Active G-code line highlighting.
- Active and executed motion row highlighting.
- Live XYZ/ABC readout.
- Full toolpath and executed-path SVG rendering.
- Moving toolhead marker.
- Real operator-provided G-code execution:
  - `linuxCncRealSimulationApi.runProgramText(programText, metadata)`;
  - `linuxCncRealSimulationApi.loadProgramFile(file)`;
  - toolbar `Open` file input for `.ngc/.nc/.tap/.gcode/.txt`.
- Editable AXIS-style G-code pane:
  - loaded file/custom/built-in program text is visible in a textarea;
  - editor text can be changed before execution;
  - toolbar `Run Editor Text` executes the current editor contents;
  - `getProgramText()`, `setProgramText()`, and `runEditorProgramText()` are
    exposed on the browser API.
- Program source metadata:
  - `program.source`;
  - `program.sourceLabel`;
  - `program.filename`;
  - `document.body.dataset.simulationProgramSource`.

## Current Browser API

Available on the simulation page:

```js
window.linuxCncRealSimulationApi
```

Current methods:

- `getState()`
- `getPrograms()`
- `runProgramById(programId)`
- `runProgramText(programText, metadata)`
- `loadProgramFile(file)`
- `getProgramText()`
- `setProgramText(text, metadata)`
- `runEditorProgramText()`
- `getPlaybackFrame()`
- `resetPlayback()`
- `stepPlayback(delta)`
- `play()`
- `pause()`
- `finishPlayback()`

All G-code execution must continue to go through LinuxCNC-backed WASM. Browser
JS must not interpret G-code or implement planner/machine semantics.

## Validation Baseline

The latest full validation completed successfully:

```text
real_simulation_programs_node_smoke=ok
ui_node_smokes=ok
browser_real_simulation_page_smoke=ok
axis_style_simulation_docs_node_smoke=ok
real_browser_simulation_priority_docs_node_smoke=ok
project_release_gate=ok
```

Useful focused commands:

```bash
wasm-port/tests/ui/node/verify_real_simulation_programs.sh
wasm-port/tests/ui/node/verify_ui_node_smokes.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh
wasm-port/tests/docs/node/verify_axis_style_simulation_docs.sh
wasm-port/tests/docs/node/verify_real_browser_simulation_priority_docs.sh
```

Full gate:

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

## Semantic Boundary

LinuxCNC remains the only CNC semantic source.

Browser code may:

- render UI layout and interaction state;
- read user-selected G-code files/text;
- call the SDK/WASM boundary;
- render LinuxCNC-produced canonical output;
- animate playback over LinuxCNC-produced canonical motion events;
- stage future OPFS/session files when that integration is added.

Browser code must not implement:

- G-code interpretation;
- modal semantics;
- tool/cutter compensation semantics;
- parameter semantics;
- kinematics;
- remap behavior;
- planner behavior;
- machine-control behavior not backed by LinuxCNC/WASM or validated session APIs.

## Next Recommended Batches

### 1. Real G-code Editor Pane

Status: complete.

Implemented:

- show loaded file/custom text in an editor area;
- allow editing before run;
- add `Run Editor Text`;
- preserve line numbers and active-line highlighting;
- expose `linuxCncRealSimulationApi.getProgramText()`;
- expose `linuxCncRealSimulationApi.setProgramText(text, metadata)`.
- expose `linuxCncRealSimulationApi.runEditorProgramText()`.

Validation:

- browser smoke sets editor text, runs it, and verifies changed LinuxCNC
  canonical output.

### 2. OPFS Program Persistence

Connect real G-code programs to OPFS:

- save current program text to OPFS;
- load program text from OPFS;
- show OPFS path in status/readiness;
- keep browser file loading as an import path, not the only path.

Validation:

- browser smoke load file -> save OPFS -> reload OPFS -> run -> playback.

### 3. AXIS DRO/Modal State Expansion

Improve the DRO panel without inventing unsupported data:

- derive any available fields from LinuxCNC output or validated summaries;
- show unavailable fields as `n/a`;
- add tests that prevent silently fabricated G54/G92/TLO/DTG values.

Validation:

- browser smoke verifies no `undefined` and no fabricated unavailable values.

### 4. Preview Controls

Add AXIS-like preview controls:

- zoom fit;
- reset view;
- optional pan/viewport scale state;
- keep SVG nonblank and stable.

Validation:

- browser smoke verifies viewBox changes/reset and toolhead/path remain visible.

### 5. Machine/Session Integration

Use the existing OPFS/session workflow:

- load machine session;
- show machine file readiness;
- stage INI/parameter/tool-table/program into WASM;
- block run with explicit reasons when readiness is missing.

Validation:

- OPFS/session browser smoke covers load -> run -> playback.

## Adding More Built-In Test Programs

To add a built-in test:

1. Add a module under:

```text
wasm-port/runtime/ui/simulation/programs/
```

2. Export an object with:

```js
{
  id,
  label,
  category,
  text,
  expectedMotionTypes,
}
```

3. Import it in:

```text
wasm-port/runtime/ui/simulation/programs/index.js
```

4. Add it to `SIMULATION_TEST_PROGRAMS`.

5. Run:

```bash
wasm-port/tests/ui/node/verify_real_simulation_programs.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh
```

## Commit / Upload Rule

After each completed implementation batch:

1. Run focused smoke(s).
2. Run `wasm-port/tests/host/verify_project_release_gate.sh` when feasible.
3. Commit with a Chinese title and conclusion body.
4. Push to:

```text
http://154.8.160.151:3000/wangdequan/cnc_wams.git
```

## Immediate Suggested Start

Start with "OPFS Program Persistence" because the page can now load, edit, and
run real G-code text/files, but programs are not yet saved to or restored from
OPFS from within the AXIS shell.

Updated after batch 9: compact run summary is implemented. The next recommended
batch is adding machine/session run summaries to saved OPFS/session artifacts or
making the preview controls more AXIS-like with fit-state persistence.

## Execution Record

### 一、2026-06-16 Real G-code Editor Pane

按 `text16.txt` 的 Immediate Suggested Start 推进，完成 AXIS shell 内真实 G-code 编辑区。

本批新增实质能力：

- `runtime/ui/simulation/index.html` 新增 editable G-code pane：
  - `data-axis-shell="program-editor"`；
  - `textarea[data-program-editor]`；
  - `data-program-editor-status`；
- 工具栏新增：
  - `Run Editor Text`；
- 页面状态新增 `programEditorState`；
- 内置程序加载、真实文本执行、真实文件加载都会同步到 editor textarea；
- editor textarea 修改后可直接运行；
- `window.linuxCncRealSimulationApi` 新增：
  - `getProgramText()`；
  - `setProgramText(text, metadata)`；
  - `runEditorProgramText()`；
- browser smoke 新增验证：
  - 初始内置程序会同步到 editor；
  - `runProgramText()` 后 editor 同步自定义文本；
  - `loadProgramFile()` 后 editor 同步文件文本和 filename metadata；
  - `setProgramText()` 修改 editor 内容；
  - `runEditorProgramText()` 通过 LinuxCNC WASM 执行修改后的 G-code，并验证 canonical output
    出现修改后的坐标。

关键验证：

```text
browser_real_simulation_page_smoke=ok
real_simulation_programs_node_smoke=ok
```

语义边界：

- editor 只保存和显示 G-code 文本；
- G-code 执行仍通过 LinuxCNC-backed WASM；
- JS 不解释 G-code、不规划刀路、不推导 modal/machine semantics。

### 二、2026-06-16 AXIS DRO/Modal State and Preview Controls

继续按 AXIS 实现文档推进，完成 Phase 3/4 的可落地部分。

本批新增实质能力：

- `runtime/ui/simulation/simulation-app.js` 新增：
  - `createModalState(resultText)`；
  - `createDroState(frame)`；
  - `zoomToolpathViewBox(viewBox, zoom)`；
- modal display 不再使用硬编码 active G-code 文本，改为显示 LinuxCNC
  `canon_event=UPDATE_TAG` 字段；
- DRO 显示：
  - actual X/Y/Z/A/B/C 来自 LinuxCNC canonical motion playback frame；
  - DTG/G54/G92/TLO/velocity 在当前 canonical 输出不可用时明确显示 `n/a`；
- `window.linuxCncRealSimulationApi` 新增：
  - `getDroState()`；
  - `getModalState()`；
  - `fitPreview()`；
  - `zoomPreview(factor)`；
  - `resetPreview()`；
- preview toolbar 新增 Fit / Zoom In / Zoom Out / Reset；
- SVG preview 保留 full path、executed path、toolhead，并支持 viewBox 缩放/恢复。

测试覆盖：

- Node smoke 验证：
  - modal state 来自 `UPDATE_TAG`；
  - unavailable DRO 字段保持显式 `n/a`；
  - viewBox zoom 会收窄；
- browser smoke 验证：
  - API 暴露；
  - modal DOM 显示 LinuxCNC 字段；
  - DRO 不伪造 DTG/G54/G92/TLO/velocity；
  - preview zoom 改变 viewBox，reset 恢复 fit viewBox；
  - 现有 built-in/custom/file/editor G-code 执行继续走 LinuxCNC WASM。

关键验证：

```text
real_simulation_programs_node_smoke=ok
axis_style_simulation_docs_node_smoke=ok
browser_real_simulation_page_smoke=ok
```

语义边界：

- JS 只解析 LinuxCNC canonical text 中已经输出的 `UPDATE_TAG` 字段用于显示；
- JS 不解释 G-code，不推导 modal 语义，不计算 DTG/G54/G92/TLO/velocity；
- preview zoom/reset 只改变 SVG viewBox，不改变 LinuxCNC 生成的 motion events。

### 三、2026-06-16 AXIS OPFS Program Persistence

继续按 AXIS 实现文档推进，完成 Phase 2 的 OPFS program persistence 闭环。

本批新增实质能力：

- AXIS editable G-code pane 新增：
  - OPFS G-code filename 输入；
  - `Save OPFS`；
  - `Load OPFS`；
- 页面复用既有 OPFS helpers：
  - `saveGcodeProgram()`；
  - `loadGcodeProgram()`；
  - `gcodeProgramPath()`；
- `window.linuxCncRealSimulationApi` 新增：
  - `getOpfsProgramState()`；
  - `saveProgramToOpfs(filename)`；
  - `loadProgramFromOpfs(filename)`；
- OPFS load 会调用现有 `runProgramText()`，因此 reloaded program 仍通过
  LinuxCNC-backed WASM 执行；
- OPFS metadata 写入 program state：
  - `program.source = "opfs"`；
  - `program.opfsPath = "linuxcnc/gcode/<filename>"`；
  - statusbar 显示 `Loaded OPFS: ...`。

browser smoke 新增验证：

- 设置 editor G-code；
- 保存到 `linuxcnc/gcode/axis-opfs-roundtrip.ngc`；
- 修改 editor 为不同内容；
- 从 OPFS 重新加载保存的程序；
- 验证 reloaded canonical output 出现保存内容的坐标；
- 验证 editor、body dataset、statusbar 和 OPFS state metadata 同步。

关键验证：

```text
browser_real_simulation_page_smoke=ok
real_simulation_programs_node_smoke=ok
```

语义边界：

- OPFS 只持久化 G-code 文本；
- OPFS load 后仍走 LinuxCNC-backed WASM interpreter；
- JS 不解释 G-code，不推导机床状态，不绕过 canonical output。

### 四、2026-06-16 AXIS Machine/Session Readiness

继续按 AXIS 实现文档推进，完成 Phase 5 的 readiness 前半段。

本批新增实质能力：

- Manual Control panel 新增 `data-axis-shell="machine-session"` 区域：
  - machine id；
  - session id；
  - G-code filename；
  - `Check Session`；
  - readiness phase/missing/path readout；
- 页面复用既有 OPFS/session helper：
  - `readMachineSessionReadiness()`；
- `window.linuxCncRealSimulationApi` 新增：
  - `getMachineReadiness()`；
  - `checkMachineSessionReadiness(options)`；
- readiness DOM 同步：
  - `document.body.dataset.machineSessionReady`；
  - `data-machine-readiness-phase`；
  - `data-machine-readiness-missing`；
  - `data-machine-readiness-path="ini"`；
  - `data-machine-readiness-path="gcode"`。

browser smoke 新增验证：

- 检查不存在的 machine/session，确认 readiness 为 blocked，并报告 missing
  `ini` 和 `gcode`；
- 通过既有 OPFS helpers 写入：
  - machine INI；
  - parameter file；
  - tool table；
  - G-code；
  - machine session snapshot；
- 再次调用 AXIS API 检查 readiness，确认 phase 为 `ready`，missing 为空；
- 验证 readiness paths 与 OPFS path model 一致，并同步到 API/body/DOM。

关键验证：

```text
browser_real_simulation_page_smoke=ok
```

语义边界：

- readiness 只检查 OPFS 文件存在和 snapshot envelope；
- JS 不合成 INI/tool/parameter/G-code 语义；
- 本批不加载或执行完整 machine session，只把可运行前置条件暴露给 AXIS shell。

### 五、2026-06-16 AXIS Machine/Session WASM Staging

继续按 AXIS 实现文档推进，完成 Phase 5 的 ready session load/stage 能力。

本批新增实质能力：

- Manual Control panel 的 machine-session 区域新增：
  - `Load Session`；
  - loaded INI WASM path；
  - loaded parameter WASM path；
  - loaded tool-table WASM path；
- 页面复用既有 LinuxCNC-backed helpers：
  - `createLinuxCncInterpSdk()`；
  - `createLinuxCncIniSdk()`；
  - `loadMachineSessionFromOpfs()`；
- `window.linuxCncRealSimulationApi` 新增：
  - `getMachineSessionLoadState()`；
  - `loadReadyMachineSession(options)`；
- `loadReadyMachineSession()` 先调用 readiness check；blocked 时返回 blocked
  load state，不 stage；
- ready 时创建 LinuxCNC interp/INI SDK，调用 `loadMachineSessionFromOpfs()`，
  把 OPFS INI/parameter/tool-table staged 到 WASM filesystem；
- DOM 同步：
  - `document.body.dataset.machineSessionLoaded`；
  - `data-machine-session-loaded-path="ini"`；
  - `data-machine-session-loaded-path="parameters"`；
  - `data-machine-session-loaded-path="toolTable"`。

browser smoke 新增验证：

- 在 ready machine/session 基础上调用 `loadReadyMachineSession()`；
- 使用显式 WASM paths：
  - `/work/axis-smoke/machine.ini`；
  - `/work/axis-smoke/linuxcnc.var`；
  - `/work/axis-smoke/tool.tbl`；
- 验证 API 返回 loaded state；
- 验证 API/body/DOM 中 loaded paths 同步。

关键验证：

```text
browser_real_simulation_page_smoke=ok
```

语义边界：

- session load/stage 完全通过既有 LinuxCNC-backed SDK 和 OPFS bridge；
- JS 不解释 INI/tool-table/parameter/G-code；
- 本批只 stage machine session，不改变 program execution semantics。

### 六、2026-06-16 AXIS Loaded-Session Program Execution

继续按 AXIS 实现文档推进，让 loaded machine session 真正参与程序执行。

本批新增实质能力：

- `simulation-app.js` 新增：
  - `runLinuxCncProgram(interp, programText, options)`；
  - `runRealBrowserSimulation({ interp, iniPath })`；
  - state.execution：
    - `mode: "linuxcnc-wasm"`；
    - `mode: "linuxcnc-wasm-with-ini"`；
    - `iniPath`；
- AXIS page 在 `loadReadyMachineSession()` 成功后保留 staged interpreter；
- 后续 built-in/custom/editor/OPFS text execution 自动使用：
  - staged interpreter；
  - loaded session INI WASM path；
  - LinuxCNC-backed `runProgramWithIni()`；
- program source label 会显示 `Session INI: <wasm path>`。

browser smoke 新增验证：

- 在 ready session staged 后设置 editor G-code；
- 调用 `runEditorProgramText()`；
- 验证 returned state：
  - `execution.mode === "linuxcnc-wasm-with-ini"`；
  - `execution.iniPath === "/work/axis-smoke/machine.ini"`；
  - canonical output 包含修改后的 G-code 坐标；
  - source label 包含 loaded session INI path。

Node smoke 新增验证：

- `runLinuxCncProgram()` 无 INI 时调用 `runProgram()`；
- 有 INI 时调用 `runProgramWithIni()`；
- `runRealBrowserSimulation({ interp, iniPath })` 返回
  `linuxcnc-wasm-with-ini` execution metadata。

关键验证：

```text
real_simulation_programs_node_smoke=ok
browser_real_simulation_page_smoke=ok
```

语义边界：

- JS 只选择 LinuxCNC-backed interpreter SDK 方法；
- G-code 解释仍由 LinuxCNC WASM 执行；
- INI/tool/parameter state 仍由 existing OPFS bridge 和 LinuxCNC SDK staged/loaded。

### 七、2026-06-16 AXIS Run Mode Status and OPFS Session-Backed Execution

继续按 AXIS 实现文档推进，补齐 visible run-mode 状态，并扩展 OPFS program
在 loaded session 下的执行覆盖。

本批新增实质能力：

- toolbar status strip 新增：
  - `Run mode Standalone`；
  - loaded session 执行后显示 `Session-backed: <iniPath>`；
- statusbar 同步显示 run mode；
- body dataset 新增：
  - `data-run-mode="standalone"`；
  - `data-run-mode="session-backed"`；
- `window.linuxCncRealSimulationApi` 新增：
  - `getRunMode()`；
- 每次 program render 后同步 run-mode UI/API 状态。

browser smoke 新增验证：

- 初始页面 run mode 为 standalone；
- loaded session editor run 后 run mode 切换为 session-backed；
- OPFS-loaded G-code 在 session-backed 模式下执行：
  - 保存 `axis-session-opfs.ngc` 到 OPFS；
  - 修改 editor 为不同内容；
  - 从 OPFS load/run；
  - 验证 `execution.mode === "linuxcnc-wasm-with-ini"`；
  - 验证 canonical output 来自 OPFS 保存内容；
  - 验证 source label 同时包含 OPFS path 和 Session INI path。

关键验证：

```text
real_simulation_programs_node_smoke=ok
browser_real_simulation_page_smoke=ok
```

语义边界：

- run mode 只反映当前 LinuxCNC-backed execution path；
- OPFS program load/run 仍通过 LinuxCNC-backed `runProgramWithIni()`；
- JS 不解释 G-code，也不实现 session semantics。

### 八、2026-06-16 AXIS Run Mode Session Toggle

继续按 AXIS 实现文档推进，补齐 standalone/session-backed 显式切换。

本批新增实质能力：

- Manual Control machine-session actions 新增：
  - `Use Session` checkbox；
- run mode 逻辑新增：
  - loaded session 存在且 Use Session 开启时，程序执行走 loaded session INI；
  - Use Session 关闭时，即使 session staged，也回到 standalone execution；
- `window.linuxCncRealSimulationApi` 新增：
  - `setUseLoadedSession(value)`；
- `getRunMode()` 返回：
  - `mode`；
  - `label`；
  - `iniPath`；
  - `useLoadedSession`；
  - `sessionLoaded`。

browser smoke 新增验证：

- loaded session editor run 后为 session-backed；
- 调用 `setUseLoadedSession(false)` 后 checkbox 同步关闭；
- 再次运行 editor G-code，确认：
  - `execution.mode === "linuxcnc-wasm"`；
  - `execution.iniPath === null`；
  - body run mode 维持 standalone；
- 调用 `setUseLoadedSession(true)` 后 checkbox 同步开启；
- 后续 OPFS program load/run 再次使用 loaded session INI。

关键验证：

```text
real_simulation_programs_node_smoke=ok
browser_real_simulation_page_smoke=ok
```

语义边界：

- Use Session 只选择 LinuxCNC-backed execution method；
- 不修改 staged machine session 内容；
- JS 不解释 G-code/INI/tool/parameter semantics。

### 九、2026-06-16 AXIS Run Summary

继续按 AXIS 实现文档推进，增加 compact run summary，方便操作时判断当前运行上下文。

本批新增实质能力：

- Machine State readout 新增：
  - Execution Mode；
  - Session INI；
  - OPFS Program；
  - Session State；
- `window.linuxCncRealSimulationApi` 新增：
  - `getRunSummary()`；
- run summary 字段：
  - `apiName: "real-browser-simulation-run-summary"`；
  - `executionMode`；
  - `runMode`；
  - `sessionIniPath`；
  - `opfsProgramPath`；
  - `readinessPhase`；
  - `sessionLoadPhase`；
  - `programSource`；
- readiness/load/run/toggle 后都会同步 run summary DOM。

browser smoke 新增验证：

- 初始 summary 为 standalone `linuxcnc-wasm`；
- session ready/load 后 summary 显示 `ready / loaded`；
- session-backed editor run 后 summary 显示：
  - `linuxcnc-wasm-with-ini`；
  - `/work/axis-smoke/machine.ini`；
- Use Session 关闭后 standalone run summary 回到：
  - `linuxcnc-wasm`；
  - Session INI `n/a`；
- session-backed OPFS run 后 summary 显示：
  - OPFS program path `linuxcnc/gcode/axis-session-opfs.ngc`。

关键验证：

```text
real_simulation_programs_node_smoke=ok
browser_real_simulation_page_smoke=ok
```

语义边界：

- run summary 只汇总已有 LinuxCNC-backed execution/session/readiness state；
- 不推导 CNC semantics；
- 不改变 execution path。

### 十、2026-06-17 AXIS MDI Text and Preview Pan

继续按 AXIS 实现文档推进，补齐 MDI scratch 文本路径和 preview pan 控制。

本批新增实质能力：

- AXIS MDI panel 新增：
  - `data-mdi-program-text` scratch G-code textarea；
  - `Load MDI Text`；
  - `Run MDI Text`；
  - `data-mdi-status`；
- `window.linuxCncRealSimulationApi` 新增：
  - `loadProgramText(text, metadata)`；
  - `runMdiProgramText(text)`；
- `loadProgramText()` 只把 operator text staging 到 AXIS G-code pane，
  不执行、不解释 G-code；
- `runMdiProgramText()` 复用既有 `runProgramText()`，继续走 LinuxCNC-backed
  WASM execution；
- preview controls 新增 pan left/right/up/down；
- `simulation-app.js` 新增纯显示 helper：
  - `panToolpathViewBox(viewBox, offset)`；
- browser API 新增：
  - `panPreview(dx, dy)`；
- `fitPreview()` / `resetPreview()` 清除 pan offset，zoom/pan 不改变 canonical
  motion state。

browser smoke 新增验证：

- MDI 控件存在；
- `loadProgramText()` 可以 staging 文本到 editor/statusbar；
- MDI scratch 文本执行后 canonical output 包含 LinuxCNC-produced
  `STRAIGHT_FEED`；
- `panPreview()` 会移动已填充 toolpath viewBox；
- `fitPreview()` 会清除 pan offset 并恢复 fit viewBox。

Node smoke 新增验证：

- `panToolpathViewBox()` 只平移 viewBox，不改变 width/height；
- existing zoom helper 仍围绕 fit viewBox 缩放。

文档同步：

- `wasm-port/docs/axis-style-simulation-implementation.md` 将
  `loadProgramText()` 从 future API 移入 required API；
- Phase 2 增补 MDI text execution；
- Phase 4 增补 preview pan；
- Immediate Next Batch 更新为当前后续方向：run-control blocked/ready
  messaging 和 preview state readback。

关键验证：

```text
real_simulation_programs_node_smoke=ok
axis_style_simulation_docs_node_smoke=ok
browser_real_simulation_page_smoke=ok
git diff --check ok
```

语义边界：

- MDI text 只是 operator-provided program text，仍由 LinuxCNC WASM 解释；
- preview pan/zoom 只改变 SVG viewBox；
- JS 不实现 G-code、modal、planner、kinematics 或 machine runtime semantics。

### 十一、2026-06-17 AXIS Run-Control State and Preview State Readback

继续按“页面样式和功能对标 LinuxCNC AXIS”的方向推进，补齐 operator-facing
状态反馈，避免 session-backed/standalone 执行路径不透明。

本批新增实质能力：

- toolbar status strip 新增 run-control 状态行：
  - `data-run-control-state`；
  - `document.body.dataset.runControlPhase`；
  - 初始显示 `fallback`，说明 Use Session 开启但尚无 loaded machine session；
- Machine State readout 新增：
  - Run Control；
  - Preview；
- browser API 新增：
  - `getRunControlState()`；
  - `getPreviewState()`；
- run-control state 字段：
  - `apiName: "real-browser-simulation-run-control-state"`；
  - `phase`；
  - `requestedMode`；
  - `executionMode`；
  - `reason`；
  - `canUseSession`；
  - `loadedIniPath`；
- preview state 字段：
  - `apiName: "real-browser-simulation-preview-state"`；
  - `zoom`；
  - `pan`；
  - `fitViewBox`；
  - `viewBox`；
- preview controls 的 pan/zoom 状态同步到：
  - `data-preview-zoom`；
  - `data-preview-pan`；
  - Machine State Preview；
- Manual Control 新增 explicit blocked LED：
  - `data-axis-machine-control-state`；
  - 明确 jog/spindle/coolant 未绑定真实 machine runtime。

AXIS visual polish：

- preview pan controls 从长文字改为更接近工具按钮的 `< > ^ v`；
- 保留 `aria-label` 和原有 `data-preview-pan-*` selectors；
- 控件仍保持密集、灰色、边框式 AXIS 风格。

browser smoke 新增验证：

- 初始 run-control 为 fallback；
- run-control DOM 解释 standalone fallback 原因；
- preview pan 后 API/DOM 同步；
- fit 后 pan offset 清零；
- session load 后 run-control 显示 loaded INI 可用；
- session-backed run 后 run-control 为 ready；
- Use Session 关闭后 run-control 说明 loaded session 被 bypass；
- real-machine controls 必须显示 `not bound` blocked 状态。

文档同步：

- `wasm-port/docs/axis-style-simulation-implementation.md` 增补：
  - `getRunControlState()`；
  - `getPreviewState()`；
  - real jog/spindle/coolant unbound blocked state；
  - run-control ready/fallback 状态。

关键验证：

```text
real_simulation_programs_node_smoke=ok
axis_style_simulation_docs_node_smoke=ok
browser_real_simulation_page_smoke=ok
git diff --check ok
```

语义边界：

- run-control 只反映当前 LinuxCNC-backed execution path；
- preview state 只反映 SVG viewBox；
- unbound machine-control 状态不伪造真实 jog/spindle/coolant 行为。

### 十二、2026-06-17 AXIS Canonical Machine Status Panel

继续推进 AXIS 对标，把左侧和 Machine State 中的 spindle/coolant/tool/override
状态从静态 UI 升级为 LinuxCNC canonical output 派生状态。

本批新增实质能力：

- `simulation-app.js` 新增：
  - `createMachineStatusState(resultText)`；
- simulation state 新增：
  - `machineStatus`；
- browser API 新增：
  - `getMachineStatusState()`；
- machine status 字段：
  - `apiName: "real-browser-simulation-machine-status-state"`；
  - `source: "linuxcnc-canonical-events"`；
  - `spindle.state` / `spindle.direction` / `spindle.speed`；
  - `coolant.mist` / `coolant.flood`；
  - `tool.selected` / `tool.current` / `tool.pocket` / `tool.lengthOffset`；
  - `overrides.feed` / `overrides.speed` / `overrides.adaptiveFeed` /
    `overrides.feedHold`；
- AXIS UI 新增/同步：
  - Manual Control Spindle status LED；
  - Manual Control Coolant status LED；
  - Machine State Spindle row；
  - Machine State Coolant row；
  - Machine State Tool row；
  - Machine State Overrides row；
- 没有 LinuxCNC canonical event 的字段保持 `n/a`。

canonical event mapping：

- `SET_SPINDLE_SPEED` -> spindle speed；
- `START_SPINDLE_CLOCKWISE` / `START_SPINDLE_COUNTERCLOCKWISE` -> spindle
  direction/state；
- `STOP_SPINDLE_TURNING` -> spindle off；
- `MIST_ON/OFF`、`FLOOD_ON/OFF` -> coolant state；
- `SELECT_TOOL`、`CHANGE_TOOL_NUMBER`、`USE_TOOL_LENGTH_OFFSET` -> tool/TLO；
- `ENABLE/DISABLE_*_OVERRIDE`、`ENABLE/DISABLE_FEED_HOLD` -> override rows。

browser smoke 新增验证：

- 初始 built-in program 未产生 machine-status events 时，spindle/coolant
  明确显示 `n/a`；
- 执行包含 `S1200 M3/M5/M7/M8/M9/M48/M50/T2/M6/G43/G49/M61` 的 program；
- 验证 API 和 DOM 中 spindle/coolant/tool/override 状态均来自 LinuxCNC
  canonical output。

Node smoke 新增验证：

- `createMachineStatusState()` 对 unavailable canonical output 返回 `n/a`；
- spindle/coolant/override canonical events 可正确折叠为最终状态；
- tool/TLO canonical events 可正确折叠为当前 tool/pocket/TLO。

文档同步：

- `wasm-port/docs/axis-style-simulation-implementation.md` 增补：
  - machine status canonical event display；
  - `getMachineStatusState()` required API。

关键验证：

```text
real_simulation_programs_node_smoke=ok
browser_real_simulation_page_smoke=ok
git diff --check ok
```

语义边界：

- machine status 只显示 LinuxCNC canonical output 已产生的事件；
- 前端不推断真实 spindle/coolant/tool runtime；
- 缺失字段继续显示 `n/a`。

### 十三、2026-06-17 AXIS Preview Extents, Origin, and Legend

继续推进 AXIS preview 对标，补齐更接近 LinuxCNC AXIS 的预览覆盖层。

本批新增实质能力：

- SVG preview 新增：
  - `data-preview-extents` extents rectangle；
  - `data-preview-origin-x` / `data-preview-origin-y` origin axes；
  - `data-preview-origin` origin marker；
  - `data-axis-shell="preview-legend"` legend；
  - `data-preview-extents-label` extents text；
- `renderToolpath()` 现在基于 LinuxCNC canonical motion 生成的
  `createToolpathViewBox(motion)` 同步：
  - extents rectangle；
  - origin axes；
  - origin marker size；
  - extents label；
- preview legend 显示：
  - Extents；
  - Full path；
  - Executed path；
  - Origin。

browser smoke 新增验证：

- AXIS shell 必须存在 preview legend；
- SVG extents rectangle width/height > 0；
- origin marker 位于 `0,0`；
- extents label 同时包含 X/Y 范围；
- zoom/pan/fit 仍保持原有行为。

文档同步：

- `wasm-port/docs/axis-style-simulation-implementation.md` Phase 4 记录：
  - motion-derived extents rectangle；
  - origin axes/marker；
  - preview legend。

关键验证：

```text
real_simulation_programs_node_smoke=ok
axis_style_simulation_docs_node_smoke=ok
browser_real_simulation_page_smoke=ok
git diff --check ok
```

语义边界：

- extents 只来自 LinuxCNC canonical motion path；
- origin marker/axes 只是 preview display overlay；
- 不新增浏览器端 CNC 几何、planner 或 kinematics 语义。

### 十四、2026-06-17 AXIS Three.js Toolpath Preview

按用户要求，刀具轨迹预览区改用 Three.js，版本要求为：

```json
"three": "^0.183.2"
```

本批新增实质能力：

- 新增 `wasm-port/runtime/ui/simulation/package.json`：
  - `dependencies.three = "^0.183.2"`；
- 新增 `package-lock.json` 记录 resolved dependency；
- 新增 vendored runtime build：
  - `runtime/ui/simulation/vendor/three/three.module.js`；
  - `runtime/ui/simulation/vendor/three/three.core.js`；
  - `runtime/ui/simulation/vendor/three/LICENSE`；
- `simulation-app.js` 直接 import：
  - `./vendor/three/three.module.js`；
- preview 主视图新增：
  - `<canvas data-toolpath-three>`；
- 原 SVG preview 改为 hidden fallback/state surface：
  - 保留 `data-toolpath-svg`；
  - 保留 polyline/head/extents selectors，兼容既有 tests/API。

Three.js 渲染内容：

- orthographic camera；
- dark AXIS-like background；
- motion-derived extents outline；
- origin X/Y axes；
- full canonical path；
- executed canonical path；
- origin marker；
- active tool marker；
- camera fit/zoom/pan 与现有 `fitPreview()` / `zoomPreview()` /
  `panPreview()` 同步。

browser smoke 新增验证：

- `data-toolpath-three.dataset.threeReady === "true"`；
- Three.js revision 为 `183`；
- Three path point count 等于 LinuxCNC canonical motion count；
- playback step 后 Three executed point count 前进；
- SVG fallback viewBox/polyline 继续保持兼容。

docs smoke 新增验证：

- AXIS implementation docs 记录 Three.js `^0.183.2`；
- simulation package 声明 `three: ^0.183.2`；
- vendored `three.core.js` revision 为 `183`；
- real-browser priority docs smoke 确认页面包含 `data-toolpath-three`。

关键验证：

```text
real_simulation_programs_node_smoke=ok
axis_style_simulation_docs_node_smoke=ok
real_browser_simulation_priority_docs_node_smoke=ok
browser_real_simulation_page_smoke=ok
git diff --check ok
```

语义边界：

- Three.js 只负责显示 LinuxCNC canonical motion；
- 不在 Three.js/浏览器端实现 G-code、planner、kinematics 或 compensation；
- SVG fallback 仅作为 state/compatibility surface 保留。

### 十五、2026-06-17 AXIS Three.js Preview View Modes

继续按用户“预览区使用 threejs、对标 LinuxCNC AXIS”的要求推进，补齐 AXIS
常见预览视图模式。

本批新增实质能力：

- Preview toolbar 新增：
  - `Top`；
  - `Front`；
  - `Side`；
  - `Iso`；
- `window.linuxCncRealSimulationApi` 新增：
  - `setPreviewViewMode(mode)`；
- `getPreviewState()` 新增：
  - `viewMode`；
- Three.js preview canvas 新增/同步：
  - `data-three-view-mode`；
- UI 新增：
  - `data-preview-view-label`；
  - active view-mode button state。

Three.js camera behavior：

- `top`：正交 top view；
- `front`：正交 front-style view；
- `side`：正交 side-style view；
- `iso`：isometric-like angled view；
- zoom/pan/fit 继续复用现有 viewBox 状态；
- Three.js 仅显示 LinuxCNC canonical motion，不改变 motion semantics。

browser smoke 新增验证：

- API 必须存在 `setPreviewViewMode()`；
- 切换 `iso` 后：
  - `getPreviewState().viewMode === "iso"`；
  - Three canvas `data-three-view-mode === "iso"`；
  - UI label 显示 `Iso`；
- 切换 `side` 后：
  - active button state 为 true；
- 切回 `top` 后继续执行 fit/zoom/pan smoke。

文档同步：

- `wasm-port/docs/axis-style-simulation-implementation.md` Phase 4 增补
  Top/Front/Side/Iso view modes；
- Required APIs 增补 `setPreviewViewMode(mode)`。

关键验证：

```text
real_simulation_programs_node_smoke=ok
axis_style_simulation_docs_node_smoke=ok
browser_real_simulation_page_smoke=ok
git diff --check ok
```

语义边界：

- view modes 只改变 Three.js camera；
- 不改变 LinuxCNC canonical motion path；
- 不实现浏览器端 kinematics 或 G-code 几何语义。

### 十六、2026-06-17 AXIS Three.js Grid, Axis Labels, and Pixel Smoke

继续按“预览区使用 threejs、对标 LinuxCNC AXIS”推进，补齐 Three.js 预览的
网格、轴标签和 canvas 非空验证。

本批新增实质能力：

- Three.js preview 新增动态 grid：
  - `chooseGridStep(span)` 按 viewBox 尺寸选择 1/2/5/10 系列步距；
  - `addThreeGrid(scene, viewBox)` 生成 viewBox 覆盖范围内的 grid lines；
  - canvas dataset：
    - `data-three-grid-step`；
    - `data-three-grid-lines`；
- Three.js preview 新增 X/Y/Z axis labels：
  - `createTextSprite(text, color)`；
  - `addThreeAxisLabels(scene, viewBox)`；
  - canvas dataset：
    - `data-three-axis-labels="3"`；
- Three.js preview smoke 新增 WebGL pixel readback：
  - 获取 `webgl2` / `webgl` context；
  - `readPixels()` 检查 canvas 中心像素非透明空白；
  - 继续验证 Three.js revision/path points/executed points。

文档同步：

- `wasm-port/docs/axis-style-simulation-implementation.md` Phase 4 增补：
  - dynamic grid lines；
  - X/Y/Z axis labels；
  - browser smoke canvas pixel check。

关键验证：

```text
real_simulation_programs_node_smoke=ok
axis_style_simulation_docs_node_smoke=ok
browser_real_simulation_page_smoke=ok
git diff --check ok
```

语义边界：

- grid 和 labels 只使用当前 preview viewBox；
- Three.js 仍只显示 LinuxCNC canonical motion；
- pixel smoke 只验证渲染非空，不推导 CNC 语义。

### 十七、2026-06-17 Browser Virtual HAL for AXIS Manual Controls

接续“虚拟 HAL 业务”，本批把 AXIS 手动控制从静态未绑定状态推进为浏览器
虚拟 HAL 状态模型，并接入 UI、SDK 和 WASM bridge。

本批新增实质能力：

- 新增 `wasm-port/runtime/sdk/src/linuxcnc-hal.js`：
  - `VIRTUAL_HAL_AXISUI_PINS` 对齐 `linuxcnc/bin/axis` 中的 axisui pins：
    `jog.x/y/z/a/b/c/u/v/w`、`jog.increment`、notification pins、
    `error`、`abort`；
  - `createVirtualHalState()` / `applyVirtualHalAction()` 管理 ESTOP、Power、
    Home、Jog、Touch Off、Spindle、Coolant、Limit Override、Brake 等状态；
  - `createVirtualHalDroState()` 输出 AXIS DRO 默认/实际位置；
  - `createVirtualHalLimitsHomeState()` 输出 home/limit/fault rows；
  - `createVirtualHalMachineStatusState()` 输出 spindle/coolant/tool/override state；
  - `createVirtualHalWasmBridgeSnapshot()` 生成可写入 standalone HAL adapter 的
    pin/value snapshot。

- SDK 入口新增 re-export：
  - virtual HAL 状态、action、DRO、limits/home、machine status、runtime、
    bridge snapshot、jog increment parser。

- Interpreter SDK 新增 HAL bridge API：
  - `hasWasmFunction(name)`；
  - `resetHal()`；
  - `setHalValue({ kind, name, type, value, connected })`；
  - `applyVirtualHalSnapshot(snapshot, options)`；
  - `applyVirtualHalState(halState, options)`；
  - `probeHalNamed(name)`。

- WASM wrapper 新增导出：
  - `lcinterp_hal_reset()`；
  - `lcinterp_hal_set_value()`；
  - `lcinterp_probe_hal_named()`；
  - `build_wasm_core.sh` 已把这些函数加入 `EXPORTED_FUNCTIONS`。

- Browser simulation UI 接入：
  - F1/F2 快捷键驱动 ESTOP/Power；
  - Ctrl-Home 驱动 Home All；
  - Manual jog 更新 `jog.*` pins 和 DRO；
  - spindle/coolant/brake/limit override 写入 virtual HAL state；
  - diagnostics artifact 新增 `virtualHal`；
  - machine control state 从“not bound”变为
    `browser virtual HAL active for jog/spindle/coolant/home simulation`。

- Bridge snapshot 同时输出：
  - AXIS Python component 内部 pin 名，例如 `jog.x`；
  - HAL 全名，例如 `axisui.jog.x`；
  - machine/task pins，例如 `halui.machine.is-on`；
  - spindle/coolant pins，例如 `spindle.0.speed-out`、
    `iocontrol.0.coolant-flood`。

新增/扩展验证：

- `verify_real_simulation_programs.mjs` 覆盖 virtual HAL SDK、runtime snapshot、
  `axisui.*` HAL 全名、DRO、home、spindle、coolant；
- `real_simulation_page_smoke.html` 覆盖 F1/F2/Ctrl-Home/jog/spindle/coolant 的
  浏览器交互；
- `verify_interp_wasm.mjs` 在新 WASM 导出存在时验证：
  `applyVirtualHalState()` 写入 HAL adapter 后，`probeHalNamed()` 能读到
  `_hal[jog.x]`、`_hal[axisui.jog.x]`、`_hal[halui.machine.is-on]`、
  `_hal[spindle.0.speed-out]`、`_hal[iocontrol.0.coolant-flood]`。

关键验证：

```text
real_simulation_programs_node_smoke=ok
interp_wasm_node_smoke=ok (SKIP_INTERP_BUILD=1)
browser_real_simulation_page_smoke=ok (SKIP_INTERP_BUILD=1)
sdk_surface_node_smoke=ok
ui_node_smokes=ok
git diff --check ok
```

当前构建备注：

- 当前 Codex shell 的 `PATH` 未解析到 `emcc`，完整 `build_wasm_core.sh` 会停在
  `emcc: command not found`；
- 这不是代码侧缺少 Emscripten 的结论，只是本 shell 未加载用户已有的
  Emscripten 环境；
- 快速 smoke 已用现有 WASM 产物验证旧路径不回归，新导出的 HAL bridge smoke 会在
  重建后的 WASM 存在对应函数时自动执行。

语义边界：

- virtual HAL 只模拟 AXIS/manual UI 与 standalone HAL adapter 的状态桥；
- 不在浏览器端实现 LinuxCNC realtime HAL、motion controller、planner、
  kinematics 或 G-code 语义；
- G-code execution 和 canonical motion 仍来自 LinuxCNC interpreter WASM。

### 十八、2026-06-17 Project-level Virtual HAL SDK Boundary

按用户要求“虚拟 HAL 适合整个项目”，本批把上一节的 simulation/UI 虚拟 HAL
提升为项目级 SDK host-boundary 能力，供 simulation、INI panel、Node/browser
smoke、外部 dashboard 和 interpreter bridge 共用。

新增项目级 API：

- `VIRTUAL_HAL_WASM_BRIDGE_FUNCTIONS`：
  - `lcinterp_hal_reset`；
  - `lcinterp_hal_set_value`；
  - `lcinterp_probe_hal_named`；
- `VIRTUAL_HAL_PROJECT_PIN_GROUPS`：
  - `axisui`；
  - `axis`；
  - `task`；
  - `spindle-coolant`；
- `createVirtualHalPinInventory(halState)`：
  - 输出全部 bridge pin rows；
  - 按项目级 pin group 归类；
- `createVirtualHalBridgeReadiness(options)`：
  - 检查 snapshot 是否包含 required pins；
  - 检查 caller-provided WASM function evidence 是否覆盖
    `VIRTUAL_HAL_WASM_BRIDGE_FUNCTIONS`；
  - 无 evidence 时显式 blocked：`wasm-function-evidence`；
- `createVirtualHalBridgeActionPlan(readiness)`：
  - blocked 时给出下一步：
    `wasm-port/tools/build_wasm_core.sh`；
    `SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh`；
  - ready 时返回空 command；
- `createVirtualHalProjectReport(options)`：
  - 汇总 state、DRO、limits/home、machine status、pin inventory、
    WASM bridge snapshot、bridge readiness、bridge action plan；
- `applyVirtualHalToInterpSdk(interpSdk, halState, options)`：
  - 对有 `applyVirtualHalState()` 或 `applyVirtualHalSnapshot()` 的 interpreter
    SDK 做统一适配；
- `createLinuxCncVirtualHalRuntime()` 新增：
  - `getPinInventory()`；
  - `getProjectReport(options)`；
  - `applyToInterpSdk(interpSdk, options)`。

文档同步：

- `runtime/sdk/README.md` 新增 `Project-level virtual HAL boundary` 小节；
- `docs/project-release-handoff.md` 把 project-level virtual HAL 纳入当前支持
  workflow；
- `PROJECT_COMPLETION_TRACKER.md` 的 SDK/API surface 行记录 pin inventory、
  bridge readiness/action-plan、project report、interpreter apply adapter；
- `verify_project_release_handoff_docs.mjs` 增加文档 gate，防止后续退回页面私有
  virtual HAL。

新增/扩展验证：

- `verify_sdk_surface.mjs` 覆盖：
  - SDK re-export；
  - constants discovery；
  - virtual HAL snapshot；
  - pin inventory；
  - blocked/ready bridge readiness；
  - bridge action plan；
  - project report；
  - runtime project report；
  - fake interpreter SDK apply wrapper；
- `verify_project_release_handoff_docs.mjs` 覆盖 handoff/README 中的项目级
  virtual HAL API 和 realtime HAL runtime 边界声明。

关键验证：

```text
sdk_surface_node_smoke=ok
project_release_handoff_docs_node_smoke=ok
real_simulation_programs_node_smoke=ok
git diff --check ok
```

语义边界：

- project-level virtual HAL 仍是 host-boundary adapter；
- 它可服务 UI/session/diagnostics/dashboard 和 `_hal[...]` interpreter probe；
- 它不是 LinuxCNC realtime HAL runtime；
- 不提升 task、motion、HAL module loading、HALUI process、Tcl/Python process、
  external device semantics。

### 十九、2026-06-17 Simulation-grade LinuxCNC Realtime HAL / halcmd / Motion Replacement

按用户要求“在满足仿真方面，实现 LinuxCNC realtime HAL / halcmd / motion controller
的完整替代，以虚拟 HAL 方式实现”，本批把上一节项目级 virtual HAL 从 bridge/readiness
推进为仿真级 runtime replacement。

新增实质能力：

- `VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS`：
  - `linuxcnc-realtime-hal`；
  - `halcmd`；
  - `motion-controller`；
- `VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES` 新增：
  - `realtime-hal-simulation-replacement`；
  - `halcmd-simulation-replacement`；
  - `motion-controller-simulation-replacement`；
- 新增 `executeVirtualHalcmd()`：
  - 作为 `executeVirtualHalCommand()` 的 halcmd 语义别名；
  - 支持当前仿真所需 `setp`、`sets`、`newsig`、`net`、`show`、`getp`、`gets`、
    `loadrt`、`loadusr`、`addf`、`start`、`stop`、`unlinkp`、`motion-step`；
- 新增 `stepVirtualHalMotionController()`：
  - 多周期 servo step；
  - 输出每周期 position/motion frame；
  - 更新 target、velocity、distance-to-go、in-position；
- 新增 `createVirtualHalSimulationReplacementReport()`：
  - 明确报告 realtime HAL、halcmd、motion controller 三类仿真替代 ready 状态；
  - 保留 `hardRealtime: false`、`realtimeKernelAbi: false`，避免伪装为 Linux kernel
    realtime ABI；
- `createLinuxCncVirtualHalRuntime()` 新增：
  - `executeHalcmd()`；
  - `stepMotionController()`；
  - `getSimulationReplacementReport()`。

Browser simulation API 新增：

- `executeVirtualHalcmd()`；
- `executeVirtualHalCommand()`；
- `stepVirtualHalMotionController()`；
- `getVirtualHalSimulationRuntimeReport()`；
- `getVirtualHalSimulationReplacementReport()`；
- `getVirtualRealtimeHalRuntimeReport()`；
- diagnostics artifact 新增 `virtualHalSimulationReplacement`。

新增/扩展验证：

- `verify_sdk_surface.mjs` 覆盖：
  - replacement target constants；
  - runtime capability constants；
  - `executeVirtualHalcmd()`；
  - `stepVirtualHalMotionController()`；
  - `createVirtualHalSimulationReplacementReport()`；
  - runtime wrapper 的 `executeHalcmd()` / `stepMotionController()` /
    `getSimulationReplacementReport()`；
- `real_simulation_page_smoke.html` 覆盖：
  - 页面 API 暴露 halcmd/motion/replacement report；
  - browser 中执行 `loadrt`、`addf`、`setp`、`show pin`；
  - browser 中执行 motion controller 多周期 step；
  - diagnostics artifact 携带 replacement report。

关键验证：

```text
sdk_surface_node_smoke=ok
project_release_gate_manifest_node_smoke=ok
project_release_artifact_url_workflow_node_smoke=ok
project_batch_acceptance_workflow_node_smoke=ok
project_batch_acceptance_artifact_node_smoke=ok
real_simulation_programs_node_smoke=ok
browser_real_simulation_page_smoke=ok
git diff --check ok
```

语义边界：

- 本批实现的是 simulation-grade replacement；
- Browser/SDK 仿真不再依赖 host `halcmd`、host realtime HAL process 或 host motion
  process 来完成页面/Node 仿真闭环；
- 仍不声称提供 Linux kernel hard-realtime ABI、外部硬件驱动 ABI、native HAL module ABI；
- G-code 执行、canonical event、planner/interpreter 语义仍来自 LinuxCNC-backed WASM。
