文件: internal/swarm/idle_runner.go L300-311(quota/auth 永久失败)、L326-335(max_retries 停车)、L611-621(allBlockersComplete)
问题描述
两条停车路径把任务标为 StatusCompleted(仅 metadata 记 permanent_error),但依赖门禁 allBlockersComplete 只检查 Status == StatusCompleted 不读 metadata。"completed" 被赋予双重含义(真完成 vs 停车),依赖门禁只认字面 status。
触发场景
Task A 执行时 quota 耗尽 → 标 completed(permanent_error=quota) → 下一 tick Task B(BlockedBy=[A])通过 allBlockersComplete 被认领执行。B 的任务前提(A 的产出已存在)实际不成立,在错误前提上烧整轮 LLM 调用,产出幻觉结果还标 completed,继续向 C 级联污染。
预期 vs 实际
预期:永久失败的上游应阻断(或显式失败解锁)下游;实际:下游被静默解锁执行。
注:停车设计本身是 #1295 防重试风暴,测试 zz_issue2579_test.go 只覆盖任务自身停车,未覆盖下游解锁。
修复建议
allBlockersComplete 增查 blocker metadata 的 permanent_error 标记(或将停车状态改为独立 Status,如 StatusParked)。
严重程度: high(静默级联执行,LLM 成本浪费 + 产出污染)
来源: cron-1 深度审查,初审 sa-133 + 独立复核 sa-134 双重确认
文件: internal/swarm/idle_runner.go L300-311(quota/auth 永久失败)、L326-335(max_retries 停车)、L611-621(allBlockersComplete)
问题描述
两条停车路径把任务标为
StatusCompleted(仅 metadata 记permanent_error),但依赖门禁allBlockersComplete只检查Status == StatusCompleted不读 metadata。"completed" 被赋予双重含义(真完成 vs 停车),依赖门禁只认字面 status。触发场景
Task A 执行时 quota 耗尽 → 标 completed(permanent_error=quota) → 下一 tick Task B(BlockedBy=[A])通过 allBlockersComplete 被认领执行。B 的任务前提(A 的产出已存在)实际不成立,在错误前提上烧整轮 LLM 调用,产出幻觉结果还标 completed,继续向 C 级联污染。
预期 vs 实际
预期:永久失败的上游应阻断(或显式失败解锁)下游;实际:下游被静默解锁执行。
注:停车设计本身是 #1295 防重试风暴,测试 zz_issue2579_test.go 只覆盖任务自身停车,未覆盖下游解锁。
修复建议
allBlockersComplete 增查 blocker metadata 的 permanent_error 标记(或将停车状态改为独立 Status,如 StatusParked)。
严重程度: high(静默级联执行,LLM 成本浪费 + 产出污染)
来源: cron-1 深度审查,初审 sa-133 + 独立复核 sa-134 双重确认