# 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 的默认执行后端。
