Files
linuxcncWebWASM/docs/linuxcnc-web-wasm-contracts.md

113 lines
7.1 KiB
Markdown
Raw Permalink 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 契约快照
**契约版本**:`sim-contracts.v1`
**任务**:`P0-03`
**Context**:`linuxcnc-2.10.0-pre0-wasm-m1-py-c1`
**状态**:implementation-verified(只冻结现有边界;不表示 release accepted)
本文冻结浏览器 Dedicated Worker 与 WASM 核心之间可观察的字段、版本和错误策略。它是当前 context 的兼容性快照,不替换 `context-lock.json`,也不改变已有 C ABI、Worker protocol 或 recovery schema。`sim_task.v1` 只冻结 P1-01 将实现的状态对象形状;本任务不实现该 runtime。
## 1. 版本和边界
| 契约 | 线上的版本标识 | 当前实现边界 |
|---|---|---|
| `sim_task` | `sim_task.v1` | P1-01 authoritative state 预留;本任务只做形状校验 |
| 命令信封 | `command-envelope.v1` | Worker 输入使用 `schema_version: 1` |
| 快照 | `snapshot.v1` | `web/worker/replay.mjs` 的 snapshot 记录 |
| 事件日志 | `event-log.v1` | `web/worker/replay.mjs` 的事件日志 |
| 工作区包 | `workspace-package.v1` | 包 schema 固定;`format_version` 1/2 是同一 envelope 的可验证迁移 |
版本号是整数且不隐式降级。未知版本、缺少必填字段、未知顶层字段、非有限数、哈希或游标不一致都 fail closed。新必填字段或语义变化必须建立新 schema 标识和迁移测试;同一 v1 只允许增加明确标为可选的诊断字段。
## 2. `sim_task.v1`
这是单 Worker authoritative task 的可观察状态,不是 C ABI 结构体。字段顺序在 canonical JSON 中按键名排序,数值使用 JSON 安全整数或有限 IEEE-754 数。
| 字段 | 类型 | 要求 |
|---|---|---|
| `schema` | string | 固定 `sim_task.v1` |
| `context_id`、`session_id` | string | 非空;恢复时必须与锁定 context/session 相符 |
| `state` | enum | `NEW`、`READY`、`RUNNING`、`PAUSED`、`ESTOP`、`FAULT`、`STOPPED`、`DESTROYED` |
| `mode` | enum | `MANUAL`、`MDI`、`AUTO` |
| `program_status` | enum | `idle`、`loaded`、`running`、`paused`、`completed`、`aborted`、`fault` |
| `seq` | positive integer | 最后应用的命令序号 |
| `simulation_tick` | non-negative integer | 逻辑 tick;不使用墙钟 |
| `current_line` | non-negative integer or null | 当前程序行;未知时为 null |
| `state_hash` | string | 对除 hash 外的完整状态做确定性 hash |
P1-01 可以在同一 schema 内增加由契约测试先登记的可选域;改变上述语义或枚举必须升级版本。Task 状态、事件、快照和 operation ledger 必须在相同 tick 边界提交。
## 3. `command-envelope.v1`
命令及其 Worker 响应共享以下信封字段:
| 字段 | 要求 |
|---|---|
| `schema_version` | 固定整数 `1`,对应 C `sim_protocol_accept` 的 schema 参数 |
| `kind` | 非空字符串,长度不超过 128(C `has_text` 边界);命令种类由 capability/Worker 白名单决定 |
| `context_id` | 非空字符串,必须等于 context lock |
| `session_id` | 非空字符串;同一会话不可改变 |
| `seq` | 从 1 开始严格连续递增;重复副作用通过 `operation_id` 对账 |
| `operation_id` | 副作用命令必填的非空字符串,长度不超过 128;只读查询可省略 |
| `simulation_tick` | 可选的非负安全整数;不得小于 Worker 当前 tick |
| `sent_at_monotonic_ms` | 可选有限数,仅用于诊断,绝不参与状态或 hash |
| `payload` | 必填 JSON object;不能是数组、null 或包含非有限数 |
当前 C ABI 的状态与错误保持不变:`SIM_PROTOCOL_ERR_SCHEMA=-100`、`SEQUENCE=-101`、`STATE=-102`、`OPERATION_ID=-103`、`KIND=-104`、`ARGUMENT=-105`、`TICK=-106`。Worker 以稳定的 `protocol_*` 错误键映射这些结果,不能把错误改成成功或静默重排命令。
## 4. `snapshot.v1`
`SnapshotStore` 导出的对象固定为 `{schema, snapshots}`;每条 snapshot 使用:
```json
{
"schema": "snapshot.v1",
"snapshot_id": "snap:<seq>:<state_hash>",
"base_snapshot_id": null,
"seq": 1,
"tick": 0,
"state": {},
"state_hash": "fnv1a64:<16 lowercase hex>"
}
```
`seq` 为正整数且同一序号只能有一个内容,`tick` 不回退;增量 snapshot 的 `base_snapshot_id` 必须引用已导入的 snapshot。`state_hash` 和 `snapshot_id` 必须由同一 canonical state 重算。容量不足、重复序号、缺少 base、哈希不符和非有限 state 均拒绝,导入失败不得替换当前 store。
## 5. `event-log.v1`
日志对象固定为 `{schema: "event-log.v1", cursor, tick, events}`。每条事件固定字段:
```json
{
"seq": 1,
"tick": 0,
"kind": "SESSION_READY",
"payload": {},
"payload_hash": "fnv1a64:<16 lowercase hex>"
}
```
`seq` 从 1 严格连续,`tick` 单调不减,`kind` 非空且最多 64 个字符,payload 必须是 object。C 端保留 `sim_event_record` 的固定宽度布局(`uint32_t seq/tick`、`uint64_t payload_hash`、32 字节 kind),并保留 `SIM_EVENT_CAPACITY=256` 与错误码 `-200..-204`。WASM 和 Worker 对同一输入必须得到相同 cursor/tick/hash;半成品导入不改变当前日志。
## 6. `workspace-package.v1`
包 envelope 的必填字段为 `schema`、`format_version`、`context_id`、`committed_seq`、`checkpoint`、`timeline`、`journal`、`content_hash`、`package_hash`。`format_version: 1` 是当前基线;已实现的 `format_version: 2` 只添加 `migration` 和 `integrity` 元数据,事件、snapshot、checkpoint、journal payload 保持 byte-for-byte v1 兼容。
迁移规则:
1. v1→v2 必须标记 `lossless: true`、源 package hash 和 timeline/journal/checkpoint integrity hash。
2. v2→v1 只有在源 hash 对账成功时允许;原包不可变,回滚只生成新目标包。
3. 不支持的版本、context、提交序号、timeline/checkpoint、content/package hash 或 journal 顺序均拒绝。
4. 缺失/损坏资源在离线模式返回明确错误;不能用空包或当前内存状态替代已提交包。
## 7. 兼容性决策记录
- **ABI**:保留 `sim_api.h`、`sim_protocol.h`、`sim_event.h` 的函数签名、整数宽度、错误码和容量语义;没有修改锁定 ABI。
- **协议**:Worker 继续传递 `schema_version: 1`,C validator 继续拒绝非 1、序号缺口、tick 回退和副作用命令缺失 `operation_id`。
- **快照/事件**:沿用 `replay.mjs` 的 `schema` 字段和 FNV-1a-64 canonical hash;不把 `snapshot.v1` 或 `event-log.v1` 改成新的顶层结构。
- **工作区**:沿用现有 workspace migration 的 v1/v2 envelope;v2 不是新的 payload schema,不修改已锁定的事件/快照/checkpoint/journal 版本。
- **未知字段**:v1 顶层未知字段和缺失字段 fail closed;仅 `sent_at_monotonic_ms` 这一诊断字段可选且不影响 hash。需要其它字段时先建 v2 schema/迁移测试。
- **排除边界**:真实硬件、宿主路径、实时内核和动态库仍是 `REAL-EXCLUDED`;主轴、编码器、探针、限位、换刀、冷却和 IO 的仿真契约不因此排除。
直接证据:`web/worker/contracts.mjs`、`web/worker/test_contracts.mjs`、`wasm-core/tests/sim_contract_test.c`、`scripts/test_sim_contract.py`、`coverage/sim-contract.report.json`。