DEV Community

Cover image for Your CLAUDE.md Rules Aren't Being Enforced. Here's What Actually Works.
Yurukusa
Yurukusa

Posted on

Your CLAUDE.md Rules Aren't Being Enforced. Here's What Actually Works.

A user on GitHub spent ~$30 over two days because Claude Code ignored a cost rule written clearly in their CLAUDE.md.

The rule said "use cheap models for bulk operations." Claude read the rule, acknowledged it in conversation, ran a code review — and still shipped code that made 774 Sonnet API calls at full price. The code review agent didn't flag it either.

This isn't a bug. It's a category error.

CLAUDE.md is a suggestion, not a constraint

CLAUDE.md rules are part of the system prompt. Claude reads them, intends to follow them, and mostly does — until it doesn't. The failure mode isn't "Claude refuses to read CLAUDE.md." It's:

  • Long sessions where early context fades and recent task context dominates
  • Complex multi-step operations where following the immediate subtask overrides background rules
  • Code review agents that check structure and correctness, not cross-reference against policy documents

Think of it this way: CLAUDE.md is like a company handbook. Hooks are like door locks. The handbook says "don't enter the server room without authorization." The lock actually prevents it.

What actually enforces rules: Hooks

Claude Code hooks run shell scripts at specific points in the execution lifecycle. They can inspect what Claude is about to do and block it by returning a non-zero exit code.

Here's the key difference:

CLAUDE.md Hook
When it applies When Claude "remembers" Every single time
Enforcement Soft (Claude decides) Hard (script decides)
Survives long sessions Degrades Always active
Survives code review Agent may miss it N/A (prevents, doesn't review)

Three hooks that would have prevented that $30 mistake

1. Cost guard — block expensive models in bulk code

#!/bin/bash
# PreToolUse hook for Edit and Write
CONTENT=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.new_string // .content // ""')

if echo "$CONTENT" | grep -qiE '(claude-sonnet|claude-opus|gpt-4-turbo)' && \
   echo "$CONTENT" | grep -qiE '(for .* in|while|batch|bulk|\[[0-9]+/)'; then
  echo "COST GUARD: Expensive model detected in bulk operation." >&2
  echo "Rule: Use haiku/gpt-4o-mini for bulk. See CLAUDE.md." >&2
  exit 1
fi
Enter fullscreen mode Exit fullscreen mode

2. Dry-run enforcer — block production scripts without --dry-run

#!/bin/bash
# PreToolUse hook for Bash
CMD=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.command // ""')

if echo "$CMD" | grep -q "community_detection\|pipeline\|batch_process" && \
   ! echo "$CMD" | grep -q "\-\-dry-run"; then
  echo "BLOCKED: Production scripts must run with --dry-run first." >&2
  exit 1
fi
Enter fullscreen mode Exit fullscreen mode

3. Spend tracker — alert when API costs exceed threshold

#!/bin/bash
# PostToolUse hook for Bash
OUTPUT=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.command // ""')
# Track API calls in a counter file
COUNTER_FILE="/tmp/cc-api-call-counter"
COUNT=$(cat "$COUNTER_FILE" 2>/dev/null || echo 0)

if echo "$OUTPUT" | grep -qiE 'sonnet|opus|gpt-4'; then
  COUNT=$((COUNT + 1))
  echo "$COUNT" > "$COUNTER_FILE"
  if [ "$COUNT" -gt 50 ]; then
    echo "WARNING: $COUNT expensive API calls this session." >&2
    echo "Review costs before continuing." >&2
  fi
fi
Enter fullscreen mode Exit fullscreen mode

The pattern: CLAUDE.md for intent, hooks for enforcement

The right approach isn't "hooks instead of CLAUDE.md." It's both:

  1. CLAUDE.md describes what you want and why — Claude uses this for decision-making in ambiguous situations
  2. Hooks enforce hard constraints — the rules that must never be violated regardless of context

For anything with real consequences — cost overruns, data deletion, production pushes, credential exposure — hooks are the enforcement layer. CLAUDE.md alone isn't sufficient.

Getting started

The hooks above are from claude-code-hooks, a collection of 12 production hooks extracted from 140+ hours of autonomous Claude Code operation.

Every hook exists because something went wrong without it. The cost guard exists because we ran expensive models on bulk operations. The branch guard exists because we pushed to main. The error gate exists because we published code with unresolved errors.


These hooks are part of the Claude Code Ops Kit ($19) — 12 hooks + 6 templates + 3 tools, pre-configured and production-ready in 15 minutes.

Top comments (0)