AUTO-TABLE inventories: a structured codebase index Claude Code can actually trust
Deterministic, auto-regenerated codebase inventories embedded in your docs — the structured index that lets Claude Code stop re-deriving your architecture.
Every fresh Claude Code session starts with the same expensive ritual: the agent re-derives your codebase. It greps, it reads, it builds a mental model — and on a solution of any real size, that model is always slightly wrong. Not dramatically wrong; slightly wrong, in the way that produces an endpoint added to the wrong feature folder, a duplicate of a service that already exists three projects over, or a migration named against a convention the agent never saw.
The fix we run is a mechanism we call the AUTO-TABLE inventory: structured tables of what actually exists in the codebase — endpoints, entities, consumers, migrations, pages — generated by deterministic scanners, embedded directly in the documentation the agent already reads, and regenerated automatically so they cannot drift. This post shows the real implementation: the marker format, the scanner registry, when regeneration fires, and why deterministic is the property that makes the whole thing work.
The problem: documentation that lies
Hand-maintained inventories are lies waiting to happen. Someone writes “the Billing feature has these six endpoints” in a doc, three sprints pass, and now it has nine — but the doc still says six, and an agent reading it trusts the six. Stale documentation is strictly worse than no documentation for an agent, because agents (unlike humans) do not carry an instinct for “this file smells out of date.”
Teams respond in one of two bad ways. They stop documenting inventories at all — so every session pays the full re-derivation cost, burning context on rediscovering what a table could have said in forty tokens per row. Or they keep the docs and accept the drift — so the agent’s confident starting model is confidently wrong. The investigation-gate hook catches wrong references before they become edits, but it cannot give the agent a correct big picture. That takes an index — and an index is only useful if it is mechanically guaranteed fresh.
The mechanism: markers plus scanners
The implementation is deliberately boring. Documentation files carry paired HTML comments:
<!-- AUTO-TABLE:endpoints:Features/Billing/Endpoints -->
(generated content — do NOT edit manually)
<!-- /AUTO-TABLE -->
A scanner walks every .md file, finds the markers, scans the filesystem at the declared scope, and replaces everything between the markers with a fresh table. Two parameters: a type naming which scanner to run, and a scope naming the directory to scan. That’s the entire authoring experience — you place a marker once, and the table underneath it stays true forever.
Ours registers sixteen scanner types. The ones that earn their keep on a .NET solution:
| Type | Scans for | Table columns |
|---|---|---|
structure | Directory tree | Rendered ├──/└── tree |
endpoints | FastEndpoints classes | Endpoint, route, method, request, response |
entities | EF entity classes | Entity, schema, key properties |
migrations | SQL migration files | Migration, execution order |
consumers | MassTransit consumers | Consumer, message type |
services | Service/interface pairs | Service, interface, implementation |
pages | Blazor .razor pages | Page, route (@page) |
jobs | Scheduled job classes | Job, schedule |
Each scanner is a small, dumb function — and dumb is a compliment here. Here is the shape of the endpoints scanner:
function scanEndpoints(absPath) {
if (!fs.existsSync(absPath) || !fs.statSync(absPath).isDirectory()) return null;
const rows = [];
for (const d of safeReaddir(absPath).filter(e => e.isDirectory())) {
rows.push(...scanEndpointDir(path.join(absPath, d.name), d.name));
}
return { headers: ['Endpoint', 'Route', 'Method', 'Request', 'Response'], rows };
}
scanEndpointDir finds the *Endpoint.cs file, extracts the actual route and HTTP method from its code, and derives the request/response contract types from the *Request.cs / *Response.cs files sitting beside it. The output for a billing feature looks like:
| Endpoint | Route | Method | Request | Response |
| --------------- | ---------------------------- | ------ | -------------------- | --------------------- |
| CreateCheckout | /billing/checkout | POST | CreateCheckoutRequest| CreateCheckoutResponse|
| CancelPlan | /billing/subscription/cancel | POST | CancelPlanRequest | — |
| GetInvoices | /billing/invoices | GET | — | GetInvoicesResponse |
Note what is not involved: no LLM summarizing the codebase, no embeddings, no inference. The table is a pure function of the filesystem and the code. Run it twice on the same commit and you get byte-identical output — which is precisely the property an agent can trust, and the property that makes the table’s diff meaningful in review.
When regeneration fires
A fresh index is only fresh if regeneration is nobody’s job. Ours runs on four triggers:
| Trigger | When |
|---|---|
| Feature start | Before the agent’s task loop begins — every session opens against current tables |
| Doc sync | As part of the pre-commit documentation pass |
| Audit | Freshness check; regenerates anything stale |
| Manual | node scan.js --inventories, any time |
Alongside the scanners sits a doc registry: path rules that map changed code files to the documentation affected by them (Features/*/Endpoints/ → the API docs that inventory those endpoints). When a PR touches billing endpoints, the registry knows which docs carry billing tables, the scanner refreshes them, and the regenerated table lands in the same PR as the code change. That is the detail we would defend hardest: the diff of the inventory is an architectural diff of the PR. A reviewer — human or agent — sees “this PR adds two endpoints and a consumer” as table rows, without trusting the PR description to say so.
The edge cases that will find you
Running this on real solutions surfaces a predictable set of edges, so here they are in advance.
Extraction fails visibly, never silently. When the endpoints scanner cannot parse a route out of an *Endpoint.cs — an unusual base class, a route built from constants — the row says (route not extracted) rather than being dropped. A visible gap invites a fix; a silent omission teaches the agent that the table is incomplete, which quietly destroys the trust the whole mechanism exists to create.
Grouping directories need recursion, but only one level. Real feature folders nest — Endpoints/Payments/ containing three endpoint directories. The scanner recurses exactly one level into directories that contain no *Endpoint.cs of their own. Unlimited recursion turned out to be a mistake: it happily inventoried test fixtures and generated code, and the tables grew past the point of being readable at session start.
Scanners rot when conventions change. Rename your *Request.cs convention to *Command.cs and the contract columns go blank — the scanner does not error, it just finds nothing. The mitigation is the same discipline as the CLAUDE.md maintenance rule: a blank column in a freshly-generated table is a bug report against the scanner, fixed the same day. Deterministic tools do not adapt; that is their virtue and their cost.
Keep generated content out of git conflicts. Because tables regenerate in the same PR as code changes, two branches touching the same feature both rewrite the same table — and the merge conflict is noise. Resolution is mechanical (regenerate after merge), and worth automating in CI so nobody hand-merges a generated table.
None of these are reasons not to build it. They are the difference between the idea and the tool — the same gap this whole series keeps pointing at.
How this compares to the wiki generators
The adjacent tools deserve honest positioning. Devin Wiki and DeepWiki generate readable, LLM-authored wikis from a repository — genuinely useful, and for onboarding a human into an unfamiliar codebase, better than AUTO-TABLE, because prose explains why and tables cannot. codewithmukesh’s convention-learner skill (from his .NET Claude Kit) extracts coding conventions from an existing repo in one pass — closer kin to our pattern snapshots than to inventories, and a good public reference for that idea.
AUTO-TABLE differs from the wiki generators on three axes, and the differences are the point:
- Deterministic, not generated. A wiki is an LLM’s interpretation; an inventory is a scan. Interpretations are excellent until they are subtly wrong, and subtle wrongness in an agent’s ground truth compounds.
- Regenerated per change, not on demand. The tables refresh when code changes, in the same commit — freshness is structural, not scheduled.
- Embedded at the point of read. The tables live inside the docs the agent already opens, not in a separate system it must know to consult. The index finds the agent, not the reverse.
The honest limitation cuts the other way: sixteen scanner types cover the inventoriable surface of a codebase — the things with names, files, and conventions. They do not capture intent, history, or the reasoning behind a design. That is what the prose around the tables is for, and why this mechanism complements documentation rather than replacing it.
Why “trust” is the operative word
The title claims the agent can trust this index, and the claim rests on the two properties above: determinism and structural freshness. When those hold, something useful happens to agent behavior — it stops re-deriving. A session that opens against trustworthy tables skips the twenty-file exploratory read and spends that context on the actual task. The duplicate-implementation failure mode (the agent writes a service that already exists because its search missed it) drops sharply, because “does one exist?” is answered by a table lookup instead of a grep gamble.
We saw the effect most clearly on our own template build — five connected applications and 23 features, where the inventories tracked feature × endpoint × entity coverage across the whole solution and every fresh session opened against the same architectural snapshot. The AUTO-TABLE layer exists in its current form because that build exposed exactly how much session time was going into re-derivation.
Where it sits, and where to start
AUTO-TABLE is layer four of the twelve-layer system — the ground-truth layer that the spec-driven CLAUDE.md points at and the investigation gate presumes. The glossary entry for the term is here.
If you want to build a minimal version: pick your single most drift-prone inventory (for most .NET teams, it is the endpoint list), write one scanner that walks the feature folders and extracts routes, and wire it to a marker in the doc your agent reads first. One table, regenerated in CI, will teach you whether the pattern pays for your codebase — ours says it does, roughly forty tokens per row versus a re-derivation that costs thousands.
We install the full sixteen-scanner setup, registry included, as part of Sprint Zero — but like everything in this series, the pattern is reproducible from what’s on this page.
Related Posts
Bayesian build-outcome confidence: telling Claude when its plan is likely to fail before it costs you a build
Your agent already generates the data to predict its own build failures. How we score plan steps with Bayesian pattern confidence and cascade-risk analysis.
How we run Claude Code on a 5-developer .NET team — with our actual CLAUDE.md, hooks, and skills
Our real production setup, published: the hook roster, five subagents with model tiers, eight MCP servers, and the machinery that makes agent output shippable.
GitHub Copilot modernize-dotnet vs. Claude Code on a real .NET 8 → 10 migration
Same model, three tools, one migration — measured. What Copilot's upgrade agent, plain Claude Code, and a guarded setup actually delivered, warts included.