Nine Months of Nagging, Zero Reading
The Jar Nobody Counted
Nine months of commits, every single one carrying a footer that states how much of it I actually wrote, and I could not have told you the number-not roughly or even within twenty points. It was all sitting in git log, structured, and enforced on every commit by a hook I built specifically for that purpose. But it was completely inert. Dropping change in a jar isn't the same as knowing how much money is in it.
rai-lint will block your commit until you write the footer, but then it's done-the pile just sits there, and I never built the thing that adds it up.
anchildress1 / rai-lint
Dual-language linter for Responsible AI commit footers - shared logic for Node (commitlint) and Python (gitlint). Stop playing hide-and-seek with AI in your commits. A dual-language validation framework that makes AI attribution non-negotiable.
๐ Project Stats
๐ฃ๏ธ Languages
๐ฆ Packages
๐ค AI & Automation
๐ง Quality & Standards
Installation ยท Quick Start ยท Required Commit Footers ยท Documentation
What is this? ๐ค RAI Lint enforces Responsible AI (RAI) attribution in every commit. No more "who wrote this?" moments. No more mystery code. Just honest, trackable AI contributions.
Read the full story: Did AI Erase Attribution? Your Git History Is Missing a Co-Author
%%{init: {'theme':'dark'}}%%
flowchart LR
A[Developer Commits] --> B{Has AI Footer?}
B -->|Yes| C[Commit Accepted โ
]
B -->|No| D[Commit Rejected โ]
C --> E[Clear AI Attribution]
D --> F[Add Footer & Retry]
Why does this exist? Because transparency matters. When AI writes code, everyone should know. This isn't about fear or compliance theater-it's about building trust and maintaining clear audit trails.
Features ๐ฏ
๐ โฆ
View on GitHub
The footers aren't some format I made up, either. They're Git trailers-the same key-value convention Signed-off-by and Co-authored-by have used for years, which is exactly why git log can hand them back without anything having to parse prose.
That was my first idea: encode attribution in a shape Git already understands, so something else can read it later. Turns out everyone had roughly the same idea. The Linux kernel codified Assisted-by: AGENT_NAME:MODEL_VERSION, Fedora and LLVM recommend the same trailer in their AI contribution policies, and Artsy argued the whole thing out in public in an RFC titled "disclose LLM usage in commits or PRs", opened in May 2026 and merged in June. Three projects that don't talk to each other landed on the same trailer.
So rai-lint isn't a special private dialect-it enforces vocabulary the rest of the industry already uses, and it fails the commit if yours is missing, which is more than a policy document can do.
Now rai-commit-badge reads them back. It weights every footer in the history by the lines that commit actually changed, then writes the result into a README as a Shields.io badge.
anchildress1 / rai-commit-badge
Scores RAI attribution footers in your git history and publishes a shields.io badge. Companion to rai-lint.
rai-commit-badge
Your git history already knows how much AI wrote. This reads it back. A GitHub Action that scores RAI attribution footers and publishes a shields.io badge.
๐ Project Stats
๐ฆ Marketplace
That badge is this action, scoring itself.
๐ฃ๏ธ Languages
๐ค AI & Automation
๐ง Quality & Standards
Getting Started ยท Configuration ยท About ยท Score ยท Related
Getting Started ๐
Mark where the badge belongs in your
README.md:<!-- START_SECTION:rai-badge --> <!-- END_SECTION:rai-badge -->Tip If prettier formats your README, wrap the pair in
<!-- prettier-ignore-start -->and<!-- prettier-ignore-end -->. Prettier adds a blank line after the start marker, which the action then rewrites on the next run - the fences keep the block byte-stable.Add the workflow:
name: RAI Attribution on: push: branches: [main] concurrency: group: ${{ github.workflow }}-${{ github.ref }} jobs: score: runs-on: ...
The color moves with the number-blue at 33% or less, purple through 66%, and magenta once you clear 67%-so the band reads before the numbers do.
๐ก ProTip: Purple is where rai-lint landed-66% across 158 commits going back to October. The badge's own repo, rai-commit-badge, sits in the pink band at 88%, which is what two days of mostly-Claude output looks like next to nine months of my own typing.
Not Every Footer Weighs the Same โ๏ธ
Assisted-by and Generated-by are not the same confession, and a scorer that flattens them into one bit-"AI touched this, yes or no"-throws away the only interesting thing the convention captured. So every footer is worth something different, the same way a handful of change would be:
| Footer | Declares | Weight |
|---|---|---|
Authored-by |
Zero AI | 0.00 |
Commit-generated-by |
Trivial AI, no code | 0.05 |
Assisted-by |
AI helped, human led | 0.25 |
Co-authored-by |
Roughly 50/50 | 0.50 |
Generated-by |
Majority AI | 0.90 |
Keeping Co-authored-by in that scale is a call some of the newer policies would argue with, but I made it anyway: it's already widely accepted. Nothing I do changes that and dropping it would mean I'm scoring a vocabulary nobody actually uses. The cost is real though-the scorer has to know which co-authors are AI, and that list gets unmanageable eventually-but until there's a better system, this one works just fine.
Counting commits tells you nothing, since one commit is a typo fix and the next is a new module, so each commit gets weighted by the lines it actually changed. Lockfiles, dependency trees, build output, and minified assets don't count toward that total-a regenerated lockfile says nothing about who wrote the feature.
The ceiling is 0.90 and it doesn't move. Even on a commit where the model wrote every line, a human still chose to build the thing and at least directed it-even if that human reviews code the way I do. Ninety percent is the highest this system will reasonably attribute to AI, so nothing here ever reads 100%.
๐ก ProTip: 0.05 covers trivial contributions, an AI-generated commit message being the obvious one. I didn't put it at zero because it isn't the same as writing your own.
Since When, Exactly ๐ช
A bare percentage doesn't tell you much. 66% of what-everything since the repo was created, back when there were no footers at all and every commit scores as human? That version of the number only ever gets smaller, and it says more about how old the repo is than about how I work now.
So the window opens at the earliest RAI footer, auto-detected, and the date sits right there in the badge: 66% since 2025-10. In practice that reads as "since we adopted this," which is what it actually means.
I stopped at month precision on purpose, because since 2025-10-31 makes people ask what happened on Halloween, and the answer is nothing-that's just the day the first footer went in. A day-precise date claims more accuracy than the input has, and three extra characters on an already-wide badge aren't worth it.
You don't lose the detail: the workflow job summary prints the exact window start next to the commit counts. That table is also where the rest of the story shows up. Of those 158 commits, 114 carry a footer and 44 don't, and every one of those 44 scores as human. Twenty-eight are squashed, so their footer weights got averaged, which the summary says out loud instead of folding it quietly into the number.
Inside that window, a commit with no footer at all counts as human, at weight zero. That's a hole and I know it's a hole-skip the footer and the number drops, and nothing in git distinguishes a commit a human wrote from a commit someone didn't feel like labeling. Erring toward human is still the right default. This tool exists to attribute AI where AI is known, and if I started guessing at the commits I can't account for, the badge would stop being a measurement.
I'm not the only one stuck there. An IETF Internet-Draft, Identity-Attributed Git Commits via Tier-Structured Trailers, goes considerably further than a footer and a hook, splitting attribution across three tiers:
| Trailer | Tier | Can sign |
|---|---|---|
Acted-By: |
Human | Yes |
Executed-By: |
Bot | Yes, scoped |
Drafted-With: |
AI instrument | No keys, ever |
An Ed25519 signature over the commit's tree hash rather than its commit hash keeps that attribution intact through rebase and squash, which is genuinely more than I built. Section 11.7 still lands where I did: it names negative-attribution risk and concludes the protocol layer can't close it. The draft gives honest committers a way to say so and stops there.
๐ฆ Before anyone cites that at me as settled: it's an individual submission, it has no formal standing in the IETF process, and it expires in November 2026. I'm pointing at it because someone else mapped the same territory and reached the same dead end.
One PR, Open Until You Merge It ๐ช
You should find out your attribution number changed by reading a diff, not by noticing a commit you didn't make. So nothing here touches your default branch: every run cuts rai-badge--branches--<base>, force-pushes the rewritten README onto it, and opens a pull request, reusing the one already open if there is one. The branch gets rebuilt from base each time, so that PR holds exactly one commit no matter how many times you push.
Setup is two markers and a workflow. The markers go wherever the badge belongs:
<!--START_SECTION:rai-badge-->
<!--END_SECTION:rai-badge-->
If prettier formats your README, wrap the pair in <!-- prettier-ignore-start --> and <!-- prettier-ignore-end -->, since prettier adds a blank line after the start marker that the action rewrites on the next run. The fences keep the block byte-stable.
Then the workflow:
name: RAI Attribution
on:
push:
branches: [ main ]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
score:
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: anchildress1/rai-commit-badge@v1
fetch-depth: 0 is required, because the default checkout is shallow and a shallow clone doesn't carry the history the score is made of-the run fails rather than publish a number derived from four commits. Over in Settings โ Actions โ General โ Workflow permissions, Actions also needs permission to create and approve pull requests, or you get a clean score and a 403 where the PR should be.
๐ฆ Check it out on the Marketplace at https://github.com/marketplace/actions/rai-commit-attribution-badge and leave a star if you found it useful.
The Number Was Always There ๐ช
Every commit in that history already stated how much of it was mine. All I added was arithmetic. rai-lint enforces the footer at commit time. rai-commit-badge scores what it collected.
If you've been writing RAI footers, your number already exists whether or not you go looking for it. Mine is 66% across nine months, and I'd rather publish that than pretend I don't know.
Ashley Childress
Follow
Distributed backend specialist. Perfectly happy playing second fiddle-it means I get to chase fun ideas, dodge meetings, and break things no one told me to touch, all without anyone questioning it. ๐
๐ก๏ธ The Jar Counted Itself
Claude wrote most of the scorer, then wrote this footer about the scorer, which under my own weights lands at Generated-by - 0.90, the heaviest coin in the jar. I counted the change. It did the multiplication.
Comments
No comments yet. Start the discussion.