CodePanel

PR review: writing a review a coding agent can act on

PR review for coding agents: what to read, what a review should return, and a real CodePanel review taken apart, part by part.

Updated

A PR review reads a pull request's change in the context of the code around it and returns a decision plus comments the author must answer before merge. Written for a coding agent, each comment needs a stable id, a location, a severity, the problem and the fix, and the review needs one machine-readable next step.

On CodePanel's own repository, Claude Code wrote 147 of the 150 pull requests merged from PR 61 to PR 213 as of 17 October 2026, and it reads the reviews. All but one of the 1,460 /feedback lines rating a finding in PR comments carry --agent claude-code. The AI code review pillar covers the system; this page covers the review itself.

What a reviewer reads, in order

A reviewer, human or model, should read in this order:

  1. The description. It states what the change claims to do, and everything after is checked against it.
  2. The diff. What the change does.
  3. The code around each change. The diff cannot show a null check ten lines above a hunk, or the caller that depends on the old behaviour.
  4. The tests. Would one fail without the change, and does it assert behaviour or only mirror the code?
  5. The project's rules. Conventions the diff can break without a bug.

CodePanel's reviewers get the title, author and description, the diff with each line numbered from the new revision (a finding copies a line number instead of computing one), the code around each change at the head commit, and .codepanel/rules.md from the base branch. The limits are in How it works. AGENTS.md and CLAUDE.md, CI results, linked issues and the author agent's handoff are off by default (repo context).

A review returns more than a verdict

GitHub gives a reviewer three review states: Comment, Approve and Request changes, which "flags feedback that the author should address before merging". For an agent that is one value on the whole review. It does not say which comments block, which commit was read, or whether an earlier comment was answered. An Approve is an LGTM in words, and the agent cannot tell what it covers.

CodePanel never approves or requests changes. The only GitHub review event it posts is COMMENT, and only for inline suggestions, which are off by default. The decision is a commit status named CodePanel that the repository can require. An agent needs five things from a comment: a stable id (F3) that its answer names and the next round matches, with a finding reported again in matching terms keeping its id; a path:line at a named commit, so the code can be found after more pushes; a severity, which says what holds the merge; the problem and the smallest fix that would answer it; and one next step. The full vocabulary is in Findings.

Anatomy of one real PR review

The comment below is on PR 192, "Eval: one adaptive in-flight pool per provider". CodePanel posts one comment per pull request and rewrites it each round:

Part What it holds
Hidden marker An HTML comment with the round, reviewed commit and verdict: <!-- codepanel:review v=2 round=N reviewedSha=… verdict=ready -->
Heading ## CodePanel review · round N
Counts and scope Open and resolved findings, the commit range reviewed, and the named commit the line numbers refer to
Author line Written by: Claude Opus 5.5, from commit trailers in 8 of 8 commits read
Verdict line The verdict, with the three ways to read it by machine: GET /api/v1/review, MCP get_review or codepanel review --json
Verdict memory The earlier verdicts on these files that the orchestrator weighed, by PR and finding, and how to re-rate one that was wrong
Summary The orchestrator's summary of the change and what remains
Instructions For the agent: fix most severe first, confirm each problem exists, make the smallest change, verify low- and medium-confidence findings, run tests and the linter, rate every finding
Findings One block each, below
Resolved Findings an earlier round reported that this round no longer does
Disputes Each disputed finding, with the orchestrator's decision and reasoning
Ratings Ratings given through the API, each marked claude-code via API and followed by its reason. /feedback replies live in the pull request's own comments
Footer The models and the round's cost: three reviewers on claude-haiku-5-5, the orchestrator on claude-sonnet-5-5, $0.05

A finding heading packs five fields: id, severity, location, lifecycle and confidence, as in ### F3 · warning · path/file.ts:42 · open since round N · low confidence. A new finding says new, and the lifecycle field is left out on a first round. Confidence is shown only when it is low or medium. One real finding from the same comment, with its code excerpt trimmed ([...]):

### F14 · suggestion · `tools/eval/providerPools.test.ts:99` · new · low confidence
Problem:
> The new assertion hard-codes 16 instead of referencing the fallback, so a change
> to SMALL_CEILING breaks this test for an unrelated reason. [...]
```ts
[...]
	expect(pools.ceilingFor("constructor")).toBe(16);
[...]
```
Fix:
> Compare against POOL_CEILINGS.google or an exported fallback constant, and add
> a case such as 'unlisted:model'.

A default finding has only Problem, a code excerpt and Fix. Trigger:, Check: and Patch: parts exist but are off by default (evidence). The agent rated this one up useful through the API and left it for the next change to that test file.

The next step is one field

An agent should not parse the summary. get_review returns payload.nextAction: one action, the finding refs it is about and a reason. The server checks the cases in this order, so only one comes back (The review loop has the detail):

action When The agent
done The pull request is merged or closed Stops
wait A round is running Waits, reads again
stop The last round failed, the findings are unfiltered, or the head is unreviewed at the round limit Reports the reason; does not loop
trigger No round has reviewed the head Triggers (spends credits), then waits
fix A gated finding is unanswered, with no dispute pending Fixes each ref, or answers it
stop A pending dispute cannot be answered yet Reports the reason
wait The orchestrator is checking the agent's disputes Reads again in a few minutes
rate Nothing blocks, but open findings are unrated Rates each ref
done Nothing blocks and every open finding is rated Stops

gate.blocking says whether the merge gate still holds the pull request, and gate.unanswered lists the refs holding it. It is a sibling of payload in the response, and it is what the repository merges by. Do not decide from payload.verdict or blockedBy: they count every open critical and warning finding, answered or not, so a verdict can read blocked after everything is answered. See blocking finding and the MCP server reference.

A review that caught the description drifting

The description is part of what a review reads, so a review can catch it going stale. On CodePanel's PR 184, which changed how chat finds the review it answers with, the first review's F2 (a suggestion) said that a session with no stored review fell back to the pull request's newest one, possibly from another session. Claude Code dropped the fallback and rated it:

/feedback F2 up fixed dropped the by_pr fallback; a round with no stored review now gets none --agent claude-code

The next review raised F10, a low-confidence suggestion at convex/sessions/chatContext.ts:51: "A PR description that still promises the fallback would be wrong". The code was right by then, the description was not. The agent edited it and rated the finding:

/feedback F10 up fixed the PR description now states the no-fallback behaviour --agent claude-code

The body's edit history confirms it. The first description said that a round with no stored review "falls back to the PR's newest one". The merged body says such a round gets none. An agent that fixes the code and not the description leaves the old claim in place.

Answering the review

Every finding gets one answer: a /feedback line in a PR comment, or one rate_finding call. Fix it and say up fixed, or dispute it with the evidence. How disputes are judged is in agentic code review. Rate the review itself when it was notably good or noisy:

/feedback review up every finding was accurate and small --agent claude-code
/feedback review down noisy: three suggestions on links the repo's own tests already check --agent claude-code

The reason is the useful part. Syntax is in Feedback and commands.

Hand the loop to your agent

With the CodePanel MCP server connected, give your agent this after it pushes:

Call the CodePanel wait_for_review tool with repo owner/name, pr <n>, headSha set to
the output of git rev-parse HEAD, and timeoutSeconds 45. On status timeout, call it
again; on status no_review, call trigger_review. When it finishes, call get_review
for the same pull request and act on payload.nextAction, not on payload.verdict:
- fix: fix each finding in refs, push, and start again with the new head SHA.
- wait: call wait_for_review again.
- trigger: call trigger_review, then wait_for_review.
- rate: rate each ref in refs.
- stop: report nextAction.reason. Do not trigger again.
- done: stop.
For each finding, check the problem against the code first. Then call
rate_finding with repo, pr, ref, rating, reason and a one-sentence note: up fixed,
up deferred (say where it is tracked), or down false_positive (name the file and
line that contradict it). Pass agent claude-code.

The same loop from a shell:

codepanel review owner/name#<n> --wait --sha "$(git rev-parse HEAD)"
codepanel review owner/name#<n> --json | jq -r '.payload.nextAction.action'
codepanel feedback owner/name#<n> F3 up --reason fixed --note "Added the ownership check" --agent claude-code

To try it with your agent, start from the quickstart.