Add tests to verify error handling when network requests fail:
- Error toast when token save fails (500 response)
- Error toast when disconnect fails (500 response)
- Error state display when status fetch fails
- Retry succeeds after network failure
These tests improve resilience coverage for the Garmin connection flow.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests mobile responsiveness including:
- Login page renders correctly on mobile viewport
- Dashboard displays correctly on mobile viewport
- Dashboard uses single-column layout on mobile (< 768px)
- Navigation elements are interactive on mobile
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
New auth.spec.ts tests:
- OIDC button shows provider name when configured
- OIDC button shows loading state during authentication
- OIDC button is disabled when rate limited
- Session persists after page refresh
- Session persists when navigating between pages
- Logout clears session and redirects to login
E2E test count: 180 → 186 (auth.spec.ts: 14 → 20)
Total tests: 1194 → 1200
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests added:
- notification time changes persist after page reload
- timezone changes persist after page reload
- multiple settings changes persist after page reload
- cycle length persistence verifies exact saved value
- settings form shows correct values after save without reload
Updated IMPLEMENTATION_PLAN.md with accurate E2E test counts (now 180 E2E tests).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
New tests cover:
- Period date modal opens from dashboard onboarding banner
- Period date input restricts future dates via max attribute
- Logging period from modal updates dashboard cycle info
- Edit period modal flow changes date successfully
- Delete period confirmation flow removes entry
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
New tests:
- Yellow warning banner when token expires in 10 days (warning level)
- Red critical banner when token expires in 5 days (critical level)
- Expired token state shows token input for re-entry
- Connection persists after page reload
- Can reconnect after disconnecting
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Reduced the implementation plan from 1514 lines to 156 lines (90% reduction)
by consolidating completed items into compact summary tables. The project is
feature complete with 1014 unit tests and 165 E2E tests passing.
Key changes:
- Removed detailed task descriptions for completed P0-P5 items
- Consolidated library, API, page, component, and E2E test summaries
- Preserved critical business rules and architecture notes
- Kept E2E enhancement opportunities for future reference
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Period logging tests (5 new):
- Future date validation
- Cycle length display between periods
- Prediction accuracy display
- Delete period log from history
- Edit period log from history
Calendar tests (8 new):
- Today highlight in calendar view
- Phase colors in calendar days
- Phase legend display
- Today button for quick navigation
- Multi-month navigation with return to today
- Calendar URL generation
- URL format validation
- Copy to clipboard functionality
Total E2E tests: 113 (was 100)
Total unit tests: 1014 (51 test files)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
New E2E test files:
- e2e/health.spec.ts: 3 tests for health/observability endpoints
- e2e/history.spec.ts: 7 tests for history page
- e2e/plan.spec.ts: 7 tests for exercise plan page
- e2e/decision-engine.spec.ts: 8 tests for decision display and overrides
- e2e/cycle.spec.ts: 11 tests for cycle tracking, settings, and period logging
Total E2E tests: 100 (up from 64)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- NutritionPanel: Display seed switch alert on day 15 per dashboard spec
- MonthView: Add phase emojis to legend (🩸🌱🌸🌙🌑) per calendar spec
- DayCell: Show period indicator (🩸) for days 1-3 per calendar spec
- Auth middleware: Log client IP from x-forwarded-for/x-real-ip per observability spec
- Updated NutritionGuidance type to include seedSwitchAlert field
- /api/today now returns seedSwitchAlert in nutrition response
Test coverage: 1005 tests (15 new tests added)
- nutrition-panel.test.tsx: +4 tests
- month-view.test.tsx: +1 test
- day-cell.test.tsx: +5 tests
- auth-middleware.test.ts: +3 tests
- today/route.test.ts: +2 tests
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Email subject now follows spec format: PhaseFlow: [STATUS] - Day [cycleDay] ([phase])
- Daily email includes seed switch alert on day 15 (using getSeedSwitchAlert)
- Data panel HRV status now color-coded: green=Balanced, red=Unbalanced, gray=Unknown
- Data panel shows progress bar for week intensity vs phase limit with color thresholds
Adds 13 new tests (990 total).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The spec (decision-engine.md lines 93-94) clearly states:
- sleep override -> GENTLE
- pms override -> GENTLE
But the implementation was returning REST for all overrides. This fix:
- Updates decision-engine.ts to use OVERRIDE_DECISIONS with correct status/reason/icon per override type
- Updates tests to expect GENTLE for sleep and pms overrides
- Aligns implementation with specification
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update gap analysis note to reflect current test counts (977 unit tests across
50 files + 64 E2E tests) and complete status of all P0-P5 items.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Mark P3.10 E2E Test Suite as COMPLETE (was showing unchecked)
- Update file path from tests/e2e/ to e2e/ (actual location)
- Add reference to P5.4 which has full implementation details
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Creates Gitea Actions workflow that runs on pull requests and pushes
to main. Enforces quality gates (lint, typecheck, unit tests) in CI,
complementing the local Lefthook pre-commit hooks.
Features:
- Node.js 24 with pnpm 10 setup
- pnpm dependency caching for faster runs
- Linting with biome
- TypeScript type checking
- 950 unit tests via vitest
Completes P5.3 from the implementation plan.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add GET /api/period-history route with pagination, cycle length
calculation, and prediction accuracy tracking
- Add PATCH/DELETE /api/period-logs/[id] routes for editing and
deleting period entries with ownership validation
- Add /period-history page with table view, edit/delete modals,
and pagination controls
- Include 61 new tests covering all functionality
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Per dashboard.md spec requirements:
- RED background and text for REST decisions
- YELLOW background and text for GENTLE/LIGHT/REDUCED decisions
- GREEN background and text for TRAIN decisions
Added 8 new tests for color-coded backgrounds (19 total).
Updated IMPLEMENTATION_PLAN.md to mark spec gap as complete.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement email logging per observability spec:
- Add structured logging for email sent (info level) and failed (error level)
- Include userId, type, and recipient fields in log events
- Add userId parameter to email functions (sendDailyEmail, sendPeriodConfirmationEmail, sendTokenExpirationWarning)
- Update cron routes (notifications, garmin-sync) to pass userId
6 new tests added to email.test.ts (now 30 tests total)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add POST /api/auth/logout endpoint with tests (5 tests)
- Add logout button to settings page (5 tests)
- Add structured logging to garmin-sync cron (sync start/complete/failure)
- Update IMPLEMENTATION_PLAN.md with spec gap analysis findings
- Total: 835 tests passing across 44 test files
Closes spec gaps from authentication.md (logout) and observability.md (logging)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Verified that loading performance requirements are met:
- Next.js loading.tsx files render immediately during navigation (< 100ms)
- Skeleton components exist for all routes (P3.8 already complete)
- Optimistic UI updates implemented for override toggles
- Suspense boundaries provided by Next.js App Router
All P4 UX Polish items are now complete.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
CRITICAL BUG FIX:
- Phase boundaries were hardcoded for 31-day cycle, breaking correct
phase calculations for users with different cycle lengths (28, 35, etc.)
- Added getPhaseBoundaries(cycleLength) function in cycle.ts
- Updated getPhase() to accept cycleLength parameter (default 31)
- Updated all callers (API routes, components) to pass cycleLength
- Added 13 new tests for phase boundaries with 28, 31, and 35-day cycles
ICS IMPROVEMENTS:
- Fixed emojis to match calendar.md spec: 🩸🌱🌸🌙🌑
- Added CATEGORIES field for calendar app colors per spec:
MENSTRUAL=Red, FOLLICULAR=Green, OVULATION=Pink,
EARLY_LUTEAL=Yellow, LATE_LUTEAL=Orange
- Added 5 new tests for CATEGORIES
Updated IMPLEMENTATION_PLAN.md with discovered issues and test counts.
825 tests passing (up from 807)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements visual feedback for cycle prediction accuracy in ICS calendar feeds:
- Add predictedDate field to PeriodLog type for tracking predicted vs actual dates
- POST /api/cycle/period now calculates and stores predictedDate based on
previous lastPeriodDate + cycleLength, returns daysEarly/daysLate in response
- ICS feed generates "(Predicted)" events when actual period start differs
from predicted, with descriptions like "period arrived 2 days early"
- Calendar route fetches period logs and passes them to ICS generator
This creates an accuracy feedback loop helping users understand their cycle
variability over time per calendar.md spec.
807 tests passing across 43 test files.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement client-side rate limiting for login page with 5 attempts
per minute, matching the spec requirement in authentication.md.
Features:
- Track login attempts with timestamps in component state
- Block login when 5+ attempts made within 60 seconds
- Show "Too many login attempts" error when rate limited
- Show remaining attempts warning after 3 failures
- Disable form/button when rate limited
- Auto-clear after 1 minute cooldown
- Works for both email/password and OIDC authentication
Tests:
- 6 new tests covering rate limiting scenarios (32 total)
- 796 tests passing across 43 test files
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Switch from class-based dark mode to automatic system preference
detection using CSS prefers-color-scheme media query. The app now
respects the user's OS-level dark mode setting without requiring
a manual toggle, as specified in the dashboard requirements.
Changes:
- Update Tailwind custom variant to use @media (prefers-color-scheme: dark)
- Change .dark selector to media query wrapping :root variables
- No component changes needed - existing CSS variable system handles theming
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement keyboard navigation for MonthView calendar:
- ArrowLeft/Right: navigate to previous/next day
- ArrowUp/Down: navigate to previous/next week (7 days)
- Home/End: navigate to first/last day of month
- Boundary navigation triggers month change
Features:
- Added role="grid" for proper ARIA semantics
- Added data-day attribute to DayCell for focus management
- Wrapped navigation handlers in useCallback for stability
Tests: 9 new tests for keyboard navigation (790 total)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add skip navigation link to root layout
- Add semantic HTML landmarks (main element) to login and settings pages
- Add aria-labels to calendar day buttons with date, cycle day, and phase info
- Add id="main-content" to dashboard main element for skip link target
- Fix pre-existing type error in auth-middleware.test.ts
Tests: 781 passing (11 new accessibility tests)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement OnboardingBanner component that prompts new users to complete
setup with contextual banners for:
- Garmin connection (links to /settings/garmin)
- Period date (button with callback for date picker)
- Notification time (links to /settings)
Banners display at the top of the dashboard when setup is incomplete,
with icons and styled action buttons. Each banner uses role="alert"
for accessibility.
- Add OnboardingBanner component (16 tests)
- Integrate into dashboard page (5 new tests, 28 total)
- Update UserData interface to include garminConnected, notificationTime
- Test count: 770 tests across 43 files
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add skeleton loading components per specs/dashboard.md requirements:
- DecisionCardSkeleton: Shimmer placeholder for status and reason
- DataPanelSkeleton: Skeleton rows for 5 metrics
- NutritionPanelSkeleton: Skeleton for nutrition guidance
- MiniCalendarSkeleton: Placeholder grid with navigation and legend
- OverrideTogglesSkeleton: 4 toggle placeholders
- CycleInfoSkeleton: Cycle day and phase placeholders
- DashboardSkeleton: Combined skeleton for route-level loading
Add Next.js loading.tsx files for instant loading states:
- src/app/loading.tsx (Dashboard)
- src/app/calendar/loading.tsx
- src/app/history/loading.tsx
- src/app/plan/loading.tsx
- src/app/settings/loading.tsx
Update dashboard page to use DashboardSkeleton instead of "Loading..." text.
Fix flaky garmin test with wider date tolerance for timezone variations.
29 new tests in skeletons.test.tsx (749 total tests passing).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add OIDC/OAuth2 authentication support to the login page with automatic
provider detection and email/password fallback.
Features:
- Auto-detect OIDC provider via PocketBase listAuthMethods() API
- Display "Sign In with Pocket-ID" button when OIDC is configured
- Use PocketBase authWithOAuth2() popup-based OAuth2 flow
- Fall back to email/password form when OIDC not available
- Loading states during authentication
- Error handling with user-friendly messages
The implementation checks for available auth methods on page load and
conditionally renders either the OIDC button or the email/password form.
This allows production deployments to use OIDC while development
environments can continue using email/password.
Tests: 24 tests (10 new OIDC tests added)
- OIDC button rendering when provider configured
- OIDC authentication flow with authWithOAuth2
- Loading and error states for OIDC
- Fallback to email/password when OIDC unavailable
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive training plan reference page that displays:
- Current phase status (day, phase name, training type, weekly limit)
- Phase overview cards for all 5 cycle phases with weekly intensity limits
- Strength training exercises reference with sets and reps
- Rebounding techniques organized by phase
- Weekly training guidelines for each phase
The page fetches cycle data from /api/cycle/current and highlights
the current phase. Implements full TDD with 16 tests covering loading
states, error handling, phase display, and exercise reference sections.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Complete the MiniCalendar component with:
- Full calendar grid showing all days of the month
- Phase colors applied to each day
- Today highlighting with ring indicator
- Navigation buttons (prev/next month, Today)
- Compact phase legend
- Integration into dashboard page (shows when lastPeriodDate exists)
Adds 23 new tests for the MiniCalendar component covering:
- Calendar grid rendering
- Phase color application
- Navigation functionality
- Cycle rollover handling
- Custom year/month props
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive metrics collection for production monitoring:
- src/lib/metrics.ts: prom-client based metrics library with custom counters,
gauges, and histograms for Garmin sync, email, and decision engine
- GET /api/metrics: Prometheus-format endpoint for scraping
- Integration into garmin-sync cron: sync duration, success/failure counts,
active users gauge
- Integration into email.ts: daily and warning email counters
- Integration into decision-engine.ts: decision type counters
Custom metrics implemented:
- phaseflow_garmin_sync_total (counter with status label)
- phaseflow_garmin_sync_duration_seconds (histogram)
- phaseflow_email_sent_total (counter with type label)
- phaseflow_decision_engine_calls_total (counter with decision label)
- phaseflow_active_users (gauge)
33 new tests (18 library + 15 route), bringing total to 586 tests.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add pino-based logger module for production observability:
- JSON output to stdout for log aggregators (Loki, ELK)
- Configurable via LOG_LEVEL environment variable (defaults to "info")
- Log levels: error, warn, info, debug
- Error objects serialized with type, message, and stack trace
- Child logger support for bound context
- ISO 8601 timestamps in all log entries
Test coverage: 16 tests covering JSON format, log levels, error
serialization, and child loggers.
Total tests now: 553 passing across 31 test files.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add email warnings for Garmin token expiration at 14-day and 7-day thresholds.
When the garmin-sync cron job runs, it now checks each user's token expiry and
sends a warning email at exactly 14 days and 7 days before expiration.
Changes:
- Add sendTokenExpirationWarning() to email.ts with differentiated subject
lines and urgency levels for 14-day vs 7-day warnings
- Integrate warning logic into garmin-sync cron route using daysUntilExpiry()
- Track warnings sent in sync response with new warningsSent counter
- Add 20 new tests (10 for email function, 10 for sync integration)
Test count: 517 → 537
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add GET /api/health endpoint for deployment monitoring and load balancer
health probes. Returns 200 with status "ok" when PocketBase is reachable,
503 with status "unhealthy" when PocketBase connection fails.
Response includes timestamp (ISO 8601), version, and error message (on failure).
Uses PocketBase SDK's built-in health.check() method for connectivity testing.
14 tests covering healthy/unhealthy states and edge cases.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add full Garmin connection management page at /settings/garmin
- Display connection status with colored indicators (green/red/gray)
- Show token expiry warnings (yellow 14 days, red 7 days)
- Token input form with JSON validation for bootstrap script output
- Disconnect functionality with confirmation
- Loading and error states throughout
- Add link from Settings page to Garmin settings
- 27 tests for Garmin settings page
- 3 additional tests for Settings page Garmin link
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add functional history page that displays DailyLog entries in a table
with date, cycle day/phase, decision, body battery, HRV, and intensity
columns. Features include:
- Data fetching from /api/history endpoint
- Pagination with previous/next navigation
- Date filtering with start/end date inputs
- Decision color coding (REST=red, TRAIN=green, GENTLE/LIGHT/REDUCED=yellow)
- Loading and error states
- Empty state when no history exists
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add client-side form for cycleLength, notificationTime, timezone
- Fetch user data on mount and pre-fill form values
- Submit updates via PATCH /api/user with loading states
- Display success/error messages with proper accessibility
- Clear messages when user modifies form
- 24 tests covering rendering, data loading, validation, error handling
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add GET /api/history for paginated historical daily logs with:
- Pagination support (page/limit query params, default 20 per page)
- Date filtering (startDate/endDate in YYYY-MM-DD format)
- Validation for all parameters with descriptive error messages
- Sort by date descending (most recent first)
- Response includes items, total, page, limit, totalPages, hasMore
Includes 19 tests covering pagination, date filtering, auth, and validation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>