# O APPROVE do modelo não é o merge

> Por que o veredito de um agente reviewer é um sinal de entrada, e como transformar isso em política determinística.

- Por: Djan Magno
- Publicado em: 2026-09-19
- Atualizado em: 2026-09-19
- URL: https://t25.io/blog/o-approve-do-modelo-nao-e-o-merge/
- Engenharia · code review com IA, human in the loop, agentes de código, gate de aprovação, evaluateReview, qualidade de software

> **Resumo:** > - O `APPROVE` que um agente reviewer escreve é **um palpite do modelo**, não uma verificação. Tratá-lo como decisão é deixar quem foi avaliado assinar a própria prova.
> - No T25, o veredito final é **recalculado** por uma função determinística a partir dos findings: qualquer finding em escopo bloqueia, mesmo que o modelo tenha escrito APPROVE.
> - Aprendemos isso do jeito caro: por semanas, um QA que respondia "REPROVADO" deixava a tarefa seguir, porque ninguém lia o veredito.
> - O merge na main continua sendo humano. A política decide o que chega até você; você decide o que entra.

## O problema em uma frase

Um modelo pode escrever `"verdict": "APPROVE"` e, três linhas abaixo, listar um bug de implementação com severidade alta dentro do escopo da tarefa. As duas coisas cabem no mesmo JSON. Se o pipeline lê só a primeira, o bug vai para o pull request com um carimbo de aprovado.

Não é defeito de um modelo específico. É o que acontece quando o mesmo texto carrega a evidência e a conclusão, e o sistema confia na conclusão.

## A régua: evidência entra, veredito sai

A saída estruturada do reviewer no T25 tem dois campos: o veredito e uma lista de findings. Cada finding diz a severidade, a categoria, se está dentro do escopo do plano e o que está errado.

```json
{
  "verdict": "APPROVE",
  "findings": [
    {
      "severity": "high",
      "category": "implementation",
      "inScope": true,
      "description": "o retry reusa o token expirado"
    }
  ]
}
```

O pipeline não usa o `verdict` que veio do modelo. Ele passa o relatório por `evaluateReview()`, que refaz a conta:

```ts
// src/pipeline/evaluate.ts (simplificado)
export function evaluateReview(report: ReviewReport): ReviewReport {
  const blocking = blockingFindings(report.findings);
  return {
    verdict: blocking.length === 0 ? 'APPROVE' : 'REQUEST_CHANGES',
    findings: report.findings,
  };
}
```

`blockingFindings()` descarta o que está fora do escopo e mantém o resto. No exemplo acima, o resultado é `REQUEST_CHANGES` e a tarefa volta para implementação com o finding anexado. O modelo continua útil: ele achou o bug. Só não é ele quem decide o que o bug significa.

É exatamente isso que o cartão interativo na [página inicial](https://t25.io/#replica) reproduz: você aperta APPROVE num review que já tem um finding em escopo, e a política recusa.

## O bug que nos ensinou isso

Durante uma rodada de dogfood em agosto de 2026, um agente de QA terminou o relatório com a palavra **"REPROVADO"** e listou dois problemas bloqueadores. A tarefa andou para `REVIEW` como se tivesse passado.

A causa era simples e desconfortável. Reviewer e security tinham parsers de veredito. QA não tinha. O laço de QA verificava só se o processo do agente tinha terminado com sucesso, e um processo que termina bem escrevendo "reprovado" termina bem. Cada vez que uma tarefa passou de QA para review até ali, isso provava que o CLI não travou, não que o QA aprovou.

A correção foi dar ao QA o mesmo tratamento: veredito obrigatório `PASS`/`FAIL` em saída estruturada, validado pelo pipeline, com falha devolvendo a tarefa para `IMPLEMENTING`. A lição que ficou é mais geral que o bug:

> Se um estágio não tem um veredito que o código lê, esse estágio não é um gate. É um log.

## O que conta como bloqueante

Recalcular o veredito só funciona se a regra for explícita. A do T25 cabe em três linhas:

| Finding | Bloqueia? | Por quê |
|---|---|---|
| Em escopo, com causa conhecida (`implementation`, `spec`, `evidence`, `policy`...) | Sim | É trabalho que a tarefa prometeu entregar |
| Fora do escopo do plano (`inScope: false` ou `out-of-scope`) | Não | Vira registro, não retrabalho; senão o loop de correção vira reescrita |
| Em escopo, categoria antiga sem causa (`bug`, `style`, `other`) | Sim, e falha fechado | Vira `unknown`: bloqueia, mas a política não autoriza retrabalho no escuro |

A segunda linha importa tanto quanto a primeira. Um reviewer que acha um problema real em outro módulo não deveria gastar o orçamento da tarefa atual consertando o que ninguém pediu. O finding fica registrado; a tarefa segue.

## Onde o humano entra

Tirar a decisão do modelo não significa passá-la inteira para o código. O T25 tem três pontos de parada humanos:

1. **Aprovação da spec.** Sempre. A spec só vira plano quando tem critérios de aceite parseáveis, o checklist obrigatório está completo e não há comentário aberto.
2. **Aprovação do plano.** Depende do risco da tarefa. Nos padrões, risco baixo não pede aprovação, risco médio pede aprovação do plano, risco alto pede plano e pull request, e crítico exige humano sempre. Cada projeto pode ligar ou desligar o gate de plano.
3. **O merge.** Sempre humano. A fábrica abre o pull request, verifica os checks obrigatórios e para. Quem responde pela main aperta o botão.

A política decide o que chega até você. Você decide o que entra.

## Como aplicar isso no seu pipeline, com ou sem o T25

Se você já roda agentes de review, dá para adotar a ideia hoje:

1. **Peça saída estruturada.** Findings com severidade, categoria e escopo, em JSON, não um parágrafo.
2. **Ignore o veredito do modelo na decisão.** Guarde para auditoria, mas decida com uma função sua sobre os findings.
3. **Falhe fechado.** Saída que não parseia não é aprovação. É erro, com o texto original anexado.
4. **Dê veredito a todo estágio que você chama de gate.** QA incluído. Se o código não lê, não é gate.
5. **Separe escopo.** Finding fora do escopo vira issue, não retrabalho.

## Perguntas frequentes

### Por que não confiar no APPROVE se o modelo é bom?

Porque o problema não é a qualidade do modelo, é quem assina. Mesmo um reviewer excelente pode listar um problema sério e ainda assim concluir que está tudo bem. Recalcular a partir dos findings custa uma função e elimina essa contradição.

### O T25 faz merge automático quando o review aprova?

Não. Um review aprovado leva a tarefa até o pull request. O merge é sempre uma ação humana, depois dos checks obrigatórios do repositório.

### Isso não deixa o pipeline mais lento?

Deixa a tarefa voltar mais vezes para implementação quando há finding em escopo, que é o comportamento desejado. O que fica mais rápido é a sua revisão, porque o que chega ao pull request já passou por uma régua que não depende do humor do modelo.

### Funciona com qualquer CLI de agente?

Sim. O veredito é recalculado a partir da saída estruturada, então não importa se quem revisou foi Claude Code, Codex ou outro CLI da lista de fallback.
