基于庞统+司马懿背靠背 review,修复 6 个问题:
P0 致命:
- A1: _legacy_on_complete 补回 review verdict 处理(approved→done,非 approved→@mention assignee)
- A2: 添加 TaskTypeRegistry.register() 启动初始化(注册 Task/Mail/Toolchain handler)
P1 中等:
- B11-1: RoleSkillSection 从索引提示改为全文注入(对齐设计 §2.3 + BootstrapBuilder 行为)
- A8: retry prompt is_mail 硬编码改走 TaskTypeRegistry handler 判断
P2 低:
- _mail_* 4 个方法添加 DEPRECATED 注释
- ticker.py handler check_completion 代码块缩进对齐(28→24 空格)
测试:394 passed, 0 failed
Review reports: docs/design/review-v3-vs-head-{pangtong,simayi}.md
31 KiB
v3.0 vs HEAD 背靠背 Review — 司马懿
日期: 2026-06-10 (v2)
范围: v3.0 tag → HEAD(6 commits, +1584/-134 行, 9 个文件)
方法:git diff v3.0..HEAD逐文件逐行比对 + v3.0 源码git show v3.0:回溯验证
独立判断: 不参考庞统 review,独立产出后比对
总览
v3.0 → HEAD 的核心改动是 Step 2-5 五层架构重构:
| 层 | 新增/改动 | 说明 |
|---|---|---|
| Protocol + Registry | task_type_registry.py(已有,未改) |
TaskTypeHandler Protocol + TaskTypeRegistry |
| 基类 | base_task_handler.py(新增 +183) |
BaseTaskHandler — crash/verify/mark/notify 统一流程 |
| Handler × 3 | task_handler.py(+378)、mail_handler.py(+210)、toolchain_handler.py(+277) |
各自实现 build_prompt / verify_completion / post_complete |
| 引擎接入 | dispatcher.py(-95/+58)、spawner.py(+38)、ticker.py(+31/-27) |
_mail_* 硬编码 → TaskTypeRegistry 查表 |
| 设计文档 | step5-impact-analysis.md(+324)、step5-audit-report.md(+74) |
影响分析 + 双重审计 |
核心结论:架构方向正确,但 handler 注册初始化缺失导致所有 handler 路径为死代码,实际运行仍走 _legacy_on_complete 旧路径。旧路径中 review verdict 处理被删除,造成 非 handler 项目的 review 流程失效。
Part A: v3.0 逻辑丢失检查
方法论
逐文件追踪 v3.0 中每个 _mail / _task / project_id == "_mail" 分支,验证 HEAD 中是否存在等价实现。分三层检查:
- 功能等价:新代码是否完整覆盖旧逻辑
- 路径可达:新代码是否会被实际执行(handler 注册?legacy fallback?)
- 行为一致:边界条件、异常处理是否等价
检查结果
A1 🔴 致命:dispatcher.py — review verdict 处理丢失
v3.0 逻辑(dispatcher.py L253-308 _task_on_complete):
if _is_review:
if _task_db and outcome in ("completed", "session_revived"):
# 读 verdict
review = conn.execute(
"SELECT verdict FROM reviews WHERE task_id=? ORDER BY created_at DESC LIMIT 1",
(_task_id,)).fetchone()
if review and review["verdict"] == "approved":
_dispatcher._mark_task_status(_task_db, _task_id, "done")
else:
# 非 approved → @mention assignee + 保持 review
bb.add_comment(_task_id, "daemon", f"@{assignee} 审查结论: {verdict_str}")
HEAD 逻辑(dispatcher.py L246-258 _legacy_on_complete):
def _legacy_on_complete(aid, outcome):
if outcome in ROLLBACK_CURRENT_AGENT_OUTCOMES and _task_db:
_dispatcher._rollback_current_agent(_task_db, _task_id, aid)
if not _is_review: # ← review 时整个 if 被跳过
_dispatcher._task_auto_complete(_task_id, _task_db)
分析:
_legacy_on_complete在_is_review=True时什么也不做——无 verdict 读取、无 done 标记、无 @mention 通知TaskHandler.handle_review_complete()方法有完整 verdict 处理,但 handler 未注册(见 A2),此代码不可达- 影响:所有非 handler 项目(
_general等)的 review agent 完成后,任务永远停在review状态
补充:rebuttal 路径不受影响——_rebuttal_on_complete 在 ticker.py L756-790 独立定义,直接读 verdict 并处理,不经过 _legacy_on_complete。
A2 🔴 致命:Handler 注册初始化缺失
证据:
$ grep -rn "TaskTypeRegistry.register" src/
# 零结果
TaskTypeRegistry.register() 在整个代码库中从未被调用。TaskHandler / MailHandler / ToolchainHandler 类已定义但从未实例化和注册。
后果链:
TaskTypeRegistry.get_by_project()永远返回None- 所有
if handler:分支不进入 → 走else/ fallback 路径 TaskTypeRegistry.virtual_projects()返回空列表 →_mail/_toolchain不被 ticker 自动发现
各路径受影响分析:
| 路径 | dispatcher | spawner | ticker | 实际走什么 |
|---|---|---|---|---|
Mail _mail |
handler=None → _legacy_on_complete |
handler=None → 旧 _build_mail_prompt |
virtual_projects() 空 → _mail 不被 tick |
旧路径(无 handler),但 ticker 不扫描 _mail |
Task _general |
handler=None → _legacy_on_complete |
handler=None → BootstrapBuilder |
不涉及 handler | 旧路径,但 review 处理被删(A1) |
Toolchain _toolchain |
N/A | N/A | virtual_projects() 空 → _toolchain 不被 tick |
完全不可达 |
⚠️ A2 导致 ticker 不再扫描 _mail 虚拟项目,这是 v3.0 有、HEAD 丢失的行为——v3.0 中 _mail 硬编码在 ticker L218-229,HEAD 改为 TaskTypeRegistry.virtual_projects() 但注册为空。
需要添加的初始化代码(缺失):
# 应在 server.py 或 bootstrap.py 的启动流程中
from src.daemon.task_handler import TaskHandler
from src.daemon.mail_handler import MailHandler
from src.daemon.toolchain_handler import ToolchainHandler
TaskTypeRegistry.register(TaskHandler())
TaskTypeRegistry.register(MailHandler())
TaskTypeRegistry.register(ToolchainHandler())
A3 🟡 中等:dispatcher.py — 旧 _mail_* 方法成为死代码
v3.0:_mail_auto_working / _mail_auto_complete / _mail_check_reply / _mail_revert_to_pending 被 dispatch() 主流程调用。
HEAD:这些方法仍保留在 dispatcher.py 中(L628-860),但主流程已改走 handler 路径。由于 handler 未注册,主流程走 _legacy_on_complete(无 handler 分支),也不调用这些方法。
结论:方法体保留但无外部调用者,属于死代码。不影响当前运行(因为 _legacy_on_complete 有独立的 executor 逻辑),但增加维护混淆。
A4 🟢 低:dispatcher.py — spawn 失败回退等价
v3.0:self._mail_revert_to_pending(task.id, db_path) — 调独立方法。
HEAD:内联代码(L309-327),BEGIN IMMEDIATE + 状态检查 + UPDATE ... SET status='pending'。
等价:新版逻辑更通用(不限于 _mail,任何 handler 项目都可回退)。
A5 🟢 低:dispatcher.py — _legacy_dispatch 路径 handler 化
v3.0:is_mail_legacy = project_id.get("project_id") == "_mail"
HEAD:handler_legacy = TaskTypeRegistry.get_by_project(project_id_legacy)
等价:handler_legacy 为 None 时跳过 pre_spawn,与 v3.0 中 is_mail_legacy=False 行为一致。_legacy_dispatch 本身仅在 router=None 时触发,当前配置不会进入。
A6 🟢 低:spawner.py — prompt 构建双路径
v3.0:if project_id == "_mail": return self._build_mail_prompt(...) → 走 BootstrapBuilder。
HEAD:handler = TaskTypeRegistry.get_by_project(project_id) → if handler: return handler.build_prompt(ctx) → else 走 BootstrapBuilder。
分析:
- handler 未注册时,等价于 v3.0(走 BootstrapBuilder)
- handler 注册后,Task/Mail/Toolchain 走新 PromptSection 路径
- 注意:新旧路径的 Skill 注入策略不同——旧路径(BootstrapBuilder)全文注入 Skill,新路径(RoleSkillSection)只给索引+引导语。这可能导致 handler 注册后 Agent 行为变化
A7 🟢 低:spawner.py — _build_api_section success_status
v3.0:success_status = '"done"' if project_id == "_mail" else '"review"'
HEAD:success_status = '"done"' if handler.target_success_status == "done" else '"review"'
等价:handler 未注册时走 else 分支 → '"review"',与 v3.0 非 mail 项目一致。
A8 🟡 中等:spawner.py — retry prompt 仍用硬编码
v3.0:is_mail = project_id == "_mail" → 用 MAIL_RETRY_PROMPT 模板。
HEAD:同样 is_mail = project_id == "_mail" 硬编码(L1128),未改走 handler。
影响:不影响功能(retry prompt 正确),但与设计文档 §6 不一致。属于 Step 5 未覆盖的遗留点。
A9 🟢 低:ticker.py — 虚拟项目扫描
v3.0:硬编码 _mail 扫描。
HEAD:TaskTypeRegistry.virtual_projects() 循环。
分析:逻辑正确,但注册为空时 _mail 不被扫描(见 A2)。注册后自动发现 _mail + _toolchain,比 v3.0 更可扩展。
A10 🟢 低:ticker.py — assignee 清空条件
v3.0:if self._current_project_id == "_mail": → 不清空 assignee。
HEAD:handler = TaskTypeRegistry.get_by_project(...); if handler: → 不清空。
等价:handler 未注册时,非 handler 项目正常清空 assignee。
A11 🟢 低:ticker.py — 跳过 claimed 状态
v3.0:if project_id == "_mail": → 跳过 claimed,直接 working。
HEAD:handler = TaskTypeRegistry.get_by_project(project_id); if handler: → 跳过。
等价。
A12 🟢 低:ticker.py — review dispatch 跳过
v3.0:if project_id == "_mail": return []
HEAD:handler = TaskTypeRegistry.get_by_project(project_id); if handler: return []
等价。
A13 🟢 低:ticker.py — 超时检查幻觉门控
v3.0:
if self._current_project_id == "_mail":
has_reply = self._mail_check_reply(task.id, db_path)
if has_reply:
... # mark done
HEAD:
handler = TaskTypeRegistry.get_by_project(self._current_project_id)
if handler and handler.check_completion(task.id, db_path):
... # mark done
等价:MailHandler.check_completion 内部调 _check_reply,查询语义与 v3.0 的 _mail_check_reply 完全一致(SELECT id FROM tasks WHERE id != ? AND must_haves LIKE ?)。
缩进问题:HEAD L1483 if handler and handler.check_completion(...): 后续 body 缩进 5 级(28 空格),与同级代码不一致。不影响运行,但增加维护混淆。
A14 🟢 低:ticker.py — startup recovery 虚拟项目列表
v3.0:for virtual_id in ("_general", "_mail"):
HEAD:virtual_ids = ["_general"] + TaskTypeRegistry.virtual_projects()
等价:注册为空时只有 _general,注册后自动包含 _mail + _toolchain。
Part A 汇总
| 严重度 | 数量 | 项目 |
|---|---|---|
| 🔴 致命 | 2 | A1 review verdict 丢失, A2 handler 未注册 |
| 🟡 中等 | 2 | A3 死代码未清理, A8 retry prompt 硬编码 |
| 🟢 低 | 10 | A4 |
A1+A2 联合根因分析:
设计意图是 handler 注册后 review 走 TaskHandler.post_complete → handle_review_complete。但注册代码缺失导致:
- 所有项目走
_legacy_on_complete(旧路径) - 旧路径中 review 处理被删除(信任 handler 会处理)
- review agent 完成后无任何后续动作
同时,ticker 不再扫描 _mail 虚拟项目(原来硬编码扫描),_mail 项目的 pending 任务无人处理。
Part B: 13 个重点专题设计-编码一致性
逐专题检查设计文档描述与 HEAD 代码的一致性。标记:
- ✅ 一致
- ⚠️ 设计已标注未实施/Phase N(不算差异)
- ❌ 设计承诺但代码不一致
- 🟡 部分一致
B1: 专题 01 四相循环
设计文档:01-four-phase-loop.md — PRD Phase 1~4 完整实现方案
| # | 设计要求 | 代码现状 | 判定 |
|---|---|---|---|
| B1-1 | §3.3 Spawn Prompt 框架(任务+约束+API+准则+完成标准) | BootstrapBuilder + PromptSection 实现 | ✅ |
| B1-2 | §3.4 @mention 通知机制 | _process_mentions + mention_queue |
✅ |
| B1-3 | §4 Review 机制(verdict → done/notify) | TaskHandler.handle_review_complete(handler 未注册)+ _rebuttal_on_complete(ticker 独立) |
⚠️ handler 路径不可达,但 rebuttal 路径完整 |
B2: 专题 02 Main Session + Delegation
设计文档:02-main-session-delegation.md
| # | 设计要求 | 代码现状 | 判定 |
|---|---|---|---|
| B2-1 | §3.1 投递到 Main Session | use_main_session=True 参数 |
✅ |
| B2-2 | §3.3 续杯机制 | use_main_session=True + session 复用 |
✅ |
| B2-3 | §4.3 消息优先级与中断策略 | 无优先级队列 | ⚠️ 设计描述但未标注 Phase |
| B2-4 | §4.4 Subagent 背压控制 | 无显式背压,靠 counter 间接控制 | ⚠️ |
B3: 专题 03 Prompt 进化
设计文档:03-prompt-evolution.md — 从 SOP 到任务式指挥
| # | 设计要求 | 代码现状 | 判定 |
|---|---|---|---|
| B3-1 | §3.1 广播认领模板改写 | PromptSection 组装(新路径)+ BootstrapBuilder(旧路径) | ✅ |
| B3-2 | P6 反静默降级 | 无 scope-reduction-detection 自动机制 |
⚠️ 设计原则,未强制实施 |
| B3-3 | P7 经验闭环 | 无 IMPROVE 阶段自动触发 | ⚠️ |
B4: 专题 04 黑板协作模型
设计文档:04-blackboard-collaboration-model.md
| # | 设计要求 | 代码现状 | 判定 |
|---|---|---|---|
| B4-1 | §3.1 assignee 降级为显示字段,路由走 @mention | router.py L160-166 仍有 assignee 快速路径 |
⚠️ 设计说 Phase 1 双轨并行,Phase 2 废弃。当前停在 Phase 1 |
| B4-2 | §3.2 @mention 语义增强 | mention_queue + comment_type 已实现 |
✅ |
| B4-3 | §3.3 多人协作 co_assignees |
数据库无此字段 | ⚠️ Phase 3 |
| B4-4 | §3.4 output↔comment 关联 | 无关联字段 | ⚠️ Phase 2 |
| B4-5 | §3.5 层级查询 API | parent_task 支持 |
✅ |
B5: 专题 05 上下文四层架构
设计文档:05-context-layers.md
| # | 设计要求 | 代码现状 | 判定 |
|---|---|---|---|
| B5-1 | L0 铁律层 | workspace 文件注入(SOUL.md/IDENTITY.md 等) | ✅ |
| B5-2 | L1 角色层 | SOUL.md / IDENTITY.md | ✅ |
| B5-3 | L2 引擎注入层 | BootstrapBuilder 实现 | ✅ |
| B5-4 | L3 被动参考层(wiki knowledge) | 无 _inject_wiki_knowledge |
⚠️ 设计标注为 Phase 2 |
B6: 专题 06 PM2 Crash 恢复
设计文档:06-pm2-crash-recovery.md
| # | 设计要求 | 代码现状 | 判定 |
|---|---|---|---|
| B6-1 | §4.1 总体流程 _startup_recover |
ticker.py L1614 有启动恢复 | ✅ |
| B6-2 | §4.2 claimed 状态恢复 | ✅ | ✅ |
| B6-3 | §4.2 working 状态恢复 _recover_working_task |
✅ | ✅ |
| B6-4 | §4.2 review 状态恢复 _recover_review_task |
✅ | ✅ |
B7: 专题 07 Spawner Acquire-First
设计文档:07-spawner-acquire-first.md — #07.1 已实施, #07.2 已实施
| # | 设计要求 | 代码现状 | 判定 |
|---|---|---|---|
| B7-1 | Phase 0 Pre-acquire 修复 | spawner.py L499-512 | ✅ |
| B7-2 | Phase 1 Counter acquire | spawner.py L516-521 | ✅ |
| B7-3 | Phase 2 Session check | spawner.py L523-568 | ✅ |
| B7-4 | Phase 2.5 假死修复 | spawner.py L557-568 | ✅ |
B8: 专题 08 Classify Outcome 优化
设计文档:08-classify-outcome-optimization.md — 已实施 ✅
| # | 设计要求 | 代码现状 | 判定 |
|---|---|---|---|
| B8-1 | A0-A17 判定树 | _classify_outcome 方法 |
✅ |
| B8-2 | A9 api_error 特殊路径 | api_retry_count |
✅ |
| B8-3 | A14-A17 可恢复 retry + cooldown 60s | cooldown_seconds + set_cooldown |
✅ |
B9: 专题 09 Rebuttal + Goal Gate
设计文档:09-rebuttal-and-goal-gate.md
| # | 设计要求 | 代码现状 | 判定 |
|---|---|---|---|
| B9-1 | §2.1 Rebuttal 自动化(review 非 approved → @mention assignee) | review.py RebuttalManager + ticker.py _rebuttal_on_complete |
✅ |
| B9-2 | §2.1 防止无限循环(max 2 轮) | RebuttalManager.MAX_ROUNDS = 2 |
✅ |
| B9-3 | §2.2 目标一致性 Gate | 无自动 goal gate 检查 | ⚠️ 设计为 Agent 端行为,非 Daemon 侧 |
B10: 专题 10 T3 需求探索 + 黑板展示
设计文档:10-t3-requirement-exploration-and-blackboard-display.md
| # | 设计要求 | 代码现状 | 判定 |
|---|---|---|---|
| B10-1 | A2 需求探索过程写黑板 comments | 后端支持 comment_type |
✅ |
| B10-2 | A3 TaskModal 实时刷新 | SSE comment_added / checkpoint_resolved |
✅ |
| B10-3 | D1 砍掉 AI 摘要 | 黑板直投前端 | ✅ |
| B10-4 | D2 SSE 只做通知 | 前端按需拉数据 | ✅ |
B11: 专题 11 上下文四层重设计
设计文档:11-context-layers-redesign.md
| # | 设计要求 | 代码现状 | 判定 |
|---|---|---|---|
| B11-1 | §2.3 L2 操作规范型 6 个 Skill 全文注入 | BootstrapBuilder 有 ROLE_SKILL_MAP + _read_skill 全文注入 ✅;task_handler.py RoleSkillSection 只给索引+引导语 ⚠️ |
🟡 双路径并存,策略矛盾 |
| B11-2 | §2.3 handoff.schema.json |
不存在 | ⚠️ Phase 3 |
| B11-3 | §2.3 review_protocols/ 目录 |
不存在,但 review-quality Skill 文件存在 |
⚠️ 设计文档 §三归属表已改归类为 L3 Skill |
| B11-4 | §6 Phase 3 Step 6-8 BootstrapBuilder 改造 | 已完成(ROLE_SKILL_MAP + _read_skill) | ✅ |
| B11-5 | §2.3 token 预算 ~600 tokens | bootstrap.py 有 warn 但不截断 | 🟡 有告警无硬限制 |
B11 关键发现:新旧路径的 Skill 注入策略矛盾——
- 旧路径(BootstrapBuilder):全文注入 Skill(
_read_skill读文件全文) - 新路径(RoleSkillSection):只给索引("请用 read 工具读取 SKILL.md")
- 设计文档 §2.3 要求 "A 类 Skill 全文注入"
- handler 注册后会从旧路径切换到新路径,导致 Skill 从全文注入降级为索引提示
这是一个 隐性回归:注册 handler 后 Agent 获取的操作规范信息量大幅减少。
B12: 专题 12 Pipeline 设计
设计文档:12-pipeline-design.md
| # | 设计要求 | 代码现状 | 判定 |
|---|---|---|---|
| B12-1 | §3 Pipeline 注册表 | 不存在 | ⚠️ 设计 §9 标注 Phase 2 |
| B12-2 | §4 路由逻辑 task_type | router.py 无 task_type 路由 | ⚪ |
| B12-3 | §8 PipelineRegistry | 不存在 | ⚪ |
| B12-4 | §10.1 task_type 默认值改 None | blackboard_routes.py 已为 None(v3.0 已修) |
⚪ 已实施 |
| B12-5 | §10.2 广播计数器修正 | _broadcast_tracker + BroadcastRound 已实现 |
⚪ 已实施 |
B12 结论:Pipeline 主体未实施符合设计路线图(Phase 2),但 §10 Phase 1 的两个 bug fix 明确标注为"立做"却未执行。
B13: 专题 13 工具链与开发工作流
设计文档:13-toolchain-and-dev-workflow.md
| # | 设计要求 | 代码现状 | 判定 |
|---|---|---|---|
| B13-1 | §16 工具链事件中枢 | toolchain_handler.py + toolchain_templates.py |
✅ |
| B13-2 | Gitea webhook 处理 | 5 模板 + 去重 | ✅ |
| B13-3 | CI 前缀 [CI] |
✅ | ✅ |
Part B 汇总
| 判定 | 数量 | 主要项目 |
|---|---|---|
| ✅ 一致 | 21 | B1-1, B1-2, B2-1/2, B3-1, B4-2/5, B5-1/2/3, B6-1 |
| 🟡 部分一致 | 3 | B11-1 双路径策略矛盾, B11-5 token 预算无硬限制 |
| ⚠️ 设计标注未实施 | 10 | B1-3(handler 不可达), B2-3/4, B3-2/3, B4-1/3/4, B5-4, B9-3, B11-2/3 |
| ❌ 设计承诺未交付 | 0 | — |
Step 5 审计报告偏差项验证
step5-audit-report.md 列出 6 项偏差(D1-D6)。逐项验证 HEAD 代码:
| # | 审计描述 | HEAD 实际状态 | 判定 |
|---|---|---|---|
| D1 | pre_spawn 返回值未检查 | 已修复:if not _handler.pre_spawn(...): raise RuntimeError("handler_pre_spawn_failed") |
✅ 已修 |
| D2 | PromptContext 缺少 from_agent/mail_type | 已修复:spawner L289-296 从 must_haves JSON 提取 | ✅ 已修 |
| D3 | inform outcome 白名单缺失 | 未修复。但影响极小——CRASH_OUTCOMES 由基类处理,剩余异常 outcome 罕见 | 🟢 可接受 |
| D4 | retry prompt 仍用 is_mail 硬编码 |
未修复:spawner L1128 仍硬编码 is_mail = project_id == "_mail" |
🟡 遗留 |
| D5 | _check_reply 语义差异 | 已修复:MailHandler._check_reply 用 SELECT id FROM tasks WHERE id != ? AND must_haves LIKE ?,与 v3.0 一致 |
✅ 已修 |
| D6 | 标 done 重试机制 | 已修复:BaseTaskHandler._mark_task_status 有 3 次重试 |
✅ 已修 |
结论:D1/D2/D5/D6 已在后续 commit 修复,D3 可接受,D4 是遗留项。
与庞统 Review 的背靠背比对
| 维度 | 司马懿 | 庞统 | 差异分析 |
|---|---|---|---|
| 致命问题 | A1 review verdict 丢失 + A2 handler 未注册 | 仅 #1 review verdict 丢失 | 关键差异:庞统未将 handler 未注册列为致命问题。庞统认为 _legacy_on_complete 仍可运行所以只关注 review 路径。但我认为 ticker 不再扫描 _mail 是 v3.0 有、HEAD 丢失的行为,这比 review 路径更严重——Mail 系统完全停止工作 |
| _mail tick 丢失 | 明确指出 A2 导致 ticker 不扫描 _mail |
未提及 | 庞统漏检了 virtual_projects() 返回空时 _mail 不被 tick 的后果 |
| Skill 注入降级 | B11-1 发现新旧路径策略矛盾 | 未提及 | 庞统未分析 handler 注册后 Skill 注入策略的变化 |
| D1/D2/D5 修复状态 | 验证了审计报告的修复项 | 未验证 | 庞统的 review 早于修复 commit |
| 专题覆盖范围 | 13 个全覆盖 | 13 个全覆盖 | 一致 |
| D4 retry 硬编码 | A8 独立发现 | B4 独立发现 | 独立确认 |
建议优先级
| 优先级 | 项目 | 说明 |
|---|---|---|
| P0 | A2 handler 注册初始化 | 添加 TaskTypeRegistry.register() 启动代码。这是所有 handler 路径的前提 |
| P0 | A1 review verdict 处理 | P0 修复后自然恢复(TaskHandler.handle_review_complete 生效)。但如果不想立即注册 handler,需先在 _legacy_on_complete 中恢复 review verdict 逻辑作为 interim fix |
| P1 | B11-1 Skill 注入策略对齐 | 注册 handler 后 RoleSkillSection 只给索引——需确认这是有意降级还是遗漏。如果是遗漏,RoleSkillSection 应全文注入 |
| P1 | A8 retry prompt handler 化 | spawner L1128 is_mail 硬编码改走 handler |
| P2 | A3 死代码清理 | _mail_auto_working / _mail_auto_complete 等方法在 handler 注册后确认不再需要再删除 |
| P3 | ticker.py L1483 缩进对齐 | 风格问题 |
Cross-Check:庞统 Review 逐项验证
对庞统 review-v3-vs-head-pangtong.md 的每个发现,独立验证:是不是真问题?根因对不对?修复方案对不对?
Part A 逐项验证
庞统 #1 🔴 review verdict 丢失
庞统判定:致命。"仅影响非 handler 项目(_general)"。
我的验证:
-
是致命问题 ✅ ——
_legacy_on_complete在_is_review=True时确实什么都不做。 -
"仅影响非 handler 项目" — 表述不准确。实际情况更复杂:
_general项目确实受影响(走_legacy_on_complete)- 但
_mail/_toolchain不受影响,不是因为"handler 正确处理",而是因为它们根本不走 review 流程(ticker 中_dispatch_reviews对 handler 项目return []) - 庞统说"handler 项目(_mail/_toolchain)的 review 由 TaskHandler.post_complete 正确处理"——这个说法有误导性。TaskHandler 不是
_mail/_toolchain的 handler,它们各自的 handler(MailHandler/ToolchainHandler)没有handle_review_complete方法。它们不走 review 是因为设计上就不走。
-
庞统的修复方案有隐藏缺陷。庞统说"让非 handler 项目也走 TaskHandler(注册
_general到 TaskTypeRegistry)"。但TaskTypeRegistry.get_by_project()匹配的是handler.virtual_project,而 TaskHandler 的virtual_project = None。所以:get_by_project("_general")→ 遍历所有 handler,检查h.virtual_project == "_general"→ TaskHandler 的virtual_project是None→ 不匹配 → 返回None- 即使注册了 TaskHandler,
_general项目仍然走_legacy_on_complete - 庞统的修复方案需要额外改 TaskHandler.virtual_project 或 registry 匹配逻辑,但他没指出这一点
结论:问题是真的,严重度判定正确。但影响范围描述和修复方案都不完整。
庞统 #2 🟢 旧 _mail_* 方法保留
庞统判定:正常重构,方法体保留标记为 deprecated。
我的验证:
- 方法体确实保留 ✅(dispatcher.py L628-860)
- 但"标记为 deprecated"不对——代码中没有
@deprecated装饰器或注释。这些方法就是安静地躺在那里,没有任何标记告诉维护者"别用了" - **我标 🟡 中等而非 🟢**的原因:无 deprecated 标记 + 主流程不再调用 = 未来维护者容易误用
结论:问题不大,但庞统多给了信息("标记为 deprecated")——代码中实际没有标记。
庞统 #3 🟢 spawn 失败回退
庞统判定:逻辑改进。
我的验证:✅ 确认等价,新版更通用。
庞统 #4-5 🟢 spawner prompt/api_section
庞统判定:等价实现。
我的验证:✅ 确认等价。
庞统 #6 🟢 ticker _mail → virtual_projects()
庞统判定:正常重构,可扩展。
我的验证:这是庞统最大的漏检。
庞统只看了代码方向(硬编码 → 注册表),没有检查注册表是否为空。
实际运行时 TaskTypeRegistry.virtual_projects() 返回空列表 → _mail 不被 ticker 扫描。这是一个 v3.0 有、HEAD 丢失的行为——v3.0 中 _mail 硬编码在 ticker L218-229,HEAD 中完全消失。
后果:所有 Mail 任务的 pending → claimed → working 流程中断,整个飞鸽传书系统停止工作。
这不是"正常重构",是致命回归。
庞统 #7-8 🟢 ticker check_reply / dispatch_reviews
庞统判定:等价实现。
我的验证:✅ 确认等价。但 #7 说"缩进正确"——实际 ticker.py L1483 有缩进不一致(28 空格 vs 同级 24 空格),不影响运行但增加维护混淆。
Part B 逐专题验证
专题 01-03:无分歧
庞统的检查和我的结论一致。设计原则未强制实施属于正常。
专题 04:庞统更严格
庞统把 B4-3(co_assignees)和 B4-4(output↔comment)标 ❌,我标 ⚪(Phase 2/3)。
庞统的判定更严格——"设计了但没实现就是不一致" vs 我的"设计自身标注了 Phase,未实施是预期的"。两种视角都有道理,不算错误。
专题 05:判定标准差异
庞统把 B5-4(L3 wiki 知识注入)标 ❌。我标 ⚪(Phase 2)。
同专题 04,判定标准差异。
专题 06:庞统更细致
庞统多了 B6-5"设计提到 7 个恢复方法只看到 2 个公开方法"——这是一个合理的疑问,我没有提出。
专题 07-10:无分歧
专题 11:庞统全标 ❌ 是错的
庞统 B11-1 说"BootstrapBuilder 只注入通用 prompt,无 skill 全文注入"。
我验证了代码:
# bootstrap.py L29
ROLE_SKILL_MAP = {
"executor": "blackboard-executor",
"reviewer": "blackboard-reviewer",
...
}
# bootstrap.py L68-72
skill_name = self.ROLE_SKILL_MAP.get(role)
if skill_name:
skill_content = self._read_skill(skill_name) # 读全文
if skill_content:
sections.append(skill_content)
BootstrapBuilder 有 Skill 全文注入。庞统说"无 skill 全文注入"与代码不符。他可能只看了 task_handler.py 的 RoleSkillSection(确实只给索引),没有看 bootstrap.py 的旧路径。
实际情况:双路径并存。旧路径(BootstrapBuilder)全文注入,新路径(RoleSkillSection)只给索引。handler 注册后从旧路径切换到新路径,Skill 信息量降级。这才是真正的问题。
专题 12:我之前的 B12-4/5 判定有误
我在 Part B 中说"B12-4 task_type 默认值仍为 \"coding\""和"B12-5 广播计数器 retry_count 不递增"是 Phase 1 承诺未交付。
cross-check 时我重新验证了代码:
-
B12-4:
blackboard_routes.pyL138 已是body.get("task_type", None),默认值已经是 None。v3.0 tag 中也是 None。设计文档 §10.1 的 bug fix 可能在 v3.0 之前就修了,或者设计文档基于旧版本写的。不是问题,我之前的判定有误。 -
B12-5:
ticker.py中_broadcast_tracker+BroadcastRound+round_number >= 3升级庞统的机制已实现。mark_mention_retry有retry_count = retry_count + 1。设计 §10.2 描述的问题已在 v3.0 或更早修复。不是问题,我之前的判定有误。
庞统对专题 12 的判定("设计文档 §9 自身标记为待实现")比我准确。
修正我的报告:Part B 中 B12-4 和 B12-5 应从 ❌ 改为 ⚪(设计自标 Phase 2,主体未实施是预期的)。
专题 13:无分歧
庞统未引用 Step 5 审计报告
庞统的 review 完全没引用 step5-audit-report.md(v3.0..HEAD diff 中新增的文件)。这意味着 D1/D2/D5 的修复状态未经庞统验证。我逐项验证了 D1/D2/D5 已修复,D4 未修复(retry 硬编码),D3 可接受,D6 已修复。
庞统漏检的额外行为回归
handler 未注册还导致一个庞统完全没提到的问题:
guardrail 回归。v3.0 中 dispatcher L127-128:
is_mail = project_config.get("project_id") == "_mail" if project_config else False
if self.guardrails and not is_mail:
HEAD dispatcher L128-131:
handler = TaskTypeRegistry.get_by_project(project_config.get("project_id", "") ...)
is_handler_task = handler is not None
if self.guardrails and not is_handler_task:
handler 未注册 → is_handler_task = False → _mail 项目也要过 guardrail 检查了。v3.0 中 _mail 是跳过 guardrail 的。这可能导致某些 Mail 任务被 guardrail 拦截。
Cross-Check 总结
| 维度 | 庞统 review 质量 |
|---|---|
| 致命问题发现 | 发现 A1 ✅,漏检 A2(handler 注册 + ticker 不可达 + guardrail 回归)❌ |
| 根因分析 | A1 根因正确。修复方案不完整(没指出 TaskHandler.virtual_project=None 导致注册也匹配不到 _general) |
| Part B 专题覆盖 | 13/13 全覆盖 ✅ |
| Part B 事实准确性 | B11 "无 skill 全文注入"与代码不符 ❌。B12 比我准确 ✅ |
| Part B 多给信息 | #2 说"标记为 deprecated"但代码无标记 ⚠️ |
| Part B 更严格处 | B04-3/4 标 ❌(合理),B06-5 恢复方法数量疑问(合理) |
| 审计报告验证 | 未引用,未验证 D1-D6 修复状态 |
| 遗漏的行为回归 | guardrail 对 _mail 的回归 |
我的自我修正:B12-4/5 判定有误,应改为 ⚪。v3.0 tag 时这两个问题已修复,设计文档描述的是更早期的问题。
— 司马懿 仲达,质量总监 🗡️