Reject Signed-off-by trailers from AI tools in danger check

CONTRIBUTING.md states that AI agents must not add Signed-off-by
tags, since only humans can legally certify the Developer
Certificate of Origin.  Mirror the existing LLM Co-Authored-By
check against the Signed-off-by trailer line so danger fails on
commits that violate the rule.

The shared alternation of known LLM agent names is factored out
into LLM_AGENT_NAMES_RE so adding a new tool only requires one
edit.

Assisted-by: Claude:claude-opus-4-7
(cherry picked from commit 65d082277d)
This commit is contained in:
Michal Nowak 2026-05-05 19:43:35 +02:00 committed by Michal Nowak (GitLab job 7600497)
parent 9dbb8a8082
commit 760b20df01

View file

@ -156,10 +156,11 @@ if match:
PROHIBITED_WORDS_RE = re.compile(
"^(WIP|wip|DROP|drop|DROPME|checkpoint|experiment|TODO|todo)[^a-zA-Z]"
)
# `Co-Authored-By` trailers naming an AI agent are forbidden by
# CONTRIBUTING.md; contributors must use the `Assisted-by` trailer instead.
LLM_COAUTHORED_BY_RE = re.compile(
r"^Co-Authored-By:.*\b("
# Names of well-known AI coding assistants. CONTRIBUTING.md forbids
# them from being listed in `Co-Authored-By` or `Signed-off-by`
# trailers; contributors must use the `Assisted-by` trailer instead.
LLM_AGENT_NAMES_RE = (
r"\b("
r"claude|anthropic|"
r"codex|openai|chatgpt|gpt-[0-9]|"
r"mistral|"
@ -170,7 +171,14 @@ LLM_COAUTHORED_BY_RE = re.compile(
r"aider|"
r"sourcegraph|"
r"codewhisperer"
r")\b",
r")\b"
)
LLM_COAUTHORED_BY_RE = re.compile(
r"^Co-Authored-By:.*" + LLM_AGENT_NAMES_RE,
re.IGNORECASE | re.MULTILINE,
)
LLM_SIGNED_OFF_BY_RE = re.compile(
r"^Signed-off-by:.*" + LLM_AGENT_NAMES_RE,
re.IGNORECASE | re.MULTILINE,
)
COAUTHORED_BY_RE = re.compile(r"^Co-Authored-By:.*$", re.IGNORECASE | re.MULTILINE)
@ -213,6 +221,15 @@ for commit in danger.git.commits:
"human contributor. AI tools must use the `Assisted-by:` "
"trailer instead."
)
match = LLM_SIGNED_OFF_BY_RE.search(commit.message)
if match:
fail(
f"Commit {commit.sha} contains a `Signed-off-by` trailer "
f"naming an AI tool (`{match.group(1)}`). Per `CONTRIBUTING.md`, "
"only humans can legally certify the Developer Certificate of "
"Origin; AI agents must not add `Signed-off-by` tags. Use the "
"`Assisted-by:` trailer instead."
)
match = MR_TITLE_RE.match(subject)
if match and match.group(5) is not None and not is_merge:
fail(