claude.md is not a linter

I ran into a repo where every commit message had to reference a Jira ticket like PROJ-1234. Reasonable rule. The enforcement mechanism was a line in CLAUDE.md telling the agent to check for a ticket reference before committing.

That's a script problem wearing a chatbot costume.

The rule is knowable, the input is a string, and the answer is a boolean. Every commit on every branch does not need a model call to answer "does this string match a regex?" And the version in CLAUDE.md does nothing at all for the people on the team committing from their terminal, which was most of them.

The real version is a commit-msg hook:

#!/usr/bin/env bash
# .git/hooks/commit-msg  (or via husky, lefthook, pre-commit)

commit_msg=$(cat "$1")

if ! echo "$commit_msg" | grep -qE '[A-Z]+-[0-9]+'; then
  echo "✖ Commit message must reference a Jira ticket (e.g. PROJ-1234)."
  echo "  Your message: $commit_msg"
  exit 1
fi

Write it once, commit it, and it runs for everybody on every commit forever. It costs nothing, it never drifts, and it can't forget. Same rule: one version is infrastructure and the other is vibes.

The test I use now

Before reaching for a model, I ask whether ten different engineers handed this exact problem would all produce the same answer. If yes, it's a script. The answer is knowable, the solution is shareable, and every run after the first is free.

If no (summarizing a thread, pulling intent out of messy free text, judging whether a PR description is "small" or "large"), then the variance is the point, and there's no cheaper tool that does the job. That's when it's worth it.

Where this gets expensive is verification. A script gets checked once, when you write it. A model call gets checked every single time, or you're trusting it blind. If it's right 99% of the time and you run it ten thousand times, that's a hundred wrong outcomes you have to go find, plus ten thousand calls you didn't need to make.

The loophole

The best use of AI on a deterministic problem is to use it exactly once: have it write the script, then commit the script and never call the model for that task again. One prompt produces an artifact that replaces thousands of future prompts. Let the model do the writing and let boring infrastructure do the running.

That's mostly how I use it now. CLAUDE.md is for context and taste: the things that actually need judgment. It's not a linter, a test runner, or a pre-commit hook, and asking it to be one costs more than the real tool, which is sitting right there and free.