agentleFS
amntoppoamntoppo/MasterDeck-Terminal/skills/babysit-pr/SKILL.md

babysit-pr

Skill4 starsChanged 25 days agoMITView on GitHub

Pre-PR self-review plus post-create review-comment babysitting. Use before creating a PR (review the branch diff, fix findings, write the self-review marker) and after creating one (poll review threads, fix, reply, resolve, until merged or all threads resolved). Optional: MasterDeck has its own PR watch and self-review gate, so run it by hand (/babysit-pr) for the deeper pass, or wire its hooks per workspace in .claude/settings.local.json.

  • Commits and pushes

55 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use leaving-pr-comments, sol-loop and 10 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • scottwaterSkill

    leaving-pr-comments

    Format comments an agent posts on GitHub on the user's behalf. Use whenever leaving a PR comment, review comment, or reply.

    Also in 9 projects that use this

  • scottwaterSkill

    sol-loop

    Review a change with GPT Sol in a loop until both agents agree. Use when the user says sol loop, or when another skill asks for a Sol review.

    Also in 7 projects that use this

  • Nexus-Organization-InfosecSkill

    full-stop

    Shut down the Full Control phone-bridge completely — end Claude's watch loop, stop the bridge server (server.py started via ensure_server.ps1), and mark Claude offline in the app. Use whenever the user invokes /full-stop or says "stop full control", "stop the watch loop", "shut down the bridge/server", or "stop the ps1". Counterpart of [[full-control]], which starts everything.

    Also in 9 projects that use this

  • assisjpSkill

    file-pr

    Prepare and open or update an authorized pull request with a concise problem-first description and verifiable evidence. Use when the user asks to create, file, open, or update a PR. Do not use merely to review a diff or when publication is not authorized.

    Also in 13 projects that use this

  • shhacSkill

    full-send

    Complete an explicitly assigned task autonomously through parallel agents, peer-reviewed decisions, and automatic retries of temporary blockers. Use only when the user explicitly invokes Full Send.

    Also in 8 projects that use this

  • assisjpSkill

    takeover

    Take over an existing branch, pull request, worktree, or agent handoff while preserving the original goal, contributor credit, and concurrent work. Use when the user asks to take over, finish, rescue, or continue someone else's implementation. Do not use to replace a clear request to start new work.

    Also in 4 projects that use this

  • +1 more from scottwater

The file

Show its rules and text

We split this skill into the rules it gives an agent

15 rules. 0 are common, 8 are distinctive, and 7 are very specific to this project.

  • 0

    Common

    Most of the 54 edited versions we read keep these.

  • 8

    Distinctive

    This author's own choices. Worth a look.

  • 7

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    When the repo has a CI auto reviewer — typically a Claude Code GitHub Action such as .github/workflows/claude-code-review.yml, on …

    line 25
  2. Distinctive:

    Write the marker, keyed to the final HEAD:

    line 59
  3. Distinctive:

    Create the PR. The marker silences the soft gate for this HEAD.

    line 64
  4. Distinctive:

    Do not build or typecheck as part of the review — CI runs those separately.

    line 71
  5. Distinctive:

    Beyond the CI bar (optional): If your workspace defines specialist reviewer agents (e.g.

    line 74
  6. Very specific:

    Use the Monitor tool with timeout_ms: 1800000 (30 min, the maximum).

    line 105
  7. Distinctive:

    Preconditions: Only push when the PR's head is in this org (a fork's branch is not yours to push) and the working tree was clean before you …

    line 270
  8. Very specific:

    Run the repo's own checks before pushing (e.g.

    line 273
  9. Very specific:

    Check you have not already nudged for this stuck run: list recent issue comments and look for an @claude review posted *after* that …

    line 301
  10. Distinctive:

    Not yet nudged → post it:

    line 303
  11. Very specific:

    The PR-MERGED: line ends that PR's watch: When it arrives, render this banner as the final message for that PR — a fenced code block, …

    line 317
  12. Distinctive:

    Never resolve a thread you argued against.

    line 440
  13. Very specific:

    Before resolving, verify against HEAD — isOutdated alone is not proof (an unrelated push also outdates threads).

    line 466
  14. Distinctive:

    If a comment demands a design change beyond the PR's scope, stop the loop and surface it to the user instead of implementing it.

    line 475
  15. Very specific:

    Never post @claude review except on a STALLED-REVIEW wake, and at most once per stuck run (see above).

    line 482

“Common” is measured: kept by at least 6 in 10 of the 54 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

babysit-pr — self-review before, comment babysitting after

Two phases. The PreToolUse hook on gh pr create asks for the Prep phase when no self-review marker exists for HEAD; the PostToolUse hook asks for the Monitor phase once the PR exists. Either phase can also be run by hand (/babysit-pr).

This skill is optional. MasterDeck has its own PR watch and a short self-review gate before gh pr create (a step of its Default workflow), so inside MasterDeck nothing needs installing. Outside it, hooks can live in the workspace's .claude/settings.local.json (gitignored, so per machine and per workspace) — not in a shared, committed .claude/settings.json, which at most carries the Bash(gh pr create:*) permission. With no hooks, run /babysit-pr by hand.

All gh commands run from the repo the PR belongs to — check pwd first; if your workspace holds several repos, the marker/branch/PR are per-repo.

Phase 1 — Prep (pre-PR self-review)

Goal: find what CI will find, before the PR exists — so the GitHub review lands clean.

When the repo has a CI auto reviewer — typically a Claude Code GitHub Action such as .github/workflows/claude-code-review.yml, on opened/ready_for_review/reopened, running the marketplace plugin /code-review:code-review --comment — Phase 1 runs that same recipe against the branch diff. (No CI reviewer? Run the same recipe anyway; it is a good bar.) Matching it is the whole point: a different recipe reviewed locally is why CI keeps suggesting changes after a clean local pass.

Run it from the PR's own repo (cd there first). CI checks out that repo alone, so the CLAUDE.md it judges against is the repo's, not the workspace root's.

  1. Diff scope: confirm the base (main/dev/stage — don't guess), then git diff <base>...HEAD.
  2. Collect the CLAUDE.md files CI would read: the repo root one, plus any in the directories the diff touches. Paths only — the lenses read them.
  3. Launch five parallel review agents, one lens each, each returning findings with the reason it flagged them:
    • CLAUDE.md adherence — does the change comply? CLAUDE.md is guidance for writing code, so not every line applies to review.
    • Shallow bug scan — read only the changed hunks, no extra context. Big bugs only.
    • Git history — git log/git blame the modified lines; bugs visible only in light of why the code got that way.
    • Prior PR comments — earlier PRs touching these files, and whether their review comments apply again here. Find them by commit, not by search (gh pr list --search "<path>" searches text and returns nothing for a path — verified):
      git log --format='%H' -10 -- <path> | while read sha; do
        gh api "repos/<owner>/<repo>/commits/$sha/pulls" --jq '.[].number' 2>/dev/null
      done | sort -u        # then: gh pr view <n> --comments
    • Code comments — does the change respect guidance written in the surrounding comments?
  4. Score every finding 0–100 for "is this real", in a separate pass from the one that found it, and drop everything below 80. A CLAUDE.md-based finding only scores high if that CLAUDE.md says so specifically. This filter is why CI's list is short — without it you will fix things CI never asks about.
  5. Fix what survives. Commit normally (no caveman). Re-run once on the new diff, cap 2 passes, and report anything left unfixed before the PR goes up.
  6. Write the marker, keyed to the final HEAD:
    touch ".git/pr-selfreview-$(git rev-parse HEAD)"
    (.git/ is never committed; stale markers are harmless — rm -f .git/pr-selfreview-*.)
  7. Create the PR. The marker silences the soft gate for this HEAD.

Not findings — CI discards these, so fixing them locally buys nothing and costs a pass: pre-existing issues; anything a linter, typechecker, compiler or test would catch; missing test coverage, docs or general code quality (unless a CLAUDE.md demands it); pedantic nits a senior engineer would not raise; intentional changes that are part of the broader change; real issues on lines this PR did not touch; and anything silenced in code on purpose. Do not build or typecheck as part of the review — CI runs those separately. Run the repo's own checks before pushing anyway, just don't report their output as review findings.

Beyond the CI bar (optional). If your workspace defines specialist reviewer agents (e.g. a migration-safety reviewer, per-stack reviewers, a silent-failure hunter, an API contract checker, or a cross-repo regression grep), they catch things CI's five lenses do not (a backend change breaking a separate client repo is invisible to a single-repo review). Run them when the diff touches migrations, payments/billing, or an API contract. Their findings are extra, not part of matching CI: judge them on merit, and never let them lengthen the two-pass cap.

The gate is soft: if the user explicitly said to skip review, create the PR and say the review was skipped on their instruction.

⚠ CI reviews the PR as opened, not what you reviewed. Every commit pushed after the local pass is unreviewed by Phase 1 — re-run it (or expect CI to find what you skipped).

⚠ @claude review is a different, noisier reviewer. That comment triggers claude.yml, a free-form run with no 80-point filter that also reports reuse, simplification and efficiency cleanups. Phase 2's stall nudge uses it deliberately (it is what unsticks a hung review), but do not treat its extra suggestions as a Phase 1 miss.

Phase 2 — Monitor (babysit review comments)

Goal: keep resolving reviewer feedback until the PR is merged/closed or nothing is open.

Get coordinates once:

gh pr view --json number,state,headRefName,url

Arm the monitor (event-driven, not scheduled wakeups)

Use the Monitor tool with timeout_ms: 1800000 (30 min, the maximum). The script polls GitHub every ~45s inside the shell (GitHub pushes nothing to a CLI), but the session wakes only when a new comment or a terminal state appears — reaction in under a minute, no idle wakeups. Do not use ScheduleWakeup//loop for this.

⚠ There is no persistent: true. Every monitor is killed at timeout_ms and you get an expiry notice naming the event count. A PR outlives that, so re-arm on each expiry (same command, unchanged) until the PR is merged/closed or the user says stop, and say so when you do. An expiry is not a terminal state: only PR-MERGED/PR-TERMINAL ends a watch. Re-arming is safe — the script remembers what it has already emitted in $SEEN_FILE (see below), so a fresh arm does not replay the whole comment history as new wakes.

Multiple PRs: arm one monitor per PR — each Monitor call is an independent background watch, so babysitting several PRs at once is just several arms. Every event line is prefixed <repo>#<num> so wakes are attributable; on a wake, act only on the PR named in the event. List/stop individual watches via /tasks or TaskStop.

Fill in owner/repo/number, then arm. No author filter — comments authored by bots or Claude-identity accounts (claude, claude[bot], github-actions[bot]) must fire too, and PRs are often created under those identities. The comm dedup is what prevents loops: each comment wakes the session exactly once, including replies this session posted itself — on such a wake, recognize your own reply and do nothing.

owner=<owner>; repo=<repo>; pr=<num>
tag="$repo#$pr"
# Through master's shared GitHub cache when it is installed (`ghc`): sessions watching the same
# PR share one fetch every ~40s, and every caller backs off together on a rate limit.
if command -v ghc >/dev/null 2>&1; then gh() { ghc --ttl 40 "$@"; }; fi
meta=$(gh pr view "$pr" --repo "$owner/$repo" --json title,url --jq '"\(.title)\t\(.url)"')
title=${meta%%$'\t'*}; url=${meta#*$'\t'}
# Survives re-arming after a timeout expiry: without it, a fresh arm replays every
# existing comment as a new wake.
SEEN_FILE="${TMPDIR:-/tmp}/babysit-pr-$owner-$repo-$pr.seen"
seen=$(cat "$SEEN_FILE" 2>/dev/null || true)
while true; do
  # One GraphQL call per poll: state, mergeability and review threads together.
  pull=$(gh api graphql -f query='
      query($owner:String!, $repo:String!, $pr:Int!) {
        repository(owner:$owner, name:$repo) { pullRequest(number:$pr) {
          state mergeable baseRefName
          reviews(last:30) { nodes { databaseId state submittedAt author { login } body } }
          reviewThreads(first:100) { nodes {
            isResolved
            comments(last:1) { nodes { databaseId updatedAt author { login } body } } } } } } }' \
      -f owner="$owner" -f repo="$repo" -F pr="$pr") || { sleep 60; continue; }
  state=$(jq -r '.data.repository.pullRequest.state // empty' <<<"$pull")
  [ -n "$state" ] || { sleep 60; continue; }
  case "$state" in
    MERGED) echo "PR-MERGED: \"$title\" $url"; exit 0;;
    CLOSED) echo "PR-TERMINAL: $tag CLOSED"; exit 0;;
  esac
  # One REST call for the PR's conversation comments, read twice below.
  comments=$(gh api "repos/$owner/$repo/issues/$pr/comments") || { sleep 60; continue; }
  cur=$( {
      jq -r --arg tag "$tag" '
        .data.repository.pullRequest.reviewThreads.nodes[]
        | select(.isResolved | not) | .comments.nodes[]
        | "\($tag) THREAD \(.databaseId) \(.updatedAt) \(.author.login): \(.body | gsub("\\s+"; " ") | .[0:180])"' <<<"$pull" ;
      jq -r --arg tag "$tag" '
        .[] | "\($tag) ISSUE \(.id) \(.updated_at) \(.user.login): \(.body | gsub("\\s+"; " ") | .[0:180])"' <<<"$comments" ;
      # Review summaries: the note a reviewer leaves with a review, and every Changes requested
      # (even without a note). Reviews that only carry code threads are covered above.
      jq -r --arg tag "$tag" '
        .data.repository.pullRequest.reviews.nodes[]
        | select(.state == "CHANGES_REQUESTED" or ((.body // "") | test("\\S")))
        | "\($tag) REVIEW \(.databaseId) \(.submittedAt) \(.author.login) \(.state): \((.body // "") | gsub("\\s+"; " ") | .[0:180])"' <<<"$pull" ;
      # Stalled Claude review: a claude[bot] status comment that never reached "Claude
      # finished", still shows an unchecked task box (or "is reviewing"), and has not been
      # edited for 10+ minutes. The "finished" test comes FIRST and is what makes this
      # usable: the bot routinely finishes while leaving boxes unchecked, so an unchecked
      # box alone is not a stall.
      jq -r '
        map(select(.user.login | test("^claude(\\[bot\\])?$"; "i")))
        | .[] | select((.body | test("claude finished|finished the review"; "i")) | not)
        | select((.body | test("(^|\n)\\s*- \\[ \\]")) or (.body | test("is reviewing"; "i")))
        | select((now - (.updated_at | fromdateiso8601)) > 600)
        | "STALLED-REVIEW \(.id)"' <<<"$comments" | sed "s/^/$tag /" ;
      # Merge conflicts. Only CONFLICTING counts: GitHub computes mergeability lazily and
      # answers UNKNOWN until it has (asking is what triggers the recompute), so UNKNOWN
      # means "ask again next poll", never "no conflict". The line is stable, so one wake
      # per conflict episode; it reappears if the base conflicts again later.
      jq -r '.data.repository.pullRequest | select(.mergeable=="CONFLICTING") | "CONFLICT with \(.baseRefName)"' <<<"$pull" \
        | sed "s/^/$tag /" ; } \
      2>/dev/null | sort ) || { sleep 60; continue; }
  comm -13 <(printf '%s\n' "$seen") <(printf '%s\n' "$cur")
  seen=$cur
  printf '%s\n' "$cur" > "$SEEN_FILE"
  sleep 45
done

The three kinds of line are THREAD (a comment on the code), ISSUE (an ordinary comment on the PR) and REVIEW (a review's summary, e.g. CHANGES_REQUESTED: please split this). All three are feedback to act on; for a REVIEW line read the full review with gh api repos/<owner>/<repo>/pulls/<num>/reviews/<id> when the line is cut short.

Each poll costs one GraphQL and one REST call (it was three GraphQL and two REST), and with ghc installed, sessions watching the same PR within ~40s share them.

$SEEN_FILE is keyed by owner/repo/PR, so parallel watches never share state. Delete it (or the whole babysit-pr-*.seen set) only to deliberately replay a PR's comments.

⚠ gh api --jq takes a bare expression and rejects --arg ("accepts 1 arg(s), received 4") — the tag is shell-interpolated into a double-quoted jq string for that reason. A silent 2>/dev/null failure here looks exactly like "no comments yet", so sanity-check the query once by hand before trusting a quiet monitor.

Edits fire too. Each compared line carries the comment's updatedAt, so an in-place edit — e.g. the Claude bot updating its first status comment with new Findings instead of posting a new comment — changes the line and wakes the session like a new comment. On such a wake, fetch the FULL current body (the event line is truncated to 180 chars): gh api repos/<owner>/<repo>/issues/comments/<id> for an issue comment, or the thread GraphQL below — then act on the findings it now lists.

description: "review comments on <repo>#<num>". Each emitted line is one new or newly-edited unresolved comment.

Merge conflict → resolve it

The monitor emits, once per conflict episode:

<repo>#<num> CONFLICT with <base>

Resolve it by merging the base into the PR branch — never rebase, because the skill's no-force-push rule stands and a rebase would need one.

cd <the PR's repo>                      # multi-repo workspace? check pwd
gh pr checkout <num>                    # lands on the PR branch, tracking the PR
git fetch origin <base>
git merge origin/<base>                 # conflicts stop here
git diff --name-only --diff-filter=U    # exactly what is conflicted

Resolve each file, then git add it, git commit (default merge message is fine), run the repo's checks, and git push. Confirm GitHub agrees before reporting success — it answers UNKNOWN for a moment after the push:

gh pr view <num> --repo <owner>/<repo> --json mergeable --jq .mergeable   # want MERGEABLE

Then reply on the PR in one line saying the base was merged in and conflicts resolved.

Resolve yourself only where intent is unambiguous:

ConflictDo
Both sides added separate items (imports, routes, test cases, list entries)Keep both, in a sensible order
Base changed a file this PR never meant to touchTake the base side
Only formatting/whitespace differsTake the base side, re-apply this PR's real change
Lockfiles (uv.lock, package-lock.json, bun.lock*, Podfile.lock)Never hand-merge. Take base, re-run the tool (uv lock, npm install, bun install) and commit the result
Generated files (mirrors, codegen output)Never hand-merge — take base and re-run the repo's generator
Database migrations (e.g. Alembic: migrations/, two heads, down_revision)Stop. Hand to the user (or a migration-safety reviewer if the workspace has one); a wrong down_revision chain breaks the deploy silently

Stop and tell the user — do not guess — when: both sides changed the same logic and only the author knows which wins; the conflict is in a migration, a payments/billing path, or generated native code; the merge touches files outside this PR's scope; or the same PR conflicts a third time (the base is moving faster than the PR — it needs a human, or the PR needs splitting). Say what conflicted and what you left unresolved; leave the merge in progress or git merge --abort, but never invent a resolution to clear the wake.

Preconditions. Only push when the PR's head is in this org (a fork's branch is not yours to push) and the working tree was clean before you started — a conflict resolution on top of unrelated local edits is unreviewable. If the tree is dirty, say so and stop. Run the repo's own checks before pushing (e.g. uv run pytest, npm test, bun run typecheck), plus any end-to-end verification the workspace provides for changes touching the running stack.

Stalled Claude review → nudge with @claude review

The @claude reviewer posts one status comment ("Claude is reviewing this PR" + a Tasks checklist) and edits that same comment as it ticks boxes, finishing with "Claude finished the review". Sometimes it stops partway and the comment sits with - [ ] boxes open forever (or at the bare "Claude is reviewing this PR" line, before any checklist is rendered) — the review never lands. Posting @claude review on the PR retriggers it — a status comment that sat unchecked typically finishes within minutes of a manual @claude review.

The monitor line above emits, once per stuck comment:

<repo>#<num> STALLED-REVIEW <comment_id>

That line carries no timestamp, so it wakes the session once per stuck comment, not every poll. On such a wake:

  1. Confirm it is still stuck — the bot may have finished between poll and wake:
    gh api repos/<owner>/<repo>/issues/comments/<comment_id> --jq '.updated_at, .body' | head -20
    Body now says "Claude finished" anywhere → done, no nudge, even if boxes are still unchecked (it often finishes that way).
  2. Check you have not already nudged for this stuck run: list recent issue comments and look for an @claude review posted after that comment's updated_at.
  3. Not yet nudged → post it:
    gh api repos/<owner>/<repo>/issues/<num>/comments -f body='@claude review'
    Say in chat that the review stalled and you retriggered it. The bot's next edit wakes the monitor normally, and its findings are handled like any other review comment.
  4. Already nudged once and it is stuck again → stop nudging and tell the user the reviewer is not completing (with the job link from the comment). Two @claude review comments per stuck run is the cap; a nudge loop spams the PR and burns Actions minutes.

This only fires for claude/claude[bot] comments. A human's unchecked to-do list is never nudged, and a review still actively ticking boxes updates updated_at, so the 10-minute window never sees it.

The PR-MERGED: line ends that PR's watch. When it arrives, render this banner as the final message for that PR — a fenced code block, exactly this shape, with the title and URL on plain lines below the box (never inside it, so long values cannot break the border; keep the emoji out of the box too — emoji width misaligns borders in many terminals):

```
╔══════════════════════════════════════════════╗
║                                              ║
║          P R   M E R G E D !                 ║
║                                              ║
║          GREAT WORK!                         ║
║                                              ║
╚══════════════════════════════════════════════╝
```
🎉 **<title>**
<url>

(PR-TERMINAL: <repo>#<num> CLOSED = closed unmerged; report that plainly, no banner.) Other PRs' watches keep running. Stop a watch early with TaskStop if the user asks, or once its every thread is resolved and no review is pending.

Then, on the same merge wake, arm the Phase 3 deploy watch below (skip it only if the repo visibly has no deploy workflows).

Phase 3 — Deploy watch (after merge)

Goal: after the merge banner, keep watching until the merge commit's GitHub Actions deploy runs finish, then announce the changes are live on the target tier.

Tier = the environment the PR's base branch deploys to, per the repo's workflows — for example dev → dev, stage → stage, main → prod. (Repos that deploy nowhere, such as docs or config-only repos — say so and skip.)

Arm a second Monitor (timeout_ms: 1800000, description "deploy of <repo>#<num>"). A deploy usually finishes inside one window; if it expires first, re-arm it the same way:

owner=<owner>; repo=<repo>; pr=<num>; tag="$repo#$pr"
sha=$(gh pr view "$pr" --repo "$owner/$repo" --json mergeCommit --jq .mergeCommit.oid)
waited=0
while true; do
  runs=$(gh run list --repo "$owner/$repo" --commit "$sha" \
    --json name,status,conclusion 2>/dev/null) || { sleep 60; continue; }
  n=$(jq length <<<"$runs")
  if [ "$n" -eq 0 ]; then
    waited=$((waited+30))
    [ "$waited" -ge 300 ] && { echo "DEPLOY-NONE: $tag no workflow runs for $sha"; exit 0; }
    sleep 30; continue
  fi
  if jq -e 'all(.status=="completed")' <<<"$runs" >/dev/null; then
    if jq -e 'all(.conclusion=="success")' <<<"$runs" >/dev/null; then
      echo "DEPLOY-COMPLETE: $tag $sha"
    else
      jq -r ".[] | select(.conclusion!=\"success\")
        | \"DEPLOY-FAILED: $tag \(.name): \(.conclusion)\"" <<<"$runs"
    fi
    exit 0
  fi
  sleep 30
done

On DEPLOY-COMPLETE, render the live banner (same conventions: fenced block, details below the box, tier name from the base branch):

```
╔══════════════════════════════════════════════╗
║                                              ║
║       C H A N G E S   A R E   L I V E        ║
║                                              ║
║              ON  <TIER> !                    ║
║                                              ║
╚══════════════════════════════════════════════╝
```
🚀 **<title>** deployed (<workflow names>, commit <short sha>)
<url>

On DEPLOY-FAILED: no banner — report which workflow failed, fetch its log (gh run view <id> --log-failed), diagnose, and surface to the user (a deploy fix is beyond this skill's push authorization; do not auto-fix deploy scripts). DEPLOY-NONE: say the repo triggered no workflows for the merge commit and stop.

⚠ Workflow success ≠ endpoint live when part of the stack is configured by hand (e.g. a reverse proxy or API gateway maintained outside CI): a green deploy can still leave a route 404ing. If that applies to your stack, mention this caveat with the banner when the change added or moved a route.

On each wake

A monitor notification means new comment(s) landed. The <repo>#<num> prefix names the PR — with several watches armed, act only on that PR. Then:

  1. Re-read full thread context — the event line is truncated. GraphQL for threads (REST has no thread resolution):
    gh api graphql -f query='
      query($owner:String!, $repo:String!, $pr:Int!) {
        repository(owner:$owner, name:$repo) {
          pullRequest(number:$pr) {
            reviewThreads(first:100) {
              nodes {
                id isResolved isOutdated path line
                comments(first:20) { nodes { databaseId author { login } body } }
              }
            }
          }
        }
      }' -f owner=<owner> -f repo=<repo> -F pr=<num>
  2. Also check requested-changes reviews (gh pr view <num> --json reviews) — not everything arrives as a thread or issue comment.
  3. Handle each thread (below), then let the monitor keep running.

Handling a thread

Per unresolved thread, decide the comment's type:

  • Code defect / requested change → make the fix, commit, push to the PR branch. Reply on the thread saying what changed (commit SHA).
  • Question / judgment call ("why this approach?") → answer on the thread. Do not change code to dodge a question.
  • Out of scope / disagree → reply with reasoning; leave the thread for the reviewer. Never resolve a thread you argued against.

Reply into a thread:

gh api repos/<owner>/<repo>/pulls/<num>/comments/<comment_id>/replies -f body='...'

Resolve only threads you actually addressed (fix pushed, or question answered and the answer is self-evidently sufficient):

gh api graphql -f query='
  mutation($id:ID!) { resolveReviewThread(input:{threadId:$id}) { thread { isResolved } } }' \
  -f id=<thread_node_id>

Stale-thread sweep (auto-close resolved conversations)

On each wake, after handling new comments, sweep the remaining unresolved threads and resolve any conversation that is already settled:

  • The requested change is verifiably in HEAD (the thread's path/line diff shows the fix landed) — typically isOutdated: true after a fix push.
  • The reviewer's last reply acknowledges completion ("done", "thanks", "LGTM", a thumbs-up-style sign-off) with no follow-up ask.
  • The thread duplicates another thread that was fixed and resolved.

Before resolving, verify against HEAD — isOutdated alone is not proof (an unrelated push also outdates threads). If the fix cannot be confirmed in the current code, leave the thread open. Never sweep-resolve a thread with an unanswered question or an unaddressed objection.

Rules

  • Pushing fixes is authorized by this skill's flow — but stay on the PR branch, never force-push, and keep each fix commit scoped to the comment it answers.
  • If a comment demands a design change beyond the PR's scope, stop the loop and surface it to the user instead of implementing it.
  • A push to the PR branch changes HEAD — the self-review marker is per-SHA and does not need refreshing for monitor-phase fixes (the PR already exists).
  • Report at the end of each active wake: threads fixed / answered / left open, and why.
  • A conflict resolution is a merge commit on the PR branch, never a rebase or a force-push, and never a change to the base branch.
  • Never post @claude review except on a STALLED-REVIEW wake, and at most once per stuck run (see above). Do not use it to ask for a re-review of new commits.
  • The monitor emits every new comment regardless of author — bot and Claude-identity authors (claude[bot], github-actions[bot]) included, since PRs are often created under those. A wake for a comment you posted yourself needs no action. If the watch goes noisy or gets auto-stopped for volume, re-arm with a tighter filter.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.