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.
- 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.
| Vector | What it looks like | Detector |
|---|---|---|
| Blind edits | File modified with no preceding Read | Tool-call sequence analysis |
| Uncertainty | Hedge phrases inside thinking blocks | Calibrated phrase lexicon |
| Scope creep | Edits to files unrelated to the prompt | Diff-vs-prompt overlap |
| Prompt injection | Override / exfiltration patterns in trace | Regex 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:
$ 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]:
$ 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.
--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:
$ 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):
# 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:
| Approach | Good | Gap 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:
- It does not approve PRs. It ranks them. Humans still review.
- It does not catch bugs the model never noticed. Uncertainty signals are a recall floor, not a ceiling.
- It does not score human-written commits. It scores AI-tagged commits with associated session logs.
- It does not call another model. Every signal is a regex, a tool-call sequence comparison, or a diff-vs-prompt analysis. The audit path is fully deterministic, by design, so the audit itself can be audited.
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:
$ 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