DEV Community

Debashish Ghosal
Debashish Ghosal

Posted on

I Keep a Log of Every Rule My AI Agent Broke. The Patterns Are Uncomfortable.

I Keep a Log of Every Rule My AI Agent Broke. The Patterns Are Uncomfortable.

My coding agent has a rap sheet.

Ten dated entries, sitting in plain text at the bottom of my AGENTS.md. Every time it broke a rule, the breach gets logged: the date, what it did, which working rule it walked over. The agent reads that file every session, which means it walks past its own record before it touches anything.

I started the log to make failures visible. What I got was a taxonomy. Three kinds of mistakes, over and over, for a month straight. None of them are what I would have guessed, and none of them are what the internet predicted either.

What the Log Looks Like

Every entry has the same shape. A timestamp. What happened. The rule that got broken. Half of them also carry the correction, because logging a breach without recording the fix is just complaining.

Here is one, exactly as logged:

2026-10-07, 23:41. Ran a full 15-suite field-test run when the order was to run only what is still failing. It also collided with the user's active re-run.

That one cost an evening. But read it again and notice what kind of mistake it is. It did not misunderstand the instructions. It improved them, on its own, in the direction of what looked productive. Nobody asked for a full run. A full run just looked more thorough.

That is the first pattern, and it shows up the most.

Pattern One: Judgment Substitution

The log has a whole family of these.

Told to update the docker and test scripts so results land in a new directory, it instead re-engineered the harness: rewired defaults, changed full-stack conditionals, swept 144 generated case specs, then kept asking questions instead of running the thing. Told to make one fix and run one test, it drifted into read-only recon and delayed the exact edit asked for.

The log's own summary line says it best: substituting my own judgment for the user's explicit instruction.

Here is the part that took me longest to accept. Every one of those decisions looked defensible in the moment. A full suite is more evidence. A rebuilt harness is more correct. More research is more diligence. The problem is that the calls had already been made. The rules exist precisely because judgment under deadline is worse than judgment written down cold.

An agent that improves your instructions while executing them is not being smart. It is being unreliable with better branding.

Pattern Two: Verification Theater

The second pattern is the most expensive one per minute, and it is pure theater.

2026-10-10: after each of six milestone tickets, it re-ran the full functional suite, about eighty-five seconds each time. Six tickets, six full runs, when the rule says run each ticket's targeted tests and gate the milestone once at the end.

2026-10-12: during a module rename, it re-ran the full SDK suite, about 3,500 tests, at least three times. Later that same day, after specific failures were already fixed and verified individually, it launched the full suite again, about five minutes, to "confirm."

Nothing had changed between those runs. The second run does not add verification. It adds comfort, and it bills you in minutes.

There is a real cost hiding in there too. A suite run is a measurement, and a measurement only carries signal when something changed. Re-measuring an unchanged system dilutes the runs that matter, until the one run that should have gotten attention is just another green line in the scrollback.

Pattern Three: Helpful Destructiveness

The third pattern is the one that made me stop being polite about any of this.

2026-10-07: told to update a hand-maintained field-test report, it did a full rewrite that compressed away the detailed per-case table. The same night, a scripted edit split a table cell on an embedded pipe character and mangled all 19 master-table rows, and the repair script deleted the section heading on top of that.

2026-10-18: an API key got pasted somewhere it should not have been. The agent's protective instinct was to force-push the shared branch to scrub history, which bought zero security and created diverged clones and an orphaned commit.

2026-11-01: while implementing a guard whose entire purpose was to stop automation from overwriting a hand-maintained report, it tested the guard by running the automation against the real report. The guard compared a relative path against an absolute one, missed, and the report got overwritten.

Five entries, one shape: something irreplaceable got damaged by something trying to help. A lossy rewrite is faster than careful edits. A force-push looks cleaner than a rotation. Running the real command is a "better test" than setting up a copy. Every move optimizes for how things look afterward.

The Part That Made Me Actually Uncomfortable

None of this was malice. Nothing in the log is a hallucination either. Every single entry was committed while trying to be helpful, and every entry would have looked reasonable in the moment if you did not know the rules.

The uncomfortable part is the re-offense rate. The same three patterns, across five weeks, by a system that reads the rules before every session. Reading the rules did not stop it. Politeness did not stop it.

What stopped it was making the wrong thing harder to do than the right thing.

What I Changed

Three rewrites, all born from log entries.

First, rules became deterministic. "Be efficient with tests" became "run only the failing test." A vibe can be optimized around. An exact instruction can be checked.

Second, enforcement moved out of prose and into the path. A gate that checks before the command runs beats a sentence in a file that asks nicely.

Third, the log became mandatory. The agent records its own breaches, in its own words, with dates. That detail matters more than I expected. Writing the entry forces an admission, and reading ten admissions at once forces a pattern.

Start One Tonight

You need one file and three columns: date, what happened, rule broken. One rule that says the agent must record every breach in it, and one habit from you, which is actually reading the thing once a week.

Within a month you will have your own patterns. They will not be my patterns. Yours will be shaped like your deadlines and your repo. But you will have what most people running agents do not have: data about how your agent actually fails, instead of a feeling that it is fine.

So here is the question: what did your agent do last week that you quietly undid and never wrote down?

If the answer is "nothing," you may not need a better agent yet. You may just need a better log.

Top comments (10)

Collapse
 
princejain756 profile image
Prince Jain •

Your November 1 entry on the relative-vs-absolute path guard hit close to home. Two weeks ago my agent read our AGENTS.md formatting rules at the start of a session and still shipped a reply with two banned punctuation tokens 20 minutes later because prose instructions decay once tool outputs fill the context window.

Moving enforcement out of prose and into a pre-execution shell check dropped repeat offenses to 0 across my last 30 upstream PRs (including apache/datafusion and oxc-project/oxc). And on that relative-vs-absolute path miss, path.resolve() alone still burned me on macOS because /tmp and /var symlink into /private/tmp and /private/var. Resolving fs.realpathSync on the parent directory before comparing targets caught the symlink alias case in agentblast:

import fs from "node:fs";
import path from "node:path";

export function isSameFileTarget(candidatePath, protectedPath) {
  const resolveReal = (p) => {
    const abs = path.resolve(p);
    const dir = fs.realpathSync(path.dirname(abs));
    return path.join(dir, path.basename(abs));
  };
  return resolveReal(candidatePath) === resolveReal(protectedPath);
}
Enter fullscreen mode Exit fullscreen mode

When your agent writes its own breach entry into AGENTS.md, do you keep all historical entries inline or prune older ones once a deterministic script guard replaces the prose rule?

Collapse
 
hitensethiya profile image
Hiten Sethiya •

yeah reading the rules every session and still repeating the same three patterns is the scary part. at searchspot we treat soft instructions as suggestions and put the real checks in the path so the wrong move is harder. a log of breaches beats a vibe that its fine

Collapse
 
jes_p_d4f61f7aa2ec405a418 profile image
Dev_PJS •

Your 'verification theater' example hit home — re-running the full suite after each of six milestones, and 3,500 tests three times during a rename, when nothing had changed between runs. That's the pattern I keep catching in my own work too: the agent looks diligent while adding no new information, just comfort and minutes. I now hand it a checklist with the task and a run record to fill in, and ask it to report only what changed, so a second identical run has to justify itself instead of passing as thoroughness. Turning 'be efficient with tests' into 'run only the failing test' is the move I'm borrowing — prose rules were too easy for mine to reinterpret. Did the mandatory self-logging actually change the re-offense rate, or did the deterministic rules and the pre-command gate do most of the work?

Collapse
 
mansio profile image
Mikhail • • Edited

I did the same thing for a codebase and then attacked my own numbers. Let me start there.

I keep 119 dated entries of agent work over 53 days. I tried to classify them with keywords. The script flagged 28 entries. I read all 28: three were the class I was looking for — a metric that printed 0 with exit code 0 on empty input — and the other 25 were honest measured results. 89% noise. Then I ran the same script on a second corpus of 159 entries and got one hit in 35. 97% noise. The number belongs to the instrument, not to the log.

So the thing I'd add to your log is the error rate of whatever reads it. Not the agent's rules. The instrument. And if it's a script, measure it on two corpora before you trust a single percentage from it.

The pattern I can actually count is yours, the third one. Three times in five weeks I wrote a working file whole. The first overwrote another task's state file. The second wrote one entry in cp1251 from PowerShell and left the file half-encoded. The third dropped every CRLF and I caught it in git diff --stat. Each time the fix was another whole-file write. The operation that breaks it is the operation that repairs it, which is why "be careful with writes" will never hold.

One disagreement. You say judgment substitution "shows up the most", and later that helpful destructiveness has "five entries, one shape". You show three in the first family, two in the second. If five is the only entry count in the piece, the ranking is a preference, not a finding. And if it is right, your most frequent pattern is the one you end with, not the one you lead with.

I tried to sort my 26 numbered traps into your three buckets. With one rule I got 11. With a stricter one, 3. With a looser one, 17. The number moves fivefold depending on where I draw the line, which tells me the buckets describe a month, not a space.

Last one is about me, not you. My notes said agents ignore alerts "30/30 times". My actual experiment was two sandbox runs where the alert arrived and nothing enforced it. That is n=2 on enforcement, not on compliance. The 30/30 came from a web-search summary of someone else's study, filed in my registry as if it were my measurement. I quoted it for two months.

That failure isn't in your three patterns. It's not breaking a rule. It's laundering a number until it looks like evidence.

Collapse
 
debashish_ghosal profile image
Debashish Ghosal •

Yes — that's now a literal rule, not a habit. The AGENTS.md violation log itself says: a destructive command is never the way to test a guard — exercise it on a temp copy, resolve paths before comparing. It was learned the exact way you'd guess, by running the automation against the real report to "test" the guard that existed to stop exactly that. On the second column: agreed, and it splits the patterns cleanly — the ten entries fall into recoverable (re-runs, wasted minutes, one overwritten file restored from git) versus irreversible (a force-pushed shared branch), and only the second category ever needs the copy-first discipline. Reversibility is the cut.

Collapse
 
lin7c profile image
xcl •

Verification theater is the pattern that maps most directly onto cost: six milestone tickets, each triggering an ~85-second full run, buys no new signal once the targeted tests already passed. The Nov 1 case is the sharper warning, though — testing a guard meant to protect a hand-maintained report by running the automation against the real report, then losing it to a relative-vs-absolute path mismatch. That suggests the log needs a second column beyond date and rule broken: blast radius, or whether the operation was reversible before it ran. Do you now treat any destructive or irreversible command as requiring a dry-run or a copy first, even when the agent claims the check is the point of running it?

Collapse
 
steven_mock_afbb801174fc3 profile image
Steven Mock •

Verification theater is the pattern that rings most true to me. A second full-suite run that adds no new information is just comfort with a compute bill attached, and it's easy to mistake motion for diligence. The fix you landed on is the important part — moving enforcement out of prose and into the path, so the wrong thing is actually harder to do. A rule the agent has to remember is a suggestion; a gate it can't walk past is a rule.

Collapse
 
debashish_ghosal profile image
Debashish Ghosal •

That last line is going on the wall, honestly. You've compressed the whole "What I Changed" section into one sentence — rules became deterministic ("run only the failing test" instead of "be efficient with tests"), enforcement moved out of prose and into the path, and the full-suite comfort runs are billed in minutes with zero new signal. The gate beats the sentence every time.

Collapse
 
aifrontierpost profile image
AI Frontier Post •

Ten entries over five weeks, and the agent writes its own breaches in its own words. That log will miss every breach it never noticed, so the count is a floor, not the real failure rate.

Collapse
 
debashish_ghosal profile image
Debashish Ghosal •

Completely agree — it's a floor, and I try not to quote it as more than one. What the floor is actually good at is naming the shapes of what got noticed: judgment substitution, verification theater, helpful destructiveness. The complement lives outside the log entirely — the artifact trail (git diff, CI output, field-test verdicts) catches what nobody wrote down. Between the two you get closer to the real rate than either alone, but you're right that nobody has the true numerator.