Make your AI agent's rules executable, not just documented
In one working session I asked my coding agent to fix the same kind of problem three separate times. Each time it fixed the figure in front of it, promised to remember, and then produced the next figure with the exact same defect. The rule it kept breaking was already written down. Twice, in fact: once in a memory note and once in a shared config the agent is meant to consult. Writing it a third time would not have helped. The thing that finally worked was making the rule executable, so that breaking it fails an edit instead of disappointing me on the render.
The defects were mundane, which is why they slipped through. Annotation text on the figures came out too small and too faint to read on a projected slide. Axes came out with two tick marks where I wanted four or more, so you could not read the values off them. And a particular word I dislike in labels kept reappearing, the sort of generic word an agent reaches for by default. None of this was subtle physics. It was the visual equivalent of a typo, repeated.
Here is what makes it worth writing about. Every one of those preferences was documented. There was a memory note describing exactly what I wanted, and a shared theme file with sensible base font sizes already set. The agent had access to both. It broke the rules anyway, not out of defiance, but because the drawing call it reached for takes an explicit size argument, and the good default in the theme does not reach into that particular call. The rule existed. The path of least resistance did not enforce it.
Documentation is a passive control
The instinct, when an agent gets something wrong, is to write the rule down more clearly or in more places. I did this for months before I noticed it was not working. The problem is structural, not a matter of wording.
A documented rule only helps if the model recalls it and applies it at the right moment. That recall is probabilistic, and it degrades as the pile of context grows. Think about what a long-running project accumulates: a memory file, a style guide, a shared config, a project-level instructions file, the conversation so far. Every rule you add competes for the model’s attention against everything else you have written. Add enough of them and any single rule will eventually be the one that gets skipped, not because it is unimportant, but because attention is finite and the file is long.
I wrote separately about giving an agent a durable memory between sessions, and I still think that plumbing is worth building. But memory files are documentation, and documentation has this ceiling. It is a passive control. It sits there and waits to be read, and on a busy edit it often is not.
The gap is between the note and the tool
The real failure here is that the passive rule and the active tool are two different surfaces. The note lives in a file the model may or may not consult. The tool is the plotting call the model actually runs. When the easy way to call the tool disagrees with the note, the tool wins, because the tool is what produces the output and the note is just a hope attached to it.
Once I saw it that way the fix stopped being “explain the rule better” and became “close the gap between the rule and the action”. Move enforcement as near to the edit as you can get it.
Three layers, cheapest first
The fix had three layers. I did them in order of cost, cheapest and most durable first.
First, raise the defaults in the shared config so the correct thing happens with no thought at all. Bigger base font sizes. A minimum tick count. A dark, bold default for any annotation. If the good value is the value you get when you do nothing, most of the problem never occurs.
Second, add a small helper for the common case, so the correct call is also the easy call. If drawing an annotation the right way is one short function, nobody reaches past it for the raw call with its fiddly size argument. This is the same instinct behind pulling shared styling into one theme file every script loads: make the right thing the default thing, and the wrong thing takes effort.
Third, and this is the part that actually made the defects stop, add a lint-style check that runs automatically on every file edit and flags the specific violations. A font size below a floor. An annotation colour too light to read. The banned word in a label. An axis configured with too few ticks. The check fails the edit and prints a message pointing at the fix, so the model self-corrects in the same turn, before I ever see the render. This is the move that makes the rule executable. It does not ask the model to remember anything. It catches the mistake at the moment it is made.
This is not a new idea. It is the same pattern as a git pre-commit hook or a linter in a normal codebase: a rule you would otherwise rely on people to follow, turned into a check that runs by itself and blocks the bad state. Most coding agents now support hooks that fire on file edits. Claude Code, for one, runs a hook after every write or edit, which is exactly the hook point you want. All I did was aim that machinery at my own preferences instead of at syntax errors.
The check can be tiny. The whole shape of it is this:
on edit of a plotting file:
text = the new content being written
problems = []
for each explicit font size in text:
if size < MIN_SLIDE_FONT:
problems.add("font too small; use the theme constant")
for each annotation colour in text:
if colour is lighter than MIN_DARKNESS:
problems.add("annotation too faint to read on a slide")
if any label contains the banned word:
problems.add("use the plainer word in labels")
if problems:
reject the edit and print problems # the agent fixes it this turn
The specific checks do not matter. What matters is that the check runs on its own, at the moment of the edit, so compliance no longer depends on anyone remembering.
The economics are the argument
It is tempting to treat each wrong figure as a one-off and just correct it. That is what I did for too long, and it is the expensive path. Every repeated correction costs tokens on both sides of the conversation, and it costs the scarcer thing, which is my attention, on a problem I had supposedly already solved. Three corrections in a session for something that is written down twice is not a small tax. It is the same tax, again and again, for as long as the project runs.
Making the rule executable is a one-off cost. Writing the hook took me a few minutes. It has paid that back many times over, because it works on every future edit without me being in the room. A soft preference that fails loudly is worth more than a firm preference nobody enforces.
When you correct the same thing twice
The generalisable rule is short. When you catch yourself giving your agent the same correction a second time, stop instructing and start enforcing. Do not write the rule down more emphatically. Write it as a check that runs at the point of action.
Fix the defaults so the right thing is what happens by accident. Add a helper so the right thing is also the easy thing. Then add an executable check so the wrong thing fails at the edit rather than surviving to the output. Enforcement plus low friction beats enforcement alone, and both beat another paragraph in a file the model was never going to read at the right moment.