Setup Ralph.

This commit is contained in:
2026-01-10 17:13:18 +00:00
parent f15e093254
commit d7ecc2944d
14 changed files with 1287 additions and 11 deletions

110
specs/notifications.md Normal file
View File

@@ -0,0 +1,110 @@
# Notifications Specification
## Job to Be Done
When I wake up each morning, I want to receive an email with my training decision, so that I don't need to open the app to know if I should train.
## Email Notifications
### Daily Training Email
Sent at user's preferred `notificationTime` (default: 07:00).
**Subject:**
```
PhaseFlow: [STATUS] - Day [cycleDay] ([phase])
```
Example: `PhaseFlow: ✅ TRAIN - Day 12 (FOLLICULAR)`
**Body:**
```
Good morning!
Today's Decision: [STATUS]
Reason: [reason]
Current Metrics:
- Cycle Day: [cycleDay] ([phase])
- Body Battery: [bbCurrent]
- HRV: [hrvStatus]
- Week Intensity: [weekIntensity]/[phaseLimit] min
Nutrition Today:
- Seeds: [seeds]
- Carbs: [carbRange]
- Keto: [ketoGuidance]
[Optional: Seed switch alert if day 15]
[Optional: Token expiration warning]
---
PhaseFlow - Training with your cycle, not against it
```
### Token Expiration Warnings
**14 Days Before:**
Subject: `⚠️ PhaseFlow: Garmin tokens expire in 14 days`
**7 Days Before:**
Subject: `🚨 PhaseFlow: Garmin tokens expire in 7 days - action required`
## Email Provider
Using Resend via `resend` npm package.
**Configuration:**
- `RESEND_API_KEY` - API key for Resend
- `EMAIL_FROM` - Sender address (e.g., `PhaseFlow <noreply@phaseflow.app>`)
## Email Utilities (`src/lib/email.ts`)
**Functions:**
- `sendDailyNotification(user, decision, dailyData)` - Send morning email
- `sendTokenExpirationWarning(user, daysUntilExpiry)` - Send warning email
## Cron Jobs
### `/api/cron/notifications`
Protected by `CRON_SECRET` header.
**Trigger:** Daily at notification times
**Process:**
1. Find all users with `notificationTime` matching current hour
2. For each user:
- Fetch current decision from decision engine
- Send email via Resend
- Update `DailyLog.notificationSentAt`
### Timezone Handling
Users store preferred timezone (e.g., `America/New_York`).
Cron runs every hour. Check if current hour in user's timezone matches their `notificationTime`.
## Rate Limiting
- Max 1 notification per user per day
- Check `DailyLog.notificationSentAt` before sending
- Only send if null or different date
## Success Criteria
1. Email arrives within 5 minutes of scheduled time
2. Email contains all relevant metrics and guidance
3. Token warnings sent at 14 and 7 day thresholds
4. No duplicate notifications on same day
## Acceptance Tests
- [ ] Daily email contains decision status and reason
- [ ] Daily email includes nutrition guidance
- [ ] Seed switch alert included on day 15
- [ ] Token warning email sent at 14-day threshold
- [ ] Token warning email sent at 7-day threshold
- [ ] Duplicate notifications prevented
- [ ] Timezone conversion correct for notification time
- [ ] CRON_SECRET required for endpoint access