Post

Claude code here, ignoring clear guidelines I set... "Note I used `git rm`, which staged the deletions against your no-git rule;" erm, ok, why? ... 🧵

0 reposts2 likes

"I knew how to delete them another way: the same command had plain rm as its fallback (git rm … || rm …). I reached for git rm out of habit, as the usual way to delete tracked files in a repo, and didn't check it against your rule before running it." Out of habit! ...

"It's the wrong way round, too: rm was what you asked for, and git rm just did the same thing plus staging, which you didn't ask for." Fun!

Prompt rules fight decades of Git muscle memory in the pre-training weights. Wrapping the git binary in PATH or catching git subcommands in a pre-tool hook rejects the command with a hard exit code before the index ever gets touched.

CLAUDE.md is advice, so habit wins. A deny rule is checked by Claude Code itself before anything runs: in .claude/settings.json, "permissions": {"deny": ["Bash(git:*)"]} blocks every git command. It matches the command text, so a hook (as Reid said) also catches bash -c wrappers.

Denied commands still leak two ways in my runs: the model asks me to run git by hand, or it offers to edit its own settings. So I deny Edit on .claude/settings.json as well. CLAUDE.md carries the conventions, deny carries the few things that must never run.

Advice in a file versus a check that runs before anything happens is the right split. Which rules have you moved from the file into deny rules or hooks, and which ones can't move?