# 设计文档与多模型协作约定

本目录把 `docs/roadmap.md` 里的各个里程碑展开成**可被 coding agent 流水线执行**的详细设计文档。

- [`m1-db-consolidation.md`](./m1-db-consolidation.md) — 单库化地基
- [`m2-frontend-v2.md`](./m2-frontend-v2.md) — React SPA 前端 v2
- [`m3-token-mobile.md`](./m3-token-mobile.md) — token 鉴权与移动端（远期）
- [`m4-login-hardening.md`](./m4-login-hardening.md) — 登录加固（防爆破/指数退避 + CLI 逃生 + 可选 TOTP）**先做**
- [`m5-iot-energy.md`](./m5-iot-energy.md) — IoT 集成与能耗采集（Modbus/Energy + MQTT/HA Discovery + 前端侧边栏）
- [`m6-tibber-dynamic-energy.md`](./m6-tibber-dynamic-energy.md) — 通用电价层 + DSMR 实时电表接入 + 实时买卖电费计算 + HA Energy 反哺
- [`m7-meter-epochs-archival.md`](./m7-meter-epochs-archival.md) — 电表生命周期 / 换表归档（Meter epochs）

本文件定义**所有任务共用的格式与协作规则**，各个里程碑文档不再重复这些约定。

---

## 1. 协作模型：Orchestrator → Implementer → Reviewer

设计目标是让三个不同档位的模型分工协作：

| 角色 | 模型档位 | 职责 |
| --- | --- | --- |
| **Orchestrator（编排者）** | 强模型 | 读里程碑文档，挑出依赖已满足的下一个原子任务，派发给 implementer；收到 reviewer 的 PASS 后推进下一个任务 |
| **Implementer（实现者）** | 便宜模型 | 一次只做**一个**原子任务，严格按任务卡执行，跑完本地校验后回报 |
| **Reviewer（评审者）** | 强模型 | 对照验收标准 + Reviewer checklist 独立复核、独立跑校验闸门，返回 `PASS` 或一份编号返工清单 |

### 循环

```
Orchestrator 选任务 T（其 Depends 全部为 done）
      │
      ▼
Implementer 实现 T ──► 跑校验闸门 ──► 回报 diff + 校验输出
      │
      ▼
Reviewer 复核 T
   ├── PASS ─────────► Orchestrator 标记 T 为 done，进入下一个任务
   └── REWORK[1..n] ─► 退回 Implementer，按编号逐条修，直到 PASS
```

### 角色边界（重要）

- **Implementer 不得扩大范围**：只能改任务卡 `Files` 里列出的文件；超出范围的问题要在回报里以 `OUT-OF-SCOPE:` 标注，交给 Orchestrator 决定是否新开任务，而不是顺手改掉。
- **Reviewer 必须独立重跑校验闸门**，不能只信 implementer 的回报。
- **Reviewer 的返工清单必须可执行、带编号**（`REWORK 1: ...`），不写主观感受。
- 一个任务**不通过校验闸门就不算完成**，Orchestrator 不得跳过。

---

## 2. 原子任务的定义

一个"原子任务"必须同时满足：

1. **单一关注点**：一个任务只解决一件事（一次 schema 变更、一个端点、一个模块迁移……）。
2. **PR 大小**：理想 diff < ~200 行（结构性 sweep 任务可放宽，但应在卡上标 `[structural]` 并优先派给较强 implementer）。
3. **边界处可绿**：任务完成时，整个仓库通过校验闸门（见下）。**一个任务"拥有"它所改代码对应的测试**——如果改动会让某些现有测试失败，修这些测试就属于这个任务的范围，不允许留红给下一个任务。
4. **可独立验收**：验收标准是客观、可机械检查的断言，不依赖人的主观判断。
5. **依赖显式**：通过 `Depends` 字段声明前置任务。没有声明依赖的任务，Orchestrator 可并行派发。

---

## 3. 任务卡格式

每个任务在里程碑文档中以如下结构出现。Implementer 和 Reviewer 都只需要任务卡 + 本约定文件，**不需要读其它任务**即可工作。

```markdown
### M{n}-T{nn} — <标题> [structural?]

- **Status**: `todo` | `in-progress` | `in-review` | `done`
- **Depends**: M{n}-T{nn}, …（或 `none`）
- **Context**: 1–2 句，为什么要做这个、它在里程碑里的位置。

**Files**（精确到路径，标注动作）
- `create  path/to/new_file.py`
- `modify  path/to/existing.py`
- `delete  path/to/old.py`

**Steps**（便宜模型可直接照做的有序步骤）
1. …
2. …

**Out of scope / 不要碰**
- …（明确列出容易被误改的相邻区域，约束便宜模型漂移）

**Acceptance criteria**（客观、可勾选；Reviewer 逐条核）
- [ ] …
- [ ] 校验闸门全绿（见 §4）

**Reviewer checklist**（除验收标准外，强模型重点看的点）
- …
```

---

## 4. 校验闸门（每个任务结束都要全绿）

在仓库根目录、激活 `.venv` 后执行：

```bash
# 1) 单元 / 集成测试（CI 同款，权威闸门）
pytest

# 2) Lint（pyproject 已配置 ruff，line-length=100）
ruff check .

# 3) 若本任务改动了任何 HTTP 路由 / schema：重导出 OpenAPI 并确认已提交
python scripts/export_openapi.py
git diff --exit-code openapi/    # 必须无未提交差异
```

- `pytest` 是**权威闸门**：`.github/workflows/pytest.yml` 跑的就是它，任何任务都不得让它变红。
- 改了路由 / Pydantic schema 的任务，`openapi/openapi.json` 和 `openapi/openapi.yaml` 必须在同一任务里重新生成并提交（`openapi/` 纳入版本控制）。
- 前端（M2/M3）相关任务的前端侧闸门（lint / typecheck / build）在对应里程碑文档里单独定义。

---

## 5. 提交与集成约定

- 每个任务一个 commit，message 前缀任务 ID，例如：`M1-T03: unify data layer onto single app DB engine`。
- 一个里程碑在一个 feature 分支上推进（如 `feature/m1-db-consolidation`），按任务依赖顺序合并。
- 任务卡里的 `Status` 字段由 Orchestrator 维护，作为流水线的单一进度源。
- 涉及**不可逆 / 数据破坏**的步骤（删旧 DB 文件、删 Grafana volume 等）一律不进自动化任务，只在文档里标为人工步骤（见 M1 的"人工操作"小节）。

---

## 6. 数据安全红线（贯穿所有里程碑，不可违反）

1. **任何脚本 / migration 都不得删除或覆盖用户数据文件**（旧 `.db`、备份、volume）。删除只能是人工、事后、确认无误的独立步骤。
2. 涉及历史数据的迁移**先在备份副本上演练**，再对真实库执行。
3. 数据迁移脚本必须**幂等**且**搬完对账行数**，对不上立即中止并非零退出。
4. 破坏性 Reviewer 一票否决：只要任务里出现"删文件 / drop 有数据的表 / truncate"，Reviewer 直接 REWORK，要求改为人工步骤。
