Two guards that stop Co-Authored-By: trailers, π€ Generated with β¦ footers
and session URLs from reaching a remote.
They catch different things, and you want both β a pre-push hook never sees a
PR body, and a gh wrapper never sees a commit message.
| Guard | Covers |
|---|---|
pre-push |
commit messages, author and committer fields |
gh |
--body / -b / --body-file / -F / --notes / --notes-file on pr, issue, release, gist, api |
Prose mentioning a tool passes; attribution metadata does not:
feat(claude-api): add streaming support allowed
docs: update Claude model IDs in the README allowed
fix: handle co-authored PRs from squash merge allowed
Co-Authored-By: Claude <noreply@anthropic.com> BLOCKED
π€ Generated with [Claude Code](...) BLOCKED
https://claude.ai/code/session_01ABC BLOCKED
# 1. pre-push hook, applied to every repo
mkdir -p ~/.config/git/hooks
install -m 755 pre-push ~/.config/git/hooks/pre-push
git config --global core.hooksPath ~/.config/git/hooks
# 2. gh wrapper β must sit ahead of the real gh on PATH
install -m 755 gh ~/.local/bin/ghVerify the wrapper actually intercepts:
which gh # => ~/.local/bin/gh, not /opt/homebrew/bin/ghIf it still resolves to the real binary, something later in your shell startup
is re-prepending that directory. Homebrew's brew shellenv is the usual
culprit β it prepends /opt/homebrew/bin, so anything that adds ~/.local/bin
before that line loses. Add this after it:
path=("$HOME/.local/bin" ${path:#$HOME/.local/bin})Both honour ALLOW_ATTRIBUTION=1. You need it when GitHub's squash-merge adds
a legitimate human Co-authored-by: trailer.
- A repo-local
core.hooksPath(Husky,.githooks/) shadows the global hook. Check withgit config --get core.hooksPath. To cover such a repo, chain the check from its own hook β but note that if the hooks directory is tracked in git, you are committing your personal policy into a shared repo. - The wrapper only covers the
ghCLI. The web UI and other API clients are unguarded. pre-pushscans only commits in the push range, so it is cheap on large repos, but it will not catch attribution already on the remote.