243 lines
18 KiB
Markdown
243 lines
18 KiB
Markdown
# `ce-resolve-pr-feedback`
|
||
|
||
> Evaluate, fix, and reply to PR review feedback in parallel. Fix what's real; don't churn on what isn't.
|
||
|
||
`ce-resolve-pr-feedback` is the **incoming-feedback resolution** skill. After your PR gets review comments, this skill fetches all unresolved threads, classifies them as new vs already-handled, then judges every finding centrally — in the one context that holds all threads at once — and fans out parallel subagents only to *implement* the fixes it has approved. It commits and pushes, then posts replies and resolves threads via GitHub's GraphQL API. It judges every item on its merits — regardless of source (human or bot) or form (inline thread, formal review body, or top-level comment) — and defaults to fixing, diverting only when reading the code trips a concrete signal (the finding's wrong, the fix would harm, it buys nothing, or the risk can't be bounded). The central judgment is what catches a confidently-wrong code-review bot before it's blindly fixed.
|
||
|
||
The compound-engineering ideation chain is `/ce-ideate → /ce-brainstorm → /ce-plan → /ce-work`. `ce-resolve-pr-feedback` is the **post-PR feedback loop** — invoked after reviewers leave comments, complementary to `/ce-code-review` (which reviews *before* the PR is open) and `/ce-debug` (which investigates broken behavior, not review feedback).
|
||
|
||
---
|
||
|
||
## TL;DR
|
||
|
||
| Question | Answer |
|
||
|----------|--------|
|
||
| What does it do? | Fetches unresolved review threads + PR comments, judges each finding centrally, fans out parallel subagents to fix the approved ones, commits/pushes, replies and resolves threads |
|
||
| When to use it | After a PR receives review feedback you want to address |
|
||
| What it produces | Commits with fixes, replies on each thread, resolved threads via GraphQL, summary of what was done per verdict |
|
||
| Modes | Full (all unresolved threads), Targeted (single thread URL) |
|
||
|
||
---
|
||
|
||
## The Problem
|
||
|
||
Resolving PR feedback at scale fails in predictable ways:
|
||
|
||
- **Over-fixing bot noise** — auto-review bots over-flag, flag immaterial things, and are sometimes wrong; a "fix everything" reflex churns the code and the PR with low-value changes
|
||
- **Findings taken on authority, not merit** — fixing because a reviewer (or bot) said so, without confirming the issue actually exists in the code
|
||
- **Already-replied items re-surface every run** — top-level PR comments and review bodies have no resolve mechanism, so they keep appearing until manually checked
|
||
- **Bot wrapper noise** — review-bot boilerplate ("Here are some automated review suggestions...") inflates the work count
|
||
- **Sequential fixes are slow** — addressing 12 threads one at a time is 12× the wall-clock time
|
||
- **Parallel fixes collide** — two agents writing the same file silently lose one of the changes
|
||
- **No combined validation** — each fixer runs targeted tests on its own change; cross-agent regressions slip through
|
||
- **Outdated comment line numbers** — feedback on lines that have since drifted is hard to relocate
|
||
|
||
## The Solution
|
||
|
||
`ce-resolve-pr-feedback` runs feedback resolution as a structured pipeline:
|
||
|
||
- **Fetch all unresolved feedback** (review threads + PR comments + review bodies) via GraphQL
|
||
- **Triage new vs already-handled** — a substantive reply that defers action counts as handled; only new items are processed
|
||
- **Drop bot wrapper noise silently** — non-actionable boilerplate is filtered, not announced
|
||
- **Judge centrally — the legitimacy gate** — the orchestrator decides each finding's verdict in its own context, where it can dedup reads, cluster a systematically-wrong reviewer's findings across threads, and weigh the author's design intent; it fixes by default and diverts only on a concrete signal (the finding's wrong, the fix would harm, it buys nothing, or the risk can't be bounded)
|
||
- **Fan out only the fixes** — subagents are dispatched solely to implement approved fixes (pure executors, no re-judging); fixers that touch overlapping files serialize automatically
|
||
- **Combined validation** — one full validation run after all fixers complete, catches cross-agent regressions
|
||
- **Reply with quoted context** — every reply quotes the relevant feedback for continuity, then states what was done
|
||
- **Resolve via GraphQL** — review threads get resolved; PR comments and review bodies get a top-level reply (no resolve mechanism in the API)
|
||
|
||
---
|
||
|
||
## What Makes It Novel
|
||
|
||
### 1. Default to fixing — divert only on a tripwire
|
||
|
||
Most review feedback — across P0–P2, nitpicks included — is correct and worth fixing, so the default is to fix it. Crucially, validation isn't a separate analysis pass: the agent has to read the code to make the fix anyway, and the checks are *tripwires it notices during that read*, not a gate every item must argue its way through. When nothing trips, it fixes and moves on — no per-item deliberation. The deep work (reading callers, assessing blast radius, writing a decision for the user) is spent only on the minority of items that trip a wire.
|
||
|
||
An item diverts from a fix only on a concrete signal:
|
||
|
||
- **the finding doesn't hold** (reading the code disproves it) → `not-addressing` with evidence
|
||
- **the fix would make the code worse** → `declined` citing the harm
|
||
- **the change buys nothing real** (cosmetic or immaterial — small *real* improvements still get fixed; the skip bar is "no benefit," not "minor") → `replied`
|
||
- **the change is risky and the blast radius can't be bounded** (a one-line edit can hit a hot path or thinly-tested code; the reviewer, especially a bot, usually couldn't see it) → de-risk with a test and fix if possible, else `needs-human`
|
||
- **it's a question, not a change** → `replied`, or `needs-human` for a product/business call
|
||
|
||
The guardrail against over-thinking is explicit: "I'm uneasy" is not a tripwire; "I read the callers and this breaks X" is. This matters most for auto-review bots, which over-flag — but the rule is source-agnostic: a bot or a human asserting something is not evidence it's correct.
|
||
|
||
### 2. Judge on merit, not source or form
|
||
|
||
Every item is evaluated the same way regardless of **who** raised it (human reviewer or review bot) or **what form** it arrived in (inline review thread, formal review body, or top-level PR comment). Correctness doesn't depend on the source or the surface. Structural form changes only the *response mechanics* (inline threads resolve via GraphQL; review bodies and top-level comments get a top-level reply) — never whether a finding is right.
|
||
|
||
### 3. Six verdicts — each with a different action
|
||
|
||
| Verdict | Meaning | Action |
|
||
|---------|---------|--------|
|
||
| `fixed` | Code change made as requested | Commit + reply + resolve |
|
||
| `fixed-differently` | Code change made, with a better approach than suggested | Commit + reply explaining the divergence + resolve |
|
||
| `replied` | No code change needed; question answered, design explained, or change not warranted | Reply + resolve |
|
||
| `not-addressing` | Feedback is factually wrong about the code | Reply with evidence + resolve |
|
||
| `declined` | Implementing the suggested fix would actively make code worse | Reply citing harm + resolve |
|
||
| `needs-human` | Cannot determine the right action | Reply with structured `decision_context` + leave open |
|
||
|
||
`needs-human` is high-signal and rare — it includes structured analysis of what the reviewer said, what the agent investigated, why a decision is needed, and concrete options with tradeoffs.
|
||
|
||
### 4. Triage — new vs already-handled
|
||
|
||
For each piece of feedback, the skill classifies before processing:
|
||
|
||
- **Review threads** — read the thread; a substantive reply that defers action ("need to align on this", "going to think through this") is **pending**, don't reprocess. Only original-comment-only threads are **new**.
|
||
- **PR comments + review bodies** — no resolve mechanism, so they reappear every run. Two filters: actionability (skip review wrappers, approvals, status badges, CI summaries with no asks), then already-replied (existing reply that quotes and addresses the feedback). Anything passing both is **new**.
|
||
|
||
Bot wrappers from CodeRabbit, Codex, Gemini Code Assist, Copilot are dropped silently — recognized by boilerplate content, never announced or counted. This is a *content* check (is there anything actionable here?), not a source check, so it holds regardless of which bot's format changes.
|
||
|
||
### 5. Central judgment, then parallel fixes with file-collision avoidance
|
||
|
||
The validity decision is made once, by the orchestrator, over the whole batch — not fanned out to a subagent per thread. Judging centrally is both cheaper (one fetch already holds every thread; reads dedup by file; no per-agent overhead paid on threads that turn out to be skips) and stronger (cross-thread clustering catches a systematically-wrong reviewer; the author's design intent is in view). Subagents are dispatched **only** for items already approved for a fix: for 1-4, all run in parallel; for 5+, batches of 4. **Before dispatching, the skill checks file overlaps** — overlapping fixers serialize so two never write the same file in parallel.
|
||
|
||
Sequential fallback: platforms without parallel dispatch run fixers sequentially.
|
||
|
||
### 6. Combined validation after all fixers complete
|
||
|
||
Each fixer runs targeted tests on its own changes. After all fixers return, the skill aggregates `files_changed` and runs the project's full validation **once** — catching cross-agent interactions targeted runs can't see.
|
||
|
||
| Outcome | Action |
|
||
|---------|--------|
|
||
| Green | Proceed to commit |
|
||
| Red, failures touch fixer-changed files | One inline diagnose-and-fix pass; if still red, escalate as `needs-human` and don't commit |
|
||
| Red, failures touch only files no fixer changed | Treat as pre-existing; commit with a footer note |
|
||
|
||
### 7. Reply format with quoted context
|
||
|
||
Every reply quotes the relevant part of the original feedback for continuity, then states what was done:
|
||
|
||
- **Fixed:** `> [quoted feedback]` followed by `Addressed: [brief description of the fix]`
|
||
- **Not addressing:** `> [quoted feedback]` followed by `Not addressing: [reason with evidence]`
|
||
- **Declined:** `> [quoted feedback]` followed by `Declined: [specific harm cited]`
|
||
|
||
This keeps reviewers oriented when they read the reply weeks later — they see what's being addressed without re-reading the whole thread.
|
||
|
||
### 8. Outdated comment relocation
|
||
|
||
Threads on outdated lines often have `line: null` and require fallback to `originalLine`. The orchestrator carries the `isOutdated` flag and all four location fields (`line`, `originalLine`, `startLine`, `originalStartLine`) into the gate, relocates the concern via the comment's anchor when the line has drifted, and passes the resolved location to the fixer so it edits the right place.
|
||
|
||
### 9. Two-pass loop with escalation
|
||
|
||
If new threads remain after the verify step, the skill repeats from triage for the remaining threads. After two fix-verify cycles, the skill stops looping and surfaces the recurring pattern as `needs-human`: "Multiple rounds of feedback on [theme] suggest a deeper issue."
|
||
|
||
### 10. Two modes — Full and Targeted
|
||
|
||
| Mode | When | Behavior |
|
||
|------|------|----------|
|
||
| **Full** _(default)_ | No URL provided | Process all unresolved threads on the PR |
|
||
| **Targeted** | Comment/thread URL provided | Process only that specific thread |
|
||
|
||
Targeted mode is for "address just this one comment" cases — common when the user wants to handle one piece of feedback in isolation.
|
||
|
||
---
|
||
|
||
## Quick Example
|
||
|
||
A reviewer (and a review bot) leave 8 comments on your PR. You invoke `/ce-resolve-pr-feedback`.
|
||
|
||
The skill detects the PR from the current branch, fetches via GraphQL: 6 unresolved review threads, 2 review bodies (one is a CodeRabbit wrapper), 0 PR comments. Triage: the CodeRabbit wrapper is non-actionable boilerplate — dropped silently. One review thread has a substantive reply from yesterday deferring action — pending, skip. That leaves 5 review threads + 1 review body as **new**.
|
||
|
||
Step 3 is the gate: the orchestrator judges all 6 new items in its own context, reading the code where a verdict turns on it (and reading `app/services/dispatcher.rb` once for the two threads that land on it):
|
||
|
||
- 2 findings are clearly correct → `fixed`
|
||
- 1 suggests an approach that works, but a cleaner one exists → `fixed-differently`
|
||
- 1 is a bot finding flagging a "possible null deref" the type system already rules out → confirmed against the code, doesn't hold → `not-addressing` with evidence (no churn, and no subagent ever spun up for it)
|
||
- 1 asks "is this intentional?" → answerable from the code → `replied`
|
||
- the review body asks a design question → `replied`
|
||
|
||
So only 3 items reach step 4, which dispatches 3 generic fixer subagents seeded with the skill-local `pr-comment-resolver.md` prompt. File-collision check: the two `dispatcher.rb` fixers serialize; the rest run in parallel. Each fixer implements its already-approved change and returns. Combined validation runs once against the 3 changed files; tests pass. Commit + push.
|
||
|
||
Step 7 posts replies: each quotes the original feedback and states what was done — fixers' replies for the 3 fixes, the orchestrator's composed replies for the `not-addressing`/`replied` items. All 5 review threads resolve via GraphQL; the review body gets a top-level PR comment (no resolve mechanism in the API). Step 8 verify: fetched again — empty. Done. Summary surfaces.
|
||
|
||
---
|
||
|
||
## When to Reach For It
|
||
|
||
Reach for `ce-resolve-pr-feedback` when:
|
||
|
||
- Your PR received review feedback you want to address
|
||
- An auto-review bot left a pile of findings and you want them validated against the code, not blindly applied
|
||
- You want to handle a specific comment in isolation (Targeted mode with the comment URL)
|
||
- A previous run left `needs-human` items and you've decided how to proceed
|
||
|
||
Skip `ce-resolve-pr-feedback` when:
|
||
|
||
- The PR has no feedback yet
|
||
- You only want to ack the feedback without fixing — the skill expects to act, not just acknowledge
|
||
- The feedback is on a brainstorm or plan doc, not code → use `/ce-doc-review`
|
||
|
||
---
|
||
|
||
## Use as Part of the Workflow
|
||
|
||
`ce-resolve-pr-feedback` is the closing loop after `/ce-commit-push-pr` opens a PR:
|
||
|
||
```text
|
||
/ce-work → /ce-commit-push-pr → reviewer leaves comments → /ce-resolve-pr-feedback
|
||
```
|
||
|
||
It complements:
|
||
|
||
- **`/ce-code-review`** — reviews *before* the PR is open; this skill handles incoming feedback *after*
|
||
- **`/ce-debug`** — for broken behavior; this skill is for review-comment resolution
|
||
|
||
After resolution lands on the PR, the standard merge / re-review cycle applies. If the next review round produces more feedback, this skill can run again on the new round.
|
||
|
||
---
|
||
|
||
## Use Standalone
|
||
|
||
The skill works directly:
|
||
|
||
- **Current branch's PR** — `/ce-resolve-pr-feedback`
|
||
- **Specific PR** — `/ce-resolve-pr-feedback 1234`
|
||
- **Targeted (single thread)** — `/ce-resolve-pr-feedback https://github.com/.../pull/1234#discussion_r5678901`
|
||
|
||
In Targeted mode, only the URL's specific thread is addressed — no other threads are fetched or processed.
|
||
|
||
---
|
||
|
||
## Reference
|
||
|
||
| Argument | Effect |
|
||
|----------|--------|
|
||
| _(empty)_ | Full mode — current branch's PR |
|
||
| `<PR number>` | Full mode — that PR |
|
||
| `<comment/thread URL>` | Targeted mode — only that thread |
|
||
|
||
Scripts in `scripts/`: `get-pr-comments` (GraphQL fetch), `get-thread-for-comment` (map comment → thread for targeted), `reply-to-pr-thread` (GraphQL mutation), `resolve-pr-thread` (GraphQL mutation).
|
||
|
||
---
|
||
|
||
## FAQ
|
||
|
||
**Do you still fix nitpicks?**
|
||
Yes — by default. Most feedback, nitpicks included, is correct and worth fixing, so the agent fixes unless reading the code trips a concrete signal: the finding doesn't hold, the fix would make the code worse, or the change buys nothing real. A correct nit that improves the code (even slightly) gets fixed; a purely cosmetic one with no benefit gets a brief reply instead of churn. The skip bar is "no benefit," not "minor."
|
||
|
||
**Does it treat bot feedback differently from human feedback?**
|
||
No — and that's deliberate. Validation is judged on merit, not authority: reading the actual code to confirm a finding is the same work whether a bot or a human raised it, and an authority heuristic ("bot → probably noise") risks dismissing a real bot-caught bug. The merit tripwires (does the finding hold? does the fix actually help?) naturally filter bot noise — mostly speculative or immaterial — without ever needing to classify the source. The same applies to *form* — inline thread vs. formal review body vs. top-level comment changes only how the reply is posted and resolved, never whether the finding is correct.
|
||
|
||
**Why drop bot wrappers silently?**
|
||
Because announcing them adds noise without value. CodeRabbit boilerplate ("Here are some automated review suggestions...") wraps real findings; the wrapper itself isn't actionable. Counting or listing dropped wrappers in the summary clutters the report. The script-level filter handles only CI/status bots; the content-aware drop (an actionability check, not a source check) catches the rest.
|
||
|
||
**What if two parallel agents conflict?**
|
||
The file-collision check before dispatch catches most cases — overlapping items serialize. For rare cases where a fix expands beyond its referenced file (rename updates callers elsewhere), the combined validation in step 5 catches test breakage and the verify step in step 8 catches unresolved threads. If either surfaces inconsistency, the skill re-runs the affected agents sequentially.
|
||
|
||
**What does `needs-human` mean?**
|
||
The agent investigated the feedback and the code, but cannot determine the right action confidently — usually because the choice depends on user intent the agent can't infer. The thread stays open with an acknowledgment reply, and the summary surfaces a structured `decision_context`: quoted feedback, investigation findings, options with tradeoffs, the agent's lean if any.
|
||
|
||
**What if the feedback loop never converges?**
|
||
After two fix-verify cycles, the skill stops looping and escalates the recurring pattern as `needs-human` with the cumulative context. It doesn't retry indefinitely.
|
||
|
||
---
|
||
|
||
## See Also
|
||
|
||
- [`ce-code-review`](./ce-code-review.md) — pre-PR review; this skill handles post-PR feedback
|
||
- [`ce-commit-push-pr`](./ce-commit-push-pr.md) — opens the PR that this skill responds to
|
||
- [`ce-debug`](./ce-debug.md) — for broken behavior reported as a bug, not review feedback
|
||
- [`ce-doc-review`](./ce-doc-review.md) — for feedback on requirements or plan docs, not code
|