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

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 计数
This commit is contained in:
cfdaily
2026-06-15 09:48:09 +08:00
parent 1f373d5cb5
commit d6cb854f68
2 changed files with 58 additions and 7 deletions
+22 -4
View File
@@ -625,19 +625,24 @@ curl -X POST http://{self.api_host}:{self.api_port}/api/projects/{project_id}/ta
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
self._register_session(session_id, agent_id, task_id, proc.pid,
# use_main_session=True 时 session_id 为 None,但 _register_session 和
# _monitor_process 需要一个非 None 的 key;同时 ticker 等调用方用
# `result is not None` 判断 spawn 是否成功,返回 None 会被误判为失败。
# 统一用 "main" 作为占位标识。
effective_sid = session_id or "main"
self._register_session(effective_sid, agent_id, task_id, proc.pid,
broadcast_task_ids=broadcast_task_ids)
logger.info("Spawned agent %s (session=%s, pid=%d)",
agent_id, session_id, proc.pid)
agent_id, effective_sid, proc.pid)
# Schedule monitor(传 wrapped_on_complete)
asyncio.create_task(
self._monitor_process(session_id, proc, agent_id, task_id,
self._monitor_process(effective_sid, proc, agent_id, task_id,
on_complete=_wrapped_on_complete,
db_path=task_db_path or self.db_path)
)
return session_id
return effective_sid
except Exception as e:
# spawn 失败也要 release counter
@@ -1949,6 +1954,19 @@ curl -X POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_
try:
from src.daemon.mail_notify import _is_mail_project, notify_mail_failed
if _is_mail_project(db_path):
# 防御性检查:如果 task 已经 done,不触发失败通知(竞态保护)
# 场景:spawner 标 failed 和 handler 标 done 同时发生
try:
conn2 = get_connection(db_path)
current_status = conn2.execute(
"SELECT status FROM tasks WHERE id=?", (task_id,)
).fetchone()
conn2.close()
if current_status and current_status["status"] == "done":
logger.info("Task %s already done, skipping mail failure notification", task_id)
return
except Exception:
pass
# Mail 失败:通知发件人,不 @pangtong
notify_mail_failed(db_path, task_id, reason, detail)
else: