Files
cfdaily 98eb15125d
CI / lint (pull_request) Successful in 6s
CI / test (pull_request) Successful in 9s
CI / notify-on-failure (pull_request) Successful in 0s
chore(docs): 归档 §20 审查文档至 archive-3.0,追加审查历史
- review-v3-vs-head-pangtong.md → archive-3.0/
- review-v3-vs-head-simayi.md → archive-3.0/
- step5-audit-report.md → archive-3.0/
- step5-impact-analysis.md → archive-3.0/
- §20 新增 §19 审查与验证历史(关键发现+修复状态汇总)
2026-06-13 08:49:41 +08:00

12 KiB
Raw Permalink Blame History

Step 5 引擎接入 — 影响分析与逐点对照

方法论

逐行审查 dispatcher.py / spawner.py / ticker.py 中所有 is_mail / _mail / project_id == "_mail" 分支, 对照 handler 实现,确认每个特殊处理的去向。


一、dispatcher.py985 行)

1.1 Guardrail 跳过(L127-129

is_mail = project_config.get("project_id") == "_mail" if project_config else False
if self.guardrails and not is_mail:
    violations = self.guardrails.check_task(task)

特殊处理Mail 不做 guardrail 检查。

Handler 覆盖:设计文档 D6 "skip_guardrail 从接口删除,guardrail 自己判断"。Step 5 改为:if self.guardrails and handler is None(无 handler 时走 guardrail),或者用 handler.virtual_project 判断。handler 存在时跳过 guardrail。

改动is_mailTaskTypeRegistry.get_by_project(project_id) is not None


1.2 Mail on_checks_passedL194-213

on_checks_passed = None
_mail_marked_working = False
if is_mail and db_path:
    def _mail_on_checks_passed():
        nonlocal _mail_marked_working
        if not _disp._mail_auto_working(_task_id, _mail_db):
            raise RuntimeError("mail_auto_working_failed")
        _mail_marked_working = True
    on_checks_passed = _mail_on_checks_passed

特殊处理Mail spawn 前通过 on_checks_passed 回调标 working,标记成功后才 spawnspawn 失败回退。

Handler 覆盖MailHandler.pre_spawn 调用 _auto_mark_working,和 _mail_auto_working 逻辑完全一致。

改动

  • on_checks_passed 改为调用 handler.pre_spawn(task_id, db_path)
  • _mail_marked_working 标记保留,用于 Exception 回退

1.3 Mail on_completeL224-238

if is_mail:
    def _mail_on_complete(aid, outcome):
        _dispatcher._mail_auto_complete(_task_id, aid, _mail_db, _must_haves, outcome=outcome)
    on_complete = _mail_on_complete

特殊处理Mail on_complete 调用 _mail_auto_complete(含 inform/request 分支、幻觉门控、重试 3 次、失败通知)。

Handler 覆盖MailHandler 使用基类 post_complete 统一流程(crash→verify→mark→notify)。但现有 _mail_auto_complete 有几个细节差异需要注意:

现有逻辑 Handler 覆盖 差异
request 无回复 → 重试 3 次标 failed on_failure 标 failed + notify ⚠️ 缺少 3 次重试
inform 只在特定 outcome 标 done verify 始终返回 True → 基类标 done 简化了,合理
标 done 重试 3 次 _mark_task_status 单次 ⚠️ 缺少重试
notify_mail_failed on_failure 中调用 notify_mail_failed 一致

⚠️ 关键发现:现有代码标状态时有 重试 3 次 机制(防止 DB 锁),handler 的 _mark_task_status 只做一次。需要把重试逻辑补到 _mark_task_status 或在 handler 层加。

改动on_complete 改为调用 handler.post_complete(task_id, agent_id, outcome, db_path)


1.4 Task on_completeL241-310

else:
    def _task_on_complete(aid, outcome):
        # #07.2: crash 回退
        if outcome in ROLLBACK_CURRENT_AGENT_OUTCOMES and _task_db:
            _dispatcher._rollback_current_agent(_task_db, _task_id, aid)
        
        if _is_review:
            if outcome in ("completed", "session_revived"):
                # 读 verdict → approved 标 done / 非 approved @mention assignee
            else:
                logger.warning("review agent outcome=%s, NOT marking done", outcome)
        else:
            # executor: 三信号验证 → 标 review
            _dispatcher._task_auto_complete(_task_id, _task_db)

特殊处理清单

  1. #07.2 crash 回退executor 和 review 都回退 current_agent → assignee
  2. review 分支outcome 必须是 "completed" 或 "session_revived" 才走 verdict 读取
  3. review verdict 读取approved → done,非 approved → @mention assignee + 保持 review
  4. review @mention:通过 Blackboard.add_commentcomment_type="review"
  5. executor 分支:走 _task_auto_complete → 三信号验证 → review

Handler 覆盖

  • crash 回退: BaseTaskHandler.post_complete 第一步
  • review verdict⚠️ TaskHandler.handle_review_complete 存在但未被 dispatcher 调用。现有 dispatcher 直接在闭包里做了,不走 handler。
  • @mention⚠️ handler 用 conn.execute("INSERT INTO comments") 直接插入,dispatcher 用 Blackboard.add_comment(会做更多处理,如 comment_type="review"
  • executor 三信号: TaskHandler.verify_completion

⚠️ 关键发现

  1. dispatcher 的 review @mention 用 bb.add_comment(..., comment_type="review")handler 直接 INSERT 不带 comment_type。需要修复 handler。
  2. dispatcher 对 review outcome 有白名单检查(只处理 "completed"/"session_revived"),handler 的 post_complete 没有 outcome 白名单——crash 已在基类处理,其他 outcome 都会走 verify。
  3. dispatcher review 非 approved 时保持 review 状态handler 的 handle_review_complete 标回 working。这是行为差异

改动:需要先修复 handler 的 review 分支,再替换 on_complete。


1.5 Mail spawn 失败回退(L355-358

except Exception as e:
    if _mail_marked_working:
        self._mail_revert_to_pending(task.id, db_path)

特殊处理spawn 失败(subprocess 启动失败)回退 working → pending。

Handler 覆盖 handler 没有这个。这是 dispatcher 级别的异常处理,和 handler 无关。但 toolchain 也需要类似逻辑。

改动:保留在 dispatcher 中,改为 _mail_marked_workinghandler_marked_working


1.6 Legacy dispatchL584-660

is_mail_legacy = project_config.get("project_id") == "_mail"
if is_mail_legacy:
    if not self._mail_auto_working(task.id, db_path_legacy):
        return error

特殊处理legacy 路径(router=None 时触发)也有 mail 特殊处理。

Handler 覆盖:同 1.2/1.3,用 handler 替代。

改动:同样用 handler.pre_spawn 和 handler.post_complete 替代。


1.7 现有 Mail 辅助方法(L658-870

_mail_auto_working / _mail_revert_to_pending / _mail_auto_complete / _mail_check_reply

改动:Step 5 不删这些方法(安全起见保留,标记 deprecated),只改调用方。确认稳定后再删。


二、spawner.py1704 行)

2.1 _build_prompt 中的 mail 分支(L282-284

if project_id == "_mail":
    return self._build_mail_prompt(task_id, title, description, must_haves, agent_id)

特殊处理Mail 用专用精简模板。

Handler 覆盖MailHandler.build_prompt 通过 PromptComposer 拼 3 个 section。

改动:查注册表 → handler.build_prompt(context)。需要构建 PromptContext 传入。


2.2 _build_api_sectionL321-325

success_status = '"done"' if project_id == "_mail" else '"review"'

特殊处理Mail 的 success_status 是 done。

Handler 覆盖:已由 handler 的 PromptSection 处理(TaskApiSection hardcode reviewMailApiSection 不含 status 回写指令)。

改动:如果 handler 存在,跳过 _build_api_sectionhandler.build_prompt 已包含)。


2.3 classify_outcome 中的 handler 调用

spawner 在 classify_outcome 后调 on_complete(outcome)。on_complete 是 dispatcher 传入的闭包。

改动on_complete 闭包改为调用 handler.post_complete。spawner 本身不直接查注册表。


三、ticker.py1897 行)

3.1 虚拟项目扫描(L218-229

mail_db = Path(self.registry.root) / "_mail" / "blackboard.db"
if mail_db.exists() and "_mail" not in active_projects:
    pr = await self._tick_project("_mail", {...})

特殊处理_mail 硬编码扫描。

Handler 覆盖TaskTypeRegistry.virtual_projects() 返回 ["_toolchain", "_mail"]。

改动:循环 TaskTypeRegistry.virtual_projects() 替代硬编码。_toolchain 如果也需要 ticker 扫描就自动发现。但需确认 _toolchain 是否需要 ticker——当前 toolchain 任务创建和完成都在 toolchain_routes.py 中处理,可能不需要 ticker 扫描。


3.2 _transition_status 中 mail assignee 不清空(L953-960

if new_status == "pending":
    if self._current_project_id == "_mail":
        # Mail 的 assignee 是收件人,永不清空
        conn.execute("UPDATE tasks SET status=?, updated_at=? WHERE id=?", ...)
    else:
        conn.execute("UPDATE tasks SET status=?, assignee=NULL, ...", ...)

特殊处理Mail 重置到 pending 时不清空 assigneeassignee 是收件人)。

Handler 覆盖 handler 不管 ticker 的状态转换逻辑。这是 ticker 内部逻辑。

改动:用 TaskTypeRegistry.get_by_project(project_id) 判断替代硬编码。


3.3 Mail 跳过 claimed 状态(L1029-1043

if project_id == "_mail":
    conn.execute("UPDATE tasks SET current_agent=? WHERE id=?", ...)
    # 跳过 claimed,直接 working

特殊处理Mail 不走 claimed 中间态(已在 dispatcher 中标 working)。

Handler 覆盖handler.pre_spawn 的 _auto_mark_working 跳过了 claimed。

改动:用 handler 判断替代硬编码。


3.4 _dispatch_reviews 跳过 mailL1304

if project_id == "_mail":
    return []

特殊处理Mail 不走 review 流程。

Handler 覆盖MailHandler.target_success_status = "done",不走 review。但 ticker 的 _dispatch_reviews 是看项目级。

改动:用 handler 判断。


3.5 Mail 幻觉门控兜底(L1474-1492

if self._current_project_id == "_mail":
    has_reply = self._mail_check_reply(task.id, db_path)
    if has_reply:
        # working → done

特殊处理:Ticker 超时检查时,如果 mail 有回复,标 done 而非 failed。

Handler 覆盖 handler 的 check_completion 只返回 bool,不做状态标记。

改动:调用 handler.check_completion 替代 _mail_check_reply。状态标记逻辑保留在 ticker 中。


3.6 _mail_check_replyL1555-1575

和 dispatcher 版本一致。

改动:用 handler.check_completion 替代。


3.7 虚拟项目 init + recovery 扫描(L1625-1643

for virtual_id in ("_general", "_mail"):
    ...
    # _mail 项目不清空 assignee

改动virtual_projects() + _general 硬编码。


四、Handler 缺陷(需在 Step 5 前修复)

# 缺陷 影响 修复方案
H1 BaseTaskHandler._mark_task_status 无重试 DB 锁时标状态失败,任务卡住 加 3 次重试(和 dispatcher 现有行为一致)
H2 TaskHandler.handle_review_complete 中 @mention 不带 comment_type="review" review comment 无类型标记 INSERT 加 comment_type
H3 dispatcher review 非 approved 保持 review 状态,handler 标 working 行为差异 handler 改为保持 review 状态(和 dispatcher 一致)
H4 dispatcher review outcome 有白名单("completed"/"session_revived"),handler 无 crash 之外的异常 outcome 也会走 verify handler 的 post_complete 已在基类处理 crash,其余 outcome 走 verify 是合理的

H3 最关键——dispatcher review 非 approved 保持 review 状态(等 assignee 自己处理),handler 标 working 会触发 ticker 重新 dispatch executor,这不是预期行为。

五、改动策略

不删旧代码,只改调用方

  1. dispatcher 中 is_mail → handler 判断,on_checks_passed/on_complete → handler.pre_spawn/post_complete
  2. spawner 中 _build_prompt → handler.build_prompt
  3. ticker 中虚拟项目扫描 → registry.virtual_projects()mail 特殊判断 → handler 判断
  4. 旧方法(_mail_auto_working 等)标记 @deprecated 保留,不删

先修 handler 缺陷(H1-H3),再改引擎