Watch CI
Monitor GitHub Actions CI runs for the current HEAD (or a specified SHA) until completion, then report verdict.
Trigger
- Keywords: watch CI, check CI, CI status, monitor build, build status, is CI passing, watch actions, CI result
When NOT to Use
- Pushing code to remote (use
/push-ci) - Creating pull requests (use
/create-pr) - Running local tests (use
/verifyor/precommit)
Workflow
Auto-detect (branch + SHA) → Find matching runs → Quick-check status → Watch or Report → Verdict
Step 1: Resolve Target
Determine which CI runs to monitor. Use arguments if provided, otherwise auto-detect.
BRANCH=${ARG_BRANCH:-$(git rev-parse --abbrev-ref HEAD)}
HEAD_SHA=${ARG_SHA:-$(git rev-parse HEAD)}
TIMEOUT=${ARG_TIMEOUT:-10}
INTERVAL=${ARG_INTERVAL:-30}
If --run-id <id> is specified, skip run discovery and monitor that specific run directly.
Step 2: Find CI Runs
Find runs matching the target SHA on the target branch:
gh run list --branch "$BRANCH" --limit 30 \
--json databaseId,headSha,status,name,url
Note: Use--limit 30(not 10) to avoid missing target SHA runs on busy branches. Filter results client-side byHEAD_SHA.
Filter results to those matching HEAD_SHA.
Retry logic: If no matching runs found, retry up to 3 times by re-running the gh run list command. The natural processing delay between retries provides sufficient wait time — do not use sleep N (N ≥ 2) as the first command, the harness will block it. All retry commands must start with gh or git to match allowed-tools. CI workflows may take a few seconds to trigger after push.
If still no runs found after retries:
⚠️ No CI run detected for SHA <sha>. Possible causes:
- No workflow configured for this branch
- Path-filtered workflow didn't trigger
- Check: gh run list --branch <branch>
Step 3a: Quick Status Check
Before starting a long-running watch, check if runs are already completed:
gh run view <run-id> --json status,conclusion,name,url
| Result | Action | |--------|--------| | All runs completed | Skip to Step 4 (Verdict) immediately — no watching needed | | Some completed, some in progress | Report completed verdicts, watch remaining (Step 3b) | | All in progress | Proceed to Step 3b |
Step 3b: Watch Runs
For each in-progress run, monitor with gh run watch:
gh run watch <run-id> --exit-status -i "$INTERVAL"
Poll interval: $INTERVAL defaults to 30 seconds (configurable via --interval). gh's own default is 3 seconds; at that rate, Monitor streaming surfaces ~20 notifications per minute, which has been reported as noisy. 30 seconds reduces poll noise by ~90% at the cost of ≤ 27 s additional completion-detection lag, which is negligible for typical multi-minute CI runs. Pass --interval 3 to restore the old cadence when near-real-time feedback matters.
Execution mode: Monitor streaming is the default — non-blocking, reliable notifications for each status line.
| Mode | When | Behavior | |------|------|----------| | Monitor (default) | No mode flag | Stream gh run watch via Monitor tool. Each stdout line arrives as a notification. Claude processes verdict on completion. Non-blocking. | | Foreground (--blocking) | --blocking flag passed | Execute gh run watch inline (blocking). Claude waits for completion, then reports verdict. Use when Monitor is unavailable or for simple single-run cases. | | Background (--background) | --background flag passed | Launch with Bash(run_in_background: true). Legacy fallback only — run_in_background delivers a single completion event, not streaming progress, so Monitor is preferred for rich updates. Provide a manual check command. |
Monitor mode (default) — behavior:
- Launch
gh run watch <run-id> --exit-status -i "$INTERVAL"via Monitor tool withdescription: "CI run <run-id> (<name>)"andtimeout_ms: TIMEOUT 60 1000 - Each stdout line (status update) arrives as a streaming notification —
$INTERVALcontrols how often those lines fire - On exit (run completes or fails), parse final output for pass/fail status
- Report verdict
Foreground mode (--blocking) — behavior:
- Execute
gh run watch <run-id> --exit-status -i "$INTERVAL"inline via Bash - Wait for completion (blocking) —
$INTERVALonly affects how oftenghpolls the API, not wall-clock completion - Parse output for pass/fail status
- Report verdict
Background mode (--background) — legacy fallback only:
- Quick-check (Step 3a) first — if already completed, report immediately and skip background
- If still running, launch
gh run watch <run-id> --exit-status -i "$INTERVAL"withBash(run_in_background: true);$INTERVALstill applies (sameghcall), but because background mode only surfaces a single completion event, poll cadence has no user-visible effect here - Inform the user honestly: "CI monitoring launched in background for run
<id>. Background notifications may not auto-report reliably. To check manually:gh run view <id>or re-run/watch-ci" - Do NOT promise streaming progress updates —
Bash(run_in_background: true)only delivers a single completion event, not per-status-line streaming; for rich updates, use Monitor mode
Multiple runs: If multiple workflow runs match (e.g. CI + Auto Release), launch parallel Monitor instances — one per run. Each Monitor reports its own per-run verdict via notifications. Overall verdict = worst individual result (any fail → overall fail). In --blocking mode, watch sequentially. In --background mode, launch each as a separate background task.
Timeout enforcement: Default 10 minutes (configurable via --timeout). In Monitor mode, set timeout_ms: TIMEOUT 60 1000 (Monitor tool enforces deadline). In --blocking mode, enforce via Bash tool's timeout parameter (milliseconds). If a timeout occurs, report the run as timed out. Timeout applies per individual run invocation, not to the entire monitoring session.
Step 4: Verdict
| CI Result | Output | |-----------|--------| | All pass | "✅ CI passed" + per-run URLs | | Any fail | Failing jobs + gh run view <id> --log-failed summary | | Timeout | "⚠️ CI still running after <N>min — gh run watch <id>" |
Overall verdict = worst individual result (any fail → overall fail).
Prohibited Actions
❌ Running `gh run view` once and treating that as "monitoring" — one-shot status check is NOT watching
❌ Promising per-status-line streaming updates in background mode — `Bash(run_in_background: true)` only delivers a single completion event; use Monitor for rich streaming
❌ Skipping the quick-check step (Step 3a) — always check status before deciding to watch
❌ Reporting "CI monitoring started" without actually launching `gh run watch`
❌ Using `gh run list` results as the final verdict — list shows status at query time, not completion
❌ Using `sleep N` (N ≥ 2) as the first Bash command — harness blocks it; retry by re-running `gh run list` directly
❌ Using commands outside `allowed-tools` (only `gh`, `git`, `Read`, and `Monitor` are permitted)
❌ Using `Bash(run_in_background: true)` when Monitor is available — Monitor is the preferred streaming mechanism
Arguments
| Argument | Description | Default | |----------|-------------|---------| | --sha <sha> | SHA to monitor | git rev-parse HEAD | | --branch <branch> | Branch to filter runs | git rev-parse --abbrev-ref HEAD | | --timeout <min> | Watch timeout in minutes | 10 | | --interval <sec> | Poll interval for gh run watch -i — controls how often gh queries the API and emits a status line (Monitor mode surfaces each line as one notification) | 30 | | --run-id <id> | Monitor a specific run ID directly | auto-detect | | --blocking | Use foreground blocking mode instead of Monitor streaming | Monitor | | --background | Legacy fallback: launch in background (unreliable auto-