Build This Now
Build This Now
Keyboard ShortcutsStatus Line Guide
Hooks GuideCross-Platform Hooks for Claude CodeClaude Code Setup HooksStop HooksSelf-Validating Claude Code AgentsClaude Code Session HooksContext Backup Hooks for Claude CodeSkill Activation HookClaude Code Permission Hook
Get Build This Now
speedy_devvkoen_salo
Blog/Toolkit/Hooks/Self-Validating Claude Code Agents

Self-Validating Claude Code Agents

Wire PostToolUse hooks, Stop hooks, and read-only reviewer agents into agent definitions so bad output never reaches you.

Problem: An agent hands back work that looks fine at a glance. Then the linter screams, an export is missing, and a whole file was skipped. You only spot it during review, twenty minutes after the run finished.

Quick Win: Drop a PostToolUse hook straight into the agent definition. Every file this agent writes goes through the linter before you ever see it:

# .claude/agents/frontend-builder.md
---
name: frontend-builder
description: Build React components with automatic quality checks
model: sonnet
hooks:
  PostToolUse:
    - matcher: "Write|Edit"
      hooks:
        - type: command
          command: 'npx eslint --fix "$CLAUDE_TOOL_INPUT_FILE_PATH" && npx prettier --write "$CLAUDE_TOOL_INPUT_FILE_PATH"'
---
You are a frontend builder agent. Create React components following
the project's established patterns. Every file you write is automatically
linted and formatted by your embedded hooks.

Unlinted code is no longer an option for this agent. The check belongs to who it is, not something you bolt on afterwards.

There are three tiers to a self-validating agent. Each one catches a different class of bug, and they stack cleanly.

Micro is per tool call. A PostToolUse hook on the agent definition runs a linter, a formatter, or a type checker right after a file gets written. Anything broken gets caught seconds after it happens.

Macro is whole-job. When the agent tries to finish, a Stop hook asks the real questions: are the required files there, do exports exist, does the test suite go green. If any of it fails, the agent is not allowed to call the job done.

Team pulls in a second agent. A read-only reviewer opens the builder's work with a clean context window. It is the builder/reviewer pattern, one level higher up the stack.

PostToolUse: Micro Validation on Every Write

Hooks declared inside agent frontmatter only fire when that agent is the one running. Scope is automatic. ESLint belongs to the frontend builder, Ruff belongs to the Python builder, and they never collide.

Here is a Python agent wired to Black and mypy:

# .claude/agents/python-builder.md
---
name: python-builder
description: Build Python modules with automatic formatting and type checking
model: sonnet
hooks:
  PostToolUse:
    - matcher: "Write|Edit"
      hooks:
        - type: command
          command: 'black "$CLAUDE_TOOL_INPUT_FILE_PATH" && mypy "$CLAUDE_TOOL_INPUT_FILE_PATH" --ignore-missing-imports'
---
You are a Python builder agent. Write clean, typed Python code.
Your hooks automatically format with Black and check types with mypy.

The win is scope. These hooks belong to the agent. Nothing leaks into project-level settings. When the orchestrator spins this agent up through the Task tool, the validation goes along for the ride.

Micro catches syntax and formatting. It does not catch a file that never got written. For that, you need a Stop hook.

Stop Hooks: Macro Validation Before Completion

Stop hooks in agent frontmatter turn into SubagentStop events. This script confirms every required output file exists and has the content you asked for:

#!/bin/bash
# .claude/scripts/validate-output.sh
# Validates that agent output meets structural requirements

INPUT=$(cat)
STOP_HOOK_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false')

if [ "$STOP_HOOK_ACTIVE" = "true" ]; then
  exit 0
fi

# Check that required files exist
REQUIRED_FILES=("src/components/index.ts" "src/components/Button.tsx")
MISSING=""

for file in "${REQUIRED_FILES[@]}"; do
  if [ ! -f "$file" ]; then
    MISSING="$MISSING $file"
  fi
done

if [ -n "$MISSING" ]; then
  echo "{\"decision\": \"block\", \"reason\": \"Missing required files:$MISSING\"}"
  exit 0
fi

# Check that index.ts contains exports
if ! grep -q "export" src/components/index.ts; then
  echo "{\"decision\": \"block\", \"reason\": \"index.ts has no exports. Add barrel exports for all components.\"}"
  exit 0
fi

exit 0

Then wire it into the agent itself:

---
name: component-builder
description: Build component libraries with output validation
hooks:
  PostToolUse:
    - matcher: "Write|Edit"
      hooks:
        - type: command
          command: 'npx prettier --write "$CLAUDE_TOOL_INPUT_FILE_PATH"'
  Stop:
    - hooks:
        - type: command
          command: "bash .claude/scripts/validate-output.sh"
---

Now both tiers are live. Each write gets formatted. The whole output gets graded before the agent is allowed to stop. If the Stop hook blocks, the agent keeps going until the checks finally pass.

Read-Only Validator Agents

The third tier is a reviewer that cannot touch files. disallowedTools enforces that at the tool layer:

# .claude/agents/output-validator.md
---
name: output-validator
description: Validate agent output without modifying files. Use after builder agents complete.
model: haiku
disallowedTools: Write, Edit, NotebookEdit
---

You are a read-only validator. Your job:

1. Read all files the builder created or modified
2. Verify exports, type safety, and error handling
3. Run the test suite with Bash
4. Report issues as a list. Do NOT fix anything.

If all checks pass, say "Validation passed" with a summary.
If issues exist, list each one with file path and line reference.

Writing files is off the table for this one. Reading and reporting is all it can do. Pair it with a builder via task dependencies:

TaskCreate(subject="Build auth module", description="...")
TaskCreate(subject="Validate auth module", description="Run output-validator on src/auth/")
TaskUpdate(taskId="2", addBlockedBy=["1"])

When to Use Each Tier

Micro only (PostToolUse) is the right fit for small, tight tasks whose quality bar starts and stops with linting. Overhead is low. Feedback is instant.

Micro plus macro (PostToolUse + Stop) suits agents that ship several files with a structural shape attached. The Stop hook catches what a linter never does: files that never got written, logic left half-done, a suite of red tests.

All three tiers belong on code paths you cannot afford to get wrong. The machine-driven checks do the grind. A separate reviewer gives you a second opinion the builder's own hooks could not.

Start the micro tier on the agent you use most. The moment an agent hands you half-finished work, bolt on a Stop hook. Bring in the reviewer once you care that the whole deliverable hangs together, not only that each file parses. Agent configs are fine to keep in your CLAUDE.md, or they can sit as their own files under .claude/agents/, whichever fits the project.

Beyond Single Agents

Think of self-checks and team checks as a stack, not alternatives. Around 90% of the quality bugs get handled by the embedded PostToolUse hooks plus the Stop script before the reviewer ever opens a file. What is left for the reviewer is integration and architecture, not the lint errors the hooks already swallowed.

More in this guide

  • Keyboard Shortcuts
    Configure custom keyboard shortcuts in Claude Code.
  • Status Line Guide
    Set up a custom Claude Code status line showing model name, git branch, cost, and context usage.
  • AI SEO and GEO Optimization
    A rundown of Generative Engine Optimization: how to get content cited inside ChatGPT, Claude, and Perplexity responses instead of just ranked on Google.
  • Claude Code vs Cursor in 2026
    A side-by-side look at Claude Code and Cursor in 2026: agent models, context windows, pricing tiers, and how each tool fits different developer workflows.
  • Claude Code VSCode Extension
    Anthropic's VS Code extension puts Claude Code inside the editor sidebar as a Spark-icon panel, with inline diffs, plan mode, subagents, and MCP support.

Stop configuring. Start building.

SaaS builder templates with AI orchestration.

Get Build This Now

Stop Hooks

Block Claude from ending a response while tests, builds, or lint checks still fail. Four patterns plus loop protection.

Claude Code Session Hooks

Four lifecycle hooks for Claude Code. Run init steps on demand, inject project context at every start, back up transcripts, and log cleanup on exit.

On this page

PostToolUse: Micro Validation on Every Write
Stop Hooks: Macro Validation Before Completion
Read-Only Validator Agents
When to Use Each Tier
Beyond Single Agents

Stop configuring. Start building.

SaaS builder templates with AI orchestration.

Get Build This Now