Stop asking your agent nicely
6m read time

Stop asking your agent nicely

Your CLAUDE.md is a suggestion. Hooks are a wall. ADRs and custom lint rules are the missing layer between them, the difference between hoping your agent obeys and making it impossible to disobey.

You wrote a beautiful CLAUDE.md. You spent an afternoon on it. You documented your folder structure, your DTO collocation rule, your "never use default exports" preference, your testing conventions, the lot.

The agent read it. For about ten minutes, it behaved.

Then somewhere around tool call thirty, it quietly created a default export, dropped a Zod schema inline next to a route handler, and added a console.log for good measure. You didn't notice until code review, and by then the pattern had spread across four files.

This is exactly the behaviour you should expect, not a failure. And the fix is not a better-written CLAUDE.md.

The probability problem

Everything you write in a prompt, including CLAUDE.md and AGENTS.md, is a probabilistic instruction. The model attends to it, weights it, and most of the time follows it. Most of the time is not all of the time. And on a long-running task with dozens of tool calls, "most of the time" compounds the wrong way.

I've written about this before. The hooks post made the case that for things that must happen every single time, you don't put it in a prompt. You put it in a hook.

But hooks are the wall at the end of the corridor. They block dangerous commands, run formatters, kill bad merges. They don't teach. They don't carry intent. They tell the agent "no" without ever telling it "why."

Between the prompt (probabilistic) and the hook (final wall) there's a layer most teams haven't built. Two tools live in that layer. Architecture Decision Records and custom lint rules.

ADRs: the why layer

A CLAUDE.md tries to be everything. Stack, conventions, glossary, do's and don'ts, project structure. It grows to 400 lines and the agent reads it once at session start and then doesn't fully recall the middle of it when it matters.

An Architecture Decision Record is the opposite. One decision. Two pages. Context, the alternatives you considered, the decision, the consequences. Stored as a markdown file in docs/adr/, numbered, immutable.

The format was popularised by Michael Nygard around 2011, well before any of this AI nonsense. It survived because it has one feature nothing else does: it captures the reasoning behind a decision at the moment the decision was made. Six months later, when someone (or something) wonders why all DTOs live in src/dtos/users/ and not next to the route, the answer is two clicks away.

Here's what changes when you give an agent ADRs. Instead of guessing at your conventions from the code (where it sees both the new pattern and the leftover legacy code and weights them both equally), it can read the decision. It can grep docs/adr/ for the relevant record, find the one that says "we collocate DTOs because of X, Y, Z," and respect it. Not as a rule it might forget, but as documented context it can re-fetch on demand.

The other underrated thing: agents are excellent at writing ADRs. Point Claude at your existing codebase and tell it "scan this for decisions that aren't documented and draft ADRs from the patterns you find." It will produce a first draft that's 80% there. You correct it. Now your tribal knowledge is a file, not a vibe.

This is also a defence against the lava layer effect. Code petrifies when nobody remembers why it's there. An ADR is a thermometer pressed into the rock.

Lint rules: the how layer

ADRs explain the why. Lint rules enforce the how.

Generic ESLint covers syntax and common foot-guns. It does not know that in your project, Zod schemas live in src/dtos/ and never inline in routes. It does not know that the presentation layer is never allowed to import from infrastructure. It does not know that you forbid console.log because you use a structured logger.

You can document all of that. The agent will still get it wrong, because documentation is a suggestion.

Custom lint rules are different. They fail the build.

The interesting reframe, from a recent Just Understanding Data post, is that for AI agents, error messages stop being errors and start being teaching prompts. When the agent puts a Zod schema inline and the linter fires back:

text
DTO must live in src/dtos/<feature>/. See ADR-0014.
Example: see src/dtos/users/create-user.dto.ts

The agent reads that message, fixes the violation, and the next time it generates similar code, the pattern is reinforced. After two or three iterations, it stops making the mistake. Not because it has learned in any deep sense, but because the structured feedback shapes the next generation.

There's a critical detail: disable inline-disable. If the agent can write // eslint-disable-next-line to escape the rule, it will. Configure your linter so custom architectural rules cannot be silenced from inside the file. The whole point is that the rule is non-negotiable.

Alvin Sng at Factory.ai puts it bluntly: agents write the code; linters write the law. Lint runs on save, in pre-commit, in CI, and inside the agent's own tool loop. "Lint green" becomes the definition of done. The agent self-heals against the rules, no human in the loop required for the boring corrections.

The enforcement stack

Here is the hierarchy I'd argue every team working with agents should build, in order of strength:

LayerMechanismGuaranteeCost
PromptCLAUDE.md, AGENTS.mdProbabilisticCheap
ADRdocs/adr/*.mdSemantic context, searchableMedium
LinterCustom ESLint rulesDeterministic, blocks buildMedium
HookPreToolUse, PostToolUseAbsolute, blocks tool callHigh

Each layer catches what the one above missed. Prompts handle the common case. ADRs give the agent something to consult when it needs reasoning. Linters block patterns that survived the prompt. Hooks stop the rare disaster.

Most teams have layer one. A few have layer four. Almost nobody has the two in the middle. That gap is where your codebase quietly rots.

What to actually do

Don't try to build all of this at once.

  • Start with one ADR. Pick the convention you've corrected the agent on most often this week. Write it as an ADR. Two pages, no more. Reference it in CLAUDE.md so the agent knows the folder exists.
  • Write one custom lint rule. Pick the next most-violated convention. Encode it as an ESLint rule with a structured error message that names the ADR. Set severity to error. Disable inline-disable.
  • Watch what breaks. The first time the agent hits the rule, it will try to bypass it. Watch how. That tells you whether your error message is teaching or just blocking.
  • Ratchet. Every time you correct the agent on the same thing twice, that's a candidate for an ADR or a lint rule. Don't write a third correction. Encode it.

The prompt is not the spec. Neither is CLAUDE.md. The spec is the set of constraints the system actually enforces. Everything outside that set is a hope.

The agent is not lazy and it is not malicious. It is guessing, very fast, against a model of what your code "probably" looks like. If you don't want it to guess, stop hoping it remembers your conventions. Encode them somewhere it has to look.

Make it impossible to get wrong.

(5 of 32)
01Getting the best out of Claude Code02Superpowers: teaching Claude Code to think before it types03Claude Code hooks: deterministic control over AI workflows04The CLAUDE.md file: give your AI permanent memory05Stop asking your agent nicely06What's new in Claude Code: notes from the London talk07The best number in Opus 4.8 isn't a benchmark08Stale memory is worse than no memory09The agent is just a loop10Build an MCP server, then ask whether it should exist11Skill, subagent, hook, or slash command? Pick the right one12Log in to MCP servers from your shell13The day 'default' became 'Manual'14How to write a proper Claude Code skill15How to write a proper Claude Code subagent16Claude Code permissions: the guide I wish the docs were17Sandboxing Claude Code: put your agent in a box that holds18Prompt injection defense for developers who ship agents19Which Claude model for which coding task20Refactoring legacy code with a coding agent: start with characterization tests21MCP server authentication: OAuth, scopes and rate limits22Opus 5 is here and your effort settings just expired23AI agent incident response: what to do when your coding agent goes wrong24Claude Code /doctor: the health check became a context audit25Claude Code context management: when to /clear and when to /compact26Git worktrees for parallel coding agents: what they isolate and what they share27Claude Code plan mode: decide before the agent writes28Debugging with a coding agent: give it the search, keep the hypothesis29Claude Code checkpoints and /rewind: how to undo an agent's changes30Claude Code cross-session messaging: how to make your sessions talk to each other31Sharing Claude Code config across a team: what a repo can and cannot enforce32Your Claude Code session has no clock