feat(engine): reminders and escalation policies (#27, part 2) #29

Merged
KofTwentyTwo merged 2 commits from feature/reminders-escalation into develop 2026-10-14 15:58:29 +00:00
KofTwentyTwo commented 2026-10-14 15:52:37 +00:00 (Migrated from github.com)

Part 2 of 3 for #27: reminders and escalation. Part 3 is #28 (team sources and mutes); part 1 (quiet hours and digests) is a separate PR.

Reminders: an OPEN alert that isn't acknowledged, snoozed, resolved or muted sends a REMINDER through the normal router (subscription event filters, visibility, suppression) every reminderIntervalMinutes of its type. CRITICAL defaults to 60 minutes as the design says, and an explicit 0 turns reminders off. lastNotifiedAt is updated.

Escalation policies (design §4 table 14, the P6 piece):

  • Tables alerts_escalation_policy (name, description, repeatAfterMinutes, maxRepeats, enabled) and alerts_escalation_step (stepOrder, afterMinutes, targetType USER/TEAM/CHANNEL plus target, onlyIfStatus). Steps are a child list on the policy record, the policy table is with the Alerts admin tables, and both need alerts.admin.
  • escalationPolicyId on types and groups; the type's policy overrides the group's.
  • A hidden scheduled job, on the configured scheduler and idempotent within the minute: when an alert reaches the next step's age, it notifies that step's target with kind ESCALATED, writes an ESCALATED event (from and to level) and sets escalationLevel. After the last step it repeats every repeatAfterMinutes, up to maxRepeats. Acknowledge, snooze, resolve and mute stop escalation and reminders; a reopen resets them.
  • AlertRouter gets remind(...) and escalate(...) entry points on top of the existing delivery checks.

EXPIRED: the design doesn't define when an alert expires, so the value stays reserved and is left out of the selectable resolutions (README says so).

Schema: guarded changesets 23-26 (#28 takes 21-22). If this merges before #28, I'll renumber on the merge.

Limit: the job assumes one scheduler node; there's no distributed lock yet. kof22 runs a single scheduler.

Tests: mvnw -o -B verify green (127 tests, 94.1% instruction coverage, checkstyle, guardrails). The 22 timed-notification tests cover reminders that are due, not due, acknowledged, snoozed or disabled; step 1 then step 2 by time; capped repeats; stopping on acknowledge; USER, TEAM and CHANNEL targets; and an idempotent re-run.

🤖 Generated with Claude Code

Part 2 of 3 for #27: reminders and escalation. Part 3 is #28 (team sources and mutes); part 1 (quiet hours and digests) is a separate PR. **Reminders:** an OPEN alert that isn't acknowledged, snoozed, resolved or muted sends a REMINDER through the normal router (subscription event filters, visibility, suppression) every `reminderIntervalMinutes` of its type. CRITICAL defaults to 60 minutes as the design says, and an explicit 0 turns reminders off. `lastNotifiedAt` is updated. **Escalation policies** (design §4 table 14, the P6 piece): - Tables `alerts_escalation_policy` (name, description, repeatAfterMinutes, maxRepeats, enabled) and `alerts_escalation_step` (stepOrder, afterMinutes, targetType USER/TEAM/CHANNEL plus target, onlyIfStatus). Steps are a child list on the policy record, the policy table is with the Alerts admin tables, and both need `alerts.admin`. - `escalationPolicyId` on types and groups; the type's policy overrides the group's. - A hidden scheduled job, on the configured scheduler and idempotent within the minute: when an alert reaches the next step's age, it notifies that step's target with kind ESCALATED, writes an `ESCALATED` event (from and to level) and sets `escalationLevel`. After the last step it repeats every `repeatAfterMinutes`, up to `maxRepeats`. Acknowledge, snooze, resolve and mute stop escalation and reminders; a reopen resets them. - `AlertRouter` gets `remind(...)` and `escalate(...)` entry points on top of the existing delivery checks. **EXPIRED:** the design doesn't define when an alert expires, so the value stays reserved and is left out of the selectable resolutions (README says so). **Schema:** guarded changesets **23-26** (#28 takes 21-22). If this merges before #28, I'll renumber on the merge. **Limit:** the job assumes one scheduler node; there's no distributed lock yet. kof22 runs a single scheduler. **Tests:** `mvnw -o -B verify` green (127 tests, 94.1% instruction coverage, checkstyle, guardrails). The 22 timed-notification tests cover reminders that are due, not due, acknowledged, snoozed or disabled; step 1 then step 2 by time; capped repeats; stopping on acknowledge; USER, TEAM and CHANNEL targets; and an idempotent re-run. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.