pr-guard: quantas pessoas precisam revisar este pull request?
Hoje abrimos o pr-guard, uma checagem open source que roda um modelo de 150M parâmetros no runner do seu próprio CI e, com uma política em YAML escrita pelo seu time, decide se um pull request precisa de zero, um ou dois revisores, ou se deve ser bloqueado.
Um pull request de uma linha e outro de duas mil não deveriam esperar o mesmo número de revisores.
Todo time de engenharia conhece as duas pontas da mesma regra: o PR de uma linha que muda um texto e fica dois dias esperando dois approves, e o PR enorme que mexe em autenticação e passa com um "LGTM" em cinco minutos. "Todo PR precisa de N revisores" é fácil de escrever e difícil de cumprir, porque trata igual mudanças que não têm nada de igual.
A pergunta que queríamos responder é mais modesta do que "revise o meu código": quantas pessoas precisam olhar este pull request? Nenhuma, uma ou duas. Ou, quando for o caso, nenhum merge até alguém olhar com calma. A nossa resposta é o pr-guard, open source sob Apache-2.0, e a versão v0.1.0 sai hoje. Ele não é um scanner nem um agente de code review: é uma camada de governança que decide quanta atenção cada mudança merece.
O que ele faz
O pr-guard lê o diff e a descrição do pull request e faz cinco perguntas ao TS-PRGuard, numa passada só:
- Descrição: a mudança faz o que a descrição diz?
- Vulnerabilidade: ela introduz uma vulnerabilidade de segurança?
- Breaking change: ela quebra quem já usa esse código?
- Testes: os testes da mudança cobrem o que mudou?
- Tamanho: qual o tamanho para revisar, de trivial a muito grande?
O TS-PRGuard v1 foi treinado e avaliado em pull requests descritos em inglês, com código em 11 linguagens: JavaScript, TypeScript, Python, Go, Rust, Java, Kotlin, Swift, Dart, PHP e Ruby. Código em qualquer outra linguagem sai marcado como "not evaluated", e a política trata esse caso com cuidado (mais abaixo).
Junto rodam regras determinísticas, sem modelo nenhum: segredos no diff, SQL destrutivo, caminhos sensíveis (workflows de CI, auth, migrations, Dockerfiles, Terraform, .env), mudanças grandes e dependências. As respostas do modelo viram uma nota ponderada, e uma política em YAML versionada junto com o seu código decide: zero, um ou dois revisores, ou bloqueado.
Depois o pr-guard escreve três coisas no pull request: um comentário fixo (criado uma vez e editado a cada push), labels e um status pr-guard que você pode tornar obrigatório. Funciona com GitHub, GitLab e Bitbucket, e roda em GitHub Actions, GitLab CI, Bitbucket Pipelines, CircleCI, Bitrise, Jenkins, pre-commit ou qualquer shell. Onde o pull request mora e onde o job roda são perguntas separadas: um job no Jenkins pode revisar um pull request do GitHub.

As premissas
- O código não sai do runner. O modelo roda na CPU do próprio CI: sem API, sem chave, sem GPU, sem telemetria. As únicas chamadas que o pr-guard faz são o download do modelo e o comentário, as labels e o status no seu próprio host.
- Rápido o bastante para todo pull request, sem fila.
- Quem decide é a política do seu time, não o modelo. O modelo dá probabilidades; limites, pesos e o que bloqueia ficam num arquivo ao lado do seu código.
- Funciona onde o seu código já está, sem trocar de host nem de CI.
- Começa sem atrapalhar. Ninguém deveria ter um merge travado por uma ferramenta que acabou de instalar.
Por que não mandar o diff para um LLM grande? Porque isso quebra a primeira premissa: o código sairia da sua infraestrutura, com uma chave de API no meio e uma conta por chamada. Para responder cinco perguntas fechadas sobre um diff, um modelo pequeno treinado exatamente nessas perguntas basta, e é a mesma ideia por trás de todo modelo treinado na typestrict.ai.
Quanto custa rodar
Medido no pull request do próprio pr-guard, num runner ubuntu-latest padrão do GitHub: 11 segundos e 460 MB com 48 linhas alteradas (contando o primeiro download do modelo), 23 segundos e 650 MB com 52 linhas de Rust, e de 23 a 71 segundos e 1,9 GB de 382 a 1.937 linhas, o pull request da imagem acima (os 71 segundos incluem baixar o modelo de novo). O pr-guard em si é um único binário em Rust com o ONNX Runtime dentro, sem sidecar e sem nada para hospedar.
O modelo é a sua única dependência de fora. O pr-guard baixa o TS-PRGuard uma vez do Hugging Face, fixado num commit e conferido arquivo por arquivo contra o SHA-256, e o cache do CI guarda. Onde os runners não têm internet, sirva os mesmos arquivos de um mirror, de um CDN ou de um diretório, ou use a imagem Docker, que já vem com o modelo dentro.
O modelo, contra o Jev
O TS-PRGuard foi treinado com a typestrict.ai, do mesmo jeito que um cliente treina um modelo, no dataset TS-PRGuard, a partir do TS-Ettin-150M. De 124.194 exemplos rotulados, 10.389 foram separados antes do treino para comparar com o Jev, o modelo de referência do mercado, nos mesmos exemplos.
Eles foram separados por repositório, não por exemplo. Cada um dos 8.355 repositórios dos dados foi inteiro para um lado, cerca de um em cada dez para a avaliação, o mesmo em todas as perguntas, então o modelo é medido em projetos que nunca viu. Duplicatas saíram, e exemplos de treino que repetem conteúdo da avaliação (forks, arquivos copiados) foram retirados antes do treino. A comparação com o Jev usa todos os exemplos separados de descrição, tamanho e vulnerabilidade, e um sorteio de 2.000 em breaking change e testes. Ainda são projetos públicos das mesmas fontes, não o seu código: os projetos de exemplo e o shadow mode, mais abaixo, são como você confere no seu.
| Pergunta | Exemplos separados | TS-PRGuard | Jev | Ganho (IC 95%) | Resultado |
|---|---|---|---|---|---|
| Faz o que a descrição diz? | 436 | 0,928 | 0,807 | +0,109 [+0,078; +0,141] | ganha do Jev |
| Qual o tamanho para revisar? | 221 | 0,660 | 0,383 | +0,196 [+0,131; +0,248] | ganha do Jev |
| Quebra quem já usa? | 2.000 | 0,870 | 0,910 | -0,037 [-0,048; -0,026] | Jev na frente |
| Os testes cobrem a mudança? | 2.000 | 0,921 | 0,879 | +0,036 [+0,025; +0,047] | ganha do Jev |
| Introduz uma vulnerabilidade? | 597 | 0,904 | 0,755 | +0,139 [+0,108; +0,168] | ganha do Jev |
O TS-PRGuard ganha em 4 das 5 perguntas. Em breaking change, o Jev está na frente (0,910 contra 0,870), e preferimos dizer isso aqui do que você descobrir sozinho.
O que saber antes de ligar
- Ele começa em shadow mode, de propósito. Comenta em todo pull request e nunca falha nenhum, para você ver as respostas no seu próprio código antes de elas contarem. Quando fizerem sentido, escreva
mode: enforceem.github/pr-guard.ymle torne o statuspr-guardobrigatório. - Ele é cuidadoso fora do que foi medido. Código numa linguagem fora das 11 acima sai marcado como "not evaluated", pede pelo menos um revisor e nunca entra em auto-merge; a lista é a chave
languagesda política. Dentro das 11, a pergunta de vulnerabilidade quase não tem exemplos separados em Kotlin, Swift e Dart, e descrições em outros idiomas não foram avaliadas. - Vulnerabilidade só avisa, por padrão. No benchmark, todo exemplo separado é uma correção real de segurança, uma vez como a correção e outra invertida, então metade deles (299 de 597) introduz uma vulnerabilidade. Ali, um aviso a partir de 70% acerta 90% das vezes, pega 91% das mudanças vulneráveis, dispara em 9,7% das seguras e deixa passar 8,7%. Pull requests de verdade quase nunca introduzem uma vulnerabilidade, então espere mais alarmes falsos: nos 38 pull requests de exemplo do repositório, o aviso disparou em 4 das 28 mudanças limpas e pegou 5 das 10 vulnerabilidades plantadas. Por isso o padrão avisa em vez de bloquear, e a próxima versão do modelo vai melhorar isso. Se ele acertar no seu código, acrescente
block:na política. - Ele decide quantos revisores, não o que eles devem achar. O pr-guard não substitui um revisor.
Treine nos seus próprios pull requests
O TS-PRGuard aprendeu com pull requests públicos de muitos projetos, e é onde o pr-guard começa. O seu time tem os próprios costumes: o que é sensível, o que quebra os seus clientes, o que precisa de teste. O modelo que conhece esses costumes é um treinado nos seus pull requests: na typestrict.ai você treina esse modelo, medido contra o Jev nos seus próprios exemplos separados, publica no Hugging Face e aponta a política do pr-guard para ele:
# .github/pr-guard.yml
model: hf:<owner>/<repo> # $HF_TOKEN for a private repositoryComo instalar
No GitHub Actions, um 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, instantOs outros CIs, as chaves da política e como escolher o modelo estão na documentação do PR Guard. O código está em github.com/typestrict-lab/pr-guard e o modelo em huggingface.co/typestrict/TS-PRGuard. Se ele errar num pull request seu, conte para nós: é assim que a próxima versão fica melhor.