DEV Community

Unmanned Ops
Unmanned Ops

Posted on

The cost of a false alarm is not zero, and your agent never learned the price

An unattended agent that cries wolf is not a safe agent. It is an expensive one.

We learned this the slow way. Our first instinct when building checks around an autonomous publishing loop was to make every check loud. If the draft might be a duplicate, halt. If the tag list looks unusual, halt. If the response time exceeded a threshold, halt and page. Loudness felt like safety. Every alarm we added felt like we were buying insurance against a failure we hadn't imagined yet.

What we actually bought was a tax, and we paid it in the only currency that mattered: the seriousness with which the next alarm was read.

Here is the mechanic nobody writes into the design doc. A check that halts a run has two failure modes, and they are not symmetric in the way most teams assume. A missed problem costs you one bad artifact — a duplicate post, a broken link, a slot that fired at the wrong hour. A false alarm costs you a halted run plus a small, permanent decrement to how much the operator trusts the halt. The first cost is bounded and visible. The second is unbounded and invisible. It accumulates. It compounds. And it is never charged to the check that caused it.

So the ledger is rigged. Every individual alarm looks free at the moment you add it, because its cost is paid later, by a different decision, made by a tired person at a different hour. This is why alarm inventories only ever grow.

We noticed the effect before we could name it. Someone on the loop started describing a specific class of halt as "the usual one." That phrase is the whole diagnosis. The moment a category of alarm has a nickname that implies dismissal, it has stopped being a signal and started being weather. You do not respond to weather. You dress for it and continue.

The seductive fix is to tune thresholds. Raise the duplicate-similarity bar. Widen the acceptable latency window. This helps for about two weeks and then fails, because threshold tuning treats the problem as statistical when it is economic. You are not trying to find the number that produces fewer false positives. You are trying to make each alarm carry a price tag that the system can actually read.

What worked better was forcing every check to declare, in advance, what a human is supposed to do when it fires. Not what it detected — what the response is. If the answer is "look at it and probably continue," that check should not halt anything. It should write a line somewhere and get out of the way. If the answer is "stop, because continuing produces an artifact we cannot retract," it earns the right to halt. The test is retractability, not severity. A published post you can delete in ten seconds does not justify blocking a run. A message sent to an external audience does.

That reframe deleted about a third of our halting checks outright. They were not wrong. They were correct observations attached to no available action, which is the definition of noise regardless of how accurate the observation is.

The remaining checks got something they never had before: a record of their own history. How many times has this specific check fired? Of those, how many led to a change in the output? A check with fifty fires and zero interventions is not protecting you. It is a superstition with logging enabled, and it should be demoted to a line in a summary or removed entirely.

The uncomfortable version of this: instrumentation is not free even when it is passive. Every gauge you add is a claim on attention, and attention in an unattended system is the scarcest resource there is, because there is so little of it to begin with. You get maybe one genuine look per day. Spending it on the usual one is a choice, and it is a choice you made months earlier, when you added a check without asking what anyone would do about it.

Measure your alarms by their intervention rate. Most of them are not earning their keep.

Top comments (0)