fix(alarms): give up after three runs without delivery (FLAW-12)
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m34s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m34s
New column alarm_failures counts ticker runs in which no channel delivered; at three the alarm is marked as done and a warning is logged instead of retrying every minute forever. Success, a new due date or reopening the task starts over with three attempts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b5f926bcf7
commit
d1982816c1
8 files changed
+80
-13
No files matched your search
@@ -138,3 +138,50 @@ func TestAlarmResetsOnNewDueDateOrReopen(t *testing.T) {
|
||||
t.Errorf("reopening must re-arm the alarm")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlarmGivesUpAfterThreeFailedRuns(t *testing.T) {
|
||||
store, err := NewStore(filepath.Join(t.TempDir(), "alarmfail.db"))
|
||||
if err != nil {
|
||||
t.Fatalf("NewStore: %v", err)
|
||||
}
|
||||
defer store.Close()
|
||||
u, _ := store.CreateUser("anna@example.com", "hash", "Anna", "member")
|
||||
task, _ := store.CreateThoughtWithDetails(u.ID, "Webhook kaputt", "normal", "manual", "2026-10-30", "", "todo", "")
|
||||
_, _ = store.UpdateThoughtTriage(u.ID, task.ID, "keep", "task", "", "", "")
|
||||
pending := func() bool {
|
||||
alarms, _ := store.GetDueTasksForAlarm()
|
||||
for _, a := range alarms {
|
||||
if a.TaskID == task.ID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
for run := 1; run <= MaxAlarmFailures; run++ {
|
||||
gaveUp, err := store.RecordTaskAlarmFailure(task.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("run %d: %v", run, err)
|
||||
}
|
||||
if gaveUp != (run == MaxAlarmFailures) || pending() == (run == MaxAlarmFailures) {
|
||||
t.Fatalf("run %d: gaveUp=%v pending=%v", run, gaveUp, pending())
|
||||
}
|
||||
}
|
||||
|
||||
// A new due date gets three fresh attempts
|
||||
later := "2026-11-02"
|
||||
_, _ = store.UpdateThoughtDetails(u.ID, task.ID, nil, nil, &later, nil, nil, nil, nil, nil, nil)
|
||||
if !pending() {
|
||||
t.Fatalf("new due date must re-arm the alarm")
|
||||
}
|
||||
if gaveUp, _ := store.RecordTaskAlarmFailure(task.ID); gaveUp {
|
||||
t.Errorf("failure counter must start over after a new due date")
|
||||
}
|
||||
// Success clears the counter
|
||||
_ = store.MarkTaskAlarmSent(task.ID)
|
||||
var failures int
|
||||
_ = store.db.QueryRow(`SELECT alarm_failures FROM thoughts WHERE id = ?`, task.ID).Scan(&failures)
|
||||
if failures != 0 {
|
||||
t.Errorf("successful alarm should reset failures, got %d", failures)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user