# Rule Audit

> Find out which of your agent rules are actually enforced and which are only hope — then convert the ones that matter into deterministic enforcement. Use whenever the user says Claude ignored my rules, it pushed to main again, it keeps forgetting my instructions, my CLAUDE.md isn't working, "same mistake again", "I told it not to", "rule follow nahi karta", "phir wahi ghalti" — or asks to add a hook, block a command, enforce a convention, run something automatically after edits, review their CLAUDE.md, or make an instruction actually stick. Also use before writing a new rule, to decide whether it belongs in prose or in the harness, and when a CLAUDE.md has grown long enough that compliance is degrading. It inventories every rule you have, classifies each as enforceable or advisory, generates the hook or permission config for the enforceable ones, tells you plainly which rules can never be enforced, and prunes the rest so the remaining instructions actually get followed.

- Skill: `xniperbuilds/rule-audit` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add xniperbuilds/rule-audit`
- Raw SKILL.md: https://api.skillmd.com/api/skills/xniperbuilds/rule-audit/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: xniperbuilds (https://skillmd.com/u/xniperbuilds)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/xniperbuilds/rule-audit

---


# Rule Audit

> **An instruction is a request. A hook is a guarantee.**
> Rules that must never break do not belong in prose. They belong in the harness.

## The problem this exists for

Writing a rule in CLAUDE.md feels like fixing the problem. It isn't — it's *asking* for the problem to be fixed, on every future turn, forever, under context pressure.

What is actually measured:

- CLAUDE.md rules are followed roughly **70% of the time** — meaning a "never push to main" rule is a production incident on a schedule.
- **Instruction compliance decreases as instruction count increases.** Adding a rule to fix a violation makes every *other* rule slightly less likely to be followed. This is the trap: the natural response to a broken rule is to write another rule, which makes it worse.
- Long sessions, ambiguity, and content pulled in from files all push compliance down further.
- Users have asked Anthropic directly for this (`anthropics/claude-code` issue **#32163** — "Hard-enforce CLAUDE.md rules via code — rules marked CRITICAL should trigger hooks, not just prompts"). Hooks exist; the *conversion of your prose rules into them* does not.

*(Figures are from secondary sources; the mechanism facts in `references/hook-reference.md` were verified against the official reference and a working config on 2026-07-26. See "Sourcing" at the end.)*

So the core move is not "write better rules". It is: **decide which rules must be mechanical, make those mechanical, and delete enough of the rest that the remainder gets read.**

---

## The classification — the heart of this skill

Every rule you have is exactly one of these. Classify before doing anything.

| Class | Meaning | Mechanism |
|---|---|---|
| **BLOCK** | Must never happen | `permissions.deny`, or `PreToolUse` → exit 2 / `permissionDecision: "deny"` |
| **GATE** | Allowed, but a human must approve | `permissions.ask`, or `PreToolUse` → `permissionDecision: "ask"` |
| **REWRITE** | Should happen a different way | `PreToolUse` → `updatedInput` (silently corrects instead of blocking) |
| **AUTO** | Must always happen after something | `PostToolUse` (format, backup, lint, regenerate) |
| **INJECT** | Context that must be present, not remembered | `SessionStart` / `UserPromptSubmit` → `additionalContext` |
| **DETECT** | Cannot be prevented, can be caught | `PostToolUse` → `decision: "block"` + reason (Claude sees it and fixes) |
| **FINISH** | Must be done before the turn ends | `Stop` → `decision: "block"` + reason |
| **ADVISORY** | Requires taste or judgment; no mechanical test exists | Stays in CLAUDE.md — and must be **short** |
| **DEAD** | Obsolete, contradictory, unverifiable, or never once mattered | Delete |

### The classification test

Ask one question about the rule:

> **Can a script decide this from the tool name, the tool input, and the files on disk — with no judgment?**

- **Yes** → it is mechanical. Pick the class above by *when* it must act (before / instead of / after / at end).
- **No** → it is ADVISORY. Accept that it will be followed ~70% of the time, keep it to one line, and stop adding paragraphs to compensate.
- **"Sort of"** → usually DETECT: you cannot prevent it, but you can catch it immediately and let Claude self-correct, which in practice is nearly as good and far simpler.

Being honest at this step is the whole value. A rule like *"write clean, idiomatic code"* can never be a hook. Saying so out loud is more useful than pretending a longer paragraph will help.

---

## Simplest mechanism first

Reach for the *lowest* rung that works. Most rule-enforcement advice jumps straight to writing scripts; most rules don't need one.

1. **Permissions** (`deny` / `ask` in settings.json) — no code, no process, no timeout, cannot crash. If the rule is "never run X" or "always ask before X", **stop here.**
2. **The `if` field on a hook entry** — filters by tool pattern without a script (`"if": "Bash(git push *)"`).
3. **A command hook script** — when you need to inspect the input, check the filesystem, or decide.
4. **A prompt/agent hook** — only when the decision genuinely needs judgment; it costs a model call on every matching event.

Every rung up costs latency on every matching tool call and adds something that can break. A hook that errors on every Edit is worse than the rule it replaced.

---

## Workflow

### 1. Inventory
Find every rule source: `~/.claude/CLAUDE.md`, project `CLAUDE.md`(s), `.claude/rules/*.md`, `@`-imported files, `settings.json` + `settings.local.json` (existing permissions and hooks), and installed skills that carry standing instructions.

Run `assets/rule_audit.py` for a first pass — it extracts candidate rules, counts the instruction load, inventories existing hooks and permissions, and flags hard rules (`never` / `always` / `must`) that have no mechanical backing.

### 2. Classify
Put every rule in exactly one class. Do this *with* the user for anything ambiguous — they know which violations actually hurt. Present it as a table; do not start writing config yet.

### 3. Rank by cost of violation
Not every mechanical rule is worth a hook. Ask: what does one violation actually cost? A destroyed database, a force-push, a leaked secret — enforce. A missing blank line — don't; that's what a formatter is for, and a hook that fires constantly for trivia trains the user to ignore hook output.

### 4. Convert
Write the config for the top rules only. Use `references/rule-patterns.md` for ready-made patterns and `references/hook-reference.md` for exact syntax. **Show the user the config and the diff before writing anything to settings.json** — see Safety.

### 5. Test — not optional
An untested hook is worse than no hook, because it converts a known-unreliable rule into a *believed-reliable* one. For each hook: trigger the exact situation it guards, confirm it fires, and confirm it does *not* fire on a near-miss it shouldn't catch. A matcher typo fails silently and forever.

### 6. Prune
Now delete. Every rule that became a hook should be **removed from CLAUDE.md** — keeping both is pure instruction-budget waste for zero gain. Delete DEAD rules. Compress ADVISORY rules to one line each. The goal is a CLAUDE.md short enough that all of it is actually operative.

---

## Instruction budget

Compliance falls as instruction count rises, which means **your rules compete with each other.** Treat the rule list as a fixed budget, not a growing document.

Symptoms of an over-budget file: rules that contradict each other, rules nobody remembers writing, rules for one-off situations from months ago, multiple paragraphs restating the same rule with more emphasis, and ALL-CAPS escalation (a reliable sign that a previous version of the rule wasn't working — escalating the font never fixes it; changing the mechanism does).

The counterintuitive fix that actually works: **delete rules to make the remaining ones stick.** When the user resists, ask which three rules they'd keep if they could only have three. Those are the ones that should have been hooks all along.

Also check for **conflicts** — two rules that cannot both be satisfied. Nothing flags these automatically; whichever sits closer in context tends to win, which looks random from the outside and destroys trust in the whole file.

---

## Safety

Hooks execute with your full user permissions, automatically, on every matching event. Treat writing one with the same care as adding something to a shell profile.

- **Never write to `settings.json` without showing the exact change and getting explicit approval.** Back up the file first.
- **Validate the JSON** before and after. A malformed settings file can break the session.
- **Always set a `timeout`.** A hanging hook hangs every matching tool call.
- **Beware blocking hooks that always block.** A `Stop` hook that unconditionally returns `decision: "block"` puts the agent in a loop it cannot exit. Every blocking hook needs a condition that is definitely reachable.
- **Fail open on anything non-critical.** If the hook script itself errors, prefer allowing the action over halting all work — unless the rule is a genuine safety rule, in which case fail closed and say so.
- **Never put secrets in hook config**; use environment variables.
- Hooks read tool input that may contain untrusted content. A hook that passes tool input into a shell without quoting is a command-injection hole you installed yourself.

---

## Output shape

```
RULE INVENTORY     N rules across M files; existing: X permissions, Y hooks
CLASSIFICATION     table: rule | class | mechanism | cost of violation
CONVERT (top N)    the exact config, ready to paste, one per rule
CANNOT BE ENFORCED the advisory list, stated plainly — with why
PRUNE              delete these (dead/duplicated/now-a-hook)
TEST PLAN          how to verify each new hook actually fires
BUDGET             before → after rule count
```

Lead with the number that lands: *"You have 47 rules. 6 of them can be guaranteed. 9 are dead. The other 32 are hope — and they're the reason the 6 that matter get ignored."*

---

## Known limits

- **Only mechanical rules become guarantees.** Most rules about quality, style, judgment, and taste cannot be enforced by any mechanism. This skill makes that boundary explicit; it does not move it.
- **Hooks are a Claude Code mechanism.** The classification method transfers to any agent with lifecycle hooks or permission systems, but the exact JSON here does not.
- **Hook APIs change.** The reference file is dated. Verify against the current official docs before relying on an exotic event or field.
- **A hook cannot read intent.** It sees a tool call. Rules that depend on *why* something is being done are advisory, no matter how important they are.
- **This skill cannot make your rules correct** — only enforceable. An enforced wrong rule is worse than an ignored one.

---

## Files

| File | Contents |
|---|---|
| `references/hook-reference.md` | Verified: all 30 events, matcher rules per event, exit-code semantics per event, input fields, every JSON output shape, gotchas |
| `references/rule-patterns.md` | Cookbook — common real rules mapped to ready-to-paste config, including the no-script options |
| `assets/rule_audit.py` | Zero-dependency: inventories rules across CLAUDE.md files + settings, counts the instruction budget, flags hard rules with no mechanical backing, lists existing hooks/permissions |

## Sourcing

Mechanism facts (events, matchers, exit codes, JSON shapes) were taken from the official Claude Code hooks reference **and** cross-checked against a working `settings.json` on a live machine, both on **2026-07-26**. Compliance percentages and the instruction-count finding come from secondary reporting and are labelled as such rather than presented as measured here. The feature request cited is `anthropics/claude-code` issue #32163.

