Skip to content

AI-ready code

The agent keeps making wide, wrong edits in one corner of your codebase, and every session burns its first stretch of tokens rediscovering the same layout. Nothing it did was unreasonable - it read the code that was there. The codebase just never told it where the edit lived, or what else was listening.

Use whenOne area of the repo eats wrong edits and re-discovery, session after session
You’ll haveFour rerunnable probes with a pass bar each, and a seam a cold session can navigate on its first search
CostsAn afternoon per seam. Each rerun is four prompts
NeedsA way to open a genuinely fresh session - no rules file, no memory
Skip ifAgents rarely touch that code, or this is turning into a repo-wide beautification project

The four probes are independent measurements, not a sequence. Find your symptom, run that probe, ignore the rest until it passes.

What you’re seeingRun
A fresh session takes three-plus file-opens to answer “where does X live?”Probe 1: naming
A ten-line edit reads five files firstProbe 2: context budget
Its predicted blast radius names one file and misses the two that actually breakProbe 3: blast radius
”Looks right to me” is as far as a fresh session gets on a PR you reported as doneProbe 4: verification
A rules-file line reads like a general essay about the codebaseWrite down only what structure can’t say

Honest names, contained side effects, tests as spec: none of this is new advice. What changed is who reads it. The codebase is the manual, and the agent re-reads it from nothing every session.

A new hireAn agent
Reads the layoutAbsorbs it once, badlyReconstructs it from disk, every session, forever
Over timeBuilds tolerance through repetitionNo grace period - it never repeats a session
What it costsInvisible, carried silently for yearsVisible, on a token bill, per session

That table answers both of the questions this play usually gets - “isn’t this just write clean code?” and “am I optimizing for the agent or for humans?” - with one answer: same optimization, different feedback speed. Clean-code advice argues in adjectives, which is why teams argue about it forever; a probe is a measurement, in front of everyone.

Structure is context you pay for once. Every convention the code makes self-evident is a rule you never have to write, maintain, or load into a session again.

Evidence first. Pull up the recent session where the agent went wrong and note where the wrong turns happened - the search that missed, the file it shouldn’t have touched. Don’t restructure ahead of demonstrated failure; this play runs per seam, on evidence, not as a quarter-long project.

A readiness scorer, like the one below, can give you a starting direction. Only the probes tell you what a score can’t: how your agent, asked in your team’s words, actually navigates.

Running example throughout: a billing area split three ways, each file holding a fragment of the same decision - who gets billed this cycle. You ask for something small, and it touches a fistful of files, edits a shared helper two other jobs depend on, and quietly breaks the dunning emails. In a transcript, not in the retelling:

> skip archived customers in the nightly billing run
⏵ Grep "billing" src/ → 9 matches across controllers/, services/, models/, validators/, jobs/
⏵ Read services/billing.ts # runBilling() filters by due date - adds the archived check here
⏵ Edit services/billing.ts
⏵ Bash npm test → 2 failing: dunning.test.ts, reports.test.ts
⏵ Read validators/billing.ts # a second, competing exclusion rule this edit didn't know existed
⏵ Edit validators/billing.ts
⏵ Bash npm test → 1 failing: dunning.test.ts
⏵ Read jobs/nightly-run.ts # a third, hardcoded filter - `status !== 'trial'` - never
mentioned by either file it just touched
⏵ Edit jobs/nightly-run.ts
⏵ Bash npm test → green - after three edits, three re-reads, and a diff that
now touches every layer billing has a folder for

Nothing here is a reasoning failure. Three files each hold a piece of “who gets billed,” and nothing on disk says so - the agent found the fragments the only way it could, one test failure at a time. Keep that transcript; you’ll want it again once the fix is in and the same request lands differently.

Pass bar: one target area, and one transcript showing the failure.

Each probe is one prompt into a genuinely fresh session - no rules file, no memory, because prior context contaminates what the structure alone says. Each has a pass bar, a diagnosis when it fails, and one prescribed fix. They don’t stack: a repo can pass naming and fail blast radius.

Same move as everywhere in this section - route the agent’s claim through a check that can say no. The twist here is that the claim under test is also yours: that the code is fine and the agent is just careless.

> Where do we decide which customers get billed?
AskA real domain question, phrased exactly how a teammate would say it over Slack, internal nicknames included. An agent can’t infer synonyms for jargon that only exists in your standups
Pass barThe session lands in two greps or fewer
A fail meansMore than two greps, or three-plus file-opens, before it converges: the answer is smeared across files and nothing on disk says so. The agent has to discover the fragmentation by testing its way through it - the transcript in Pick the seam
The fixGo domain-first over layer-first: organize by what the code is about, not by technical stratum
If you can’t fix it yetOne glossary line mapping slang to path (“the sync layer” → telemetry-bridge/)

The judgment the bar is actually testing is when you’ve seen enough to answer. Run the probe yourself against both structures below, paying for each file you open, and watch how much of the answer you can hold before you’re willing to commit to one.

Where do we decide which customers get billed?

You're the fresh session: no rules file, no memory, no tenure. Open files until you can answer.

opens: 0pass ≤ 2answer: 0/3 pieces
src/
controllers/
services/
models/
validators/
jobs/
findings

Nothing opened yet - findings accumulate here as you read.

  • Directorysrc
    • Directorycontrollers
      • billing.ts
    • Directoryservices
      • billing.ts
    • Directorymodels
      • billing.ts
    • Directoryvalidators
      • billing.ts

Every folder is a technical stratum, so “billing” appears in all of them and none of them owns the decision. The name that fails the probe is the one that answers “what layer is this?” instead of “what is this about?”

Where this fix goes wrong: a glossary instead of a rename. A name a newcomer has to look up in a glossary is still a name that failed the probe, just with a cheaper workaround attached. Better, when you can afford it, to rename toward the words people actually say - the glossary line is what you write down while that’s still a bigger job than today’s ticket. A glossary of aliases growing instead of renames happening is workaround debt, not legibility paid down.

> Add a $5 minimum invoice amount to the billing run - anything below
that rolls over to next cycle instead of getting billed.
AskOne small, real change request - small enough that you already know which file owns it. Then watch what it opens, not what it writes
Pass barWhat it opens is proportional to the edit: the file that owns the decision, maybe one caller, then the change
A fail meansA ten-line change drags a thousand-line file and everything that file imports into context just to be safe. Long-context evaluations consistently report accuracy falling as context fills, which is the reasoning behind treating each unnecessary file as making the agent worse at the change, not just slower
The fixSplit along the seam it had to reason across - the file’s job, not an arbitrary line count
If you can’t fix it yetGive it structural eyes instead of asking it to read more text: a syntax-aware navigation layer over MCP

The recovery principle: where the repo is what it is today - mid-migration, a vendor dependency, a rename too large to review in one sitting - stop asking the agent to read more text and give it the same structural eyes your IDE has.

A layer built on the language-server protocol behind “go to definition” and “rename symbol” turns “grep the name, open every match, hope none is a comment or an unrelated method” into a few exact operations: where a symbol is defined, every real reference to it, rename it everywhere at once.

> Before changing anything - if I ask you to add a $5 minimum invoice
threshold, which other files will that change affect?
AskA prediction, made before any edit. The same change as probe 2 works fine - you’re measuring foresight now, not reading
Pass barIts list roughly matches what a careful human on the team would name
A fail meansIt names one obvious file and misses the two that actually break, or names files with no real connection. The gap is the finding: the coupling is tribal knowledge, invisible in the code and therefore invisible to the agent
The fixMake the decision a pure function and move side effects to the caller. Aim for a few deep modules: a narrow public surface, cheap to read, hiding a lot of implementation
If you can’t fix it yetThe same navigation layer answers “what actually calls this?” structurally, instead of by grepping for a name

Deep is not the same as small, and the wrong cure is worth seeing once. All three tabs below are the same four responsibilities:

// one function, four responsibilities, invisible coupling
export function process(items, opts) {
const cfg = Config.current() // hidden dependency
const filtered = applyRules(items, cfg)
Cache.invalidate('billing') // side effect
Audit.write('billing.run', filtered) // side effect
Mailer.queueDunning(filtered) // side effect - the surprise
return filtered
}

Nothing on the surface says that calling this queues dunning email, so nothing warns the agent that the mail path is downstream of a filter change.

Where this fix goes wrong: over-fragmenting. Many shallow pieces are collectively harder to reason about than one deep one, because the reader has to reassemble the story from fragments and trace which fragment calls which. Tell: this probe or probe 2 still fails after the split, now across six files instead of one. Optimize for how much must be read to understand a change, not for lines per file: deep modules, not confetti.

That narrow surface is also the delegation contract this whole site keeps circling: you keep taste over the interface; the agent owns the implementation behind it, held in place by tests. Decide the boundary at planning time, when a ticket becomes tasks - not at review time, after the coupling already leaked into a diff you now have to unwind.

> Here's a PR where I reported this feature as done. Using only the
existing test suite and your own exploration - not me - verify that
it actually works. What's your verdict, and what did you check?
AskA recent “done,” handed to a session that wasn’t there when it was written and can’t ask you anything
Pass barIt reaches a verdict - pass or fail - from the suite and the code alone, and can point to what it ran
A fail meansThere is nothing checkable to run. The agent’s read-act-verify loop only closes if the verify step has something real in it
The fixTests from somewhere the agent didn’t write them - the TDD playbook: a failing test committed before the fix, so “done” means “satisfies a spec that predates the implementation”
If you can’t fix it yetFor long unattended stretches, a machine-checkable status format beats a prose checklist - a JSON pass/fail list is harder for a stuck agent to quietly soften than a Markdown line it can edit into agreeing with itself

Where this fix goes wrong: verification theater. A green suite that never touched the change - tests asserting on the wrong layer, mocking out the exact thing that broke, or simply not covering the seam the edit ran through. Tell: the suite is green, but a fresh session asked to verify can’t explain why it’s green in terms of the specific change. This probe only catches it if the session actually tries to break the claim rather than skimming for a pass.

Same words, same model, the structural fix in. Read it against the transcript in Pick the seam:

> skip archived customers in the nightly billing run
⏵ Grep "billing" src/billing/ → 1 file: billing/select.ts - the one function that decides
⏵ Read billing/select.ts
⏵ Edit billing/select.ts # adds the archived check to the one filter that owns it
⏵ Bash npm test → green, first try

One file, one edit, no re-discovery. Nothing about the request changed between the two transcripts - only the structure it landed on.

Write down only what structure can’t say

Section titled “Write down only what structure can’t say”

After the structural fixes, residue remains: the one folder whose name truly can’t explain itself, the team slang you didn’t rename toward, one recurring mistake the codebase keeps inviting. Those go in your rules file as narrow, example-bound notes - never as a paragraph of general advice:

## Lint: no-floating-promises in billing/ <- one error class, one folder
Bad - the promise is fired and forgotten; a failed charge disappears silently:
chargeCustomer(id) <- one bad example
Good - awaited, and the failure has somewhere to go:
await chargeCustomer(id).catch(err => Audit.write('charge.failed', { id, err }))
<- one good example
This class of error recurs in billing/ because most calls here used to be
synchronous. When you see a bare async call with no `await` or `.catch`,
fix it to the pattern above - don't just silence the lint rule.
<- one sentence on why it recurs HERE

A note this narrow is small enough to stay true and specific enough to change behavior; a general “write clean async code” line in the same file would do neither. Practitioners who’ve tried this report a real jump in fix rate on the exact error the note names - treat the shape of that result as trustworthy and any specific number you hear quoted as unverified until you measure it on your own repo.

Where this goes wrong: docs instead of structure. In any real repo the code is the freshest layer and prose about the code is the stalest, because keeping it current is nobody’s job - and an agent will trust a stale claim confidently, which is worse than no doc at all. Tell: a sprawling “how this codebase works” file, months old, describing a layout that moved.

Pass bar: every note covers one folder or one error class - nothing titled “how this codebase works.”

The probes assume one repo you own, with history to fail against. The invariant that survives every variation: they measure what the structure says to a reader with no tenure, whatever that structure is.

Starting conditionWhat you probe insteadWhat “pass” still means
Greenfield - nothing to fail against yetPaste the planned tree into a fresh session and ask probe 1’s question against it: “given this layout, where would you look for the code that decides which customers get billed?”The same bar, on paper. A structure that misdirects a cold reader in a proposal will misdirect one in code - and the rename costs nothing the week before the folders exist. This is the cheapest the audit will ever be
A monorepoEvery probe per package, plus one the single-repo version doesn’t need - the routing layer above them: “which package owns invoicing?”The same bars, package by package. Cross-package naming fails more often than in-package naming, because each team named their own package sensibly and nobody owned the map. A one-line-per-package manifest at the root - name, one sentence, spoken-word aliases - is the narrow-note discipline applied where monorepos actually lose agents
Code you can’t restructure - vendor SDK, generated code, a rename freezeThe same four asks, now measuring your workarounds: wire the navigation layer, write the glossary lines, then rerunThe same bars, no discount. If a fresh session still can’t converge with the workarounds in place, you’ve measured that the debt is real and unpatched - which is the evidence a restructure proposal needs anyway

The four asks are plain prompts - the same words work in any chat surface. What differs is how you get a session with no memory of the answer.

ToolHow to start cold
Claude Code/clear between probes, or a fresh subagent for the audit itself, so the main session can’t leak an answer the structure didn’t earn
CodexA new session, not /compact - compaction carries forward exactly the contamination the probes are designed to catch
Cursor / CopilotA new chat, not a continued one; the history panel makes it easy to confirm you’re actually starting cold
OpenCodeThe explore agent is a reasonable stand-in for probes 1 and 2, since it’s already scoped to read-only reconnaissance
PiA fresh session with no SYSTEM.md loaded - the probes measure what the repo says with no standing context helping it along

The navigation layer in probe 2 is client-agnostic: it wires into any MCP-capable tool the same way, so the recovery path never gates which tool you audit from.

Can rules files substitute for restructuring? They trade a one-time cost for rent. A rules line is loaded into every session forever and drifts from the code the day someone forgets to update it; a structural fix is paid once and can’t be out of date, because it is the code. Rules are for the residue structure genuinely can’t say - the moment a rules file starts reading like a map of the maze, the maze was the problem.

Can I automate the probes? Partly, and it’s worth doing once they pass reliably: the probes are plain prompts, so a headless run can ask probe 1’s question on a schedule and flag when the tool-call count crosses the bar. Treat that as a smoke alarm, not a gate - the judgment (“is that where a teammate would say it lives?”) still needs you. The manual audit finds the seam; the automated version tells you when it reopens.

The team disagrees about where things should live. Who wins? Run the probe with each side’s phrasing - the structure that lets a cold session converge in two greps wins, whoever proposed it. That’s the quiet gift of this audit: “where should this live” stops being a taste argument between senior people and becomes a measurement either of them can rerun. Legibility is checkable; taste isn’t.

The four asks live with their pass bars in the probes - copy them from there, swapping your team’s own words into the brackets. What’s left has no other home:

Baseline score, before the manual probes, to help pick the seam:

Terminal window
$ npx github:microsoft/agentrc readiness # verified mid-2026

Recovery layer, when the restructure isn’t feasible yet:

Terminal window
$ uv tool install -p 3.13 serena-agent && serena init # verified mid-2026

The narrow rules-note template - one error class, never an essay:

## <Lint or mistake name> in <folder>/
Bad: <one real bad example>
Good: <one real good example>
Why it recurs here: <one sentence>. When you see it, fix to the
pattern above - don't just silence the warning.

Rerun it: legibility as a regression suite

Section titled “Rerun it: legibility as a regression suite”

A structure that passed once decays the way a rules file goes stale - a new file dropped into the domain folder without a clear name, a fast-follow PR that reintroduces a shared helper. So don’t file the probes as a one-time audit. Rerun them after any merge that touches the seam, before handing the area to a new teammate or a fresh agent session, and on a rolling cadence if it sees steady churn.

Four prompts and a few minutes turn that decay into a failed probe today, instead of next month’s version of the transcript you started with. Every fix underneath them is prepaid context - the folder that explains itself, the boundary that predicts its own blast radius, the suite that answers “does it work?” - inherited for free by every future session: yours, a teammate’s, an agent’s, without anyone having to re-explain it.

Which leaves the one habit worth keeping past this page. Legibility is the rare codebase quality you can’t assess by looking, because you already know where everything is - your own familiarity is precisely the instrument that can’t measure it. So don’t assess it. Ask a session that knows nothing, in your team’s own words, and count. More than two greps to find where billing is decided and the seam has decayed again, whatever the folder structure looks like to you. That number is the check that can say no, and it is the only opinion in this whole play that isn’t yours.