Vibhanshu Sharma
active · powerplay
PORTFOLIO.SYS›content›blog›claudemd-vs-hooks.mdx
Markdown · 9 min read · 2026-06-17

Why Claude Kept Ignoring My Rules (And What I Did About It)

I wrote clear instructions in CLAUDE.md. My AI kept breaking them anyway. Here's the mental model that changed everything — and the fix that actually enforces your rules.


// tl;dr
  • CLAUDE.md is advice — Claude reads it, means to follow it, and can still lose track of it under pressure mid-task.
  • Hooks are enforcement — a PreToolUse hook can block a file write before it happens, no matter what the AI was thinking.
  • The mental model: CLAUDE.md is a speed-limit sign, a hook is a speed bump — one you can drive past, the other you physically can't.
  • Rules that genuinely must not be broken need mechanical authority (hooks, linters, gates), not just advisory context.

I'm building my personal website with Claude Code — an AI that writes code for me, directly in my terminal.

Somewhere around day three, I get smart about it. I create a file called CLAUDE.md. This is a special file that Claude reads before every session — your chance to tell the AI exactly how you want it to work. I add this line:

Always use existing Mantine UI components. Never create custom components when a Mantine equivalent exists.

Clear. Specific. Right at the top of the file.

Twenty minutes later, Claude writes a custom <Button> component from scratch. Hundreds of lines. Its own styles. A completely invented wheel.

I stare at the screen. The rule is right there. I copy it. I paste it again — in bold, in capital letters, three times. I run the command again.

Claude writes another custom component.

I had it completely backwards. And figuring out why taught me the most important mental model I've learned about working with AI tools.


First, a Thought Experiment

Your company has a staff handbook. Page 47 says: "All API calls must include error handling." It's a good rule. Everyone's read it. It was added after a painful production incident two years ago.

Now imagine you're deep into a 3am debugging session. Critical bug. You need to make one quick API call to test something. You know the rule. You skip the error handling. Just this once. You'll add it back. You're exhausted.

This is not negligence. This is how humans work. The rule exists in the handbook, but nothing mechanically prevents you from violating it.

I was treating CLAUDE.md like a handbook.

The AI reads it. Takes it seriously. But three hundred lines of context later — building something complex, solving an immediate problem — the rule from the top of the file loses its weight. Not because the AI is bad. Because that's not how reasoning systems work under pressure.

The fix I needed wasn't better instructions. It was a different kind of instruction entirely.


The Mental Model That Changed Everything

Here's the insight, and once you have it you'll see it everywhere:

CLAUDE.md is advice. Hooks are enforcement.

Let me make this concrete.

Think about how cities control driving speed.

Speed limit signs are advice. They communicate the rule clearly. Most drivers follow them. But nothing mechanically stops you from driving 100km/h in a 30km/h zone — except a cop who happens to be watching at that exact moment.

Speed bumps are enforcement. They don't tell you the rule. They physically prevent the behavior. You cannot drive 100km/h over a speed bump. The constraint is baked into the road.

CLAUDE.md is speed limit signs. Claude reads them, understands them, intends to follow them — and sometimes forgets mid-journey.

Hooks are speed bumps. They run automatically, every single time, regardless of what the AI was thinking about. They can stop the action completely.

Click the tabs below to see exactly how each plays out:

📖
01Claude reads your rules at session start
🧠
02Rules become part of working memory
⚡
03Claude gets deep into building a feature…
📉
04Your rule from line 3 loses salience vs. the immediate problem
❌
05Custom component written — rule quietly ignored

What Hooks Actually Are

In Claude Code, a hook is a shell command that runs automatically at specific moments in the AI's workflow.

The AI is about to write a file? A hook runs first. The AI just ran a command? A hook runs after. The AI is about to edit code? A hook can examine the change before it happens.

The critical part: if the hook exits with an error, Claude stops and reconsiders.

Not "log a warning." Not "make a note." Stop. Claude sees the error output, understands what went wrong, and tries a different approach — one that passes the check.

Here is what a hook configuration looks like in .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "node .claude/hooks/check-mantine.js"
          }
        ]
      }
    ]
  }
}

This says: before every single file write, run this script first.


The Four Hook Moments

Claude Code gives you four interception points:

PreToolUse    → Before Claude uses any tool (read, write, run a command…)
PostToolUse   → After a tool completes
PreCompact    → Before Claude compresses its memory in long sessions
Stop          → When Claude's turn ends

For enforcing rules like "use Mantine components," PreToolUse is the one. It runs before the file is written, so you can prevent the violation entirely.

PostToolUse is useful for verification — checking the output after a write, making sure it meets your standards.


My Actual Problem, Solved

Here is exactly what I was dealing with on this website.

The CLAUDE.md rule that kept getting ignored:

## UI Components
Always use Mantine components. Import from '@mantine/core'.
Never create custom buttons, inputs, or modals.

The hook that actually enforces it:

// .claude/hooks/check-mantine.js
import { readFileSync } from 'fs';
 
const input = JSON.parse(readFileSync('/dev/stdin', 'utf8'));
const code = input?.tool_input?.content ?? '';
 
const forbidden = [
  /function\s+Button\s*\(/,
  /const\s+Button\s*=\s*\(/,
  /<button\s+className/,
];
 
const violation = forbidden.find(p => p.test(code));
 
if (violation) {
  console.error(`
[HOOK] Mantine rule violated.
You're creating a custom button component.
Use <Button> from '@mantine/core' instead.
  `);
  process.exit(1);
}
 
process.exit(0);

When Claude tries to write a custom button, the hook fires, prints the error message, and exits with code 1. Claude reads the error, understands what it did wrong, and tries again — this time reaching for the Mantine import.

The rule that lived in the handbook and got ignored? It now lives in the road. You can't go around it.

See it happen live — press ▶ RUN DEMO to watch the hook fire, block Claude, and force a correct retry:

hook-trace — bash
Press ▶ RUN DEMO to see how a hook intercepts Claude in real-time…
❯Claude
▸Tool call
⚙Hook
✗Blocked
✓Allowed

Why This Matters If You're New to AI

If you're just starting out with AI coding tools, here's the most important thing to understand:

AI assistants are not obedient executors. They are reasoning engines.

They take your instructions as input, weigh them against everything in their context — the immediate problem, the code they're looking at, the error message from three steps ago — and make a judgment call. They're usually right. But rules that feel non-negotiable to you can lose priority against the concrete, salient, right-in-front-of-them problem they're solving.

This is not a flaw. It's what makes them powerful. But it means you can't rely on instructions alone for rules that genuinely must not be broken.

Here's the three-layer model I now use. Click any layer to expand it:

01
📋CLAUDE.mdContext & orientation

Tells the AI about your project, stack, and style preferences.

ADVISORY▼
rules not enforced here may slip through ↓
02
⚡HooksMechanical enforcement

Shell commands that fire before/after every tool use.

ENFORCED▼
code that passes hooks gets verified by ↓
03
🧪Tests & CIFinal verification gate

Proves the output is correct, not just rule-compliant.

BLOCKING▼

The Deeper Pattern

I spent three days frustrated by what felt like "the AI isn't listening." But once I found the real model, I realized I'd been asking the AI to enforce its own constraints — which is like writing "don't forget" on a sticky note and trusting the sticky note to remind you.

The rule needs to live somewhere with mechanical authority over the process, not just advisory presence in the context.

For Claude Code: hooks.

For your codebase: linters, type checkers, pre-commit hooks.

For your team: automated gates, not just documented guidelines.

The pattern is the same everywhere. Written rules tell people what to do. Automated checks ensure they actually did it. Both together is where the magic is.


Try It Yourself

Work through the four steps below. Each one builds on the last — start with observation, end with enforcement. Click any step to expand it, then check it off when done.

// try it yourself0/4 done
01.Add a visibility hook▲

Add the echo hook to .claude/settings.json so you can see when PreToolUse fires.

"hooks": { "PreToolUse": [{ "matcher": "Write", "hooks": [{ "type": "command", "command": "echo \'[HOOK] About to write a file\'" }] }] }
02.Run Claude and watch the log▼
03.Log what Claude is writing▼
04.Write your first enforcement▼

The gap between "I told the AI to do X" and "the AI will always do X" is where most frustration with AI tools lives.

CLAUDE.md opens the conversation. Hooks end it.

Write the rules. Then build the speed bumps.

Keep reading

We Shipped an AI Code Reviewer With Three Prompts. It Was Wrong Too Often and Quiet Too Long.

2026-07-28 · 9 min read

One Reviewer, Four Codebases, Four Different Definitions of Correct

2026-07-28 · 10 min read

Our Cross-File Pass Couldn't See Other Files. Tree-sitter Fixed That.

2026-07-28 · 10 min read

We Put a Cheap Model in Charge of the Expensive Ones

2026-07-28 · 10 min read
← all posts