The first version of our triage engine did the obvious thing.
A ticket came in — say, a Sentry error pointing at checkout.ts — and we stuffed as much of the repository as we could into the model's context, asked it to find the likely cause, and waited. It worked. It was also the single most expensive line item in our infrastructure, and the dumbest.
Here's the math that finally embarrassed us into fixing it. Reading a mid-sized production repository costs roughly 1.4 million tokens. Absorbing a single merge — the actual change since the last time you looked — costs about 3.1 thousand. That's a 450x difference, and in v1 we were paying the big number on every single ticket. Ten tickets touching the same service in one afternoon? We read the same unchanged code ten times.
Nobody would design a system this way on purpose. But if you look at how most AI tools handle repository context today, this is exactly what they do — because re-reading everything is the easy thing, and remembering is the hard thing.
This post is about the hard thing. What we built at FlowTux, what broke along the way, and why I now think incremental indexing is the difference between an AI that uses your codebase and one that knows it.
1.4M
tokens to read a mid-sized repository once
3.1k
tokens to absorb a single merge
450x
difference between the two per event
The re-read tax
Every AI tool that touches code has to answer the same question: where does context come from?
The dominant answers in 2025–26 were:
1. Stuff the context window. Long-context models made this tempting. Dump the repo, or the "relevant" chunk of it, into every request.
2. RAG over embeddings. Chunk the repo, embed it, retrieve top-k at query time.
3. Agentic exploration. Let the model grep and open files until it finds what it needs.
All three share a flaw that's invisible in a demo and brutal in production: they treat every request as the first request. The model wakes up with amnesia, re-derives an understanding of your architecture, answers, and forgets.
For a coding assistant helping one developer, that's tolerable — the human carries the memory. For a helpdesk triaging hundreds of tickets a week against the same codebase, it's a tax you pay on every ticket. And it's not just a cost problem. It's a quality problem, because an understanding rebuilt from scratch each time is shallow. The model can see that checkout.ts calls promotions.find(), but it can't know that this module was rewritten twice, that ticket TKT-0142 already fixed a null-promo bug here, or that the engineer who shaped it left in March.
That last part matters more than the token bill. Codebases carry institutional memory that lives in nobody's head after enough attrition. A system that re-reads from zero can never accumulate it.
What "indexing once" actually means
So we flipped the model. Instead of ticket → read repo → answer, we do merge → update map → tickets consult the map.
Concretely:
The initial pass is expensive, and that's fine. When a customer connects GitHub or GitLab, we do one full read — the 1.4M-token read — and build a structural map: modules, their responsibilities, their dependency edges, and a natural-language summary of why each piece is shaped the way it is. For one of our production customers that map covers ~1,800 files. You pay this cost exactly once.
Every merge is a diff, not a re-read. When a PR lands, we don't touch the whole repo. We read the diff, figure out which parts of the map it invalidates, and update only those entries. A typical merge costs ~3.1k tokens to absorb. The map stays current with the code as it is today, not as it was at index time — which is where most "we indexed your repo" tools quietly rot.
Tickets attach to the map. When a ticket is resolved and the fix touched payments/checkout.ts, that link is stored. The next error in that file arrives with its history: 41 previous tickets, the last fix, the PR that changed the session TTL two days ago. Triage stops being a cold start.
If you want the full mechanics — including how renames are tracked so ticket links migrate when services/email becomes services/notifications — we've written up the whole path a ticket takes.
Re-read per request
- Every ticket starts from zero
- Same unchanged code read ten times a day
- No memory of past fixes or past tickets
- Cost scales with ticket volume
Index once, absorb diffs
- One full read when the repo is connected
- Each merge updates only what it invalidates
- Resolved tickets attach to the modules they touched
- Cost scales with merge volume, not tickets
The parts that broke
I promised war stories, so here are the three that cost us the most sleep.
1. Drift detection is the real problem, not indexing. Building the initial map is a weekend project. Knowing which parts of the map a diff invalidates is the actual engineering. A one-line change to a config constant can invalidate assumptions in five modules that never appear in the diff. Our first heuristic — "update entries for files in the diff" — missed these constantly, and stale map entries are worse than no map, because the model trusts them. We now propagate invalidation along the dependency edges we recorded at index time, and flag entries as "drift detected" rather than silently serving them.
2. Renames nearly wrecked ticket history. A team renamed a service directory and, from the map's perspective, 34 tickets' worth of institutional memory suddenly pointed at files that didn't exist. Content-similarity matching across the rename fixed it, but it taught us that the map's identity layer (what is this module, across time) matters as much as its content layer.
3. Not everything belongs in the map. Early on we tried to route every ticket through repository context, including "the office Wi-Fi is down." The codebase has no opinion about Wi-Fi. We ended up drawing a hard line: engineering-shaped tickets — errors, regressions, anything traceable to code — consult the map; device and access issues go through a completely different resolution path. Knowing when not to use your expensive index is part of the architecture.
Does the map actually help? We benchmarked it.
"Our AI understands your codebase" is the kind of claim every vendor makes and nobody checks, so we checked ours. Four arms, same model, identical prompts: with the index, without it, with the repo name hidden, and — the control I care most about — a private repository no model has ever seen in training, so memorization can't rescue the score.
On bug reports (n=81), the indexed model named a file the real fix actually touched 50.6% of the time. The same model with no index and the repository name hidden: 14.8%. On the private repo, the indexed arm held at 50% while every baseline scored zero — which is the result that convinced me, because it separates "the model read your code" from "the model saw this repo on GitHub in 2024."
The full method — including the paired comparison and, importantly, the findings we withdrew when they didn't survive scrutiny — is published on our benchmark page. I'd genuinely rather you read the caveats than quote the headline number.
50.6%
indexed: named a file the real fix touched (bug reports, n=81)
14.8%
no index, repository name hidden
0%
every baseline arm on a private repo, where the indexed arm held at 50%
The bigger point: memory is becoming the moat
Step back from helpdesks for a second. The 2026 pattern across AI tooling is the same everywhere: models are converging, context is not. Everyone can call the same frontier model. What differs is what you feed it — and whether that context compounds.
Stateless tools have a ceiling: their best answer today is their best answer forever. A system with a living map gets structurally better every merge and every resolved ticket, because each one is a deposit into memory rather than a transaction that evaporates. That compounding is why our auto-resolution rates climb over months rather than plateauing at week two.
The token savings — 3.1k vs 1.4M per event — are what get people's attention. But honestly, the cost is the least interesting part. The interesting part is that incremental indexing turns your codebase from a document the AI reads into a place the AI lives. Context that outlives the engineer who wrote it. Triage that arrives pre-informed. A repo rename that doesn't amnesia-wipe two years of ticket history.
If you're building anything AI-powered on top of a large, evolving corpus — code, docs, tickets, contracts — the lesson generalizes: index once, absorb diffs, attach events to the map, and detect drift aggressively. Re-reading everything per request is the pattern that demos well and scales terribly.
We learned it by paying the 1.4M-token bill a few thousand times more than we should have. You don't have to.
