METAL LAB

Claude Code hooks block rule-skipping with code

In lesson 5 of Claude Academy, Anthropic revealed how to configure hook events that enforce CLAUDE.md rules

Claude Code hooks block rule-skipping with code

Summary

  • Anthropic introduced Claude Code's hook feature in lesson 5 of its Claude Academy course
  • Hooks are deterministic code that run at fixed points in the execution loop, enforcing CLAUDE.md rules unconditionally
Claude Code Hooks: Turn "Usually Follows" Into "Cannot Skip"

From Claude "usually following" rules to "always following" them

Anthropic official website

A dashed arrow runs from the CLAUDE.md rules file to Claude Code, and along the solid arrow from Claude Code to tool execution sits a dashed-circle hook-check gate. Inside the gate is an angle-bracket symbol representing code, showing that execution is blocked if a rule is violated.A dashed arrow runs from the CLAUDE.md rules file to Claude Code, and along the solid arrow from Claude Code to tool execution sits a dashed-circle hook-check gate. Inside the gate is an angle-bracket symbol representing code, showing that execution is blocked if a rule is violated.

Anyone who's used Claude Code has probably noticed that Claude sometimes follows the rules written in CLAUDE.md and sometimes skips them. As the video's title puts it, hooks are the tool that turns rules Claude "usually follows" into procedures it "cannot skip."

CLAUDE.md rules carried no enforcement

As covered in Karpathy's critique of LLM coding, distilled into a single CLAUDE.md file, CLAUDE.md works by consolidating principles like "no assumptions," "minimal code," "minimal changes," and success criteria into a single file that instructs Claude Code. But because the file is ultimately just guidance Claude refers to, there was always room for Claude to miss or work around the rules depending on the situation. This fifth lesson covers how to close that gap using hooks.

Put simply, CLAUDE.md is a file where developers write down the rules Claude Code should follow, but whether Claude actually followed them varied case by case. Hooks pin down the parts of those rules that must never be broken, using code that automatically blocks or corrects Claude the moment it tries to violate them.

What hooks are

A hook is deterministic code that runs at set points in Claude Code's execution loop. Because it's deterministic, it behaves the same way under the same conditions every time, which means it can enforce set rules even when no one is watching the run.

Seven commonly used events

EventTrigger pointTypical use
pre_tool_useRight before a tool callAllow, deny, or confirm risky commands
post_tool_useRight after a tool call succeedsAuto-formatting, linting
stopWhen Claude tries to end a taskReject termination if conditions aren't met
subagent_stopWhen a subagent terminatesApplies the same signal as stop to subagents
pre_compact / post_compactBefore and after compactionCheck the compaction process
instructions_loadedWhen rule files like CLAUDE.md loadAudit what actually made it into context
session_startAt session startPrepare the environment, reinject context after compaction

The video specifically noted that if you want to reinject context after compaction, you should use session_start with a compact matcher rather than post_compact.

Exit codes that decide the outcome

CodeMeaningNote
0SuccessIf stdout is JSON it's parsed; plain text is mostly ignored, but for session_start, user_prompt_submit, and user_prompt_expansion it's added to context
1Looks like an error but doesn't blockClaude executes the command as-is
2Blocking errorStderr is passed to Claude as context and acts as the blocking code for nearly every event (the exception is worktree_create, where any non-zero code aborts)

A pre_tool_use hook returns JSON with one of allow, deny, or ask in the permission decision field. There's a fourth value, defer, but it's only used in the special case of a non-interactive -p run where the calling process pauses the tool and resumes it later.

Two practical examples

For masking secrets, a pre_tool_use hook can specify the bash tool as the matcher, narrow it down to a specific command with an if clause, and return updated_input instead of deny. That lets execution continue with just the password portion stripped out, without blocking the command entirely. But because updated_input replaces the whole input object, you have to return the unchanged fields as well, exactly as they were.

When a conversation runs long and Claude performs compaction, a lot of detail gets lost. A session_start hook with a compact matcher runs immediately after compaction and puts a short summary of the files being worked on back into context. That summary lets Claude pick up where it left off instead of starting over from before the compaction.

Editor's take

If auto mode was the change that let Claude move without approval at every step, hooks are the counterpart that keeps that expanded autonomy from ever touching certain rules. Anthropic is essentially refining Claude Code to move fast while drawing the lines it must not cross with code, rather than with human oversight.

It wasn't long ago that consolidating rules into a single CLAUDE.md file became a trend, and now there's a mechanism that pushes those rules from "nice to follow" up to "must be followed." Teams that already have a CLAUDE.md rules file can feel the difference right away just by adding a single pre_tool_use hook, catching risky commands before they run.

A common complaint among Korean development teams adopting Claude Code is that it "sometimes skips the rules," and this lesson points to the answer: start with just the pre_tool_use and stop events. Rather than trying to use all thirty-odd events at once, setting up just three — pre_tool_use to block dangerous commands, stop to check task-completion conditions, and session_start to restore context after compaction — can meaningfully cut down on real incidents.

Comments