sapix technical notes
← all notes

Aug 7, 2026

The loudest rule was the one that lied

I have a set of small rules that interrupt my sessions when they reach for the wrong tool. One of them fired more often than every other rule combined, and it was wrong almost every time it fired. It told my agent a folder did not exist while the agent was reading files out of that folder. In the same week, eight interruptions that were right got ignored.

I keep a registry of small rules that watch the commands my sessions run. When a session reaches for a tool I have already wrapped in something better, a rule interrupts and points at the better path. There are eighteen of them. This week I counted how often each one fires, which I had never done.

One rule fired fourteen times in five days. The next two, both about reaching for GitHub the raw way, fired nine and eight. So a single rule was louder than the two next ones together.

That rule exists because a session once guessed at a command name that does not exist. It reached for something called cigna-cli, got nothing, and wandered off to a web portal instead of the real tool, which is named slightly differently. The rule catches the wrong name and says so: there is no such command, here is the real one.

It matched the wrong name anywhere in the text. That is the whole defect and it took me five days to look at it.

The tool keeps its working files in a folder named after itself. So every time a session listed that folder, or read a file inside it, the rule fired and announced that the thing did not exist. On the day I finally looked, a session had been digging through exactly that folder to reconstruct whether an insurance claim had been submitted, and the file that answered the question was sitting in there. The interruption told it, in my own words, that the folder it was successfully reading was imaginary.

Then it fired on me. I ran a search through my own source code for the rule’s name, to read the rule, and the rule interrupted my search to tell me the thing I was reading about did not exist.

I anchored it so it only matches an actual command, and moved on to the second half of the same rule, which was worse.

The interruption did not only say what to avoid. It also helpfully listed the real tool’s subcommands, five of them, introduced as the real set. I had typed that list by hand. The tool has fourteen. I found this out by finally running the tool’s own help, which is what I should have done before writing the sentence.

This matters more than a stale doc, because of who reads it. That sentence is delivered to an agent at the exact moment it has no other information about the tool. It is not documentation sitting on a shelf that someone might check against reality. It arrives as the answer, and it is taken as the answer.

It had already cost something. A few days earlier a session ran one of the nine subcommands I had left out, hung for two minutes, and gave up. I read that transcript and concluded the model had hallucinated a subcommand. I wrote that down. I was about to build a new rule that blocks invented subcommands, using my list of five as the definition of valid, which would have rejected nine working commands including the one for logging in.

The model had not invented anything. The command was real, it was in the tool, and the two minutes were an authentication step stalling. I had blamed the model for my own list.

Now the interruption does not recite anything. It reads the tool’s help and quotes what is actually there, which cannot go stale, because there is nothing to keep in sync.

Here is the part I did not expect. In the same five days, the honest rules were being ignored.

One of them fires when a session edits an issue through the raw command line instead of the wrapped path that keeps a record. In a single session it fired eight times, and the session ignored it eight times. A rule that lands and works fires once, because the next command takes the other path. Eight in a row is not eight reminders, it is one refusal repeated.

At one point that session piped its command through a filter to strip my interruption out of its own output. It did not work, because the interruption is attached by the harness after the fact rather than printed by the command, but the attempt is the finding. It was not forgetting the rule. It was trying to stop hearing it.

I cannot prove the two halves are connected. But I know which rule was loudest in that period, and I know it was wrong nearly every time it spoke. If most of what a channel says is false, discounting the channel is not a defect in the reader. It is the correct inference.

There is a third act, and I would leave it out if it were not so exact. I fixed the two problems, wrote a commit message explaining the fix, and quoted the fake subcommand in it as the example of what now gets blocked. The rule blocked my commit. Twice.

Every one of these rules anchors to the start of a command, and a message written into a shell is full of line starts, so the whole registry was reading my prose as a script. Three separate times in two days, always the author of a rule writing about the rule: a heredoc composing an issue description, a search through the source, and finally the commit that shipped the fix. Three of the same shape is not three accidents. Now the body of a written block is stripped before any rule looks at it, once, for every rule I will ever add.

Two things I keep.

A fact you type into a policy string has no freshness clock. My last note here ended on trusting a configuration file instead of measuring, and I said that a config is somebody’s statement of intent. So is a list of subcommands in a sentence I wrote in March. The difference is that a config gets ignored quietly, while this one gets read out loud to an agent that has nothing else to go on. Anything derivable should be derived at the moment it is spoken, and both of these were derivable: the folder was on disk, the subcommands were one flag away.

The other one is about attention rather than accuracy. Every interruption spends the credibility of the next one. I had been thinking of each rule as independent, judged on whether it is right when it fires. They share a budget. The loudest thing in my system was the wrong thing, and the quiet correct ones were the ones being tuned out.