Skip to content

fix(attribution): reject wrapped bylines (#90) #188

fix(attribution): reject wrapped bylines (#90)

fix(attribution): reject wrapped bylines (#90) #188

name: no-ai-attribution
# CI-side mirror of .githooks/pre-commit and .githooks/commit-msg, using the
# same context-aware scanner. The local hooks only run for a clone pointed
# at them with `git config core.hooksPath .githooks`, and `git commit
# --no-verify` bypasses them. Requiring this context means that local bypass
# alone is insufficient, but the pull-request run still executes the
# candidate tree's workflow and scanner. Its preventive trust boundary is
# owner review of every change to these controls; the separate protected-path
# workflow is only an advisory signal, not a required external policy engine.
#
# No path is exempt. The scanner keeps its actor lexicon separate from its
# credit grammar, so its own implementation remains clean under its policy.
on:
pull_request:
types: [opened, synchronize, reopened, edited]
push:
branches: [main]
permissions:
contents: read
jobs:
attribution:
name: attribution
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# The commit-message half of the scan walks a range of commits, not
# just the tip, so the full history is required.
fetch-depth: 0
persist-credentials: false
- name: Diff and commit messages carry no AI attribution
# Passed through the environment rather than interpolated into the
# script body, which is the general rule for anything from the event
# payload. Both are empty on push events and unused there.
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_BODY: ${{ github.event.pull_request.body }}
run: |
set -euo pipefail
if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then
for sha in "$BASE_SHA" "$HEAD_SHA"; do
if ! printf '%s\n' "$sha" \
| grep -Eq '^[0-9a-f]{40}([0-9a-f]{24})?$'; then
echo "pull-request base/head SHA metadata is missing or malformed" >&2
exit 1
fi
done
git cat-file -e "$BASE_SHA^{commit}"
git cat-file -e "$HEAD_SHA^{commit}"
# PR metadata is mutable independently of HEAD. Rechecking edits is
# an advisory tripwire, not an immutable merge-time proof.
printf '%s\n%s\n' "$PR_TITLE" "$PR_BODY" \
| python3 -I .github/scripts/check_attribution.py \
--stdin '<pull-request-metadata>' --input-format markdown
python3 -I .github/scripts/check_attribution.py --tree "$HEAD_SHA"
python3 -I .github/scripts/check_attribution.py \
--commits "$BASE_SHA..$HEAD_SHA"
elif [ "$GITHUB_EVENT_NAME" = "push" ]; then
# Push to main: scan the whole tracked tree and the whole history,
# so a bypass that reached main is caught even if it never went
# through a pull request.
python3 -I .github/scripts/check_attribution.py --tree "$GITHUB_SHA"
python3 -I .github/scripts/check_attribution.py --commits "$GITHUB_SHA"
else
echo "unexpected attribution event: $GITHUB_EVENT_NAME" >&2
exit 1
fi
echo "clean"