PT EN
Install

The model's APPROVE is not the merge

Why an AI reviewer's verdict is an input signal, and how to turn it into deterministic policy.

Ler em português Markdown version

An orange hangar gate closed at night, a single light on.

The problem in one sentence

A model can write "verdict": "APPROVE" and, three lines down, list a high-severity implementation bug inside the task's scope. Both fit in the same JSON. If the pipeline only reads the first one, the bug ships to the pull request with an approval stamp on it.

That isn't a flaw of one particular model. It's what happens when the same text carries both the evidence and the conclusion, and the system trusts the conclusion.

The rule: evidence in, verdict out

T25's reviewer returns structured output with two fields: a verdict and a list of findings. Each finding has a severity, a category, whether it's inside the plan's scope, and what's wrong.

{
  "verdict": "APPROVE",
  "findings": [
    {
      "severity": "high",
      "category": "implementation",
      "inScope": true,
      "description": "retry reuses the expired token"
    }
  ]
}

The pipeline doesn't use the verdict the model sent. It runs the report through evaluateReview(), which does the math again:

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

blockingFindings() drops anything out of scope and keeps the rest. In the example above the result is REQUEST_CHANGES, and the task goes back to implementation with the finding attached. The model is still useful: it found the bug. It just doesn't get to decide what the bug means.

That's exactly what the interactive card on our home page reproduces (in Portuguese): you press APPROVE on a review that already has an in-scope finding, and the policy refuses.

The bug that taught us this

During a dogfooding round in August 2026, a QA agent ended its report with the word "REPROVADO" (Portuguese for "failed") and listed two blocking problems. The task moved on to REVIEW as if it had passed.

The cause was simple and uncomfortable. Reviewer and security had verdict parsers. QA didn't. The QA loop only checked whether the agent's process exited successfully, and a process that successfully writes "failed" exits successfully. Every time a task had moved from QA to review until then, it proved the CLI didn't crash, not that QA approved.

The fix was to give QA the same treatment: a mandatory PASS/FAIL verdict in structured output, validated by the pipeline, with a failure sending the task back to IMPLEMENTING. The lesson is broader than the bug:

If a stage has no verdict that code reads, that stage isn't a gate. It's a log.

What counts as blocking

Recomputing the verdict only works if the rule is explicit. T25's fits in three rows:

Finding Blocks? Why
In scope, known cause (implementation, spec, evidence, policy...) Yes It's work the task promised to deliver
Out of the plan's scope (inScope: false or out-of-scope) No It gets recorded, not reworked; otherwise the fix loop turns into a rewrite
In scope, legacy category with no cause (bug, style, other) Yes, fail closed Becomes unknown: it blocks, but policy won't authorize blind rework

The second row matters as much as the first. A reviewer who spots a real problem in another module shouldn't spend the current task's budget fixing something nobody asked for. The finding is logged; the task moves on.

Where the human comes in

Taking the decision away from the model doesn't mean handing all of it to code. T25 has three human stops:

  1. Spec approval. Always. A spec only becomes a plan once it has parseable acceptance criteria, the required checklist is complete, and no comment is left open.
  2. Plan approval. Driven by the task's risk. With the defaults, low risk needs no approval, medium needs plan approval, high needs plan and pull request approval, and critical always requires a human. Each project can turn the plan gate on or off.
  3. The merge. Always human. The factory opens the pull request, checks the required status checks, and stops. Whoever answers for main presses the button.

Policy decides what reaches you. You decide what goes in.

How to apply this in your own pipeline, with or without T25

If you already run AI review agents, you can adopt the idea today:

  1. Ask for structured output. Findings with severity, category and scope, in JSON, not a paragraph.
  2. Ignore the model's verdict when deciding. Keep it for audit, but decide with your own function over the findings.
  3. Fail closed. Output that doesn't parse isn't approval. It's an error, with the original text attached.
  4. Give every stage you call a gate a verdict. QA included. If code doesn't read it, it isn't a gate.
  5. Separate scope. An out-of-scope finding becomes an issue, not rework.

FAQ

Why not trust APPROVE if the model is good?

Because the problem isn't model quality, it's who signs. Even an excellent reviewer can list a serious problem and still conclude everything is fine. Recomputing from findings costs one function and removes that contradiction.

Does T25 auto-merge when review approves?

No. An approved review takes the task as far as the pull request. The merge is always a human action, after the repository's required checks.

Doesn't this slow the pipeline down?

It sends tasks back to implementation more often when there's an in-scope finding, which is the point. What gets faster is your review, because whatever reaches the pull request has already passed a rule that doesn't depend on the model's mood.

Does it work with any agent CLI?

Yes. The verdict is recomputed from structured output, so it doesn't matter whether the review came from Claude Code, Codex or another CLI in the fallback list.

Run T25 on your own machine.

Access is by invite: a personal download link arrives by e-mail, the installer verifies the package checksum and doctor --evaluation validates the environment. Free for the 30 days of the early evaluators program — your code and credentials never leave your machine.

t25 doctor --evaluation
t25 create "Add retry with backoff to the HTTP client"

Request an invite