|
| 1 | +# CI Triage Watchdog Runbook |
| 2 | + |
| 3 | +## Purpose |
| 4 | +Provide a deterministic incident path for failed required checks when primary log retrieval is unavailable or stalled. |
| 5 | + |
| 6 | +## Scope |
| 7 | +- Pull requests targeting protected branches (`develop`, `main`) |
| 8 | +- Required status checks: `governance`, `boundary-validation` |
| 9 | +- Blockers caused by checks, unresolved review conversations, or signature policy |
| 10 | + |
| 11 | +## Watchdog SLA |
| 12 | +- `T+0 min`: classify blocker type. |
| 13 | +- `T+2 min`: identify failing job/step from check-runs + jobs metadata. |
| 14 | +- `T+5 min`: if raw logs are still unavailable, switch to fallback path. |
| 15 | +- `T+10 min`: if still blocked without root cause, escalate with explicit decision log. |
| 16 | + |
| 17 | +No indefinite waiting is allowed. |
| 18 | + |
| 19 | +## Step 1 - Classify Merge Blocker |
| 20 | +Run: |
| 21 | + |
| 22 | +```bash |
| 23 | +gh api repos/T72/task-orchestrator/pulls/<PR_NUMBER> \ |
| 24 | + | jq -r '{mergeable,mergeable_state,state,head:.head.sha,base:.base.ref}' |
| 25 | +``` |
| 26 | + |
| 27 | +Classification matrix: |
| 28 | + |
| 29 | +| Blocker Signal | Meaning | Immediate Action | |
| 30 | +|---|---|---| |
| 31 | +| required check failed | CI/code issue | Continue to Step 2 | |
| 32 | +| required check pending/queued | infrastructure/scheduling | retry monitor until `T+5`, then escalate | |
| 33 | +| all comments must be resolved | unresolved review threads | resolve threads or respond | |
| 34 | +| commits must have verified signatures | policy gate | sign/rewrite commits or relax branch policy explicitly | |
| 35 | +| mergeable=false/conflicting | branch divergence/conflict | rebase/cherry-pick and rerun checks | |
| 36 | + |
| 37 | +## Step 2 - Identify Failing Job and Step |
| 38 | +Run: |
| 39 | + |
| 40 | +```bash |
| 41 | +gh api repos/T72/task-orchestrator/commits/<HEAD_SHA>/check-runs \ |
| 42 | + | jq -r '.check_runs[] | [.id,.name,.status,.conclusion,.html_url] | @tsv' |
| 43 | + |
| 44 | +gh api repos/T72/task-orchestrator/actions/runs/<RUN_ID>/jobs \ |
| 45 | + | jq -r '.jobs[] | "JOB\t\(.id)\t\(.name)\t\(.conclusion)\n" + (.steps[] | "STEP\t\(.number)\t\(.name)\t\(.conclusion)")' |
| 46 | +``` |
| 47 | + |
| 48 | +Record the exact failing step name before attempting fixes. |
| 49 | + |
| 50 | +## Step 3 - Primary Log Retrieval |
| 51 | +Attempt log retrieval for up to 5 minutes: |
| 52 | + |
| 53 | +```bash |
| 54 | +gh api repos/T72/task-orchestrator/actions/jobs/<JOB_ID>/logs > /tmp/job.log |
| 55 | +``` |
| 56 | + |
| 57 | +If successful, extract failure signatures: |
| 58 | + |
| 59 | +```bash |
| 60 | +rg -n "FAIL:|Traceback|Error|No module named|EOFError|database is locked|exit code" /tmp/job.log |
| 61 | +``` |
| 62 | + |
| 63 | +## Step 4 - Fallback Path (Mandatory After `T+5`) |
| 64 | +If job logs are unavailable: |
| 65 | + |
| 66 | +1. Query check annotations: |
| 67 | + |
| 68 | +```bash |
| 69 | +gh api repos/T72/task-orchestrator/check-runs/<CHECK_RUN_ID>/annotations \ |
| 70 | + | jq -r '.[] | [.path,.start_line,.annotation_level,.message] | @tsv' |
| 71 | +``` |
| 72 | + |
| 73 | +2. Download diagnostics artifacts from the failed run: |
| 74 | + |
| 75 | +```bash |
| 76 | +gh api repos/T72/task-orchestrator/actions/runs/<RUN_ID>/artifacts \ |
| 77 | + | jq -r '.artifacts[] | [.id,.name,.archive_download_url] | @tsv' |
| 78 | + |
| 79 | +gh run download <RUN_ID> --repo T72/task-orchestrator --name <ARTIFACT_NAME> --dir /tmp/diag |
| 80 | +``` |
| 81 | + |
| 82 | +3. Reproduce the failing step locally in clean worktree: |
| 83 | + |
| 84 | +```bash |
| 85 | +bash tests/test_storage_contract.sh |
| 86 | +bash tests/test_collaboration_event_store.sh |
| 87 | +bash tests/test_error_exit_codes.sh |
| 88 | +bash tests/test_public_manifest_pipeline.sh |
| 89 | +``` |
| 90 | + |
| 91 | +## Step 5 - Decide and Act |
| 92 | +- Root cause found: |
| 93 | + - create focused fix commit, |
| 94 | + - rerun checks, |
| 95 | + - close related review thread/comments. |
| 96 | +- Root cause not found by `T+10`: |
| 97 | + - publish evidence bundle (commands, outputs, timestamps), |
| 98 | + - escalate as infra/observability incident, |
| 99 | + - do not continue passive waiting. |
| 100 | + |
| 101 | +## Required Evidence Checklist |
| 102 | +- PR state snapshot (`mergeable_state`, head SHA, timestamp) |
| 103 | +- Check-runs snapshot (status/conclusion for required checks) |
| 104 | +- Failing job + step name |
| 105 | +- Log retrieval attempts + timestamps |
| 106 | +- Artifact retrieval result |
| 107 | +- Local reproduction result |
| 108 | +- Final decision and next action |
| 109 | + |
| 110 | +## Exit Criteria |
| 111 | +- Blocker type explicitly classified. |
| 112 | +- Root cause identified or escalation explicitly documented. |
| 113 | +- No unbounded monitoring wait remains. |
0 commit comments