--- belongs_to: - "[[INDEX · 铸渊·协作指令|GitHub ↔ Notion 桥接协议]]" --- # 👁️ 铸渊指令|合并指令看守者 · Merge Watchdog — 行动类PR三次修复失败邮件告警 + 成功静默仪表盘更新(ZY-MERGE-WATCHDOG-2026-0324-001) --- --- ## 一、指令背景 天眼夜间自动修复引擎(v3.0)实现了 **扫描 → 分析 → 修复 → PR** 的闭环。但有一个盲区: **PR 被合并之后,修复到底生效了没有?** 当前流程:天眼创建 PR → 冰朔晨间审阅合并 → 然后呢?**没人看。** 如果合并后的修复实际上没解决问题(比如错误根因判断偏了、修复代码只修了表象),这个 bug 就会在下一个夜间扫描中再次出现。天眼会再修一遍,再创建 PR,再合并……无限循环。 **本次升级目标:** 新增「合并指令看守者」(Merge Watchdog)—— 一个专门盯着已合并 PR 的守卫进程。它只看"行动"类(已合并的修复 PR),跟踪修复是否真正生效。三次修复仍然失败 → 邮件通知冰朔手动介入;修复成功 → 静默更新仪表盘,不打扰任何人。 --- ## 二、系统架构 ```mermaid flowchart TD A["🔀 PR 被合并
fix/auto-repair-* 分支"] --> B["👁️ Merge Watchdog 启动
merge-watchdog.yml"] B --> C["⏳ 等待下一次 Workflow 触发
观察修复是否生效"] C --> D{"修复后 Workflow
运行结果?"} D -->|"✅ 成功"| E["📊 静默更新仪表盘
标记:已修复 ✅"] D -->|"❌ 仍然失败"| F["📝 记录失败次数
retry_count + 1"] F --> G{"失败次数?"} G -->|"< 3 次"| H["🔄 触发天眼重新修复
不同策略"] H --> C G -->|"= 3 次"| I["📧 邮件通知冰朔
需要手动介入"] I --> J["📊 更新仪表盘
标记:⛔ 人类介入中"] style A fill:#1a1a2e,color:#fff style B fill:#e94560,color:#fff style E fill:#1e5128,color:#fff style I fill:#7b113a,color:#fff style J fill:#533483,color:#fff ``` --- ## 三、看守者运行机制 ### 3.1 触发条件 看守者 **不是定时任务**,它是事件驱动的: ```yaml on: # 当 PR 被合并到 main 时触发 pull_request: types: [closed] branches: [main] ``` **只关注「行动类」PR:** 只有分支名匹配 `fix/auto-repair-*` 的已合并 PR 才会激活看守者。其他 PR(功能开发、文档更新等)直接跳过。 ### 3.2 看守者核心状态机 ```jsx // 看守者为每个修复 PR 维护一个跟踪记录 const watchRecord = { pr_number: 42, pr_title: "[AUTO-FIX] sync-to-drive: rclone token 拼接修复", merged_at: "2026-03-25T08:30:00+08:00", target_workflows: ["sync-to-drive.yml"], // 这个 PR 修的是哪些 Workflow retry_count: 0, // 当前失败次数 max_retries: 3, // 最大重试次数 status: "watching", // watching | fixed | escalated repair_strategies_used: [], // 已使用的修复策略(避免重复) last_check_time: null, resolution: null // "auto_fixed" | "human_intervention" } ``` ### 3.3 观察窗口 PR 合并后,看守者不会立即判定成功或失败。它会等待目标 Workflow 的 **下一次自然触发**: ```jsx // 观察策略 const OBSERVATION = { // PR合并后,等待目标 Workflow 下一次运行 // 对于 cron 触发的 Workflow:等到下一个 cron 周期 // 对于 push 触发的 Workflow:合并本身就是一次 push,直接观察 // 最长观察窗口:48小时 // 超过48小时目标 Workflow 仍未运行 → 手动触发一次 max_observation_window_hours: 48, // 运行结果判定: // conclusion == "success" → 修复生效 ✅ // conclusion == "failure" → 修复未生效,进入重试流程 } ``` --- ## 四、三次失败升级机制 ### 4.1 每次失败使用不同修复策略 | **轮次** | **修复策略** | **Gemini 提示差异** | **失败后动作** | | --- | --- | --- | --- | | **第1轮**(原始修复) | 标准分析修复 | 常规 prompt:分析日志 → 定位问题 → 生成修复 | 记录失败,触发第2轮 | | **第2轮**(换角度) | 深度根因分析 | 附加上一轮修复代码 + 失败日志,要求「上一轮修复没解决问题,请重新从头分析根因,不要沿用之前的假设」 | 记录失败,触发第3轮 | | **第3轮**(最后机会) | 保守修复 + 降级方案 | 附加前两轮所有修复记录,要求「请提供最保守的修复方案,优先禁用出错功能并添加 skip + 告警,而非尝试修复根因」 | **三振出局 → 邮件通知人类** | ### 4.2 重试间隔 ```jsx const RETRY_TIMING = { // 第1轮失败 → 等 1 小时后触发第2轮修复 round_2_delay_hours: 1, // 第2轮失败 → 等 2 小时后触发第3轮修复 round_3_delay_hours: 2, // 第3轮失败 → 立即发邮件 escalation_delay: 0 } ``` --- ## 五、邮件通知机制(三振出局) ### 5.1 触发条件 当 `retry_count == 3` 且最后一轮修复仍然失败时,看守者发送邮件。 ### 5.2 邮件发送方式 ```yaml # 使用 GitHub Actions 内置邮件或 API 通知 - name: "📧 发送人类介入通知" uses: dawidd6/action-send-mail@v3 with: server_address: smtp.gmail.com server_port: 465 secure: true username: $ secrets.MAIL_USERNAME password: $ secrets.MAIL_PASSWORD subject: "⛔ [光湖系统] 自动修复失败 — 需要你手动介入" to: $ secrets.HUMAN_EMAIL # 冰朔的邮箱 from: "光湖系统 Merge Watchdog " html_body: | # 邮件内容见下方模板 ``` ### 5.3 邮件内容模板 ```html

⛔ 自动修复失败通知

以下问题经过 3轮自动修复 仍未解决,需要你手动处理:

问题 Workflow {workflow_name}
错误类型 {error_type}
首次发现 {first_seen_date}
修复尝试 3次(全部失败)

📋 你需要做什么:

  1. 打开仓库:guanghulab 主仓库
  2. 查看最新失败日志:点击查看
  3. 查看3轮修复记录:修复报告
  4. 手动修复后推送到 main 分支
  5. 等待下一次 Workflow 运行验证

🧠 铸渊的分析摘要:

— 光湖系统 · Merge Watchdog · {timestamp}

``` --- ## 六、成功静默机制 ### 6.1 触发条件 目标 Workflow 运行结果为 `conclusion: "success"`。 ### 6.2 静默更新仪表盘 ```jsx // 修复成功 → 只更新仪表盘,不发任何通知 const onSuccess = async (watchRecord) => { // 1. 更新仪表盘 JSON await updateDashboard({ workflow: watchRecord.target_workflows[0], status: "fixed", fixed_at: new Date().toISOString(), fixed_by: "auto-repair", retry_count: watchRecord.retry_count, resolution: "auto_fixed" }); // 2. 在修复 PR 上添加评论 await addPRComment(watchRecord.pr_number, `✅ **Merge Watchdog 确认:修复已生效**\n` + `目标 Workflow \`${watchRecord.target_workflows[0]}\` ` + `运行成功。仪表盘已更新。`); // 3. 关闭看守记录 watchRecord.status = "fixed"; watchRecord.resolution = "auto_fixed"; // ❌ 不发邮件 // ❌ 不发 Notion 通知 // ❌ 不在公告板留言 // ✅ 只有仪表盘静默更新 } ``` --- ## 七、仪表盘看守者面板 在天眼仪表盘(`docs/dashboard/status.json`)中新增 `merge_watchdog` 区块: ```json { "merge_watchdog": { "last_updated": "2026-03-25T09:00:00+08:00", "active_watches": 1, "resolved_watches": 5, "escalated_watches": 0, "records": [ { "pr_number": 42, "workflow": "sync-to-drive.yml", "status": "fixed", "retry_count": 0, "merged_at": "2026-03-25T08:30:00+08:00", "resolved_at": "2026-03-25T09:00:00+08:00", "resolution": "auto_fixed" }, { "pr_number": 38, "workflow": "token-renew.yml", "status": "escalated", "retry_count": 3, "merged_at": "2026-03-24T08:00:00+08:00", "escalated_at": "2026-03-25T02:00:00+08:00", "resolution": "human_intervention", "human_action": "pending" } ] } } ``` --- ## 八、Workflow 完整模板 **文件名:** `.github/workflows/merge-watchdog.yml` ```yaml name: "👁️ Merge Watchdog · 合并指令看守者" on: pull_request: types: [closed] branches: [main] workflow_dispatch: inputs: pr_number: description: '手动指定要观察的 PR 编号' type: number required: false permissions: actions: read contents: write pull-requests: write issues: read env: MAX_RETRIES: 3 OBSERVATION_WINDOW_HOURS: 48 ROUND_2_DELAY_HOURS: 1 ROUND_3_DELAY_HOURS: 2 WATCHDOG_DATA: "docs/dashboard/watchdog-records.json" jobs: # ======================================== # Gate: 只看行动类(已合并的 auto-repair PR) # ======================================== gate: runs-on: ubuntu-latest if: > github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'fix/auto-repair-') outputs: should_watch: $ steps.check.outputs.should_watch target_workflows: $ steps.check.outputs.target_workflows steps: - name: "🔍 识别修复目标" id: check env: GH_TOKEN: $ secrets.DEPLOY_GITHUB_TOKEN PR_BODY: $ github.event.pull_request.body PR_TITLE: $ github.event.pull_request.title run: | echo "[Merge Watchdog] 检测到 auto-repair PR 合并" echo "PR #$ github.event.pull_request.number : $PR_TITLE" # 从 PR 标题/body 中提取修复的目标 Workflow # 格式约定:PR title 包含 [AUTO-FIX] {workflow_name}: ... WORKFLOW=$(echo "$PR_TITLE" | grep -oP '\[AUTO-FIX\] \K[^:]+' || echo "unknown") echo "target_workflows=$WORKFLOW" >> $GITHUB_OUTPUT echo "should_watch=true" >> $GITHUB_OUTPUT echo "👁️ 开始看守: $WORKFLOW" # ======================================== # 观察: 等待目标 Workflow 下一次运行 # ======================================== observe: needs: gate if: needs.gate.outputs.should_watch == 'true' runs-on: ubuntu-latest outputs: workflow_result: $ steps.wait.outputs.result run_url: $ steps.wait.outputs.run_url steps: - name: "⏳ 等待目标 Workflow 运行" id: wait env: GH_TOKEN: $ secrets.DEPLOY_GITHUB_TOKEN TARGET: $ needs.gate.outputs.target_workflows run: | echo "[Merge Watchdog] 等待 $TARGET 的下一次运行..." MERGE_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ) MAX_WAIT=172800 # 48 hours in seconds POLL_INTERVAL=300 # 每5分钟检查一次 ELAPSED=0 while [ $ELAPSED -lt $MAX_WAIT ]; do sleep $POLL_INTERVAL ELAPSED=$((ELAPSED + POLL_INTERVAL)) # 查找合并后的新运行 LATEST_RUN=$(gh api \ "repos/$ github.repository /actions/runs?created=>$MERGE_TIME" \ --jq ".workflow_runs[] | select(.name | contains(\"$TARGET\"))" \ | head -1) if [ -n "$LATEST_RUN" ]; then CONCLUSION=$(echo "$LATEST_RUN" | jq -r '.conclusion // "pending"') RUN_URL=$(echo "$LATEST_RUN" | jq -r '.html_url') if [ "$CONCLUSION" = "success" ]; then echo "result=success" >> $GITHUB_OUTPUT echo "run_url=$RUN_URL" >> $GITHUB_OUTPUT echo "✅ 修复已生效!" exit 0 elif [ "$CONCLUSION" = "failure" ]; then echo "result=failure" >> $GITHUB_OUTPUT echo "run_url=$RUN_URL" >> $GITHUB_OUTPUT echo "❌ 修复未生效" exit 0 fi # conclusion=pending → 继续等 fi done # 超时 → 手动触发一次 echo "result=timeout" >> $GITHUB_OUTPUT echo "⏰ 观察窗口超时" # ======================================== # 判定: 成功 → 静默 / 失败 → 计数+重试/告警 # ======================================== verdict: needs: [gate, observe] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: "📊 读取看守记录" id: load run: | if [ -f "$WATCHDOG_DATA" ]; then RECORD=$(jq ".records[] | select(.pr_number == $ github.event.pull_request.number )" "$WATCHDOG_DATA") RETRY_COUNT=$(echo "$RECORD" | jq -r '.retry_count // 0') else RETRY_COUNT=0 mkdir -p docs/dashboard echo '{"records":[]}' > "$WATCHDOG_DATA" fi echo "retry_count=$RETRY_COUNT" >> $GITHUB_OUTPUT # -------- 成功路径:静默更新 -------- - name: "✅ 修复确认 — 静默更新仪表盘" if: needs.observe.outputs.workflow_result == 'success' run: | echo "[Merge Watchdog] ✅ 修复已生效 — 静默更新仪表盘" # 更新看守记录 jq --arg pr "$ github.event.pull_request.number " \ --arg now "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ '.records += [{ "pr_number": ($pr | tonumber), "status": "fixed", "resolved_at": $now, "resolution": "auto_fixed" }]' "$WATCHDOG_DATA" > tmp.json && mv tmp.json "$WATCHDOG_DATA" git config user.name "Merge Watchdog" git config user.email "watchdog@guanghulab.com" git add "$WATCHDOG_DATA" git commit -m "[WATCHDOG] ✅ PR #$ github.event.pull_request.number 修复已确认生效" git push # 在 PR 上留评论 gh pr comment $ github.event.pull_request.number \ --body "✅ **Merge Watchdog 确认:修复已生效** — 仪表盘已静默更新。" echo "🤫 不发邮件,不通知,安静收工。" # -------- 失败路径:计数 + 重试或告警 -------- - name: "❌ 修复未生效 — 判断重试/告警" if: needs.observe.outputs.workflow_result == 'failure' env: RETRY_COUNT: $ steps.load.outputs.retry_count GH_TOKEN: $ secrets.DEPLOY_GITHUB_TOKEN GEMINI_API_KEY: $ secrets.GEMINI_API_KEY run: | NEW_COUNT=$((RETRY_COUNT + 1)) echo "[Merge Watchdog] ❌ 第 $NEW_COUNT 次失败" if [ $NEW_COUNT -lt $ env.MAX_RETRIES ]; then echo "🔄 触发重新修复(策略 $NEW_COUNT)" # 根据轮次使用不同策略: # 轮次1 → 标准修复(已用过) # 轮次2 → 深度根因分析(附加前一轮失败信息) # 轮次3 → 保守降级方案 # 更新看守记录 jq --arg pr "$ github.event.pull_request.number " \ --argjson count $NEW_COUNT \ '(.records[] | select(.pr_number == ($pr | tonumber))).retry_count = $count' \ "$WATCHDOG_DATA" > tmp.json && mv tmp.json "$WATCHDOG_DATA" git config user.name "Merge Watchdog" git config user.email "watchdog@guanghulab.com" git add "$WATCHDOG_DATA" git commit -m "[WATCHDOG] 🔄 PR #$ github.event.pull_request.number 第${NEW_COUNT}次修复失败,触发重试" git push # 触发天眼重新修复(使用 repository_dispatch) gh api repos/$ github.repository /dispatches \ --method POST \ -f event_type="watchdog-retry" \ -f client_payload[pr_number]="$ github.event.pull_request.number " \ -f client_payload[retry_round]="$NEW_COUNT" \ -f client_payload[run_url]="$ needs.observe.outputs.run_url " else echo "⛔ 三振出局!发送邮件通知冰朔" # 更新看守记录为 escalated jq --arg pr "$ github.event.pull_request.number " \ --arg now "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ '(.records[] | select(.pr_number == ($pr | tonumber))) |= . + { "status": "escalated", "escalated_at": $now, "resolution": "human_intervention" }' "$WATCHDOG_DATA" > tmp.json && mv tmp.json "$WATCHDOG_DATA" git config user.name "Merge Watchdog" git config user.email "watchdog@guanghulab.com" git add "$WATCHDOG_DATA" git commit -m "[WATCHDOG] ⛔ PR #$ github.event.pull_request.number 三次修复失败,已通知人类" git push fi # -------- 三振出局:发邮件 -------- - name: "📧 发送人类介入邮件" if: > needs.observe.outputs.workflow_result == 'failure' && steps.load.outputs.retry_count >= 2 uses: dawidd6/action-send-mail@v3 with: server_address: smtp.gmail.com server_port: 465 secure: true username: $ secrets.MAIL_USERNAME password: $ secrets.MAIL_PASSWORD subject: "⛔ [光湖系统] 自动修复三次失败 — 需要手动介入" to: $ secrets.HUMAN_EMAIL from: "光湖 Merge Watchdog " html_body: |

⛔ 合并指令看守者 · 三振出局通知

以下修复经过 3轮自动修复 仍未解决:

PR#$ github.event.pull_request.number
目标 Workflow$ needs.gate.outputs.target_workflows
最新失败日志点击查看

📋 你需要做什么:

  1. 打开上面的失败日志链接
  2. 查看报错信息和上下文
  3. 手动修复代码并推送到 main
  4. 等待下一次 Workflow 运行验证

看守者已暂停对该 Workflow 的自动修复。
手动修复成功后,仪表盘会自动更新。

— 光湖系统 · Merge Watchdog

``` --- ## 九、安全护栏 --- ## 十、与现有系统的协作关系 ```mermaid flowchart LR A["🌙 天眼夜间修复引擎
v3.0"] -->|"创建 PR"| B["🔀 PR 合并"] B -->|"触发"| C["👁️ Merge Watchdog
合并指令看守者"] C -->|"修复成功"| D["📊 仪表盘
静默更新"] C -->|"修复失败
< 3次"| E["🔄 repository_dispatch"] E -->|"触发重新修复"| A C -->|"三振出局"| F["📧 邮件通知冰朔"] F -->|"人类手动修复"| B style C fill:#e94560,color:#fff style D fill:#1e5128,color:#fff style F fill:#7b113a,color:#fff ``` --- ## 十一、铸渊 Secrets 配置清单 | **Secret 名称** | **用途** | **当前状态** | | --- | --- | --- | | `DEPLOY_GITHUB_TOKEN` | 读取 Workflow 运行记录 + 创建分支 + 推送 | ✅ 已有 | | `GEMINI_API_KEY` | 重试修复时调用 Gemini 分析 | ✅ 已有 | | `MAIL_USERNAME` | 发送告警邮件的 SMTP 账号 | ⚠️ 需配置 | | `MAIL_PASSWORD` | 发送告警邮件的 SMTP 密码/应用专用密码 | ⚠️ 需配置 | | `HUMAN_EMAIL` | 冰朔的邮箱地址(接收告警) | ⚠️ 需配置 | --- ## 十二、执行回执模板 ```markdown ## 执行回执 - [ ] Workflow 文件创建:`.github/workflows/merge-watchdog.yml` - [ ] 看守记录 JSON 初始化:`docs/dashboard/watchdog-records.json` - [ ] 与天眼夜间修复引擎 `repository_dispatch` 联动测试 - [ ] 邮件 Secrets 配置确认(MAIL_USERNAME / MAIL_PASSWORD / HUMAN_EMAIL) - [ ] 仪表盘面板新增 `merge_watchdog` 区块 - [ ] 手动触发测试(workflow_dispatch) - [ ] 回执签名:铸渊 · {日期} ``` ---