CCA-F Study Day 4/20: The Hooks System — Deterministic Enforcement
🎯 CCA-F Study Day 4/20: The Hooks System — Deterministic Enforcement
Domain 1: Agentic Architecture & Orchestration (~25-27% of exam)
📌 Today's Focus
Today you master the Hooks System — the mechanism that separates advisory instructions from guaranteed enforcement. This is one of the most heavily tested concepts on the exam because it directly addresses the #1 anti-pattern: using prompts to enforce critical business rules. The exam will present scenarios where you must choose between prompt-based guidance and programmatic hooks, and knowing the difference between "Claude should" and "Claude must" is the key to getting these questions right.
📚 Core Concepts
- What Are Hooks?
Hooks are callback functions (shell commands, HTTP endpoints, MCP tools, prompts, or agents) that run YOUR code in response to specific agent lifecycle events. The critical distinction:
CLAUDE.md instructions = advisory (the model should follow them, but it's non-deterministic)
Hooks = deterministic (your code runs regardless of what the model decides — guaranteed enforcement)
This is the single most important sentence for the exam: "Unlike CLAUDE.md instructions which are advisory, hooks are deterministic and guarantee the action happens."
- Hook Event Types (Full Lifecycle)
The exam tests your knowledge of WHEN each hook fires. Here's the complete set:
Hook Event When It Fires Can Block? Use Case
SessionStart Session begins/resumes No Load dev context, set env vars
PreToolUse Before a tool call executes Yes Block dangerous commands, modify inputs
PostToolUse After a tool succeeds No (feedback only) Audit logging, transform outputs
PostToolUseFailure After a tool fails No Error handling, alerting
PostToolBatch After full batch of parallel tool calls Yes Cross-tool validation
Stop Agent finishes responding Yes Enforce completion criteria
SubagentStart Subagent spawned No Inject context, track parallel tasks
SubagentStop Subagent finishes Yes Validate subagent output
PreCompact Before context compaction Yes Archive full transcript
Notification Agent status messages No Forward to Slack/PagerDuty
PermissionRequest Permission dialog shown Yes Auto-approve/deny specific operations
TaskCreated Task being created Yes Enforce naming conventions
TaskCompleted Task being marked done Yes Require tests to pass
- Hook Handler Types (5 Types)
The exam expects you to know all five hook handler types:
Command hooks (type: "command") — Shell scripts that receive JSON on stdin
HTTP hooks (type: "http") — POST to a URL endpoint
MCP tool hooks (type: "mcp_tool") — Call a tool on an MCP server
Prompt hooks (type: "prompt") — Send a prompt to Claude for single-turn evaluation
Agent hooks (type: "agent") — Spawn a subagent to verify conditions
- Permission Decision Priority
When multiple hooks apply, priority is: deny > defer > ask > allow
If ANY hook returns deny, the operation is blocked regardless of what other hooks return. This is critical for understanding how enforcement layers compose.
- The PreToolUse Decision Control (Most Tested)
PreToolUse is the most exam-relevant hook because it's where enforcement happens. Four possible decisions:
"allow" — Skip the permission prompt, proceed
"deny" — Block the tool call entirely
"ask" — Show a confirmation prompt to the user
"defer" — Pause execution (for -p mode integrations)
- Configuration Locations & Scope
Location Scope Shareable?
~/.claude/settings.json All your projects No (local)
.claude/settings.json Single project Yes (committable)
.claude/settings.local.json Single project No (gitignored)
Managed policy settings Organization-wide Yes (admin-controlled)
Plugin hooks/hooks.json When plugin enabled Yes (bundled)
⚠️ Anti-Patterns & Exam Traps
The #1 Anti-Pattern: Prompt-Based Enforcement
❌ Wrong Answer (Exam Trap) ✅ Correct Approach Why
"Add to system prompt: Never delete production files" Use a PreToolUse hook that blocks rm commands on production paths Prompts are advisory; hooks are deterministic
"Tell Claude to always log actions" Use a PostToolUse hook for audit logging Logging must happen regardless of model behavior
"Instruct the model to reject PII" Use a PreToolUse hook with PII regex checking Compliance cannot depend on model judgment
"Add a note in CLAUDE.md saying 'always validate inputs'" Write a hook that programmatically validates inputs before tool execution CLAUDE.md is advisory — critical validation needs guarantees
Other Traps to Watch For:
❌ Confusing "hooks" with "tools" — Hooks intercept the agent lifecycle; tools are what the agent calls
❌ Exit code 1 blocks the action — WRONG. Only exit code 2 blocks. Exit 1 is a non-blocking error
❌ PostToolUse can prevent the tool from running — WRONG. The tool already ran. PostToolUse only provides feedback
❌ Hooks replace CLAUDE.md — WRONG. They complement each other. CLAUDE.md for guidance, hooks for enforcement
💻 Code Examples
Example 1: Block Destructive Commands (PreToolUse)
#!/bin/bash
# .claude/hooks/block-destructive.sh
# This is a PreToolUse hook matching "Bash" tools
INPUT=$(cat) # Read JSON from stdin
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')
# Block rm -rf, DROP TABLE, etc.
if echo "$COMMAND" | grep -qE 'rm -rf|DROP TABLE|truncate|format'; then
# Exit code 2 = BLOCKING error
echo "Destructive command blocked by compliance hook" >&2
exit 2
fi
# Exit 0 = allow the command
exit 0
Example 2: Structured JSON Decision (PreToolUse)
#!/bin/bash
# .claude/hooks/protect-env-files.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
if [[ "$FILE_PATH" == *.env* ]]; then
# Return structured JSON decision
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Cannot modify .env files — use secrets manager instead"
}
}'
else
exit 0 # Allow
fi
Example 3: Audit Logging (PostToolUse)
#!/bin/bash
# .claude/hooks/audit-log.sh
# Fires after Write|Edit tools succeed
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name')
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path')
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
# Deterministic logging — happens regardless of model behavior
echo "{\"timestamp\": \"$TIMESTAMP\", \"tool\": \"$TOOL\", \"file\": \"$FILE\"}" >> ~/.claude/audit.jsonl
exit 0
Example 4: Hook Configuration in settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(rm *)",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-destructive.sh",
"args": []
}
]
},
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"if": "Write(*.env)|Edit(*.env)",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/protect-env-files.sh",
"args": []
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/audit-log.sh",
"args": []
}
]
}
]
}
}
Example 5: HTTP Hook for External Compliance Service
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "http",
"url": "http://localhost:8080/hooks/validate-command",
"timeout": 30,
"headers": {
"Authorization": "Bearer $COMPLIANCE_TOKEN"
},
"allowedEnvVars": ["COMPLIANCE_TOKEN"]
}
]
}
]
}
}
🎬 Video to Watch
Claude Code Advanced Patterns: Subagents, MCP, and Scaling to Real Codebases — Anthropic webinar covering hooks configuration, subagent patterns, and real-world scaling. Focus on the hooks section where they demonstrate how teams use PreToolUse hooks for compliance enforcement in production deployments.
📖 Reading
Primary: Claude Code Hooks Reference — The full official documentation covering all hook events, configuration, and decision control
Secondary: Automate Workflows with Hooks (Guide) — Practical walkthrough with real examples
Context: Building Agents with the Claude Agent SDK — Engineering blog post covering hooks in the broader agent architecture
🛠️ Hands-On Exercise (20 minutes)
Build a compliance enforcement hook system:
Create a project directory with .claude/hooks/ and .claude/settings.json
Write a PreToolUse hook (compliance-check.sh) that:
Blocks any Bash command containing rm -rf
Blocks any Write/Edit to files in a config/secrets/ directory
Logs all blocked attempts to an audit file with timestamp and reason
Write a PostToolUse hook (audit-log.sh) that logs every successful file write to ~/.claude/audit.jsonl with the tool name, file path, and timestamp
Configure both hooks in .claude/settings.json with correct matchers and if conditions
Test mentally: What happens when Claude tries to rm -rf /tmp/build? What exit code blocks it? What happens if your hook exits with code 1 instead of 2?
📝 Quick Quiz
Question 1: A financial services company needs to ensure that no agent can ever execute a database DELETE command in production. Which approach provides the strongest guarantee?
A) Add "Never execute DELETE commands in production" to the system prompt
B) Include a rule in CLAUDE.md: "Production DELETE commands are prohibited"
C) Implement a PreToolUse hook that inspects Bash commands and returns exit code 2 for any containing DELETE
D) Set max_turns to 1 to prevent the agent from taking destructive actions
Question 2: When multiple PreToolUse hooks apply to the same tool call and return different decisions, what is the resolution order?
A) First hook registered wins
B) allow > ask > deny > defer
C) deny > defer > ask > allow
D) The most recently modified hook takes precedence
Question 3: A hook script returns exit code 1 on a PreToolUse event. What happens?
A) The tool call is blocked and the error message is shown to Claude
B) The tool call proceeds — exit code 1 is a non-blocking error
C) The entire agent session terminates
D) Claude retries the tool call with modified parameters
Answers:
C — PreToolUse hooks are deterministic. Prompts and CLAUDE.md are advisory (the model might ignore them). max_turns doesn't address the specific threat.
C — deny > defer > ask > allow. If ANY hook denies, the action is blocked regardless of others.
B — Only exit code 2 blocks. Exit code 1 is a non-blocking error — the tool call still proceeds, and stderr is logged. This is a common exam trap.
👀 Tomorrow's Preview
Day 5 covers Task Decomposition & Exam Scenarios — the final Domain 1 day where you'll put everything together with the Customer Support and Multi-Agent Research exam scenarios, focusing on error boundaries, context isolation, and how to design escalation logic based on structured criteria.