Files
linuxcncWebWASM/docs/linuxcnc-web-wasm-execution-strategy.md

329 lines
32 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# LinuxCNC Web/WASM 仿真项目执行与交付策略
**文档状态**:Draft v0.1
**适用范围**:本仓库的纯 Web/WASM 仿真移植、浏览器运行时、OPFS 工程存储、覆盖矩阵和发布物
**固定基线**:LinuxCNC `2.10.0~pre0`(以 `linuxcnc/VERSION` 和锁定的源码提交为准)
**关联文档**:[技术路线](linuxcnc-web-wasm-technical-roadmap.md)、[完整仿真机床实施蓝图](linuxcnc-web-wasm-full-port-implementation.md)、[运动学完整移植计划](linuxcnc-web-wasm-kinematics-migration-plan.md)、[仿真全功能覆盖矩阵](linuxcnc-web-wasm-simulation-coverage.md)、[功能与源程序关联目录](linuxcnc-web-wasm-source-map.md)、[前端与 LinuxCNC 通信详细设计](linuxcnc-web-wasm-frontend-communication-design.md)、[总控执行手册](linuxcnc-web-wasm-integration-playbook.md)、[上下文治理与最小任务目录](linuxcnc-web-wasm-context-governance.md)
本文件是项目执行的治理入口。技术路线回答“做什么以及采用什么架构”,覆盖矩阵回答“逐项覆盖什么以及如何验收”,本文件回答“谁在什么固定上下文中交付、如何在断网或失败后接续、什么证据才能过门禁”。三份文档必须以同一基线版本更新;若规则冲突,以本文件的交付门禁和覆盖矩阵的逐项验收要求为准。
## 1. 治理缺口与补强决定
| 已有内容 | 缺口 | 本策略的控制措施 | 证据位置 |
|---|---|---|---|
| 技术路线已有 A-E 阶段 | 没有阶段退出条件、依赖关系和返工规则 | 为每个里程碑定义入口、出口、证据和阻断条件;未过门禁不得进入下一阶段 | 第 6、7 节 |
| 覆盖矩阵要求 100% | `sim_status`、`web_status` 没有统一枚举,差异可能被静默接受 | 固定状态机和缺失项 CI 门禁;`REAL-EXCLUDED` 与未实现严格分离 | 第 3.4 节;覆盖矩阵第 1、13 节 |
| OPFS 已有 journal 思路 | 没有断网、标签页挂起、Worker 崩溃和配额不足的接续协议 | 本地优先、事务提交、checkpoint、重放和幂等命令协议;恢复结果必须可验证 | 第 5 节 |
| 固定 tick 和 golden 已提出 | 没有明确“同一上下文”的构成,难以复现差异 | 建立 context lock,提交和 golden 必须携带锁定摘要 | 第 3 节 |
| 仿真与真实硬件边界已说明 | 任务容易跨越 WASM、Worker、UI 和存储责任边界 | 以工作流和接口为边界分工;跨边界改动必须有契约变更记录 | 第 4 节 |
| 有原生/WASM 对照目标 | 没有统一的变更、回滚和发布证据包 | 采用小步、单主题提交;发布只消费完整证据包和签名的 manifest | 第 3.3、7 节 |
治理原则:基线可重建、仿真结果可重复、持久数据可恢复、差异可定位、排除项可识别。任何“先合并再补测试”或“离线时先静默丢弃”的做法都不满足本策略。
## 2. 文档与决策层级
文档关系固定如下:
```text
执行与交付策略(本文件)
├─ 技术路线:架构、范围、阶段 A-E 和风险决策
├─ 覆盖矩阵:功能来源、manifest 条目、测试和逐项验收
├─ 源程序关联目录:功能到源文件、配置、符号和测试的反向索引
├─ 前端与通信设计:UI 工作流、Worker 协议、状态契约、任务和 FE/COM 里程碑
└─ 总控执行手册:端到端依赖、统一任务目录、场景验收和当前实施状态
```
架构、范围或阶段发生变化时,先更新技术路线;功能条目、标记、测试或差异状态发生变化时,先更新覆盖矩阵;两者都需要同步更新本文件中的里程碑或门禁时,必须在同一变更包中完成。文档链接、基线版本和状态枚举由文档一致性检查自动校验。
### 2.1 全功能移植闭环
“全部功能”按可观察的仿真能力定义,而不是按成功编译的源码行数定义。功能全集由以下来源的并集规范化得到:
```text
F = docs capabilities
∪ exported/configurable behavior in src/emc, src/hal, classicladder, usr_intf and Python APIs
∪ configs/sim/** resources and workflows
∪ tests/** and unit_tests/** cases
∪ Web UI, VCP, visualization and persistence workflows
```
M0 必须冻结来源扫描和规范化规则;每个来源项至少映射到一个 manifest 条目,每个 manifest 条目必须反向列出来源、依赖、输入、输出、错误、仿真替代方案、测试和 oracle。来源项没有映射、manifest 条目没有测试、依赖没有递归闭包或新增源码未触发 manifest 变化时,CI 直接阻断。一个来源可以拆成多个功能条目,但不能把多个未验证功能合并成一个笼统的“全覆盖”。
每个功能条目都要通过六个覆盖维度:正常行为、边界/资源限制、错误和报警、暂停/恢复/急停、持久化/刷新/离线恢复、UI/API/脚本工作流;需要与其他域交互的条目还必须进入关键路径或 pairwise 交互测试。`REAL-EXCLUDED` 只允许用于真实硬件、硬实时和物理安全;任何 `configs/sim` 依赖都必须映射为可观测的 `SIM-CORE`、`SIM-ADAPTER`、`SIM-DEVICE`、`WEB-EQUIV` 或 `PY-WASM` 行,不能用排除项掩盖仿真缺口。
完整性证明按“来源扫描 → manifest 闭包 → 实现映射 → 多维测试 → 原生/WASM 差异 → 发布签署”顺序生成。[功能与源程序关联目录](linuxcnc-web-wasm-source-map.md)是来源扫描和反向索引的规范入口;发布报告必须同时给出已接受条目、排除条目、阻断条目、孤儿来源和未覆盖交互;只给出一个百分比不能作为全功能声明。
## 3. 固定上下文与提交策略
### 3.1 Context lock
每个可复现构建、golden 集和发布候选都绑定一个不可变的 context lock。锁至少包含以下字段,实际格式可为 `context-lock.json` 或 CI 产物:
| 字段 | 要求 |
|---|---|
| `context_id` | 全局唯一,例如 `linuxcnc-2.10.0-pre0-wasm-c1` |
| `linuxcnc_version`、`linuxcnc_source_sha` | 与 `linuxcnc/VERSION` 和源码提交一致 |
| `roadmap_revision`、`coverage_revision` | 两份治理文档的提交摘要或版本号 |
| `emscripten_version`、`compiler_flags` | 包括优化、浮点、异常、线程和文件系统选项 |
| `python_runtime` | Pyodide/CPython-WASM 版本及包锁摘要 |
| `schema_versions` | C ABI、命令、事件、快照和工程格式版本 |
| `browser_matrix` | 浏览器、版本范围、操作系统和是否跨源隔离 |
| `test_manifest_sha`、`golden_manifest_sha` | 输入清单及参考结果的内容摘要 |
锁定后,不能在同一 `context_id` 内悄悄升级编译器、Python、浏览器基线、schema 或 LinuxCNC 源码。升级必须创建新的 `context_id`,重新生成受影响的 golden,并在迁移说明中列出差异。
### 3.2 固定大小的任务上下文包
“固定上下文”指每次任务交接和每次提交都重新装载一个固定格式、固定预算的上下文包,而不是把整个历史对话、全部日志或已完成阶段重新带入。上下文包是可丢弃的工作索引;完整源码、日志、golden 和差异报告留在仓库或 CI 产物中,只通过路径和摘要引用。
启动顺序同样受预算约束:先只读取 `nextTask.md` 指针(不超过 8 KiB),再按当前子任务读取 `context-lock.json` 摘要和直接输入。`docs/task-history.md`、完整路线图、覆盖矩阵、`coverage/*.jsonl` 和完整 stdout/stderr 均为按需 L3 资料,不能在启动时批量加载。
上下文包使用固定的八段结构,逻辑预算为 **8,192 tokens,UTF-8 序列化不超过 32 KiB**。实现上由 [`scripts/context_packet.py`](../scripts/context_packet.py) 在选择证据时执行预算;校验器不能成为第一次发现超限的地方。每段有上限,缺省内容用 `none`,不得追加自由历史段:
| 段 | 固定内容 | 建议上限 |
|---|---|---:|
| `header` | `context_id`、`packet_id`、任务和里程碑 | 512 tokens |
| `baseline` | 源码/工具链/schema/golden 摘要 | 1,024 tokens |
| `scope` | 主工作流、目标、非目标、受影响 manifest ID | 1,024 tokens |
| `contracts` | 仅列出本任务读写的 ABI、事件、文件和状态契约 | 1,536 tokens |
| `evidence` | 已通过测试、失败测试和产物路径/哈希 | 1,536 tokens |
| `risks` | 未解决风险、阻断原因和回滚点 | 1,024 tokens |
| `next` | 下一步最多三项可执行动作 | 768 tokens |
| `forbidden` | 本任务明确不得触碰的边界 | 768 tokens |
包的 `packet_id` 由 `context_id`、任务 ID 和修订号组成;修订只替换对应段,不复制旧包。超过任一段上限或总预算时,必须先拆分任务、生成摘要或把内容移到可寻址产物,不能扩大上下文预算。每次交接从最新包重建,旧包只保留哈希和归档链接,因此移植从阶段 A 深入阶段 E 时,上下文大小不变。
模型调用还必须显式保留输出空间:
```text
request_input_tokens = packet_input_tokens + fixed_overhead_tokens
model_context_window >= request_input_tokens + max_output_tokens + safety_margin
```
请求前由 `validate_model_budget.py` 阻断超限;请求后还必须由 `validate_provider_response` 校验 provider 返回的 usage,确认 `output_tokens <= max_output_tokens`。若 `finish_reason` 为 `length`/`max_tokens`/`max_output_tokens`,只保存 metadata-only checkpoint 并从最新 packet 发起更小的新请求,不能把截断响应追加回下一轮。
`fixed_overhead_tokens` 必须由调用方按当前请求重新计数,不能把上一次请求的历史累加进去。`max_output_tokens` 应按任务类型固定(文档 768、feature 1,024、contract 1,536、milestone 2,048 的默认上限),而不是使用模型的最大值;调用参数只能缩小 profile 的输入/输出预算,不能放大。`context_packet.py` 的字节、证据文件数和令牌覆盖值同样只能缩小 profile。没有 provider tokenizer 时按 UTF-8 字节作硬上界,拿到精确令牌数后可用 `--input-tokens` 替换估算。输入包达到预算 80% 或模型返回 `context_length_exceeded` 时,先移除最低优先级证据、缩短摘要或拆分子任务;不得把旧对话和失败响应再次拼回请求。
流式调用必须由 [`scripts/model_stream.py`](../scripts/model_stream.py) 的有界 SSE 解码器消费。只有 `response.completed` 且 `usage.input_tokens/output_tokens/total_tokens` 一致、输出未超 `max_output_tokens` 时才标记可复用;中途断流、解码失败、`response.failed`、上下文超限或 `response.incomplete` 只生成 metadata-only checkpoint。恢复动作固定为丢弃可选证据并从新 packet 重建,禁止续接半截文本或重试同一个超大请求。
#### 生成阶段的预算算法
证据选择必须在写入输出文件前完成,按以下顺序执行:
1. 加载固定上下文骨架并计算 `skeleton_bytes`;骨架超过 `byte_limit` 立即失败。
2. 将候选证据规范化为 `path`、`bytes`、`sha256`、`kind`、`priority`、`required`,按必需、优先级、路径排序,保证选择确定性。
3. 逐项检查:单项证据字节预算、证据总字节预算、证据文件数,以及把该项加入 `selected` 后的完整上下文剩余空间;完整上下文按 `packet_bytes + selected_evidence_bytes` 计算,不能只计算 JSON 清单。
4. 可选项无法容纳时不写入包,记录稳定排除码;必需项无法容纳时直接失败,不生成部分成功包。
5. 生成最终预算元数据后再次序列化校验;若审计清单或令牌估算使包超限,从最低优先级可选项回退并以 `E5=context_remaining_bytes_insufficient` 或 `E6=token_budget_exceeded` 记录。若只剩必需项仍超限,失败。
排除原因使用稳定机器码,避免长文本侵占上下文:`E1 duplicate_path`、`E2 evidence_item_bytes_exceed_budget`、`E3 evidence_file_count_exceed_budget`、`E4 evidence_bytes_exceed_budget`、`E5 context_remaining_bytes_insufficient`、`E6 token_budget_exceeded`。输出必须包含选中证据字节数、选中/排除文件数、证据预算、总包预算、最终包字节数和排除码;CI 同时检查 `packet_bytes <= byte_limit`、`packet_bytes + selected_evidence_bytes <= byte_limit` 和 token 上限,而不是先写超限包再报告错误。
上下文包的最小机器可读形状如下;CI 应检查字段齐全、段顺序、预算、`context_id` 一致性和引用产物存在:
```yaml
context_id: linuxcnc-2.10.0-pre0-wasm-c1
packet_id: M2-WS1-interpreter-r07
task: "[WS-1][M2] ..."
header: { milestone: M2, owner_role: runtime }
baseline: { source_sha: "...", schema: { abi: 1, snapshot: 1 } }
scope: { include: ["coverage/..."], exclude: ["WS-5"], manifest: ["..."] }
contracts: ["sim_step", "snapshot.v1"]
evidence: { passed: ["..."], failed: ["..."], artifacts: ["..."] }
risks: ["..." ]
next: ["...", "...", "..."]
forbidden: ["real hardware", "host process", "unversioned schema"]
budget: { token_limit: 8192, byte_limit: 32768 }
```
### 3.3 提交和证据包
提交按一个可审查主题组织:运行时、持久化、组件、UI、测试或文档只能有一个主主题;跨主题改动拆成有依赖顺序的提交。每个提交或合并请求必须声明:
```text
Context-ID: <context_id>
Context-Packet: <packet_id>
Scope: <workstream>/<milestone>
Contract-Change: none | <ABI/schema/format/compatibility id>
Evidence: <test command or artifact paths>
Offline-Impact: none | cached-start | mid-run | recovery
Coverage: <manifest ids or paths>
Context-Budget: 8192 tokens / 32768 bytes
```
合并请求的证据包至少包括变更摘要、受影响 manifest 条目、测试日志、golden/difference 摘要、性能或配额影响、回滚方式和文档链接。提交正文只保存上下文包和产物摘要,不复制完整日志或历史讨论。没有 `Context-ID`、`Context-Packet`、预算声明、证据路径或覆盖条目的提交只能作为草稿,不能进入受保护分支。
### 3.4 覆盖状态机
`sim_status` 和 `web_status` 使用同一组值:
`planned` → `in_progress` → `implemented` → `verified` → `accepted`。
允许从任何非终态回到 `blocked`;`blocked` 必须填写阻断原因、责任工作流、下一步和复核日期。`REAL-EXCLUDED` 是终态,但只允许用于覆盖矩阵定义的真实硬件、硬实时和安全排除项。`accepted` 必须同时具备实现、自动测试、差异报告和 context lock;没有这些证据不得通过文字描述或 UI 状态伪装成已支持。
### 3.5 变更分类和审查
* **C0 文档/注释**:不改变契约;文档链接和术语检查通过即可。
* **C1 行为实现**:必须更新 manifest、golden 或差异说明,并运行受影响功能域测试。
* **C2 契约/格式**:改变 ABI、消息、快照或工程格式;必须先提交迁移方案、双读/版本策略和兼容测试。
* **C3 基线/工具链**:改变 LinuxCNC、Emscripten、Python 或浏览器基线;必须新建 context lock,完整重跑基线回归。
C2/C3 变更需要运行时、测试/兼容性和存储(若受影响)三个责任角色共同审查。任何改动都不得把真实硬件能力从 `REAL-EXCLUDED` 改成普通覆盖项而不修改产品边界和安全说明。
## 4. 工作流拆分与边界
工作流以可交付接口划分,避免按目录切割导致责任重叠。角色是责任类别,不预设具体人员。
| ID | 责任工作流 | 交付物 | 依赖 | 明确不负责 |
|---|---|---|---|---|
| WS-0 | 基线与清单 | context lock、来源扫描、`coverage/manifest.jsonl`、golden 索引 | 无 | 运行时实现 |
| WS-1 | WASM 核心与 ABI | `sim_*` 生命周期、解释器/TP/kinematics 适配、版本化 ABI | WS-0 | UI、OPFS 事务 |
| WS-2 | Worker 调度与虚拟设备 | 固定 tick、HAL、IO、故障模型、事件序列 | WS-1 | 浏览器布局和用户文件选择器 |
| WS-3 | Workspace/OPFS | 虚拟 POSIX、journal、checkpoint、迁移、配额诊断 | WS-0;消费 WS-1 schema | 运动语义和 UI 状态管理 |
| WS-4 | Python/插件兼容 | Pyodide 虚拟模块、remap/filter/VCP 权限和生命周期 | WS-1、WS-3 | 宿主进程、网络和设备访问 |
| WS-5 | Web UI/可视化 | Worker 客户端、控制台、3D、工具和配置工作流 | WS-2、WS-3 | 改写核心算法和持久化协议 |
| WS-6 | 参考运行器与测试 | 原生/WASM golden、浏览器 E2E、故障注入、差异报告 | WS-0;随各工作流增量接入 | 为失败测试降级或删除样本 |
| WS-7 | 发布与文档 | 兼容报告、许可证清单、迁移说明、离线包和发布证据 | WS-0..6 | 绕过门禁发布 |
跨工作流接口只通过已版本化的 ABI、命令/事件 schema、快照 schema、工程格式和 manifest 交换;WS-2/WS-5 的具体接口、FE/COM 任务和 FE-M0..M5 退出条件见[前端与 LinuxCNC 通信详细设计](linuxcnc-web-wasm-frontend-communication-design.md)。若需要直接读取另一个工作流的内部结构,应先提出契约变更(C2),而不是复制内部状态。每个任务必须标注一个主工作流和一个里程碑;一个任务同时改动三个以上工作流时,默认拆分。
## 5. 稳定性、断网与接续
### 5.1 本地优先原则
已缓存的应用壳、WASM、Pyodide、样例和工程可以在无网络时启动;仿真核心、Worker、OPFS 和 UI 主流程不得把网络可用性当作前置条件。网络只用于可选更新、协作或远程调试。断网期间禁止自动覆盖已提交版本,禁止删除唯一可恢复的 checkpoint,禁止把失败写入标记为成功。
### 5.2 故障分类与策略
| 故障 | 运行策略 | 接续依据 | 验收结果 |
|---|---|---|---|
| 启动时无网 | 从 Service Worker 缓存启动;缺少资源则显示明确缺件 | cache manifest、context lock | 已缓存版本可启动;未缓存依赖可诊断 |
| 运行中断网 | 仿真继续按固定 tick 运行;本地命令和日志照常提交 | OPFS journal、`seq`、`simulation_tick` | 重载页面后恢复最近已提交状态 |
| 标签页挂起/Worker 重启 | 在安全 checkpoint 重启并重放增量事件 | checkpoint 基线 + event log | 重放终点与连续运行一致 |
| 写入中止/崩溃 | 只接受完整 commit;清理临时文件或回滚 | prepare/commit、哈希和版本索引 | 不出现半文件,不损坏旧版本 |
| 配额不足 | 停止非关键 cache/日志写入,保留工程和最近 checkpoint | 配额诊断、版本保留策略 | 已提交工程可读;用户得到导出/清理动作 |
| 重新联网 | 不自动重放重复命令;按摘要对账可选远程数据 | `context_id`、`seq`、`tick`、状态哈希 | 对账成功或明确冲突,不静默合并 |
### 5.3 命令、事件和快照接续协议
每条控制命令带单调递增 `seq`、`context_id`、客户端 `session_id` 和幂等键;每个事件和快照带 `seq`(最后应用的命令)、`simulation_tick`、`schema_version` 和状态哈希。恢复流程固定为:
1. Worker 读取并校验最新完整 checkpoint 和 journal;只接受哈希、版本和路径均匹配的记录。
2. 以 checkpoint 的 tick 为起点按序重放事件;遇到缺号、重复或未知 schema 时停止并报告,不猜测补齐。
3. 发布 `recovered` 事件,包含恢复到的 tick、最后提交序号、丢弃的未提交记录和差异摘要。
4. UI 只有在收到 `recovered` 后才允许继续发送依赖运行态的命令;重复提交使用幂等键返回原结果。
快照采用“完整基线 + 有界增量”策略:增量达到大小、数量或时间上限就合并为新基线。UI 偏好与仿真状态分离;恢复仿真不得依赖尚未提交的 UI 偏好。连续运行与恢复运行必须在相同 `context_id`、输入文件哈希和故障注入种子下得到相同终点。
自动接续使用以下状态机,不依赖用户手动刷新或重复点击:
```text
online_ready
└─ 网络/资源不可用 ─▶ offline_degraded
└─ 本地资源完整 ─▶ local_continuation
└─ online 事件 ─▶ reconnecting
├─ 摘要一致 ─▶ reconciled ─▶ online_ready
└─ 摘要冲突 ─▶ conflict(暂停远程同步,保留本地版本)
```
`reconnecting` 使用指数退避和上限,不重复执行已确认的 `seq`;`local_continuation` 只把命令写入本地 journal,`reconciled` 后按序发送未确认摘要。出现 `conflict` 时不能自动覆盖任一版本,必须保留两个可导出的版本和差异报告。网络恢复不是仿真恢复的前提:即使永远不重新联网,已缓存资源和 OPFS 工程仍应按本地策略完成运行、暂停、快照和导出。
### 5.4 稳定性指标
每个里程碑都记录以下指标,不以平均值掩盖长尾:Worker 周期 overrun、命令确认延迟、快照吞吐、checkpoint 写入耗时、恢复耗时、journal 未完成事务数、OPFS 错误率和 UI 主线程阻塞时间。指标必须按浏览器矩阵和场景分组;性能回归超过基线阈值时阻止发布,除非有带期限的书面例外。
### 5.5 高效执行策略
效率优化不能改变固定 tick、事件顺序或恢复结果。执行和构建按以下规则收敛:
* **按影响图测试**:提交前只运行受影响 manifest 的快速测试;里程碑运行整个功能域;M5 运行全量来源、golden、浏览器和故障矩阵。影响图由来源、契约和测试 ID 生成,不能由提交者手工缩小。
* **按 context 缓存**:构建、解析和轨迹缓存的键至少包含 `context_id`、源码摘要、工具链摘要和输入文件摘要;不同 context 不共享可能改变语义的缓存。
* **并行与串行分界**:独立 manifest、浏览器和 golden 任务可并行;同一仿真会话内的 tick、命令、HAL 和 OPFS 提交必须串行。并行只是缩短验证时间,不得引入非确定性。
* **冷热路径分离**:仿真周期只处理内存中的热点状态;OPFS、日志压缩、差异计算和完整快照在有界批次中运行。任何后台工作都必须有队列上限和取消点,不能拖慢周期或无限积压。
* **预算先测后优化**:M1 建立参考场景的周期耗时、P95/P99 Worker 延迟、内存、快照吞吐、恢复时间和 UI 帧时间基线;后续提交默认不得超过基线 20%,否则必须附 profiling、替代方案和期限明确的例外。
性能优化提交必须同时携带确定性回放结果和资源指标;只有速度变快但无法证明状态、错误和恢复一致的实现不得合并。
### 5.6 问答、工具或执行失败后的接续
断网之外,还必须处理请求超时、回答传输中断、工具调用返回失败、工具已执行但确认消息丢失、结果校验失败和 Worker 崩溃。系统不能把“没有收到回答”当成“没有执行”,也不能把“回答失败”当成“任务失败且需要从头开始”。每个任务使用持久化任务账本记录可恢复边界:
| 字段 | 含义 |
|---|---|
| `operation_id` | 一次任务或工具操作的稳定幂等键 |
| `packet_id`、`context_id` | 恢复时必须重新装载的固定上下文 |
| `phase`、`step_id` | 当前里程碑、工作流和步骤;步骤必须可重入 |
| `intent_hash` | 输入、目标和契约的摘要,防止用新任务误恢复旧任务 |
| `prepare_ref`、`commit_ref` | 操作前后 journal/产物引用 |
| `last_verified_step` | 已验证完成的最后步骤,不以“已发送”代替“已完成” |
| `status` | `running`、`retrying`、`recovering`、`awaiting_reconcile`、`completed` 或 `blocked` |
每个有副作用的步骤遵循 `prepare → execute → verify → commit`:先写入 prepare 记录,再以 `operation_id` 调用;收到结果后校验状态哈希、产物和契约,最后写 commit。重试前先查询 `operation_id` 是否已有 commit:已有则直接复用结果,只有明确没有 commit 才能再次执行。执行完成但确认丢失时进入 `awaiting_reconcile`,通过状态查询或产物哈希判定结果,禁止盲目重放。
恢复流程固定为:
1. 重新装载 context lock、最新固定大小上下文包和任务账本,检查 `intent_hash` 是否匹配。
2. 扫描 prepare/commit、工具产物、Worker 事件和 OPFS journal,建立每个 `step_id` 的 `unknown`、`verified` 或 `unfinished` 判定。
3. 已 `verified` 的步骤只引用原证据;`unfinished` 步骤从最近 checkpoint 继续;`unknown` 步骤先查询和对账,不能猜测。
4. 每完成一个步骤立即更新 `last_verified_step` 和上下文包的 `evidence/risks/next` 段;不追加历史对话,因此恢复前后仍受 8,192 tokens/32 KiB 预算约束。
5. 所有步骤完成后写入 `completed` commit,并生成可重跑的证据索引;无法安全判定时保留本地版本,进入 `blocked` 或 `awaiting_reconcile`,不得静默标记成功。
网络和回答通道都恢复后,自动接续只发送未确认的操作摘要,使用指数退避和有限重试;重复响应、重复事件和迟到响应按 `operation_id` 丢弃或关联到原结果。该机制保证在本地已提交边界内可继续执行;对外部服务不可查询、数据已发生不可逆变化或上下文摘要冲突的情况,只能保证不丢失本地证据并明确要求对账,不能伪造“恰好执行一次”的结论。
## 6. 里程碑、依赖和退出条件
技术路线的阶段 A-E 映射到以下可审查里程碑。里程碑不是日期承诺,而是证据包边界。
| 里程碑 | 对应路线 | 关键交付 | 退出条件 |
|---|---|---|---|
| M0 基线锁定 | A | context lock、来源/测试清单、状态枚举、责任分工 | 清单可重建;缺项 CI 失败;文档链接和基线一致 |
| M1 运行时骨架 | A | 单 Worker、固定 tick、ABI、最小 OPFS journal、Python 启动 | create/load/step/snapshot/recover ABI 测试通过;无网加载缓存样例 |
| M2 核心行为 | B | G/M/O、TP、坐标、补偿、[K0-K5 运动学主线](linuxcnc-web-wasm-kinematics-migration-plan.md)、原生 golden | 受影响核心 manifest 全部 `verified`;每个运动学族的首个 divergent tick 可定位 |
| M3 虚拟设备与持久化 | C | HAL/PLC/IO、K6 运动集成、数字孪生、事务恢复、断网接续 | 故障注入和恢复矩阵通过;配额不足不损坏已提交版本 |
| M4 Web 工作流 | D | 控制台、3D、VCP/配置工具、导入导出、离线 PWA | 关键 UI 工作流在浏览器矩阵通过;键盘/触摸和刷新恢复有证据 |
| M5 发布候选 | E | 全量覆盖、兼容/许可证报告、迁移工具、离线包 | 所有可计入条目 `accepted`,排除项可识别,发布门禁全绿 |
里程碑依赖单向推进:M2 不能绕过 M1,M3 不能把未版本化的核心状态写入 OPFS,M4 不能以 UI mock 替代 M2/M3 的真实状态,M5 不接受“后续补齐”的覆盖条目。发现契约或基线变化时,回退到受影响的最早里程碑并重新生成证据。
## 7. CI、合并与发布门禁
### 7.1 合并门禁
每个变更包至少通过:文档链接和术语检查、格式/静态检查、受影响单测、manifest 完整性检查、原生/WASM 差异测试(适用时)、离线影响场景(适用时)、`context_packet.py` 生成器测试、`validate_task_pointer.py` 启动指针检查、`validate_model_budget.py` 请求预算检查、上下文包字段/大小检查和证据包字段检查。任何新增来源、配置、组件、G/M/O 功能或 UI 工作流没有 manifest 条目时 CI 失败。上下文包超过 8,192 tokens 或 32 KiB、启动指针超过 8 KiB、模型输入加输出预留超出窗口、出现未定义段、把旧包全文嵌入新包,均直接阻止合并。生成阶段不能产生超限包;验证阶段只能复核,不能负责裁剪。
### 7.2 里程碑门禁
* **G0 基线门禁**:context lock 可重建,依赖和生成器版本锁定。
* **G1 行为门禁**:核心状态、canonical 输出、错误码和关键轨迹对照通过;容差写入报告。
* **G2 持久化门禁**:刷新、Worker 重启、半提交、损坏记录、迁移和配额不足场景通过。
* **G3 离线门禁**:断网启动、中途断网、标签页挂起、重新联网对账、请求超时、回答中断、工具结果丢失和 Worker 重启场景通过;没有静默丢命令、重复副作用或跳过步骤。
* **G4 覆盖门禁**:所有 `SIM-*`、`WEB-EQUIV`、`PY-WASM` 条目达到 `accepted`;`REAL-EXCLUDED` 均有明确错误和说明。
* **G5 发布门禁**:浏览器矩阵、性能预算、安全/权限审查、许可证清单、迁移和回滚演练全部有签名证据。
门禁失败时,发布候选保持原 context lock 不变;修复可以追加提交,但若改变契约或基线,必须创建新锁并重新跑受影响门禁。任何例外都必须记录范围、风险、补救责任人和失效日期,不能永久豁免。
## 8. 任务编写与追踪规则
任务标题使用 `[WS-x][Mx]` 前缀,正文包含目标、非目标、输入契约、输出证据、依赖、风险、回滚和受影响 manifest。父任务必须按[上下文治理与最小任务目录](linuxcnc-web-wasm-context-governance.md)拆成单结果子任务;执行者只读取该子任务及其直接依赖,不把整份路线、覆盖矩阵或父任务历史装入上下文。任务启动时创建一个固定大小的上下文包;每次交接只更新 `evidence`、`risks` 和 `next` 等对应段,并递增 `packet_id`,不把历史对话或旧包拼接进来。证据候选必须交给 `scripts/context_packet.py` 预筛选,禁止手工把完整日志塞进包。任务完成不是“代码已写”,而是对应条目达到约定状态并附上可重跑命令。阻塞任务保留原上下文和失败日志的摘要及哈希;不得通过删除样本、降低容差或把 `in_progress` 改成 `REAL-EXCLUDED` 来清除阻塞。
最小任务拆分建议与技术路线第 11 节一致:先做 WS-0 的清单和 golden 格式,再做 WS-1/WS-2 的 `sim_step` 与快照,随后做 WS-3 的 WorkspaceRuntime 和恢复测试,最后接入 UI 和全量回归。每个垂直切片都必须同时包含一个正常路径、一个错误路径和一个恢复路径,避免只交付“能运行一次”的演示。
## 9. 里程碑检查清单
里程碑评审前,负责人逐项确认:
1. context lock、源码/工具链版本和文档 revision 一致。
2. 每个任务上下文包均为固定八段结构,未超过 8,192 tokens/32 KiB,旧包只以哈希引用。
3. 新增或变更的 manifest 条目、测试 ID、golden 和差异报告互相可追溯。
4. 正常、边界、错误、暂停/恢复、断网和回答/工具失败场景均有自动化结果。
5. OPFS 已提交版本、临时文件、journal、任务账本和 checkpoint 状态可解释且可恢复。
6. 失败重试使用幂等键,已确认步骤不会重复执行,未确认步骤不会被跳过。
7. 真实硬件排除项未被误报为仿真支持,仿真数字孪生仍覆盖所需可观察行为。
8. 性能、配额、浏览器兼容和许可证风险已记录,未关闭的风险有责任人和期限。
9. 变更可按证据包中的步骤回滚,并且回滚后旧 context lock 仍可重建。
通过后,将评审记录、门禁结果和 context lock 摘要作为里程碑附件;未通过则保持里程碑开放,不发布“部分完成”的兼容声明。
## 10. 与现有路线的落地关系
技术路线第 8 节的测试层级和第 9 节的阶段 A-E 由本文件第 6、7 节转化为可执行退出条件;覆盖矩阵第 1、2、13 节的状态、manifest、完整性和覆盖率要求由本文件第 2.1、3.4、7 节落实。OPFS 的目录、事务和快照设计以技术路线第 5 节为实现约束,并以本文件第 5 节的故障矩阵作为验收补充。浏览器 UI、Worker 通信、快照、背压、前端任务和 FE-M0..M5 以[前端与 LinuxCNC 通信详细设计](linuxcnc-web-wasm-frontend-communication-design.md)为实现约束。固定上下文包和提交字段由本文件第 3.2、3.3 节统一,任何后续文档若新增阶段、状态或门禁,必须在这里登记并同步关联文档。