7.1 KiB
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 使用:
{
"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}。每条事件固定字段:
{
"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 兼容。
迁移规则:
- v1→v2 必须标记
lossless: true、源 package hash 和 timeline/journal/checkpoint integrity hash。 - v2→v1 只有在源 hash 对账成功时允许;原包不可变,回滚只生成新目标包。
- 不支持的版本、context、提交序号、timeline/checkpoint、content/package hash 或 journal 顺序均拒绝。
- 缺失/损坏资源在离线模式返回明确错误;不能用空包或当前内存状态替代已提交包。
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。