Skip to content

ci: reject commits that carry AI attribution - #8

Merged
zuhairhalawa merged 1 commit into
mainfrom
ci/attribution-check
Oct 24, 2026
Merged

zuhairhalawa merged 1 commit into
mainfrom
ci/attribution-check

Conversation

@zuhairhalawa

Copy link
Copy Markdown
Owner

Why

The public history was rewritten on 2026-10-24 to remove AI co-author trailers. A local Git hook strips them from new commits and refuses pushes that carry them, but a local hook can be bypassed (--no-verify, hooks disabled, a cherry-pick, git commit-tree) and does not exist for other contributors. This adds a server-side check to the required verify job so such a commit cannot reach main unnoticed.

What changes

  • scripts/check-attribution.mjs: reads the commits of the current pull request (base..head from the GitHub event) or push (before..after), falling back to the last 20 commits when there is no event. It fails (exit 1, with a GitHub ::error annotation) on
    • a trailer line (Co-authored-by, Co-developed-by, Assisted-by, Generated-by, Signed-off-by) that names Claude, Anthropic, Copilot, ChatGPT, OpenAI, Codex or Gemini,
    • a "Generated with/by ... Claude/Anthropic" footer,
    • an author or committer whose name or email contains claude or anthropic.
      Human co-authors pass, and so does prose that only mentions Claude: the subject line (first line) is never judged, and a body line is judged only if it is a trailer or a footer.
      Also --range A..B, --all (audit of every ref) and --self-test.
  • tests/check-attribution.test.ts: runs the self-test (20 cases against throw-away repositories) inside pnpm test, so pnpm check also covers the rules.
  • package.json: check:attribution script. .github/workflows/ci.yml: one - run: pnpm check:attribution step in verify, after scan:secrets. The existing pinned action SHAs are untouched. Because verify is the required check on the Protect main ruleset, the check is enforced for every pull request.
  • CONTRIBUTING.md: one bullet.

No application source changes.

Verified

Locally, on this branch rebased onto the merged dependency fix (main cfe6dac): pnpm install --frozen-lockfile, tsc -b --noEmit, worker typecheck, pnpm test (46 files, 656 tests), pnpm scan:secrets, pnpm check:attribution, pnpm audit (no known vulnerabilities) and pnpm build all pass. --all over every ref of the public repository reports clean.

Detection, tried in a scratch clone with commits that skip the local commit-msg hook (exit status of the checker; 1 = rejected):

Commit Result
Claude trailer, git commit --no-verify 1
Anthropic-address trailer, -c core.hooksPath=/dev/null (all hooks off) 1
Claude trailer written with git commit-tree 1
"Generated with Claude Code" footer, --no-verify 1
AI author identity, --no-verify 1
Bad commit in the middle of a 3-commit branch, via a pull_request event payload 1, annotated
Human Co-authored-by: Sam Rivera 0
Subject and body that mention Claude, Anthropic and "generated with a script" 0

The same bad commit was also refused by the global pre-push hook on the owner's machine (the second line of defence); that hook's own test suite passes and it chains to a repository's own hooks.

Known limits

  • The check cannot see a commit message that does not exist yet: the merge commit GitHub writes at merge time. It is covered afterwards by the push run on main.
  • The author/committer rule matches the text claude or anthropic anywhere in a name or email, so a human contributor named Claude would be flagged. That is deliberate for now (a contributor can be added by changing the rule); say so if you want it narrower.
  • It checks commit metadata, not file contents.

A script checks the commits of each pull request or push for AI co-author or
assistant trailers, "Generated with" footers and AI author or committer
identities, and the verify job runs it as pnpm check:attribution. Human
co-authors are allowed, and so is prose that only mentions Claude.

The rules are tested against throw-away repositories (tests/check-attribution.test.ts,
run by pnpm test and pnpm check). The step catches what the local commit-msg
guard cannot: a commit made with --no-verify, with hooks disabled, or built
with git commit-tree.
@zuhairhalawa
zuhairhalawa merged commit fb4a7ea into main Oct 24, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant