controller (unreleased): R-921 check-first pre-check (no stop while another tier's job is in flight); R-922 email_cleared on a household's deliberate clear
gates / gates (push) Successful in 1m0s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-09 12:57:34 +02:00
parent 13eeb484ab
commit ec9b997151
16 changed files with 798 additions and 18 deletions
+12 -2
View File
@@ -1793,8 +1793,9 @@ func main() {
if notifier.IsEnabled() {
go func() {
prefs := sett.GetNotificationPrefs()
if prefs.Email != "" {
if err := notifier.SyncPreferences(prefs.Email, prefs.EnabledEvents, prefs.CooldownHours); err != nil {
// R-922: a household's clear is pushed too, so a clear the hub missed reaches it at the next start.
if prefs.SyncOnStartup() {
if err := notifier.SyncPreferences(prefs.Email, prefs.EnabledEvents, prefs.CooldownHours, prefs.EmailCleared); err != nil {
logger.Printf("[WARN] Failed to sync notification preferences on startup: %v", err)
}
}
@@ -3556,6 +3557,15 @@ func (b quiesceBackend) BackupStatusFor(ctx context.Context, target string) (str
return r.Phase, err
}
// BackupJobsInFlight satisfies quiesce.InFlightProber (R-921): the loop asks it before it stops any app.
// The compile-time assertion below is the wiring pin — a pre-check whose adapter stops implementing the
// interface would silently fall back to "stop, then ask" (a seam built but never wired).
var _ quiesce.InFlightProber = quiesceBackend{}
func (b quiesceBackend) BackupJobsInFlight(ctx context.Context) ([]string, error) {
return b.c.BackupJobsInFlight(ctx)
}
// quiesceTierNotifier adapts *notify.Notifier to quiesce.TierNotifier (R-97a).
//
// The whole-guest tier had NO route to the hub at all — `internal/quiesce` did not import