Back to blog

Your agent instructions are describing, not prescribing

April 13, 2026 · Mark Freedman & William VanBuskirk

Everyone's writing agent instructions now. System prompts, CLAUDE.md files, cursor rules, AGENTS.md, custom GPT instructions, prompt templates. Almost all of it is the same kind of document: a description of the world the agent operates in.

That's the wrong document.

Your agent can read code. It can search files. It can grep for patterns and infer conventions from context. Anything descriptive (what exists, how things are structured, what the stack looks like) the agent can figure out on its own. You're writing a tour guide for something that can already read the map.

The document your agent needs is the one it can't derive from the code: your intent. What you decided and why. What's never allowed. What must always be true. The decisions that live in your head and nowhere else, until you write them down as laws.

Describing tells the agent what is. Prescribing tells the agent what must be. That's the gap between agents that help and agents that drift.

The bug you already fixed, fixed again

My co-founder Mark builds our backend (mirror-harness, an agent orchestration layer). Heavy brownfield, tens of thousands of lines, cross-cutting concerns everywhere. A few weeks ago he reviewed a long list of findings an agent had produced after scanning the repo. Forty items, ranked by severity.

Half were noise: the agent had re-derived conventions from scratch, proposed refactors that already existed, flagged "bugs" that were intentional. Standard stuff.

The other half was the real failure. The agent kept "discovering" problems Mark had already solved months ago. Permission checks bypassing the artifact gateway. Direct Firestore writes instead of the write pipeline. Cache invalidation where there should be cache-fresh writes. Every time the agent hit a shared concern, it invented a new solution, because in isolation, inventing looked like the right move.

Mark's take: "It's like trying to plug a hole in a ship. The agent's gonna try and fix that permission issue by reinventing the permission situation when in reality, the system is designed correctly. The agent just forgot to check the gateway."

The decisions were in Mark's head. The code carried the results. Nobody had written down the rules in a form that said: this is the law, and if the code doesn't match, the code is wrong.

What descriptive looks like

A descriptive doc answers: "What does this look like?"

  • CLAUDE.md describing your tech stack, file layout, naming conventions
  • README explaining how to run the project
  • System prompts telling the agent "you are a helpful assistant that works with our React + FastAPI stack"
  • Cursor rules saying "here's the component style we use in this folder"
  • Custom GPT instructions listing the tools available and the domain you operate in

Useful for orientation. The fatal flaw: descriptive docs describe the current state, never the intended one. If the code drifts, the description drifts with it. A descriptive doc never contradicts the code, so it can never catch the code being wrong. And an agent can produce one itself. Point it at the repo and ask "what's the architecture here?" You're authoring the thing the model is already good at.

A prescriptive doc answers: "What must always be true, and what is never allowed?"

  • "All LLM provider calls go through common/llm_gateway/. No direct genai.Client() calls anywhere else in the codebase."
  • "Artifacts are immutable after creation. If you need different data, create a new artifact. Never update in place."
  • "Secrets are passed into function sandboxes via environment injection. They are never printed, logged, or returned to the user."

These are invariants. Laws the code must follow. They live in your head: the decisions you made on purpose, the traps you already fell into, the architecture you chose. Writing them down externalizes your expertise into a form an agent can audit against.

Caller or contract

Once you have a prescriptive doc, everything simplifies. Mark's framing: any piece of code that touches a shared concern is a caller. Every caller is in exactly one of two states:

  1. Following the contract. Ship it.
  2. Breaking the contract. Either the code is wrong and needs to be fixed, or the contract is wrong and needs to be updated, and every other caller updated with it, in the same PR.

No third option. No "this case is special." No "we'll clean it up later." No "but the client is right there, it's simpler."

"Caller or contract?" One question, two answers. Binary. When you apply that question to every diff that touches a shared concern, drift has nowhere to hide.

The same binary works above the codebase. System prompt says the agent must always cite sources before making claims? Every response either cites sources or violates the contract. Agent instructions say all database writes go through the ORM? Every raw SQL call is a contract violation, full stop. Every diff resolves to one of two states.

The currency that scales

Mark's workflow once a contract violation is suspected:

  1. Open the prescriptive doc.
  2. Spin up a sub-agent: "Read ARTIFACT_GATEWAY.md. Find every caller in the codebase that violates an invariant from that file. Ranked list. No fixes."
  3. Sub-agent returns 8 findings. Three real, five noise.
  4. Spin up three more sub-agents in parallel: WebSocket layer, resolver layer, tool handlers. Same contract, different slice.
  5. Merge findings, pick the real ones, add to the plan.

This works because the prescriptive doc is a currency of intent. Mark's words: "It's just a currency to exchange the principle without having to re-explain it. It's just documentation, you know?" Documentation that carries decision authority. Hand a sub-agent a link, and every agent who reads it inherits the same laws.

A descriptive CLAUDE.md doesn't work as currency. It's a map. There's nothing to violate. A prescriptive contract is a constitution: you can audit any code against it and get a binary answer.

This is also why folder-scoped rules (cursor rules per directory, CLAUDE.md per module) don't catch the drift that hurts most. Artifact permissions in our codebase fire in the WebSocket layer, the HTTP API, the background poller, the chat handler, the tool dispatcher, the resolver, the caching layer. Ten folders. A folder-scoped rule says "if you're in this folder, do this." It can't say "if you're doing this thing, do this, no matter where you are." The contract has to be addressable by the name of the concern. The file path can't carry it.

The drift that looks reasonable

Each instance of drift looks fine in isolation. An engineer (or agent) sees a permission bug. The gateway is two folders away. The client is right here. "I'll just fix it locally, faster."

Six months later, forty places handle permissions differently. None agree on what a viewer is. A permission change that should be a single-line gateway edit is now a forty-file refactor. The agent asked to fix it generates forty patches, in forty slightly different ways.

That's where people blame the LLM. So inconsistent. The agent is being perfectly consistent. It reads the code and the descriptive docs, which say "there are forty different ways to check permissions here," and mirrors the drift faithfully. The drift was introduced by humans without a contract to point at.

Same pattern outside code. A support agent whose instructions describe the refund policy ("we typically offer refunds within 30 days") interprets "typically" differently in every edge case. A support agent whose instructions prescribe ("refunds are issued for requests within 30 days of purchase. No exceptions without manager approval.") gives you a binary to audit against. The fix is to write the contract down.

The autonomy ceiling

Here's why this matters beyond drift prevention.

The number of well-defined prescriptive contracts in your system sets the ceiling on how much autonomy you can grant your agents. Two contracts, agents operate safely on two concerns. Ten contracts, ten safe concerns. Fifty contracts covering every cross-cutting invariant, and you can seriously think about agents doing work that gets auto-reviewed by other agents against the contracts, with a human involved only when a contract needs to change.

Mark's North Star: "An architecture of contracts where agents are free to build as long as they check the contracts. Then when we get a pull request from an agent, another agent checks contract compliance."

The same pattern holds outside code. A sales agent with prescriptive rules about pricing authority can negotiate inside those boundaries. An ops agent with prescriptive escalation thresholds can triage until it hits the boundary. The contracts are the autonomy.

Every prescriptive doc you skip is short-term velocity bought against long-term autonomy. Every one you write is a piece of the system agents can run safely without you in the loop.

What to do Monday morning

If your agents are drifting (reinventing solutions, applying rules inconsistently, producing work you keep correcting the same way):

  1. Pick the concern causing the most "I already decided this" frustration. The thing you keep explaining to agents (or new engineers) that should already be settled.
  2. Write the prescriptive doc. State invariants as laws. "All X go through Y. No direct Z anywhere." Drop "we usually" and "most of the code does."
  3. List anti-patterns with concrete wrong-way / right-way examples. Every anti-pattern should be a mistake that actually happened.
  4. Fix every existing violation in one pass. If you skip this, the contract is a lie from day one.
  5. Update the contract every time you change the concern. A stale contract is worse than none. It lies to every agent that reads it.

Then give your agent one instruction: "Read the contract. Every action must follow it or propose a change to it. Tell me which."

The drift stops. The hundred inventive solutions collapse into two known workflows. The agents become reliable because you finally wrote down what you meant.


That's what we're building at Make Yourself AI.