# Rust 性质测试与 TDD 验证契约

| 项目 | 值 |
|---|---|
| 状态 | Approved execution plan：用户授权自行完成并免逐步 review |
| 日期 | 2026-10-12 |
| 已确认基线 | [Rust 数据模型](0001-rust-data-model.md)、[核心 RFC](../rfcs/0001-async-mailbox-and-memory-sync.md)、[Dashboard RFC](../rfcs/0002-operator-dashboard.md) |
| 本阶段输出 | 观察边界、独立模型、生成器、故障与反例、平台验证、TDD切片及验收口径 |

这是执行测试契约，用户已授权自行完成实现并免逐步 review。当前尚未创建测试或服务源码。P01–P18的规范定义以[原性质计划](../rfcs/0001-properties.md)为准；本文明确 Rust实施方法，并增加 Dashboard验证。按用户最新授权直接进入 Step 4。

## 1. 确认的观察边界

| 边界 | 真实观察入口 | 测试方式 |
|---|---|---|
| 公开领域服务 | send、inbox、ACK、status、message/Blob/memory读取 | Rust集成测试与 proptest命令历史 |
| 持久存储适配 | 相同公开操作，新实例读取实际字节 | 文件 SQLite与文件对象；D1/R2契约验证 |
| 传输 | HTTP请求与真实 MCP协商、工具发现/调用 | 认证、schema、错误和跨入口重试 |
| 参考消费者 | sync、待处理列表、处理确认与出站提交 | 本机 SQLite重启与故障历史 |
| Dashboard | 管理 HTTP接口与真实页面交互 | 集成测试、浏览器操作及内容检查 |
| Skill与宿主适配 | 实际调用轨迹及回执报告 | 场景评测，区分引导与真实 Hook能力 |

通过公开读取和下载断言业务结果。直接 SQL只用于夹具、适配诊断和故障布置，不作为业务正确性的唯一旁路；不测试私有 helper调用次数。

## 2. 独立参考模型与生成

参考模型只保存身份/授权关系、请求对应的稳定结果、发布事实顺序、消费者声明及检查点、来源记忆的完整状态、封存字节。它可以使用简单映射和集合，不复刻生产 SQL或状态机实现。

公共 DTO与 ID类型可以共享；生产授权、游标编码、CAS、去重、JCS实现和分页 helper不能作为参考模型的判定函数。JCS使用 RFC 8785外部固定向量验证，包括 UTF-16属性排序、数组顺序、数字序列化和禁止重复对象键；不靠调用被测指纹函数重新计算期望。[RFC 8785](https://www.rfc-editor.org/rfc/rfc8785)

proptest生成一段有界命令历史，测试驱动逐项调用真实公开操作并比较结果。采用符号角色、请求和资源引用，使反例缩减后不依赖偶然生成的生产 ID。非法引用仍作为输入执行，检查稳定错误与状态不变。

| 命令族 | 必须生成的维度 |
|---|---|
| Send / Retry / Status | 同键同内容、同键变异、默认收件、回复、跨入口、响应丢失 |
| Read / Ack | 页大小1和边界、稀疏可见项、迟到发布、重复/逆序阶段、不同视图 |
| Prepare / Put / Seal | 空内容、边界长度、摘要/类型不符、迟到 PUT、封存/清理竞争 |
| Snapshot / Delta / ReadMemory | 空快照、遗漏条目、删除不存在项、旧版本、无变化及固定版本分页 |
| Rotate / Revoke / Disable | 同权限换凭据、改变grants、等待发布时撤销、停用来源 |
| Crash / Recover / AdvanceClock | 关闭进程状态再打开实际数据库、有限存储故障、期限推进 |
| AdminRead / Login / Logout | 角色混用、会话到期、secret轮换、Inspector读取与分页 |

本地/CI先采用有界短历史，数量以执行开销调整。确定性回归始终运行，不依赖随机是否再次命中。失败保留 seed和缩减历史；公开提交反例仅包含合成夹具，不保存真实凭据或用户内容。proptest回归 seed文件进入版本控制，稳定反例另外变成具名用例，避免生成器变更后失去原场景。[失败持久化文档](https://proptest-rs.github.io/proptest/proptest/failure-persistence.html)

## 3. P01–P18到运行用例的映射

| 性质 | 主要随机历史 | 公开可观察断言 |
|---|---|---|
| P01 隔离 | 多Space、同名身份、外部ID、越权Blob与Collection | 不读取或改变未授权资源；错误遵循存在性隐藏规则 |
| P02 幂等 | 多次同键、内容等价变换、响应丢失和重启 | 一个资源和一次发布，稳定ID/时间/版本；当前授权有效 |
| P03 内容冲突 | 改正文、收件人、附件、版本参数后复用键 | IDEMPOTENCY_CONFLICT；原结果及Feed不变 |
| P04 可恢复 | committed后关闭全部服务实例再打开 | 原正文、附件与稳定提交结果真实可读 |
| P05 未发布不可见 | 对象成功后阻断元数据提交 | Feed和业务读取无该资源，恢复后恰一次发布 |
| P06 晚提交 | A先保留，B先发布，读B后完成A | A序号晚于B，下一次增量读得到A |
| P07 消费独立 | 两Consumer分别ACK、停用、重启 | A的状态不改变B的进度或原内容 |
| P08 回执单调 | processed先到、received迟到、多次重试、确认receipt项 | 阶段不回退；无重复推进事件或确认循环 |
| P09 游标 | 多页、权限/过滤改变、篡改、同权限轮换 | 稳定视图完整有序；跨范围拒绝；新视图可重新补收 |
| P10 CAS | 同基础版本准备两个更新，交错完成 | 最多一次前进；失败无版本/Feed；原成功键仍回原版本 |
| P11 模式含义 | 完整替换、局部更新、空快照、无变化 | Snapshot等于完整输入；Delta保留遗漏；无变化不增版本 |
| P12 删除来源 | 不同Collection复用ID，删除、旧同步、明确恢复 | 来源隔离；旧更新拒绝；历史Tombstone可读 |
| P13 封存 | 错摘要、未封存引用、迟到PUT和清理竞争 | 发布的全部资料完整且字节不变，整批失败无半份内容 |
| P14 轮换撤销 | 上传/准备间换凭据或撤销，旧键查询 | 同权限连续性；旧凭据无法读取原结果或完成发布 |
| P15 通知独立 | 可选Webhook实现后才生成重投与到期历史 | 核心进度不受通知影响；未实现时capability为false |
| P16 入口一致 | HTTP→MCP及MCP→HTTP重试，合法/非法输入 | 解包后的领域结果一致，仍为同一幂等操作 |
| P17 本机队列 | 下载、本机提交、ACK、处理之间逐点重启 | 已收取输入仍有待处理记录；未保存不推进服务检查点 |
| P18 最终补收 | 有限故障后停止写入、恢复原请求并完整分页 | 在有效授权、保留与公平重试条件下覆盖全部可见提交 |

HTTP/MCP响应的传输封装与只读跟踪ID可以不同；比较领域错误、状态与资源身份。MCP必须正确标记工具失败，不把JSON-RPC协议错误硬映射成业务成功。

## 4. 必须保留的确定性反例

| 反例 | 期待行为 |
|---|---|
| A先保留、B先发布、读B、再完成A | 后续页仍收到A |
| 第二个记忆CAS实际命中0行 | 原请求记录冲突，但无新版本、head推进或Feed |
| 无变化判定后另一请求前进head | 旧expected_revision不能取得无变化成功 |
| 成功请求重试时head已经前进 | 先查原幂等结果，仍返回原revision |
| 两个Consumer读取同一项，只确认其中一个 | 另一Consumer进度独立 |
| processed先到再received | 保持processed且无额外推进事件 |
| ACK提交成功但响应丢失 | 原键重试无第二次回执变化 |
| token轮换与准备期间撤销 | 新同权限凭据可恢复；已撤销凭据不能发布 |
| 空snapshot及迟到旧delta | 清空有明确Tombstone，旧同步不能自动恢复 |
| memory第一页后head改变 | 后续页仍属于第一页绑定的版本 |
| 零字节Blob、重复未封存PUT、封存后迟到PUT | 校验真实空摘要；同内容不覆盖；封存后拒绝 |
| view/grants变化后旧checkpoint更大 | 新视图从起点补收，不被旧进度拦住 |
| 语义相同JSON键/收件顺序改变 | 同一提交；附件顺序改变仍按有序列表语义处理 |
| 重复JSON键、未知请求字段、路径越界、超部署限额 | 整批拒绝，无隐式截断或发布副作用 |

故障通过真实外部存储/网络边界的可控包装器在操作前后注入。测试可以丢弃已成功响应；Crash丢失服务内存并重新建立实例，保留已确认耐久写入。CAS失败用实际被推进的Collection造成，不让mock冒充0行正确性。

多请求交错在单线程测试执行器中可控重放；这不要求生产增加线程池。另验证相同真实数据库上的多个服务实例。数据库全局物理丢失与无限断网不在已确认性质前置条件内。

## 5. Dashboard性质 D01–D09

| ID | 操作/生成 | 永远满足的观察结果 |
|---|---|---|
| D01 管理身份隔离 | 普通Agent凭据、缺失Cookie、其他身份调用管理路由 | 无管理内容或状态变更；只接受有效实例管理员 |
| D02 会话生命周期 | 登录、退出、期限推进、secret轮换，写操作改变Origin | 过期/撤销/旧secret会话失效；跨来源Cookie写入被拒绝 |
| D03 事实与筛选 | 多Space/Actor及不同消息、版本、回执，随机过滤分页 | 管理视图对应所选范围的真实事实，时间不表示在线状态 |
| D04 阶段准确 | committed、received、processed及重复/逆序回执 | 页面与接口呈现真实阶段，不宣称外部任务已经成功 |
| D05 固定版本检查 | 指定两个revision，分页间更新head | 展示与结构差异始终属于选定版本 |
| D06 查看无消费副作用 | 登录后访问概览、消息、活动、版本和对比 | Consumer回执、检查点和待处理状态不被查看推进 |
| D07 凭据输出受限 | 生成、导航、重新列表、导出与日志检查 | 原token仅首次发放时显示；后续数据、静态HTML和日志不泄露 |
| D08 内容仅作数据 | HTML/脚本、Markdown链接、恶意文件名及metadata | Inspector不执行正文脚本或权限指令，管理状态不变 |
| D09 管理创建幂等 | 同管理request_id重试、丢响应、同键内容变异 | 一次创建、同结果；冲突不产生另一个身份或凭据 |

D01/D02/D05/D06/D09主要通过管理HTTP与公开数据面交叉观察；D04/D07/D08补真实浏览器交互。页面断言依据可见内容和实际状态，不断言CSS类名或组件私有结构。浏览器失败保存截图与操作轨迹，不以截图单独证明权限或持久性。[Playwright断言](https://playwright.dev/docs/test-assertions)

## 6. 原生与Cloudflare适配验证

| 适配 | 必查契约 | 结果口径 |
|---|---|---|
| 文件SQLite | 原子发布、CAS、外键隔离、重启、回执及checkpoint | 实际文件数据库通过，不能用内存实现替代 |
| 文件对象 | create-only、真实字节校验、封存、文件与目录耐久写入 | 新实例下载原字节 |
| 本地Worker模拟 | Wasm构建、MCP/HTTP路由、bindings及D1/R2执行路径 | 单独标注模拟验证 |
| 真正D1/R2 | batch guard、0行CAS、条件put结果、checksum、授权撤销 | 单独标注真实平台验证 |
| 平台限额 | 查询/参数预算、请求大小、集合条数、CPU/内存 | describe只公布实际可支持值 |

Cloudflare验证使用隔离的测试资源和合成数据。没有真实平台运行结果时，必须明确“未验证”，不能把模拟结果写成生产适配通过。真实资源配置在实现产生可运行产物后处理；本阶段不部署。

## 7. Nowledge Mem借鉴的接入与携带验证

[研究笔记](../research/0002-nowledge-mem.md)给出一手依据与借鉴边界。首版采用以下行为，保留已有身份和schema：

- 无真实生命周期hook时标为guided/manual；MCP可调用不代表私有对话自动导出，摘要不能称完整备份。
- 接入例子用真实request/resource ID验收：describe→send→status→新Consumer收取→下载原文→本机保存→ACK；记忆按返回revision和item_id回读。
- 来源缺失不猜测；metadata中runtime/verified声明不改变认证发布者和grants。
- 固定revision导出带明确范围、路径与摘要，排除凭据；单Collection资料包不称完整实例备份。
- Dashboard区分当前active条目、Tombstone和历史版本，查看不ACK；相同名称和宿主不合并Actor身份。

这些场景分别扩展P01/P02/P04/P09/P11/P16/P17及D03–D07。知识图谱、语义检索、Working Memory生成、专用hook及OKF exporter留作独立范围，不把它们强加到核心服务。

## 8. Step 4的TDD切片顺序

每次先写一个公开行为失败用例，再写最少实现使其通过，随后增加该切片的性质与恢复场景。不会先写全部想象中的测试再一次实现全部服务。

1. 验证Rust原生/Wasm运行目标、最小认证控制面与MCP describe/工具发现路径。
2. 形成真实SQLite与不可变正文支持的投递→查询→收取闭环，验证幂等、隔离和恢复。
3. 加入独立Consumer、回执、checkpoint与回信，保留晚发布反例。
4. 加入实际字节上传、create-only和封存、授权下载。
5. 加入记忆Snapshot/Delta、CAS、历史版本、Tombstone与完整导出。
6. 完成11个必需MCP/HTTP操作的同语义验证。
7. 加入参考消费者SQLite待处理/出站队列，以及真实Skill调用场景。
8. 加入Dashboard管理会话、具名读取、Inspector与身份/凭据管理页面。
9. 运行真实Cloudflare适配契约，测量并固定部署限额。

原生测试使用cargo test与proptest；Wasm构建、传输互操作及浏览器检查分别记录。依赖与构建版本在Step 4根据官方支持固定，不在本阶段生成占位crate或CI。

## 9. 验收与执行授权

- 必需的P01–P14、P16–P18与D01–D09均有运行用例和可复现反例机制。
- P15随可选事件适配实施，未实现时明确capability=false，不声称该性质已通过。
- 服务、参考消费者、Skill、页面、模拟平台和真实平台结果分别报告。
- 接入说明与Skill不得把工具可用说成宿主已自动保存、定时运行或获准执行外部动作。
- 当前没有已通过的服务测试声明；按用户最新授权直接依上述切片开始实现。
