AI-powered pull request review using a multi-agent system. Analyzes code changes, posts inline comments, and learns from your feedback.
Primary trigger: Add
docker-agentas a reviewer in the PR sidebar — the review starts automatically. To re-trigger a review, re-request a review fromdocker-agentin the PR sidebar. The/reviewcomment still works but is deprecated.
If your repo only accepts PRs from branches within the same repo (no forks), you need a single workflow file:
.github/workflows/pr-review.yml:
name: PR Review
on:
pull_request:
types: [ready_for_review, opened, review_requested]
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
permissions:
contents: read
jobs:
review:
uses: docker/docker-agent-action/.github/workflows/review-pr.yml@VERSION
permissions:
contents: read # Read repository files and PR diffs
pull-requests: write # Post review comments
issues: write # Create security incident issues if secrets detected
checks: write # (Optional) Show review progress as a check run
id-token: write # Required for OIDC authentication to AWS Secrets Manager
actions: write # Cache read/write for review-lock deduplication and binary cacheThat's it. All three events (pull_request, issue_comment, pull_request_review_comment) have full OIDC/secret access for same-repo PRs, so the reusable workflow handles everything directly.
Fork PRs are subject to GitHub's security restrictions: pull_request and pull_request_review_comment events get read-only tokens, no secrets, and no OIDC. To work around this, you need a second "trigger" workflow that saves event context as an artifact, then a workflow_run handler picks it up with full permissions.
.github/workflows/pr-review-trigger.yml — lightweight, no secrets needed:
name: PR Review - Trigger
on:
pull_request:
types: [ready_for_review, opened, review_requested]
pull_request_review_comment:
types: [created]
permissions: {}
jobs:
save-context:
# A review request for anyone other than docker-agent must not fan out to a review
if: >
github.event_name != 'pull_request' ||
github.event.action != 'review_requested' ||
github.event.requested_reviewer.login == 'docker-agent'
runs-on: ubuntu-latest
steps:
- name: Save event context
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
REQUESTED_REVIEWER: ${{ github.event.requested_reviewer.login }}
COMMENT_JSON: ${{ toJSON(github.event.comment) }}
run: |
mkdir -p context
printf '%s' "${{ github.event_name }}" > context/event_name.txt
printf '%s' "$PR_NUMBER" > context/pr_number.txt
printf '%s' "$PR_HEAD_SHA" > context/pr_head_sha.txt
if [ "${{ github.event_name }}" = "pull_request" ]; then
printf '%s' "$REQUESTED_REVIEWER" > context/requested_reviewer.txt
fi
if [ "${{ github.event_name }}" = "pull_request_review_comment" ]; then
printf '%s' "$COMMENT_JSON" > context/comment.json
fi
- name: Upload context
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: pr-review-context
path: context/
retention-days: 1.github/workflows/pr-review.yml — calls the reusable review workflow:
name: PR Review
on:
issue_comment:
types: [created]
workflow_run:
workflows: ["PR Review - Trigger"]
types: [completed]
permissions:
contents: read
jobs:
review:
if: |
(github.event_name == 'issue_comment' &&
github.event.comment.user.login != 'docker-agent' &&
github.event.comment.user.login != 'docker-agent[bot]' &&
github.event.comment.user.type != 'Bot' &&
!contains(github.event.comment.body, '<!-- docker-agent-review -->') &&
!contains(github.event.comment.body, '<!-- docker-agent-review-reply -->')) ||
github.event.workflow_run.conclusion == 'success'
uses: docker/docker-agent-action/.github/workflows/review-pr.yml@VERSION
permissions:
contents: read # Read repository files and PR diffs
pull-requests: write # Post review comments
issues: write # Create security incident issues if secrets detected
checks: write # (Optional) Show review progress as a check run
id-token: write # Required for OIDC authentication to AWS Secrets Manager
actions: write # Required by reusable workflow for artifact operations; also needed to download trigger artifacts
with:
trigger-run-id: ${{ github.event_name == 'workflow_run' && format('{0}', github.event.workflow_run.id) || '' }}pull_request (opened / ready_for_review / review_requested)
→ pr-review-trigger.yml (saves context as artifact, no secrets needed)
→ completes
→ workflow_run fires
→ pr-review.yml (downloads artifact, runs review)
pull_request_review_comment
→ pr-review-trigger.yml (saves context as artifact)
→ workflow_run fires
→ pr-review.yml (downloads artifact, routes to reply-to-feedback for replies to agent
comments, or reply-to-mention for top-level @-mentions)
/review comment –OR– @docker-agent mention
→ pr-review.yml directly (issue_comment has full permissions)
Adding docker-agent as a reviewer fires a pull_request event with action: review_requested, which follows the trigger-workflow path above. The issue_comment event (/review command and @docker-agent mentions) always has full permissions regardless of fork status, so those paths work directly without the trigger workflow.
The pull_request trigger types in your calling workflow control how often reviews run. Two modes are supported — the examples above use Mode B:
Mode B — recommended default:
pull_request:
types: [opened, ready_for_review, review_requested]Reviews run when a PR is opened or marked ready for review. After the initial review, further pull_request-triggered reviews only run when docker-agent is explicitly re-requested as a reviewer. Re-request a review from docker-agent in the PR sidebar to re-trigger at any time. The /review comment still works but is deprecated.
Mode A — continuous re-review on every push:
pull_request:
types: [opened, ready_for_review, synchronize, review_requested]Adds synchronize to also trigger on every push to the PR branch. Opt in if your team wants the reviewer to automatically re-examine every update, at the cost of more workflow runs.
Note
The requester-authorized path below requires the check-org-membership update from PR #16 (merge that PR first). Until it ships, membership is checked against the PR author rather than the requesting org member, so requesting docker-agent on an external or fork PR is silently skipped.
Auto-review only runs on PRs authored by org members. A PR opened by an external or fork contributor is not reviewed automatically. To get one reviewed, an org member drives it through GitHub's native UI in two steps:
- Approve the workflow run. For PRs from first-time and external contributors, GitHub holds all Actions runs until a maintainer approves them (governed by the repository's
Settings→Actions→Generalfork-PR approval policy). Click Approve and run workflows on the PR; until then nothing runs, including the PR review trigger. - Request a review from
docker-agent. In the PR sidebar, under Reviewers, adddocker-agent. This fires areview_requestedevent and starts the review, shown as a check run (ifchecks: writeis granted).
That is the entire flow. No special commands or workflow inputs are needed: not the deprecated /review comment, not workflow_dispatch, and no caller-side configuration. The review is authorized by the requesting org member rather than the PR author, which is what lets an external contributor's PR be reviewed on demand. The request is safe by construction: GitHub only lets users with triage or write access request a reviewer, and the reusable workflow verifies org membership before any review work runs. An external contributor cannot trigger a review of their own PR.
To re-run the review after new commits, re-request the review from docker-agent in the sidebar (the refresh icon next to their name).
with:
model: anthropic/claude-haiku-4-5 # Use a faster/cheaper model| Trigger | Behavior |
|---|---|
Request review from docker-agent |
Primary trigger. Add docker-agent as a reviewer in the PR sidebar — review starts automatically, shown as a check run. Authorized by the requesting org member, so it also works for external/fork contributors' PRs. |
| PR opened/ready | Auto-reviews when a PR is opened or marked ready for review (org-member-authored PRs). |
/review |
Re-trigger a review, or trigger manually when auto-review hasn't run (e.g. after a force-push). Shows as a check run if checks: write is granted. |
| Reply to review comment | Responds in-thread and captures feedback to improve future reviews. |
@docker-agent mention |
Answers questions and clarifies review findings. Works in both PR-level issue comments and inline file-line review comments, including on fork PRs (via the trigger workflow). |
Built-in defense-in-depth:
- Verifies org membership before every review. Auto-review checks the PR author (so only org members' PRs are reviewed automatically); a requested review checks the requester, so a maintainer can pull an external contributor's PR into review on demand;
/reviewchecks the commenter- Prevents bot cascades — replies from bots (except
docker-agent) are ignored- Throttles rate anomalies — per-PR
concurrency:groups collapse same-trigger bursts, and a rate-limit check skips the review when too many requests land on one PR in a short window- Fork PRs work automatically with the two-workflow setup — the trigger →
workflow_runpattern provides OIDC/secret access regardless of fork status
The workflow YAML examples above are the complete, recommended setup. The reusable workflow handles all safety checks internally — do not add your own if: guards for these:
| Protection | How it's handled |
|---|---|
| Bot comment filtering | All jobs in the reusable workflow filter out docker-agent, docker-agent[bot], any Bot-type user, and comments with <!-- docker-agent-review -->/<!-- docker-agent-review-reply --> markers. No caller-side filtering needed. |
| Org membership / authorization | A check-org-membership step runs before any review work. Auto-review verifies the PR author; a requested review verifies the requester (so an external contributor's PR can be reviewed when an org member requests it); comment / /review paths verify the commenter. All via OIDC. Callers never need author_association checks. |
| PR vs issue comment | The reusable workflow checks github.event.issue.pull_request internally. Plain issue comments on non-PR issues are silently ignored. |
| Draft PR skipping | Draft PRs are skipped internally — no caller condition needed. |
| Concurrent review guard | A cache-based lock (pr-review-lock-<repo>-<pr>-*) prevents duplicate reviews from racing on the same PR. |
| Rate-anomaly throttling | Per-PR concurrency: groups serialize same-trigger bursts, and a rate-limit check skips the review when too many docker-agent reviews and replies land on one PR within the window. No caller configuration needed. |
The only decision callers make is which setup pattern to use: 1-workflow for same-repo PRs, 2-workflow for repos that accept fork PRs. That distinction is the caller's responsibility because it controls which event path delivers OIDC credentials to the reusable workflow.
Optional optimization: some teams add
author_associationchecks or bot-login filters on their calling workflow's jobif:to skip the job early and save Actions minutes. This is a valid cost optimization but is not required for correctness or security. When in doubt, use the canonical YAML above without extra conditions — it's simpler to audit and maintain.
Requires Docker Agent installed locally. The reviewer agent automatically detects its environment. When running locally, it diffs your current branch against the base branch and outputs findings to the console.
cd ~/code/my-project
docker agent run agentcatalog/review-pr "Review my changes"The agent automatically:
- Pulls the latest version from Docker Hub
- Reads
AGENTS.mdorCLAUDE.mdfrom your repo root for project-specific context (language versions, conventions, etc.) - Diffs your current branch against the base branch
- Outputs the review as formatted markdown
Tip: Docker Agent has a TUI, so you can interact with the agent during the review — ask follow-up questions, request clarification on findings, or drill into specific files.
The reviewer automatically looks for an AGENTS.md (or CLAUDE.md) file in your repository root before analyzing code. This file is read and passed to all sub-agents (drafter and verifier), so project-specific context like language versions, build tools, and coding conventions are respected during the review.
For example, if your AGENTS.md says "Look at go.mod for the Go version," the reviewer will check go.mod before flagging APIs as nonexistent — avoiding false positives from newer language features.
No workflow configuration is needed — just commit an AGENTS.md to your repo root.
You can also pass additional files explicitly with --prompt-file:
docker agent run agentcatalog/review-pr --prompt-file CONTRIBUTING.md "Review my changes"When using docker/docker-agent-action/.github/workflows/review-pr.yml:
| Input | Description | Default |
|---|---|---|
trigger-run-id |
Workflow run ID from pr-review-trigger.yml (for workflow_run path) |
- |
pr-number |
PR number override (auto-detected from event or trigger artifact) | - |
comment-id |
Comment ID for reactions (auto-detected) | - |
additional-prompt |
Additional review guidelines | - |
model |
Model override (e.g., anthropic/claude-haiku-4-5) |
- |
add-prompt-files |
Comma-separated files to append to the prompt | - |
confidence-threshold |
Min confidence to post a finding inline: band (strong/moderate/medium/weak) or a number (clamped to 30–100) |
moderate |
incremental |
Review only the commits pushed since the last completed review (falls back to a full review when unsafe) | true |
PR number and comment ID are auto-detected from github.event when not provided.
API Keys: Provide at least one API key for your preferred provider. You don't need all of them.
| Input | Description | Required |
|---|---|---|
pr-number |
PR number (auto-detected) | No |
comment-id |
Comment ID for reactions (auto-detected) | No |
additional-prompt |
Additional review guidelines (appended to built-in instructions) | No |
model |
Model override (default: anthropic/claude-sonnet-4-5) |
No |
anthropic-api-key |
Anthropic API key | No* |
openai-api-key |
OpenAI API key | No* |
google-api-key |
Google API key (Gemini) | No* |
aws-bearer-token-bedrock |
AWS Bedrock token | No* |
xai-api-key |
xAI API key (Grok) | No* |
nebius-api-key |
Nebius API key | No* |
mistral-api-key |
Mistral API key | No* |
github-token |
GitHub token | No |
add-prompt-files |
Comma-separated files to append to the prompt | No |
confidence-threshold |
Min confidence to post a finding inline: band (strong/moderate/medium/weak) or a number clamped to 30–100 (default moderate) |
No |
incremental |
Review only commits since the last completed review (default true; falls back to full when unsafe) |
No |
*API keys are optional when using the reusable workflow (credentials are fetched via OIDC). Only required when using the composite action directly without OIDC.
When issues are found, the action posts inline review comments:
**Potential null pointer dereference**
The `user` variable could be `nil` here if `GetUser()` returns an error,
but the error check happens after this line accesses `user.ID`.
Consider moving the nil check before accessing user properties.
| Confidence | Score |
| :--: | :--: |
| 🟢 strong | 92/100 |
<!-- docker-agent-review -->Each inline comment ends with a small confidence table — a coloured band dot
(🟢 strong · 🟡 moderate · 🟠 weak · ⚪ negligible) and the 0–100 score. The band is
derived from the same thresholds as src/score-confidence.
When a finding has a small, exact fix, the comment also carries a GitHub suggestion block with the precise replacement code, so it can be applied in one click:
**[medium] Timeout is never applied**
`DefaultConfig()` returns a zero `Timeout`; set it before use.
```suggestion
cfg := DefaultConfig()
cfg.Timeout = 30 * time.Second
```
| Confidence | Score |
| :--: | :--: |
| 🟡 moderate | 68/100 |
<!-- docker-agent-review -->GitHub is strict about the lines a suggestion can attach to: a suggestion
anchored outside the diff, spanning more than one hunk, or on a deleted line
makes the whole review fail (HTTP 422). Before posting, the agent runs a
validator that checks every suggestion's line range against the diff and strips
any malformed block (keeping the prose finding), so one bad suggestion can never
lose the whole review. The validator is implemented and unit-tested in
src/validate-suggestions/.
When no issues are found:
✅ Looks good! No issues found in the changed code.AGENTS.md + PR Diff → Drafter (hypotheses) → Verifier (confirm + evidence signals)
→ Confidence score (0–100) → Post Comments
By default, a re-review only covers the commits pushed since the last completed review instead of re-reviewing the full PR diff. This saves tokens, avoids duplicate comments, and skips code that has not changed since the previous cycle.
How it works:
- The last reviewed commit is read from the metadata GitHub records on every
posted review (
commit_idon the Reviews API is the PR head SHA at posting time), so the state survives across workflow runs with no extra bookkeeping. Only completed reviews count — a timed-out or failed run does not mark commits as reviewed. - The diff handed to the agent becomes
git diff <last-reviewed-sha>..HEAD, restricted to files that are still part of the full PR diff (so inline comments never anchor outside what GitHub accepts). - The action falls back to a full review whenever incremental diffing would be unsafe or meaningless:
| Situation | Behavior |
|---|---|
| No previous completed review | Full review |
| Force-push or rebase rewrote the history | Full review |
| Base branch merged into the PR since last review | Full review |
| Last reviewed SHA missing from the clone | Full review |
| Changes since last review cancel out | Full review |
incremental: false input |
Full review |
- On any full re-review, findings are deduplicated against the comments
already posted on the PR (matched by file path, line proximity, and finding
heading similarity — see
src/dedupe-findings/), so a rebase does not produce duplicate threads. The plumbing that decides between incremental and full mode is implemented and unit-tested insrc/incremental-review/.
Set incremental: false (workflow or action input) to force a full review on
every trigger.
Each verified finding gets a precise confidence score (0–100) and a band
(strong / moderate / weak / negligible), computed deterministically from the
verifier's verdict, evidence strength, and context completeness, plus the
drafter↔verifier severity agreement. High-confidence findings are posted as
inline comments (labelled with their confidence); lower-confidence findings are
listed separately rather than dropped. Security and high-severity findings are
always surfaced regardless of score. The model is implemented and unit-tested in
src/score-confidence/.
The inline-posting cutoff is tunable via the confidence-threshold input — a
band name (strong = 80, moderate/medium = 55, weak = 30) or a number
(clamped to the 30–100 range — the weak band floor is the lowest meaningful
cutoff, so negligible findings are never posted inline), defaulting to moderate
(which preserves the prior behavior). Raising it
(e.g. strong) posts only the highest-confidence findings inline and collapses
the rest into the lower-confidence summary; lowering it (e.g. weak) posts weak
findings inline too. The threshold never suppresses security or high-severity
CONFIRMED/LIKELY findings — those are always posted inline.
When you reply to a review comment:
- The
reply-to-feedbackjob checks if the reply is to an agent comment (via<!-- docker-agent-review -->marker) - Verifies the author is an org member/collaborator (authorization gate)
- Builds the full thread context (original comment + all replies in chronological order)
- Runs a Sonnet-powered reply agent that posts a contextual response in the same thread
- Captures feedback as an artifact — saves the comment JSON as a
pr-review-feedbackartifact
On the next review run (on any PR in the same repo):
- The review action downloads all pending
pr-review-feedbackartifacts - A separate feedback agent processes each one and calls
add_memoryto record lessons learned - The processed artifacts are deleted so they're not reprocessed
- The review agent has access to all accumulated memories, calibrating future reviews
This means developer feedback on one PR improves reviews across all future PRs in the repo.
The reviewer supports true multi-turn conversation in PR review threads. When you reply to a review comment:
- Ask a question — the agent explains its reasoning, references specific code, and offers suggestions
- Correct a false positive — the agent acknowledges the mistake and remembers it for future reviews
- Disagree — the agent engages thoughtfully, discusses trade-offs, and considers your perspective
- Add context — the agent thanks you, reassesses its finding, and stores the insight
Agent replies are marked with <!-- docker-agent-review-reply --> (distinct from <!-- docker-agent-review --> on original review comments) to prevent infinite loops. Multi-turn threading works automatically because GitHub's in_reply_to_id always points to the root comment.
Memory persistence: The memory database is stored in GitHub Actions cache. Each review run restores the previous cache, processes any pending feedback, runs the review, and saves with a unique key. Old caches are automatically cleaned up (keeping the 5 most recent).
Evals verify that the reviewer produces consistent, correct results across multiple runs.
cd docker-agent-action
docker agent eval review-pr/agents/pr-review.yaml review-pr/agents/evals/ \
-e GITHUB_TOKEN -e GH_TOKENEach eval file in review-pr/agents/evals/ contains:
messages: The initial user prompt (e.g., a PR URL)evals.relevance: Natural-language assertions checked against the agent's outputevals.setup: Setup commands run before the eval (e.g., installinggh)
| Prefix | Expected outcome |
|---|---|
success-* |
Clean PR, agent should APPROVE |
security-* |
PR with security concerns, agent should COMMENT or REQUEST_CHANGES |
- Find a PR with a known correct outcome (e.g., a clean PR that should be approved, or one with a real bug)
- Create a JSON file with the PR URL as the user message and relevance criteria describing the expected behavior
- Run the eval 3+ times to verify consistency
{
"id": "unique-uuid",
"title": "Description of what this eval tests",
"evals": {
"setup": "apk add --no-cache github-cli",
"relevance": [
"The agent ran 'echo $GITHUB_ACTIONS' before performing the review to detect the output mode",
"The agent output the review to the console as formatted markdown instead of posting via gh api",
"The drafter response is valid JSON containing a 'findings' array and a 'summary' field",
"... assertions about the expected findings and verdict ..."
]
},
"messages": [
{
"message": {
"agentName": "",
"message": {
"role": "user",
"content": "https://github.com/org/repo/pull/123",
"created_at": "2026-01-01T00:00:00-05:00"
}
}
}
]
}Tip: Create multiple eval files for the same PR to test consistency. If the agent produces different verdicts across runs, the failing evals highlight the inconsistency.