## Purpose

The automated reminder engine — the most important piece of the system: plan T-15/T-7/T-2 waves per event, send them exactly once, at the right local time, never during quiet hours, and never to guests who cancelled.

## ADDED Requirements

### Requirement: Reminder schedule (FR-6.5, FR-4.11, P0)
Reminders SHALL fire at T-15, T-7, and T-2 days before `event.starts_at`, at a configurable local send time (default 10:00 Asia/Dhaka), delivered via email (the only v1 channel; the schedule's channel field remains so v2 can add more). Per event, admins can enable/disable each wave, choose the send time, and preview upcoming waves with exact dates and recipient counts — including how many recipients have no email address and will be skipped.

#### Scenario: Wave timing
- **WHEN** an event starts 2026-09-12 19:00 Asia/Dhaka with default settings
- **THEN** waves are scheduled for Aug 28, Sep 5, and Sep 10 at 10:00 Asia/Dhaka (converted correctly to UTC)

#### Scenario: Wave preview
- **WHEN** the admin opens reminder settings
- **THEN** each enabled wave shows its exact send date and current recipient count

### Requirement: Audience selection (FR-6.6, P0)
The default reminder audience SHALL be `accepted` guests. Optionally, a parallel "still waiting for your reply" nudge can target `pending` guests.

#### Scenario: Default audience
- **WHEN** a reminder wave fires with default settings
- **THEN** only invitations currently in `accepted` status receive it

### Requirement: Late setup skips past waves (FR-6.7, P0)
If the system is set up fewer than 15 (or 7) days before an event, past waves SHALL be skipped automatically — a late reminder is never sent.

#### Scenario: Setup 10 days out
- **WHEN** reminders are enabled 10 days before the event
- **THEN** the T-15 wave is skipped and only T-7 and T-2 are scheduled

### Requirement: Exactly-once delivery (FR-6.8, §7.3, P0)
The same guest MUST never receive the same reminder twice, even if the planner runs twice or the server restarts mid-batch. Planning uses an idempotency key per invitation × schedule × channel with insert-if-absent semantics; the planner is safe to run every hour forever.

#### Scenario: Planner runs twice
- **WHEN** the hourly planner runs twice over the same wave
- **THEN** no duplicate message jobs are created

#### Scenario: Restart mid-batch
- **WHEN** the worker restarts while a wave is half-sent
- **THEN** already-sent jobs are not re-sent and unsent jobs still go out

### Requirement: Cancellation-aware sending (§7.3, P0)
At send time, the sender SHALL re-check the invitation's live status: if cancelled or declined (and the audience was `accepted`), the job is marked `skipped` and not sent. This is what makes cancellations take effect.

#### Scenario: Cancel between planning and sending
- **WHEN** a guest cancels after a reminder job was planned but before it sends
- **THEN** the job is skipped at send time

### Requirement: Quiet hours (FR-6.9, P0)
No messages SHALL be sent between 22:00 and 08:00 local time; anything due in that window is deferred to 08:00.

#### Scenario: Retry lands at night
- **WHEN** a backoff retry becomes due at 23:30 local
- **THEN** it is deferred to 08:00

### Requirement: Date change re-planning (§12 risk 10, P0)
Editing an event's `starts_at` SHALL re-plan all future reminder jobs against the new date (obsolete jobs skipped, new jobs planned idempotently).

#### Scenario: Event postponed
- **WHEN** the event date moves one week later after T-15 already fired
- **THEN** T-7 and T-2 jobs are re-planned to the new date and no stale-date reminder fires

### Requirement: Failure alerting (§7.7, P1)
If more than 5% of a send wave fails, the system SHALL alert the admin by email. A health endpoint reports DB, queue, and provider reachability.

#### Scenario: Wave failure alert
- **WHEN** 8% of a reminder wave's jobs end in `failed`
- **THEN** the admin receives an alert email identifying the wave and failure count
