Files
phaseflow/IMPLEMENTATION_PLAN.md
Petru Paler 58f6c5605a
All checks were successful
Deploy / deploy (push) Successful in 1m36s
Add period prediction accuracy feedback (P4.5 complete)
Implements visual feedback for cycle prediction accuracy in ICS calendar feeds:

- Add predictedDate field to PeriodLog type for tracking predicted vs actual dates
- POST /api/cycle/period now calculates and stores predictedDate based on
  previous lastPeriodDate + cycleLength, returns daysEarly/daysLate in response
- ICS feed generates "(Predicted)" events when actual period start differs
  from predicted, with descriptions like "period arrived 2 days early"
- Calendar route fetches period logs and passes them to ICS generator

This creates an accuracy feedback loop helping users understand their cycle
variability over time per calendar.md spec.

807 tests passing across 43 test files.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 22:21:52 +00:00

979 lines
58 KiB
Markdown

# PhaseFlow Implementation Plan
This file is maintained by Ralph. Run `./ralph-sandbox.sh plan 3` to generate tasks.
## Current State Summary
### Overall Status: 807 tests passing across 43 test files
### Library Implementation
| File | Status | Gap Analysis |
|------|--------|--------------|
| `cycle.ts` | **COMPLETE** | 9 tests covering all functions, production-ready |
| `nutrition.ts` | **COMPLETE** | 17 tests covering getNutritionGuidance, getSeedSwitchAlert, phase-specific carb ranges, keto guidance |
| `email.ts` | **COMPLETE** | 24 tests covering sendDailyEmail, sendPeriodConfirmationEmail, sendTokenExpirationWarning, email formatting, subject lines |
| `ics.ts` | **COMPLETE** | 28 tests covering generateIcsFeed (90 days of phase events), ICS format validation, timezone handling, period prediction feedback |
| `encryption.ts` | **COMPLETE** | 14 tests covering AES-256-GCM encrypt/decrypt round-trip, error handling, key validation |
| `decision-engine.ts` | **COMPLETE** | 8 priority rules + override handling with `getDecisionWithOverrides()`, 24 tests |
| `garmin.ts` | **COMPLETE** | 33 tests covering fetchGarminData, fetchHrvStatus, fetchBodyBattery, fetchIntensityMinutes, isTokenExpired, daysUntilExpiry, error handling, token validation |
| `pocketbase.ts` | **COMPLETE** | 9 tests covering `createPocketBaseClient()`, `isAuthenticated()`, `getCurrentUser()`, `loadAuthFromCookies()` |
| `auth-middleware.ts` | **COMPLETE** | 9 tests covering `withAuth()` wrapper for API route protection, structured logging for auth failures |
| `middleware.ts` (Next.js) | **COMPLETE** | 12 tests covering page protection, redirects to login |
| `logger.ts` | **COMPLETE** | 16 tests covering JSON output, log levels, error stack traces, child loggers |
| `metrics.ts` | **COMPLETE** | 33 tests covering metrics collection, counters, gauges, histograms, Prometheus format |
### Infrastructure Gaps (from specs/ - pending implementation)
| Gap | Spec Reference | Task | Priority |
|-----|----------------|------|----------|
| Health Check Endpoint | specs/observability.md | P2.15 | **COMPLETE** |
| Prometheus Metrics | specs/observability.md | P2.16 | **COMPLETE** |
| Structured Logging (pino) | specs/observability.md | P2.17 | **COMPLETE** |
| OIDC Authentication | specs/authentication.md | P2.18 | **COMPLETE** |
| Token Expiration Warnings | specs/email.md | P3.9 | **COMPLETE** |
### API Routes (17 total)
| Route | Status | Notes |
|-------|--------|-------|
| GET /api/user | **COMPLETE** | Returns user profile with `withAuth()` |
| PATCH /api/user | **COMPLETE** | Updates cycleLength, notificationTime, timezone (17 tests) |
| POST /api/cycle/period | **COMPLETE** | Logs period start, updates user, creates PeriodLog with prediction tracking (13 tests) |
| GET /api/cycle/current | **COMPLETE** | Returns cycle day, phase, config, daysUntilNextPhase (10 tests) |
| GET /api/today | **COMPLETE** | Returns decision, cycle, biometrics, nutrition (22 tests) |
| POST /api/overrides | **COMPLETE** | Adds override to user.activeOverrides (14 tests) |
| DELETE /api/overrides | **COMPLETE** | Removes override from user.activeOverrides (14 tests) |
| POST /api/garmin/tokens | **COMPLETE** | Stores encrypted Garmin OAuth tokens (15 tests) |
| DELETE /api/garmin/tokens | **COMPLETE** | Clears tokens and disconnects Garmin (15 tests) |
| GET /api/garmin/status | **COMPLETE** | Returns connection status, expiry, warning level (11 tests) |
| GET /api/calendar/[userId]/[token].ics | **COMPLETE** | Token validation, ICS generation with period prediction feedback, caching headers (11 tests) |
| POST /api/calendar/regenerate-token | **COMPLETE** | Generates 32-char token, returns URL (9 tests) |
| POST /api/cron/garmin-sync | **COMPLETE** | Syncs Garmin data for all users, creates DailyLogs, sends token expiration warnings (32 tests) |
| POST /api/cron/notifications | **COMPLETE** | Sends daily emails with timezone matching, DailyLog handling (20 tests) |
| GET /api/history | **COMPLETE** | Paginated historical daily logs with date filtering (19 tests) |
| GET /api/health | **COMPLETE** | Health check for deployment monitoring (14 tests) |
| GET /metrics | **COMPLETE** | 33 tests (18 lib + 15 route) |
### Pages (7 total)
| Page | Status | Notes |
|------|--------|-------|
| Dashboard (`/`) | **COMPLETE** | Wired with /api/today, DecisionCard, DataPanel, NutritionPanel, OverrideToggles |
| Login (`/login`) | **COMPLETE** | Email/password form with auth, error handling, loading states, rate limiting |
| Settings (`/settings`) | **COMPLETE** | Form with cycleLength, notificationTime, timezone |
| Settings/Garmin (`/settings/garmin`) | **COMPLETE** | Token management UI, connection status, disconnect functionality, 27 tests |
| Calendar (`/calendar`) | **COMPLETE** | MonthView with navigation, ICS subscription section, token regeneration, 23 tests |
| History (`/history`) | **COMPLETE** | Table view with date filtering, pagination, decision styling, 26 tests |
| Plan (`/plan`) | **COMPLETE** | Phase overview, training guidelines, rebounding techniques, 16 tests |
### Components
| Component | Status | Notes |
|-----------|--------|-------|
| `DecisionCard` | **COMPLETE** | Displays status, icon, reason |
| `DataPanel` | **COMPLETE** | Shows BB, HRV, intensity data |
| `NutritionPanel` | **COMPLETE** | Shows seeds, carbs, keto guidance |
| `OverrideToggles` | **COMPLETE** | Toggle buttons with callbacks |
| `DayCell` | **COMPLETE** | Phase-colored day with click handler |
| `MiniCalendar` | **COMPLETE** | Compact calendar widget with phase colors, navigation, legend (23 tests) |
| `OnboardingBanner` | **COMPLETE** | Setup prompts for new users with icons and action buttons, 16 tests |
| `MonthView` | **COMPLETE** | Calendar grid with DayCell integration, navigation controls, phase legend, keyboard navigation |
### Test Coverage
| Test File | Status |
|-----------|--------|
| `src/lib/cycle.test.ts` | **EXISTS** - 9 tests |
| `src/lib/decision-engine.test.ts` | **EXISTS** - 24 tests (8 algorithmic rules + 16 override scenarios) |
| `src/lib/pocketbase.test.ts` | **EXISTS** - 9 tests (auth helpers, cookie loading) |
| `src/lib/auth-middleware.test.ts` | **EXISTS** - 9 tests (withAuth wrapper, error handling, structured logging) |
| `src/lib/logger.test.ts` | **EXISTS** - 16 tests (JSON format, log levels, error serialization, child loggers) |
| `src/lib/metrics.test.ts` | **EXISTS** - 18 tests (metrics collection, counters, gauges, histograms, Prometheus format) |
| `src/middleware.test.ts` | **EXISTS** - 12 tests (page protection, public routes, static assets) |
| `src/app/api/user/route.test.ts` | **EXISTS** - 21 tests (GET/PATCH profile, auth, validation, security) |
| `src/app/api/cycle/period/route.test.ts` | **EXISTS** - 13 tests (POST period, auth, validation, date checks, prediction tracking) |
| `src/app/api/cycle/current/route.test.ts` | **EXISTS** - 10 tests (GET current cycle, auth, all phases, rollover, custom lengths) |
| `src/app/api/today/route.test.ts` | **EXISTS** - 22 tests (daily snapshot, auth, decision, overrides, phases, nutrition, biometrics) |
| `src/app/api/overrides/route.test.ts` | **EXISTS** - 14 tests (POST/DELETE overrides, auth, validation, type checks) |
| `src/app/login/page.test.tsx` | **EXISTS** - 32 tests (form rendering, auth flow, error handling, validation, accessibility, rate limiting) |
| `src/app/page.test.tsx` | **EXISTS** - 28 tests (data fetching, component rendering, override toggles, error handling) |
| `src/lib/nutrition.test.ts` | **EXISTS** - 17 tests (seed cycling, carb ranges, keto guidance by phase) |
| `src/lib/email.test.ts` | **EXISTS** - 24 tests (email content, subject lines, formatting, token expiration warnings) |
| `src/lib/ics.test.ts` | **EXISTS** - 28 tests (ICS format validation, 90-day event generation, timezone handling, period prediction feedback) |
| `src/lib/encryption.test.ts` | **EXISTS** - 14 tests (encrypt/decrypt round-trip, error handling, key validation) |
| `src/lib/garmin.test.ts` | **EXISTS** - 33 tests (fetchGarminData, fetchHrvStatus, fetchBodyBattery, fetchIntensityMinutes, token expiry, error handling) |
| `src/app/api/garmin/tokens/route.test.ts` | **EXISTS** - 15 tests (POST/DELETE tokens, encryption, validation, auth) |
| `src/app/api/garmin/status/route.test.ts` | **EXISTS** - 11 tests (connection status, expiry, warning levels) |
| `src/app/api/cron/garmin-sync/route.test.ts` | **EXISTS** - 32 tests (auth, user iteration, token handling, Garmin data fetching, DailyLog creation, token expiration warnings, error handling) |
| `src/app/api/cron/notifications/route.test.ts` | **EXISTS** - 20 tests (timezone matching, DailyLog handling, email sending) |
| `src/app/api/calendar/[userId]/[token].ics/route.test.ts` | **EXISTS** - 11 tests (token validation, ICS generation with period prediction feedback, caching, error handling) |
| `src/app/api/calendar/regenerate-token/route.test.ts` | **EXISTS** - 9 tests (token generation, URL formatting, auth) |
| `src/app/api/history/route.test.ts` | **EXISTS** - 19 tests (pagination, date filtering, auth, validation) |
| `src/app/api/health/route.test.ts` | **EXISTS** - 14 tests (healthy/unhealthy states, PocketBase connectivity, error handling) |
| `src/app/history/page.test.tsx` | **EXISTS** - 26 tests (rendering, data loading, pagination, date filtering, styling) |
| `src/app/api/metrics/route.test.ts` | **EXISTS** - 15 tests (Prometheus format validation, metric types, route handling) |
| `src/components/calendar/month-view.test.tsx` | **EXISTS** - 30 tests (calendar grid, phase colors, navigation, legend, keyboard navigation) |
| `src/app/calendar/page.test.tsx` | **EXISTS** - 23 tests (rendering, navigation, ICS subscription, token regeneration) |
| `src/app/settings/page.test.tsx` | **EXISTS** - 29 tests (form rendering, validation, submission, accessibility) |
| `src/app/settings/garmin/page.test.tsx` | **EXISTS** - 27 tests (connection status, token management) |
| `src/components/dashboard/decision-card.test.tsx` | **EXISTS** - 11 tests (rendering, status icons, styling) |
| `src/components/dashboard/data-panel.test.tsx` | **EXISTS** - 18 tests (biometrics display, null handling, styling) |
| `src/components/dashboard/nutrition-panel.test.tsx` | **EXISTS** - 12 tests (seeds, carbs, keto guidance) |
| `src/components/dashboard/override-toggles.test.tsx` | **EXISTS** - 18 tests (toggle states, callbacks, styling) |
| `src/components/dashboard/mini-calendar.test.tsx` | **EXISTS** - 23 tests (calendar grid, phase colors, navigation, legend) |
| `src/components/dashboard/onboarding-banner.test.tsx` | **EXISTS** - 16 tests (setup prompts, icons, action buttons, interactions, dismissal) |
| `src/components/calendar/day-cell.test.tsx` | **EXISTS** - 27 tests (phase coloring, today highlighting, click handling, accessibility) |
| `src/app/plan/page.test.tsx` | **EXISTS** - 16 tests (loading states, error handling, phase display, exercise reference, rebounding techniques) |
| `src/app/layout.test.tsx` | **EXISTS** - 3 tests (skip navigation link rendering, accessibility) |
| E2E tests | **AUTHORIZED SKIP** - Per specs/testing.md |
### Critical Business Rules (from Spec)
1. **Override Priority:** flare > stress > sleep > pms (must be enforced in order)
2. **HRV Unbalanced:** ALWAYS forces REST (highest algorithmic priority, non-overridable)
3. **Phase Limits:** Strictly enforced per phase configuration
4. **Token Expiration Warnings:** Must send email at 14 days and 7 days before expiry (IMPLEMENTED - P3.9 COMPLETE)
5. **ICS Feed:** Generates 90 days of phase events for calendar subscription
---
## P0: Critical Blockers ✅ ALL COMPLETE
These must be completed first - nothing else works without them.
### P0.1: PocketBase Auth Helpers ✅ COMPLETE
- [x] Add authentication utilities to pocketbase.ts
- **Files:**
- `src/lib/pocketbase.ts` - Added `createPocketBaseClient()`, `getCurrentUser()`, `isAuthenticated()`, `loadAuthFromCookies()`
- **Tests:**
- `src/lib/pocketbase.test.ts` - 9 tests covering auth state management, cookie loading
- **Why:** Every protected route and page depends on these helpers
- **Blocking:** P0.2, P0.4, P1.1-P1.7, P2.2-P2.13
### P0.2: Auth Middleware for API Routes ✅ COMPLETE
- [x] Create reusable auth middleware for protected API endpoints
- **Files:**
- `src/lib/auth-middleware.ts` - Added `withAuth()` wrapper for route handlers
- `src/middleware.ts` - Added Next.js middleware for page protection
- **Tests:**
- `src/lib/auth-middleware.test.ts` - 6 tests covering unauthorized rejection, user context passing, error handling
- `src/middleware.test.ts` - 12 tests covering protected routes, public routes, API routes, static assets
- **Why:** All API routes except `/api/calendar/[userId]/[token].ics` and `/api/cron/*` require auth
- **Depends On:** P0.1
- **Blocking:** P0.4, P1.1-P1.5
### P0.3: Decision Engine Override Handling ✅ COMPLETE
- [x] Add override priority logic before algorithmic decision
- **Files:**
- `src/lib/decision-engine.ts` - Added `getDecisionWithOverrides(data, overrides)` function
- **Tests:**
- `src/lib/decision-engine.test.ts` - 24 tests covering all 8 priority rules + override scenarios
- **Override Priority (enforced in this order):**
1. `flare` - Always forces REST
2. `stress` - Forces REST
3. `sleep` - Forces REST
4. `pms` - Forces REST
- **Why:** Overrides are core to the user experience per spec
- **Blocking:** P1.4, P1.5
### P0.4: GET /api/user Implementation ✅ COMPLETE
- [x] Return authenticated user profile
- **Files:**
- `src/app/api/user/route.ts` - Implemented GET handler with `withAuth()` wrapper
- **Tests:**
- `src/app/api/user/route.test.ts` - 4 tests covering auth, response shape, sensitive field exclusion
- **Response Shape:**
- `id`, `email`, `garminConnected`, `cycleLength`, `lastPeriodDate`, `notificationTime`, `timezone`, `activeOverrides`
- Excludes sensitive fields: `garminOauth1Token`, `garminOauth2Token`, `calendarToken`
- **Why:** Dashboard and all pages need user context
- **Depends On:** P0.1, P0.2
- **Blocking:** P1.7, P2.9, P2.10
---
## P1: Core Functionality ✅ ALL COMPLETE
Minimum viable product - app can be used for daily decisions.
### P1.1: PATCH /api/user Implementation ✅ COMPLETE
- [x] Allow profile updates (cycleLength, notificationTime, timezone)
- **Files:**
- `src/app/api/user/route.ts` - Implemented PATCH handler with validation
- **Tests:**
- `src/app/api/user/route.test.ts` - 17 tests covering field validation, persistence, security
- **Validation Rules:**
- `cycleLength`: number, range 21-45 days
- `notificationTime`: string, HH:MM format (24-hour)
- `timezone`: non-empty string
- **Security:** Ignores attempts to update non-updatable fields (email, tokens)
- **Why:** Users need to configure their cycle and preferences
- **Depends On:** P0.1, P0.2
### P1.2: POST /api/cycle/period Implementation ✅ COMPLETE
- [x] Log period start date, update user record, create PeriodLog
- **Files:**
- `src/app/api/cycle/period/route.ts` - Implemented POST handler with validation and prediction tracking
- **Tests:**
- `src/app/api/cycle/period/route.test.ts` - 13 tests covering auth, date validation, user update, PeriodLog creation, prediction tracking
- **Why:** Cycle tracking is the foundation of all recommendations
- **Depends On:** P0.1, P0.2
### P1.3: GET /api/cycle/current Implementation ✅ COMPLETE
- [x] Return current cycle day, phase, and phase config
- **Files:**
- `src/app/api/cycle/current/route.ts` - Implemented GET using cycle.ts utilities with `withAuth()` wrapper
- **Tests:**
- `src/app/api/cycle/current/route.test.ts` - 10 tests covering auth, validation, all phases, cycle rollover, custom cycle lengths
- **Response Shape:**
- `cycleDay`, `phase`, `phaseConfig`, `daysUntilNextPhase`, `cycleLength`
- **Why:** Dashboard needs this for display
- **Depends On:** P0.1, P0.2, P1.2
### P1.4: GET /api/today Implementation ✅ COMPLETE
- [x] Return complete daily snapshot with decision, biometrics, nutrition
- **Files:**
- `src/app/api/today/route.ts` - Implemented GET with `withAuth()` wrapper, aggregates cycle, biometrics, and nutrition
- **Tests:**
- `src/app/api/today/route.test.ts` - 22 tests covering auth, validation, decision calculation, overrides, phases, nutrition
- **Response Shape:**
- `decision` (status, reason, icon), `cycleDay`, `phase`, `phaseConfig`, `daysUntilNextPhase`, `cycleLength`
- `biometrics` (hrvStatus, bodyBatteryCurrent, bodyBatteryYesterdayLow, weekIntensityMinutes, phaseLimit)
- `nutrition` (seeds, carbRange, ketoGuidance)
- **Fallback Behavior:** When no DailyLog exists (Garmin not synced), returns defaults: hrvStatus="Unknown", BB=100, weekIntensity=0
- **Why:** This is THE core API for the dashboard
- **Depends On:** P0.1, P0.2, P0.3, P1.3
### P1.5: POST/DELETE /api/overrides Implementation ✅ COMPLETE
- [x] Toggle override flags on user record
- **Files:**
- `src/app/api/overrides/route.ts` - Implemented POST (add) and DELETE (remove) handlers with validation
- **Tests:**
- `src/app/api/overrides/route.test.ts` - 14 tests covering auth, override types, persistence, validation, edge cases
- **Override Types:** flare, stress, sleep, pms
- **POST Response:** Returns updated user with new override added to activeOverrides array
- **DELETE Response:** Returns updated user with override removed from activeOverrides array
- **Validation:** Rejects invalid override types, duplicates on POST, missing overrides on DELETE
- **Why:** Emergency overrides are critical for flare days
- **Depends On:** P0.1, P0.2, P0.3
### P1.6: Login Page Implementation ✅ COMPLETE
- [x] Functional login form with PocketBase auth
- **Files:**
- `src/app/login/page.tsx` - Client component with email/password form, error handling, loading states, redirect
- **Tests:**
- `src/app/login/page.test.tsx` - 14 tests covering rendering, form submission, auth flow, error handling, validation
- **Infrastructure Added:**
- `src/test-setup.ts` - Global test setup with @testing-library/jest-dom and cleanup
- Updated `vitest.config.ts` to include setupFiles
- **Why:** Users need to authenticate to use the app
- **Depends On:** P0.1
### P1.7: Dashboard Page Implementation ✅ COMPLETE
- [x] Wire up dashboard with real data from /api/today
- [x] Integrate DecisionCard, DataPanel, NutritionPanel, OverrideToggles
- [x] Implement override toggle functionality with optimistic updates
- [x] Add error handling and loading states
- **Files:**
- `src/app/page.tsx` - Client component fetching /api/today, rendering all dashboard components
- **Tests:**
- `src/app/page.test.tsx` - 23 tests covering data fetching, component rendering, override toggles, error handling
- **Features Implemented:**
- Real-time decision display with cycle phase information
- Biometrics panel (HRV, Body Battery, Intensity Minutes)
- Nutrition guidance panel (seeds, carbs, keto)
- Override toggles with optimistic UI updates
- Error boundaries and loading states
- **Why:** This is the main user interface
- **Depends On:** P0.4, P1.3, P1.4, P1.5
---
## P2: Important Features
Full feature set for production use.
### P2.1: Garmin Data Fetching Functions ✅ COMPLETE
- [x] Add specific fetchers for HRV, Body Battery, Intensity Minutes
- **Files:**
- `src/lib/garmin.ts` - Added `fetchHrvStatus()`, `fetchBodyBattery()`, `fetchIntensityMinutes()`
- **Tests:**
- `src/lib/garmin.test.ts` - 33 tests covering API calls, response parsing, error handling (increased from 14 tests)
- **Functions Implemented:**
- `fetchHrvStatus()` - Fetches HRV status (balanced/unbalanced) from Garmin
- `fetchBodyBattery()` - Fetches current and yesterday's low body battery values
- `fetchIntensityMinutes()` - Fetches weekly moderate + vigorous intensity minutes
- **Why:** Real biometric data is required for accurate decisions
### P2.2: POST/DELETE /api/garmin/tokens Implementation ✅ COMPLETE
- [x] Store encrypted Garmin OAuth tokens
- **Files:**
- `src/app/api/garmin/tokens/route.ts` - POST/DELETE handlers with encryption, validation
- **Tests:**
- `src/app/api/garmin/tokens/route.test.ts` - 15 tests covering encryption, validation, storage, auth, deletion
- **Features Implemented:**
- POST: Accepts oauth1, oauth2, expires_at; encrypts tokens; stores in user record
- DELETE: Clears tokens and sets garminConnected to false
- Validation for required fields and types
- Returns daysUntilExpiry in POST response
- **Why:** Users need to connect their Garmin accounts
- **Depends On:** P0.1, P0.2
### P2.3: GET /api/garmin/status Implementation ✅ COMPLETE
- [x] Return Garmin connection status and days until expiry
- **Files:**
- `src/app/api/garmin/status/route.ts` - GET handler with expiry calculation
- **Tests:**
- `src/app/api/garmin/status/route.test.ts` - 11 tests covering connected/disconnected states, expiry calc, warning levels
- **Response Shape:**
- `connected` - Boolean indicating if tokens exist
- `daysUntilExpiry` - Days until token expires (null if not connected)
- `expired` - Boolean indicating if tokens have expired
- `warningLevel` - "critical" (<=7 days), "warning" (8-14 days), or null
- **Why:** Users need visibility into their Garmin connection
- **Depends On:** P0.1, P0.2, P2.1
### P2.4: POST /api/cron/garmin-sync Implementation ✅ COMPLETE
- [x] Daily sync of all Garmin data for all users
- **Files:**
- `src/app/api/cron/garmin-sync/route.ts` - Iterates users, fetches data, stores DailyLog
- **Tests:**
- `src/app/api/cron/garmin-sync/route.test.ts` - 32 tests covering auth, user iteration, token handling, Garmin data fetching, DailyLog creation, token expiration warnings, error handling
- **Features Implemented:**
- Fetches all users with garminConnected=true
- Skips users with expired tokens
- Decrypts OAuth2 tokens and fetches HRV, Body Battery, Intensity Minutes
- Calculates cycle day, phase, phase limit, remaining minutes
- Computes training decision using decision engine
- Creates DailyLog entries for each user
- Sends token expiration warning emails at 14 and 7 days before expiry
- Returns sync summary (usersProcessed, errors, skippedExpired, timestamp)
- **Why:** Automated data sync is required for morning notifications
- **Depends On:** P2.1, P2.2
### P2.5: POST /api/cron/notifications Implementation ✅ COMPLETE
- [x] Send daily email notifications at user's preferred time
- **Files:**
- `src/app/api/cron/notifications/route.ts` - Timezone-aware user matching, DailyLog fallback, email sending
- **Tests:**
- `src/app/api/cron/notifications/route.test.ts` - 20 tests covering timezone matching, DailyLog handling, email sending
- **Features Implemented:**
- Timezone-aware notification matching (finds users whose notificationTime matches current hour in their timezone)
- DailyLog-based notifications with fallback to real-time calculation when DailyLog missing
- Duplicate prevention (only sends once per user per hour)
- Nutrition guidance integration (seeds, carbs, keto)
- CRON_SECRET authentication
- Returns summary with emailsSent count and timestamp
- **Why:** Email notifications are a key feature per spec
- **Depends On:** P2.4
### P2.6: GET /api/calendar/[userId]/[token].ics Implementation ✅ COMPLETE
- [x] Return ICS feed for calendar subscription
- **Files:**
- `src/app/api/calendar/[userId]/[token].ics/route.ts` - Validates token, generates ICS with 90 days of phase events and period prediction feedback
- **Tests:**
- `src/app/api/calendar/[userId]/[token].ics/route.test.ts` - 11 tests covering token validation, ICS generation with period predictions, caching headers, error handling
- **Features Implemented:**
- Token-based authentication (no session required)
- Validates calendar token against user record
- Fetches period logs and passes them to ICS generator for prediction feedback
- Generates 90 days of phase events using `generateIcsFeed()`
- Returns proper Content-Type header (`text/calendar; charset=utf-8`)
- Caching headers for calendar client optimization
- 404 for non-existent users, 401 for invalid tokens
- **Why:** Calendar integration for external apps
### P2.7: POST /api/calendar/regenerate-token Implementation ✅ COMPLETE
- [x] Generate new calendar token
- **Files:**
- `src/app/api/calendar/regenerate-token/route.ts` - Creates random 32-char token, updates user
- **Tests:**
- `src/app/api/calendar/regenerate-token/route.test.ts` - 9 tests covering token generation, URL formatting, auth
- **Features Implemented:**
- Requires authentication via `withAuth()` middleware
- Generates cryptographically secure 32-character hex token
- Updates user's `calendarToken` field in database
- Returns new token and formatted calendar URL
- Old tokens immediately invalidated
- **Why:** Security feature for calendar URLs
- **Depends On:** P0.1, P0.2
### P2.8: GET /api/history Implementation ✅ COMPLETE
- [x] Return paginated historical daily logs
- **Files:**
- `src/app/api/history/route.ts` - Query DailyLog with pagination, date filtering, validation
- **Tests:**
- `src/app/api/history/route.test.ts` - 19 tests covering pagination, date filtering, auth, validation
- **Features Implemented:**
- Pagination with page/limit parameters (default: page=1, limit=20)
- Date filtering with startDate/endDate query params (YYYY-MM-DD format)
- Validation for all parameters with descriptive error messages
- Sort by date descending (most recent first)
- Returns items, total, page, limit, totalPages, hasMore
- **Why:** Users want to see their training history
- **Depends On:** P0.1, P0.2
### P2.9: Settings Page Implementation ✅ COMPLETE
- [x] User profile management UI
- **Files:**
- `src/app/settings/page.tsx` - Form for cycleLength, notificationTime, timezone with validation, loading states, error handling
- **Tests:**
- `src/app/settings/page.test.tsx` - 24 tests covering rendering, data loading, form submission, validation, error handling
- **Why:** Users need to configure their preferences
- **Depends On:** P0.4, P1.1
### P2.10: Settings/Garmin Page Implementation ✅ COMPLETE
- [x] Garmin connection management UI
- **Files:**
- `src/app/settings/garmin/page.tsx` - Token input form, connection status, expiry warnings, disconnect button
- **Tests:**
- `src/app/settings/garmin/page.test.tsx` - 27 tests covering rendering, connection states, warning levels, token submission, disconnect flow
- `src/app/settings/page.test.tsx` - 3 additional tests for Garmin link (28 total)
- **Features Implemented:**
- Connection status display with green/red/gray indicators
- Token expiry warnings (yellow for 14 days, red for 7 days)
- Token input form with JSON validation
- Instructions for running bootstrap script
- Disconnect functionality
- Loading and error states
- **Why:** Users need to manage their Garmin connection
- **Depends On:** P0.4, P2.2, P2.3
### P2.11: Calendar Page Implementation ✅ COMPLETE
- [x] In-app calendar with phase visualization
- **Files:**
- `src/app/calendar/page.tsx` - Month view with navigation, ICS subscription section with URL display, copy button, token regeneration
- `src/components/calendar/month-view.tsx` - Complete calendar grid with DayCell integration, navigation controls, phase legend
- **Tests:**
- `src/components/calendar/month-view.test.tsx` - 21 tests covering calendar grid, phase colors, navigation, legend
- `src/app/calendar/page.test.tsx` - 23 tests covering rendering, navigation, ICS subscription, token regeneration
- **Why:** Planning ahead is a key user need
- **Depends On:** P2.6
### P2.12: History Page Implementation ✅ COMPLETE
- [x] View past training decisions and data
- **Files:**
- `src/app/history/page.tsx` - Data fetching, table display, pagination, date filtering
- **Tests:**
- `src/app/history/page.test.tsx` - 26 tests covering rendering, data loading, pagination, filtering, error handling
- **Why:** Users want to review their training history
- **Depends On:** P2.8
### P2.13: Plan Page Implementation ✅ COMPLETE
- [x] Phase-specific training plan view
- **Files:**
- `src/app/plan/page.tsx` - Phase overview, training guidelines, exercise reference, rebounding techniques
- **Tests:**
- `src/app/plan/page.test.tsx` - 16 tests covering loading states, error handling, phase display, exercise reference, rebounding techniques
- **Features Implemented:**
- Current phase status display (day, phase name, training type, weekly limit)
- Phase overview cards for all 5 phases with weekly intensity minute limits
- Strength training exercises reference with descriptions
- Rebounding techniques organized by phase (follicular and luteal)
- Weekly guidelines for each phase with training goals
- **Why:** Users want detailed training guidance
- **Depends On:** P0.4, P1.3
### P2.14: Mini Calendar Component ✅ COMPLETE
- [x] Dashboard overview calendar
- **Current State:** COMPLETE - Compact calendar grid with phase colors, navigation buttons, today highlighting, phase legend
- **Files:**
- `src/components/dashboard/mini-calendar.tsx` - Complete calendar grid with DayCell integration
- **Tests:**
- `src/components/dashboard/mini-calendar.test.tsx` - 23 tests (calendar grid, phase colors, navigation, legend)
- **Features Implemented:**
- Calendar grid using DayCell component
- Current week/month view
- Phase color coding
- Today highlight
- Navigation buttons (prev/next month)
- Phase legend
- **Why:** Quick visual reference on dashboard
### P2.15: Health Check Endpoint ✅ COMPLETE
- [x] GET /api/health for deployment monitoring
- **Current State:** Fully implemented with PocketBase connectivity checks
- **Files:**
- `src/app/api/health/route.ts` - Returns health status with PocketBase connectivity check
- **Tests:**
- `src/app/api/health/route.test.ts` - 14 tests for healthy (200) and unhealthy (503) states
- **Response Shape:**
- `status` - "ok" or "unhealthy"
- `timestamp` - ISO 8601 timestamp
- `version` - Application version
- **Checks Performed:**
- PocketBase connectivity
- Basic app startup complete
- **Why:** Required for Nomad health checks, load balancer probes, and uptime monitoring (per specs/observability.md)
### P2.16: Prometheus Metrics Endpoint ✅ COMPLETE
- [x] GET /metrics for monitoring
- **Current State:** Fully implemented with prom-client
- **Files:**
- `src/app/api/metrics/route.ts` - Returns Prometheus-format metrics (15 tests)
- `src/lib/metrics.ts` - Metrics collection with prom-client (18 tests)
- **Tests:**
- `src/lib/metrics.test.ts` - 18 tests covering metrics collection, counters, gauges, histograms, Prometheus format
- `src/app/api/metrics/route.test.ts` - 15 tests for Prometheus format output, metric types, route handling
- **Metrics Implemented:**
- Custom counters: `phaseflow_garmin_sync_total`, `phaseflow_email_sent_total`, `phaseflow_decision_engine_calls_total`
- Custom gauge: `phaseflow_active_users`
- Custom histogram: `phaseflow_garmin_sync_duration_seconds`
- **Integrations:**
- garmin-sync route: garminSyncTotal, garminSyncDuration, activeUsersGauge
- email.ts: emailSentTotal (daily and warning types)
- decision-engine.ts: decisionEngineCallsTotal
- **Why:** Required for Prometheus scraping and production monitoring (per specs/observability.md)
### P2.17: Structured Logging with Pino ✅ COMPLETE
- [x] Create pino-based logger with JSON output
- **Files:**
- `src/lib/logger.ts` - Pino logger configuration with LOG_LEVEL env var support
- **Tests:**
- `src/lib/logger.test.ts` - 16 tests covering JSON format, log levels, error stack traces, child loggers
- **Features Implemented:**
- JSON output to stdout for log aggregators (Loki, ELK)
- Log levels: error, warn, info, debug
- LOG_LEVEL environment variable configuration (defaults to "info")
- Error objects serialized with type, message, and stack trace
- Child logger support for bound context
- ISO 8601 timestamps
- **Why:** Required for log aggregators and production debugging (per specs/observability.md)
- **Next Step:** Integrate logger into API routes (can be done incrementally)
### P2.18: OIDC Authentication ✅ COMPLETE
- [x] Replace email/password login with OIDC (Pocket-ID)
- **Files:**
- `src/app/login/page.tsx` - OIDC button with email/password fallback
- **Tests:**
- `src/app/login/page.test.tsx` - 24 tests (10 new OIDC tests)
- **Features Implemented:**
- Auto-detection of OIDC provider via `listAuthMethods()` API
- "Sign In with Pocket-ID" button when OIDC provider is configured
- Email/password form fallback when OIDC is not available
- PocketBase `authWithOAuth2()` popup-based OAuth2 flow
- Loading states during authentication
- Error handling with user-friendly messages
- **Flow:**
1. Page checks for available auth methods on mount
2. If OIDC provider configured, shows "Sign In with Pocket-ID" button
3. User clicks button, PocketBase handles OAuth2 popup flow
4. On success, user redirected to dashboard
5. Falls back to email/password when OIDC not available
- **Environment Variables (configured in PocketBase Admin):**
- Client ID, Client Secret, Issuer URL configured in PocketBase
- **Why:** Required per specs/authentication.md for secure identity management
---
## P3: Polish and Quality
Testing, error handling, and refinements.
### P3.1: Decision Engine Tests ✅ COMPLETE
- [x] Comprehensive unit tests for all decision paths
- **Files:**
- `src/lib/decision-engine.test.ts` - All 8 priority rules, override combinations (24 tests)
- **Test Cases Covered:**
- HRV Unbalanced always forces REST (highest algorithmic priority)
- Override priority: flare > stress > sleep > pms
- Phase limits strictly enforced
- All override bypass and fallthrough scenarios
- **Why:** Critical logic is now fully tested
### P3.2: Nutrition Tests ✅ COMPLETE
- [x] Unit tests for nutrition guidance
- **Files:**
- `src/lib/nutrition.test.ts` - 17 tests covering seed cycling, carb ranges, keto guidance by phase
- **Test Cases Covered:**
- Seed cycling recommendations by phase (flax/pumpkin vs sunflower/sesame)
- Carb range calculations per phase
- Keto guidance by cycle day
- Edge cases and phase transitions
- **Why:** Nutrition advice accuracy is now fully tested
### P3.3: Email Tests ✅ COMPLETE
- [x] Unit tests for email composition
- **Files:**
- `src/lib/email.test.ts` - 14 tests covering email content, subject lines, formatting
- **Test Cases Covered:**
- Daily email composition with decision data
- Period confirmation email content
- Subject line formatting
- HTML email structure
- **Why:** Email formatting correctness is now fully tested
### P3.4: ICS Tests ✅ COMPLETE
- [x] Unit tests for calendar generation
- **Files:**
- `src/lib/ics.test.ts` - 28 tests covering ICS format validation, 90-day event generation, timezone handling, period prediction feedback
- **Test Cases Covered:**
- ICS feed generation with 90 days of phase events
- RFC 5545 format compliance
- Timezone handling (UTC conversion)
- Event boundaries and phase transitions
- Period prediction accuracy feedback ("Predicted" labels)
- **Why:** Calendar integration compatibility is now fully tested
### P3.5: Encryption Tests ✅ COMPLETE
- [x] Unit tests for encrypt/decrypt round-trip
- **Files:**
- `src/lib/encryption.test.ts` - 14 tests covering AES-256-GCM round-trip, error handling, key validation
- **Test Cases Covered:**
- Encrypt/decrypt round-trip verification
- Key validation and error handling
- IV generation uniqueness
- Malformed data handling
- **Why:** Token security is now fully tested
### P3.6: Garmin Tests ✅ COMPLETE
- [x] Unit tests for Garmin API interactions
- **Files:**
- `src/lib/garmin.test.ts` - 33 tests covering API calls, error handling, token expiry (expanded in P2.1)
- **Test Cases Covered:**
- fetchGarminData, fetchHrvStatus, fetchBodyBattery, fetchIntensityMinutes HTTP calls and response parsing
- isTokenExpired logic with various expiry dates
- daysUntilExpiry calculations
- Error handling for invalid tokens and network failures
- Response parsing for biometric data structures
- **Why:** External API integration robustness is now fully tested
### P3.7: Error Handling Improvements ✅ COMPLETE
- [x] Add consistent error responses across all API routes
- [x] Replace console.error with structured pino logger
- [x] Add logging for key events per observability spec
- **Files:**
- `src/lib/auth-middleware.ts` - Replaced console.error with structured logger, added auth failure logging
- `src/app/api/cycle/period/route.ts` - Added "Period logged" event logging, structured error logging
- `src/app/api/calendar/[userId]/[token].ics/route.ts` - Replaced console.error with structured logger
- `src/app/api/overrides/route.ts` - Added "Override toggled" event logging
- `src/app/api/today/route.ts` - Added "Decision calculated" event logging
- **Tests:**
- `src/lib/auth-middleware.test.ts` - Added 3 tests for structured logging (9 total)
- **Events Logged (per observability spec):**
- Auth failure (warn): reason
- Period logged (info): userId, date
- Override toggled (info): userId, override, enabled
- Decision calculated (info): userId, decision, reason
- Error events (error): err object with stack trace
- **Why:** Better debugging and user experience with structured JSON logs
### P3.8: Loading States ✅ COMPLETE
- [x] Add loading indicators to all pages
- **Files:**
- `src/components/dashboard/skeletons.tsx` - Skeleton components (DecisionCardSkeleton, DataPanelSkeleton, NutritionPanelSkeleton, MiniCalendarSkeleton, OverrideTogglesSkeleton, CycleInfoSkeleton, DashboardSkeleton)
- `src/components/dashboard/skeletons.test.tsx` - 29 tests
- `src/app/loading.tsx` - Dashboard route loading state
- `src/app/calendar/loading.tsx` - Calendar route loading state
- `src/app/history/loading.tsx` - History route loading state
- `src/app/plan/loading.tsx` - Plan route loading state
- `src/app/settings/loading.tsx` - Settings route loading state
- **Features:** Skeleton placeholders with shimmer animations matching spec requirements, updated dashboard page to use skeleton components
- **Why:** Better perceived performance
### P3.9: Token Expiration Warnings ✅ COMPLETE
- [x] Email warnings at 14 and 7 days before Garmin token expiry
- **Files:**
- `src/lib/email.ts` - Added `sendTokenExpirationWarning()` function
- `src/app/api/cron/garmin-sync/route.ts` - Added token expiry checking and warning logic
- **Tests:**
- `src/lib/email.test.ts` - 10 new tests for warning email function (24 total)
- `src/app/api/cron/garmin-sync/route.test.ts` - 10 new tests for warning integration (32 total)
- **Features Implemented:**
- Sends warning email at exactly 14 days before token expiry
- Sends warning email at exactly 7 days before token expiry
- Warning logic integrated into garmin-sync cron job
- Email includes days until expiry and instructions for refreshing tokens
- **Why:** Users need time to refresh tokens (per spec requirement in specs/email.md)
### P3.10: E2E Test Suite (AUTHORIZED SKIP)
- [ ] Comprehensive end-to-end tests
- **Files:**
- `tests/e2e/*.spec.ts` - Full user flows
- **Test Scenarios:**
- Login flow
- Period logging and phase calculation
- Override toggle functionality
- Settings update flow
- Garmin connection flow
- Calendar subscription
- **Why:** Confidence in production deployment
- **Status:** Per specs/testing.md: "End-to-end tests are not required for MVP (authorized skip)"
### P3.11: Missing Component Tests ✅ COMPLETE
- [x] Add unit tests for untested components
- **Components Tested (5 total):**
- `src/components/dashboard/decision-card.tsx` - 11 tests for rendering decision status, icon, reason, styling
- `src/components/dashboard/data-panel.tsx` - 18 tests for biometrics display (BB, HRV, intensity), null handling, styling
- `src/components/dashboard/nutrition-panel.tsx` - 12 tests for seeds, carbs, keto guidance display
- `src/components/dashboard/override-toggles.tsx` - 18 tests for toggle states, callbacks, styling
- `src/components/calendar/day-cell.tsx` - 23 tests for phase coloring, today highlighting, click handling
- **Test Files Created:**
- `src/components/dashboard/decision-card.test.tsx` - 11 tests
- `src/components/dashboard/data-panel.test.tsx` - 18 tests
- `src/components/dashboard/nutrition-panel.test.tsx` - 12 tests
- `src/components/dashboard/override-toggles.test.tsx` - 18 tests
- `src/components/calendar/day-cell.test.tsx` - 23 tests
- **Total Tests Added:** 82 tests across 5 files
- **Why:** Component isolation ensures UI correctness and prevents regressions
---
## P4: UX Polish and Accessibility
Enhancements from spec requirements that improve user experience.
### P4.1: Dashboard Onboarding Banners ✅ COMPLETE
- [x] Show setup prompts for missing configuration
- **Spec Reference:** specs/dashboard.md mentions onboarding banners
- **Implementation Details:**
- `OnboardingBanner` component created at `src/components/dashboard/onboarding-banner.tsx`
- Tests at `src/components/dashboard/onboarding-banner.test.tsx` - 16 tests
- Dashboard integration at `src/app/page.tsx` - 5 new tests added to page.test.tsx (28 total)
- Features: Garmin connection banner, period date banner with callback, notification time banner
- Banners render at top of dashboard when setup incomplete
- **Files:**
- `src/app/page.tsx` - Added conditional banner rendering at top of dashboard
- `src/components/dashboard/onboarding-banner.tsx` - New component with icons, action buttons, styling
- `src/components/dashboard/onboarding-banner.test.tsx` - 16 tests covering rendering, interactions, dismissal
- **Why:** Helps new users complete setup for full functionality
### P4.2: Accessibility Improvements ✅ COMPLETE
- [x] Skip navigation link
- [x] Semantic HTML landmarks (main elements)
- [x] Screen reader labels for calendar buttons
- [x] Keyboard navigation for calendar
- **Spec Reference:** specs/dashboard.md accessibility requirements
- **Implementation Details:**
- Skip navigation link added to layout with sr-only styling
- Semantic HTML landmarks (main element) added to login and settings pages
- Aria-labels added to DayCell calendar buttons with date and phase information
- Keyboard navigation: ArrowLeft/Right for prev/next day, ArrowUp/Down for prev/next week, Home/End for first/last day, boundary navigation triggers month change
- **Files:**
- `src/app/layout.tsx` - Added skip navigation link with sr-only styling
- `src/app/layout.test.tsx` - 3 tests for skip link rendering and accessibility
- `src/app/login/page.tsx` - Wrapped content in main element
- `src/app/login/page.test.tsx` - Added 2 accessibility tests (26 total)
- `src/app/settings/page.tsx` - Wrapped content in main element
- `src/app/settings/page.test.tsx` - Added 2 accessibility tests (29 total)
- `src/app/page.tsx` - Added id="main-content" to existing main element
- `src/components/calendar/day-cell.tsx` - Added aria-label with date/phase info, dataDay prop
- `src/components/calendar/day-cell.test.tsx` - Added 4 accessibility tests (27 total)
- `src/components/calendar/month-view.tsx` - Added role="grid", keyboard navigation handler
- `src/components/calendar/month-view.test.tsx` - Added 9 keyboard navigation tests (30 total)
- **Why:** Required for accessibility compliance
### P4.3: Dark Mode Configuration ✅ COMPLETE
- [x] Complete dark mode support
- **Current State:** COMPLETE - Dark mode auto-detects system preference via `prefers-color-scheme`
- **Implementation Details:**
- Changed `@custom-variant dark` from class-based to `@media (prefers-color-scheme: dark)` in globals.css
- Changed `.dark` CSS selector to `@media (prefers-color-scheme: dark) { :root { ... } }` for CSS variables
- Dark mode now auto-detects system preference (no manual toggle needed per spec)
- No component changes needed - existing CSS variables handle the theming
- **Files:**
- `src/app/globals.css` - Updated dark mode detection to use media query instead of class
- **Why:** User preference for dark mode
### P4.4: Loading Performance
- [ ] Loading states within 100ms target
- **Spec Reference:** specs/dashboard.md performance requirements
- **Features:**
- Skeleton loading states
- Optimistic UI updates (partially done with overrides)
- Suspense boundaries for code splitting
- **Files:**
- Page files - Add loading.tsx skeletons
- **Why:** Perceived performance improvement
### P4.5: Period Prediction Accuracy Feedback ✅ COMPLETE
- [x] Mark predicted vs confirmed period dates
- **Spec Reference:** specs/calendar.md mentions predictions marked with "Predicted" suffix
- **Files Modified:**
- `src/types/index.ts` - Added `predictedDate` field to PeriodLog type
- `src/lib/ics.ts` - Modified `generateIcsFeed()` to accept period logs and mark events with "(Predicted)" when actual differs from predicted
- `src/app/api/cycle/period/route.ts` - POST handler calculates predicted date (lastPeriodDate + cycleLength), stores in PeriodLog, returns daysEarly/daysLate
- `src/app/api/calendar/[userId]/[token].ics/route.ts` - Fetches period logs and passes them to ICS generator
- **Tests Added:**
- `src/app/api/cycle/period/route.test.ts` - 5 new tests (13 total): predictedDate storage, daysEarly/daysLate calculations
- `src/lib/ics.test.ts` - 5 new tests (28 total): "(Predicted)" label on events when actual differs from predicted
- `src/app/api/calendar/[userId]/[token].ics/route.test.ts` - 1 new test (11 total): period logs fetching and passing to ICS generator
- **Features Implemented:**
- PeriodLog stores predictedDate calculated from previous period (lastPeriodDate + cycleLength)
- POST /api/cycle/period calculates predicted vs actual date, returns daysEarly (negative) or daysLate (positive)
- ICS feed shows "(Predicted)" suffix on menstruation events when actual date differs from predicted date
- Calendar route fetches all period logs and passes them to ICS generator for prediction feedback
- **Why:** Creates feedback loop for understanding cycle prediction accuracy per calendar.md spec
### P4.6: Rate Limiting ✅ COMPLETE
- [x] Login attempt rate limiting
- **Spec Reference:** specs/email.md mentions 5 login attempts per minute
- **Files:**
- `src/app/login/page.tsx` - Added client-side rate limiting with 5 attempts per minute
- **Tests:**
- `src/app/login/page.test.tsx` - 6 new tests (32 total) covering rate limiting
- **Features Implemented:**
- Client-side rate limiting tracking login attempts with timestamps
- Rate limit: 5 attempts per minute (RATE_LIMIT_WINDOW_MS = 60000)
- Shows "Too many login attempts. Please try again in 1 minute." error when rate limited
- Shows remaining attempts warning after 3 failures
- Disables form/button when rate limited
- Auto-clears after cooldown period
- Works for both email/password and OIDC login
- **Why:** Security requirement from spec
---
## Implementation Order
```
P0.1 PocketBase Auth ──┬──> P0.2 Auth Middleware ──> P0.4 GET /api/user
P0.3 Override Logic ───┴──> P1.4 GET /api/today ──> P1.7 Dashboard
P1.1 PATCH /api/user ────> P2.9 Settings Page
P1.2 POST period ────────> P1.3 GET current ────> P1.7 Dashboard
P1.5 Overrides API ──────> P1.7 Dashboard
P1.6 Login Page ─────────> P2.18 OIDC Auth (upgrade)
P2.1 Garmin fetchers ──> P2.2 Garmin tokens ──> P2.4 Cron sync ──> P2.5 Notifications
└──> P3.9 Token Warnings
P2.3 Garmin status ────> P2.10 Garmin settings
P2.6 ICS endpoint ─────> P2.11 Calendar page
P2.7 Regen token
P2.8 History API ──────> P2.12 History page
P2.13 Plan page
P2.14 Mini calendar
P2.15 Health endpoint (independent - HIGH PRIORITY for deployment)
P2.16 Metrics endpoint (independent)
P2.17 Structured logging (independent, but should be done before other items for proper logging)
P3.11 Component tests ─> Can be done in parallel with other work
P4.* UX Polish ────────> After core functionality complete
```
### Remaining Work Priority
| Priority | Task | Effort | Notes |
|----------|------|--------|-------|
| Low | P4.4 Loading Performance | Small | After core complete |
| Done | P4.5 Period Prediction | Complete | Prediction tracking with feedback loop |
| Done | P4.6 Rate Limiting | Complete | Client-side rate limiting implemented |
### Dependency Summary
| Task | Blocked By | Blocks |
|------|------------|--------|
| P0.1 | - | P0.2, P0.4, P1.1-P1.6, P2.2-P2.3, P2.7-P2.8 |
| P0.2 | P0.1 | P0.4, P1.1-P1.5, P2.2-P2.3, P2.7-P2.8 |
| P0.3 | - | P1.4, P1.5 |
| P0.4 | P0.1, P0.2 | P1.7, P2.9, P2.10, P2.13 |
| P3.9 | P2.4 | - |
---
## Completed
### Library
- [x] **cycle.ts** - Complete with 9 tests (`getCycleDay`, `getPhase`, `getPhaseConfig`, `getPhaseLimit`)
- [x] **decision-engine.ts** - Complete with 24 tests (`getTrainingDecision` + `getDecisionWithOverrides`)
- [x] **pocketbase.ts** - Complete with 9 tests (`createPocketBaseClient`, `isAuthenticated`, `getCurrentUser`, `loadAuthFromCookies`)
- [x] **nutrition.ts** - Complete with 17 tests (`getNutritionGuidance`, `getSeedSwitchAlert`, phase-specific carb ranges, keto guidance) (P3.2)
- [x] **email.ts** - Complete with 24 tests (`sendDailyEmail`, `sendPeriodConfirmationEmail`, `sendTokenExpirationWarning`, email formatting) (P3.3, P3.9)
- [x] **ics.ts** - Complete with 28 tests (`generateIcsFeed`, ICS format validation, 90-day event generation, period prediction feedback) (P3.4, P4.5)
- [x] **encryption.ts** - Complete with 14 tests (AES-256-GCM encrypt/decrypt, round-trip validation, error handling) (P3.5)
- [x] **garmin.ts** - Complete with 33 tests (`fetchGarminData`, `fetchHrvStatus`, `fetchBodyBattery`, `fetchIntensityMinutes`, `isTokenExpired`, `daysUntilExpiry`, error handling) (P2.1, P3.6)
- [x] **auth-middleware.ts** - Complete with 6 tests (`withAuth()` wrapper)
- [x] **middleware.ts** - Complete with 12 tests (Next.js page protection)
- [x] **logger.ts** - Complete with 16 tests (JSON output, log levels, error serialization, child loggers) (P2.17)
- [x] **metrics.ts** - Complete with 18 tests (metrics collection, counters, gauges, histograms, Prometheus format) (P2.16)
### Components
- [x] **DecisionCard** - Displays decision status, icon, and reason
- [x] **DataPanel** - Shows body battery, HRV, intensity data
- [x] **NutritionPanel** - Shows seeds, carbs, keto guidance
- [x] **OverrideToggles** - Toggle buttons for flare/stress/sleep/pms
- [x] **DayCell** - Phase-colored calendar day cell with click handler
- [x] **MonthView** - Calendar grid with DayCell integration, navigation controls (prev/next month, Today button), phase legend, 21 tests
- [x] **MiniCalendar** - Compact calendar widget with phase colors, navigation, legend, 23 tests (P2.14)
### API Routes (17 complete)
- [x] **GET /api/user** - Returns authenticated user profile, 4 tests (P0.4)
- [x] **PATCH /api/user** - Updates user profile (cycleLength, notificationTime, timezone), 17 tests (P1.1)
- [x] **POST /api/cycle/period** - Logs period start date, updates user, creates PeriodLog with prediction tracking, 13 tests (P1.2, P4.5)
- [x] **GET /api/cycle/current** - Returns cycle day, phase, phaseConfig, daysUntilNextPhase, cycleLength, 10 tests (P1.3)
- [x] **GET /api/today** - Returns complete daily snapshot with decision, biometrics, nutrition, 22 tests (P1.4)
- [x] **POST /api/overrides** - Adds override to user.activeOverrides array, 14 tests (P1.5)
- [x] **DELETE /api/overrides** - Removes override from user.activeOverrides array, 14 tests (P1.5)
- [x] **POST /api/garmin/tokens** - Stores encrypted Garmin OAuth tokens, 15 tests (P2.2)
- [x] **DELETE /api/garmin/tokens** - Clears tokens and disconnects Garmin, 15 tests (P2.2)
- [x] **GET /api/garmin/status** - Returns connection status, expiry, warning level, 11 tests (P2.3)
- [x] **POST /api/cron/garmin-sync** - Daily sync of Garmin data for all connected users, creates DailyLogs, sends token expiration warnings, 32 tests (P2.4, P3.9)
- [x] **POST /api/cron/notifications** - Sends daily email notifications with timezone matching, DailyLog handling, nutrition guidance, 20 tests (P2.5)
- [x] **GET /api/calendar/[userId]/[token].ics** - Returns ICS feed with 90-day phase events and period prediction feedback, token validation, caching headers, 11 tests (P2.6, P4.5)
- [x] **POST /api/calendar/regenerate-token** - Generates new 32-char calendar token, returns URL, 9 tests (P2.7)
- [x] **GET /api/history** - Paginated historical daily logs with date filtering, validation, 19 tests (P2.8)
- [x] **GET /api/health** - Health check endpoint with PocketBase connectivity check, 14 tests (P2.15)
- [x] **GET /metrics** - Prometheus metrics endpoint with counters, gauges, histograms, 33 tests (18 lib + 15 route) (P2.16)
### Pages (7 complete)
- [x] **Login Page** - OIDC (Pocket-ID) with email/password fallback, error handling, loading states, redirect, rate limiting, 32 tests (P1.6, P2.18, P4.6)
- [x] **Dashboard Page** - Complete daily interface with /api/today integration, DecisionCard, DataPanel, NutritionPanel, OverrideToggles, 23 tests (P1.7)
- [x] **Settings Page** - Form for cycleLength, notificationTime, timezone with validation, loading states, error handling, 28 tests (P2.9)
- [x] **Settings/Garmin Page** - Token input form, connection status, expiry warnings, disconnect functionality, 27 tests (P2.10)
- [x] **Calendar Page** - MonthView with navigation controls, ICS subscription section with URL display, copy button, token regeneration, 23 tests (P2.11)
- [x] **History Page** - Table view of DailyLogs with date filtering, pagination, decision styling, 26 tests (P2.12)
- [x] **Plan Page** - Phase overview, training guidance, exercise reference, rebounding techniques, 16 tests (P2.13)
### Test Infrastructure
- [x] **test-setup.ts** - Global test setup with @testing-library/jest-dom matchers and cleanup
### P3: Quality and Testing
- [x] **P3.1: Decision Engine Tests** - Complete with 24 tests covering all 8 priority rules and override combinations
- [x] **P3.2: Nutrition Tests** - Complete with 17 tests covering seed cycling, carb ranges, keto guidance by phase
- [x] **P3.3: Email Tests** - Complete with 24 tests covering daily emails, period confirmation, token expiration warnings
- [x] **P3.4: ICS Tests** - Complete with 28 tests covering ICS format validation, 90-day event generation, timezone handling, period prediction feedback
- [x] **P3.5: Encryption Tests** - Complete with 14 tests covering AES-256-GCM round-trip, error handling, key validation
- [x] **P3.6: Garmin Tests** - Complete with 33 tests covering API interactions, token expiry, error handling
- [x] **P3.7: Error Handling Improvements** - Replaced console.error with structured pino logger across API routes, added key event logging (Period logged, Override toggled, Decision calculated, Auth failure), 3 new tests in auth-middleware.test.ts
- [x] **P3.8: Loading States** - Complete with skeleton components (DecisionCardSkeleton, DataPanelSkeleton, NutritionPanelSkeleton, MiniCalendarSkeleton, OverrideTogglesSkeleton, CycleInfoSkeleton, DashboardSkeleton), 29 tests in skeletons.test.tsx; loading.tsx files for all routes (dashboard, calendar, history, plan, settings); shimmer animations matching spec requirements
- [x] **P3.9: Token Expiration Warnings** - Complete with 10 new tests in email.test.ts, 10 new tests in garmin-sync/route.test.ts; sends warnings at 14 and 7 days before expiry
- [x] **P3.11: Missing Component Tests** - Complete with 82 tests across 5 component test files (DecisionCard: 11, DataPanel: 18, NutritionPanel: 12, OverrideToggles: 18, DayCell: 23)
### P4: UX Polish and Accessibility
- [x] **P4.1: Dashboard Onboarding Banners** - Complete with OnboardingBanner component (16 tests), dashboard integration (5 new tests)
- [x] **P4.2: Accessibility Improvements** - Complete with skip navigation, semantic landmarks, calendar screen reader labels, keyboard navigation (9 new tests)
- [x] **P4.3: Dark Mode Configuration** - Complete with automatic dark mode via prefers-color-scheme media query
- [x] **P4.5: Period Prediction Accuracy Feedback** - Complete with predictedDate tracking in PeriodLog, daysEarly/daysLate calculations, "(Predicted)" labels in ICS feed (11 new tests across 3 files)
- [x] **P4.6: Rate Limiting** - Complete with client-side login rate limiting (5 attempts per minute, 6 new tests)
---
## Discovered Issues
*Bugs and inconsistencies found during implementation*
- [x] ~~`src/lib/auth-middleware.ts` does not exist~~ - CREATED in P0.2
- [x] ~~`src/middleware.ts` does not exist~~ - CREATED in P0.2
- [x] ~~`garmin.ts` is only ~30% complete - missing specific biometric fetchers~~ - FIXED in P2.1 (added fetchHrvStatus, fetchBodyBattery, fetchIntensityMinutes)
- [x] ~~`pocketbase.ts` missing all auth helper functions~~ - FIXED in P0.1
- [x] ~~`src/app/api/today/route.ts` type error with null body battery values~~ - FIXED (added null coalescing)
- [x] Build requires RESEND_API_KEY and ENCRYPTION_KEY environment variables - works with `pnpm test:run` but `pnpm build` fails without them. This is expected behavior for production builds.
---
## Notes
1. **TDD Approach:** Each implementation task should follow TDD - write failing tests first, then implement
2. **Auth First:** P0 items unlock all other work; prioritize ruthlessly
3. **Incremental Delivery:** P1 completion = usable app without Garmin (manual data entry fallback)
4. **P2 Completion:** Full feature set with automation
5. **P3:** Quality and polish for production confidence
6. **P4:** UX polish and accessibility improvements from spec requirements
7. **Component Reuse:** Dashboard components are complete and can be used directly in P1.7
8. **HRV Rule:** HRV Unbalanced status ALWAYS forces REST - this is the highest algorithmic priority and cannot be overridden by manual toggles
9. **Override Order:** When multiple overrides are active, apply in order: flare > stress > sleep > pms
10. **Token Warnings:** Per spec, warnings are sent at exactly 14 days and 7 days before expiry (P3.9 COMPLETE)
11. **Health Check Priority:** P2.15 (GET /api/health) should be implemented early - it's required for deployment monitoring and load balancer health probes
12. **Structured Logging:** P2.17 (pino logger) is COMPLETE - new code should use `import { logger } from "@/lib/logger"` for all logging
13. **OIDC Authentication:** P2.18 COMPLETE - Login page auto-detects OIDC via `listAuthMethods()` and shows "Sign In with Pocket-ID" button when configured. Falls back to email/password when OIDC not available. Configure OIDC provider in PocketBase Admin under Settings → Auth providers → OpenID Connect
14. **E2E Tests:** Authorized skip per specs/testing.md - unit and integration tests are sufficient for MVP
15. **Dark Mode:** Partial Tailwind support exists via dark: classes but may need prefers-color-scheme configuration in tailwind.config.js (see P4.3)
16. **Component Tests:** P3.11 COMPLETE - All 5 dashboard and calendar components now have comprehensive unit tests (82 tests total)