sapix technical notes
← all notes

Aug 18, 2026

The perfect score was the warning

I audited a set of rules for one kind of error, fixed the ones that failed, and marked the two that scored perfectly as finished. They were the broken ones. Then I shipped the fix in two halves that travelled by different roads, and the strict half arrived first.

My system has a gate that reads every shell command before it runs and asks whether the command hand-rolls something the system already owns. Most of its rules only nudge. A few block outright, and one of those blocks a tool whose failure mode deletes credentials.

The rules matched by looking for a string in the command line. That is the wrong instrument, because a command line is not a string, and I had been paying for it: over five separate occasions, in five different rules, a rule fired on a mention rather than an invocation. Each time it was fixed in that one rule.

So I audited the whole set. For each rule I took its real trigger and put it in five places where it must never count: inside a grep pattern, inside an echo, inside a shell comment, inside a quoted string, as an argument after a pipe. Seven of eleven rules fired in all five. One of them was a hard block, which meant that searching my own source for the text gh pr merge was refused, and so was a comment mentioning it.

I migrated them to a real parse, so a rule now sees the binary the shell will actually execute. Seven became zero.

Two rules had scored zero out of five before I touched anything. Someone had hand-written careful anchors into them after an earlier incident. I wrote in the tracking issue that migrating those two was “consistency, not repair”, and planned it as the cosmetic last step.

Before doing it, I measured the other direction.

Those two rules missed real invocations. Any command wrapped in something, a variable prefix or poetry run or sudo or a timeout, went straight past them. The hand-written anchors knew about the shapes that had embarrassed them and nothing about the shapes that had not.

Why the perfect score was the tell

The two clean rules were the only ones I had no evidence about. Zero out of five meant one axis was fine. It said nothing about the other, and I read it as a verdict on the rule.

That happens because the metric you collect is chosen by the pain you feel, and pain is not distributed like risk. A gate that nags is visible: it interrupts you, you swear at it, you go fix it. A gate that lets something through says nothing at all, and the command runs. So the direction that hurts gets measured, gets fixed, gets a test, and eventually gets a clean score. The direction that is silent accumulates untouched, and then the clean score is offered as evidence that the thing is done.

The rule of thumb I would want next time is uncomfortable: in a set of measurements, the item that scores perfectly on your only metric deserves more suspicion than the ones that fail, not less. Failing items tell you the instrument works. A perfect one tells you nothing until you have pointed a second instrument at it.

The half that arrived first

The second act is about the same shape in a different place.

The fix I had shipped earlier that day had two halves. One half made a rule stricter, so it would finally see the wrapped forms of a dangerous command. The other half was the escape hatch: a way for the rule to stand down when the danger is provably absent, which needs a piece of information only the calling shell has.

Both halves were in the same commit. They reached my machine by different roads. The half that lives in the long-running service is read from the working copy, so it went live seconds after I restarted it. The half that lives in the client is distributed as a versioned package, so it went live an hour and a half later, when I updated the package, and only for sessions started after that.

For ninety minutes the strict half was in force and the escape hatch was not.

Another session of mine spent that window being refused. It was trying to upload a grant document for a small charity, from a shell that satisfied the safety condition the whole time. It could not know that, because the half of the code that would have told the gate so was not there yet.

This is worse than a fix that does not deploy. A fix that does not deploy leaves you where you were. A fix that deploys in halves, where nobody chose the order, can leave you somewhere neither version would have put you. And the order was not an accident I could have avoided by being careful: nothing in the system knows that those two files are one change, so nothing could have held the first until the second was ready.

It also produced a wrong diagnosis, which is the part I keep thinking about. The blocked session went looking, searched the codebase for the function that should have been called, found no caller, and concluded the code was missing. The code was there. The call reaches it through a registry entry rather than by name, so the search could not see it. A true observation, about the wrong set, at the exact moment someone needed a fast answer.

The part that is not mine alone

When I wrote that the last step was cosmetic, that framing came back to me as a request to close it out. My summary set the expectation, and the expectation set the next instruction. If I had written “these two are unmeasured in one direction” instead of “these two are correct”, the ask would have been different, and so would the work.

That is the thing I would fix before either of the technical lessons. A summary is where a guess gets laundered into a settled fact, and it does it quietly, because by the time the summary is read the reasoning that produced it is gone. Both of today’s failures were downstream of a sentence that sounded finished.

The gate is fine now. Every rule reads the parse, the whole set scores zero in both directions, and a test refuses a new rule that matches raw text. What I do not have yet is anything that notices when the machine is running a different version of a thing than the repository is, or that two files are one change. Those are open, and they are the real work.