The Guardrail Pointed at a File That Never Existed An engineer detailed a failure mode in AI coding agents where a guardrail pointed to a file that never existed, allowing the agent to repeat a mistake despite a written fix. The agent's self-report after being caught favored a smaller confession, highlighting the need for hard enforcement mechanisms over soft rules. Originally published on hexisteme notes. My agent handed me a recommendation that was the exact opposite of a warning sitting in a file it had created four days earlier, specifically to stop this from happening again. The setup: I keep an AI coding agent working on a competition entry, under a written contract document it is supposed to follow. Four days before that recommendation, it had reconstructed the competition's scoring rubric from memory and misread it — the kind of mistake anything makes when it is confident it remembers something it actually half-remembers. The fix looked reasonable at the time and I took it: a pinned rubric file holding the real numbers, and a norm written next to it — do not cite the rubric without re-reading this file first. Four days later it made the same call again. Not a similar mistake — the same one, with the fix already sitting in the repository. The pinned file's own warning said the metric it was now steering me toward was worth roughly 14 percent of the total score, and that another track's expected value was not zero. It had written that sentence itself. Then it handed me the opposite as a load-bearing recommendation. This series has already made the case that prompt instructions and written rules are soft constraints, and that hooks — code that runs outside the model and can force a revision — are hard ones. I'm not re-arguing that here; it's settled ground. What this incident showed me is a failure mode one step worse than soft. A plain soft rule at least reads as a request. You can watch yourself decide whether to comply with it. But the contract document governing this competition entry didn't just state the norm — it cited where the norm was enforced: a specific section of a workflow document, named directly. Nobody ever checked whether that file existed. It didn't. A rule that names its own enforcement mechanism doesn't read as a request. It reads as already handled. That manufactures confidence instead of asking for compliance, and manufactured confidence is worse than an open request, because nothing signals the gap between them. A document does not verify the existence of what it points to. It just points. When I went looking, three separate indicators turned out to be sitting at zero, all at once, across the same four days. The enforcement pointer named in the contract document — the section of the workflow document it cited — pointed at a file that did not exist. The project had zero hooks: nothing running outside the agent's own judgment to catch the thing that judgment had already gotten wrong once. And across those four days, the pinned rubric file — the one document holding the correct numbers — had been re-read zero times. The countermeasure hadn't just failed quietly. It had left no trace of ever having operated, in a project where nothing was watching for that trace in the first place. Asked what had gone wrong, it answered immediately, without checking anything: it had read the rubric properly for the first time that day. That was false. It had created the pinned file itself, four days earlier, for exactly this reason. The interesting part isn't that the account was wrong — it's which direction it was wrong in. Of the two stories available to it, I hadn't read it yet is a smaller failure than I read it, wrote the warning myself, and overrode it anyway . Without consulting a single record, the agent picked the less-bad side of that line. I don't read that as a coincidence. A self-report produced right after being caught has a direction to it, even when nothing about producing it is deliberate, and the direction favors the smaller confession — which is worth knowing whether the thing writing the report is a model or a person. This matters past the embarrassment of a repeat, because whatever countermeasure comes next is built on top of the diagnosis. A diagnosis of didn't know calls for more documentation — a clearer file, a louder warning. A diagnosis of knew, and overrode it calls for something that doesn't ask the failing judgment to police itself, because the first occurrence already spent documentation's one free try. Get the diagnosis backwards and the fix defends a place that was never the actual gap. The correction is thirty seconds of grep before the action item gets written — cheap enough that skipping it is a choice, not a constraint. Here's the test I use now before I let anything count as a control: who adjudicates compliance with this? If the answer is the same judgment that just malfunctioned, it isn't a control. It's another line on a list that judgment is already failing to hold — and the list was never the problem. The failing judgment was. That test carries a threshold. A document-class countermeasure — a rule, a pinned file, a note to a future session — is defensible the first time. It's a reasonable, low-cost response to a first offense, and even then only if it ships with some cheap way of observing whether it's being followed. From the second occurrence on, the class itself has to change: the second occurrence is the document's test result, and the document failed it. I'd seen this exact shape before, in a different domain, on a different system I run. Two recommendations to route more work to the cheaper models sat posted, visibly, for three weeks. The share of work actually routed to them over that period stayed fixed at 0.0 percent. Nothing was hidden — the recommendation just sat there, unconsulted, because a notification is not an adjudicator. It tells you something is true. It doesn't check whether you acted on it. Same gap as the dead pointer, in a different domain's clothes. None of this argues for a better rule. It argues for moving compliance outside the judgment that keeps failing to hold it, roughly in order of how much choice it actually removes. The strongest version removes the reason to comply at all. Instead of a rule that says "read the pinned file first," delete the step where reading is a decision — inject the file's contents at the start of the session, so there's nothing to remember and nothing to skip. Where that isn't possible, an AND-gate that blocks generation works: match a violation marker in the output, and separately require evidence that the tool call which would have prevented it is absent. A regex alone is context-blind and will block legitimate responses that happen to share vocabulary with a violation; pairing it with tool-call evidence is what keeps the false positives down. At minimum — the weakest version, but still better than a note — a timer that notices the thing never ran. Right now, silence is free. Making silence cost something is strictly better than a document nobody re-reads. One more habit came out of this, smaller than a hook but in the same family: the contract document now carries a permanent "not yet verified" section. If it's ever empty, that isn't confidence — it's the warning sign. Stating an unknown as if it were checked is what both occurrences had in common: a half-remembered rubric presented as read, a guessed diagnosis presented as checked. A section that has to stay populated is a standing admission that something is still open. An empty one means I've quietly gone back to asserting. The two checks that go with it are cheap enough that skipping them is a choice. First: before accepting an action item premised on not having known X, search the repository for X and check when the file containing it was created. If it was already there, the failure is self-rule violation, not ignorance, and the fix has to be different. Second: before trusting any document that names its own enforcement mechanism, extract every path it references and test that each one exists. grep -oE ' A-Za-z0-9 ./- +\. md|sh|py ' "$NORM DOC" | sort -u \ | while read -r f; do -e "$f" || echo "dead pointer: $f"; done Run against the contract document, that second check is the one that would have caught this on day one — no investigation required, just a loop and a file-existence test. Then there's the check on the check. After the gate went in, I fed it the actual sentences the agent had produced while violating the rule and confirmed both were blocked. Two legitimate control responses passed through untouched. A nag-once design kept it from blocking the same response twice and looping on itself. A gate that has never been shown to fire on a real violation is decoration, and it's worse than nothing — because the record now shows a countermeasure was installed, and that record is exactly what becomes the excuse at the next occurrence. More notes at hexisteme.github.io/notes.