hub(D53): held apps reach the operator — mirror, Held apps list (host + Hosts page), reminder after the grace
gates / gates (push) Successful in 1m58s

app_held / app_hold_cleared allow-listed and operator-only; handleEvent mirrors them in held_apps; HeldAppsChecker
sends ONE app_held_reminder (warning, operator-only, per-app cooldown) after the grace per reason (6 h / 1 d / 3 d),
then weekly; recorded before sending; stale rows not reminded; the hub never clears a hold. Test-only
alerting.held_app_grace_override. TestD53_* in api/monitor/notify/web, red-proved by six mutations. Commit only.
This commit is contained in:
admin committed 2026-10-26 18:25:00 +01:00
1 parent ecd82abab8
commit 7e57e824a2
27 files changed
+935 -2

No files matched your search

+14
View File
@@ -72,6 +72,9 @@ type Config struct {
// the host-root thresholds above so a single storage's policy can be tuned separately.
StorageFillWarnPercent float64 `yaml:"storage_fill_warn_percent"`
StorageFillCritPercent float64 `yaml:"storage_fill_crit_percent"`
// HeldAppGraceOverride (D53) replaces every reason's reminder grace when set (a Go duration, e.g. "3m"). A TEST
// configuration only — the live proof shortens the wait; production leaves it empty (`08` §10's per-reason grace).
HeldAppGraceOverride string `yaml:"held_app_grace_override"`
// Offsite POOL-BOX aggregate thresholds (v0.64.0, R-5). Fill = box used vs capacity (percent);
// oversub = Σ(shared soft quotas)/capacity (ratio). Empty/0/invalid → defaults 80/90/2.0 (the
// checker normalizes). Threshold VALUES are Claude's encoding of the starter suggestion — Viktor's
@@ -899,6 +902,16 @@ func main() {
// durable (events table), so a hub restart neither floods nor silently re-heals.
// R-523: the agent's in-guest controller supervisor → controller_restarted_by_agent / controller_crashloop.
controllerSupervisorChecker := monitor.NewControllerSupervisorChecker(dataStore, dispatcher.ProcessEvent, logger)
// D53 (`08` §10): a held app reaches the OPERATOR — one reminder after the grace per reason, then weekly.
var heldGrace time.Duration
if v := cfg.Alerting.HeldAppGraceOverride; v != "" {
if d, err := time.ParseDuration(v); err == nil && d > 0 {
heldGrace = d
} else {
logger.Printf("[WARN] alerting.held_app_grace_override %q is not a positive duration — ignored", v)
}
}
heldAppsChecker := monitor.NewHeldAppsChecker(dataStore, heldGrace, dispatcher.ProcessEvent, logger)
offsiteDeliveryChecker := monitor.NewOffsiteDeliveryChecker(dataStore, offsiteHealReissuer, dispatcher.ProcessEvent, logger)
// R-242 second half (S4, decision 199): published golden vs vouched golden. Needs the registry creds
// (the same Gitea client as the Day-0 dropdowns); without them the check is off and says so.
@@ -931,6 +944,7 @@ func main() {
restoreTestChecker.Check() // R-85: restore-test failure + per-tier staleness
offsiteDeliveryChecker.Check()
controllerSupervisorChecker.Check()
heldAppsChecker.Check() // D53: throttled to one pass per 15 min
if goldenVouchChecker != nil {
goldenVouchChecker.Check() // R-242: throttled to one Gitea listing per 15 min
}