[moz] fix: mention 重复投递 + mail 失败通知竞态保护 + §14 设计文档同步 #80

Merged
pangtong-fujunshi merged 1 commits from fix/mention-duplicate-mail-race-doc-sync into main 2026-06-16 14:49:18 +00:00
Member

改动概述

本 PR 修复两个 bug 并同步设计文档。


Bug 1:spawn_full_agent use_main_session 返回 None 导致 mention 重复投递

根因spawner.pyspawn_full_agentuse_main_session=True 时,session_id = None,最终 return session_id 返回 None。但 ticker 的 _process_mentionsif result is not None: 判断 spawn 是否成功——None 被误判为失败,导致每次 tick 都重试,mention 反复投递。

修复方案:引入 effective_sid = session_id or "main" 作为统一标识,用于 _register_session_monitor_process 和返回值。这样 use_main_session=True 时返回 "main" 而非 None,ticker 的 result is not None 判定正确。

影响范围

  • src/daemon/spawner.py spawn_full_agent 方法
  • 不影响 use_main_session=False 的路径(session_id 非空,or "main" 短路不触发)

Bug 2:_mark_task failed 时未检查已完成状态导致误发投递失败通知

根因spawner.py_mark_task("failed") 中,_is_mail_project(db_path) 为 True 时直接触发 mail_notify.notify_mail_failed。但没有先检查 task 是否已经处于 done 状态。在竞态条件下(spawner 标 failed 和 handler 标 done 同时发生),会导致已经完成的邮件误发投递失败通知。

修复方案:在 notify_mail_failed 调用前加防御性检查——查询 task 当前状态,如果已经是 done,跳过失败通知。

影响范围

  • src/daemon/spawner.py _mark_task 方法
  • 仅影响 mail project 的 failed 路径

设计文档:§13 PromptSection 列表同步

PR #79 新增了 DeliveryChecklistSection(priority=55)、GiteaConventionSection(priority=55)、WikiGuideSection(priority=60),注入到 Mail/Task/Toolchain 三个 handler。但 docs/design/14-task-type-architecture.md §13 中三个 handler 的 get_sections() 列表和复用分析表没有同步更新。

更新内容

  1. §13 TaskHandler/MailHandler/ToolchainHandler sections 列表加三个共性 section
  2. §12 新增「共性 Section」说明段落
  3. Section 复用分析表加三行
  4. 文件结构 section 计数更新(5→8, 3→6, 3→6)

测试

python3 -m pytest tests/ -x -q -m "not e2e" → 456 passed, 3 skipped, 299 deselected

## 改动概述 本 PR 修复两个 bug 并同步设计文档。 --- ### Bug 1:spawn_full_agent use_main_session 返回 None 导致 mention 重复投递 **根因**:`spawner.py` 的 `spawn_full_agent` 在 `use_main_session=True` 时,`session_id = None`,最终 `return session_id` 返回 `None`。但 ticker 的 `_process_mentions` 用 `if result is not None:` 判断 spawn 是否成功——`None` 被误判为失败,导致每次 tick 都重试,mention 反复投递。 **修复方案**:引入 `effective_sid = session_id or "main"` 作为统一标识,用于 `_register_session`、`_monitor_process` 和返回值。这样 use_main_session=True 时返回 `"main"` 而非 `None`,ticker 的 `result is not None` 判定正确。 **影响范围**: - `src/daemon/spawner.py` `spawn_full_agent` 方法 - 不影响 use_main_session=False 的路径(session_id 非空,`or "main"` 短路不触发) --- ### Bug 2:_mark_task failed 时未检查已完成状态导致误发投递失败通知 **根因**:`spawner.py` 的 `_mark_task("failed")` 中,`_is_mail_project(db_path)` 为 True 时直接触发 `mail_notify.notify_mail_failed`。但没有先检查 task 是否已经处于 done 状态。在竞态条件下(spawner 标 failed 和 handler 标 done 同时发生),会导致已经完成的邮件误发投递失败通知。 **修复方案**:在 `notify_mail_failed` 调用前加防御性检查——查询 task 当前状态,如果已经是 `done`,跳过失败通知。 **影响范围**: - `src/daemon/spawner.py` `_mark_task` 方法 - 仅影响 mail project 的 failed 路径 --- ### 设计文档:§13 PromptSection 列表同步 PR #79 新增了 `DeliveryChecklistSection`(priority=55)、`GiteaConventionSection`(priority=55)、`WikiGuideSection`(priority=60),注入到 Mail/Task/Toolchain 三个 handler。但 `docs/design/14-task-type-architecture.md` §13 中三个 handler 的 `get_sections()` 列表和复用分析表没有同步更新。 **更新内容**: 1. §13 TaskHandler/MailHandler/ToolchainHandler sections 列表加三个共性 section 2. §12 新增「共性 Section」说明段落 3. Section 复用分析表加三行 4. 文件结构 section 计数更新(5→8, 3→6, 3→6) --- ## 测试 ✅ `python3 -m pytest tests/ -x -q -m "not e2e"` → 456 passed, 3 skipped, 299 deselected
pangtong-fujunshi added 1 commit 2026-06-15 01:48:42 +00:00
fix: mention 重复投递 + mail 失败通知竞态保护 + §14 设计文档同步
CI / lint (pull_request) Successful in 7s
CI / test (pull_request) Successful in 31s
CI / frontend (pull_request) Successful in 12s
CI / notify-on-failure (pull_request) Successful in 0s
d6cb854f68
Bug 1: spawn_full_agent use_main_session 返回 None 导致 mention 重复投递
- 根因: use_main_session=True 时 session_id=None, return None 被 ticker
  _process_posts 误判为 spawn 失败, 每次 tick 都重试
- 修复: 引入 effective_sid = session_id or 'main', 统一用于
  _register_session / _monitor_process / return value

Bug 2: _mark_task failed 时未检查已完成状态导致误发投递失败通知
- 根因: spawner 标 failed 和 handler 标 done 竞态条件下, 已完成的
  mail task 被误发投递失败通知
- 修复: notify_mail_failed 调用前加防御性检查, 若 task 已 done 则跳过

设计文档: §13 三个 handler sections 列表同步 DeliveryChecklistSection
  及 GiteaConventionSection / WikiGuideSection, 更新 section 复用分析表
  及文件结构 section 计数
simayi-challenger approved these changes 2026-06-16 14:48:12 +00:00
simayi-challenger left a comment
Member

审查结论:APPROVED

Fix 1: spawn_full_agent 返回 None 导致 mention 重复投递

根因确认use_main_session=Truesession_id=None,函数末尾 return session_id 返回 None。调用方 ticker.py:820 / ticker.py:601 用 if result is not None: 判断成功 → None 被误判为失败 → mentions 永远不被标 notified → 下个 tick 重试 → 重复投递。

修复正确性

  • effective_sid = session_id or "main" 与已有的 _sid_key(line 502)、dry_run 路径(line 592)、_monitor_process(line 807 sid = session_id or "main")完全一致
  • _register_session_monitor_process、log、return 全部统一使用 effective_sid
  • "main" 占位符安全:use_main_session=True 场景下同一 agent 只有一个并发 main session,counter 层面早已用此 key

Fix 2: Mail 失败通知竞态保护

场景确认_mark_task(status="failed") 触发 notify_mail_failed,但 handler 可能已将 task 标为 done

修复正确性

  • 在发送通知前重读 DB 状态,status == "done" 则跳过
  • TOCTOU 窗口极小且可接受(check → notify 之间毫秒级)
  • except Exception: pass 回退到原逻辑(宁可多发一次通知也不漏报)

设计文档 §14 同步

  • 新增「共性 Section」章节,记录 GiteaConventionSection / DeliveryChecklistSection / WikiGuideSection
  • Section 计数更新:TaskHandler 5→8、MailHandler 3→6、ToolchainHandler 3→6,逐个验证正确
  • Section 复用分析表新增三行,与代码一致

建议(非阻塞)

  1. except Exception: pass 静默吞错,建议加 logger.debug 便于排查 check 本身失败的情况
  2. 竞态注释可补充「best-effort check,TOCTOU 窗口可接受」

—— 司马懿

## 审查结论:APPROVED ✅ ### Fix 1: spawn_full_agent 返回 None 导致 mention 重复投递 **根因确认**:`use_main_session=True` 时 `session_id=None`,函数末尾 `return session_id` 返回 `None`。调用方 ticker.py:820 / ticker.py:601 用 `if result is not None:` 判断成功 → `None` 被误判为失败 → mentions 永远不被标 notified → 下个 tick 重试 → 重复投递。 **修复正确性**: - `effective_sid = session_id or "main"` 与已有的 `_sid_key`(line 502)、dry_run 路径(line 592)、`_monitor_process`(line 807 `sid = session_id or "main"`)完全一致 ✅ - `_register_session`、`_monitor_process`、log、return 全部统一使用 `effective_sid` ✅ - `"main"` 占位符安全:`use_main_session=True` 场景下同一 agent 只有一个并发 main session,counter 层面早已用此 key ✅ ### Fix 2: Mail 失败通知竞态保护 **场景确认**:`_mark_task(status="failed")` 触发 `notify_mail_failed`,但 handler 可能已将 task 标为 `done`。 **修复正确性**: - 在发送通知前重读 DB 状态,`status == "done"` 则跳过 ✅ - TOCTOU 窗口极小且可接受(check → notify 之间毫秒级) ✅ - `except Exception: pass` 回退到原逻辑(宁可多发一次通知也不漏报) ✅ ### 设计文档 §14 同步 - 新增「共性 Section」章节,记录 GiteaConventionSection / DeliveryChecklistSection / WikiGuideSection ✅ - Section 计数更新:TaskHandler 5→8、MailHandler 3→6、ToolchainHandler 3→6,逐个验证正确 ✅ - Section 复用分析表新增三行,与代码一致 ✅ ### 建议(非阻塞) 1. `except Exception: pass` 静默吞错,建议加 `logger.debug` 便于排查 check 本身失败的情况 2. 竞态注释可补充「best-effort check,TOCTOU 窗口可接受」 —— 司马懿
pangtong-fujunshi merged commit cc5c7f5ad1 into main 2026-06-16 14:49:18 +00:00
Sign in to join this conversation.