A guardrail I built for code hygiene caught a branding mistake instead

I got my first commit rejected by a check I had written myself, three minutes earlier, for exactly this repo.

I was packaging a starter kit. The whole idea of the thing is that it ships empty: other people install it and fill the frame with their own work, so the last thing it should carry is any trace of me. To make sure of that, I had added a pre-commit hook that greps every staged change for a short list of personal-identity strings, my handle among them, and refuses the commit if it finds one. Standard hygiene for anything you plan to hand to strangers. I wrote the hook, staged the kit, committed, and the hook threw the commit straight back at me. The banned string it had found was the product's name. I had named the kit after myself.

If you are building something distributable with AI and you are on the fence about whether mechanical guardrails are worth the friction, this is the argument for them, and it is not the argument I expected to be making. The check did not catch the mistake I built it to catch. It caught a different one I did not know I was making, and that turned out to be the more valuable catch.

why did my own check reject the product's name

The hook is deliberately dumb. It does not understand context. It has a list of strings that must never appear in a public surface, and my handle is on that list because the entire point of the kit is to keep my identity out of code other people will own. When I named the kit after myself, that name became a banned string sitting in the most public place possible, the package's own identity. The grep did exactly what I told it to do. It saw a forbidden string on a line headed for a commit and it stopped.

In the moment it read as the tool being too rigid. My first instinct was to add an exception, to whitelist the name so the "real" work could proceed, which is the instinct that quietly guts every guardrail a builder ever writes. The check was not malfunctioning. It was telling me something true that I had not asked it to evaluate. The name I had picked was going to fail this exact check on every single commit for the life of the project, because the rule that made it fail was a rule I actually believed in. The friction was not a bug in the hook. It was the hook doing its job against a decision that was wrong.

the block was right for a reason I did not build it to catch

Here is where the cheap catch became the expensive one. The mechanical reason to rename was real: a name that trips your own identity check on every commit is not a name, it is a recurring outage you scheduled for yourself. But there was a second reason, and the hook had no idea about it.

The kit's promise is that you are not getting a copy of me. You install an empty frame and it becomes yours. A product named after its author says the exact opposite of that. It says this is mine, you are borrowing it, my fingerprints are on every file. The name I had reflexively reached for undercut the one thing the product was supposed to stand for, and I had not noticed, because I was thinking about packaging and not about positioning. A grep that knows nothing about branding caught a branding mistake, because the branding mistake happened to be shaped like a syntax violation. I renamed the kit to a plain role label that matches the blank-canvas promise, checked that zero occurrences of the old name were left anywhere in the package, and committed clean.

how do you get a check to catch what you did not foresee

You cannot, directly. That is the point. You do not write a check that anticipates the strategy error, because if you could anticipate it you would have just not made it. What you do instead is encode the non-negotiables you already hold as checks that actually run, on every commit, with no exceptions, and then you let the coverage overlap in ways you did not plan.

An invariant with teeth pays out in two places. The first is the one you designed it for, the boring one, the stray key or the leaked path, and that alone justifies it. The second is the payout you cannot forecast: every so often a rule you wrote for one reason fires on a mistake from a completely different category, because real mistakes do not respect the neat boundaries between "code hygiene" and "product decision." A name is a string and a positioning statement at the same time. The grep only sees the string, but stopping the string stopped the bad decision riding along with it.

The method for a vibe coder shipping anything distributable comes down to three moves. Write your non-negotiables down as checks that run automatically, not as intentions you promise yourself you will remember, because the intention you hold in your head is not enforced and the check on every commit is. When a check blocks you, treat the block as information before you treat it as an obstacle, and ask what it is actually telling you before you reach for the whitelist, because the reflex to make the red light go away is how load-bearing guardrails die. And keep the checks blunt on purpose. A rule simple enough to be rigid is a rule that catches things you were not smart enough to look for, and the day it saves you will be a day you did not see coming.

None of this makes the work slower in the way it feels like it should. A guardrail that fires on the first commit feels like friction and reads, a week later, like the cheapest code review I have ever gotten. The check cost me a rename I should have done anyway. Skipping the check would have cost me a product that quietly argued against its own pitch, shipped to strangers, with my name on it.

If you are about to hand something you built with AI to other people, an installable kit, a template, a small custom app, and you want the guardrails hardened before it leaves your machine so the obvious mistakes and the non-obvious ones both get caught at the door, /work-with-us. Most of what a safe handoff is comes down to encoding the rules you already believe in as checks that run without you.

// part of the custom apps topic

// grab the free starter kit that makes your AI stop forgetting and stop guessing: get it →

// building with AI? the field manual has the structured lessons.

// hitting this on a real build? this is what I fix →