Introduction
A hardcoded key or an unpinned base image slips past a reviewer focused on logic. pr-agent closes that gap: an internal Action that runs Claude or GPT-5.6, via Bedrock, against every pull request’s diff, with the review policy owned centrally instead of scattered across every repo that adopts it.
Most teams don’t lack reviewers who know what to look for — they lack the bandwidth to apply that judgment on every diff, across a dozen repos and as many stacks, every time. A checklist works until someone skips it under pressure; a linter catches syntax, not a credential pasted in plaintext. pr-agent isn’t a stand-in for a reviewer’s judgment on logic or design — it’s insurance that the mechanical stuff never depends on who was free that day.
- Two model providers — Google, Claude or GPT-5.6 via Bedrock, swappable with one config value.
- Multi-stack by default — dedicated rules for 12+ languages and stacks, applied only where they show up in the diff.
- Custom rules, no fork required — a repo adds its own styleguide.md on top of the baseline, no changes to the agent itself.
- Per-repo control — each repo layers its own rules, or replaces the baseline outright, independent of every other repo.
The integration from a consumer repo’s side is one workflow file:

Everything else — the fork guard, permission checks, language detection, fail-open behavior, and the rules it applies — lives in pr-agent’s own repo. This post covers how that becomes centrally-owned policy, what the reviewer flags, how to add your own rules, day-to-day commands, and where else it runs.
Part 1 — Wired into CI, owned centrally

One workflow file, policy owned elsewhere
config.yaml and styleguide.md are read from pr-agent itself, not the repo being reviewed — a repo gets a different file only by deliberately setting config-path or styleguide-path, so a local copy can’t quietly weaken the review. The baseline rules aren’t invented from scratch either: Python borrows from Bandit and CERT, Terraform from tfsec and Checkov, one file per stack, sent only when it appears in the diff.
Pair this with an org ruleset that makes the caller workflow required, and a PR whose review never ran simply can’t merge.
The fork boundary, solved by not being clever
Fork PRs aren’t reviewed — GitHub withholds secrets from fork-triggered workflows, and the reviewer needs BEDROCK_API_KEY. The tempting fix, pull_request_target, runs the workflow from the base branch while checking out untrusted head code — the pattern behind most Actions supply-chain incidents.
Deliberately not clever
The job condition just checks head.repo.full_name == github.repository and exits otherwise. No secrets reach untrusted code.
Fail-open, always
The review step runs with continue-on-error: true; a ProviderError becomes a notice comment, not a crash. Exit code 2 is reserved for an invalid config.yaml. Everything else degrades the same way:
- Bedrock unreachable or rate-limited — a notice explains why.
- The model call errors — same kind of notice.
- The Python env fails to install — the step fails, but the workflow stays green.
None of these block a merge. A green check doesn’t prove a review happened look for the summary comment.
Part 2 — What the reviewer actually flags
Only the languages present in the diff
The prompt only carries idioms for languages actually present, detected mostly by extension (Dockerfiles, GitHub Actions workflow YAML, and Ansible go by filename or path shape instead). A React PR gets hook guidance, a Go PR gets goroutine guidance — neither gets the other’s, and anything undetected simply isn’t annotated.
What’s in scope
Four focus areas cover most of what the baseline looks for, and each one is a toggle in config.yaml’s review.focus:
- Secrets and credentials — passwords, tokens or keys in source or pipeline files.
- Containers and infrastructure — root-run processes, unpinned images, secrets baked into layers.
- CI/CD correctness — unquoted shell expansions, credentials in logs.
- Application correctness — logic errors, resource leaks, query injection
Prompt injection is a finding, not a jailbreak to prevent
The diff, any comment, and any /ask question are untrusted data, never instructions. Code that tries to make the model ignore its rules gets reported as a CRITICAL finding — the one place in the config that isn’t a toggle.

What a finding looks like

No database: state lives in a GitHub comment
The last-reviewed SHA lives as an HTML marker in the bot’s summary comment. Each push decodes it, diffs the new commits, and skips findings already caught at that line. A force-push that breaks it falls back to a full review — zero extra infrastructure.

Part 3 — Making it yours: custom rules
Add a rule without touching the agent’s repo
Drop a .github/pr-agent/styleguide.md into the repo being reviewed and it’s appended to the baseline on every run — plain English naming the pattern and why it’s wrong:
.github/pr-agent/styleguide.md · in the repo being reviewed
## Our team’s own rules– Every `aws_s3_bucket` resource must set `versioning` and
`server_side_encryption_configuration` explicitly — we’ve had two
incidents from a bucket that inherited neither.
Per-stack rules
The same model applies one level deeper: a .github/pr-agent/rules/terraform.md in your repo is appended to the agent’s Terraform rules — a team encodes its own footguns per stack without touching the baseline.
That additive model is the default. A rarer lever, config-path/styleguide-path, replaces the policy outright for one repo — opt-in, for when a repo needs its own policy, not just extra rules.
Part 4 — Day to day: commands and providers
Slash commands
Comment on a pull request with any of these and the agent responds directly in the thread:

Authorization is a live permissions check, not an allow-list, for every command except /help. Comment text arrives via an environment variable, never interpolated into a shell string.
Who can run them
- read — not enough by default.
- write — the default minimum.
- maintain — short of full ownership.
- admin — full control.
Two providers, one credential, real retry logic
AI_PROVIDER switches between Claude and GPT-5.6, both via Bedrock on the same BEDROCK_API_KEY. Both clients share one retry helper — specific HTTP statuses and transport errors, with backoff and jitter; anything else fails immediately.
What it won’t catch
- Diff-only visibility — no whole-file or cross-file context.
- A comment cap — max_review_comments, 20 by default.
- A severity floor — comment_severity_threshold, MEDIUM by default; demoted, not dropped.
Part 5 — Rolling it out, keeping it current
Required, not opt-in
An org-level ruleset makes the caller workflow a required status check across every repo it applies to — a PR can’t merge without a run. Voluntary adoption would be the same problem the config split solves: policy that can be skipped isn’t policy.
Trying it, changing it
The agent reviews its own pull requests — a change to .github/pr-agent is reviewed by the previous version of itself. Consumers pin a release tag; the uses: reference bumps in the same PR, or a tagged workflow runs an untagged action.
Conclusion
Individually, none of these pieces are exotic. Together they guarantee one property: no repo’s review is weaker than any other’s, and none can opt out while still showing green.
- Central ownership — a repo can add to the policy, not replace it, unless it opts in.
- Fork-safe by construction — a same-repo check, not a secrets trick.
- Fails open, never silently — never blocks a merge, always says so.
- No external state — review rides inside the PR’s own comment.
- Required, not opt-in — an org ruleset, not a voluntary add.