Skip to content

Commit 8337fe5

Browse files
authored
docs: add a post on how eleven projects check AI attribution in CI (#82)
1 parent 4295029 commit 8337fe5

1 file changed

Lines changed: 209 additions & 0 deletions

File tree

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
---
2+
date:
3+
created: 2026-11-10
4+
readtime: 6
5+
slug: ai-attribution-checks-in-ci
6+
description: >-
7+
I read the CI checks that eleven projects run on AI-assisted commits. The
8+
same trailer is required by one project and blocks the merge in another.
9+
Here is what each one checks, and what none of them can.
10+
categories:
11+
- UPDATES
12+
tags:
13+
- commit-check
14+
- ai
15+
- governance
16+
authors:
17+
- shenxianpeng
18+
---
19+
20+
# How eleven projects check AI attribution in CI
21+
22+
GitHub's [Octoverse report][octoverse] has this line:
23+
24+
> By September 2026, more than 40% of pull requests on GitHub started with an
25+
> agent's first draft, up from 17% in March.
26+
27+
Many of those agents sign their work. Claude Code, for one, adds a
28+
`Co-Authored-By:` line to its commits unless you turn that off.
29+
30+
Projects have answered with AI policies. A policy file doesn't stop a line
31+
that a tool adds by default, so some projects now check the rule in CI. I read
32+
eleven of those checks. Every project name below links to the file at the
33+
commit I read.
34+
35+
<!-- more -->
36+
37+
## The same two lines, opposite results
38+
39+
Take two lines an agent might leave on a commit, and ask what each project's
40+
check does with them:
41+
42+
| Project | `Co-Authored-By: Claude …` | `Assisted-by: Claude Code` |
43+
|---|---|---|
44+
| [Incus][incus] | fails | fails |
45+
| [Apache XTable][xtable] | fails | not checked |
46+
| [JUnit][junit] | fails | fails |
47+
| [Neovim][neovim] | fails | fails: only a bare `AI-assisted` is allowed |
48+
| [FreeBSD atf][atf] | fails | passes: it is the trailer they ask for |
49+
| [Nextcloud][nextcloud] | labels the pull request | labels the pull request |
50+
| [Vim][vim] | not checked | labels the pull request |
51+
| [Ant Design Vue][antdv] | passes: it is required | fails: no AI co-author |
52+
53+
Three things stand out.
54+
55+
**The same line is required in one project and blocks the merge in another.**
56+
Ant Design Vue fails a commit that has no AI co-author: "Every commit must
57+
include an AI co-author trailer." Incus and JUnit fail a commit that has one,
58+
and XTable fails a commit that credits Claude.
59+
60+
**Projects that want disclosure don't agree on its form.** FreeBSD atf and
61+
Nextcloud ask for an `Assisted-by:` trailer, and Nextcloud's example names
62+
both the tool and the model. Neovim wants the tool left out. Its check prints
63+
`Only a generic "AI-assisted" token is allowed`, and a trailer that names the
64+
tool fails.
65+
66+
**Not every check blocks.** Vim and Nextcloud add an "AI assisted" label and
67+
leave the decision to reviewers. Nextcloud fails only when an agent signs
68+
off, because the DCO "must only be attested by a human contributor".
69+
70+
So there is no habit a contributor, or an agent, can learn once and use
71+
everywhere. The rule has to come from the repository they are contributing to.
72+
73+
## Two projects check the pull request instead
74+
75+
[LangChain's docs repository][langchain] reads the pull request description,
76+
because a squash merge makes it the commit message on main. It fails on the
77+
"Generated with Claude Code" badge and on a Claude co-author line, and asks
78+
for "one plain sentence instead, for example: Prepared with Claude Code."
79+
80+
[Solana web3.js][solana] makes the author tick exactly one of two boxes in
81+
the description: "No AI tooling was used" or "AI tooling was used". The second
82+
box also needs a filled-in "Tool and extent:" line. For outside contributors
83+
the check also looks for a tool's signature left in the title, the
84+
description, the commits or the branch name, and says what it takes that to
85+
mean: "the change was opened without its author reading it."
86+
87+
## Eight shell scripts and three config files
88+
89+
Eight of the eleven checks are shell scripts the project wrote itself. Most
90+
carry their own list of tools: XTable's has one name, Incus's nine,
91+
Nextcloud's fourteen. A list like that is out of date when the next agent
92+
ships. And a pattern loose enough to find `claude` on an author line also
93+
stops a contributor whose name is Claude.
94+
95+
The other three use [Commit Check](https://github.com/commit-check/commit-check),
96+
which I maintain. There the policy is one setting, and the list of tools comes
97+
with the tool.
98+
99+
[JUnit][junit] forbids AI marks. Its contributing guide says "Git commits must
100+
be authored by you and not attribute an AI tool as co-author", and the
101+
workflow that checks it has four settings:
102+
103+
```yaml
104+
- uses: commit-check/commit-check-action@08357050a644b85d321164f23054f176c13bb018 # v2.20.2
105+
env:
106+
CCHK_AI_ATTRIBUTION: forbid
107+
CCHK_CONVENTIONAL_COMMITS: false
108+
# Disable default length rules
109+
CCHK_SUBJECT_MIN_LENGTH: 0
110+
CCHK_SUBJECT_MAX_LENGTH: 10000
111+
with:
112+
message: true
113+
branch: false
114+
```
115+
116+
[FreeBSD atf][atf] asks for disclosure. The same file also requires a
117+
sign-off and refuses GitHub's noreply author addresses. This is its AI part:
118+
119+
```toml
120+
[commit]
121+
ai_attribution = "disclose"
122+
ai_disclosure_trailers = ["Assisted-by"]
123+
```
124+
125+
[Evolution Gaming][evolution] is a company, and its policy opens with "AI
126+
tools may assist with development, but a human author takes responsibility
127+
for every commit." It publishes one workflow for its repositories to copy
128+
and mark as required. The workflow sets `CCHK_AI_ATTRIBUTION: "forbid"` and
129+
keeps a grep of its own next to it, for trailers the version it pins doesn't
130+
catch.
131+
132+
One commit message gets opposite answers from two of them. In a directory
133+
with FreeBSD atf's `commit-check.toml`:
134+
135+
```console
136+
$ cat message.txt
137+
Fix flaky retry test
138+
139+
Wait for the worker to drain before asserting.
140+
141+
Assisted-by: Claude Code
142+
Signed-off-by: Jane Doe <jane@example.org>
143+
$ commit-check --message message.txt
144+
$ echo $?
145+
0
146+
```
147+
148+
With JUnit's settings, in a directory with no config file:
149+
150+
```console
151+
$ export CCHK_AI_ATTRIBUTION=forbid CCHK_CONVENTIONAL_COMMITS=false
152+
$ export CCHK_SUBJECT_MIN_LENGTH=0 CCHK_SUBJECT_MAX_LENGTH=10000
153+
$ commit-check --message message.txt
154+
✖ Commit rejected by Commit Check · 1 check failed
155+
156+
✖ CC013 ai-attribution failed: Claude Code
157+
AI attribution is forbidden in this project — detected: Claude Code
158+
Suggest: This project does not accept AI attribution in commit messages. Remove the AI trailer lines and re-commit.
159+
Docs: https://commit-check.com/rules/#cc013
160+
```
161+
162+
## What none of them can check
163+
164+
All eleven read what a commit or a pull request says. None can tell who, or
165+
what, wrote the diff. Delete the trailer and the commit looks like any other.
166+
167+
GitHub's 40% has the same limit. Octoverse defines an agent-started pull
168+
request as one "whose first draft came from a coding agent that we can
169+
detect", and says of its agent signals: "It is a floor, since not all agent
170+
use may be detected."
171+
172+
So these checks don't catch someone who hides AI use. They catch the default:
173+
the line a tool added and the contributor never looked at. They also tell the
174+
contributor what this project wants while the pull request is still open,
175+
which a policy file can't do if nobody opened it.
176+
177+
## If you are writing this rule
178+
179+
- **Decide what a commit should say, not which tools are allowed.** Ten of the
180+
eleven take one of three positions: no AI marks (JUnit, Incus), disclose it
181+
in the form the project names (FreeBSD atf, Neovim), or label it and let
182+
reviewers decide (Vim). Ant Design Vue takes a fourth: always credit the
183+
tool.
184+
- **Put the fix in the failure message.** Nextcloud's prints the trailer to
185+
use in place of the one it rejected.
186+
- **If you squash on merge, check the description too**, as LangChain does.
187+
188+
With Commit Check the first two positions are `ai_attribution = "forbid"` and
189+
`ai_attribution = "disclose"`. The
190+
[policies guide](../../guides/policies.md#recipes-from-real-projects) has the
191+
settings for more policies, Nextcloud's among them, and
192+
[the same check for a pull request description](../../guides/policies.md#checking-a-pull-request-description).
193+
The [GitHub App](https://github.com/apps/commit-check) runs the same rules
194+
without a workflow.
195+
196+
If your project checks this in a way I didn't cover, I would like to read it.
197+
198+
[octoverse]: https://github.blog/news-insights/octoverse/octoverse-commits-pushes-reach-new-highs-in-2026-as-developers-build-with-agents/
199+
[incus]: https://github.com/lxc/incus/blob/63377d9bacba35715edf1bcfb4fd155485a6a6fb/.github/workflows/commits.yml
200+
[xtable]: https://github.com/apache/incubator-xtable/blob/0e97d0ae94ac81f9b3e2cc3f86cc2d479e67173c/.github/workflows/commit_coauthor_validation.yml
201+
[junit]: https://github.com/junit-team/junit-framework/blob/13e10545e89a8e217c9d9d2807c449f296205033/.github/workflows/_forbid-ai-attribution.yml
202+
[neovim]: https://github.com/neovim/neovim/blob/7e394dbb896eaf86cb1046666dd423d512cda55f/.github/workflows/labeler_pr.yml
203+
[atf]: https://github.com/freebsd/atf/blob/1f183309cb6f7f6352bed578c0d1554812afdcfa/commit-check.toml
204+
[nextcloud]: https://github.com/nextcloud/android/blob/23951692e38cad0518a000b8542871cfd739e060/.github/workflows/ai-policy.yml
205+
[vim]: https://github.com/vim/vim/blob/437b1deed5d7aece25ae4a5c1c11c550aa1fe206/.github/workflows/label.yml
206+
[antdv]: https://github.com/vueComponent/ant-design-vue/blob/7483836f0adac76516e527df893c3d84a7cd4005/.github/workflows/ai-co-author.yml
207+
[langchain]: https://github.com/langchain-ai/docs/blob/c219b081f6d95d741b99fb6dd69d5a60325e5706/.github/workflows/check-pr-ai-attribution.yml
208+
[solana]: https://github.com/solana-foundation/solana-web3.js/blob/a325305c5680184a1dce27ddb592188810175883/.github/workflows/pr-hygiene.yml
209+
[evolution]: https://github.com/evolution-gaming/.github/blob/9486d3e8e67c3fdcf5cb4301ca3a6cfe93072afb/.github/workflows/human-commit-ownership.yml

0 commit comments

Comments
 (0)