# PhaseFlow Implementation Plan This file is maintained by Ralph. Run `./ralph-sandbox.sh plan 3` to generate tasks. ## Current State Summary ### Library Implementation | File | Status | Gap Analysis | |------|--------|--------------| | `cycle.ts` | **COMPLETE** | 9 tests covering all functions, production-ready | | `nutrition.ts` | **Complete** | getNutritionGuidance, getSeedSwitchAlert implemented. **MISSING: tests** | | `email.ts` | **Complete** | sendDailyEmail, sendPeriodConfirmationEmail implemented. **MISSING: tests** | | `ics.ts` | **Complete** | generateIcsFeed implemented (90 days of phase events). **MISSING: tests** | | `encryption.ts` | **Complete** | AES-256-GCM encrypt/decrypt implemented. **MISSING: tests** | | `decision-engine.ts` | **COMPLETE** | 8 priority rules + override handling with `getDecisionWithOverrides()`, 24 tests | | `garmin.ts` | **Minimal (~30%)** | Has fetchGarminData, isTokenExpired, daysUntilExpiry. **MISSING: fetchHrvStatus, fetchBodyBattery, fetchIntensityMinutes** | | `pocketbase.ts` | **COMPLETE** | 9 tests covering `createPocketBaseClient()`, `isAuthenticated()`, `getCurrentUser()`, `loadAuthFromCookies()` | | `auth-middleware.ts` | **COMPLETE** | 6 tests covering `withAuth()` wrapper for API route protection | | `middleware.ts` (Next.js) | **COMPLETE** | 12 tests covering page protection, redirects to login | ### Missing Infrastructure Files (CONFIRMED NOT EXIST) - ~~`src/lib/auth-middleware.ts`~~ - **CREATED** in P0.2 - ~~`src/middleware.ts`~~ - **CREATED** in P0.2 ### API Routes (12 total) | Route | Status | Notes | |-------|--------|-------| | GET /api/user | **COMPLETE** | Returns user profile with `withAuth()` | | PATCH /api/user | 501 | Returns Not Implemented | | 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 | 501 | Returns Not Implemented | | DELETE /api/overrides | 501 | Returns Not Implemented | | POST /api/garmin/tokens | 501 | Returns Not Implemented | | DELETE /api/garmin/tokens | 501 | Returns Not Implemented | | GET /api/garmin/status | 501 | Returns Not Implemented | | GET /api/calendar/[userId]/[token].ics | 501 | Has param extraction, core logic TODO | | POST /api/calendar/regenerate-token | 501 | Returns Not Implemented | | POST /api/cron/garmin-sync | 501 | Has CRON_SECRET auth check, core logic TODO | | POST /api/cron/notifications | 501 | Has CRON_SECRET auth check, core logic TODO | ### Pages (7 total, ALL placeholders) | Page | Status | Notes | |------|--------|-------| | Dashboard (`/`) | Placeholder | Needs real data integration | | Login (`/login`) | Placeholder | Needs PocketBase auth integration | | Settings (`/settings`) | Placeholder | Needs form implementation | | Settings/Garmin (`/settings/garmin`) | Placeholder | Needs token management UI | | Calendar (`/calendar`) | Placeholder | Needs MonthView integration | | History (`/history`) | Placeholder | Needs list/pagination implementation | | Plan (`/plan`) | Placeholder | Needs phase details display | ### 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` | **Partial (~30%)** | Has header only, **MISSING: calendar grid** | | `MonthView` | **Partial (~30%)** | Has header only, **MISSING: calendar grid + DayCell integration** | ### 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** - 6 tests (withAuth wrapper, error handling) | | `src/middleware.test.ts` | **EXISTS** - 12 tests (page protection, public routes, static assets) | | `src/app/api/user/route.test.ts` | **EXISTS** - 4 tests (GET profile, auth, sensitive field exclusion) | | `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/lib/nutrition.test.ts` | **MISSING** | | `src/lib/email.test.ts` | **MISSING** | | `src/lib/ics.test.ts` | **MISSING** | | `src/lib/encryption.test.ts` | **MISSING** | | `src/lib/garmin.test.ts` | **MISSING** | | E2E tests | **NONE** | ### 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 5. **ICS Feed:** Generates 90 days of phase events for calendar subscription --- ## P0: Critical Blockers 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 Minimum viable product - app can be used for daily decisions. ### P1.1: PATCH /api/user Implementation - [ ] Allow profile updates (cycleLength, notificationTime, timezone) - **Files:** - `src/app/api/user/route.ts` - Implement PATCH handler with validation - **Tests:** - `src/app/api/user/route.test.ts` - Test field validation, persistence - **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 - [ ] Toggle override flags on user record - **Files:** - `src/app/api/overrides/route.ts` - Implement POST (add) and DELETE (remove) handlers - **Tests:** - `src/app/api/overrides/route.test.ts` - Test override types, persistence, validation - **Override Types:** flare, stress, sleep, pms - **Why:** Emergency overrides are critical for flare days - **Depends On:** P0.1, P0.2, P0.3 ### P1.6: Login Page Implementation - [ ] Functional login form with PocketBase auth - **Files:** - `src/app/login/page.tsx` - Form with email/password, error handling, redirect - **Tests:** - E2E test: valid login redirects to dashboard, invalid shows error - **Why:** Users need to authenticate to use the app - **Depends On:** P0.1 ### P1.7: Dashboard Page Implementation - [ ] Wire up dashboard with real data from /api/today - **Files:** - `src/app/page.tsx` - Fetch data, render DecisionCard, DataPanel, NutritionPanel, OverrideToggles - **Tests:** - E2E test: dashboard loads data, override toggles work - **Why:** This is the main user interface - **Depends On:** P0.4, P1.3, P1.4, P1.5 - **Note:** Components (DecisionCard, DataPanel, NutritionPanel, OverrideToggles) are already **COMPLETE** --- ## P2: Important Features Full feature set for production use. ### P2.1: Garmin Data Fetching Functions - [ ] Add specific fetchers for HRV, Body Battery, Intensity Minutes - **Files:** - `src/lib/garmin.ts` - Add `fetchHrvStatus()`, `fetchBodyBattery()`, `fetchIntensityMinutes()` - **Tests:** - `src/lib/garmin.test.ts` - Test API calls, response parsing, error handling - **Why:** Real biometric data is required for accurate decisions - **Note:** Currently only has generic fetchGarminData, isTokenExpired, daysUntilExpiry ### P2.2: POST/DELETE /api/garmin/tokens Implementation - [ ] Store encrypted Garmin OAuth tokens - **Files:** - `src/app/api/garmin/tokens/route.ts` - Implement with encryption.ts - **Tests:** - `src/app/api/garmin/tokens/route.test.ts` - Test encryption, validation, storage - **Why:** Users need to connect their Garmin accounts - **Depends On:** P0.1, P0.2 ### P2.3: GET /api/garmin/status Implementation - [ ] Return Garmin connection status and days until expiry - **Files:** - `src/app/api/garmin/status/route.ts` - Implement status check - **Tests:** - `src/app/api/garmin/status/route.test.ts` - Test connected/disconnected states, expiry calc - **Why:** Users need visibility into their Garmin connection - **Depends On:** P0.1, P0.2, P2.1 ### P2.4: POST /api/cron/garmin-sync Implementation - [ ] Daily sync of all Garmin data for all users - **Files:** - `src/app/api/cron/garmin-sync/route.ts` - Iterate users, fetch data, store DailyLog - **Tests:** - `src/app/api/cron/garmin-sync/route.test.ts` - Test auth, user iteration, data persistence - **Why:** Automated data sync is required for morning notifications - **Depends On:** P2.1, P2.2 - **Note:** Route exists with CRON_SECRET auth check, needs core logic ### P2.5: POST /api/cron/notifications Implementation - [ ] Send daily email notifications at user's preferred time - **Files:** - `src/app/api/cron/notifications/route.ts` - Find users by hour, compute decision, send email - **Tests:** - `src/app/api/cron/notifications/route.test.ts` - Test timezone handling, duplicate prevention - **Why:** Email notifications are a key feature per spec - **Depends On:** P2.4 - **Note:** Route exists with CRON_SECRET auth check, needs core logic ### P2.6: GET /api/calendar/[userId]/[token].ics Implementation - [ ] Return ICS feed for calendar subscription - **Files:** - `src/app/api/calendar/[userId]/[token].ics/route.ts` - Validate token, generate ICS - **Tests:** - Integration test: valid token returns ICS, invalid returns 401 - **Why:** Calendar integration for external apps - **Note:** Route has param extraction, needs ICS generation (90 days of events per spec) ### P2.7: POST /api/calendar/regenerate-token Implementation - [ ] Generate new calendar token - **Files:** - `src/app/api/calendar/regenerate-token/route.ts` - Create random token, update user - **Tests:** - `src/app/api/calendar/regenerate-token/route.test.ts` - Test token uniqueness, old URL invalidation - **Why:** Security feature for calendar URLs - **Depends On:** P0.1, P0.2 ### P2.8: GET /api/history Implementation - [ ] Return paginated historical daily logs - **Files:** - `src/app/api/history/route.ts` - Query DailyLog with pagination - **Tests:** - `src/app/api/history/route.test.ts` - Test pagination, date filtering - **Why:** Users want to see their training history - **Depends On:** P0.1, P0.2 ### P2.9: Settings Page Implementation - [ ] User profile management UI - **Files:** - `src/app/settings/page.tsx` - Form for cycleLength, notificationTime, timezone - **Tests:** - E2E test: settings update and persist - **Why:** Users need to configure their preferences - **Depends On:** P0.4, P1.1 ### P2.10: Settings/Garmin Page Implementation - [ ] Garmin connection management UI - **Files:** - `src/app/settings/garmin/page.tsx` - Token input form, connection status, disconnect button - **Tests:** - E2E test: connect flow, disconnect flow - **Why:** Users need to manage their Garmin connection - **Depends On:** P0.4, P2.2, P2.3 ### P2.11: Calendar Page Implementation - [ ] In-app calendar with phase visualization - **Files:** - `src/app/calendar/page.tsx` - Month view with navigation - `src/components/calendar/month-view.tsx` - **Complete calendar grid using DayCell** - **Tests:** - E2E test: navigation works, phases displayed correctly - **Why:** Planning ahead is a key user need - **Depends On:** P2.6 - **Note:** DayCell is **COMPLETE**, MonthView needs grid implementation (~70% remaining) ### P2.12: History Page Implementation - [ ] View past training decisions and data - **Files:** - `src/app/history/page.tsx` - List view of DailyLogs with pagination - **Tests:** - E2E test: history loads, pagination works - **Why:** Users want to review their training history - **Depends On:** P2.8 ### P2.13: Plan Page Implementation - [ ] Phase-specific training plan view - **Files:** - `src/app/plan/page.tsx` - Current phase details, upcoming phases, limits - **Tests:** - E2E test: correct phase info displayed - **Why:** Users want detailed training guidance - **Depends On:** P0.4, P1.3 ### P2.14: Mini Calendar Component - [ ] Dashboard overview calendar - **Files:** - `src/components/dashboard/mini-calendar.tsx` - **Complete calendar grid with phase colors** - **Tests:** - Component test: renders current month, highlights today - **Why:** Quick visual reference on dashboard - **Note:** Component exists with header only, needs calendar grid (~70% remaining) --- ## 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 - [ ] Unit tests for nutrition guidance - **Files:** - `src/lib/nutrition.test.ts` - Seed cycling, carb ranges, keto guidance by day - **Why:** Nutrition advice must be accurate ### P3.3: Email Tests - [ ] Unit tests for email composition - **Files:** - `src/lib/email.test.ts` - Email content, subject lines - **Why:** Email formatting must be correct ### P3.4: ICS Tests - [ ] Unit tests for calendar generation - **Files:** - `src/lib/ics.test.ts` - ICS format validation, 90-day event generation - **Why:** Calendar integration must work with external apps ### P3.5: Encryption Tests - [ ] Unit tests for encrypt/decrypt round-trip - **Files:** - `src/lib/encryption.test.ts` - Round-trip, error handling - **Why:** Token security is critical ### P3.6: Garmin Tests - [ ] Unit tests for Garmin API interactions - **Files:** - `src/lib/garmin.test.ts` - API calls, error handling, token expiry - **Why:** External API integration must be robust ### P3.7: Error Handling Improvements - [ ] Add consistent error responses across all API routes - **Files:** - All route files - Standardize error format, add logging - **Why:** Better debugging and user experience ### P3.8: Loading States - [ ] Add loading indicators to all pages - **Files:** - All page files - Add loading.tsx or Suspense boundaries - **Why:** Better perceived performance ### P3.9: Token Expiration Warnings - [ ] Email warnings at 14 and 7 days before Garmin token expiry - **Files:** - `src/lib/email.ts` - Add `sendTokenExpirationWarning()` - `src/app/api/cron/garmin-sync/route.ts` - Check expiry, trigger warnings - **Tests:** - Test warning triggers at exactly 14 days and 7 days - **Why:** Users need time to refresh tokens (per spec requirement) ### P3.10: E2E Test Suite - [ ] 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 --- ## 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.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 ``` ### 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 | --- ## 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`) ### 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 ### API Routes - [x] **GET /api/user** - Returns authenticated user profile, 4 tests (P0.4) - [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) --- ## 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 - [ ] `garmin.ts` is only ~30% complete - missing specific biometric fetchers - [x] ~~`pocketbase.ts` missing all auth helper functions~~ - FIXED in P0.1 --- ## 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. **Component Reuse:** Dashboard components are complete and can be used directly in P1.7 7. **HRV Rule:** HRV Unbalanced status ALWAYS forces REST - this is the highest algorithmic priority and cannot be overridden by manual toggles 8. **Override Order:** When multiple overrides are active, apply in order: flare > stress > sleep > pms 9. **Token Warnings:** Per spec, warnings must be sent at exactly 14 days and 7 days before expiry