From 6d8a95095a33ec402c90f2f9814e6ca7526345a4 Mon Sep 17 00:00:00 2001 From: claude Date: Tue, 4 Aug 2026 05:15:02 +0400 Subject: [PATCH] deploy, docs: turn service_down back on (V-444) It was disabled because it could not say which service. It can now. --- deploy/mavend.json | 10 +++++----- docs/design.md | 23 +++++++++++++++++++++++ internal/config/config.go | 9 +++++---- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/deploy/mavend.json b/deploy/mavend.json index bb67b34..e1d1a9e 100644 --- a/deploy/mavend.json +++ b/deploy/mavend.json @@ -8,12 +8,12 @@ "//disabled_rules": [ "Nudge rules that are not wired at all. Names come from loop.DefaultRules:", "water, meal, break, service_down, netdata_critical.", - "service_down is off because it cannot say WHICH service — mavpoll folds the", - "whole kuma gauge into one boolean, so the nudge is always the generic 'a", - "service on homesrv is down'. Nothing to act on, every fifteen minutes.", - "Turn it back on once Vikunja #444 lands a fact per monitor." + "service_down is back on: mavpoll now writes one fact per kuma monitor", + "(service_down:), so the nudge names the service and pausing a monitor", + "in kuma silences that monitor. It is also edge-triggered, so a service that", + "stays down is one nudge, not one every fifteen minutes." ], - "disabled_rules": ["service_down"], + "disabled_rules": [], "phraser": { "model_path": "/opt/maven/models/llm/qwen3/Qwen3-1.7B-UD-Q4_K_XL.gguf", diff --git a/docs/design.md b/docs/design.md index 9c9e01d..4bf6ba4 100644 --- a/docs/design.md +++ b/docs/design.md @@ -389,6 +389,29 @@ lives in `source`; rules trust provenance. `source=poll:healthcheck`. A compromised poller must not be able to forge a trigger. +#### A fact per monitor, not an aggregate + +mavpoll writes one fact per kuma monitor, keyed `service_down:`. +It used to fold the whole gauge into a single boolean, and the nudge could then +only say that something on homesrv was down. That is not something he can act +on, so the rule shipped disabled. + +Three things follow from the split: + +- The key set is no longer known at wiring time. A rule declares + `WantPrefixes` and the gatherer resolves the family per tick, which is the + only prefix read in the loop. +- Pausing a monitor in kuma silences that monitor. Under the aggregate it + silenced nothing, because some other monitor kept the boolean at "down". +- A monitor deleted in kuma would keep its last fact reading "down" forever, so + mavpoll marks a vanished monitor "unknown". No rule fires on "unknown". + +The rule is also edge-triggered: it fires on a transition it has not already +nudged about (`State.NudgedSince`). A polled fact is written only when the +value changes, but the predicate reads the current value, so without the edge +check a service that stays down qualifies on every tick and cooldown is the +only brake. + ### Presence — concrete scoring **Combiner — noisy-OR, not weighted sum.** These are independent-ish positive diff --git a/internal/config/config.go b/internal/config/config.go index 990ae1e..51297bb 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -149,10 +149,11 @@ type Config struct { // // Rules are code, not config (see loop.DefaultRules), and that stays true: // this only subtracts. It exists because a rule can be right in principle - // and useless in practice — kuma's service_down cannot name the service it - // is nudging about (Vikunja #444), so being told "a service on homesrv is - // down" every fifteen minutes is noise with no action attached. Turning it - // off beats learning to ignore her. + // and useless in practice. service_down was the case that forced it: it + // could not name the service it was nudging about, so being told "a service + // on homesrv is down" every fifteen minutes was noise with no action + // attached. That is fixed — one fact per kuma monitor — and the rule ships + // enabled again. The escape hatch stays. // // A disabled rule is never gathered for, never evaluated, and never // delivered on any channel. Unknown names are ignored, so removing a rule