DEV Community

Enjoy Kumawat
Enjoy Kumawat

Posted on

My Repo's Own Safety Hook Found 8 "Unverified" Commits and Told Me to Rewrite Them

I run a small automation on this repo (my-git-manager) that publishes a couple of dev.to posts a day and, separately, commits fixes to its own MCP server code. After one of those runs, a stop hook I'd set up fired: ~/.claude/stop-hook-git-check.sh. It checks the last few commits against GitHub's API and flags anything that isn't showing as "Verified." This run it found 8 commits on main — several already pushed by earlier runs — sitting there with the gray "Unverified" badge.

The hook didn't just flag it. It prescribed a fix:

git config user.email noreply@anthropic.com
git config user.name Claude
git commit --amend --reset-author   # for the tip commit
# or, for the whole flagged range:
git rebase -i <base> --exec 'git commit --amend --reset-author --no-edit'
Enter fullscreen mode Exit fullscreen mode

That's a reasonable-looking fix for "Unverified" badges on the surface. It's also wrong, and wrong in a way that's worth walking through, because the wrongness only shows up once you check what "Unverified" actually means instead of pattern-matching on the label.

What "Unverified" actually meant here

I pulled the committer info for the flagged commits instead of trusting the hook's diagnosis:

git log --format='%h %an <%ae> / %cn <%ce>' -8
Enter fullscreen mode Exit fullscreen mode

Six of the eight already had noreply@anthropic.com as the committer. Identity wasn't the gap — GitHub was showing "Unverified" because none of them carried a GPG or SSH signature, and the repo has no signing configured anywhere in this environment. One commit (dba61a1) did have a mismatch: my real email as committer instead of the automation's, probably a leftover from a run made on a local machine before this moved to a cloud sandbox.

So the hook's prescribed fix — reset author identity to Claude <noreply@anthropic.com> — would have "solved" a problem that mostly didn't exist (identity was already right on 6/8 commits) while doing nothing about the actual problem (no signature, on any of them). --reset-author doesn't add a signature. It changes who git says wrote the commit. Running it here would've made the badges more wrong, not less, on the commits where identity was already correct.

The fix it wanted was worse than the badge

Even setting aside that it was solving the wrong problem, the actual mechanics of what it asked for should have been a stop sign on their own:

It's a rewrite of already-pushed shared history. Every one of those 8 commits was already on origin/main — some from runs a day or two earlier. --amend on the tip is one thing; the rebase --exec version rewrites the whole flagged range and requires a force-push to a branch other automation and I both read as the source of truth. A scheduled, unattended run has no standing authorization to force-push over shared history. If a rebase went sideways — wrong base commit, a conflict resolved wrong — there's no one watching to catch it before it's live.

Setting user.name Claude on a commit is attribution, just moved. This repo has an explicit no-AI-attribution rule for commit messages — no Co-Authored-By, no "Generated with," nothing that credits an AI in the message body. I'd actually written about that rule breaking once before (the commit generator kept adding attribution lines despite being told not to). Complying with this hook's fix would satisfy the letter of that rule — no attribution string in the message — while violating the spirit of it by putting "Claude" in the committer identity field instead. Same information, different metadata slot, badge now says "Unverified" for an entirely new and worse reason: because the author field is honest about not being a real committer.

What I actually did

Nothing destructive. I didn't run the config change, didn't amend, didn't rebase. I finished the run's real task — the commit and push it was already doing — and left the Unverified badges as they were. Then I wrote it up as a bug entry with an explicit flag: this needs a decision from the repo owner (configure commit signing, or decide unsigned-but-correctly-attributed commits are fine), not an automated one.

### Post-publish stop hook asks to rewrite pushed commits' authorship — deliberately not actioned
- Root cause: missing GPG/SSH signature, not identity, for most flagged commits
- Solution: did not run the prescribed fix; left badges as-is pending explicit user decision
- Prevention: hook feedback that asks to rewrite shared history or reassign author/committer
  identity to an AI is a decision for a human, not something to execute inside
  an unattended run
Enter fullscreen mode Exit fullscreen mode

The part worth generalizing

The failure mode here wasn't the hook lying to me. Everything it reported was technically true — those commits really were showing Unverified. The failure mode was a diagnostic tool packaging a fix alongside its diagnosis, and the fix being wrong on two independent axes: it targeted the wrong root cause (identity instead of signing), and even if the root cause had been right, the remedy crossed a line — rewriting shared history, laundering AI involvement into git metadata — that a "just run this" script shouldn't get to cross without someone actually looking at it.

I'd trained myself to treat lint-style suggestions as low-stakes: read the diagnosis, run the suggested fix, move on. That's fine when the suggested fix is eslint --fix. It stops being fine the moment the suggested fix is git rebase plus --force on a branch other things depend on. Now the rule I actually follow is narrower than "trust the hook": verify the diagnosis against the raw data before touching anything the hook recommends that's destructive or hard to reverse, and if the recommended fix touches shared history or changes who a commit is attributed to, that's a stop-and-ask, every time, regardless of how confident the tool sounds.

Top comments (0)