# PhaseFlow Implementation Plan This file is maintained by Ralph. Run `./ralph-sandbox.sh plan 3` to generate tasks. ## Current State Summary ### Overall Status: 796 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** | 23 tests covering generateIcsFeed (90 days of phase events), ICS format validation, timezone handling | | `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 (8 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, caching headers (10 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** - 8 tests (POST period, auth, validation, date checks) | | `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** - 23 tests (ICS format validation, 90-day event generation, timezone handling) | | `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** - 10 tests (token validation, ICS generation, 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 - **Tests:** - `src/app/api/cycle/period/route.test.ts` - 8 tests covering auth, date validation, user update, PeriodLog creation - **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 - **Tests:** - `src/app/api/calendar/[userId]/[token].ics/route.test.ts` - 10 tests covering token validation, ICS generation, caching headers, error handling - **Features Implemented:** - Token-based authentication (no session required) - Validates calendar token against user record - 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` - 23 tests covering ICS format validation, 90-day event generation, timezone handling - **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 - **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 - [ ] Mark predicted vs confirmed period dates - **Spec Reference:** specs/calendar.md mentions predictions marked with "Predicted" suffix - **Features:** - Visual distinction between logged and predicted periods - Calendar events show "Predicted" label for future periods - **Files:** - `src/lib/ics.ts` - Add "Predicted" suffix to future phase events - `src/components/calendar/day-cell.tsx` - Visual indicator for predictions - **Why:** Helps users understand prediction accuracy ### 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-P4.5 UX Polish | Various | After core complete | | 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 23 tests (`generateIcsFeed`, ICS format validation, 90-day event generation) (P3.4) - [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, 8 tests (P1.2) - [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, token validation, caching headers, 10 tests (P2.6) - [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 23 tests covering ICS format validation, 90-day event generation, timezone handling - [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) --- ## 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)