"No LLM bots in Co-authored-by": checking the rule before the squash merge¶
Some projects have written down a short, strict rule about AI tools. The AI policy that pip and attrs use puts it in one line:
No LLM bots in
Co-authored-by:s.
A pull request that lists an LLM product as a co-author "can't be merged and will be closed without further discussion". Using AI tools is still allowed, but contributors "must remove any LLM co-author tags and take full ownership of every line."
A machine can check that rule. Unless one does, a reviewer has to open every commit of every pull request and read its trailers.
Why squash merges make it worse¶
The co-author line is rarely on the commit a reviewer looks at. An agent adds it to the commit it wrote, the contributor pushes a fix on top, and the review happens on the final diff.
When the project squashes on merge, GitHub's default squash message collects
the Co-authored-by: and Signed-off-by: lines from all of the pull
request's commits and appends them, whichever body the repository uses. It also credits each commit author other than the person
who opened the pull request, so commits written by the Copilot coding agent
reach main with a Co-authored-by: Copilot line. One line missed in any commit
becomes a permanent co-author credit on main.
One file¶
Commit Check reads its rules
from a cchk.toml. This one checks the co-author rule, turns off the message
format and branch name rules, and allows any subject up to 1000 characters.
The default author name and email checks (CC101,
CC102) stay on:
warn = ["CC014"] # the policy does not ask for disclosure: report it, never fail
[commit]
ai_attribution = "disclose" # fail if an AI tool is credited as co-author or sign-off
conventional_commits = false # no message format
subject_max_length = 1000 # allow long subjects
subject_min_length = 0 # and short ones
[branch]
conventional_branch = false # no branch name rules
[pull_request]
check = "squash" # check the one commit a squash merge lands
The disclose policy fails a commit that credits an AI tool as a co-author
(CC015) or has it sign off
(CC016). It also asks for an Assisted-by: disclosure
(CC014). This policy doesn't ask for one, so the
warn line reports it without failing the check.
Check the commit that lands¶
With the Commit Check App installed,
every pull request gets a Commit Check result. The [pull_request] section
makes it check the commit a squash merge would land: the title and body your
merge settings produce, plus the co-author and sign-off lines GitHub adds from
the commits. A pull request whose second commit was co-authored by Claude Code
gets this:
❌ Squash message failed (1 of 3 checks)
Squash message `Add retries to the HTTP client (#214)` CC015 ai-co-author
value: Co-Authored-By: Claude <noreply@anthropic.com>
An AI tool is credited as a co-author: Claude Code
Make Commit Check a required check in the branch's ruleset, and the pull request can't be merged until the contributor removes the line from their commits. The App can read the repository and its pull requests and can write only check runs (what it reads). It never comments, and it is free for public repositories. The App guide shows what is checked under each merge setting.
If you would rather keep it in a workflow, commit-check-action reads the same file and checks every commit of the pull request.
What it catches¶
It catches the co-author and sign-off lines that Claude Code, GitHub Copilot
(including the coding agent), Cursor, Codex, Devin, Jules, Amp, Factory Droid
and OpenHands write, and model names such as gemini-2.5-pro. It matches those
known signatures, so a tool it doesn't recognize yet passes. Human co-authors
pass, including people who happen to be called Claude or Jules, and so do
Dependabot and pre-commit.ci. Assisted-by: and Generated-by: trailers pass
too, since this policy doesn't forbid them.
What it can't do¶
It checks what a commit says, not who wrote it. A commit with no AI trailer passes, whoever wrote it. The other half of the policy, "take full ownership of every line", is still the reviewer's job. What the check removes is the part a reviewer is worst at: reading trailers on the twentieth pull request of the day.
If your project's AI policy says something different, such as "disclose it
with Assisted-by:" or "no AI trailers at all",
this earlier post shows the setting for
each.