Files
storage-labos/flashops/docs/decisions/0005-simulator-as-first-class.md
T
yuanshuai c91a64fddb
CI / Python 3.12 (push) Waiting to run
CI / Python 3.9 (push) Waiting to run
chore(repo): initialize team collaboration repository
2026-07-27 20:40:12 +08:00

2.1 KiB
Raw Blame History

ADR-0005:模拟器是一等公民,不是测试脚手架

  • 日期:2026-07-27
  • 状态:已接受

背景

第一波没有真实硬件(没有可牺牲的 DUT、没有带外控制器、没有温箱)。 常规做法是先写代码、等硬件到位再联调。

决定

模拟 DUT + 模拟带外控制器 + 故障注入器做成产品代码的一部分 services/host-agent/flashops_agent/simulator/),与真实适配器共用同一套接口, 而不是塞进 tests/ 当替身。

模拟器支持注入的故障至少覆盖报告 Phase 1a 要求验证的三类: 杀 Agent、蓝屏/panic、拔网线;外加掉盘与数据完整性失败。

理由

  1. 它是长期资产,不是临时替身。 有了真硬件之后,模拟器仍然是唯一能 在 CI 里跑"100 循环 + 注入 20 次故障"的东西。真设备跑一轮要几小时, CI 里不可能天天跑。
  2. 恢复逻辑的测试覆盖只能靠它。 五级恢复阶梯里的 L4/L5(ATX 长按、AC 断电) 在真机上每测一次都有风险且很慢。这条路径恰恰是产品价值所在,必须能高频回归。
  3. 它是谈判道具的底座。 报告把 Phase 1a 的产出定义为"一台会自救的演示台—— 同时是合伙人与客户谈判的最强道具"。在拿到硬件之前,模拟器版本已经能把 完整故事演一遍:注入蓝屏 → 看着它自己救回来 → 断点续跑 → 出证据包。
  4. 它划清了接口。 能被模拟器替换掉的地方,就是硬件接入的边界。 写模拟器的过程本身就在逼迫接口设计正确。

代价

  • 模拟器与真实实现可能漂移(模拟器里能过、真机上过不了)。 缓解:tests/test_adapter_contract.py 对模拟与真实适配器跑同一套契约测试; 真实适配器接入后,契约测试是第一道闸。
  • 有"在模拟器上跑通了就以为完事了"的风险。缓解:README 的能力表里, 模拟实现一律标 ⚠️ 或"模拟",不标 真实。

什么时候推翻

不推翻。真实硬件接入后模拟器保留,作为 CI 的默认执行后端。