Alert on the change, not the state
A fleet of sixty unattended jobs ran chronically red — not because things were failing, but because each job sensed a value, decided red or green, and pushed to a phone, all in one stateless breath. So the operator muted it. A muted alarm is a dead alarm.
The weakness
The board said 54 of 61 green and it had said something close to that for months. Almost none of the seven reds were real failures. They were settled conditions the fleet couldn't stop re-announcing, normal variance tripping a fixed line, and platform state that had gone stale hours ago. The alerts got noisy enough that the operator turned them off entirely — which is the worst outcome, because now the one real failure has nowhere to land.
The root cause wasn't any single job. It was the shape of every job. Each one did three things at once and kept no memory of having done them: it sensed a value, it decided red or green, and it pushed to the phone. Sense, judge, and page fused into one stateless act, sixty times over.
1 · Stateless re-fire. Checks asked "does the condition exist," never "did it change." A settled cloud-transfer overage — well over quota, but not growing, with nothing running — re-fired AMBER every six hours, forever. The dedup only silenced the push; it never silenced the verdict on the board. No memory means no "I already told you."
2 · Fixed thresholds flagged normal variance. A hard 60-minute freshness SLA plus a 15-minute grace meant a probe running five minutes late, on top of one network hiccup, tripped an alarm on a perfectly healthy connection that would re-probe itself in five minutes.
3 · Sticky platform state. The scheduler holds a job's last non-zero exit until its next run. A job fixed two hours ago still rendered as degraded and cascaded the whole fleet red for up to a day.
4 · No self-healing. Everything failed terminal, not fail-then-heal. A container exit-75, one sink timing out, a single cache miss — blips that clear on one retry — instead stayed red until the next scheduled run or a human.
5 · No acknowledgement of partial or paused. An intentionally paused channel counted as failing. A mirror with two of three sinks healthy reported as a monolithic failure. Not-green was treated as alarm.
The through-line: the alerting had no memory, no tolerance for variance, and no resilience layer. It was alert-fatigue by construction — the noise wasn't a misconfiguration, it was the design working as built.
The wrong fix (the one that's always tempting)
The obvious move is to tune the thresholds. Widen the grace window a bit. Raise the quota alarm. Add another dedup key. We could have spent a week nudging constants and the board would have looked calmer for a fortnight.
It's rearranging deck chairs. Tuning treats symptoms, and the noise always grows back, because the disease isn't the values of the thresholds — it's that sensing and deciding are fused in one stateless place. You cannot tune your way out of a system that has no memory. A better constant is still a constant with amnesia.
The reframe
An alarm that fires on state is noise by construction. An alarm should fire on change. The move is to separate the sensor from the verdict: let sensors emit raw facts and nothing else, and pull all judgement into one stateful decision layer — one that has memory, tolerates variance, and heals what it safely can — so the only thing that ever reaches a human is the new, stuck, and actionable.
A value being red is not news. A value turning red, and staying red after everything safe has been tried, is news.
The fix — the alert brain
We split the fleet in two. The jobs became dumb sensors: they post a raw signal to a shared sink and make no judgement at all. One central evaluator — the brain — owns every decision. It has six parts, each aimed at one of the five causes above.
- A declarative check registry. Adding a check is one line that inherits all the behaviour below — a control plane, not logic scattered across sixty scripts. The floor rises for every check at once.
- A state machine with real memory. Each check walks
green → concern → healing → confirmed → {acknowledged | recovered | paused | partial}, tracking first-seen, last-change, and ack-until. The board now knows the difference between "red right now" and "red, and I already told you at 06:00." - A variance layer. Each metric is compared to its own rolling history — a robust median-and-MAD band, not a fixed constant — with adaptive grace for freshness. Normal wobble stays green; a genuine departure stands out.
- An idempotent-only self-heal step. Before escalating, the brain retries, re-probes, or re-deploys the last known-good. This is also where the sticky-state false-red dies: the heal step re-reads the fresh stamp before it believes the red, so a job fixed two hours ago clears itself instead of cascading.
- Rule-based auto-acknowledgement. Known-standing conditions — the settled overage, a paused-by-design channel — are acknowledged by rule with a TTL, so they sit quiet until they actually change, then speak up again.
- Escalation as the only path to the phone. Confirmed, actionable, new. Everything else lives on the board and never buzzes.
Self-heal is idempotent-only by deliberate choice: retry, re-probe, re-deploy-last-good — actions safe to run twice — and nothing that mutates durable state or touches a secret. Bounded blast radius applies to remediation too. Anything riskier doesn't get healed; it gets escalated. The brain is allowed to fix, never to gamble.
And the brain itself is a monitored job with an independent watchdog — so the thing that decides whether to page you can't quietly become a silent single point of failure. A decider that can die unnoticed is just stale-green wearing a smarter hat.
The proof
The shadow week is the honest part. A new alerting layer that starts paging on day one is asking to be muted all over again. So it runs dark first: it makes every verdict, performs every heal, and writes it all to the log — but the phone stays quiet. Only once a week of its judgement matches reality does it get the interrupt. The interrupt is earned, not assumed.
The principle, generalised
An alarm that fires on state, not change, is noise by construction.
Give a monitoring system memory, let it heal what it safely can, and spend the human's attention only on what is new, stuck, and theirs. Everything else belongs on a board, not a phone. The interrupt is a privilege the alert layer has to earn — every buzz that wasn't news spends down the trust that the next real failure depends on.
- Separate the sensor from the verdict. Sensors emit raw facts; one stateful layer judges. Fusing them is how you get amnesiac alarms.
- Fire on the transition, not the level. Track first-seen and last-change, so "still red" and "newly red" are different events.
- Compare a metric to its own history, not to a fixed line. Normal variance should never buzz.
- Heal before you escalate — idempotent-only. Retry, re-probe, re-deploy-last-good. Never mutate durable state to make a red go away.
- Acknowledge the known and the paused. A standing condition, ack'd with a TTL, is silent until it changes — then it speaks again.
- Make the decider observable. The brain is a monitored job with its own watchdog, or it's a silent single point of failure.
- Earn the interrupt in the shadows. Run dark for a week before you're allowed to buzz. Trust is spent by every false page.
This is the same family of move as the earlier notes. Removing the coupling, not the component (№ 1) deleted a fragile dependency from the load-bearing path; here we delete the fusion of sensing and deciding from the alerting path. And silence is a failure mode (№ 8) warned that a dropped signal must degrade gracefully rather than vanish — this note is its mirror: too much signal is also a failure mode, and the muted phone is how it kills you. Green should mean nothing for you to do. Red should mean this one is yours.
gf.cx practice
(internal fleet diagnosis + alert-brain rebuild design) ·
source: kb.gf.cx/project_fleet_alert_brain_design_2026-07-30 ·
sibling to № 1 (remove the coupling) and № 8 (silence is a failure mode) ·
imports assets.gf.cx favicon + card primitives