Skip to content
Commit Check
All posts

How eleven projects check AI attribution in CI

GitHub's Octoverse report has this line:

By September 2026, more than 40% of pull requests on GitHub started with an agent's first draft, up from 17% in March.

Many of those agents sign their work. Claude Code, for one, adds a Co-Authored-By: line to its commits unless you turn that off.

Projects have answered with AI policies. A policy file doesn't stop a line that a tool adds by default, so some projects now check the rule in CI. I read eleven of those checks. Every project name below links to the file at the commit I read.

The same two lines, opposite results

Take two lines an agent might leave on a commit, and ask what each project's check does with them:

Project Co-Authored-By: Claude … Assisted-by: Claude Code
Incus fails fails
Apache XTable fails not checked
JUnit fails fails
Neovim fails fails: only a bare AI-assisted is allowed
FreeBSD atf fails passes: it is the trailer they ask for
Nextcloud labels the pull request labels the pull request
Vim not checked labels the pull request
Ant Design Vue passes: it is required fails: no AI co-author

Three things stand out.

The same line is required in one project and blocks the merge in another. Ant Design Vue fails a commit that has no AI co-author: "Every commit must include an AI co-author trailer." Incus and JUnit fail a commit that has one, and XTable fails a commit that credits Claude.

Projects that want disclosure don't agree on its form. FreeBSD atf and Nextcloud ask for an Assisted-by: trailer, and Nextcloud's example names both the tool and the model. Neovim wants the tool left out. Its check prints Only a generic "AI-assisted" token is allowed, and a trailer that names the tool fails.

Not every check blocks. Vim and Nextcloud add an "AI assisted" label and leave the decision to reviewers. Nextcloud fails only when an agent signs off, because the DCO "must only be attested by a human contributor".

So there is no habit a contributor, or an agent, can learn once and use everywhere. The rule has to come from the repository they are contributing to.

Two projects check the pull request instead

LangChain's docs repository reads the pull request description, because a squash merge makes it the commit message on main. It fails on the "Generated with Claude Code" badge and on a Claude co-author line, and asks for "one plain sentence instead, for example: Prepared with Claude Code."

Solana web3.js makes the author tick exactly one of two boxes in the description: "No AI tooling was used" or "AI tooling was used". The second box also needs a filled-in "Tool and extent:" line. For outside contributors the check also looks for a tool's signature left in the title, the description, the commits or the branch name, and says what it takes that to mean: "the change was opened without its author reading it."

Eight shell scripts and three config files

Eight of the eleven checks are shell scripts the project wrote itself. Most carry their own list of tools: XTable's has one name, Incus's nine, Nextcloud's fourteen. A list like that is out of date when the next agent ships. And a pattern loose enough to find claude on an author line also stops a contributor whose name is Claude.

The other three use Commit Check, which I maintain. There the policy is one setting, and the list of tools comes with the tool.

JUnit forbids AI marks. Its contributing guide says "Git commits must be authored by you and not attribute an AI tool as co-author", and the workflow that checks it has four settings:

- uses: commit-check/commit-check-action@08357050a644b85d321164f23054f176c13bb018 # v2.20.2
  env:
    CCHK_AI_ATTRIBUTION: forbid
    CCHK_CONVENTIONAL_COMMITS: false
    # Disable default length rules
    CCHK_SUBJECT_MIN_LENGTH: 0
    CCHK_SUBJECT_MAX_LENGTH: 10000
  with:
    message: true
    branch: false

FreeBSD atf asks for disclosure. The same file also requires a sign-off and refuses GitHub's noreply author addresses. This is its AI part:

[commit]
ai_attribution = "disclose"
ai_disclosure_trailers = ["Assisted-by"]

Evolution Gaming is a company, and its policy opens with "AI tools may assist with development, but a human author takes responsibility for every commit." It publishes one workflow for its repositories to copy and mark as required. The workflow sets CCHK_AI_ATTRIBUTION: "forbid" and keeps a grep of its own next to it, for trailers the version it pins doesn't catch.

One commit message gets opposite answers from two of them. In a directory with FreeBSD atf's commit-check.toml:

$ cat message.txt
Fix flaky retry test

Wait for the worker to drain before asserting.

Assisted-by: Claude Code
Signed-off-by: Jane Doe <jane@example.org>
$ commit-check --message message.txt
$ echo $?
0

With JUnit's settings, in a directory with no config file:

$ export CCHK_AI_ATTRIBUTION=forbid CCHK_CONVENTIONAL_COMMITS=false
$ export CCHK_SUBJECT_MIN_LENGTH=0 CCHK_SUBJECT_MAX_LENGTH=10000
$ commit-check --message message.txt
✖ Commit rejected by Commit Check · 1 check failed

✖ CC013 ai-attribution failed: Claude Code
  AI attribution is forbidden in this project — detected: Claude Code
  Suggest: This project does not accept AI attribution in commit messages. Remove the AI trailer lines and re-commit.
  Docs:    https://commit-check.com/rules/#cc013

What none of them can check

All eleven read what a commit or a pull request says. None can tell who, or what, wrote the diff. Delete the trailer and the commit looks like any other.

GitHub's 40% has the same limit. Octoverse defines an agent-started pull request as one "whose first draft came from a coding agent that we can detect", and says of its agent signals: "It is a floor, since not all agent use may be detected."

So these checks don't catch someone who hides AI use. They catch the default: the line a tool added and the contributor never looked at. They also tell the contributor what this project wants while the pull request is still open, which a policy file can't do if nobody opened it.

If you are writing this rule

  • Decide what a commit should say, not which tools are allowed. Ten of the eleven take one of three positions: no AI marks (JUnit, Incus), disclose it in the form the project names (FreeBSD atf, Neovim), or label it and let reviewers decide (Vim). Ant Design Vue takes a fourth: always credit the tool.
  • Put the fix in the failure message. Nextcloud's prints the trailer to use in place of the one it rejected.
  • If you squash on merge, check the description too, as LangChain does.

With Commit Check the first two positions are ai_attribution = "forbid" and ai_attribution = "disclose". The policies guide has the settings for more policies, Nextcloud's among them, and the same check for a pull request description. The GitHub App runs the same rules without a workflow.

If your project checks this in a way I didn't cover, I would like to read it.

Esc
↵ to open ↑↓ to move Esc to close
Keyboard shortcuts
Ctrl K /
Open search and commands
t
Toggle dark mode
[ ]
Previous / next page
j k
Previous / next heading
?
Show this help