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 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 specific fetchers for HRV, Body Battery, and Intensity Minutes
to enable real biometric data collection from Garmin Connect API.
Functions added:
- fetchHrvStatus(): Returns "Balanced", "Unbalanced", or "Unknown"
- fetchBodyBattery(): Returns current BB and yesterday's low value
- fetchIntensityMinutes(): Returns 7-day rolling sum of activity
All functions gracefully handle API failures with safe defaults.
Test count expanded from 14 to 33 covering all scenarios.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement comprehensive test coverage for five library modules:
- encryption.test.ts (14 tests): AES-256-GCM encrypt/decrypt round-trip,
ciphertext format validation, error handling, key padding/truncation
- nutrition.test.ts (17 tests): seed cycling by cycle day, carb ranges
by phase, keto guidance by phase, seed switch alert on day 15
- garmin.test.ts (14 tests): token expiry checks, days until expiry
calculation, API fetch with auth headers, error handling
- email.test.ts (14 tests): daily email composition with biometrics,
nutrition guidance, period confirmation emails, null value handling
- ics.test.ts (23 tests): ICS format validation, phase events with
emojis, warning events on days 22/25, cycle length handling
Total: 82 new tests bringing project total to 254 passing tests.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
Add authentication utilities to pocketbase.ts for server-side auth:
- createPocketBaseClient() - factory for fresh instances per request
- isAuthenticated(pb) - checks authStore validity
- getCurrentUser(pb) - returns typed User from authStore
- loadAuthFromCookies(pb, cookies) - loads auth from Next.js cookies
Includes 9 unit tests covering all auth state scenarios and cookie loading.
This unblocks P0.2 (auth middleware) and all downstream API/page work.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add getDecisionWithOverrides() function that checks manual overrides
before algorithmic rules. Overrides are applied in priority order:
flare > stress > sleep > pms, and all force REST status.
Includes comprehensive test suite with 24 tests covering:
- All 8 algorithmic priority rules
- Override type behaviors
- Override priority enforcement
- Empty override fallthrough to algorithmic rules
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Set up Next.js 16 project with TypeScript for a training decision app
that integrates menstrual cycle phases with Garmin biometrics for
Hashimoto's thyroiditis management.
Stack: Next.js 16, React 19, Tailwind/shadcn, PocketBase, Drizzle,
Zod, Resend, Vitest, Biome, Lefthook, Nix dev environment.
Includes:
- 7 page routes (dashboard, login, settings, calendar, history, plan)
- 12 API endpoints (garmin, user, cycle, calendar, overrides, cron)
- Core lib utilities (decision engine, cycle phases, nutrition, ICS)
- Type definitions and component scaffolding
- Python script for Garmin token bootstrapping
- Initial unit tests for cycle utilities
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>