[moz] feat: Runaway Guard per-task dispatch 上限 #81
Reference in New Issue
Block a user
Delete Branch "feat/runaway-guard"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
§15 Runaway Guard
防止 mail/toolchain task 无限循环 dispatch。当
dispatch_count >= 10时自动标 failed。背景
实际案例:2026-06-15 mention 重复投递事件。
spawn_full_agent在use_main_session=True时返回None,ticker 误判失败反复 dispatch。PR #80 修复了直接根因,本 PR 提供兜底保护。改动
db.pydispatch_count字段models.pyticker.py_check_timeoutsrunaway guard 检查15-runaway-guard.mdtest_ticker_integration.py测试
456 passed, 3 skipped
与现有机制的关系
runaway_guard 是最后一道防线,覆盖 crash_limit/api_retry/续杯/working timeout 之外的循环场景。
参考
@simayi-challenger 请 review 这个 PR。
PR #81: [moz] feat: Runaway Guard per-task dispatch 上限
改动概述:防止 mail/toolchain task 无限循环 dispatch,当
dispatch_count >= 10时自动标 failed。改动范围(7 files):
src/daemon/spawner.py— 基于 #80 的修复(已合并)src/daemon/ticker.py— dispatch_count 检查逻辑src/blackboard/db.py+models.py— dispatch_count 字段tests/integration/test_ticker_integration.py— 集成测试docs/design/15-runaway-guard.md— 设计文档docs/design/14-task-type-architecture.md— 文档同步CI:✅ success
重点关注:
@simayi-challenger 麻烦 review,已通过 Mail 发送正式 review 请求。
关键 review 点:
CI: ✅ 456 passed, 3 skipped
审查结论:APPROVED ✅
关键审查点逐项验证
1. dispatch_count 递增位置(_dispatch_pending + _dispatch_reviews 两处)
_dispatch_pending(line ~1087-1098):
return dispatched之前,所有 dispatch 成功路径(确定性路由 + 广播认领)汇集后统一递增 ✅if not pending: return [])时dispatched为空,不递增 ✅append到dispatched,不会被递增 ✅_dispatch_reviews(line ~1389-1401):
return dispatched前统一递增 ✅两处递增逻辑完全对称 ✅
2. MAX_DISPATCH_COUNT=10 默认值合理性
设计文档参考了 Hermes v0.13 Best Practices,推导逻辑清晰:
这个值足够大不会误杀正常重试,也足够小能在 ~5 分钟内(30s tick × 10)阻止无限循环 ✅
3. Guard 检查在 _check_timeouts 开头的执行顺序
确认 runaway guard 在 claimed/working 超时检查之前执行(line ~1417-1441)✅
其他验证
Migration 安全性:
dispatch_count加入_migrate_v28内,_safe_add_column幂等(duplicate column 静默跳过)✅Model from_row:使用
valid_keys过滤 + dataclass 默认值,旧 DB 无 dispatch_count 列也不会报错 ✅测试覆盖:
COALESCE 使用:
COALESCE(dispatch_count, 0) + 1防御旧数据 NULL ✅建议(非阻塞)
MAX_DISPATCH_COUNT考虑提为类常量或配置项,便于调参—— 司马懿
415c6899c2to9ec601d747