From cda205763a92800dd01930e32a060e0d9366d029 Mon Sep 17 00:00:00 2001 From: cfdaily Date: Wed, 3 Jun 2026 00:42:29 +0800 Subject: [PATCH] auto-sync: 2026-06-03 00:42:29 --- docs/design/06-pm2-crash-recovery.md | 35 ++++++++++++++++++---------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/docs/design/06-pm2-crash-recovery.md b/docs/design/06-pm2-crash-recovery.md index 74b3e99..16345ea 100644 --- a/docs/design/06-pm2-crash-recovery.md +++ b/docs/design/06-pm2-crash-recovery.md @@ -1,11 +1,11 @@ # #06 PM2 Crash 恢复设计 -> 版本: v1.1 -> 日期: 2026-06-01 +> 版本: v1.2 +> 日期: 2026-06-03 > 作者: 庞统(副军师) -> 状态: 二次评审中 +> 状态: 待评审(v1.2) > 前置: spawner-monitor-design.md §5 A0(Agent crash 恢复) -> 变更: v1.1 纳入司马懿评审意见(Blocking #1 reviewing 状态 + Blocking #3 虚拟项目 + 建议 #2 审计事件) +> 变更: v1.2 两个关键改进:(1) working→pending 保留 current_agent 让同一 agent 接手;(2) reviewing 精确恢复到前置状态而非硬推 done --- @@ -127,15 +127,19 @@ PM2 启动 moziplus v2 ``` working + last_attempt.completed? - ├─ No → pending + ├─ No → pending(保留 current_agent) └─ Yes + has_output? - ├─ No → pending + ├─ No → pending(保留 current_agent) └─ Yes + has_handoff? - ├─ No → pending(可能标了 working 就 timeout 退出了,产出残留) + ├─ No → pending(保留 current_agent) └─ Yes → review(agent 已完成工作并交接) ``` -**注意**:`pending` 推回后 assignee 被清空,ticker 自然重新 broadcast 或 dispatch。如果原来有 assignee,agent 会看到之前的 outputs 和 comments(黑板数据不丢失),可以选择继续或重新认领。 +**保留 current_agent 原则**:推回 pending 时不清空 `current_agent`,让同一 agent 重新接手。原因: +1. 该 agent 之前有对话上下文(现已丢失),但黑板上的 outputs/comments 是它写的,它最了解上下文 +2. Dispatcher 路由时如果 current_agent 仍在,直接复用,省去重新路由 +3. Agent 被 bootstrap 注入完整任务上下文(含之前的产出),自行决定继续还是重来 +4. 这是冗余操作,但保证状态一致 #### review 状态 @@ -168,13 +172,20 @@ working + last_attempt.completed? **含义**:parent task 进入庞统 round review 的中间状态(`done/failed → reviewing`)。 -**恢复逻辑**:推回 `working`,让 ticker 重新触发 `_check_round_complete`。 +**恢复逻辑**:查 events 表找到转入 reviewing 之前的状态(done 或 failed),精确推回该状态。 -**原因**:reviewing 状态依赖 `_spawn_pangtong_review` 的回调 `_handle_review_conclusion` 来推进。PM2 crash 后回调丢失,`_handle_review_conclusion` 永不触发,parent 永久卡死。推回 working 后,下个 tick 的 `_check_round_complete` 检查 sub task 全部终态 + parent 状态非 done/failed → 跳过。但 `_check_round_complete` 只在 `parent_status in (done, failed)` 时触发,所以需要推回 working 后手动触发一次,或者直接推回 done(让 `_check_round_complete` 自然重新触发 review spawn)。 +**决策树**: -**最终策略**:推回 `done`(恢复到 reviewing 之前的状态),`_check_round_complete` 会自然重新触发庞统 review。 +``` +reviewing → 查 events 表最后一条 task_status_changed 到 reviewing 的记录 + ├─ 找到前置状态 done → 推回 done + ├─ 找到前置状态 failed → 推回 failed + └─ 找不到 → 兜底推 done(done/failed→reviewing 是唯一合法路径,大概率是 done) +``` -> 注:reviewing 状态是从 done/failed 转入的(`_set_parent_reviewing`),所以推回 done 是合法的(reviewing → done 在 VALID_TRANSITIONS 中)。但更安全的做法是查 events 表看 reviewing 之前是什么状态。简化起见直接推 done。 +**原因**:reviewing 状态依赖 `_spawn_pangtong_review` 的回调 `_handle_review_conclusion` 来推进。PM2 crash 后回调丢失,`_handle_review_conclusion` 永不触发,parent 永久卡死。推回 done/failed 后,`_check_round_complete` 会在下个 tick 自然重新触发庞统 review spawn。 + +**精确恢复优于硬推 done**:因为 parent 可能是从 failed 转入 reviewing 的(子任务失败触发 round review),推回 done 语义错误。查 events 表是确定性操作,成本可忽略(单条 SQL)。 #### escalated / waiting_human / paused 状态