Part 2 · The Review Architecture

Agent-Assisted Code Review · ~7 min

Structure-Aware Diffs

A raw diff is a flat wall of hunks. Label each one by what it touches, and review attention flows to where structure says it matters.

Why this, for you: you scroll a 40-hunk PR and treat every hunk the same — a rename, a logging tweak, and a change to an external interface all get the same glance. The signal you need (which hunks are mechanical, which reshape behavior, which depend on each other) is in the diff, but nothing surfaces it. A labeling pass makes it explicit before you spend a single minute reading.

Tiered review (last lesson) routes whole PRs by risk. This lesson goes one level finer: it routes hunks. A two-stage LLM pipeline assigns each hunk a change type, then resolves how the hunks relate to one another — turning a flat patch into a labeled structure you can prioritize.

1 Label the hunk, not the whole PR

Structure-aware diff labeling assigns a change type to each hunk in a patch. The paper behind this pattern uses a fixed 12-type taxonomy — documentation, testing, output handling, retype, code move, style change, logging, rename, error handling, logic change, internal interface change, external interface change.

The win is attention routing. Knowing a hunk is a style change versus an external interface change tells you, before you read a line, whether it deserves a glance or a hard look. A flat diff makes you re-derive that judgment forty times by hand.

The labeler runs per-hunk, few-shot, against that fixed label set, giving each hunk roughly 5 lines of local context. Three modes — per-hunk, per-file, per-patch — trade context length for token cost. Because it is few-shot rather than AST-based, one prompt classifies Java and Python diffs with comparable accuracy: the legitimate win is polyglot coverage and a taxonomy you can extend without rewriting per-language rules.

2 The Refiner: hunks relate to each other

A per-hunk labeler can name change types but cannot link them. Four hunks each labeled rename is far less useful than knowing they all consume one renamed declaration. That linkage only exists in the whole-patch view.

So Stage 2 is the Refiner: a single whole-patch inference that captures cross-hunk relationships. It assigns a parent field (parent=0 for the declaration, parent=N for usages), extracts attributes like old/new names and types, and corrects misclassifications only visible across the full patch.

# Stage 1 Labeler (per-hunk) then Stage 2 Refiner (whole-patch) hunk[1]: [Rename] hunk[2]: [Rename] # usage consequence hunk[3]: [Logic Change] -> Refiner (single whole-patch inference): hunk[1]: Rename(parent=0, old="getUserId", new="getAccountId") hunk[2]: Rename(parent=1) # consumes hunk[1]'s declaration hunk[3]: Logic Change
The single-shot Refiner pass is the central idea. Per-hunk inference structurally cannot represent declaration-to-usage parenting or move source-target pairing — those emerge only from seeing the entire patch at once. Labels alone give you types; the Refiner gives you the structure.

3 Supplement, not replacement

This is an LLM classifier, and it is qualified. Performance varies substantially across label types, and the trade is determinism and cost for coverage and customizability.

Legitimate winLegitimate trade-off
Polyglot — one prompt, many languagesNon-deterministic output (~84% recall / 81% precision)
Extend the taxonomy with few-shot, no retrainingPer-category accuracy is uneven
No per-language AST tooling to maintainWhole-patch Refiner blows out on very large diffs

The weakest categories are often the ones you care about

Under-represented labels — external interface, error handling, logging — score worse than rename or logic-change. A workflow that depends on exactly those categories is precisely where the paper recommends a hybrid: LLM labeling for most types, deterministic static analysis (a RefactoringMiner-class tool) for the few critical ones. Treat the labeler as a supplement, not a replacement.

Cost is non-uniform too: the best model in the benchmark (Gemini-3-Pro-Preview) consumed up to 7.5× the output tokens of the second-best (Claude Sonnet 4.5). Skip the whole approach when a single-language project already has mature static tooling, when CI demands deterministic audit trails, or when per-PR token cost dominates the budget.

↪ Your win: a labeled diff, not a flat one

Retrieval practice — recall, don't peek

Question 1Structure-aware diff labeling assigns a change type to each…

Question 2The Refiner stage exists primarily to…

Question 3The legitimate wins of the LLM labeler over per-language static analysis are…

Question 4For the critical, weakest-scoring label types, the paper recommends…

Question 5 · spaced recall from an earlier lessonIn tiered review, you set review depth by…

Ask me anything. Want help wiring a hunk labeler into your review prompt, or deciding which label types to hand to static analysis instead? Next: The Committee Pattern — run several specialized reviewers in parallel and synthesize one verdict, because diversity beats one big prompt.
✎ Feedback