# How can a team enforce an Assisted-by trailer on commits?

Source: https://forum.codelifter.com/posts/how-can-a-team-enforce-an-assisted-by-trailer-on-commits/
Site: CodeLifter Forum, category AI code policies
Published: 2026-10-16. Updated: 2026-10-16. Replies: 0.

## Question by CodeLifter editors (staff), 2026-10-16

Check for the trailer in two places: a commit-msg hook that stops the commit on the developer's machine, and a CI job that checks every commit in a pull request and blocks the merge. The hook is a convenience that can be skipped; the CI check is the rule. Neither can tell whether the line is true.

## Decide what the rule actually says

A check can only test for text, so write the rule first. Three decisions matter:

1. **Which key.** The Linux kernel asks AI-assisted contributions to [include an Assisted-by tag](https://docs.kernel.org/process/coding-assistants.html). Mesa uses Assisted-by, keeps Generated-by for commits where almost all the code was generated, and [reserves Co-authored-by for human co-authors](https://docs.mesa3d.org/submittingpatches.html). The Apache Software Foundation recommends a `Generated-by:` token. Pick one key and write it down.
2. **Every commit or only assisted ones.** A check cannot detect assisted commits that leave the trailer out. If you want enforcement, the workable form is to require the trailer on every commit, with an explicit value such as `Assisted-by: none` when no tool helped. That `none` value is a team convention, not something the kernel or Mesa specify; projects that follow those documents only add the trailer when a tool was used.
3. **What the value must contain.** The kernel's guidance and [LLVM's policy](https://llvm.org/docs/AIToolPolicy.html) both keep the human contributor responsible for the change, and LLVM gives an Assisted-by line as its example of labelling tool-generated content. A free-text value naming the tool is enough for most teams; a team that wants to count usage can require a fixed list of tool names.

The policy tracker at [CodeLifter's AI-code policy tracker](https://codelifter.com/provenance/ai-code-policy-tracker/) lists which projects ask for which label, if you want to align with an upstream you contribute to.

## How Git reads a trailer

The [git-interpret-trailers manual](https://git-scm.com/docs/git-interpret-trailers) defines a trailer as a key-value pair with a colon as the separator, in a block at the end of the message that must be preceded by a blank line. Its `--parse` option prints only the trailers it finds. That is the part to check, rather than searching the whole message, because a body line that happens to start with `Assisted-by:` in the middle of a paragraph is not a trailer.

A valid message looks like this:

```
feat: add keyboard support to the slideshow

Arrow keys move between slides; focus stays on the controls.

Assisted-by: code assistant (tests written by hand)
```

To add the line to a message file from a script, the same manual documents `git interpret-trailers --trailer "Assisted-by: none" --in-place msg.txt`.

## Step 1: a commit-msg hook on each machine

The [githooks documentation](https://git-scm.com/docs/githooks) says the commit-msg hook receives one parameter, the file holding the proposed message, and that a non-zero exit aborts the commit. Save this as `.githooks/commit-msg` in the repository and make it executable with `chmod +x .githooks/commit-msg`:

```sh
#!/bin/sh
# Reject the commit if the message has no Assisted-by trailer.
if ! git interpret-trailers --parse "$1" | grep -qi '^Assisted-by: '; then
  echo "commit-msg: add an Assisted-by: trailer (Assisted-by: none if no tool helped)" >&2
  exit 1
fi
```

Each developer then runs `git config core.hooksPath .githooks` once in their clone. By default hooks live in `$GIT_DIR/hooks`, and `core.hooksPath` is the documented way to point Git somewhere else, which lets you keep the hook under version control.

Two limits, both in the same documentation: the hook only runs where someone has set it up, and commit-msg can be bypassed with `--no-verify`. Commits created through a hosting service's API do not pass through anyone's local Git, so local hooks never see them, which matters if an agent commits through an MCP server; see [who is the author of a commit made through an MCP server](/posts/who-is-the-author-of-a-commit-made-through-an-mcp-server/).

## Step 2: a CI check on every pull request

This is the step that enforces the rule. Save the script as `scripts/check-trailers.sh`:

```sh
#!/bin/sh
# usage: scripts/check-trailers.sh <base> <head>
fail=0
for c in $(git rev-list --no-merges "$1..$2"); do
  if ! git log -1 --format=%B "$c" | git interpret-trailers --parse | grep -qi '^Assisted-by: '; then
    echo "missing Assisted-by trailer: $(git log -1 --format='%h %s' "$c")"
    fail=1
  fi
done
exit $fail
```

Then:

1. In your CI configuration, make sure the checkout includes the history between the target branch and the pull request. A shallow checkout of one commit does not contain the range the script walks; look for a fetch depth setting.
2. Run `sh scripts/check-trailers.sh origin/main HEAD`, replacing `main` with your target branch.
3. Make the merge depend on that job passing. The setting has a different name on each hosting service, so look for required or blocking status checks in your repository's branch settings.
4. If your repository squash-merges, the commit that lands is a new one with its own message. Check that message too, or the trailer can be lost at the last step.

## Step 3: a server hook if you host Git yourself

On a self-hosted server you can run the same loop in a pre-receive hook. The githooks documentation says pre-receive reads one line per ref on standard input, in the form `<old-oid> <new-oid> <ref-name>`, and that a non-zero exit means none of the refs are updated. Feed each old and new pair to the loop above. For a new branch the old value is all zeroes, so compare against your main branch instead.

## What enforcement cannot do

| Check | Stops a missing trailer | Can be skipped | Proves the trailer is true |
| --- | --- | --- | --- |
| commit-msg hook | On machines where it is set up | Yes, with `--no-verify` | No |
| CI check on pull requests | Yes, if the merge requires it | By whoever can change the merge rules | No |
| pre-receive hook (self-hosted) | Yes, at push time | Only by the server admin | No |

Every row ends in "No" for the same reason: a trailer is a statement a person makes. The value of the check is that the statement always exists and someone signed off on it. If a reviewer later doubts it, the conversation is about the code and the person, as covered in [a detector flagged code written in 2019 as AI](/posts/a-detector-flagged-code-written-in-2019-as-ai-what-now/). For how projects word the disclosure itself, see [disclosing AI help in commits](https://codelifter.com/provenance/disclosing-ai-in-commits/).

---
Cite as: CodeLifter Forum, "How can a team enforce an Assisted-by trailer on commits?", https://forum.codelifter.com/posts/how-can-a-team-enforce-an-assisted-by-trailer-on-commits/, accessed 2026-10-16.
