Workflow · 2026-05-06

From git blame to AI blame: per-line provenance for AI-era code

git blame answers "who wrote this" with a name, a SHA, and a date. In an AI-assisted codebase that is necessary but no longer sufficient. Here's the upgrade, per-line lineage that includes the prompt, the model, the agent, and the test result that produced each line.

Key takeaways
  • git blame resolves a line to the human who committed it; for agent code the committer is not the author.
  • h5i recall blame --show-prompt adds the agent and test status per line, and the prompt per commit boundary.
  • Attribution is commit-granular and OID-keyed, so it is exactly as durable as the commit graph — squash and rebase collapse prompts.

Provenance, who asked, with what prompt, from what context, is a core property of an auditable workspace. Per-line AI blame is how that provenance reaches the developer who later needs to understand a line of code. git blame is one of the most-googled git commands ever shipped, and for good reason. The sequence "see suspicious line → run blame → read commit message → understand the change" is a foundational debugging primitive. It works because in a pre-AI codebase, the author's name plus the commit message is enough context, the human author knew why they wrote that line, and the commit message captures the why with reasonable fidelity.

That model breaks the moment a line was written by Claude. The git author is the human who ran git commit, but they didn't write the line, they reviewed it. The commit message summarizes the user-visible intent ("add retry logic") but loses the actual prompt, the model version, the agent identity, and the tests that ran. When you're debugging at 2am, the difference between "Alice wrote this in 2026" and "Alice prompted claude-sonnet-4-6 with 'add exponential backoff' and 42 tests passed afterwards" is the difference between an hour of guessing and a one-line confirmation.

h5i extends git's blame model with that data. Same command shape, more answers.

The two-output upgrade

Standard git blame on a line:

~/my-project
$ git blame -L 88,90 src/http_client.rs

a3f9c2b9 (Alice 2026-03-27 14:02:11 +0000  88) async fn send_with_retry(req: Request) -> Result<Response> {
a3f9c2b9 (Alice 2026-03-27 14:02:11 +0000  89)     let mut delay = Duration::from_millis(100);
a3f9c2b9 (Alice 2026-03-27 14:02:11 +0000  90)     for attempt in 0..MAX_RETRIES {

Same lines under h5i recall blame:

~/my-project
$ h5i recall blame src/http_client.rs --show-prompt

STAT COMMIT   AUTHOR/AGENT    | CONTENT
                              prompt: "add exponential backoff with jitter to the HTTP cli…"
 a3f9c2b9 AI:claude-code  | async fn send_with_retry(req: Request) -> Result<Response> {
 a3f9c2b9 AI:claude-code  |     let mut delay = Duration::from_millis(100);
 a3f9c2b9 AI:claude-code  |     for attempt in 0..MAX_RETRIES {

The first column is the test status carried by the commit that owns each line ( passing, failing, blank when no test metrics were captured). The AUTHOR/AGENT column reads AI:<agent> for an AI-tagged commit and Human otherwise — so a quick scan tells you which spans of a file are human-written. With --show-prompt, the human prompt is printed once per commit boundary (not once per line), keyed by the line's owning commit, so it stays readable on a long function. That's the difference between knowing that Alice landed the change and knowing what she actually asked for.

Two honest caveats on this view. The AI:<agent> label is the agent identity (claude-code, codex, …), not the model — the model name, token count, and integrity severity ride along in the commit's provenance record (next section) and surface in h5i recall log rather than in the per-line blame. And attribution is commit-granular: blame resolves a line to its owning commit, and provenance hangs off that commit. If you batch ten prompts into one commit, all ten lines share that commit's single recorded prompt. Per-prompt resolution is exactly as fine as your commit boundaries.

Where the data lives: a git note per commit

The provenance fields are stored in refs/h5i/notes as JSON keyed by commit OID — a git note, not a parallel database, so it travels with the object graph and can never drift out of sync with the commit it describes. Each AI-tagged commit has an H5iCommitRecord attached:

refs/h5i/notes
{
  "git_oid": "a3f9c2b9...",
  "parent_oid": "7216039...",
  "ai_metadata": {
    "model_name": "claude-sonnet-4-6",
    "agent_id": "claude-code",
    "prompt": "add exponential backoff with jitter to the HTTP client...",
    "usage": { "prompt_tokens": 312, "total_tokens": 1180, "model": "claude-sonnet-4-6" }
  },
  "test_metrics": {
    "tool": "pytest",
    "passed": 42, "failed": 0, "skipped": 0, "total": 42,
    "duration_secs": 1.23, "exit_code": 0
  },
  "timestamp": "2026-04-12T14:02:11Z"
}

Integrity is the one field that is not stored here: rather than freezing a possibly-stale verdict, h5i recomputes it on demand by re-running its rule set against the commit's own parent→commit diff (verify_commit_integrity), grading each commit Valid / Warning / Violation with the specific rule findings (credential leak, code execution, sensitive-file change, …). That triaged view is what h5i audit review surfaces across many commits.

The notes ref isn't pulled by a plain git fetch — it would clutter remotes that don't care, and most CI never asks for it. h5i share push and h5i share pull sync the h5i refs alongside your code, so teammates who opt in see the full provenance and everyone else sees ordinary git behavior. Provenance is additive, never load-bearing for the build.

How h5i capture commit populates the record

You can capture provenance manually:

~/my-project
$ h5i capture commit -m "add retry logic to HTTP client" \
    --model claude-sonnet-4-6 \
    --agent claude-code \
    --intent "add exponential backoff to the HTTP client" \
    --tests \
    --audit

  Committed a3f8c12  add retry logic to HTTP client
    model: claude-sonnet-4-6 · agent: claude-code · 312 tokens

In practice you rarely type --intent at all. Installing the hooks with h5i hook setup wires a UserPromptSubmit hook that captures the verbatim prompt you send to Claude Code and attaches it to the next commit — and the auto-captured prompt wins over an agent-supplied --intent, so what gets recorded is what the human actually asked, not the agent's paraphrase. (--intent stays as the fallback for Codex, CI, or manual commits where no prompt-capture hook is running; Codex instead mines its own session transcript.) The --tests flag attaches the captured test metrics and --audit runs the integrity rules before the commit lands.

The new question: per-line prompt history

Standard blame stops at the line's introducing commit. h5i recall log --ancestry <file>:<line> walks backward from HEAD, re-blaming the file at each step and following the line as it moves through edits, so it can show every prompt that ever shaped that line — not just the last one:

~/my-project
$ h5i recall log --ancestry src/http_client.rs:89

── Prompt ancestry for src/http_client.rs:89

  [3 of 3] a3f9c2b9  Alice · 2026-04-12 14:02 UTC
       line:  let mut delay = Duration::from_millis(100);
       prompt:  "add exponential backoff with jitter to the HTTP client"

  [2 of 3] 72160394  Alice · 2026-03-08 09:14 UTC
       line:  let delay = Duration::from_millis(100);
       prompt:  "fix off-by-one in retry counter"

  [1 of 3] 9eff0012  Alice · 2026-02-24 11:30 UTC
       line:  let delay = 100;
       prompt:  (none recorded) (Human)

Entries print most-recent first ([3 of 3] down to [1 of 3]), each with the line's exact content at that point in history. This answers "why is this line the way it is?" across its whole life, not just at the most recent edit. When a regression bisects to a refactor, the ancestry view tells you whether the line you're staring at was genuinely rewritten or merely moved — and which prompt did the moving. The walk is bounded (it stops at the commit that introduced the line, or after a depth cap) and gracefully interleaves untagged historical commits, which simply read (none recorded).

How it compares to what you already have

Provenance for AI code isn't a brand-new idea — there are two conventions you've probably already reached for. Both help; both have a ceiling.

Questiongit blameCommit trailers
(Co-authored-by, Assisted-by)
h5i AI blame
Which commit owns this line?YesNo (commit-level only)Yes (same git algorithm)
Who committed it / when?YesYesYes
Which agent produced it?NoSometimes (free-text, unenforced)Yes (AI:<agent> per line)
Which model / token cost?NoNoYes (in the record)
What prompt produced it?NoNoYes (--show-prompt)
Did its tests pass?NoNoYes (--tests)
Full prompt history of one line?NoNoYes (--ancestry)
Survives git push with no setup?YesYes (it's in the message)No — needs h5i share push

Good, about trailers: a Co-authored-by: Claude <…> trailer is zero-infrastructure, travels in the commit message, and is enough to answer "was a model involved at all?" — which is sometimes all you need. Gap: it is commit-granular free text. It can't tell you which of the 14 files in the commit the model touched, carries no prompt or test outcome, and nothing validates it — an agent that forgets the trailer leaves no trace. AI blame trades that zero-setup portability (the notes ref needs an explicit h5i share push) for structured, line-resolved, queryable provenance.

Failure modes worth knowing before you rely on it

AI blame inherits git blame's mechanics, so it inherits git blame's blind spots — plus a couple of its own. Knowing them up front keeps you from over-trusting a clean-looking annotation.

Practical usage patterns

Three concrete situations where AI blame earns its keep:

1. Triaging an incident

An error is firing in parse_response. h5i recall blame on the function shows it was authored by claude-sonnet-4-6 with the prompt "handle the new v2 envelope format" two weeks ago. The original v1 parser was untouched. Fastest path to root cause: check whether the error payload is v1 (the parser doesn't handle it) or v2 (the parser is buggy). Without provenance, you'd have read both code paths first.

2. Reviewing your own past decisions

Six months later you don't remember why retry_max is 5. h5i recall blame shows the prompt was "…cap retries at 5 to avoid infinite loops". Decision recovered without re-reading the PR thread, the design doc, or your Slack history. Particularly valuable for solo developers whose "team handoff" is to themselves three months later.

3. Vetting an inherited codebase

You're given a service to take over. h5i recall log --limit 200 tells you the AI ratio (60% AI? 20%?), which agents wrote which subsystems, and what kinds of prompts were used. Combined with h5i audit review, you get a triaged list of "files most likely to harbor unreviewed AI assumptions." A 30-minute orientation that previously took days.

Bonus. h5i audit vibe is the explicit command for the inherited-repo case. It scans recent commits and prints the AI footprint (what fraction carries AI metadata), which models appear, the directories with the highest AI concentration, and the riskiest files (high AI ratio, no tests, blind edits). For credential leaks and prompt-injection hits, reach for the integrity rules via h5i audit scan / h5i audit review — that's a different lens on the same provenance.

What this is not trying to be

AI blame is not a copyright assertion. The git author is still legally and organizationally the one accountable for the commit, they ran the prompt, reviewed the diff, and merged. The AI fields are provenance, not attribution. They tell you what tools shaped the line, the same way git log tells you what compiler version the CI ran. Useful for debugging, auditing, and triage; not for ownership.

It's also not a substitute for code review. The fact that claude-sonnet-4-6 wrote a line doesn't make it correct or incorrect. It just makes the next reviewer faster.

Adopt incrementally

h5i blame works the moment you start using h5i capture commit. Pre-existing commits keep their standard git blame; new commits gain the AI fields. There's no migration, no backfill, no one-time event. The provenance accrues on the commits you make from here forward, and the ancestry view interleaves it gracefully with the historical commits you've never tagged.

getting started
$ curl -fsSL https://raw.githubusercontent.com/h5i-dev/h5i/main/install.sh | sh
$ cd your-project && h5i init
$ h5i hook setup  # auto-captures prompts via UserPromptSubmit

# From your next commit forward:
$ h5i capture commit -m "..." --model claude-sonnet-4-6 --agent claude-code --tests
                              # prompt auto-attached by the hook
$ h5i recall blame src/foo.rs --show-prompt

FAQ

What is the difference between git blame and AI blame?

git blame maps each line to the commit that last changed it and shows the committer's name, SHA, and date. For agent-written code the committer is the human who ran git commit, not the model that wrote the line. AI blame keeps the same line-to-commit mapping and adds, per commit boundary, the coding agent, the human prompt, and the captured test status; the model name, token count, and integrity grade live in the commit's provenance record.

Does git blame show which AI model wrote a line?

No. A git commit object only records author and committer identity — there is no field for the model, the agent, or the prompt. That has to be captured at commit time. h5i stores it in a git note on refs/h5i/notes keyed by commit OID.

Where does h5i store per-line AI provenance?

In a git note attached to each commit on refs/h5i/notes, serialized as JSON (an H5iCommitRecord with ai_metadata, test_metrics, and a timestamp). It is not carried by a plain git push or git fetch; use h5i share push / h5i share pull to sync it alongside your code.

What happens to AI blame when commits are squashed or rebased?

Provenance is keyed by commit OID, so rewriting history changes the OID and the original notes stop matching. A squash collapses several prompts into one new commit, and blame then attributes every squashed line to that commit's single record. AI blame is exactly as durable as the commit graph it annotates — preserve commit boundaries if you want per-prompt resolution.

How do I see the prompt that produced a line?

Run h5i recall blame <file> --show-prompt to annotate each commit boundary with its prompt, agent, and test status, or h5i recall log --ancestry <file>:<line> to walk the full prompt history of one line across every commit that touched it.

The bottom line

git blame was designed around an assumption that quietly stopped holding: that the person who committed a line is the person who decided what it should say. For agent-written code those are two different actors, and the gap between them — the model, the prompt, the test outcome — is precisely the context a debugger needs at 2am. None of it is recoverable after the fact from a git commit alone; it only exists if something captured it at commit time.

That's the whole proposition, and also its honest limit. AI blame is not attribution, not a copyright claim, and not a substitute for review — it makes the next reader faster, nothing more. It resolves no finer than your commit boundaries, and it survives only as well as you preserve those boundaries. But within those limits it turns "who touched this line" into "which model, from what prompt, with what test result" — and on a codebase where most lines were written by an agent, that is the question actually worth asking.

Per-line provenance for the AI era

Try h5i on your next AI-assisted branch: create a sandboxed workspace, capture the run, and post a review-ready PR brief.

Star on GitHub Back to docs