chore(github): reject AI co-authored commits from untrusted PRs
This commit is contained in:
parent
fe5c59a53a
commit
7ad9e5ed23
2 changed files with 44 additions and 2 deletions
43
.github/workflows/pr-triage.yml
vendored
43
.github/workflows/pr-triage.yml
vendored
|
|
@ -1,12 +1,16 @@
|
|||
# Auto-triage for unattended AI-generated pull requests.
|
||||
#
|
||||
# Two tiers, both applying ONLY to authors with no history here (everyone but
|
||||
# Three tiers, all applying ONLY to authors with no history here (everyone but
|
||||
# OWNER / MEMBER / COLLABORATOR / CONTRIBUTOR):
|
||||
#
|
||||
# 1. HARD: the title carries an unattended-agent fingerprint — an agent's name, a
|
||||
# 12+ digit session id, or a bare commit-hash — the PR is closed on sight,
|
||||
# labelled `ai-slop`, and pointed at CONTRIBUTING.md.
|
||||
# 2. SOFT: the PR template's personal-review checkbox is not ticked — the PR stays
|
||||
# 2. HARD: any commit in the PR carries an AI co-author trailer ("Co-Authored-By:
|
||||
# Claude/Copilot/…"), a "Generated with …" marker, or is authored by a known
|
||||
# coding-agent bot account. A leftover trailer is the tell that nobody proofread
|
||||
# the output before shipping it. Closed with instructions to squash and reopen.
|
||||
# 3. SOFT: the PR template's personal-review checkbox is not ticked — the PR stays
|
||||
# OPEN, gets `needs-template` and one comment asking to complete it. Real
|
||||
# first-time contributors get a nudge, not a door in the face.
|
||||
#
|
||||
|
|
@ -60,6 +64,41 @@ jobs:
|
|||
return;
|
||||
}
|
||||
|
||||
// AI marks inside the COMMITS. Named agents only — a plain `[bot]` match would
|
||||
// also shoot dependabot/renovate, which are welcome here. "gpt" alone is left
|
||||
// out on purpose (it substring-matches human names); chatgpt/codex cover it.
|
||||
const aiNames = '(claude|copilot|chatgpt|openai|gemini|jules|devin|cursor|windsurf|aider|codex|sweep|openhands|codegen|anthropic)';
|
||||
const aiCoauthor = new RegExp('co-authored-by:[^\\n]*\\b' + aiNames + '\\b', 'i');
|
||||
const generatedWith = new RegExp('generated (with|by)[^\\n]*\\b' + aiNames + '\\b', 'i');
|
||||
const agentAccount = new RegExp('\\b' + aiNames + '[^\\s]*\\[bot\\]', 'i');
|
||||
|
||||
const commits = await github.paginate(github.rest.pulls.listCommits, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: pr.number,
|
||||
per_page: 100,
|
||||
});
|
||||
const marked = commits.find((c) => {
|
||||
const message = c.commit?.message || '';
|
||||
const identities = [
|
||||
c.commit?.author?.name, c.commit?.author?.email,
|
||||
c.commit?.committer?.name, c.commit?.committer?.email,
|
||||
c.author?.login, c.committer?.login,
|
||||
].filter(Boolean).join(' ');
|
||||
return aiCoauthor.test(message) || generatedWith.test(message) || agentAccount.test(identities);
|
||||
});
|
||||
if (marked) {
|
||||
await github.rest.issues.createComment({ ...issue, body:
|
||||
'One or more commits in this PR carry an AI co-author trailer or agent marker ' +
|
||||
'(e.g. `Co-Authored-By: Claude/Copilot/…`), which this project does not accept — ' +
|
||||
'a leftover trailer usually means the output was never personally reviewed. If a ' +
|
||||
'human does stand behind this change: squash your commits without the AI trailers, ' +
|
||||
'force-push, complete the PR template, and reopen. See CONTRIBUTING.md.' });
|
||||
await github.rest.issues.addLabels({ ...issue, labels: ['ai-slop'] });
|
||||
await github.rest.issues.update({ ...issue, state: 'closed' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Must match the checkbox text in .github/PULL_REQUEST_TEMPLATE.md verbatim.
|
||||
const reviewedByHuman = /-\s*\[x\]\s*I wrote or personally reviewed every line/i.test(body);
|
||||
if (!reviewedByHuman) {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ AI-assisted contributions are welcome. AI-*driven* ones are not:
|
|||
result — are **closed automatically** by the triage workflow, on sight, without
|
||||
individual discussion. Their fingerprints (agent names, session ids, or bare commit
|
||||
hashes in the title) are matched by `.github/workflows/pr-triage.yml`.
|
||||
- **Commits carrying AI co-author trailers** (`Co-Authored-By: Claude/Copilot/…`) or
|
||||
"Generated with …" markers are rejected the same way — a leftover trailer is the tell
|
||||
that nobody proofread the output. Squash them out before opening the PR.
|
||||
- Repeat offenders get blocked from the repository.
|
||||
|
||||
This is not hostility toward AI tooling — half this project is built with it. It is the
|
||||
|
|
|
|||
Loading…
Reference in a new issue