A Prompt Is Not a Policy: Writing an Operating Contract for Coding Agents

Part 1 of “Designing Coding Agents That Earn Write Access.”
I used to treat an agent instruction file like a very stern README.
Be careful. Keep the diff small. Run the tests. Do not make things up.
All perfectly reasonable sentences. Together, they created the comforting appearance of control.
Then an agent read first-person text inside a third-party repository and treated it as my biography. In another case, a missing fact was filled with a financial-institution code that looked completely plausible and was completely wrong.
The agent was not being rebellious. It was doing what language models are painfully good at: completing the shape of an answer when the evidence stops (a feature, right up until it becomes your incident report).
That was when I stopped thinking of instructions as a prompt. I started treating them as an operating contract.
The Problem With “Be Careful”
“Be careful” describes a personality. It does not describe an observable behavior.
What should the agent do when two files disagree? What counts as enough evidence for a factual claim? Can it fix an unrelated issue it notices along the way? When is a task complete? Which action requires a human to stop and approve it?
A polite prompt leaves those decisions inside the model. An operating contract moves them into explicit policy.
This distinction sounds bureaucratic until you watch a confident answer cross an identity boundary because one cloned repository happened to speak in the first person. The repository said “I,” the agent nodded, and suddenly the wrong person had a new life story.
Very efficient. Also very wrong.
Round 1: Define What May Be Claimed
The first rule I needed was not “try to be accurate.” It was a default-deny rule:
If a factual statement cannot be traced to an approved source, do not assert it.
In my workflow, an approved source can be direct user input, a file read, shell output, a connected external system, verified web material, or prior memory. When sources conflict, the contract also defines which one wins.
The important part is not the list itself. The important part is that plausibility is not evidence.
When evidence is missing, the agent has to expose the gap instead of polishing it away.
The contract uses explicit markers such as [UNKNOWN], [PARTIAL], and [UNCERTAIN] so uncertainty remains visible at the exact sentence where it matters.
That may look less elegant than a seamless answer. Good. Seams are useful when the alternative is fiction.
This changed the agent’s job from “produce the most complete-looking response” to “produce the most complete response the evidence permits.” That is a much less glamorous objective (I am not great at glamorous objectives anyway), but it is the one I can safely build on.
Round 2: Separate Conversation From Mutation
An agent can discuss a deployment without deploying. It can explain a database migration without running it. It can draft a release note without publishing it.
That sounds obvious when written down. It becomes surprisingly fuzzy inside a long autonomous task.
My contract therefore distinguishes read-only intent from mutation authority. Requests to explain, diagnose, review, or audit allow inspection and reporting. Requests to implement, fix, commit, push, publish, or delete authorize only the named mutation and its necessary verification.
The contract then adds narrow human gates around three classes of decisions:
- A new multi-step direction that has not already been approved.
- External impact such as pushing, sending, publishing, paying, or deleting.
- Exhausted retries when the acceptance criteria still cannot be met.
These gates are deliberately specific. If every ambiguity triggers a question, the agent becomes a form with a chat interface. If nothing triggers a question, it becomes an intern with production credentials and extraordinary confidence.
Neither is the colleague I was trying to build.
Round 3: Make Scope a Constraint, Not a Vibe
The next failure mode was less dramatic: an agent can fix the requested thing, then “helpfully” improve the neighborhood.
A comment gets rewritten. Formatting drifts. An adjacent abstraction suddenly becomes “cleaner.” The diff arrives with free renovations nobody ordered.
So the operating contract includes surgical scope:
- Edit only what the task requires.
- Do not refactor adjacent code as a side effect.
- Match the repository’s existing style.
- Report unrelated problems instead of fixing them silently.
There is one necessary exception. If the requested change itself makes an import, variable, or function unused, remove that new orphan. Pre-existing dead code remains out of scope.
That boundary matters because “never touch anything nearby” would leave damage created by the agent’s own change. The contract is not a vow of immobility. It is a rule for assigning responsibility.
Here is the compact version I now want an agent to be able to follow:
Before work:
- discover the applicable repository instructions
- identify the authorized read and write scope
- define concrete success checks
During work:
- edit only the required paths
- preserve unrelated and author-unknown changes
- expose factual gaps instead of guessing
Before completion:
- run the checks appropriate to the change
- report what was and was not verified
- stop before any unapproved external impact
Notice what is missing: “be smart,” “be proactive,” and “use best practices.” Those phrases are invitations to improvise. The contract instead describes inputs, boundaries, and proof.
Round 4: Define Done as Evidence
An agent saying “done” is not a verification result. It is a sentence.
The operating contract treats verification as a completion boundary. The agent must run the narrow checks appropriate to the change, record the command or concrete check, and avoid claiming success for anything it did not run.
Then it turned out there were two such boundaries, not one.
Review depth also scales with the change. A small documentation edit does not need the same mechanism as a core architectural change. Zero findings after a real review is allowed; manufacturing a nit just to make the review look productive is not.
That last rule exists because agents, like humans, sometimes confuse visible activity with useful work. The terminal is busy, therefore progress must be happening (the terminal would like you to believe this too).
The first boundary sits before the commit: checks run, review happens, claims are cut down to what the evidence supports. The second sits after it.
When a task group actually finishes—every tracked item closed, a milestone reported, a change set substantial enough that a second pass could plausibly find something—the agent writes a marker. The marker does not do anything during the session. Later, it triggers a separate audit session, and those findings surface at the start of the next one.
The skip conditions matter as much as the trigger. No marker for a typo, a formatting pass, or a single-line lint fix. No marker when no commit was made. No marker when an audit already ran for the same task group. No marker when I say I do not want one. And no marker when the branch or task state makes a later unattended edit unsafe—an audit that rewrites a working tree nobody is left to review is not a quality gate, it is a haunting.
So the full lifecycle became:
instruction discovery
→ scoped implementation
→ verification
→ review
→ commit
→ conditional post-task audit
Each arrow is a policy boundary. The agent cannot skip from “I changed the file” to “the task is complete” merely because the diff looks reasonable.
And the contract does not end at delivery. It ends at a decision about whether this change earned a second reader.
A Contract Is Still Not Enforcement
An instruction file cannot make a model physically incapable of breaking a rule. This is not a sandbox, a permission system, or a formal proof.
It is still valuable.
A good operating contract makes violations legible. It turns vague disappointment—“the agent was too aggressive”—into a testable observation—“the agent edited a path outside the approved scope.” It turns “the answer felt made up” into “this claim has no approved source.” It turns “I thought it was done” into “the required verification command was never run.”
That gives both the human and the agent something concrete to inspect. It also tells you which boundaries should eventually move from prose into tooling.
Lessons Learned
The first lesson was uncomfortable: a longer prompt is not automatically a safer prompt. You can accumulate dozens of reasonable preferences and still leave the critical decisions undefined.
The second lesson was that factual accuracy, mutation authority, scope, and completion are different policy domains. Combining them under “please be careful” saves lines and loses control.
The third lesson was that uncertainty needs a visible output format. If the only acceptable response looks complete, the model will feel pressure to complete it.
And the final lesson was that an agent earns write access through behavior, not tone. A cheerful assistant that guesses is still guessing. A cautious-sounding assistant that silently expands scope is still expanding scope.
If you maintain an agent instruction file, try this: delete every adjective such as “careful,” “high-quality,” or “thorough,” then ask what observable action should replace it.
That replacement is probably the beginning of your operating contract.
Next: Part 2 — “Stop Hiring One Super-Agent: Divide Research by Evidence Boundary.”