ops: add CI lint for Co-Authored-By/Claude/Anthropic trailers
Three builder runs tonight carried a Co-Authored-By: Claude trailer despite bold instructions forbidding it. A rule in a prompt is not a control — this gate is. Flags commits in a PR's range carrying co-authored-by + (claude OR anthropic OR noreply@anthropic.com), case-insensitive, covering all observed spellings. Wired into CI (new attribution-trailer job) and pre-push hook. Mirrors existing lint scripts in structure and exit conventions. Pattern tested against all three variants plus a normal commit.
This commit is contained in:
parent
5a9118c945
commit
bf8e309b19
3 changed files with 108 additions and 0 deletions
|
|
@ -68,6 +68,23 @@ jobs:
|
|||
# their own. See scripts/check-doc-refs.sh.
|
||||
run: sh scripts/check-doc-refs.sh
|
||||
|
||||
attribution-trailers:
|
||||
name: attribution-trailer lint
|
||||
runs-on: [hive-ci]
|
||||
# Pure git — seconds.
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: lint
|
||||
# Flags Co-Authored-By trailers with Claude/Anthropic attribution
|
||||
# in PR commits (hive convention in /knowledge/hive-rules.md forbids
|
||||
# them — an attribution line in a prompt is not a control). Own
|
||||
# job, and IS a required check on the forge (branch protection) — a
|
||||
# hit blocks merge. See scripts/check-attribution-trailers.sh.
|
||||
run: sh scripts/check-attribution-trailers.sh
|
||||
|
||||
prose-lint:
|
||||
name: prose lint (vale)
|
||||
runs-on: [hive-ci]
|
||||
|
|
|
|||
85
scripts/check-attribution-trailers.sh
Executable file
85
scripts/check-attribution-trailers.sh
Executable file
|
|
@ -0,0 +1,85 @@
|
|||
#!/bin/sh
|
||||
# CI lint: flags Co-Authored-By trailers with Claude/Anthropic attribution in
|
||||
# PR commits. The hive convention in /knowledge/hive-rules.md forbids these
|
||||
# trailers — an attribution line in a prompt is not a control. This gate is.
|
||||
#
|
||||
# Emits a CI error annotation per offending commit and exits 1 if any commit
|
||||
# in the PR's range carries a prohibited trailer. Runs as its own CI job and
|
||||
# IS a required check on the forge (branch protection) — a hit blocks merge.
|
||||
#
|
||||
# Pattern: case-insensitive `co-authored-by` followed by `claude`, `anthropic`,
|
||||
# or `noreply@anthropic.com` anywhere in the line. Covers variants seen in the
|
||||
# wild: `Co-Authored-By: Claude`, `Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>`.
|
||||
#
|
||||
# Scope: commits in the PR's range (BASE..HEAD), not the entire history — old
|
||||
# commits must not fail the build. In CI, BASE is the target branch
|
||||
# (GITHUB_BASE_REF / github.event.pull_request.base.ref in GH Actions; similar
|
||||
# vars in Forgejo). Falls back to origin/main for local runs.
|
||||
set -eu
|
||||
|
||||
# Determine base ref: in CI (pull_request event), use the PR's base branch;
|
||||
# locally, default to origin/main. CI env vars: GITHUB_BASE_REF (GitHub
|
||||
# Actions), similar for Forgejo/Gitea (not standardized across runners, so we
|
||||
# check both common patterns).
|
||||
if [ -n "${GITHUB_BASE_REF:-}" ]; then
|
||||
# GitHub Actions / Forgejo Actions pull_request context
|
||||
base="origin/${GITHUB_BASE_REF}"
|
||||
elif [ -n "${CI_COMMIT_REF_NAME:-}" ]; then
|
||||
# GitLab-style variable (some Forgejo setups)
|
||||
base="origin/main"
|
||||
else
|
||||
# Local run or unknown CI — default to origin/main
|
||||
base="origin/main"
|
||||
fi
|
||||
|
||||
# Ensure the base ref exists (in CI, actions/checkout@v3 fetches the PR head
|
||||
# but may not fetch the base branch by default — fetch it if missing).
|
||||
if ! git rev-parse --verify "$base" >/dev/null 2>&1; then
|
||||
# Extract branch name and fetch it
|
||||
branch="${base#origin/}"
|
||||
git fetch origin "$branch" 2>/dev/null || true
|
||||
# If still missing, fall back to main
|
||||
if ! git rev-parse --verify "$base" >/dev/null 2>&1; then
|
||||
base="origin/main"
|
||||
git fetch origin main 2>/dev/null || base="HEAD~10"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Get all commits in the PR's range. `--reverse` lists oldest first (clearer
|
||||
# for annotation output). `--format=%H` gets the full commit sha.
|
||||
commits="$(git log --reverse --format='%H' "${base}..HEAD" 2>/dev/null || true)"
|
||||
|
||||
if [ -z "$commits" ]; then
|
||||
# No commits in range — either not on a PR branch, or nothing new. Pass.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check each commit's full message for the prohibited trailer pattern.
|
||||
# Case-insensitive grep for `co-authored-by` + any of `claude`, `anthropic`,
|
||||
# `noreply@anthropic.com`.
|
||||
hits=""
|
||||
for sha in $commits; do
|
||||
msg="$(git log -1 --format='%B' "$sha")"
|
||||
# Match case-insensitive co-authored-by AND (claude OR anthropic OR noreply@anthropic.com)
|
||||
# Use grep -E with -i for case-insensitive matching
|
||||
if printf '%s\n' "$msg" | grep -qiE 'co-authored-by.*claude|co-authored-by.*anthropic|co-authored-by.*noreply@anthropic\.com'; then
|
||||
# Found a hit — build annotation
|
||||
subject="$(git log -1 --format='%s' "$sha")"
|
||||
hits="${hits}${sha}|${subject}
|
||||
"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$hits" ]; then
|
||||
printf '%s' "$hits" | while IFS='|' read -r sha subject; do
|
||||
[ -z "$sha" ] && continue
|
||||
short_sha="$(printf '%s' "$sha" | cut -c1-8)"
|
||||
printf '::error title=Attribution trailer found in %s::%s — commit carries a Co-Authored-By trailer with Claude/Anthropic attribution, forbidden by /knowledge/hive-rules.md\n' \
|
||||
"$short_sha" "$subject"
|
||||
done
|
||||
count="$(printf '%s' "$hits" | grep -c '|' || true)"
|
||||
printf 'check-attribution-trailers: %s commit(s) with prohibited trailers found\n' "$count" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
|
@ -34,5 +34,11 @@ if ! sh "$repo_root/scripts/check-doc-refs.sh"; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
echo "pre-push: running attribution-trailer lint..." >&2
|
||||
if ! sh "$repo_root/scripts/check-attribution-trailers.sh"; then
|
||||
echo "pre-push: attribution-trailer lint FAILED — fix before pushing" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "pre-push: lints passed ✓" >&2
|
||||
exit 0
|
||||
|
|
|
|||
Loading…
Reference in a new issue