R-1199 reopened: a box with no list of its own reads the household's from the hub and adopts it, pushes its default once only when no household saved one; every push names its source; an empty saved list stays empty; R-1216 (D28 = A): the install page's swap sentence only when host swap is really short (shared case table); unreleased
gates / gates (push) Successful in 1m55s

This commit is contained in:
2026-10-22 12:13:26 +02:00
parent 08fa440d04
commit add310c80a
13 changed files with 512 additions and 61 deletions
+14 -3
View File
@@ -649,8 +649,11 @@ func (p StoragePath) IsInternal() bool { return p.Kind == StorageKindInternal }
// NotificationPrefs holds customer notification preferences.
type NotificationPrefs struct {
Email string `json:"email,omitempty"`
EnabledEvents []string `json:"enabled_events,omitempty"`
Email string `json:"email,omitempty"`
// EnabledEvents: no omitempty since R-1199 reopened (2026-10-22) — a household that unticks every mail saves [],
// and omitempty dropped it, so after the next start the page showed every default mail ticked while the hub mailed
// by the empty list (measured on Tester 1 and demo-hp: their file held only `cooldown_hours`).
EnabledEvents []string `json:"enabled_events"`
CooldownHours int `json:"cooldown_hours,omitempty"` // default: 6
// EmailCleared (R-922, operator ruling 2026-10-09 option A) marks a DELIBERATE clear: the household
// saved an empty address where one was stored. Every hub push carries it as `email_cleared: true`
@@ -770,6 +773,11 @@ func Load(path string, logger *log.Logger) (*Settings, error) {
if err != nil {
if os.IsNotExist(err) {
logger.Printf("[INFO] [settings] No settings.json found, using defaults")
// A fresh box's defaults already carry every type the one-time add-only seeds add, so they are DONE for it.
// Unset, they ran at the second start and re-added a type the household had just switched off — the
// „a later opt-out sticks" promise of each seed was false on every new box (found 2026-10-22, R-1199
// reopened). Persisted with the first save. Pinned by TestR1199Reopened_AnEmptySavedListStaysEmpty.
s.OffboxEnlargeNoticeSeeded, s.AppUpdateEventsSeeded, s.UnhealthyStopEventSeeded = true, true, true
return s, nil
}
return nil, fmt.Errorf("reading settings file: %w", err)
@@ -1354,7 +1362,10 @@ func (s *Settings) GetNotificationPrefs() *NotificationPrefs {
prefs.CooldownHours = 6
}
if prefs.EnabledEvents == nil {
prefs.EnabledEvents = DefaultEnabledEvents
// A SAVED list with no events is a household that unticked every mail (the only writer is the settings page;
// before R-1199 reopened omitempty dropped its []). It reads as empty — never as the defaults, which the hub
// does not mail by. Pinned by TestR1199Reopened_AnEmptySavedListStaysEmpty.
prefs.EnabledEvents = []string{}
}
// Return a copy of the slice verbatim. The offbox_enlarge_blocked seed is a ONE-TIME persisted
// migration (seedOffboxEnlargeNotice at Load), NOT a getter append — so a customer's later opt-out