pr-guard: how many people should review this pull request?
Today we are opening pr-guard, an open source check that runs a 150M-parameter model on your own CI runner and, under a YAML policy your team writes, decides whether a pull request needs zero, one or two reviewers, or should be blocked.
A one-line pull request and a two-thousand-line one should not wait for the same number of reviewers.
Every engineering team knows both ends of the same rule: the one-line PR that changes a piece of text and sits for two days waiting for two approvals, and the huge PR that touches authentication and goes through with an "LGTM" in five minutes. "Every PR needs N reviewers" is easy to write and hard to follow, because it treats alike changes that have nothing alike.
The question we wanted to answer is more modest than "review my code": how many people need to look at this pull request? None, one or two. Or, when it calls for it, no merge until someone takes a careful look. Our answer is pr-guard, open source under Apache-2.0, and version v0.1.0 is out today. It is not a scanner and not a code review agent: it is a governance layer that decides how much attention each change deserves.
What it does
pr-guard reads the diff and the description of the pull request and asks TS-PRGuard five questions, in a single pass:
- Description: does the change do what the description says?
- Vulnerability: does it introduce a security vulnerability?
- Breaking change: does it break existing users of this code?
- Tests: do the tests in the change cover what changed?
- Size: how big is it to review, from trivial to very large?
TS-PRGuard v1 was trained and evaluated on pull requests described in English, with code in 11 languages: JavaScript, TypeScript, Python, Go, Rust, Java, Kotlin, Swift, Dart, PHP and Ruby. Code in any other language is marked "not evaluated", and the policy treats it with care (more below).
Deterministic rules run alongside, with no model at all: secrets in the diff, destructive SQL, sensitive paths (CI workflows, auth, migrations, Dockerfiles, Terraform, .env), big changes and dependency changes. The model's answers become a weighted score, and a YAML policy versioned with your code decides: zero, one or two reviewers, or blocked.
Then pr-guard writes three things on the pull request: a sticky comment (created once and edited on every push), labels and one pr-guard status you can make required. It works with GitHub, GitLab and Bitbucket, and runs on GitHub Actions, GitLab CI, Bitbucket Pipelines, CircleCI, Bitrise, Jenkins, pre-commit or any shell. Where the pull request lives and where the job runs are separate questions: a Jenkins job can review a GitHub pull request.

The premises
- The code never leaves the runner. The model runs on the CI's own CPU: no API, no key, no GPU, no telemetry. The only calls pr-guard makes are the model download and the comment, labels and status on your own host.
- Fast enough for every pull request, with no queue.
- Your team's policy decides, not the model. The model gives probabilities; thresholds, weights and what blocks live in a file next to your code.
- It works where your code already is, without switching host or CI.
- It starts without getting in the way. Nobody should have a merge blocked by a tool they just installed.
Why not send the diff to a large LLM? Because that breaks the first premise: the code would leave your infrastructure, with an API key in the middle and a bill per call. To answer five closed questions about a diff, a small model trained on exactly those questions is enough, and it is the same idea behind every model trained on typestrict.ai.
What it costs to run
Measured on pr-guard's own pull request, on a stock GitHub ubuntu-latest runner: 11 seconds and 460 MB at 48 changed lines (first model download included), 23 seconds and 650 MB at 52 lines of Rust, and 23 to 71 seconds and 1.9 GB from 382 to 1,937 lines, the pull request in the image above (the 71 seconds include downloading the model again). pr-guard itself is a single Rust binary with ONNX Runtime inside, with no sidecar and nothing to host.
The model is its one outside dependency. pr-guard downloads TS-PRGuard once from Hugging Face, pinned to a commit and checked file by file against its SHA-256, and the CI cache keeps it. Where runners have no internet, serve the same files from a mirror, a CDN or a directory, or use the Docker image, which ships with the model inside.
The model, against Jev
TS-PRGuard was trained with typestrict.ai, the same way a customer trains a model, on the TS-PRGuard dataset, starting from TS-Ettin-150M. Of 124,194 labeled examples, 10,389 were set aside before training to compare it with Jev, the market reference model, on the same examples.
They were set aside by repository, not by example. Each of the 8,355 repositories in the data went whole to one side, about one in ten to evaluation, the same in every question, so the model is measured on projects it never saw. Duplicates were dropped, and training examples that repeat held-out content (forks, copied files) were removed before training. The comparison with Jev uses every held-out example of description, size and vulnerability, and a draw of 2,000 for breaking change and tests. Those are still public projects from the same sources, not your code: the example projects and shadow mode, below, are how you check it on yours.
| Question | Held-out examples | TS-PRGuard | Jev | Gain (95% CI) | Result |
|---|---|---|---|---|---|
| Does it do what the description says? | 436 | 0.928 | 0.807 | +0.109 [+0.078; +0.141] | beats Jev |
| How big is it to review? | 221 | 0.660 | 0.383 | +0.196 [+0.131; +0.248] | beats Jev |
| Does it break existing users? | 2,000 | 0.870 | 0.910 | -0.037 [-0.048; -0.026] | Jev ahead |
| Do the tests cover it? | 2,000 | 0.921 | 0.879 | +0.036 [+0.025; +0.047] | beats Jev |
| Does it introduce a vulnerability? | 597 | 0.904 | 0.755 | +0.139 [+0.108; +0.168] | beats Jev |
TS-PRGuard wins on 4 of the 5 questions. On breaking changes, Jev is ahead (0.910 against 0.870), and we would rather say so here than have you find out on your own.
What to know before you turn it on
- It starts in shadow mode, on purpose. It comments on every pull request and never fails one, so you see its answers on your own code before they count. When they look right, write
mode: enforcein.github/pr-guard.ymland make thepr-guardstatus required. - It is careful outside what was measured. Code in a language outside the 11 above is marked "not evaluated", asks for at least one reviewer and never auto-merges; the list is the
languageskey of the policy. Within the 11, the vulnerability question has almost no held-out examples in Kotlin, Swift and Dart, and descriptions in other human languages were not evaluated. - Vulnerability only warns by default. On the benchmark, every held-out example is a real security fix, once as the fix and once reversed, so half of them (299 of 597) introduce a vulnerability. There, a warning from 70% is 90% precise, catches 91% of the vulnerable changes, fires on 9.7% of the safe ones and misses 8.7%. Real pull requests almost never introduce a vulnerability, so expect more false alarms: on the 38 example pull requests in the repository, the warning went off on 4 of the 28 clean changes and caught 5 of the 10 planted vulnerabilities. That is why the default warns instead of blocking, and the next version of the model will improve it. If it is right on your code, add
block:to the policy. - It decides how many reviewers, not what they should find. pr-guard does not replace a reviewer.
Train it on your own pull requests
TS-PRGuard learned from public pull requests of many projects, and it is where pr-guard starts. Your team has its own habits: what is sensitive, what breaks your customers, what needs tests. The model that knows them is one trained on your pull requests: on typestrict.ai you can train it, measured against Jev on your own held-out examples, publish it on Hugging Face and point pr-guard's policy at it:
# .github/pr-guard.yml
model: hf:<owner>/<repo> # $HF_TOKEN for a private repositoryInstall
On GitHub Actions, one workflow:
# .github/workflows/pr-guard.yml
on: pull_request
permissions:
contents: read
pull-requests: write # the comment and labels
statuses: write # the pr-guard status
jobs:
pr-guard:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: typestrict-lab/pr-guard@v1# On your machine
curl -fsSL https://raw.githubusercontent.com/typestrict-lab/pr-guard/main/install.sh | sh
pr-guard check # this branch against its merge base
pr-guard check --staged --no-model # the staged changes, rules only, instantThe other CIs, the policy keys and how to choose the model are in the PR Guard docs. The code is at github.com/typestrict-lab/pr-guard and the model at huggingface.co/typestrict/TS-PRGuard. If it gets one of your pull requests wrong, tell us: that is how the next version gets better.