| name | pr-monitor |
| description | Monitor PR status — CI checks, review bot findings, unresolved conversations — and address + resolve bot comments. After pushing fixes, waits for and handles new bot review comments before declaring merge-ready. Use when asked to "check PR status", "monitor this PR", "check CI", "resolve PR comments", "address review comments", "mark conversations resolved", or when waiting on a PR to be merge-ready. NOT for: initial code review (use pr-review), post-merge testing (use verify), or reviewing the diff itself.
|
| skills | ["fable-mode"] |
| tools | ["mcp__sequential-thinking__sequentialthinking"] |
PR Monitor
Follow Steps 1-5 in order. You may NOT report merge-ready or "ship" until Step 5,
and Step 5 has prerequisites that reference every prior step. Skipping ahead means
the prerequisites fail and you must go back.
This skill ALWAYS evaluates, replies to, and resolves bot review threads (qodo,
CodeRabbit) as part of the workflow. That is core, not optional.
Step 1: Determine the PR
- If a PR number or URL is given, use that.
- If on a feature branch:
gh pr view --json number,title,url,state,statusCheckRollup,reviewDecision,comments
- If no PR exists, say so and stop.
Record owner, repo, and PR number for API calls below.
Step 2: Run a full status pass
Run all five checks. You re-run this exact step during follow-up (Step 4), so be
consistent.
2a. CI checks
gh pr checks <number>
Report: passed / failed / pending. For failures: gh run view <run-id> --log-failed.
2b. Review decision
gh pr view <number> --json reviewDecision,reviews,latestReviews
Report: approved / changes-requested / pending. List reviewers.
2c. Unresolved review threads
gh api graphql -f query='{
repository(owner: "OWNER", name: "REPO") {
pullRequest(number: NUMBER) {
reviewThreads(first: 100) {
nodes {
id isResolved
comments(first: 5) {
nodes { author { login } body path position createdAt }
}
}
}
}
}
}'
List each UNRESOLVED thread: author, file/line, summary, and classify as one of:
- Bot -- author is a known review bot (qodo-merge-pro, coderabbitai, umm-actually, etc.)
- Claude -- author is a human account but the comment body contains a Claude
footer (see classification rules below)
- Human -- everything else
Record the unresolved count -- you compare it after follow-up in Step 4.
Claude comment detection
A comment is from another Claude instance if its body contains any of these strings
(case-insensitive, typically in the last few lines):
Claude Code
Co-Authored-By: Claude
Generated with Claude
These comments come from human-owned GitHub accounts but were authored by a Claude
agent. They are classified separately so they can be auto-handled like bot comments.
2d. Findings outside inline threads (reviews + issue comments)
Inline threads are not the only surface bots use: Sourcery puts "Overall
Comments" in the review BODY, and qodo and others post PR-level issue
comments. Neither appears in reviewThreads, so a thread-only pass silently
misses them. This step is mandatory — it is not supplementary to 2c.
Fetch both surfaces with pagination to ensure no comments are missed:
gh api repos/OWNER/REPO/pulls/NUMBER/reviews --paginate --jq '.[] | {id, user: .user.login, submitted_at, body}'
gh api repos/OWNER/REPO/issues/NUMBER/comments --paginate --jq '.[] | {id, user: .user.login, created_at, body}'
Classify every comment — not just bot-authored ones. Use the same
classification as 2c review threads:
- Bot — author is a known review bot (qodo-merge-pro, coderabbitai,
sourcery-ai, umm-actually, etc.)
- Claude — author is a human account but the body contains a Claude
footer (see 2c classification rules)
- Human — everything else
- Pipeline — the body contains this pipeline's own footer
(
🔍 ship-check) — these are the pipeline's own prior replies, skip them
Proof-of-work listing. For each non-pipeline comment found, output a
one-liner before evaluating:
Issue comment #<id> (<author>, <classification>): <first-line summary>
Review body #<id> (<author>, <classification>): <first-line summary>
This listing is mandatory — it proves you fetched and read every comment.
If the listing is empty, state "0 issue comments, 0 review bodies with
findings" explicitly.
Review bodies are NOT summaries — parse them for embedded findings.
Bot review bodies routinely contain findings that were NOT posted as inline
threads — nitpicks, low-value suggestions, and "Overall Comments" that the
bot triaged below its inline-comment threshold. These body-only findings
are invisible to the 2c thread pass and are the most commonly missed class
of bot feedback. A review body is never pure boilerplate if it contains
code suggestions, diff blocks, or file-specific recommendations.
How to parse review bodies:
- Look for the "Prompt for AI agents" section (or equivalent). Bots
like CodeRabbit include a machine-readable flat list of ALL findings —
both inline-thread and body-only — formatted specifically for AI
consumption. This is the canonical finding list. Dedupe against 2c
threads: anything in this list that does NOT have a corresponding
inline thread is a body-only finding that needs evaluation.
- Expand
<details> blocks. Findings are often nested inside
collapsed sections labeled "Nitpick comments", "Suggestions",
"Low-priority", or similar. Do not skip collapsed sections — they
contain actionable items the bot deliberately included.
- Ignore the bot's own severity labels (
Trivial, Low value,
💤) when deciding whether to evaluate. The pipeline evaluates every
finding on its own merits — a "trivial" nitpick may be a real
simplification worth applying. Only the valid/false-positive assessment
matters, not the bot's priority label.
Record the count per review body: "Review #: N body-only findings,
M already covered by inline threads." A disposition of "summary — no
findings" for a review body containing specific code suggestions or diff
blocks is a miss — this is the primary failure mode for review body
handling.
Dedupe against 2c — a finding that also exists as an inline thread is
handled once, in the thread flow. Skip items already handled: compare
against the review/comment IDs recorded on prior passes and against your
own footer-marked replies. Record the IDs of items you handle so
follow-up passes don't re-litigate them.
2e. Merge readiness
Summarize blockers: failing CI, missing approvals, unresolved threads,
unanswered non-thread bot findings, conflicts.
Step 3: Handle findings
Bot threads (qodo, CodeRabbit, etc.)
For each unresolved bot thread, do ALL of these in order:
-
Evaluate: valid finding or false positive? Check against AGENTS.md conventions.
There are only two outcomes: valid or false positive. Do NOT invent a third
category. In particular:
- "False positive" means the finding is factually wrong — the code pattern it
describes does not actually exist, or the behavior it warns about cannot happen.
"The code was already like this" is NOT a false positive — it's a valid finding on
pre-existing code. If the finding describes a real issue (unsafe cast, missing
validation, incorrect type), it is valid regardless of when the code was written or
why it appears in the diff.
- "Pre-existing" is not a reason to skip. If the PR touches the code path a
finding describes — even if the underlying pattern existed before — it is in scope.
A PR that widens a filter or adds a code path inherits the obligations of that path.
- Formatter-exposed code is in scope. When prettier, eslint --fix, or any
formatter brings pre-existing lines into the diff, and a bot flags something real
about those lines, that is a valid finding — not a false positive. The formatter
created an opportunity to fix it. Evaluate fix effort the same way you would for any
valid finding: if the fix is straightforward, fix it now.
- "Out of scope" is not yours to declare. Only the user decides scope. If a
finding is valid and the fix is reasonable effort, fix it. If it's genuinely high
lift (major refactor, new async boundary, architectural change), describe the effort
and ask the user — don't defer it unilaterally.
- "Cross-cutting" requires a count. Before dispositioning a valid finding as a
cross-cutting refactor or too large, grep the actual sites and state the numbers:
files touched, import sites, net line change. A contained, net-neutral move — a
handful of files, no interface or behavior change — is reasonable effort; do it
now. (A fixture-consolidation finding dispositioned "valid but out of scope" was
implemented by the author 4 minutes later: 4 import sites, net-neutral diff.)
- "Belongs in a separate PR" is not a valid disposition. Assess the lift. If
it's a few lines, fix it now. If it's large, present the tradeoff to the user.
-
Report as a one-liner before acting:
Thread 1 (callback naming): Valid — AGENTS.md requires descriptive params → fixed
Thread 2 (broken-link guidance): Valid — vault_find_orphans is wrong tool → fixed
Thread 3 (truncated sentence): False positive — line 621 is complete
-
If valid: make the code fix. If the lift is unclear, assess it first (how many
lines? does it need tests? does it change an interface?) and state it in the one-liner.
Fix anything that's reasonable effort; only escalate to the user when the fix is
genuinely high lift.
Never resolve a thread without replying first. A silently resolved thread looks
like the feedback was dismissed without reading. The reply provides context for anyone
reviewing the PR history and is how the reviewer (human or bot) knows their comment
was addressed.
Claude threads (human account with Claude footer)
Handle the same way as bot threads -- evaluate, reply, fix if valid, resolve. These are
from another Claude instance and do not require user approval. Include in your reply
that you're addressing feedback from another Claude session, e.g.:
"Addressed -- [description]. (Responding to Claude-authored review.)\n\n---\n🔍 ship-check · pr-monitor · MODEL_ID"
Bot and Claude findings without a thread (review bodies, PR-level comments — from 2d)
Evaluate each body-only finding exactly like a bot thread — same
valid/false-positive rules, same fix-or-escalate bar. This includes
nitpicks and suggestions embedded in review bodies — items the bot
triaged below its inline-thread threshold but still reported. They are
findings, not decoration.
There is no thread to resolve, so the disposition must land on the PR as
a reply instead: post one gh pr comment covering the body-only findings
you handled — name each finding by its review ID and description, state
fixed/intentional with the reasoning, include the same attribution footer.
A finding answered nowhere on the PR is indistinguishable from one that was
never read.
Human findings without a thread (issue comments from 2d classified as Human)
Present each to the user — the same way you handle human review threads.
Quote the comment text and author. Do NOT auto-resolve, auto-reply, or
dismiss human issue comments without explicit user approval. If the user
provides a response, reply on their behalf via gh pr comment (with the
attribution footer).
Human threads
Present each to the user. Do NOT auto-resolve human comments without explicit approval.
If the user provides a response, reply on their behalf and resolve.
After handling all threads
If you fixed any code: stage, commit, push. Then go to Step 4 -- this is mandatory.
If no fixes were needed and no code was pushed: go to Step 5.
Step 4: Follow-up check after pushing (MANDATORY)
You are NOT done. Bot review tools re-analyze the entire PR after every push.
New comments arrive within 2-5 minutes. If you skip this step, those new comments go
unresolved and you have failed the task.
"CI is green right now" means nothing if you just pushed -- bots haven't run yet.
Do NOT go to Step 5 without completing at least one follow-up pass after the last push.
Procedure
-
Tell the user: "Pushed fix(es). Waiting for bot reviews (~3 min)..."
-
Schedule a follow-up check. ScheduleWakeup is available in all Claude Code
sessions — /loop context is NOT a prerequisite. Call it:
ScheduleWakeup(delaySeconds: 180, reason: "waiting for bot reviews after push",
prompt: "/pr-monitor")
-
On wake: re-run Step 2 (all five checks). Compare the unresolved thread count
to what it was before pushing, and compare 2d's review/comment IDs against the
ones you've already handled.
-
New unresolved threads or new non-thread bot findings exist -- go to Step 3
(reply, fix, resolve, push). If Step 3 pushes more code, return here and repeat
Step 4 from the top.
-
Nothing new -- go to Step 5.
If ScheduleWakeup is not available
ScheduleWakeup is available in all Claude Code sessions. The fallback below applies
only when the ScheduleWakeup tool call itself returns an error — not when you
reason it "shouldn't be needed" or "the pipeline is finishing." If in doubt, call it —
a rejected tool call is cheap, a missed bot comment is not.
Never substitute your own judgment for this step. "CI was already green before I pushed"
and "bots already commented" are not reasons to skip — bots re-analyze the entire PR
after every push.
If ScheduleWakeup genuinely errors (tool not found, permission denied):
- Run an immediate re-check (Step 2) right after pushing -- this catches fast bots.
- If no new comments yet, tell the user: "Bot reviews typically take 2-5 minutes. Run
/loop /pr-monitor for continuous monitoring, or say 'check' when you want me to
look again."
- When the user responds, re-run Step 2.
Step 5: Final report
Prerequisites -- ALL must be true before you may report:
- All CI checks passing (or only known-flaky / unrelated failures)
- All bot threads resolved (each one replied to before resolving)
- All non-thread findings (2d: review bodies, PR-level comments — bot, Claude, AND
human) evaluated and replied to on the PR (human findings presented to user)
- Issue comment coverage check: the number of issue comments evaluated plus
pipeline-own replies equals the total issue comment count on the PR. If counts
don't match, re-fetch with
--paginate before proceeding
- If code was pushed during this run: at least one follow-up check (Step 4) completed
after the most recent push with no new unresolved threads
- No new unresolved threads in the most recent status pass
- Pipeline context only (ship-check Phase 5): the pre-merge delta review has run
against the current head — the diff from the last phase-reviewed SHA was checked,
and if substantive, a delta review was dispatched and completed (see the ship-check
skill's "Pre-merge delta review" section). Fixes this monitoring loop wrote are part
of that delta, not exempt from it.
If any prerequisite is not met, go back to the relevant step.
Report:
PR #<number> status:
- CI: all passing / N failing (names)
- Reviews: approved / pending / changes-requested
- Bot threads: all resolved (N handled, all replied to)
- Non-thread findings: N handled (review bodies / PR comments — bot: A, Claude: B, human: C)
- Claude threads: all resolved (N handled, all replied to)
- Human threads: N unresolved (listed above)
- Issue comment coverage: N/M evaluated (pipeline replies excluded)
- Verdict: merge-ready / blocked by [specific blocker]
After reporting, continue monitoring — do not stop. "Merge-ready" is a status
report, not a termination signal. The user will say stop when they're done. If the
user doesn't respond, keep the monitoring loop running.
Continuous monitoring
Continue monitoring after Step 5 — whether invoked via /loop or not:
- Call
ScheduleWakeup with delaySeconds: 240 (stays in prompt cache).
- On each wake, run Step 2. If new findings, handle via Steps 3-4.
- Stop the loop only when:
- The user explicitly says stop (or said "check once" / "one-shot" at invocation)
- The PR is merged or closed
- Never auto-terminate for any other reason — not after clean checks, not after
silence, not because the user hasn't responded. The user may be away and expects
monitoring to keep running until they return.
Pipeline context
When invoked as part of a pipeline (e.g., ship-check Phase 5), the pipeline's other
phases being complete does NOT mean monitoring is done. Phase 5 outlives the pipeline.
Continue monitoring until the user explicitly says stop or the PR is merged/closed.
If you feel pressure to "wrap up" because a reporting template exists — that template
is a status snapshot, not a termination signal. Output the report, then continue the
monitoring loop.