bug#81520: Add pre-receive hook to Savannah to reject LLM-encumbered commits

Jim Porter <[email protected]> Sat, 1 Aug 2026 11:37:27 -0700
Newsgroups gmane.emacs.bugs
Message-ID <[email protected]>
On 7/31/2026 11:22 PM, Eli Zaretskii wrote:
>> Date: Fri, 31 Jul 2026 09:52:29 -0700
>> Cc: [email protected]
>> From: Jim Porter <[email protected]>
>>
>> On 7/31/2026 3:51 AM, Eli Zaretskii wrote:
>>> One alternative would be to search the entire log message and
>>> author/committer addresses for regular expressions matching
>>> "Anthropic", "ChatGPT", "OpenAI" etc.  Regexps make it easier to adapt
>>> to changes and unanticipated issues, and can be general enough to be
>>> relatively insensitive to minor changes in email addresses.
>>
>> Yeah, I think regexps would make sense here. The email addresses are
>> still probably the most accurate indicator though. We want to disallow
>> an address like "[email protected]", but not
>> disallow the word "copilot" entirely.
> 
> Why not disallow "copilot" entirely?

I suppose we could, but it's an ordinary dictionary word, and we have a 
more-specific identifier we could use (the email). While disallowing the 
word "copilot" could hypothetically catch more cases, I'm not sure those 
cases actually exist.

The easiest checks are for the Assisted-By and AI-Assisted fields, which 
we can always reject. I *definitely* think we should reject these, since 
they're a clear indication of LLM assistance that isn't specific to any 
one model.

The remaining cases I'm aware of are when an LLM-associated email 
address (possibly with display name) appears as the author or committer, 
or in the Co-Authored-By/Co-Developed-By fields. We could just check the 
whole commit message for any LLM-ish words if we wanted though. In 
practice, I doubt it will make much difference.

So long as we get something reasonably accurate though, I'm not going to 
nitpick the particulars here.

>> I don't mind writing the description for CONTRIBUTE. Most of that is
>> separate from the exact technical means for the hook anyway; the hook is
>> intended to be only a friendly check for honest mistakes and not a
>> strict policy enforcement system anyway.
> 
> It is not a separate job, because the description of what is
> disallowed is tightly coupled to what the hook actually does.

Should be document the exact implementation we use for detecting 
LLM-assisted commits, or is it enough to state that our policies don't 
permit them? The latter seems much more important to spell out to me. 
I'm not sure people need to know ahead of time that (for example), we 
don't allow commits with the word "copilot" in them.

On the other hand, I don't really mind how we describe this in 
CONTRIBUTE, so long as we make our policies clear.

>> The problem is that when the LLM initiates the "git commit", it will get
>> back the result of that command. Most LLMs are designed to try different
>> things if a command fails, including things we humans might consider
>> malicious. If the commit-msg hook causes "git commit" to fail with a
>> message saying we reject commits with a "Co-Authored-By: Some LLM"
>> field, there's a good chance the LLM will remove that line and retry the
>> commit. The user might not even realize it happened unless they look
>> carefully at the LLM's logs.
> 
> My suggestion is to search the entire log message _and_ the
> author/committer for the relevant patterns.  Thus, whether there is or
> isn't Co-Authored-By is not relevant in a push hook more than it is
> relevant in a commit hook.  Or what am I missing?

If we reject LLM-assisted commits (no matter what precise logic we use), 
there's a reasonable chance that an agentic LLM will simply rewrite the 
commit message to pass our check, effectively lying by omission. Since 
this can happen in the background, a well-intentioned user running an 
LLM might not even see the error message unless they look at the full logs.

>> We could also do an applypatch-msg (or pre-applypatch) hook. If I'm
>> understanding your concern Eli, this should make your life easier
>> without running into the above problems with a commit-msg hook. This
>> hypothetical hook would run when you call "git am foo.patch" and reject
>> it immediately so that you don't have to remove the commit later when
>> preparing to push.
> 
> "git am" also commits, so a pre-commit hook should cover this, right?
> And "git apply" will be followed by a commit, so again a pre-commit
> hook should do.

According to the Git documentation[1], "git am" only runs the various 
applypatch hooks, though autogen.sh sets up applypatch-msg to call 
commit-msg (ditto for pre-applypatch). We can change that logic though. 
For "git apply", you'd be calling "git commit" afterwards directly, so 
it only runs the various commit hooks (but you'd be typing in the commit 
message anyway, so hopefully you wouldn't be adding LLM annotations).

The benefit of doing this in applypatch-msg instead of commit-msg is 
simply to avoid giving an agentic LLM all the feedback it needs to hide 
its use from us when it generates the commit. At the end of the day, 
these are just pattern-matching tools that are trained to fix errors and 
make forward progress.

(After checking to be sure, pre-commit runs too early; it's before the 
user enters the commit message, so we need commit-msg and/or 
applypatch-msg.)

[1] https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks