Address review
Work through the open review comments on your own pull request: triage every comment into a table, fix everything that needs no human input, then bring the blockers and the decisions to the human one at a time.
When to use
- "What did the reviewers say and can you fix it?"
- "Address the comments on PR 412."
- "Reply to these threads and mark them resolved."
- "Address the review comments" with no PR named, while sitting in the branch's checkout or worktree.
Do not use this when the user wants to review someone else's PR and leave feedback.
That is the review-pr skill.
Prerequisites
gh auth status # must be logged in
If gh is missing or unauthenticated, stop and tell the user; do not guess at PR content.
Command economy is a hard requirement. Every script here batches into a single API call. Never loop a command per thread, and never dump raw JSON into context.
Workflow
The triage table is built first and drives everything after it. Do not start editing code before the table is complete.
0. Identify the PR
When the user gives a PR URL or number, use it and skip to step 1.
When they do not, resolve the PR from the branch checked out in the current directory,
which works the same in a plain clone and in a git worktree. gh reads the branch of
the working directory it runs in:
gh pr view --json number,title,headRefName,url,state --jq '"\(.number)\t\(.state)\t\(.headRefName)\t\(.title)"'
Confirm the match before doing anything else: state the PR number, title, and branch back to the user in one line, and carry on without waiting for a reply. Then read the slug and number for the scripts:
gh repo view --json nameWithOwner --jq .nameWithOwner
Handle the ambiguous cases rather than guessing:
| Situation | Do |
|---|---|
| No PR for this branch | Say so and stop. Do not open one, and do not fall back to another branch's PR. |
| The PR is closed or merged | Say which, and ask whether to continue before fetching comments. |
| Several PRs share the head branch | List them with number, title, and base, and ask which one. |
| Not inside a git repo | Ask for the PR URL or number. |
| Detached HEAD | There is no branch to match. Ask for the PR URL or number. |
See references/gh-commands.md for the worktree details and the fallback when gh cannot
infer the repository.
1. Fetch the open review
bash scripts/open_review.sh <owner/repo> <n>
One call returns both kinds of open comment:
REVIEWlines: the overall body of each non-approving review (CHANGES_REQUESTED,COMMENTED,DISMISSED,PENDING).THREADlines: unresolved inline threads, either line-level (path:41) or file-level (path:0).
An approved review carries no open ask, so the script drops it. Resolved threads are
dropped too; pass --all only when you need to re-read something already closed, and
--full when a clipped body is genuinely ambiguous.
If the script returns nothing, there is nothing open. Say so and stop.
2. Fill the comment column
Put every returned comment into the table, one row per distinct ask. A single review body that raises three separate points becomes three rows. A thread whose later comments are just back-and-forth on one point stays one row.
| comment | severity | needsChanges | anyBlockers | anyDecisions |
|---|
Columns:
- comment — the ask in your own words, plus its location:
src/auth.ts:41,src/auth.ts (file), orreview body (bob). Keep the thread ID with the row. - severity —
Blocker|Suggestion|Nitpick|Praise. Seereferences/triage-table.md. - needsChanges —
trueif resolving it requires a code change, elsefalse. - anyBlockers —
nilwhen nothing blocks it, otherwise a detailed list of what does. - anyDecisions —
nilwhen no human choice is needed, otherwise a detailed list of the choices.
At this step, fill only the comment column. Leave the rest empty.
3. Mark the praise rows
Go through the table and find the comments that only compliment the change and ask for nothing. Fill those rows out completely and immediately:
severity: Praise, needsChanges: false, anyBlockers: nil, anyDecisions: nil.
A comment that praises and then asks for something is not praise. Classify it by the ask.
4. Investigate everything else against the codebase
For each remaining row, read the actual code the comment points at, plus the code around it, before classifying. A comment is only understood once you can say what the current behavior is and what the reviewer wants instead.
Then fill in severity, needsChanges, anyBlockers and anyDecisions.
- A blocker is something outside this comment that must land first: a dependency that is not merged, a missing API, an unreleased upstream fix, another comment's change that this one sits on top of.
- A decision is a choice a human must make because the codebase and the PR context do not settle it: naming that has no precedent, a behavior tradeoff, scope, anything with two defensible answers.
- If the codebase or the PR discussion already answers it, it is not a decision.
Follow the existing convention and record
nil.
Show the completed table to the user before doing anything in step 5.
5. Dispatch the clear rows
Take every row where anyBlockers is nil and anyDecisions is nil and
needsChanges is true. These need no human input, so start them now.
Order them by severity first, Blocker then Suggestion then Nitpick. Within one
severity level, do the fastest first. Severity always beats easiness: never start a
nitpick before an unstarted blocker of the same clearance.
If an orchestration or agent-swarm capability is available, give each row its own worker
so they run in parallel. Otherwise do them inline, in the same order. See
references/dispatch.md for what each worker must be told and how to keep parallel edits
from colliding.
Rows where needsChanges is false need no worker. Reply directly with the explanation
and resolve.
6. Report the blockers while that work runs
Do not wait for the workers. While they run, list every row with a non-nil
anyBlockers to the human:
- what the comment asks for,
- exactly what is blocking it,
- what would unblock it.
If a blocker corresponds to a tracked item, link it. Check whichever tracker the user is actually authenticated for rather than assuming:
gh issue list --search "<keywords>" --state open --limit 5
gh pr list --search "<keywords>" --state open --limit 5
Use the Jira or Linear tooling instead when that is what is connected. If no tracker is available, say the blocker is untracked rather than inventing a ticket.
7. Ask the decisions, one at a time
After the blocker list, work through the rows with non-nil anyDecisions. Ask about
one decision, wait for the answer, then ask the next. Never batch them into a wall of
questions.
Each question must stand on its own for a reader with no context on the product:
- The problem — what is wrong or undecided right now, in plain words.
- The context — how the code behaves today and why the reviewer raised it.
- The options — each realistic choice, with its consequence.
- The tradeoff — what each option costs, and your recommendation with a reason.
No jargon, no unexplained internal names, no assumed knowledge of the PR. See
references/asking-decisions.md for the shape of a good question.
8. Act on each answer immediately
The moment a decision is answered, or the user says a blocker is now cleared, that row becomes clear. Start it right away, in its own worker if orchestration is available, while you ask the next decision question. Do not queue answered work until the end of the interview.
Re-apply the step 5 ordering as rows unblock: a newly cleared blocker jumps ahead of an in-queue nitpick.
9. Park what stays unresolved
If the user says a decision will be made later, or that a blocker stays for now, or to ignore something, do not resolve that thread. Keep a running parked list holding the comment, its thread ID, its severity, and exactly what it is waiting on. Carry it to the end of the task.
10. Close out
When every clear row is done:
Run the project's checks (tests, lint, build). Never reply "fixed" without evidence.
Commit and push.
git add -A && git commit -m "<what changed and why>" && git pushReply and resolve each addressed thread in one call.
bash scripts/reply_thread.sh <threadId> "Fixed in <sha> — <what changed>." --resolveBatch already-answered threads instead of looping:
bash scripts/resolve_thread.sh <id1> <id2> <id3>If the parked list from step 9 is non-empty, record it where the team will see it: add a section to the PR description naming each parked comment and what it waits on, and add the same note to the tracked issue when one exists.
gh pr edit <n> --body-file <file>Tell the human plainly which items are still undecided or still blocked, and that the PR is not fully addressed until those are settled.
Reply style
- Say what changed and where, in one line:
Fixed in a1b2c3d — added the null guard in auth.ts:41. - Praise rows get a resolve with no reply, or a one-word acknowledgement. Do not write a paragraph back at a compliment.
- Disagreeing is fine, but give the reason, not just a refusal.
- No filler ("Great catch!", "Thanks so much!"). Reviewers read dozens of these.
- Never mark a thread resolved with a reply that promises a future fix.
Rules
- When no PR is named, resolve it from the current branch or worktree and say which PR you picked. Never open a PR that does not exist, and never fall back to a different branch's PR.
- Build the whole table before changing any code. No edits during triage.
- Never resolve a thread you did not address, and never resolve another reviewer's thread on a disagreement.
- Never resolve a parked row. Parked means unresolved, by definition.
- Severity ordering beats easiness when choosing what to start next.
- Ask decision questions one at a time, and never let an answered decision sit idle while the interview continues.
- Do not invent a decision for something the codebase already settles; follow the existing convention instead.
- Never force-push a shared PR branch without the user explicitly asking.
- Never merge or close the PR unless the user asked for that exact action.
- One API call per job. Use the batching scripts; never loop per thread, and never
re-fetch what
open_review.shalready returned. - Scope each edit to the comment that prompted it. Do not opportunistically refactor.
- No vendor or AI attribution in replies, commit messages, or PR text.
References
Load these on demand, not up front.
references/triage-table.md— how to classify severity, what counts as a real blocker versus a real decision, and a worked example table. Read at steps 2 to 4.references/dispatch.md— worker briefs, ordering, and avoiding collisions between parallel edits. Read at step 5.references/asking-decisions.md— the four-part question shape with a full example. Read at step 7.references/gh-commands.md— batchedgh/GraphQL recipes for reviews, threads, replies, and resolution state, plus resolving the PR from the current branch or worktree. Read at step 0, or when a command needs changing.
Scripts
| Script | Calls | Purpose |
|---|---|---|
scripts/open_review.sh |
1 | Open review bodies and unresolved threads; --all, --full |
scripts/reply_thread.sh |
1 | Reply, plus --resolve in the same call |
scripts/resolve_thread.sh |
1 | Resolve/unresolve many IDs in one mutation; reads stdin |
To review someone else's PR and leave feedback, use the review-pr skill.