Phase 14 · Lesson 38 of 54

Verification Gates

The agent does not get to mark its own work as done. A verification gate reads the scope contract, the feedback log, the rule report, and the diff, and answers a single question: is this task actually complete? If the gate says no, the task is not done, no matter what the chat says.

Lesson 38 of 54Build~55 minutesPython (stdlib)

Before this: Lesson 33 of Phase 14 (Rules), lesson 36 of Phase 14 (Scope), lesson 37 of Phase 14 (Feedback)

On this page

Learning Objectives

  • Define a verification gate as a deterministic function over workbench artifacts.
  • Combine rule report, scope report, feedback records, and diff into a single verdict.
  • Emit a verification_report.json the reviewer agent and CI can both read.
  • Refuse to advance a task on any block-severity failure, without exception.

The Problem

Agents declare success too easily. Three failure shapes dominate:

  • "Looks good." The model read its own diff and decided it was correct.
  • "Tests passed." Said with confidence. No record of the test actually running.
  • "Acceptance met." Acceptance criteria interpreted loosely enough to mean "anything resembling done."

The workbench fix is a single verification gate that reads the artifacts the agent has already produced and makes the call. The gate is deterministic. The gate is in version control. The gate is wired into CI. The agent cannot bribe it.

The Concept

Diagram (opens full size)

What the gate checks

Check Source artifact Severity
All acceptance commands ran feedback_record.jsonl block
All acceptance commands exited zero feedback_record.jsonl block
Scope check has no forbidden writes scope_report.json block
Scope check has no off-scope writes scope_report.json block or warn
All block-severity rules pass rule_report.json block
No null exit codes in feedback feedback_record.jsonl block
Touched files match scope.allowed_files both warn

A warn finding annotates the verdict; a block finding prevents passed: true.

Deterministic, not probabilistic

The gate must produce the same verdict for the same artifact set every time. No LLM judges. LLM judges belong on the reviewer side (Phase 14 · 39) where the goal is qualitative evaluation, not status.

One report, one path

The gate emits one verification_report.json per task close-out, written under outputs/verification/<task_id>.json. CI consumes the same path. Multiple gates with different paths fork the source of truth.

Refuse without exception

Block-severity findings cannot be overridden by the agent. They can only be overridden by a human, with a recorded override_reason and an overridden_by user id. The override is a signed change, not an agent decision.

wb-gate-sequence

Build It

code/main.py implements:

  • A loader for each input artifact, all stubbed locally so the lesson is self-contained.
  • A verify(task_id, artifacts) -> VerdictReport pure function.
  • A printer that shows the per-check results and the final pass/fail.
  • A demo with three task scenarios: clean pass, scope creep, missing acceptance.

Run it:

python3 code/main.py

Output: three verdict reports, each saved next to the script.

Production patterns in the wild

Five patterns elevate the gate from "another lint job" to "the deciding edge."

Defense-in-depth, not single gate. Pre-commit hook → CI status check → pre-tool authz hook → pre-merge gate. Each layer is deterministic so a failure in one layer is caught by the next. microservices.io's March 2026 playbook is explicit: the pre-commit hook is non-bypassable because, unlike a model-side skill, it does not depend on the agent following instructions. The verification gate sits at the CI / pre-merge layer.

Defense by deterministic check, model-judge only for nuance. Anthropic's 2026 Hybrid Norm pairing: verifiable rewards (unit tests, schema checks, exit codes) answer "did the code solve the problem?" — LLM rubrics answer "is the code readable, secure, on-style?" The gate runs the first class; the reviewer (Phase 14 · 39) runs the second. Mixing them collapses the signal.

Signed override log, not Slack threads. Every override emits a row in outputs/verification/overrides.jsonl with: timestamp, finding code, reason, signing user, current HEAD commit. The runtime refuses any override that lacks the signature; the audit trail is git-tracked. This is the line between an override policy and an override theater.

Coverage floor as a first-class check. A coverage_report.json feeds a coverage_floor (default 80%) check. The gate fails if measured coverage drops below the floor or below the previous merge's floor by more than 1 percentage point. Without this check, agents quietly delete tests that fail and the verification reports stay green.

--strict mode promotes warns to blocks. For release branches, ship-blocking PRs, or post-incident triage, --strict makes every warning a hard fail. The flag is opt-in by branch; not the global default, because strict-on-everything corrodes day-to-day flow.

Use It

Production patterns:

  • CI step. A verify_agent job runs the gate against the agent's final artifacts. Merge protection refuses without passed: true.
  • Pre-handoff hook. The agent runtime calls the gate before generating the handoff doc. No green verdict, no handoff.
  • Manual triage. Operators read the report when an agent claims success and a human suspects it.

The gate is the deciding edge in the workbench flow. Every other surface is upstream of it.

Ship It

outputs/skill-verification-gate.md wires the gate into a specific project: which acceptance commands feed it, which rules are block-severity, which off-scope writes are tolerated, how the override audit log is stored.

Exercises

  1. Add a coverage_floor check: the test command must produce a coverage report with at least 80%. Decide which artifact carries the floor.
  2. Support a --strict mode that promotes every warn to block. Document the cases where strict mode is the right default.
  3. Make the gate produce a Markdown summary in addition to JSON. Defend which fields belong in the summary.
  4. Add a time_since_last_human_touch check: any file edited within 60 seconds of a human keystroke is exempt from off-scope flags.
  5. Run the gate on a real agent diff from your product. How many findings are real and how many are noise? Where does the gate need to grow?

Key Terms

Term What people say What it actually means
Verification gate "The check that stops things" Deterministic function over workbench artifacts producing a pass/fail verdict
Block severity "Hard fail" A finding that prevents passed: true and requires a signed override
Override log "Why we let it through" Signed entries with reason and user id, audited by review
Acceptance command "The proof" A shell command whose zero exit is what done means
One report path "Source of truth" outputs/verification/<task_id>.json, consumed by CI and humans alike

Further Reading

Check yourself

Quick quiz

  1. What single question does the verification gate answer?

    1. Is this task actually complete? (reading scope, rule, feedback, and diff artifacts)
    2. Is the prompt optimal?
    3. Is the model fast?
    4. Is the token budget healthy?
    Show the answer

    A. Is this task actually complete? (reading scope, rule, feedback, and diff artifacts) The gate is a deterministic function over workbench artifacts producing a pass/fail verdict.

  2. Why must the gate be deterministic?

    1. The same artifact set must produce the same verdict every time; LLM judges belong in the reviewer (qualitative), not the gate (status)
    2. Providers require it
    3. Determinism is free
    4. It saves money
    Show the answer

    A. The same artifact set must produce the same verdict every time; LLM judges belong in the reviewer (qualitative), not the gate (status) Mixing model judgment into the gate collapses the deterministic/qualitative split.

  3. What is the gate's override discipline?

    1. Block-severity findings can only be overridden by a human with a recorded override_reason and overridden_by user id in a signed audit log
    2. Overrides are forbidden
    3. Anyone can override silently
    4. Override requires a manager email
    Show the answer

    A. Block-severity findings can only be overridden by a human with a recorded override_reason and overridden_by user id in a signed audit log Signed overrides land in outputs/verification/overrides.jsonl; agent cannot self-override.

  4. What is the Hybrid Norm pairing the lesson cites?

    1. GPU and CPU split
    2. Hot/cold prompts
    3. Verifiable rewards (tests, schemas, exit codes) answer 'did it solve the problem?'; LLM rubrics answer 'is it readable, secure, on-style?'
    4. Cache vs no-cache
    Show the answer

    C. Verifiable rewards (tests, schemas, exit codes) answer 'did it solve the problem?'; LLM rubrics answer 'is it readable, secure, on-style?' Anthropic 2026 guidance: gate runs the first class; reviewer (Lesson 39) runs the second.

  5. How does defense-in-depth layer the gates?

    1. Pre-commit hook -> CI status check -> pre-tool authz hook -> pre-merge gate; each layer is deterministic so failure in one is caught by the next
    2. Single gate at merge time
    3. Only a chat reminder
    4. Only IDE warnings
    Show the answer

    A. Pre-commit hook -> CI status check -> pre-tool authz hook -> pre-merge gate; each layer is deterministic so failure in one is caught by the next Multiple non-bypassable layers catch what a single layer would miss.

  6. What does a coverage_floor check protect against?

    1. Hot-path latency
    2. Agents quietly deleting tests that fail; the gate fails if measured coverage drops below the floor or last merge by more than 1 percentage point
    3. Cold starts
    4. Outdated lockfiles
    Show the answer

    B. Agents quietly deleting tests that fail; the gate fails if measured coverage drops below the floor or last merge by more than 1 percentage point Without a floor, agents can silently lower coverage to keep the verdict green.

  7. When should --strict mode promote every warn to block?

    1. Always
    2. Never
    3. Only on Sundays
    4. Release branches, ship-blocking PRs, post-incident triage; not the daily default because strict-on-everything corrodes flow
    Show the answer

    D. Release branches, ship-blocking PRs, post-incident triage; not the daily default because strict-on-everything corrodes flow --strict is opt-in by branch; reserve for high-stakes moments.

Questions people ask

How do you know an AI agent actually finished the task?

Do not rely on its summary. Use an automatic gate that checks the changes against the agreed scope, confirms tests and rule checks passed, and looks at the real output. If any check fails, the task stays open.

What is a quality gate?

A quality gate is a checkpoint that work must pass before moving forward, such as passing tests, a clean lint or a review sign-off. For agents, it replaces trusting the agent's own word with evidence.

Next lessonReviewer Agent: Separate Builder from MarkerPrevious: Runtime Feedback Loops

Lessons from AI Engineering from Scratch by Rohit Ghumare and contributors, MIT license. Notes for owners by Lozo. License

Next step

Rather have it built for you?

Reading is optional. Tell me what's stuck and I'll build it into the business you already run.

  • A real conversation, not a pitch
  • You keep your accounts
  • Nothing sends without your yes