ci: Review PRs per-commit and attach comments to correct commits

Switch claude-review from reviewing the entire PR diff at once to
reviewing each commit individually via subagents. Each commit review
subagent receives the PR context, preceding commit diffs, and its own
commit diff, then returns comments tagged with the commit SHA. This
ensures review comments are attached to the correct commit via the
GitHub API rather than all pointing at HEAD.

Also add Bash(gh:*) to allowed tools so subagents can fetch per-commit
diffs via `gh api` without needing local git objects, and remove CI
analysis (needs to be delayed until CI finishes to be useful).

Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Daan De Meyer
2026-03-16 16:12:04 +01:00
parent 52c4aca21e
commit 94a695d89a
+34 -26
View File
@@ -117,7 +117,6 @@ jobs:
pull-requests: read # Fetch PR comments and reviews
issues: read # Fetch issue comments
id-token: write # Authenticate with AWS via OIDC
actions: read
outputs:
structured_output: ${{ steps.claude.outputs.structured_output }}
@@ -149,7 +148,7 @@ jobs:
"type": "array",
"items": {
"type": "object",
"required": ["file", "severity", "body"],
"required": ["file", "severity", "body", "commit"],
"properties": {
"file": {
"type": "string",
@@ -166,6 +165,10 @@ jobs:
"body": {
"type": "string",
"description": "The review comment body in markdown"
},
"commit": {
"type": "string",
"description": "The SHA of the PR commit that introduced the code being commented on"
}
}
}
@@ -180,8 +183,6 @@ jobs:
github_token: ${{ secrets.GITHUB_TOKEN }}
track_progress: false
show_full_output: "true"
additional_permissions: |
actions: read
claude_args: |
--model us.anthropic.claude-opus-4-6-v1
--max-turns 100
@@ -189,18 +190,15 @@ jobs:
Read,LS,Grep,Glob,Task,
Bash(cat:*),Bash(test:*),Bash(printf:*),Bash(jq:*),Bash(head:*),Bash(tail:*),
Bash(git:*),Bash(grep:*),Bash(find:*),Bash(ls:*),Bash(wc:*),
Bash(gh:api *),
Bash(diff:*),Bash(sed:*),Bash(awk:*),Bash(sort:*),Bash(uniq:*),
mcp__github__get_pull_request,
mcp__github__get_pull_request_diff,
mcp__github__get_pull_request_files,
mcp__github__get_pull_request_reviews,
mcp__github__get_pull_request_comments,
mcp__github__get_pull_request_review_comments,
mcp__github__get_pull_request_status,
mcp__github__get_issue_comments,
mcp__github_ci__get_ci_status,
mcp__github_ci__get_workflow_run_details,
mcp__github_ci__download_job_log,
"
--json-schema '${{ env.REVIEW_SCHEMA }}'
prompt: |
@@ -218,11 +216,13 @@ jobs:
Use the GitHub MCP server tools to fetch PR data. For all tools, pass
owner `${{ github.repository_owner }}`, repo `${{ github.event.repository.name }}`,
and pullNumber/issue_number ${{ needs.setup.outputs.pr_number }}:
- `mcp__github__get_pull_request_diff` to get the PR diff
- `mcp__github__get_pull_request` to get the PR title, body, and metadata
- `mcp__github__get_pull_request_comments` to get top-level PR comments
- `mcp__github__get_pull_request_reviews` to get PR reviews
Fetch the list of commits in the PR using:
`gh api repos/{owner}/{repo}/pulls/{number}/commits --paginate --jq '.[].sha'`
Also fetch issue comments using `mcp__github__get_issue_comments` with
issue_number ${{ needs.setup.outputs.pr_number }}.
@@ -230,28 +230,33 @@ jobs:
in the issue comments. If one exists, you will use it as the basis for
your `summary` in Phase 3.
Check CI status for the PR head commit using `mcp__github_ci__get_ci_status`.
If any workflow runs have failed, use `mcp__github_ci__get_workflow_run_details`
and `mcp__github_ci__download_job_log` to fetch the failure logs. Pass these
logs to the review subagents in Phase 2 so they can identify whether the PR
changes caused the failures.
## Phase 2: Per-commit review with subagents
## Phase 2: Parallel review subagents
Review each commit in the PR individually, in order (oldest first). For each
commit, fetch its diff using `gh api repos/{owner}/{repo}/commits/{sha}
-H 'Accept: application/vnd.github.diff'` via Bash, then launch a subagent
to review that commit's changes.
Review:
Each commit review subagent receives:
- The PR title and description (for overall context)
- The diffs of all preceding commits in the PR (for context on what was already changed)
- The commit message and SHA of the commit being reviewed
- The commit diff (from `gh api`)
Each commit review subagent reviews:
- Code quality, style, and best practices
- Potential bugs, issues, incorrect logic
- Security implications
- CLAUDE.md compliance
- CI failures (if any logs were fetched in Phase 1)
For every category, launch subagents to review them in parallel. Group related sections
as needed — use 2-4 subagents based on PR size and scope.
Each commit review subagent must return a JSON array of issues:
`[{"file": "path", "line": <number> (optional), "severity": "must-fix|suggestion|nit", "body": "...", "commit": "<sha>"}]`
Give each subagent the PR title, description, full patch, and the list of changed files.
Each subagent must return a JSON array of issues:
`[{"file": "path", "line": <number> (optional), "severity": "must-fix|suggestion|nit", "body": "..."}]`
The `commit` field MUST be set to the SHA of the commit being reviewed.
Each commit review subagent MUST only return comments about changes in the commit it is
reviewing — do NOT comment on code from preceding commits, even if it was
provided for context. If a preceding commit has an issue, it will be caught
by the subagent reviewing that commit.
`line` should be a line number from the NEW side of the diff **that appears inside
a diff hunk** (i.e. a line that is shown in the patch output). GitHub's review
@@ -259,15 +264,18 @@ jobs:
that are not visible in the patch. If you cannot determine a valid diff line,
omit `line` — the comment will still appear in the tracking comment summary.
Each subagent MUST verify its findings before returning them:
Each commit review subagent MUST verify its findings before returning them:
- For style/convention claims, check at least 3 existing examples in the codebase to confirm
the pattern actually exists before flagging a violation.
- For "use X instead of Y" suggestions, confirm X actually exists and works for this case.
- If unsure, don't include the issue.
Launch commit review subagents for all commits in parallel (e.g. if
reviewing a 7-commit PR, launch all 7 commit review subagents at once).
## Phase 3: Collect, deduplicate, and summarize
After ALL subagents complete:
After ALL commit review subagents complete:
1. Collect all issues. Merge duplicates (same file, lines within 3 of each other, same problem).
2. Drop low-confidence findings.
3. Check the existing inline review comments fetched in Phase 1. Do NOT include a
@@ -414,7 +422,7 @@ jobs:
owner,
repo,
pull_number: prNumber,
commit_id: headSha,
commit_id: c.commit,
path: c.file,
line: c.line,
body: `Claude: **${c.severity}**: ${c.body}`,