Files
hermes-skills/skills/legal/contract-pass-workflow/references/queue-runner-duplicate-review-20260702.md
T

4.1 KiB

Queue-Runner + Watchdog 重复审查Bug(2026-07-02 实证)

现象

Doro反复收到同一份合同的"审查完毕"通知。今天受影响的合同:

  • 夏阳合同:被通知至少2次(可能3次)
  • 家庭医生签约合同:被通知2次(第3次在final_review被手动杀掉)

Bug链条(已验证)

1. Queue-runner 启动 → START 合同X → 创建 worker PID → "Waiting for worker..."
2. Runner 进程异常退出(OOM/信号/shell被杀),但 worker 子进程继续运行
3. Worker 独立完成整个 workflow(包括 final_review = 私信通知 Doro)
4. Runner 已死 → 没有执行 "mv $FILE done/" → 文件仍在 queue 目录
5. Watchdog(20min cron)检测到:runner不在 + done/ < manifest → 重启 runner
6. 新 runner 看到文件还在 queue → SKIP逻辑只检查 `[ -e "$FILE" ]` → 认为未处理
7. 新 runner 启动第二个 thread → 从头审查 → final_review 又通知 Doro

额外失败模式(watchdog恢复旧thread)

watchdog.log:
[2026-07-02 18:40:14] suspended 06FJ42KNSK1ZA767W8AZ6MXJ6C → exec 恢复
[2026-07-02 18:40:15] idle 06FJ3ZJXR5WHJNPDHF22YYMGCM → exec 续跑

Watchdog 恢复处于 idle/suspended 状态的旧 thread,这些 thread 的同名合同可能已被新 runner 完成。 旧 thread 被唤醒后接着跑完 final_review → 又一次通知。

今天的实际时间线

时间(BJT) 事件
02:45 第一个runner启动,处理恭兴(cancelled)
05:00 Watchdog重启runner → 恭兴(成功)
06:25 肃言完成
07:28 卫健委完成
08:42 夏阳 thread#1 启动 (06FJ3ZJXR)
~11:00 夏阳#1 完成(含final_review通知);但runner已死,文件没进done/
11:40 Watchdog重启runner → 夏阳 thread#2 启动 (06FJ58AD)
12:38 夏阳#2 完成(第二次通知);家庭医生签约 thread 启动 (06FJ5NPHT)
18:40 Watchdog恢复旧idle thread 06FJ3ZJXR5WHJNPDHF22YYMGCM (夏阳#1) + 06FJ42KNSK (家庭医生#?)
19:21 家庭医生签约交付到Nextcloud
~21:00 06FJ42KNSK 完成 final_review(第2次家庭医生通知)
21:16 Watchdog再次重启runner → 家庭医生 thread#2 (06FJ5NPHT) 进入 final_review
21:30 手动 kill -9 杀掉 06FJ5NPHT 的 final_review → 阻止第3次通知

止血SOP

当 Doro 报告收到重复通知时:

  1. 找重复进程ps aux | grep -E "(background-worker|uwf-hermes)" | grep -v grep
  2. 杀掉重复进程kill -9 <worker-PID> <uwf-hermes-PID>
  3. 杀掉queue-runnerkill <queue-runner-PID>
  4. 清理queue:把所有tracker中已completed的文件移入done/
    import json, os, shutil
    tracker = json.load(open(os.path.expanduser('~/.hermes/data/contract-tracker.json')))
    completed = {c['original_filename'] for c in tracker['contracts'] if c['status'] == 'completed'}
    queue_dir = '/tmp/contract-queue'
    for f in os.listdir(queue_dir):
        if f.endswith(('.doc', '.docx', '.pdf')) and f in completed:
            shutil.move(f'{queue_dir}/{f}', f'{queue_dir}/done/{f}')
    
  5. 验证find /tmp/contract-queue/ -maxdepth 1 -name '*.doc*' 应为空
  6. 确认watchdog不会重启:done/ 文件数 ≥ manifest.txt 行数

缺失防线(待修复)

位置 应加的检查
queue-runner START 逻辑 启动workflow前查tracker:已completed直接mv到done/
watchdog 恢复thread逻辑 resume前查:同filename的tracker记录是否already completed
final_review 发通知前查:是否24h内已有同合同的通知(防御性去重)

关键文件路径

  • Queue runner: /home/maggie/.hermes/skills/devops/uwf/scripts/contract-queue-runner.sh
  • Watchdog: ~/.hermes/scripts/contract-queue-watchdog.sh
  • Queue dir: /tmp/contract-queue/ (manifest.txt + done/)
  • Queue log: /tmp/contract-queue/queue.log
  • Watchdog log: /tmp/contract-queue/watchdog.log
  • Tracker: ~/.hermes/data/contract-tracker.json
  • Cron jobs: contract-queue-watchdog (*/20, job_id:3174518affda), auto-notify-watchdog (5min, job_id:63bb31d4f050)