The last post ended with a confession. I had a checker that enforced my rule, and it had one flaw left: I had to remember to run it. A check you have to remember to run is a rule in your head wearing the costume of a tool. So the obvious next move was to take the remembering out of my hands and put it somewhere automatic. The obvious place, the place everyone reaches for first, is a commit hook.
I reached for it too. It is the wrong place, and it is wrong for a reason worth the whole post.
The hook works until the morning you are in a hurry
A pre-commit hook runs the check before every commit and refuses the bad ones. For about a day this feels like the answer. The check is now automatic. I cannot commit a violation because the hook will stop me. Problem solved, in four lines of shell.
Then comes the morning where I need to commit something unrelated, the lint is failing on a thing I genuinely will fix later, and I am in a hurry. I type the flag. Every one of these tools has the flag, because the people who build them know you sometimes have to get past them, so --no-verify exists and I use it, just this once. The next time it is easier. By the third time it is muscle memory, and the hook has quietly become advisory.
The hook did not fail loudly. It failed by training me to skip it. That is worse than no hook, because now I believe I am protected.
A guard you put in your own critical path is a guard you will eventually step over, because the critical path is exactly where you are trying to get out of the room. The flag that lets you skip it is not a flaw in the tool. It is the tool admitting it lives in the wrong place.
The problem was behavioral, not technical
Once I noticed the muscle forming, the diagnosis was easy and a little embarrassing. The hook was not failing on the merits. The check itself was fine, the same thirty lines that caught me last time. The hook was failing because of where it sat. It sat at the commit, which is the one moment I am always rushing, always trying to close the loop and move on. Putting a gate there guarantees a collision between the gate and my impatience, and my impatience has a perfect record.
I have done this before, with other guards, in other repos. Anything I put in the commit path, I have eventually learned to skip, because the commit is the door and I am usually leaving. The check did not need to be stricter. It needed to be somewhere I am not.
What worked: take the check out of band
So I moved it out of my path entirely. The lint now runs on a schedule, by the machine, not by me. It runs read-only: it never writes the vault, never makes a commit, never asks for one. It is silent when the vault is clean and it sends a single notification, with the findings written to a file I can open, when it is not.
There is no --no-verify, because there is no verify. There is nothing in my path to step over. I cannot skip a thing that does not depend on me showing up at all. The check happens whether I am sharp that morning or still on my first coffee, the same way the backup happens and the sync happens, in the background, on its own clock.
And it is not hateful, which was the other half of the problem. A blocking hook is a tax on every commit, including the thousands that were fine. The scheduled check taxes nothing. It asks for no friction in the moment, so there is no friction to resent and no flag to reach for. It only ever speaks when I have actually broken the rule, which is the only time I wanted to hear from it anyway.
The new failure mode is the opposite one
Moving the check out of band fixes the skipping, and immediately introduces the opposite risk, which is worth naming because it is the one I will hit next.
A blocking hook fails by being too present. A silent scheduled check fails by being too absent. If the schedule quietly stops firing, or the notification gets lost in the pile, the check fails the worst way a guard can fail: silently, while I keep believing it is watching. “Clean” and “never ran” look identical from where I sit, and only one of them is good news.
What next
Four posts ago this was a rule about what context is allowed to leave the machine. Then it was a sentence in an operating contract. Then it was thirty lines of script I had to remember to run. Now it is a read-only process that runs every morning on its own and only mentions itself when I have broken the rule.
That is the whole arc of making a rule real, and it is the same move every time: keep moving the rule further away from the part of you that forgets. From your intentions, to a document, to a tool, to a thing that runs itself. The work was never the rule. The work was getting the rule out of my hands, one step at a time, until the only thing left to automate was the proof that the automation is still alive.
Coming Soon
Next in The Mind Palace Diaries: watching the watcher. A silent guard that stops running looks exactly like a guard with nothing to report, and only one of those is safe.