pr-guard v0.1.0 · TS-PRGuard · Apache-2.0pr-guard
Announcement

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.

Lucas DuarteFounder, typestrict.aiNovember 4, 2026

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.

11 to 71 sper pull requestSix runs on pr-guard's own pull request, on a stock GitHub ubuntu-latest runner, from 48 to 1,937 changed lines.
1.9 GBpeak memory, at mostThe same runs: 460 MB at 48 changed lines, 650 MB at 52, 1.9 GB from 382 up.
165 MBTS-PRGuard in 8-bit ONNX150M parameters, five questions in one pass.
4 of 5questions where TS-PRGuard beats JevMacro-F1 on held-out repositories never used in training, by the refund calculation.

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.

pr-guard's sticky comment on a pull request: needs 1 reviewer, the five questions with their answers, the rule findings and how the score was computed
The comment on one of pr-guard's own pull requests: 1,937 changed lines, a CI workflow and a Dockerfile touched, tests not covering the change. Score 69%, one reviewer. "How pr-guard decided" shows the arithmetic, question by question, with the weight of each one.

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.

QuestionHeld-out examplesTS-PRGuardJevGain (95% CI)Result
Does it do what the description says?4360.9280.807+0.109 [+0.078; +0.141]beats Jev
How big is it to review?2210.6600.383+0.196 [+0.131; +0.248]beats Jev
Does it break existing users?2,0000.8700.910-0.037 [-0.048; -0.026]Jev ahead
Do the tests cover it?2,0000.9210.879+0.036 [+0.025; +0.047]beats Jev
Does it introduce a vulnerability?5970.9040.755+0.139 [+0.108; +0.168]beats Jev
Macro-F1 of TS-PRGuard and Jev on the same held-out examples, and the gain the refund calculation measures (a weighted score of four metrics) with its 95% bootstrap interval. A question is won when the whole interval stays above zero and no metric collapses. The samples differ: tests and breaking change, with 2,000 examples each, have the narrowest intervals; size, with 221 (only 17 of them large or very large), has the widest. The audit file to recompute the verdict is linked from the model card.

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: enforce in .github/pr-guard.yml and make the pr-guard status 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 languages key 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 repository

Install

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, instant

The 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.