| name | handling-pr-feedback |
| description | Addresses GitHub PR code review comments and CI check failures systematically. Fetches all comments (human + bot, resolved + unresolved), parses bot review bodies, checks CI status, categorizes by priority, and tracks progress. Use when asked to address PR feedback, fix review comments, handle failing checks, or respond to code review. |
PR Feedback Skill
Systematically address GitHub PR code review comments and CI check failures. Validates each item by reading code and logs, presents findings with proposed fixes, waits for confirmation, then fixes one commit per item with replies to exact threads.
Golden Rules
NEVER trust comments blindly. Read the code. Verify the issue exists. Run experiments if unsure.
NEVER fix without confirmation. Present your findings first. Wait for the user to approve.
ONE commit per item. Fix → commit → push → reply to thread (for comments). Then next item.
ALWAYS reply to the exact thread. Not the general PR conversation. Include the commit hash.
ALWAYS check CI status. Failed checks are as important as review comments. Handle them first.
Prerequisites
Before starting, verify gh CLI is installed and authenticated:
gh auth status
gh pr view --json number
If either fails, run the setup script: bash scripts/setup.sh
Workflow
1. Detect PR Context
Run this first. Extract owner, repo, and PR number from the current branch. Use baseRepository for API calls (works correctly with forks):
gh pr view --json number,baseRepository,headRepository,url \
--jq '{
pr: .number,
owner: .baseRepository.owner.login,
repo: .baseRepository.name,
head_owner: .headRepository.owner.login,
head_repo: .headRepository.name,
url: .url
}'
Store these values. All subsequent commands use owner and repo from baseRepository (the upstream repo where the PR lives, not the fork).
If the user provides a specific PR number instead:
gh pr view {user_provided_pr} --json number,baseRepository,url \
--jq '{pr: .number, owner: .baseRepository.owner.login, repo: .baseRepository.name, url: .url}'
2. Check CI Status
Run this early. Failed CI checks often block merging and should be prioritized alongside review comments.
gh pr view --json statusCheckRollup --jq '.statusCheckRollup[] | {name, status, conclusion, detailsUrl}'
Filter to just failures:
gh pr view --json statusCheckRollup \
--jq '.statusCheckRollup[] | select(.conclusion == "FAILURE") | {name, detailsUrl}'
Common check types:
- Lint — code formatting, style violations (gofmt, eslint, etc.)
- Build — compilation errors, type errors
- Test — failing unit/integration tests
- Security — vulnerability scans, secret detection
3. Fetch CI Failure Logs
For each failed check, fetch the logs to understand what failed:
gh api repos/{owner}/{repo}/actions/jobs/{job_id}/logs 2>&1 | tail -200
For lint failures, grep for the actual errors:
gh api repos/{owner}/{repo}/actions/jobs/{job_id}/logs 2>&1 | grep -E "^.*\.(go|ts|js|py):" | head -20
For test failures, look for FAIL or Error patterns:
gh api repos/{owner}/{repo}/actions/jobs/{job_id}/logs 2>&1 | grep -E "(FAIL|Error|error:|panic:)" | head -30
If logs say "run is still in progress", the workflow hasn't finished yet — wait or fetch via the API directly.
4. Fetch All Review Comments
PR comments live in multiple places — fetch ALL of these:
gh api repos/{owner}/{repo}/pulls/{pr}/comments --paginate
gh api repos/{owner}/{repo}/pulls/{pr}/reviews --paginate
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
comments(first: 10) {
nodes {
id
author { login }
body
path
line
}
}
}
}
}
}
}' -f owner="{owner}" -f repo="{repo}" -F pr={pr}
5. Parse Bot Reviews
AI review bots often post structured markdown in the review body. Look for common sections like:
- Actionable comments — required changes (highest priority)
- Nitpick comments — optional style suggestions
- Summary — overview (informational only)
Extract each section and treat as individual items with priority based on section type. Different bots use different formats — adapt parsing as needed.
6. Categorize by Priority
Process all items (CI failures AND comments) in this order:
- CI Failure — Failed checks block merging (fix first)
- BUG — Functional bugs, incorrect logic (fix immediately)
- Actionable — Required changes from reviewers
- Human unresolved — Unresolved threads from human reviewers
- Nitpick — Style/preference suggestions (address if easy)
- Optional — FYI comments, praise, questions
7. Validate Every Item
ASSUME ALL FEEDBACK IS WRONG UNTIL PROVEN OTHERWISE.
Review comments — especially from AI bots — are frequently incorrect, outdated, or misunderstand the code context. CI failures are usually legitimate but may also be flaky or infrastructure-related. You MUST independently verify each item before presenting it to the user.
For CI Failures
- Fetch the actual logs — use the commands from section 3 to get failure details
- Identify the specific error — file, line, and error message
- Read the code at the referenced location
- Determine the fix — is it a simple formatting issue? A real bug? A flaky test?
- Run locally if possible — reproduce the failure to confirm your fix works
Form your assessment for each CI failure:
- ✅ Real failure — code has an issue that needs fixing
- 🔁 Flaky — intermittent failure, may need retry or test fix
- 🏗️ Infrastructure — CI environment issue, not code-related
For Review Comments
For EVERY comment, do ALL of the following:
- Read the actual code at the referenced file and line — do not rely on the comment's description
- Understand the surrounding context — read the function, check imports, understand the data flow
- Verify the issue actually exists — does the code have the problem described? Or did the bot misunderstand?
- Check if already fixed — look at recent commits; the comment may reference outdated code
- Evaluate the suggested fix — would it actually solve the problem? Would it break something else?
- Run experiments if uncertain — write a quick test, run the code, check behavior. Do not guess.
Form your own independent assessment for each comment:
- ✅ Valid issue — you verified the code has the problem described
- ❌ Not an issue — you verified the code is correct; reviewer/bot misunderstood
- ⚠️ Unclear — you could not determine validity; need user input
- 🔄 Already addressed — you verified it was fixed in a subsequent commit
Examples of common false positives from bots:
- "This function doesn't handle errors" — but it does, via a defer or wrapper
- "Missing nil check" — but nil is impossible due to earlier validation
- "Unused variable" — but it's used in a conditional compilation or generated code
- "Race condition" — but access is serialized by design
If you cannot confidently validate an item, say so. Do not blindly accept or reject it.
8. Present Findings and Wait for Confirmation
STOP. DO NOT FIX ANYTHING YET.
After validating all items (CI failures AND comments), you MUST present your research findings to the user and wait for explicit confirmation before making any changes. Never skip this step unless the user has explicitly said "fix everything without asking" or similar.
Your presentation MUST include for EVERY item:
- The item — CI check name or comment (who said it, what file/line, what they're asking for)
- Your research — what you found when you read the code/logs and ran experiments
- Your verdict — ✅ Valid, ❌ Not an issue, ⚠️ Unclear, 🔄 Already fixed, 🔁 Flaky, or 🏗️ Infrastructure
- Your reasoning — why you reached that verdict (cite specific code/logs)
- Your proposed fix — if valid, exactly what you plan to change
Then ask: "Which of these should I address?"
Example presentation format:
I analyzed 1 CI failure and 4 review comments on PR #123. Here are my findings:
---
**1. CI FAILURE: Lint** (gofmt)
> handler_test.go:76:1 - File is not properly formatted
📖 **My research:** I fetched the lint job logs and found a gofmt error. The file
has trailing blank lines at the end.
✅ **Verdict: Real failure**
💡 **Proposed fix:** Run `gofmt -w handler_test.go` to fix formatting.
---
**2. BUG: api_test.go:142** (from bot)
> "Function returns wrong status code for error case"
📖 **My research:** I read the handler at api.go:89. The test expects 400 but the
code returns 500 for validation errors. The bot is correct.
✅ **Verdict: Valid issue**
💡 **Proposed fix:** Change the error response to return http.StatusBadRequest.
---
**3. Human: "Add timeout to HTTP client"** (from @reviewer)
📖 **My research:** I checked client.go:45. The http.Client has no timeout set,
which could cause hanging requests. Valid concern.
✅ **Verdict: Valid**
💡 **Proposed fix:** Add `Timeout: 30 * time.Second` to the client config.
---
**4. Human: "Use context for cancellation"** (from @maintainer)
📖 **My research:** I checked the Process function. It already accepts and uses
a context parameter (added in commit def456).
🔄 **Verdict: Already addressed**
💡 **Proposed action:** Reply to confirm this is done.
---
**5. Nitpick: utils.go:42** (from bot)
> "Consider using constants instead of magic numbers"
📖 **My research:** The code uses `1024` as a buffer size. This is a standard
power-of-2 buffer size used only once. Adding a constant adds no value.
❌ **Verdict: Not an issue**
💬 **Reasoning:** False positive. The value is self-explanatory in context.
---
**Summary:**
- 🔴 CI failures to fix: #1
- ✅ Valid issues to fix: #2, #3
- 🔄 Already done (just reply): #4
- ❌ Not an issue (skip): #5
**Which should I address?**
Wait for the user to respond. Do not proceed until they confirm.
9. Track Progress
After user confirms which items to address, create a todo list:
- [ ] CI: Fix gofmt formatting in handler_test.go
- [ ] BUG: Fix status code in error response
- [ ] Human: Add timeout to HTTP client
- [ ] Human: Reply confirming context is implemented
Mark items completed as you go.
10. Address Confirmed Items
CRITICAL RULES:
- ONE COMMIT PER COMMENT. Never batch multiple feedback items into a single commit. Each comment gets its own atomic commit.
- ALWAYS REPLY TO THE EXACT COMMENT THREAD. After pushing, reply to the specific comment thread (not the general PR conversation) with the commit hash.
For each approved item, follow this exact sequence:
- Read the referenced file/line
- Make the fix
- Verify with tests/build if applicable
- Commit immediately — one focused commit for this one comment
- Push immediately — commit must be visible before replying
- Reply to the specific comment thread with the commit hash
- Mark as done in todo list
- Then move to the next comment — repeat steps 1-7
Example: Complete cycle for ONE item:
git add path/to/file.go
git commit -m "fix(api): return 400 for validation errors
Addresses review feedback: was incorrectly returning 500."
git push
gh api repos/{owner}/{repo}/pulls/{pr}/comments/{comment_id}/replies \
-f body="Fixed in $(git rev-parse --short HEAD)"
Why one commit per comment?
- Reviewers can see exactly which commit addressed their feedback
- Easy to revert a single fix if needed
- Clear audit trail linking comments to changes
- Avoids mega-commits that are hard to review
11. Reply to Comment Threads
You MUST reply to the exact comment thread, not the general PR conversation.
First, find the comment ID for the thread you're responding to:
gh api repos/{owner}/{repo}/pulls/{pr}/comments \
--jq '.[] | {id, path, line, body: .body[:80]}'
Then reply to that specific thread:
gh api repos/{owner}/{repo}/pulls/{pr}/comments/{comment_id}/replies \
-f body="Fixed in $(git rev-parse --short HEAD)"
For GraphQL review threads (when you have the thread ID from step 2):
gh api graphql -f query='
mutation($threadId: ID!, $body: String!) {
addPullRequestReviewThreadReply(input: {pullRequestReviewThreadId: $threadId, body: $body}) {
comment { id }
}
}' -f threadId="{thread_id}" -f body="Fixed in $(git rev-parse --short HEAD)"
NEVER use gh pr comment for feedback responses — that posts to the general conversation, not the specific thread.
Quick Reference
gh pr view --web
gh pr view --comments
gh api graphql -f query='
query($owner: String!, $repo: String!, $pr: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pr) {
reviewThreads(first: 100) {
nodes {
isResolved
comments(first: 1) { nodes { author { login } body path line } }
}
}
}
}
}' -f owner="{owner}" -f repo="{repo}" -F pr={pr} \
--jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false)'
Common Patterns
Bot Reviews
- Found in
/reviews API, not /comments
- Body contains full analysis with multiple items
- Parse markdown structure to extract individual points
- Different bots use different formats — adapt as needed
Human Comments
- May be in either API endpoint
- Check
isResolved field in GraphQL for thread status
- Often have follow-up discussion in threads
Line Comments
path and line fields indicate exact location
diff_hunk shows the surrounding context
- May be marked "outdated" if the file changed
Fork PRs
- Always use
baseRepository (upstream) for API calls, not headRepository (fork)
- The PR lives on the base repo; comments are fetched from there
- Push goes to the head repo (your fork), but comment replies go to base repo
CI Failures
- Extract job ID from the
detailsUrl in statusCheckRollup
- Logs may be truncated — use grep to find relevant errors
- For lint: look for
file:line patterns
- For tests: look for
FAIL, Error, panic
- For build: look for compilation errors
Troubleshooting
"gh: command not found"
Run the setup script: bash scripts/setup.sh
"no pull requests found for branch"
Either:
- You're on a branch without a PR — create one first with
gh pr create
- The PR is on a different branch — specify the PR number explicitly
Comment reply fails with 404
- The comment ID may be wrong — re-fetch with the comments API
- For bot review comments, use the GraphQL mutation with the thread ID instead
"Not found" when fetching from fork
- Make sure you're using
baseRepository (the upstream), not headRepository (your fork)
- The PR and its comments live on the upstream repo
CI logs say "run is still in progress"
- The workflow hasn't finished yet — wait for it to complete
- Check
status field in statusCheckRollup — should be COMPLETED
- If urgent, check the GitHub Actions UI directly via
detailsUrl
CI logs are too long or truncated
- Pipe through
tail -200 or head -200 to get relevant sections
- Use grep patterns to filter:
grep -E "(error:|FAIL|panic:)"
- For lint errors:
grep -E "^.*\.(go|ts|js|py):"