
AI Coding Agents in Large Codebases: Why Repository Intelligence Beats a Bigger Context Window
AI coding agents in large codebases fail because the context that makes a change correct rarely lives in the file they're editing, and repository intelligence, instruction files, commit history, and subsystem scoping are how you fix that.
Key takeaways
- Anthropic's 2026 Agentic Coding Trends Report uses the term repository intelligence for agents that act on commit history and architectural patterns instead of the open file alone.
- Only 4% of developers say they fully trust the accuracy of AI-generated code, which argues for verification workflows that make output cheap to check rather than for trusting or distrusting it wholesale.
- A repository instruction file should state conventions and invariants the agent cannot infer from the code, not restate a directory structure the agent can already see.
- Scoping an agent to a single subsystem, with a narrower set of files and a smaller blast radius, produces more reviewable output than handing it the whole repository and a bigger context window.
An agent given a single file does fine. Ask the same agent to fix a bug in a payment flow spread across twelve files in a repository with four years of history, and the failure isn't stupidity. It's confidence. The agent writes a change that compiles, passes the tests it can see, and is subtly wrong, because the fact that would have made it right lives somewhere it never looked. A naming convention enforced in a sibling module it never opened. A migration three commits back that changed what a column means. A test written two years ago that encodes an assumption nobody wrote down in a comment.
Every model vendor has answered this the same way: a bigger context window. Windows now hold most small and medium repositories in a single prompt. That hasn't solved the problem, because the problem was never how much text an agent can hold. It's which text the agent goes looking for, and reading more files is not the same as understanding a repository.
What repository intelligence actually means
Anthropic's 2026 Agentic Coding Trends Report gives this a name: repository intelligence, the ability of an agent to act on repository context, commit history, and architectural patterns rather than the open file alone. It's a useful distinction because it draws a line most tooling glosses over. An agent that greps for a function name and reads the three files that call it has more input than one that only sees the open file. That is not the same as an agent that knows why the function was written that way, what broke the last time someone changed its signature, and which of the three callers is load-bearing and which is a stale reference nobody deleted.
The gap matters because large codebases accumulate exactly the kind of context that no single file read will surface. A convention that exists because of an incident two years ago. A pattern that looks inconsistent until you know a migration is half finished. A test that looks redundant until you realize it's the only thing guarding a race condition someone hit once in production. None of that is in the file. All of it is in the repository, and an agent that treats file contents as the whole of its evidence will miss it and sound sure of itself while doing so.
The rest of this post is about closing that gap deliberately, with five techniques you can put in place this week, and about what to do with the fact that most developers still don't trust the output even when the technique is right.
Repository instruction files: what earns a line
The instruction file, AGENTS.md or CLAUDE.md depending on your tooling, is the cheapest repository intelligence you can buy, and most teams waste it. The common mistake is writing a file that restates what the agent can already see: the api/ folder holds route handlers, components live under src/components, the project uses TypeScript. An agent with filesystem access establishes all of that in its first two tool calls. Spending your instruction budget on it is like handing someone a map of the room they're already standing in.
What earns a line is anything the agent cannot derive by reading the code: a convention it would otherwise learn wrong, an invariant that no type or test enforces, a decision that looks arbitrary until you know the reason behind it. Here's a short instruction file that does that.
# AGENTS.md
## Money math
All currency amounts are integer cents, not floats. `formatCurrency()` in
`lib/money.ts` is the only place that should turn cents into a display
string. A raw `.toFixed(2)` on a money value is a bug, not a style choice.
## Migrations
Migrations in `db/migrations/` are append-only. Never edit a migration with
a timestamp older than the oldest entry in `db/migrations/APPLIED`. If a
past migration was wrong, write a new one that corrects it.
## The legacy/ directory
Code under `legacy/` still runs, for accounts created before 2024-03. Do
not delete it because it looks unused. Check
`grep -r "legacy/" --include=*.ts src/` for the feature flag that routes
to it before touching anything in there.
## Auth
`requireAuth()` checks the session. `requireOrgAdmin()` checks the role.
Every mutation in `api/` needs both, in that order. Role checks assume a
valid session and throw an unhelpful error if the session is missing.
## Tests
Run `npm run test:unit -- <path>` scoped to the files you touched before
running the full suite. The full suite takes eleven minutes, and CI will
catch anything the scoped run misses.
Each line is doing a specific job. The money math entry heads off the single most common mistake a model makes when it hasn't seen this codebase before, defaulting to floats because that's what most training data does. The migrations entry states a rule that a directory listing cannot: files that look like ordinary SQL scripts are actually append-only history, and editing one is not a refactor, it's data corruption waiting to happen. The legacy/ entry exists because "this looks unused" is exactly the kind of judgment an agent will get wrong with total confidence, and it hands over the one command that resolves the ambiguity instead of describing the ambiguity. The auth entry states an ordering dependency between two functions that no type system encodes and that a bug report would be the usual way to discover. The tests entry isn't about correctness at all, it's about not costing the person reviewing the change eleven minutes of CI for a two-file diff.
None of these lines describe structure the agent can see for itself. All of them describe something that would otherwise have to be learned by making the mistake first.
Commit history and blame as context
A codebase's current state tells you what exists. It rarely tells you why, and "why" is usually the part that determines whether a change is correct. The diff that introduced a pattern carries the commit message, the lines that got deleted, and often a linked ticket or an adjacent test change, all of which the current file discards once the diff lands.
Blame gets you to the commit. git log -S gets you to the commit that introduced a specific string or symbol, which is often more useful than the last commit that merely touched the file.
# Find the commit that introduced the current implementation of a function,
# not just the last commit that touched the file it lives in.
git log --follow -S 'function calculateProration' --format=%H \
-- src/billing/proration.ts | tail -1
Feed the message and the diff from that commit into the prompt, not just the current function body.
COMMIT=$(git log --follow -S 'function calculateProration' --format=%H \
-- src/billing/proration.ts | tail -1)
git show "$COMMIT" -- src/billing/proration.ts
For a specific line range rather than a symbol, git blame -L gets you to the same place.
git blame -L 40,60 -- src/billing/proration.ts
Wiring this into an agent's prompt is a small amount of code. The function below takes a file and a symbol, finds the introducing commit, and returns the commit message plus the diff as a block of context to prepend to a task.
import { execSync } from "node:child_process";
function historyForSymbol(file: string, symbol: string): string {
const commit = execSync(
`git log --follow -S '${symbol}' --format=%H -- ${file} | tail -1`,
{ encoding: "utf8" }
).trim();
if (!commit) return "";
const diff = execSync(`git show ${commit} -- ${file}`, {
encoding: "utf8",
});
return `Commit that introduced ${symbol}:\n\n${diff}`;
}
Drop the result into the system prompt or the first user message alongside the task description. The commit message answers a question the current code cannot: why this shape and not the obvious one. If the shape looks wrong to a model reasoning from the file alone, the removed lines in that diff are frequently the reason it isn't.
Scope the agent to a subsystem, not the repository
Monorepo AI agent setups tend to fail the same way regardless of the model behind them: given filesystem access to the whole repository, an agent will occasionally reach across a package boundary it shouldn't, import something internal from a sibling package, or touch a shared file three teams depend on because it seemed related. None of that requires the model to be wrong about the code. It only requires the model to have access to more than the task needed.
The fix isn't a smarter model, it's a smaller blast radius. Point the agent's working directory or file access at the subsystem the task belongs to, not the repository root. Where the task genuinely needs to know about an adjacent package, hand it the public interface of that package (the exported types and function signatures) rather than its internals. A symbol index or codegraph query can produce that surface directly, so the agent sees what it's allowed to call without seeing, and potentially depending on, what it's allowed to change.
This trades context for constraint, and constraint wins more often than raw window size does. A change confined to one package is a diff a reviewer can read in full. A change that touches four packages because the agent had access to all four, whether or not the task needed it, is a diff that gets skimmed, and a skimmed diff is where the wrong assumption survives to production.
Tests as the specification the agent should read first
Comments describe intent inconsistently and go stale. Tests describe intent by construction, because a test that stops matching behavior fails, and a comment that stops matching behavior just sits there being wrong. That makes the test suite the closest thing most repositories have to a specification, and it's worth treating it that way in how you prompt.
Point the agent at the relevant test file before it touches the implementation, and say so explicitly in the task: the behavior in proration.test.ts is correct as written, and if the implementation disagrees with a test, the implementation is what's wrong, unless the task is specifically to change that behavior, in which case say which test should change and why. That single sentence removes a large source of agent error, where a model "fixes" a function by making it match what it assumes the function should do rather than what the test says it must do.
This also gives you a cheap correctness signal that doesn't depend on reading the diff line by line. Run the test file the agent was pointed at before the change and confirm it fails for the stated reason, then run it after and confirm it passes and nothing else in the package broke. That's the shape of test-driven development applied to agent output rather than to your own typing, and it works for the same reason it always has: a red test you understand is worth more than a green test you don't.
Static analysis and symbol indexes instead of stuffing files into context
The blunt way to give an agent codebase context is to concatenate files and hope the relevant one is in there. It's expensive in tokens, it degrades as the repository grows, and it still misses the calls that grep can't follow: an interface implemented three files away, a handler registered through a plugin system, a method invoked through dynamic dispatch where the string doesn't appear anywhere near the call site.
A symbol index, the kind tools like codegraph build, answers a narrower and cheaper question well: given this function, what calls it, what does it call, and what's the shortest path between these two symbols. That's a graph query, not a file read, and it returns exactly the call path instead of the files that happen to contain it. For a task that touches a function used in six places across a large repository, the difference between "read these six files in full" and "here are the six call sites and the type signatures involved" is often an order of magnitude in tokens, and the second version is more precise, not less, because it was built by following the actual edges in the code rather than by guessing from file proximity.
None of this replaces reading source. It replaces reading source you didn't need, which is most of what a naive "give the agent the whole repo" approach spends its context budget on.
The 4% trust number, and what to do about it
Only 4% of developers say they fully trust the accuracy of AI-generated code. That number is not a reason to swing toward blanket distrust, and it's not a problem the next model generation is going to quietly fix either. It's a fact about the current state of the tools, and the useful response to it is neither to trust the output nor to distrust it as a matter of policy. It's to make the output cheap to verify regardless of how much you trust it on a given day.
Cheap verification has a specific shape. Small diffs, because a hundred-line change and a five-line change do not take proportionally different amounts of reviewer attention, they take wildly different amounts, and the hundred-line one is where mistakes hide. Tests first, for the reason above: a test that already exists and already fails for the right reason gives you a pass or fail signal that doesn't require reading the implementation closely to trust. One concern per change, so that a reviewer evaluating "does this fix the proration bug" isn't also silently asked to evaluate "and is this unrelated refactor of the logging wrapper also fine," which is how unrelated mistakes get approved on the coattails of a change nobody was actually scrutinizing.
The scale at which this matters keeps growing. Gartner recorded a 1,445% rise in multi-agent system inquiries between Q1 2024 and Q2 2025, and 2026 is widely described as the year AI coding moved from experiment to operational maturity, with production systems standardizing on agents rather than piloting them. The conversation among developers has moved with it, away from whether the tools work and toward pricing, session limits, context behavior, harness design, and the everyday friction of running them at scale. That shift is itself informative. Teams stopped debating capability once they had a verification workflow that didn't depend on taking the output on faith, and started debating the operational cost of running that workflow instead.
Where this still doesn't work
Some tasks are still a poor fit, and the techniques above don't change that.
Wide cross-cutting refactors, the kind that touch two hundred call sites to rename a concept, are a bad fit even with perfect repository intelligence, because correctness at each site is frequently a judgment call rather than a mechanical transformation, and an agent applying one rule uniformly across two hundred sites will get a meaningful fraction of them wrong in ways that are individually plausible and collectively expensive to review.
Anything where the correct answer depends on product context that was never written down, a decision made in a meeting, a tradeoff a founder made verbally three years ago, is not recoverable from the repository no matter how good the commit history is. The agent can't read a fact that exists only in someone's memory, and neither can you, if you're being honest, without asking that person directly.
Code with no test coverage removes the cheapest verification signal available, and it also removes the specification the agent would otherwise read first. Both problems compound: the agent has less to go on, and you have less to check its work against, at the same time.
Before you paste a task into an agent's context window, write down the one fact that would make a wrong answer plausible. If it lives in a commit message, pull the commit. If it lives in a test, point at the test. If it lives in nobody's head, the task isn't ready for an agent yet, no matter how much context you give it.
Related reading
We built and shipped five open-source vertical AI agents. Every single one had the same class of defect: absent or unreadable input rendered as a confident, clean answer. Here is what that bug looks like, why tests miss it, and what actually catches it.
GitHub shipped native stacked pull requests into public preview on July 30, 2026. Here's how the gh stack workflow actually works, where stacking pays off, how to pick layer boundaries, and when a stack is the wrong tool.
A repeatable process for deciding whether a newly launched model belongs in your stack: shadow-mode traffic, the four metrics that matter, and why benchmark tables should never drive your routing table.
Let's build something great.
Have a project in mind? We are an elite software and AI development studio ready to bring your ideas to production. Let's talk about your roadmap.