$npx -y skills add lucasfcosta/backpressured --skill pr-monitoringUse after a pull request has been opened and the task is not finished until it lands. Triggers on "PR is open", "CI is running", "is it merged yet", a PR that is green but not merged, fresh CI that might still flip, or being tempted to report a task done at PR-open. GitHub / gh C
| 1 | # PR Monitoring |
| 2 | |
| 3 | ## Overview |
| 4 | |
| 5 | **Open is not landed. Green is not merged.** A PR you opened is unfinished work until it has actually merged with nothing outstanding — or you have named a blocker only a human can clear. You are the machine that keeps watching so a human doesn't have to babysit the PR; declaring "done" at PR-open (or at a freshly-green CI) is exactly the human-backpressure you exist to replace. |
| 6 | |
| 7 | Two traps the base instinct misses even when it resists "open = done": |
| 8 | 1. **A green check can flip red.** Late-starting jobs and slow stages mean an initial all-green is not a final all-green. |
| 9 | 2. **The watch ends at merge, not at first green.** New commits, reviewer comments, and conflicts arrive *after* CI goes green. |
| 10 | |
| 11 | ## When to Use |
| 12 | |
| 13 | - You (or the main agent) just opened a PR, or CI is running on one, and the goal is to get it merged. |
| 14 | - Asked "is it merged / did it land / is CI done yet?" on a PR you own. |
| 15 | - Tempted to report the task complete because the PR is open or CI just went green. |
| 16 | |
| 17 | **Not for:** opening the PR itself; deciding *whether* to open one; code review of the diff (that is the reviewer skills). This is purely the watch-until-landed loop. |
| 18 | |
| 19 | ## "Landed clean" — the sufficiency bar |
| 20 | |
| 21 | Report done only when **all** hold; otherwise keep monitoring or name the blocker: |
| 22 | |
| 23 | - PR **state is MERGED** (not just approved, not just green). |
| 24 | - Every **required** check is green — confirmed, not assumed (`statusCheckRollup`). |
| 25 | - No **unresolved** review-bot or human comments. |
| 26 | - No **conflict** (`mergeStateStatus` is not `DIRTY`) and the branch is not **stale** (`BEHIND`) — ideally `CLEAN`. |
| 27 | |
| 28 | ## The monitoring loop |
| 29 | |
| 30 | ```dot |
| 31 | digraph pr_monitor { |
| 32 | "PR open" [shape=box]; |
| 33 | "Watch checks to completion" [shape=box]; |
| 34 | "All required checks green?" [shape=diamond]; |
| 35 | "Read failing logs, fix root cause, push" [shape=box]; |
| 36 | "Hold a green minute (≥60s continuously green)" [shape=box]; |
| 37 | "Anything flipped red / new check started?" [shape=diamond]; |
| 38 | "Comments or conflicts outstanding?" [shape=diamond]; |
| 39 | "Address them (push fix / resolve / reply)" [shape=box]; |
| 40 | "Merge if authorized" [shape=box]; |
| 41 | "Merged?" [shape=diamond]; |
| 42 | "STOP: name the human blocker" [shape=octagon]; |
| 43 | "Landed clean — report done" [shape=doublecircle]; |
| 44 | |
| 45 | "PR open" -> "Watch checks to completion"; |
| 46 | "Watch checks to completion" -> "All required checks green?"; |
| 47 | "All required checks green?" -> "Read failing logs, fix root cause, push" [label="no"]; |
| 48 | "Read failing logs, fix root cause, push" -> "Watch checks to completion"; |
| 49 | "All required checks green?" -> "Hold a green minute (≥60s continuously green)" [label="yes"]; |
| 50 | "Hold a green minute (≥60s continuously green)" -> "Anything flipped red / new check started?"; |
| 51 | "Anything flipped red / new check started?" -> "Watch checks to completion" [label="yes — restart clock"]; |
| 52 | "Anything flipped red / new check started?" -> "Comments or conflicts outstanding?" [label="no"]; |
| 53 | "Comments or conflicts outstanding?" -> "Address them (push fix / resolve / reply)" [label="yes"]; |
| 54 | "Address them (push fix / resolve / reply)" -> "Watch checks to completion"; |
| 55 | "Comments or conflicts outstanding?" -> "Merge if authorized" [label="no"]; |
| 56 | "Merge if authorized" -> "Merged?"; |
| 57 | "Merged?" -> "STOP: name the human blocker" [label="no — needs approval/permission"]; |
| 58 | "Merged?" -> "Landed clean — report done" [label="yes"]; |
| 59 | } |
| 60 | ``` |
| 61 | |
| 62 | ## gh mechanics (quick reference) |
| 63 | |
| 64 | | Need | Command | |
| 65 | |------|---------| |
| 66 | | Watch checks (background, 1-min poll, 10-min cap) | `timeout 600 gh pr checks <pr> --watch --interval 60 &` | |
| 67 | | Structured status in one shot | `gh pr view <pr> --json state,mergeStateStatus,reviewDecision,statusCheckRollup` | |
| 68 | | Read only the failing job's logs | `gh run view <run-id> --log-failed` | |
| 69 | | Read review-bot / human comments | `gh pr view <pr> --comments` | |
| 70 | | Merge when authorized | `gh pr merge <pr> --squash` (or repo's strategy) | |
| 71 | |
| 72 | Key `mergeStateStatus` values: `CLEAN` (mergeable), `BLOCKED` (needs approval/required check), `BEHIND` (must update branch), `DIRTY` (conflict), `UNSTABLE` (a non-required check failing). |
| 73 | |
| 74 | ## Cadence & backgrounding (defaults) |
| 75 | |
| 76 | **Always monitor in the background — never block the session.** The user must be able to keep prompting while the PR is watched, so run the watch as a background job (`&`, or your harness's background-task / re-invoking monitor) and check back on it. A foreground `gh pr checks --watch` that holds the terminal is wrong here — it freezes the user out. |
| 77 | |
| 78 | - **Default window: 10 minutes** of active polling per round (`timeout 600`). |
| 79 | - **Default poll interval: 1 minute** (`--interva |