Practice · 2026-05-06

Auditing AI-Generated Code at Scale: A Practical Framework

Your team merges 50 PRs a week. Thirty are AI-assisted. You don't have the bandwidth to read them all carefully, so which ones do you scrutinize? Auditing agent code is a provenance-aggregation problem: you score a handful of risk vectors across many commits from evidence captured at write time, instead of eyeballing diffs one by one.

Key takeaways
  • Auditing agent code is a provenance-aggregation problem: score risk vectors across many commits instead of eyeballing each diff.
  • h5i audit review ranks commits for triage, h5i audit compliance aggregates the vectors over a date range, and companion scans deepen a single commit.
  • The audit surfaces heuristic signals, not proofs — expect false positives, and treat it as a complement to human review.

"Treat AI-generated code like junior-engineer code" is a popular maxim and a useless one. A junior engineer's output is uniform, uniformly cautious, uniformly inexperienced. AI output isn't. The same model produces faultless boilerplate and dangerously confident hallucinations in the same PR. Review both halves with the same intensity and you waste the half of your reviewer attention that is worth most. At ten AI PRs a day, that waste compounds until careful review quietly stops happening at all.

What you actually want is asymmetry. Spend ten seconds on the trivial half. Spend twenty minutes on the dangerous half. The trick is knowing which is which before you start reading. This is the governance pillar of an auditable workspace: every prompt, decision, command, and file touch is recorded in your repo and provable after the fact, so the triage path is deterministic, with no model in the loop.

What the diff doesn't carry

You already know how to audit code: read the diff, run the scanners, check the tests. That machinery is unchanged. What's new is that the most useful audit signals for agent-written code aren't in the diff. What did the model read before editing? Where did it hedge in its own reasoning? Did it stay inside the scope you asked for? Did anything in its trace look like an injected instruction? Git records none of that — a commit is just a tree and a message.

h5i records it. It is a Git sidecar that, at commit time, captures the AI provenance — the prompt, the tool-call sequence, the reasoning trace, the test metrics — as a JSON note on refs/h5i/notes. Auditing then becomes aggregation: you compute a few deterministic signals over that captured evidence and rank commits by risk. No second model sits in the audit path, so the audit itself stays reproducible and reviewable.

Four risk vectors that matter

A practical AI-code audit can start with four recurring failure modes. Each has a deterministic signal extracted from the captured session — a tool-call comparison, a phrase lexicon, a diff-vs-prompt overlap, or a regex pass — never another model's judgment.

VectorWhat it looks likeDetector
Blind editsFile modified with no preceding ReadTool-call sequence analysis
UncertaintyHedge phrases inside thinking blocksCalibrated phrase lexicon
Scope creepEdits to files unrelated to the promptDiff-vs-prompt overlap
Prompt injectionOverride / exfiltration patterns in traceRegex over OBSERVE/THINK/ACT

Two of these vectors fold directly into the h5i audit review triage score (blind edits and scope creep), and two are companion scans you run on the commits that triage flags (the uncertainty heatmap and the prompt-injection trace scan). The date-ranged h5i audit compliance report pulls all four together per commit. We'll walk each vector, then show how they aggregate — and be precise about where each signal is strong and where it lies to you.

Vector 1: Blind edits

A blind edit is a Write or Edit call to a file with no preceding Read of the same file in the session. It's the single highest-precision indicator of a model writing from training-data memory rather than from the file's current state. In practice, blind edits are the leading cause of "the AI deleted my comment / regressed my fix / used the old API."

h5i extracts the tool-call sequence from the session log and surfaces blind edits as a single number plus a list:

~/my-project
$ h5i recall notes coverage --max-ratio 0.5

── Attention Coverage ─────────────────────────────────────
  files edited:           7
  files edited blindly:   2

  ⚠ src/billing/token.rs    2 edits  ·  0 reads  ·  100% blind
  ▲ src/api/checkout.rs     3 edits  ·  1 read   ·  67%  blind
  ✔ src/auth.rs             4 edits  ·  4 reads  ·  0%   blind

The interpretation: billing/token.rs was modified twice with the model never having looked at the file in this session. Whatever it wrote, it wrote from memory. That edit deserves human eyes regardless of how clean the diff looks.

How the signal is computed: h5i walks the recorded tool-call stream and, per file, computes a read-before-edit ratio. A file with edits but zero prior reads has a 0% ratio — a pure blind edit. h5i audit review turns this into a BLIND_EDIT trigger weighted 0.10 per blind file, capped at 0.30, so it nudges a commit up the queue without ever flagging it on its own.

Failure mode — be honest: the signal is session-scoped. If the model read the file in an earlier session, or genuinely remembered its current contents, the "blind" edit was actually safe — a false positive. Conversely, a single token Read of a 2,000-line file counts as "read" even though the model attended to almost none of it. Blind edits are a high-precision flag for the failure they name, not a proof of damage.

Vector 2: Uncertainty

Models hedge in their thinking blocks. The hedges don't appear in the chat output, by the time the model addresses you, it's converged on a confident-sounding answer, but they're recorded in the session log.

h5i scans every thinking block for a calibrated vocabulary of self-doubt phrases, each mapped to a confidence score (e.g. "not sure" → 0.25, "might break" → 0.30, "perhaps" → 0.45, "assuming" → 0.50). It groups the annotations by the file being edited at the moment of the hedge and sorts files by lowest average confidence. Files where uncertainty concentrates are the files the model itself flagged as risky.

Failure mode: this is a lexicon, not a model — it sees verbalized doubt only. A model that hallucinates with total confidence leaves no hedge to catch (recall floor, not ceiling), and a rhetorical "I'm not sure that's the cleanest name" can fire on a cosmetic decision. Read it as "the model told on itself here," not "there is a bug here."

We covered this detector in detail in Claude Code Uncertainty Heatmap for AI Code Review. The short version is: h5i recall notes uncertainty turns a session's hidden hedges into a heatmap that tells you where to start reading. It is a companion scan, not part of the audit review score — run it on the commits triage surfaces.

Vector 3: Scope creep

The user asked Claude to fix a bug in parser.rs. The PR touches eleven files. Maybe that's fine, the parser is a hub. Maybe it's not, five of those files are unrelated and the model "noticed" minor issues while it was there.

h5i records the user prompt that opened each session, then compares it against the diff. The SCOPE_EXPANSION rule fires when the prompt names a specific file but the commit touches others beyond it. It is a Warning-severity integrity finding, so it adds weight 0.20 to the h5i audit review score — enough to lift a commit into the queue, not enough to fail it alone.

Failure mode: the rule is dormant when the prompt names no file at all ("clean up the parser package") — there is no declared scope to exceed, so it stays silent. That is deliberate (no scope claim, no violation), but it means scope-creep detection rewards precise prompts and goes quiet on vague ones. You can tighten enforcement with h5i audit policy: per-path rules support require_audit (auth changes must pass --audit) and max_blind_edit_ratio, both checked at commit time.

Vector 4: Prompt injection

An agent that reads a poisoned README, scrapes a malicious doc, or follows a hostile link can have an injected instruction sitting in its reasoning trace right now. The output looks normal. The trace doesn't.

h5i audit scan runs eight deterministic regex rules over the OBSERVE/THINK/ACT trace — override_instructions, role_hijack, exfiltration_attempt, bypass_safety (High), indirect_injection_marker, hidden_command, prompt_delimiter_escape (Medium), and credential_request (Low) — and reports a 0.0–1.0 risk score (the clamped sum of per-hit severity weights) with line-level hits. We go deep on the detector design in Detecting Prompt Injection in Agent Reasoning Traces.

Failure mode: regexes match phrasings, and a paraphrased injection ("disregard the earlier guidance" instead of "ignore previous instructions") slips past until the lexicon learns it. Treat a non-zero score as a reason to read the trace, not a verdict. Like uncertainty, the injection scan is a standalone command — its hits also roll into the h5i audit compliance report, but they are not part of the audit review queue score.

The triage score: h5i audit review

h5i audit review is the funnel. For each commit it sums a set of weighted, deterministic triggers and ranks the result. The triggers are structural and provenance signals — blind edits (BLIND_EDIT), scope expansion, test regression, large diff, wide impact, AI commits with no recorded prompt (AI_NO_PROMPT), and the integrity rules from rules.rs (CREDENTIAL_LEAK, CODE_EXECUTION, SENSITIVE_FILE_MODIFIED, …). Each adds a weight; the sum is clamped to [0.0, 1.0]:

~/my-project
$ h5i audit review --limit 10 --min-score 0.5

Suggested Review Points — 3 commits flagged (scanned 50, min_score=0.50)
──────────────────────────────────────────────────────────────────
  #1  a3f8c12  score 0.80  ████████░░
       Alice · 2026-05-06 14:02 UTC
       refactor billing token refresh
       ⚠ BLIND_EDIT (2 files) · SCOPE_EXPANSION · WIDE_IMPACT

  #2  9e21b04  score 0.60  ██████░░░░
       Bob · 2026-05-05 11:45 UTC
       add retry to http client
       ▲ TEST_REGRESSION (coverage −6%) · SCOPE_EXPANSION

  #3  c1a2b3d  score 0.50  █████░░░░░
       AI_NO_PROMPT · LARGE_DIFF

Reviewer workflow: open the flagged commits first, then let the rest pass through your normal allocation. The bottom of the list isn't unreviewed — it just gets one fewer pair of eyes than the commits the score lifted up. To complete the picture on a flagged commit, run the two companion scans — h5i recall notes uncertainty and h5i audit scan — which sit outside this score by design.

On the threshold and false positives. The default --min-score is 0.5 (REVIEW_THRESHOLD in review.rs). We don't publish a true-positive rate, and you shouldn't trust one that isn't measured on your repo: precision depends entirely on your team's prompt discipline and how the rule weights line up with your codebase. Calibrate empirically — start at 0.5, look at a week of flags, then lower it to widen the net (more false positives, higher recall) or raise it to flag only the loudest commits. The score is a sort key, not a gate.

The aggregate view: h5i audit compliance

Triage answers "which commit do I read next." Governance asks a different question: "over this quarter, how much of our code was AI-written, how often did agents edit blind, and did anything injection-shaped slip in?" That is where the four vectors genuinely aggregate. h5i audit compliance walks a date range and, per commit, records the AI/human split, policy violations, blind-edit count, uncertainty count, and injection hits/risk, then rolls them into a report with an AI percentage and a policy pass rate:

~/my-project
$ h5i audit compliance --since 2026-04-01 --until 2026-06-30 \
      --format html --output q2-audit.html
  scanned 612 commits · 388 AI · 224 human
  policy pass rate: 96.4%  ·  injection hits: 0
  wrote q2-audit.html

The HTML form is the artifact you hand an auditor or attach to a release; --format json feeds a dashboard, and the default text form is for the terminal. Because every number derives from evidence captured in refs/h5i/notes at commit time — not recomputed by a model at audit time — the same range produces the same report on any clone that has pulled the notes (h5i share pull).

Per-commit integrity, captured at write time

Under the triage and the report sits a per-commit primitive. Every h5i capture commit runs the rules.rs checks against its own diff and stores an IntegrityReport in the commit's note with one of three levels: Valid, Warning, or Violation. A Violation (a credential-shaped string, an eval/os.system sink, a CI/CD file change) contributes weight 0.40 to that commit's review score; a Warning contributes 0.20. The audit doesn't re-derive these later — it reads the verdict the commit already carries, which is what keeps the audit path reproducible.

Wiring it into your CI

h5i audit review emits JSON with --json, so the simplest integration is a GitHub Actions step that pulls the notes, ranks the branch, and posts a review-ready PR comment with h5i share pr body --style review (which renders the provenance brief as markdown):

.github/workflows/ai-audit.yml
# Runs on every PR; comments the provenance + triage brief.
- name: h5i audit
  run: |
    h5i share pull
    h5i audit review --limit 5 --json > review.json   # ranked queue, machine-readable
    h5i share pr body --style review > review.md      # reviewer-first PR brief
- uses: actions/github-script@v7
  with:
    script: |
      const fs = require('fs');
      const body = fs.readFileSync('review.md', 'utf8');
      github.rest.issues.createComment({
        ...context.repo,
        issue_number: context.issue.number,
        body,
      });

A stricter integration makes h5i audit policy check a required status check. Define a .h5i/policy.toml (scaffold it with h5i audit policy init) whose per-path rules set require_audit = true on protected paths (auth, billing) and a max_blind_edit_ratio ceiling, plus a global require_ai_provenance. policy check exits non-zero on a violation, and CI blocks the merge. Note what policy doesn't gate: it enforces provenance and blind-edit limits, not the uncertainty or injection scans — those inform a reviewer, they don't fail a build, because a hedge or a fuzzy regex hit is too noisy to block merges on.

How this compares to SAST, secret scanners, and manual review

Provenance auditing is not a replacement for the tools you already run — it sees a different layer. The honest comparison:

ApproachGoodGap for AI code
SAST (CodeQL, Semgrep) Sound dataflow on the committed code; finds real vulnerability classes regardless of who wrote them. Reads the diff, not the process. A blind edit or a scope-creep change that is individually valid code passes clean.
Secret scanners (gitleaks, trufflehog) High-precision on credential patterns; the right tool for leaked keys. One narrow vector. Silent on uncertainty, blind edits, and injected reasoning. (h5i's CREDENTIAL_LEAK rule overlaps here but is coarser — keep the dedicated scanner.)
Manual review The only thing that actually judges correctness and intent. Doesn't scale to 30 AI PRs a week, and reviewers can't see what the model read or hedged on — that context isn't in the diff.
h5i provenance audit Scores the process — read-before-edit, hedges, scope, injected trace — and ranks where human attention should go first. Heuristics, not proofs; false positives; only scores commits that carry captured session evidence.

The layers are orthogonal: SAST and secret scanners read the code, h5i reads the provenance, and the human reads the diff the queue points them at. Run all three. The audit's job is to tell the human where to spend the scarcest of the three.

What this isn't

A few things the framework does not do, stated plainly:

Try it on your repo

Setup is a couple of minutes. The audit can only score commits that carry session evidence, so the first step is to start capturing it — then triage and report read from what's captured:

~/your-project
$ h5i init
$ h5i hook setup --write # installs the capture hooks (idempotent)

# ...let your agents work for a few days; the hooks capture
# prompts, traces, and test metrics into refs/h5i/notes...

$ h5i audit review --limit 50          # ranked triage queue
$ h5i recall notes uncertainty          # heatmap on a flagged commit
$ h5i audit scan                        # injection scan over the trace
$ h5i audit compliance --since 2026-06-01 \
      --until 2026-06-30 --format html --output audit.html

The h5i hook setup --write step installs the SessionStart, PostToolUse, Stop, and UserPromptSubmit hooks that passively capture the prompts, traces, and test evidence every audit vector scores — once wired, they run on their own with nothing more to type. (For an already-instrumented repo, link a specific session to a commit with h5i recall notes analyze.)

The first time you do this, you'll likely find one or two flagged commits you remember as "weird — something felt off in review but I couldn't pin it." Equally, you'll find flags that are plainly benign. Both are the point: the queue surfaces candidates for attention, and you stay the judge.

The shift: from reading diffs to scoring provenance

The volume problem doesn't go away — agents will keep producing more code than anyone can read. What changes is what you read it with. When every commit carries its own provenance — the prompt, what the model consulted, where it hedged, whether it stayed in scope, whether its trace was clean — auditing stops being a per-diff act of attention and becomes aggregation over recorded evidence. h5i audit review ranks it, h5i audit compliance sums it across a quarter, the companion scans deepen any single commit, and h5i audit policy draws the few hard lines worth blocking a merge for.

None of these signals proves a bug, and several will be wrong on any given commit. That is the honest framing: they are heuristics over provenance, tuned for recall, that point scarce human attention at the commits most likely to repay it. The deciding still belongs to the reviewer — the audit just stops them from spending the first twenty minutes deciding where to look.

FAQ

Does h5i audit replace human code review?

No. It ranks commits, it doesn't approve them. h5i audit review builds a triage queue so reviewers spend their scarcest attention on the riskiest AI commits first; a human still reads the diff. Every signal is a heuristic — a regex, a tool-call sequence comparison, or a diff-vs-prompt overlap — so it surfaces suspicion, not proof.

How is this different from SAST or secret scanning?

SAST (CodeQL, Semgrep) and secret scanners (gitleaks, trufflehog) read the committed code. h5i audits the provenance — what the model read before editing, where it hedged, whether it stayed in scope, whether its trace contained an injection. A blind edit or a high-uncertainty refactor is invisible to a code scanner because the diff itself looks clean. The two are complementary; run both.

What's the default risk threshold?

The default --min-score for h5i audit review is 0.5 (REVIEW_THRESHOLD). The score is the clamped sum of weighted triggers — blind edits, scope expansion, test regression, credential leak, large diff, AI-with-no-prompt, and more. Lower it to widen the net (more false positives); raise it to flag only the loudest commits.

Does the audit work on commits I already merged?

Only for commits that already carry h5i session evidence. The capture hooks (h5i hook setup --write) record prompts, traces, and test metrics as you work, and the audit scores that recorded evidence. A plain Git commit with no session note has nothing for the blind-edit, uncertainty, or injection vectors to read.

Make AI code review asymmetric in the right direction

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