Appearance
4.4 防御性编程模式
本章概览:DSH 把实际踩过的 bug 类别沉淀为七条防御性编程规则(defensive patterns)。它们是"生命周期、并发、子进程、teardown"类工作的必读清单——也是理解 DSH 为什么这样写的钥匙。
官方原文:每个模式都是"实际在这里发生或几乎发生的缺陷类别,写成防止其复发的规则"。
出处:
docs/defensive-patterns.md(deepseek-harness-src/docs/defensive-patterns.md)
1. 正交的结果独立报告
一个结果可以同时是好几件事——进程可以既超时又以退出码 0 结束(因为它捕获了信号)。必须把每个独立事实(timedOut、signal、exitCode)各自报告,绝不把一个标志的报告嵌套在另一个的分支里,否则调用方会把"被截短的运行"误读为"干净的成功"。
2. 双端遵守公共契约
实现方收到同一结果的多种表示时,在通过公共 API 返回前先规范化。例子:LlmAdapter.stream() 实现可能抛错或发 finish {kind:'error'|'aborted'},但 LlmRuntime.stream() 只以终态 finish 暴露模型请求失败——中间件与消费者缺陷保持抛出。这让消费者无需猜测异常来自提供方、包裹器、chunk 日志还是自己的组装。
3. 异步状态不是同步状态
agent.followup()没有逐消息的完成或结果;- 后台任务的完成会与轮次边界竞争;
reader.close()对 EOF 与 dispose 都会触发。
因此:不要把 agent/status 或 whenIdle() 当作某条 follow-up 的结果——多个排队的 follow-up、steer、注入的工作可能共享同一个 running 区间。真正拥有一次运行的自动化调用方必须显式界定区间(从消息的持久 inbox 回执到下一个整 agent idle),并把选中的输出描述为区间范围内的,而非因果归于该消息。双向都要防:如果被等待的转换永远不会发生,等待会挂起——显式处理"没什么可等"的分支。
4. Dispose 必须到达安静,而不只是请求安静
只发出 kill/abort 就返回、工作还在跑,会留下孤儿。清理要 async 并 await 孩子的退出(kill → await done);并且在 kill 之前关闭监听器/通知注册表,让迟到的完成保持沉默。
5. 派发器包含回调异常
用户提供的监听器抛错,绝不能 reject 它所在的 promise,也不能饿死后面的监听器。在派发循环上 try/catch 并记录日志——一个坏订阅者绝不破坏核心生命周期。DSH 中每个事件派发点(session/event、agent/*、tools/result 等)都遵守此条。
6. 绝不把不受信任的输出交给环境或可预测路径
- 环境清洗:spawn 的命令获得擦洗过的 env(丢弃
*KEY*/*SECRET*/*TOKEN*/*PASSWORD*),harness 凭据不能泄漏进输出、env 或 spill 文件; - 私有临时文件:temp/spill 文件用私有(0700)目录、随机名、独占仅属主打开(
'wx'、0o600)——可预测的世界可读路径招引符号链接竞争与披露。
7. 删除链接形状的路径
可能是符号链接或 Windows junction 的路径,用 lstatSync().isSymbolicLink() 判断后 unlinkSync() 删除:unlink 只删链接、拒绝真实目录,绝不顺着链接进目标。Windows 的 rmSync(link) 对 junction 抛 ERR_FS_EISDIR;递归删除可能顺着链接进入目标。递归 rmSync 只用于已知的真实目录。
这些模式的"测试梯队"对应物
防御模式解决实现层;测试侧有对应纪律(docs/testing.md):真实入口路径(test the real entry path)、验证世界而非自述(verify the world, not the self-report)、资源所有权(resource ownership)。
小结
七条模式本质上是四个主题:结果要正交且规范化、状态要区分同步/异步与区间、清理要安静、信任边界要闭合。写 DSH 插件时若涉及 teardown、并发、子进程,先对照这份清单。
参考资料
- docs/defensive-patterns.md — 防御模式(官方)(
deepseek-harness-src/docs/defensive-patterns.md) - docs/testing.md — 测试策略(测试梯队对应物)(
deepseek-harness-src/docs/testing.md)