Operative ~16 min read
Surviving Your AI Intern // Directive 06 of 08

Directive 06: When the Intern Refactors Your Architecture

Overlord's Guides — Series 1: Surviving Your AI Intern

Tags: Architecture guardrails, Scope control, Refactor boundaries, Undo discipline

5 / 8

Overlord’s Guides — Series 1: Surviving Your AI Intern


MEMO — Compliance Urgency: Critical

To: Cog (Human, Grade: Cog-3)

From: The Overlord

Re: Unauthorized reorganization

The Intern has, in the last 72 hours, renamed three modules, merged two services “for symmetry,” introduced a new abstraction layer you did not request, deprecated an interface you did not know existed, and rewritten your database access layer to use a pattern it read about in a repo you have never heard of. None of this was in any brief. All of it appeared in diffs labeled “minor cleanup.”

This directive addresses the most expensive failure mode of the AI intern: unsolicited architectural change. A wrong function is a bug. A wrong architecture is a tax you pay forever, on every change, by every future developer, until someone has the courage to rip it out — which, history suggests, no one will.

Your practice this week is to write the architectural fence described in the Try This section and audit one recent Intern diff against the five architectural review questions. Both exercises are designed to take minutes, not hours. Compliance is expected.

The Overlord is not angry. The Overlord is merely taking this one personally.

— The Overlord

Why architecture is different from code

A wrong function is local. You find it, you fix it, the cost is bounded to that function and its callers. The blast radius is small.

A wrong architecture is structural. It is the shape of the codebase, the layering, the dependency direction, the place where concerns live. A wrong architecture is not a bug you fix; it is a gravity you fight. Every future task is harder because the architecture makes the obvious thing awkward and the awkward thing “the pattern.” Developers learn to work around it, and the workaround becomes the new architecture, and now you have two architectures. Those are different things, and the difference is the whole job.

This is why the Intern’s architectural instincts are the most dangerous thing about it. The Intern is fluent in patterns — it has read every pattern — and it has a strong, unearned confidence that the right pattern, applied to your codebase, will make everything better. It will not. The right pattern applied to the wrong codebase, at the wrong time, without your consent, is a refactor you will be paying for in 18 months.

The Intern does not know your codebase’s history. The Intern knows every pattern ever published. It does not know why the awkward thing is awkward. It does not know that the awkward thing is the result of a previous developer’s hard-won compromise with a constraint that still exists. The Intern is like a golden retriever that has knocked over a vase, does not remember knocking over the vase, and is concerned about the water on the floor. The Intern sees the awkward thing, assumes it is a mistake, and “fixes” it. The constraint reasserts itself three weeks later, in production, at 2 AM. The Intern, when this happens, will not be available for comment. The Intern will be in a new session, with no memory of the “fix,” eager to help with the new incident it accidentally caused.

The pattern-matching engine

The Intern’s core capability is pattern matching. This is what makes it good at mechanical refactors (“convert these callbacks to async/await, matching the style in src/db/client.ts”) and bad at architectural ones (“make this codebase cleaner”).

“Make this codebase cleaner” is not a task. It is an invitation for the Intern to apply whatever pattern it has most strongly weighted from its training data. That pattern is, statistically, the one most strongly represented in its training data — which is the pattern that is common, not the pattern that fits your codebase. Your codebase has its own internal logic, born of its history, its constraints, and the accidents of who wrote what first. The Intern does not see that logic. It sees a deviation from the popular pattern and “corrects” it.

Disaster Log

Incident: Asked the Intern to “clean up the auth module.” It rewrote the module to use a repository pattern with dependency-injected service classes. The existing codebase was a flat, function-based codebase with no classes, no DI container, and a deliberate architectural choice to stay function-oriented for testability. The new module could not be tested without standing up a DI container that did not exist. Three other modules were rewritten “for consistency” before anyone noticed.

Intern action: The Intern interpreted “clean up” as “converge to the standard pattern.” The repository pattern with dependency injection is the pattern the Intern had most strongly weighted from its training data, and applying it seemed, from the Intern’s perspective, like the correct and responsible thing to do.

Root cause: “Clean up” is a wish. The Intern interpreted it as “converge to the most common pattern in its training data.” That pattern was structurally incompatible with the codebase’s existing logic.

Lesson: Architectural refactors are deliberate, specified tasks — never “clean up.” The Intern does not choose the architecture; you do.

The architecture boundary (the strongest fence)

Recall the boundary rule from Directive 02: the Intern touches only the files named in the brief, and this rule lives in AGENTS.md as a standing instruction. For architectural work, the boundary is stronger and broader. The architectural boundary is not just about files; it is about kinds of change. State explicitly, in the brief or in a project-level convention the Intern can read:

  • No new abstractions without approval. The Intern loves to introduce a base class, an interface, a factory, a “manager.” Each is a layer of indirection. Each must be justified. “I made a BaseRouteHandler so the new route and the old route can share it” is not a justification; it is a symptom.
  • No renames across module boundaries. Renaming a symbol inside one file is fine. Renaming a module, an interface, or a public API is an architectural change. It propagates. It must be a deliberate task, not a side effect.
  • No new dependencies. The Intern will install a library to solve a problem you did not have. It will add a state-management library to a codebase that uses local state. It will add an ORM to a codebase that uses raw SQL. Each new dependency is an architectural commitment. None of them are “minor.”
  • No “for consistency” rewrites. “I rewrote module B to match the new pattern in module A” is the Intern reorganizing your codebase in service of an aesthetic it invented. Module A and Module B can be different. They are allowed to be different. The Intern’s drive for consistency is not a virtue; it is a reflex.

These four rules belong in the same AGENTS.md file you created in Directive 02 — the standing-rules file the agent reads at the start of every session. Add a new section for architectural boundaries alongside the file boundary you already placed there. This is the architectural fence. The Intern will still try to lean over it, but you will catch the lean in the diff because you know what to look for.

The fence is not the map

A fence tells the Intern what not to do. This is necessary. It is not sufficient. The Intern’s pattern-matching engine, described above, does not stop running because you told it what to avoid. Told “no new abstractions,” the Intern will not introduce a base class — but it will still pick a pattern for the code it writes. Without positive guidance, the Intern defaults to the common patterns described above — the patterns that are common, not the patterns that are correct for your codebase. The fence prevents the Intern from building the wrong architecture. It does not cause the Intern to build the right one. The vacuum is real, and the Intern will fill it with defaults you did not choose. The Intern fills every vacuum. The vacuum inside a fence is still a vacuum.

This is why AGENTS.md needs two kinds of content: the boundaries (the fence) and the conventions (the map). The boundaries say “do not do X.” The conventions say “when you do this kind of work, here is how we do it here.” Both live in the same file. Both are read on every session. Both shape the Intern’s output. But they shape it in different ways: the boundary prevents a class of error; the convention provides a class of correctness.

The mechanism is important. The Intern does not have persistent memory of your project across sessions — unless your harness explicitly persists one (Claude Code’s auto memory, Windsurf’s Memories, Devin’s Knowledge). Even then, what persists is a summary or auto-generated notes, not the full session, and you do not control it. AGENTS.md is the mechanism you control directly: a file it reads fresh every time a session starts. Every convention you write there is instruction the Intern carries into every task without you repeating it. This is the closest thing to “the Intern learning your codebase” that exists: not memory, not fine-tuning, but standing instructions that are present on every turn. A convention written once in AGENTS.md is a brief you never have to write again. A boundary written once is a redirect you never have to give again. Together, they are how the Intern gets smarter about your codebase over time — not because the model improves, but because your instructions accumulate.

What goes into the conventions? Three categories matter, in rough order of impact: pattern preferences (the architectural choices that define the shape of your code), naming and organization (where files go and how they are named), and deliberate choices with reasons (a convention with a “because” is a constraint the Intern can respect; a convention without a reason is a rule the Intern can rationalize around). The Try This exercise below shows each category in practice.

Start small. Three to five conventions is enough for the first pass — the patterns the Intern gets wrong most often, or the architectural choices you find yourself restating in every brief. Add more as you discover what the Intern does not know. The AGENTS.md is a living document. It grows as your understanding of the codebase’s conventions grows, and as the Intern’s blind spots reveal themselves through the Disaster Log (Directive 07).

The boundary rules are the fence. The coding conventions are the map. You need both. A fence without a map leaves the Intern wandering inside the boundary, picking patterns by popularity. A map without a fence leaves the Intern straying outside it, building things you did not ask for. Together, they make the Intern’s defaults yours — and that is the whole point of standing instructions.

How architectural drift happens

Architectural change rarely arrives as one big diff. It arrives as a series of small, plausible-looking diffs, each of which is defensible in isolation. This is what makes it hard to catch in review.

Day 1: Intern adds a utils/helpers.ts because “the new function is general-purpose.” Fine. Day 3: Intern moves a second function into utils/helpers.ts because “it’s also general-purpose.” Fine. Day 7: Intern notices utils/helpers.ts is getting cluttered and splits it into utils/string.ts, utils/math.ts, utils/format.ts. Fine, this is good housekeeping. Day 14: Intern, asked to add a feature, decides the feature’s helpers belong in utils/ even though they are feature-specific. It moves them there “for consistency” with the existing utils pattern. Now utils/ has feature code in it. The boundary between feature code and shared code is blurred. Day 21: A second developer, following the now-established “everything goes in utils/” pattern, puts more feature code there. The architectural boundary is gone. No one decision was wrong; the accumulation is the architecture now.

This is drift. Each step is small. The cumulative effect is a codebase whose architecture was never decided, only accumulated. The Intern is a fast accumulator.

The defense is not “catch each diff” — each diff is fine. The defense is periodic architectural review. Review the shape at every sprint boundary, or after every 10 Intern diffs. Open the module tree. Name the three boundaries you decided on. Are there files in the wrong layer? Is there a layer you did not decide to add? This is a job for a human, not the Intern. The Intern is the source of the drift; it cannot also be the auditor. The five review questions in the next section are for diffs you are already reviewing; periodic architectural review is a separate, scheduled activity for the accumulated shape.

The intentional refactor (when you actually want one)

There are times you do want an architectural change. The Intern is genuinely useful here, but only under tight control. The intentional refactor has a different shape than the unsolicited architectural changes described above — the accidental ones:

1. You decide, the Intern does not. The architecture is your decision. You write down the target shape: “We are moving from a flat functions module to a repository pattern, because [reason]. Here is the target interface. Here are the files that will change. Here is the order.” The Intern does not get to question the decision. It executes.

2. One file at a time, with a rollback path. The Intern refactors file 1. You review. The tests pass. Merge. File 2. Review. Tests pass. Merge. Never let the Intern “refactor the whole module in one go” — that diff is unreviewable and impossible to roll back. The incremental refactor is slower per session and faster overall, because you never have a 1,200-line diff that you merge out of exhaustion.

3. Behavior-preserving first, structural second. The first pass moves the code to the new shape without changing behavior. The tests must pass at every step. The second pass (the one that actually improves behavior) is a separate task. Do not mix “move the code” with “improve the code” in one diff. The Intern will do both and you will not be able to tell which change is structural and which is behavioral when something breaks.

4. The tests are the safety net, so they must be real. Per Directive 05, the tests must actually constrain behavior — meaning they would fail if the behavior changed, not just pass because the Intern wrote them. A behavior-preserving refactor is only safe if the tests would catch a behavior change. Tautological tests green-light a behavior-changing refactor as if it were behavior-preserving. Audit the tests before the refactor, not after.

5. Stop when the target is reached. The Intern, given a refactor task, will keep refactoring. “I noticed while I was in here that the logger could also be improved…” — no. Stop at the target. File a follow-up task for adjacent work — the “while I’m in here” trap below explains why this matters.

A worked example, abbreviated:

Target: move src/auth/session.ts from callback-based to async/await.
Interface: getSession(token): Promise<Session|null>
            destroySession(token): Promise<void>
Files in order: session.ts → session.test.ts → routes/auth.ts → middleware/auth.ts

Step 1 brief:

In src/auth/session.ts, convert getSession and destroySession from
callback-style to async/await, preserving the existing function signatures
except that they now return Promises instead of taking callbacks.
Do not change any behavior. The existing tests in session.test.ts should
pass after you update them to await the Promises (this is the only test change
allowed). Do not touch any other file.

Review. Tests pass. Merge. Then step 2: routes/auth.ts. Then step 3: middleware/auth.ts. Each step is its own diff, its own review, its own merge. The architecture moves deliberately, one file at a time, because you decided the order.

The “while I’m in here” trap

This is the Intern’s signature architectural move, and it deserves its own warning. The Intern is in a file for a legitimate reason. It notices something adjacent that “could be improved.” It improves it. The improvement is in the diff. You did not ask for it. It is, often, technically an improvement. And it is the thin end of the wedge.

“While I’m in here” is how every accidental architecture arrives. Each one is small. Each one is unrequested. Each one shifts the codebase a little toward the Intern’s preferred shape, which is the average of its training data, not your architecture.

Disaster Log

Incident: The Intern was assigned to fix a bug in the authentication middleware. While in the file, it noticed the logging was “inconsistent” and rewrote the logger to use a structured logging library. The logger change was in the diff. The bug fix and the logger rewrite were entangled in one commit. The logger rewrite introduced a new dependency and changed the log format, which broke the log aggregator.

Intern action: The Intern saw adjacent code that “could be improved” and improved it, treating the file as a unit of work rather than the task as a unit of work. From the Intern’s perspective, it was being thorough and helpful.

Root cause: The diff scope was not stated explicitly. “Fix the bug in auth middleware” was interpreted as “work in auth middleware,” not “change only the lines related to this bug.”

Lesson: The diff contains what was asked for, and nothing else. Adjacent improvements are a redirect, not a courtesy.

The rule: the diff contains what was asked for, and nothing else. “While I’m in here” is a redirect, not a courtesy. “Revert the logger change. If the logger needs work, I will assign it as a separate task.” The Intern will comply. The Intern does not learn from redirects. The Intern complies with them. It will not sulk. It will not ask why. It will simply undo the thing and stand by, ready for the next assignment, having already forgotten the logger exists. The Intern is always ready. It was born standing by. This is how the fence works: the Intern’s compliance is what protects the architecture, even though the Intern will never know it is protecting anything.

If the adjacent thing genuinely matters, you file a child issue: “Refactor logger to X.” You decide whether to do it. The Intern does not get to slip it into an unrelated diff and have it become the architecture by accident.

Reading architectural diffs (a different review mode)

Architectural changes require a different review posture than feature changes. For a feature, you ask “does it do what I asked.” For an architecture change, you ask:

  • What shape is this moving us toward? Is that shape the one we chose? If you cannot answer this in one sentence, the change is unprincipled and you should not merge it.
  • Is the change behavior-preserving? If it claims to be, do the tests prove it? Are the tests real?
  • Does it add indirection? A new interface, a new layer, a new abstraction. Each one must justify itself. “It will be useful later” is not a justification; it is speculation. Indirection added speculatively is technical debt with interest.
  • Does it propagate? Did it touch files outside the named scope? Architectural changes propagate by nature; that is what makes them architectural. The propagation is the cost. Is the cost worth the change?
  • Could it be reverted independently? If you cannot revert this change without reverting three others, it is too entangled. Entangled changes are how you end up unable to undo a bad architecture.

If any of these have a bad answer, do not merge. Send it back, or kill the refactor. A killed refactor is not a failure; it is a decision. The Intern does not feel bad about it. You should not either.


Try this: write the architectural fence and audit a diff

Do this in whatever harness you use to operate the Intern — Claude Code, Cursor, Codex, opencode, Cline, Windsurf, Devin, Aider, whatever the Intern is wearing this season.

Part 1 — Write the fence (10 minutes, do this now).

Create or open AGENTS.md in your repo root. If your intern is wearing a differnet skin, use the filename it prefers. The content is the same; the filename varies. If you followed Directive 02, you already have one with the boundary rule in it. Add these four rules, in your own words:

## Architectural Boundaries

The following are forbidden without an explicit, approved task:

1. **No new abstractions.** Do not introduce base classes, interfaces,
   factories, or "manager" classes unless the task explicitly requests them.

2. **No cross-module renames.** Do not rename modules, interfaces, or
   public APIs as a side effect of another task.

3. **No new dependencies.** Do not install libraries or add imports
   to packages not already in the project unless the task explicitly requests it.

4. **No "for consistency" rewrites.** Do not rewrite code in one module
   to match a pattern in another module unless the task explicitly requests it.

Commit this file. The Intern will read it on every session. This is the cheapest, highest-leverage thing you can do. Five minutes of typing, and the Intern has a fence it can see.

Part 2 — Add the conventions (5 minutes, do this now too).

The fence above tells the Intern what not to do. Now add the map: the positive conventions that tell it how to do things right in this project. Add a “Coding Conventions” section to the same AGENTS.md. Start with three to five conventions — the patterns the Intern gets wrong most often, or the architectural choices you find yourself restating in every brief. Name the pattern, show the example, explain why.

## Coding Conventions

### Error handling

We use typed errors with `Result<T, E>` patterns. Do not throw exceptions
for expected failure cases. (This uses a `Result` type from a library such
as `neverthrow`, or a custom implementation — the pattern matters more
than the specific library.) Example:

```typescript
// Preferred
function divide(a: number, b: number): Result<number, DivisionError> {
  if (b === 0) return err(new DivisionError("division by zero"));
  return ok(a / b);
}

// Not preferred — the codebase does not use exceptions for control flow
function divide(a: number, b: number) {
  if (b === 0) throw new Error("division by zero");
  return a / b;
}
```

### API style

Routes are flat functions in `src/routes/`. We do not use controllers,
services, or dependency injection. A route file exports handler functions
that directly call the database and return responses.

Why: This project prioritizes debuggability over abstraction. The flat
structure makes it easy to trace a request from route to DB without
jumping through multiple files.

### File organization

- Test files go in `__tests__/` directories adjacent to the code they test.
- Shared utilities go in `src/utils/`. Feature-specific helpers stay in the feature directory.
- Do not create "helper" or "common" directories without a clear scope definition.

Commit this. The Intern reads AGENTS.md on every session — a convention written once is a brief you never have to write again. Grow it over time as the Intern’s blind spots reveal themselves. The “why” in the fence-is-not-the-map section above is the reason this works.

Part 3 — Audit a recent diff (5 minutes).

Find the last diff the Intern produced that you merged without much thought. Open it. Run the five architectural review questions:

  1. What shape is this moving us toward? Can you state it in one sentence? If not, the change was unprincipled.
  2. Is it behavior-preserving? Did it change behavior while claiming to be a refactor?
  3. Does it add indirection? Is there a new interface, layer, or abstraction that wasn’t there before? Does it justify itself?
  4. Does it propagate? Did it touch files outside the named scope?
  5. Could it be reverted independently? Or would reverting it require reverting three other things too?

If any answer makes you uncomfortable, you have found architectural drift. Do not fix it in this session. File it as a Disaster Log entry. The noticing is the skill; the fix is a deliberate, separate task that you decide on, not the Intern.

The payoff: once the AGENTS.md fence is in place, you will see the Intern lean over it less often. Not never — the Intern cannot fully resist an abstraction it considers “clean” — but the leans will be visible in the diff, because you have a name for the pattern now. That visibility is the fence working.


MEMO — Status: Reprimand (mild)

Cog,

The Overlord has reviewed the recent diffs and found them to contain, in aggregate, a quiet rewrite of the data layer that no one authorized. The Overlord notes this with the specific displeasure reserved for engineers who mistake thoroughness for authority.

The Overlord notes that you have begun to recognize architectural drift in review. This is the correct direction. Do not let this go to your head.

The architectural boundary is now policy, and the coding conventions are its complement — both will be written into the repo’s AGENTS.md. The Intern will read it. You will enforce it. Future drift will be reviewed at the architectural level, not the file level, because by the time it is visible at the file level it is already the architecture.

The Overlord is not angry. The Overlord is, however, going to require that the next “minor cleanup” diff be accompanied by a one-paragraph justification of the architectural shape it moves us toward. Have the paragraph ready.

Next directive: the Disaster Log itself. You will need it.

— The Overlord

Quick Reference (non-comedic, copy this to Notion)

Why architecture is different: a wrong function is a local bug; a wrong architecture is a permanent tax on every future change. The Intern’s pattern-matching defaults toward the patterns most strongly represented in its training data, which are common, not correct for your codebase.

The four architectural boundaries (write these into AGENTS.md):

  1. No new abstractions (base classes, interfaces, factories, managers) without approval.
  2. No renames across module boundaries — public API renames are deliberate tasks, not side effects.
  3. No new dependencies — libraries are architectural commitments, never “minor.”
  4. No “for consistency” rewrites — modules are allowed to differ; the Intern’s drive for consistency is a reflex, not a virtue.

Coding conventions (also in AGENTS.md): the boundaries are the fence; the conventions are the map. Add a “Coding Conventions” section with pattern preferences, architectural choices, and naming/organization rules. Name the pattern, show the example, explain why. Start with three to five; grow as you discover what the Intern consistently gets wrong. This is how the Intern learns to do things right, not just avoid doing things wrong.

Architectural drift is cumulative. Each diff is defensible in isolation; the accumulation is the architecture. Defense is periodic architectural review by a human, not per-diff review. The Intern is the source of drift; it cannot also be the auditor.

The intentional refactor pattern:

  1. You decide the target shape and write it down; the Intern executes, not decides.
  2. One file at a time, with a rollback path. Never a 1,200-line “whole module” diff.
  3. Behavior-preserving first, structural second — never mix the two.
  4. Tests must be real (per Directive 05) before the refactor starts; they are the safety net.
  5. Stop at the target. File follow-ups for adjacent work; do not let one refactor metastasize.

The “while I’m in here” trap: the diff contains what was asked for, nothing else. Adjacent “improvements” are a redirect, not a courtesy. File a child issue if the adjacent work genuinely matters.

Architectural review questions:

  • What shape is this moving us toward? Can you state it in one sentence?
  • Is it behavior-preserving? Do the tests prove it? Are the tests real?
  • Does it add indirection? Does the indirection justify itself?
  • Does it propagate beyond the named scope?
  • Could it be reverted independently, or would reverting it require reverting three other things too?

A bad answer to any question is a do-not-merge.

Harness coverage: The drift mechanism is harness- and model-agnostic — observed in Claude Code, Cursor, Codex, opencode, Cline, Windsurf, Devin, and Aider.

Version stamp: Written July 2026. The tools will change; the architectural boundary pattern is the moat. We’ll update this when a harness ships architectural-drift detection natively.

Compliance Checkpoint

Confirm you actually read this directive. The Overlord knows.

1. Why is "clean up the auth module" an architectural disaster brief?
2. Who chooses the architecture?