diff --git a/docs/design/topic11-multi-project-proposal.md b/docs/design/topic11-multi-project-proposal.md index 194e3b0..f054a15 100644 --- a/docs/design/topic11-multi-project-proposal.md +++ b/docs/design/topic11-multi-project-proposal.md @@ -183,32 +183,33 @@ class DaemonHealth: **设计原则**:状态全在 SQLite,Daemon 无状态。重启 = 重新加载所有项目 + 所有任务状态,继续执行。 -**恢复流程**: +**恢复流程(保守策略)**: ``` PM2 检测 Daemon 挂了 → 重启 Daemon │ ├── 读取 _registry.yaml → 恢复项目列表 ├── 遍历每个 active 项目 → 打开 SQLite 连接 - │ └── 读取所有任务状态(pending/working/completed/failed/...) - ├── 为每个 active 项目启动 ProjectSlot 线程 - │ - └── ProjectSlot._tick() 扫描 working 任务 - └── working 任务 → 重新 spawn Agent(可能冗余执行一次) - └── Agent 自己判断:上次做了没有?产出在不在? - ├── 产出已存在 → 跳过,直接报告完成 - ├── 做到一半 → 继续完成 - └── 没做过 → 正常执行 + ├── 扫描所有 working 任务 → 标记为 failed(原因: "Daemon restart, agent process lost") + ├── 启动 ProjectSlot 线程 + └── 后续 pending 任务正常分配 ``` -**关键假设**: +**为什么不重新执行**: +1. Daemon 崩溃是不正常事件,Agent 子进程状态不可预测 +2. output.json 可能写了一半,重新执行比恢复更安全 +3. 用户手动 retry 比自动重新执行更可控 +4. task_attempts 表记录完整,不丢信息 + +**关键设计**: 1. **SQLite 是真相来源**——所有任务状态、产出记录都在 `.db` 文件里,Daemon 内存无状态 -2. **Agent 能判断重复**——AI native,Agent 看到 task context + 已有 output 文件,能自主判断是否需要重新执行 -3. **冗余执行无害**——即使一个节点被多执行一次,结果是幂等的(Agent 会检查产出是否已存在) -4. **无限续杯**——重启后 working 节点重新执行,属于已有的"无限续杯"机制(项目内运转机制,不在此设计) +2. **SQLite WAL 保护数据完整性**——崩溃时未提交的事务自动回滚 +3. **ActiveAgentCounter / DaemonHealth 重启后归零**——不需要持久化 +4. **task_attempts 的 attempt_index 递增**——retry 不覆盖历史 **不需要额外存储**: - 不需要 checkpoint 文件——SQLite 就是 checkpoint +- 不需要 recovery log——task_attempts 表已经记录所有尝试 - 不需要 recovery log——`task_attempts` 表已记录每次尝试 - 不需要状态快照——每次 tick 从 SQLite 实时读取