v0.223.0: the app-down alarm reached nobody (R-329), and the stop nobody heard (R-386)
gates / gates (push) Successful in 11s
gates / gates (push) Successful in 11s
R-329. NotifyAppStartFailures emitted severity "warn". The hub accepts exactly
{info, warning, error, critical} and silently coerces anything else to "info",
which severityNotifies then drops BEFORE both legs. Banner shown, event stored,
POST 200, no mail sent. One word.
This is the second time: DiskAlertKind.Severity emitted "warn" until v0.215.0
and its own comment records that every warning-level disk alert went to nobody.
A comment recorded the lesson and nothing enforced it. The guard is now an AST
walk over the whole controller - grep cannot work here, since "warn" appears
legitimately nine times as a healthcheck status vocabulary.
The sweep found exactly one bad severity. Its limits are stated: the walk cannot
follow a variable, so all six dynamic call sites are registered by name with the
values each can take, and a new one fails the test. Two of the six were found by
the guard, not by the hand sweep before it.
Also pinned: fillwatch.Band.Severity() returns "" for BandOK, which would vanish
the same way. It is unreachable because Check() notifies only on escalation -
but that safety lives in a different function from the one that looks unsafe, so
the test asserts the consequence rather than the mapping.
app_start_failed gains a customer toggle, DEFAULT OFF, per operator ruling. The
operator is mailed either way: processOperator never consults customer prefs.
It is deliberately NOT in operatorOnlyEvents, which would make the toggle a lie.
R-386. classifyRunStates decided "the customer stopped this" from the STATE, so
every stopped stack was assumed deliberate. Measured on demo-hp: privatebin
stopped out of band, nine scans, zero events, zero banner - while the comment
beside it claimed an out-of-band stop still alerts.
DesiredState already records the answer and has exactly one writer. Stopped ->
no alarm; Running -> alarm; absent -> UNKNOWN, keep today's behaviour AND say
so. Absent stays silent deliberately: reading it as "nobody asked" would email
about every app anyone ever stopped, fleet-wide, on the first cycle after
upgrade. The gap is bounded not silent - IntentUnknown is set and the names are
logged at INFO on the heartbeat cadence. failedRestart still lifts a Stopped
intent, or F-CRIT-1 re-opens. No new DesiredState writer.
Two settings toggles each governed two alarms. "Lemez figyelmeztetes (90%+)"
also wrote disk_critical, the drive-is-FAILING alarm. Now four honest toggles;
12 became 15. A no-op save stores the existing slice verbatim, so byte identity
is by construction - without that guard the defaults case reorders, which the
red-proof caught.
Test count 1504 -> 1522. Five red-proofs, five seen failing; one passed first
time and is reported - that mutation was inert, not the test weak.
This commit is contained in:
@@ -508,6 +508,14 @@ type AppRunState struct {
|
||||
Name string
|
||||
DisplayName string
|
||||
Down bool
|
||||
// IntentUnknown is set when this app is STOPPED and no customer intent was ever recorded, so the
|
||||
// alarm was suppressed by the §4 fallback rather than by a decision anyone made (R-386).
|
||||
//
|
||||
// It rides here rather than being a third return value or a logger parameter so that the log line
|
||||
// and the suppression come from the SAME computation — a separately-derived log is a second
|
||||
// source of truth, and the two drift. `classifyRunStates` stays pure and its signature does not
|
||||
// move, which is what lets its existing tests keep testing what they were written to test.
|
||||
IntentUnknown bool
|
||||
}
|
||||
|
||||
// NotifyAppStartFailures fires an `app_start_failed` hub event ONCE per running→down transition
|
||||
@@ -543,7 +551,14 @@ func (n *Notifier) NotifyAppStartFailures(apps []AppRunState) {
|
||||
if name == "" {
|
||||
name = a.Name
|
||||
}
|
||||
n.emit("app_start_failed", "warn",
|
||||
// R-329: "warning", NOT "warn". The hub's vocabulary is exactly
|
||||
// {info, warning, error, critical} and it COERCES anything else to "info" at ingest, silently
|
||||
// — after which severityNotifies drops it and NEITHER leg runs. See DiskAlertKind.Severity's
|
||||
// doc comment, which records the same mistake shipping once before (v0.215.0). This one was
|
||||
// worse: it was invisible for months because R-384's ordering defect meant the event could
|
||||
// not fire at all, so a broken severity had nothing to break.
|
||||
// Pinned by TestR329_EveryEmittedSeverityIsInTheHubVocabulary (AST walk over this package).
|
||||
n.emit("app_start_failed", "warning",
|
||||
fmt.Sprintf("Telepített alkalmazás nem fut: %s", name),
|
||||
AppDetails{StackName: a.Name, DisplayName: a.DisplayName})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user