Files
phaseflow/IMPLEMENTATION_PLAN.md
Petru Paler 76a46439b3 Implement auth middleware for API routes (P0.2)
Add authentication infrastructure for protected routes:
- withAuth() wrapper for API route handlers (src/lib/auth-middleware.ts)
- Next.js middleware for page protection (src/middleware.ts)

withAuth() loads auth from cookies, validates session, and passes
user context to handlers. Returns 401 for unauthenticated requests.

Page middleware redirects unauthenticated users to /login, while
allowing public routes (/login), API routes (handled separately),
and static assets through.

Tests: 18 new tests (6 for withAuth, 12 for page middleware)
Total test count: 60 tests passing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 18:43:19 +00:00

21 KiB

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 501 Returns Not Implemented
PATCH /api/user 501 Returns Not Implemented
POST /api/cycle/period 501 Returns Not Implemented
GET /api/cycle/current 501 Returns Not Implemented
GET /api/today 501 Returns Not Implemented
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/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
API route tests NONE
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

  • 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

  • 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

  • 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

  • Return authenticated user profile
  • Files:
    • src/app/api/user/route.ts - Implement GET handler with auth middleware
  • Tests:
    • src/app/api/user/route.test.ts - Test auth required, correct response shape
  • 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

  • Log period start date, update user record, create PeriodLog
  • Files:
    • src/app/api/cycle/period/route.ts - Implement POST handler
  • Tests:
    • src/app/api/cycle/period/route.test.ts - Test date validation, user update, log creation
  • Why: Cycle tracking is the foundation of all recommendations
  • Depends On: P0.1, P0.2

P1.3: GET /api/cycle/current Implementation

  • Return current cycle day, phase, and phase config
  • Files:
    • src/app/api/cycle/current/route.ts - Implement GET using cycle.ts utilities
  • Tests:
    • src/app/api/cycle/current/route.test.ts - Test phase calculation, config response
  • Why: Dashboard needs this for display
  • Depends On: P0.1, P0.2, P1.2

P1.4: GET /api/today Implementation

  • Return complete daily snapshot with decision, biometrics, nutrition
  • Files:
    • src/app/api/today/route.ts - Implement GET aggregating all data sources
  • Tests:
    • src/app/api/today/route.test.ts - Test decision computation, data assembly
  • 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

  • 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

  • cycle.ts - Complete with 9 tests (getCycleDay, getPhase, getPhaseConfig, getPhaseLimit)
  • decision-engine.ts - Complete with 24 tests (getTrainingDecision + getDecisionWithOverrides)
  • pocketbase.ts - Complete with 9 tests (createPocketBaseClient, isAuthenticated, getCurrentUser, loadAuthFromCookies)

Components

  • DecisionCard - Displays decision status, icon, and reason
  • DataPanel - Shows body battery, HRV, intensity data
  • NutritionPanel - Shows seeds, carbs, keto guidance
  • OverrideToggles - Toggle buttons for flare/stress/sleep/pms
  • DayCell - Phase-colored calendar day cell with click handler

Discovered Issues

Bugs and inconsistencies found during implementation

  • src/lib/auth-middleware.ts does not exist - CREATED in P0.2
  • src/middleware.ts does not exist - CREATED in P0.2
  • garmin.ts is only ~30% complete - missing specific biometric fetchers
  • 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