typestrict.aidocs

PR Guard

An open-source CLI that tells you how many people should review a pull request, with a small model on your CI runner.

pr-guard reads the diff and the description of a pull request, asks the model TS-PRGuard five questions about it, adds deterministic checks (secrets, destructive SQL, sensitive paths, size) and lets a YAML policy decide: no reviewer, one or two, or blocked. It comments on the pull request, labels it and sets one pr-guard status, on GitHub, GitLab or Bitbucket.

It is open source (Apache-2.0): typestrict-lab/pr-guard. TS-PRGuard is a model trained on typestrict.ai (150M parameters, an 8-bit ONNX file of 165 MB, Apache-2.0 too). On a stock ubuntu-latest runner a check took 11 to 71 seconds and 460 MB to 1.9 GB of memory on pull requests of 48 to 1,937 changed lines. Memory stops growing once the diff fills what the model reads (5,000 characters per question, 14,000 for breaking change).

No reviewer is not a merge: the policy asks for no extra human approval. pr-guard comments, labels and sets a status, and never merges; auto_merge: true only adds the pr-guard:auto-merge label for your own automation.

Quick start

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

@v1 is the action's major version; the binary it installs is v0.1.0. The action installs a released binary, caches the model and runs pr-guard check with the workflow's token. fetch-depth: 0 brings the history, so the merge base with the target branch exists.

On a laptop:

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

Shadow and enforce

It starts in shadow mode: it reports on every pull request, the status always passes and says what it would ask for. When its answers look right on your code, write mode: enforce in .github/pr-guard.yml and make pr-guard a required status check. In enforce mode the status fails when the policy blocks, and waits until the pull request has the approvals the verdict asks for.

The questions and the default policy

The model answers each question with the probability of every answer, in one pass on the CPU. Macro-F1 on held-out examples, from the model card:

The model is askedTS-PRGuardJevDefault in the policy
Does it do what the description says?0.9280.807warns above 40% that it does not, blocks above 70%
How big is it to review?0.6600.383warns when very large
Does it break existing users?0.8700.910warns above 50%
Do the tests cover it?0.9210.879warns above 50% that they do not
Does it introduce a vulnerability?0.9040.755warns above 70%

It beats Jev on 4 of 5 questions by the Refund calculation; on compatibility Jev is ahead.

The answers weigh into a score: 85% or more needs no reviewer, 60% or more one, below that two. A warning asks for at least one reviewer, a block for two. The comment shows the arithmetic for each pull request. Every key is optional; pr-guard init prints the whole default, commented:

mode: shadow            # or enforce
model: pr-guard:1

reviewers: { none: 85%, one: 60% }

questions:
  vulnerability: { weight: 3, warn: 70%, block: 90% }

rules:
  secrets: block
  sensitive_paths: { level: warn, paths: [infra/**, "**/auth/**"] }

Each key is explained in docs/policy.md.

Choosing the model

--model, else $PR_GUARD_MODEL, else model in the policy:

ValueWhat it is
pr-guard:1 (default)TS-PRGuard v1, pinned: the exact files this release of pr-guard was tested with, checked by SHA-256.
pr-guard:latestThe newest TS-PRGuard published on Hugging Face, before a pr-guard release pins it.
hf:<owner>/<repo>[@<revision>]Any Hugging Face repository with the same files, such as your own model. $HF_TOKEN for a private one.
https://…A base URL serving decision_ai.json, tokenizer.json and onnx/model_8bit.onnx (a CDN or an internal mirror).
a directoryUsed as it is.

The model is downloaded once, checked file by file against its SHA-256 and cached in ~/.cache/pr-guard.

Your own model

TS-PRGuard learned from public pull requests. To have one that learned from yours, train a model on typestrict.ai with your pull requests as data (see Trainings), then point the policy at it:

model: hf:your-org/your-pr-model

Publishing a model puts the package in a Hugging Face repository with exactly the files pr-guard reads. To keep it private, download the package (typestrict pull <model>) and serve it from your own URL, or use the directory directly.

Other CIs and hosts

The full setup for each is in docs/ci.md and docs/hosts.md:

  • GitLab CI: a CI/CD component on merge request pipelines; findings go to Code Quality and the test report.
  • Bitbucket Pipelines: a pipe on the Docker image; the JUnit report shows on the pull request.
  • CircleCI: an orb with install and check; pass the target branch as base.
  • Bitrise: a step that writes the JUnit report to the Tests tab.
  • Jenkins: a Jenkinsfile example that runs the Docker image on pull request builds.
  • pre-commit: a hook that runs the rules on the staged changes, without the model.
  • Anything else: the install script, Homebrew, Docker (ghcr.io/typestrict-lab/pr-guard:0.1.0) or Cargo, then pr-guard check.

On GitLab it needs a GITLAB_TOKEN with the api scope, on Bitbucket a BITBUCKET_TOKEN; Bitbucket has no labels.

Privacy

No API, no key, no telemetry. The model runs on the CI runner, the code never leaves it, and the only calls pr-guard makes are the model download and the comment, labels and status on your own host. The action never runs code from the pull request: it only reads the diff that the checkout brought.

On this page