2565133af3
implemented(除 4 篇超长文档随后补)、proposed、rejected 全树配对; 同一流水线 + 二遍校验(paraphrase-back + 仓库上下文一致性)产出。 docs/rfc/implemented/AGENTS.md 与其 CLAUDE.md 符号链接列入排除 (agent 指令文件,与根 AGENTS.md 同策略)。
3.3 KiB
3.3 KiB
RFC:对协议形态代码进行基于属性的测试
Status: implemented
English | 中文
将原始提案与决策记录合并为一篇。首次运行即发现了 BlockAssembler 的重复
block-end真实 bug。
问题
基于示例的测试只能固定我们想到的用例。harness 的核心是协议形态的代码:分片流、事件日志、schema 转换、收件箱调度。这类代码的输入空间是组合爆炸的,有趣的 bug 藏在没人写过示例的交错序列里。佐证:一个 block 组装的排序 bug 曾在 happy path 100% 行覆盖率下存活。逐文件 100% 覆盖率只能证明每行都跑过,不能证明每种交错都正确。
决策
引入 fast-check(根 devDependency),在每个协议形态的包中编写一个 tests/properties.spec.ts。生成器调优为逼真但对抗性的输入(而非均匀噪声),numRuns 控制在本地套件总耗时远低于约 10 秒。失败时打印可复现的 seed。(原始提案还草拟了一个夜间 CI job,以 100 倍迭代运行;该部分未交付——属性测试套件仅在常规 push/pull_request CI 中运行,定时高迭代 job 仍属可能的后续工作。)
- dsh-llm / BlockAssembler: 任意分片流(合法 + 畸形:重复索引、滞后分片、缺少 block-start)。不变式:
blocks()数量 ≤ 出现过的不同索引数;重组幂等(blocks()在重复调用间稳定,且message().content与之一致);blocks()从不抛异常且只产出合法的 content-block 标签;finish反映最后一个finish分片,无finish分片时默认为{kind:'stop'}。 - dsh-session: 任意事件日志。不变式:
deriveMessages确定性;从 seed 回放结果一致;seq 严格单调递增;非消息事件不影响派生历史;派生内容与日志解耦。 - dsh-tools: 任意
SchemaSpec。不变式:JSON Schema 的required等于每层required:true的键集合;转换是全函数;并且与运行时参数校验组合验证——满足 spec 的生成参数通过validateArgs,定向破坏(删除 required 键、顶层非 object)被拒绝。这封堵了 validator 与InferArgs漂移的风险。 - dsh-agent-loop: 任意发送调度,对接一个永不耗尽的适配器,通过
agent/statussettle 信号驱动(无挂钟 sleep)。不变式:无消息丢失;轮次编号严格递增;状态转换始终在合法状态机上。
后果
- 生成器质量是价值杠杆——生成器偏向小索引池和短字符串,使碰撞与交错频繁出现。
- 已经产出回报: BlockAssembler 的流测试发现了一个真实 bug——同一索引的重复
block-end覆写了已刷出的块,导致流式前缀与最终blocks()不一致。已修复(首次关闭生效,与既有的滞后分片规则一致),并附带专门的回归测试。 - 属性测试因超时而 flake 是一个发现,不应重试了事。agent loop 的属性测试在设计上是确定性的(通过
agent/statussettle),因此挂起即为真实缺陷。 - 属性测试是示例测试的补充而非替代;示例测试固定特定分支,服务于 100% 覆盖率门禁。