Watch the current branch's PR — reply to and resolve review threads, fix failing checks, and optionally merge when ready
Scanned 9/20/2026
Install to Claude Code
npx -y skills add adamayoung/TMDb --skill watch-pr --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Watch Pr?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/adamayoung-watch-pr)More formats (shields.io, HTML) on the badges page.
---
name: watch-pr
description: Watch the current branch's PR — reply to and resolve review threads, fix failing checks, and optionally merge when ready
---
# Watch PR
Watch the open pull request for the current branch: handle review conversations,
fix failing status checks, and — when asked — merge once everything is green.
Repo is `adamayoung/TMDb`. GitHub reads/writes (PR state, checks, branch update,
merge) use the **GitHub MCP** (`mcp__github__*`, owner/repo from the `origin` remote);
`gh` is kept for the **blocking CI wait** (`gh pr checks --watch`, which the MCP has
no equivalent for). If an MCP write fails with **401/403** (PAT expired or missing
scope), fall back to the equivalent `gh` command.
**Mode** — check the arguments passed to this skill (shown at the end). If they
include `merge` (e.g. `/watch-pr merge` or "merge when ready"), enable
**merge-when-ready**; otherwise run in **watch-only** mode.
**Run in the background.** Watching blocks on CI for minutes at a time, so run the
watch as a background task — the user can keep interacting while it runs, and is
pinged when the PR needs attention or is ready. Don't tie up the foreground on a
wait loop.
## 0. Find the PR
**A PR number in the arguments wins.** If the arguments include a PR number
(e.g. `/watch-pr 123` or `/watch-pr merge 123`), watch that PR and skip branch
discovery entirely. A caller that launches this skill **in the background**
(e.g. `/deliver` Phase 10, which may move the session to another deliverable's
worktree while the watch runs) must pass the number — the watch must never
depend on "current branch" staying stable for its lifetime.
Otherwise, find the open PR for the current branch with `mcp__github__list_pull_requests`
(owner/repo from the `origin` remote, `head: <owner>:<branch>`, `state: open`), then
read its details with `mcp__github__pull_request_read` method `get` — `number`,
`html_url`, `state`,
`head.ref`, and **`mergeable_state`** (the REST merge state: lowercase
`clean`/`blocked`/`behind`/`unstable`/`dirty`/`unknown`/`draft` — **not** `gh`'s
uppercase `mergeStateStatus`). Read the check runs separately with method
`get_check_runs` (§3).
- No PR for the current branch → stop and tell the user (suggest `/pr`).
- State not `open` → stop and report.
- **Thread data is a separate call** — `pull_request_read` method `get` does not
return review threads. Fetch them with method `get_review_comments` (delegated to
`/review-pr-threads`); never expect thread data on the PR `get`.
- **Pin the tree to the PR, and re-check it every pass.** The number alone is
not enough: both sweeps in §1 **edit and push the current working tree**, so
a drifted CWD doesn't just read the wrong PR, it commits to the wrong branch.
Before each pass assert `git branch --show-current` equals the PR's
`head.ref` (already fetched above, so this is free). Mismatch → **stop and
report; never sweep.** This is the case §0 exists for — a background watch
outlives the session's CWD, and `/deliver` Phase 10 moves to the next
deliverable's worktree while this loop is still running.
Keep a **run ledger** in your working notes: resolved thread IDs, a topic
signature per handled thread (`path:line` + short gist), and a fix-attempt
counter per failing check. Use it to avoid repeating work (see Loop Guard).
## 1. Watch loop
Repeat the pass below until the PR is **ready** (§3) or **stuck** (§2).
### 1a. Review threads
Delegate the thread sweep to **`/review-pr-threads <number>`** — **always pass
the PR number.** Without it the skill falls back to "the current branch's PR"
(`review-pr-threads` §0), re-introducing exactly the dependency §0 forbids. It resolves the
currently-unresolved threads in one pass (assess → fix+verify / reply-only →
reply → resolve), reusing **this run's ledger** so a topic you already fixed isn't
re-edited, and returns a summary (fixed w/ SHAs, replied-only, left-for-user,
counts, whether it pushed).
It is a **single sweep** by design — the across-push convergence is *this* loop's
job: fold its summary into the ledger, and if it pushed fixes, the next pass picks
up any fresh threads the `claude-review` bot raises (gated to Critical/High). Do
not duplicate its per-thread logic here.
### 1b. Status checks
Delegate failing-check fixing to **`/fix-pr-checks <number>`** — **always pass
the PR number**, for the same reason as §1a. It routes each failing
check to the right diagnosis skill (`/diagnose-ci-failure` or
`/diagnose-integration-failure`) via a Haiku subagent, applies and verifies the
fix, commits, pushes once, and returns a summary (fixed w/ SHAs, exhausted,
skipped, pending, whether it pushed). It shares **this run's ledger**, so the
3-attempt cap per check is honoured across passes. Fold its summary into the
ledger; if it pushed, the next pass re-checks.
**Waiting stays here** (orchestration, not the fix primitive): if checks are
`pending` and nothing is failing, block efficiently with `gh pr checks --watch`
rather than polling, then loop. If `/fix-pr-checks` reports a check **exhausted**,
stop and report it per the Loop Guard.
### 1c. Failing checks not caused by this PR (usually a flaky integration test)
`/fix-pr-checks` assumes the fix belongs on **this** branch. That is wrong when
the failing check — most often a **live integration test** — is broken or flaky
**independently of this PR's diff**. Patching it onto the feature branch would
muddy the PR's scope and leave the test broken for everyone else. Triage before
fixing here:
1. **Is the failing test in this PR's diff?** `mcp__github__pull_request_read`
method `get_files` (`pullNumber: <n>`) — if the failing test's file is **not** in
that list, this PR did not cause it.
2. **Transient or deterministic?** Re-run the failed jobs once with
`mcp__github__actions_run_trigger` method `rerun_failed_jobs` (`run_id: <id>`),
then watch the re-run (the loop's `gh pr checks --watch`, §1b). Passes on re-run →
a transient live-API flake; note it and continue. Fails again **and** not in this
PR's diff → a **pre-existing** problem (e.g. a brittle live-data assertion).
3. **A pre-existing failure is a `main` problem — hand it to
`/fix-integration-failures`**, which fixes it on its own branch off `main`,
runs `make ci`, and opens/merges a PR (never on this feature branch). Tell the
user you're opening a **second** PR to unblock this one; if they'd rather you
stop, report and stop. Once that fix is on `main`, bring this PR's branch up to
date (`mcp__github__update_pull_request_branch`, `pullNumber: <n>`) and re-watch —
the check now passes.
## 2. Loop guard (do not get stuck)
- **Thread dedup is `/review-pr-threads`' job** — it shares this ledger, so it
won't reprocess a handled thread or re-edit a topic already fixed this run (it
replies with the earlier SHA and resolves). Keep passing it the same ledger.
- **Check-fix attempts are `/fix-pr-checks`' job** — it shares this ledger and
caps each check at **3** fix→push attempts. When it reports a check
**exhausted**, **stop** and report it to the user — never loop forever.
- The `claude-review` bot re-reviews on every push (`synchronize`), so your fixes
trigger fresh reviews — this is expected, not new work to fear. By policy it now
posts inline threads **only for Critical/High** findings (Medium/Low live in its
summary comment, which is advisory — do not turn summary bullets into code
changes). A converging PR should see each push's inline batch shrink; if the same
severity-gated topic reappears across pushes, treat it as noise per the rule
above (reply with the earlier SHA, resolve, don't re-edit).
- **Re-sweep after every push — "ready" is only true of the current tip.** Any push
to the branch (a check fix, *and* a caller's exceptional post-gate commit such as
an approved skill edit or a `/deliver` retro amendment) re-triggers
`claude-review`, which can post a fresh
Critical/High thread that **blocks the merge** (`required_review_thread_resolution`).
Never declare ready off a thread/check snapshot taken *before* the latest push:
after the last push settles, run one more full pass (thread sweep + check
re-confirm) before §3. A single early "0 unresolved" check is not a standing
guarantee.
- End the loop when a full pass resolves no new threads and has no actionable
check failures. Hard backstop: ~10 passes, then report and stop.
- Waiting: use `gh pr checks --watch` for in-flight CI. When only waiting on a
human to review, pause and resume later with `ScheduleWakeup` (a few minutes
while CI is active; longer when idle). This skill also composes with `/loop`
if the user prefers harness-driven cadence.
## 3. Ready / merge
The PR is **ready** when every review thread is resolved, no check is failing or
pending, AND the branch is **up to date with `main`** (the `main` ruleset requires
it before merge).
**Verify check completeness explicitly — a running check is not a pass.** Read the
checks with `mcp__github__pull_request_read` method `get_check_runs` (owner/repo from
`origin`, `pullNumber: <n>`). "No check failing" is **not** the same as "all checks
passed": a run still `in_progress` / `queued` has **no `conclusion` yet**, so a filter
like "any conclusion != success?" reports it as *absent*, not *pending* — reading as
green when it isn't. Confirm readiness **positively**: every check run has
`status == "completed"` **and** `conclusion == "success"` on the current head commit.
`get_check_runs` is scoped to the head commit, but a re-run can still leave duplicate
rows for a check name — key on the latest run per name. Do **not** infer green from a
`gh pr checks --watch` exit alone; re-read the check runs. And when `mergeable_state`
is `blocked`, **rule out a pending required check first** (it is the common cause)
before attributing the block to a review/policy rule like code-owner review.
(Bit #361: a still-running "Build and Test" was misread as green and the block was
wrongly pinned on code-owner review.)
**Rebase before declaring ready, never after.** `main` advances while you watch
(other PRs merge), leaving the branch `BEHIND`. The moment the PR is otherwise
green, bring it up to date — `mcp__github__update_pull_request_branch`
(`pullNumber: <n>`; a merge of `main`, no force-push) — and wait for the re-triggered
CI to go green again *before* you call
it ready. "Ready" must mean "mergeable right now": never surface a `BEHIND` PR as
ready and leave the user waiting on a rebase + re-run. Update **once** at the ready
point, not eagerly on every `main` advance — each update re-runs the full ~4–7 min
CI matrix.
**On ready, move the issue to `In review` — before reporting.** Ready means the
PR is waiting on a human, which is exactly what that column tells them, and the
board is how they see it without reading this run's output. Take the issue number
from the **closing keyword in the PR body** (`Closes #NNN` / `Fixes #NNN`), which
`/pr` requires on every PR that has an issue. That is deliberately the only
source: it is the same link GitHub uses to close the issue on merge, so it cannot
disagree with what actually happens, and it needs no extra argument — a second
bare number beside the PR number would be ambiguous to parse.
Column vocabulary and the exact call:
[`.github/ISSUE_FILING.md`](../../../.github/ISSUE_FILING.md) → *Board status —
the column lifecycle*. No issue reference → skip the move and say so in the
summary; a failed board write is reported, never fatal, and never a reason to
withhold a ready PR.
Do this in **both** modes. In merge-when-ready it is momentary — the merge closes
the issue and the board's own automation takes it to Done — but it is still
correct while the merge is in flight, and it is what the board shows if the merge
then fails.
- **Watch-only**: report "PR is ready" with a short summary (threads handled,
checks green, branch up to date, issue moved to `In review`) and stop — wait
for the user's explicit go-ahead before merging.
- **Merge-when-ready**: on ready, **capture the head branch name first** (you need it
to delete the remote branch — `merge_pull_request` has no delete-branch option),
merge with `mcp__github__merge_pull_request` (owner/repo from `origin`,
`pullNumber: <n>`, `merge_method: squash`), **then** delete the remote branch, and
report the result:
```bash
git push origin --delete <branch> # only after the squash-merge has landed
```
On a 401/403, fall back to `gh pr merge --squash --delete-branch`.
**Several PRs queued?** Merge in dependency order. Stack one on another (base the
later PR on the earlier branch) **only** when they genuinely depend on each other
or the merge order is fixed up front — then merging the first leaves the next
already up to date, with no second rebase. Keep *independent* PRs on separate
`main`-based branches and rely on the rebase-before-ready rule above rather than
coupling unrelated work.
## Guardrails
- Never edit `.github/workflows/*` or other CI/config to force a check green, and
never force-push, without surfacing to the user first.
- If a requested change is ambiguous or risky, reply on the thread with your
assessment and leave it for the user (note it in the final summary) rather than
guessing.
- **There is no pre-commit hook** (`.git/hooks/` holds only `*.sample`). Nothing
lints on your behalf — run `/lint` before committing, or the PR's `Lint` job is
the first thing that tells you.
Arguments: $ARGUMENTS
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!