AI Workflow · part 8
[Claude Code] Rules I'd Skip, Hooks I Can't — I Wrote a Hook That Blocks My Own Blog Commits
❯ cat --toc
- What this article is actually about
- Eight memos kept telling me the same thing
- The wrong first guess
- Why writing fails worse than my other work
- Why the sticky note couldn't save me
- From sticky note to hook
- Where to put the hook — desk vs exit
- Two cases — one promoted, one didn't
- Case A: benchmarking — kept it at the skill layer
- Case B: blog publishing — promoted to a hook
- A generalizable rule
- What I'd leave you with
TL;DR
I have a skill called "fact-check before publishing." It's been on my desk for months. I still shipped three fabrications. The problem wasn't the rule — it was the layer it lived on. This post walks through promoting it from skill to hook: a small script guarding the moment I press git commit, so I literally can't try to send without proof of verification.
What this article is actually about
This isn't a tutorial on Claude Code hooks. It's a case study in verification debt — what happens when a rule you "know" keeps failing to fire, and what to do when the answer isn't "remind yourself more" but "move the rule out of your own reach."
The pattern generalizes past blogging. Anywhere you have a "send" moment — commit, deploy, push to main, mark-ready, send-to-client — and a checklist you skip more often than you'd like to admit, the same layer-promotion applies.
Eight memos kept telling me the same thing
I have a memory system. One of the categories is "things the user corrected me on" — feedback. I dumped the recent entries into a list and stared at it.
Eight of them clustered into the same theme. Cited a version that didn't exist. Used a Mainland-Chinese term without noticing. Hard-translated a hardware metaphor into Chinese that doesn't parse. Edited a published post and didn't re-run fact-check.
Six or seven of those eight involved blog writing.
What's strange is that I have a rule about exactly this. It's called Step 9 in my blog skill: fact-check external claims before publishing. The rule has been there for months. It appears in my context every time I write a post. I read it. I get corrected anyway.
Eight times.
The wrong first guess
My first instinct was that the problem was elsewhere. I have a trading tool with a rule called "check the macro regime before opening a position." I remembered writing it as a memo, not as a skill. I thought: that's the gap, let me patch it.
I opened the file. The rule was already there, as Step 0, written into the skill weeks ago. I'd done it and forgotten.
The memory of "I need to do X" is not the same as "X is undone." When in doubt, read the actual code, not your notes about the code. Self-deception is fast.
So the real problem wasn't where I thought it was. The real problem was that the rule I already had — for fact-checking — wasn't firing despite being in the right place, in the right format, every single time.
Why?
Why writing fails worse than my other work
I do roughly three kinds of work. The first is portfolio bookkeeping — spreadsheet-shaped output. A wrong number breaks the column sums; the structure itself flags the error. The second is hardware benchmarking — chart-shaped output. A weird cell creates a visible kink in the line; the comparison reveals it.
Writing prose is different. A five-hundred-word section with a wrong version number buried in the middle — the four hundred words around it, all internally coherent, give the error cover. There's no column it has to add up with. There's no chart line it has to fit. It just sits there, looking like ordinary text.
Benchmarks fail too, and I've gotten burned. But the failures usually have something to push against — a baseline, a control, a chart. Prose doesn't have that friction. It's the easiest place to hide a mistake.
There's a second structural problem. When I do bookkeeping, I'm working with my own positions. When I benchmark, I'm measuring my own hardware. When I write a blog post, every other paragraph is paraphrasing someone else's work — what a library version supports, what a paper's author argued, what a GPU's spec sheet says.
Paraphrase is an error factory. My own stuff I can't get wrong by much. Other people's stuff I can get wrong constantly.
The third problem — the one that actually shipped three fabrications — was that my existing gate only watched the front door. Step 9, when I designed it, had a mental model: "new article, going out for the first time." Edits, patches, post-publication corrections were a different code path. They walked right past the gate.
All three fabrications I shipped were from edits. None of them were first-publication. They came in through the side window.
The front door was watched. The side window wasn't.
Why the sticky note couldn't save me
Looking back at the eight failures, here's what's common.
A skill is a sticky note on my desk. It says "do X before Y." But when I'm doing Y, my attention is on Y. The sticky note becomes part of the wallpaper. I've read it. I "know" what it says. I do Y and skip X.
Or — and this one is sneakier — I see the note, I'm aware of it, and I make a little internal deal: "this one is small, I'll skip it just this time." Each individual deal is reasonable. They accumulate.
Or worst, I have a vague feeling that I already did X. The memory is fuzzy but confident. I didn't. I do Y.
All three failure modes share something: they happen inside my own decision loop. A sticky note can only reach me if I'm willing to read and obey it in the moment. It can't intervene in the decision to ignore it.
From sticky note to hook
Different layer. What if the rule isn't on my desk — it's outside of me entirely?
In Claude Code's vocabulary: a skill is the sticky note version — markdown the agent reads and chooses whether to follow. A hook is something else: a small script in the harness that runs automatically before or after every tool call, and can refuse the call outright. The agent isn't even consulted. It just can't perform the action.
A hook doesn't argue. If the preconditions aren't met, the action simply doesn't happen. I don't need to remember anything. I don't need discipline. I literally cannot perform the action.
The hook isn't in my layer. It's around me.
Where to put the hook — desk vs exit
Where the hook goes matters as much as the hook itself.
If I put a hook on "every time you save a file," I'd be blocked fifty times in a single editing session. Editing my own typos would require human approval. That's a tool that makes work impossible.
But the actual failure mode is never "wrote a thing in the middle of editing and forgot to verify." It's "finished editing, sent it, never went back to verify." The failure has one location: the send moment. Specifically, git commit.
So the hook goes on the exit, not on the desk. Drafts can be sloppy. Errors can sit on disk for an hour. Nothing leaks until I press send — and at that exact moment, something else is watching.
What you give up: the hook can't prevent mistakes from appearing in the working tree. It can only prevent them from leaving. The promise is narrower — no leakage, not no errors. Worth being honest about.
Two cases — one promoted, one didn't
I ran into two opportunities in the same session. Comparing them makes the judgement call concrete.
Case A: benchmarking — kept it at the skill layer
I had a benchmark skill, around fifty lines. Standard checklist: clear environment, run three times, take the median. The skill was real, the skill was being read, and I was still tripping over the same things:
I'd switch software versions and forget that three caches were holding stale compiled paths. Numbers from the old version, attributed to the new.
I'd benchmark a new GPU without running a plain baseline first. A hybrid workload's slowness got blamed on the new hardware. Nine days down the wrong investigation.
I'd test only in English, miss that my system was much weaker in Chinese, and ship a conclusion that didn't hold under a 50/50 user mix.
I'd hit the wrong API endpoint for speculative decoding — /v1/completions instead of /v1/chat/completions — and an instruct-tuned model would degenerate into repetition, faking a thirty-percentage-point boost in acceptance rate.
I added those lessons to the skill. Fifty lines became eighty.
But I didn't promote it to a hook. The reason is simple: benchmarking has no commit moment. There's no git push for a benchmark result. There's no natural exit where a hook could sit without interfering with normal work. So this one stays in the skill layer. Skill discipline, plus the knowledge that this layer is what I have.
That's fine. Not everything fits into a hook.
Case B: blog publishing — promoted to a hook
This one has a clear send moment: git commit. So this is where the hook goes.
The logic, in plain language: I'm about to commit. The hook checks the staged files (the ones I've already git add-ed). If none of them is a blog post, the hook is silent, and the commit goes through. If one of them is a blog post, the hook looks for a small file — a "I just ran both verification gates and they passed" receipt. The receipt has to exist and have been touched within the last thirty minutes. If it's there and fresh, commit proceeds. If it's missing or stale, the commit is blocked, with a stderr message telling me what I haven't done.
There's an escape hatch, with rules. Pure typo fixes, metadata adjustments, title polish — these don't need re-verification, and the hook would create friction for them. So I can include [skip-gate] in the git commit -m message and the hook will pass through.
But the rules around the escape hatch are tight. Anything touching a fact — even one word — is off-limits, by skill policy. The bypass is also self-recording: the marker has to live in the -m string, which means it ends up permanently in git log. Editor-based or -F-style commit messages don't get inspected and are blocked outright. Fail-closed.
The audit comes from git history, not from anything the hook writes. Every use of the bypass leaves a public trail.
Two layers, clean division of labor:
- The skill knows whether the verification gates ran and what they said. It understands content.
- The hook doesn't understand content at all. It just checks if a small file exists and is fresh enough.
The cooking-line analogy: a restaurant kitchen has a food-safety pass. The chef ticks a box when the dish is plated (that's the skill — the chef knows food). The expediter sees the tick and only then lets the plate leave (that's the hook — the expediter doesn't cook, but he holds the line). The expediter doesn't need to know food. He needs to know one rule: no tick, no plate goes out.
I tested six scenarios: any non-commit command (hook is invisible); a commit with no blog files staged (hook is invisible); blog staged but no receipt (blocked); [skip-gate] present (passes); fresh receipt (passes); receipt thirty-one minutes old (blocked). All six behaved correctly.
A generalizable rule
Stepping back from this specific case.
When I notice "I know the rule and I keep breaking it anyway," the rule isn't usually wrong. The layer is.
There are three layers a rule can live on. The innermost is memory — I remember the rule. Memory drifts, fades, gets crowded out by whatever's in front of me. Mostly useless under load.
The middle is the skill / sticky note — the rule is written down and read into context. This is where most discipline lives. It works until attention is full.
The outermost is the hook — the rule lives outside my own layer entirely. Not "I choose to follow it." I literally can't perform the action.
A simple heuristic: when the same correction has appeared five or more times in your feedback record, the rule needs to move outward one layer.
Not everything belongs on the outermost layer. Benchmarking, as discussed, has no natural exit point. Forcing a hook on it would break daily work for marginal benefit. The judgement call is one question: is there a single send moment for this work? If yes, the hook goes there. If no, the rule stays at the skill layer and you live with the looser guarantee.
What I'd leave you with
The hook I built here gates publishing to my blog. But the concept of a "send moment" is everywhere in software work.
Before a commit lands on main. Before a deploy ships. Before an email goes to a client. Before a scheduled job activates. Before an SDK release tag is pushed. Every one of these is a candidate hook location, if you have a checklist that you keep accidentally skipping.
What's the rule you've been corrected on more than five times despite knowing better? And does it have a send moment you could attach a hook to?
FAQ
- What's the actual difference between a skill and a hook in Claude Code?
- A skill is a markdown rule the agent reads before doing relevant work — useful, but the agent still chooses whether to follow it. Failure modes: it saw the rule but the current task absorbed all its attention; it saw the rule and decided 'this one time should be fine'; it thinks it already followed the rule but didn't. A hook is a small script the harness runs around every tool call. The agent can't skip it, because the hook isn't inside the agent's decision loop — it's outside, in the layer running the agent.
- Why hook the git commit step and not Write/Edit?
- Drafting touches the file fifty times. Blocking every save would make work impossible. The failure mode that actually shipped fabrications was 'wrote the change, sent it, never re-verified' — and that failure has exactly one moment: git commit. Putting the hook at the exit, not on the desk, means I can edit freely. Nothing leaks until I press send.
- Doesn't the [skip-gate] escape hatch defeat the whole point?
- Some, yes. So the rules around it are written into the skill: only pure typo / metadata / title polish — anything touching a verifiable fact, even one word, is off-limits. The bypass is also self-recording: the marker has to be in the git commit -m message string (editor-based or -F messages are blocked outright, fail-closed). So every use of the escape hatch leaves a permanent trail in git log. The hook isn't 100% strong, but the cost of misuse is higher than a quick decision.
- How do I figure out what to gate in my own workflow?
- Look for things you've been corrected on five or more times despite knowing the rule. Then ask: is there a single 'send' moment for this work — a commit, a deploy, a push to main, an email to a client, a PR mark-ready? If yes, that's where a hook belongs. If the work has no natural exit (running benchmarks, for example), keep the rule at the skill layer and accept it'll need vigilance.