- Add phaseflow_build_info metric with version and commit labels
- Inject GIT_COMMIT env var at build time via next.config.ts
- Add logging to all Garmin fetch functions (HRV, body battery, intensity)
- Log API response status codes, actual data values, and errors
This enables verifying which build is deployed and diagnosing
silent failures where Garmin API returns errors but sync reports success.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
PocketBase coerces null number fields to 0 when reading. When Garmin
API returned no data (null), we stored null, which became 0 on
retrieval. The nullish coalescing (?? 100) in the API route didn't
catch this because 0 is not nullish.
Now store default value 100 when Garmin returns null, matching the
existing pattern used for decision engine calculations.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
PocketBase returns date fields as ISO strings, not Date objects.
The sync was failing with "e.getTime is not a function" because
the code expected Date objects.
- Export mapRecordToUser from pocketbase.ts
- Use mapRecordToUser in cron route to properly parse dates
- Add test for handling date fields as ISO strings
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The cron job needs to list all users, but the users collection
doesn't have a public listRule (for security). Added admin
authentication so the job can access user records.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The period_logs collection was returning 403 errors because API rules
were only configured in the e2e test harness, not in the production
setup script. This consolidates the setup logic so both prod and test
use the same setupApiRules() function.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix OIDC tests with route interception for auth-methods API
- Add data-testid to DecisionCard for reliable test selection
- Fix /api/today to fetch fresh user data instead of stale cookie data
- Fix period logging test timing with proper API wait patterns
- Fix decision engine test with waitForResponse instead of timeout
- Simplify mobile viewport test locator
All 206 e2e tests now pass with 0 skipped.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Creates test infrastructure to enable previously skipped e2e tests:
- Onboarding user (no period data) for setup flow tests
- Established user (period 14 days ago) for normal usage tests
- Calendar user (with calendarToken) for ICS feed tests
- Garmin user (valid tokens) for connected state tests
- Garmin expired user (expired tokens) for expiry warning tests
Also fixes ICS feed route to strip .ics suffix from Next.js dynamic
route param, adds calendarToken to /api/user response, and sets
viewRule on users collection for unauthenticated ICS access.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add OAuth1 to OAuth2 token exchange using Garmin's exchange endpoint
- Track refresh token expiry (~30 days) instead of access token expiry (~21 hours)
- Auto-refresh access tokens in cron sync before they expire
- Update Python script to output refresh_token_expires_at
- Add garminRefreshTokenExpiresAt field to User type and database schema
- Fix token input UX: show when warning active, not just when disconnected
- Add Cache-Control headers to /api/user and /api/garmin/status to prevent stale data
- Add oauth-1.0a package for OAuth1 signature generation
The system now automatically refreshes OAuth2 tokens using the stored OAuth1 token,
so users only need to re-run the Python auth script every ~30 days (when refresh
token expires) instead of every ~21 hours (when access token expires).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1. Increase garminOauth1Token and garminOauth2Token max length from
5000 to 20000 characters to accommodate encrypted OAuth tokens.
Add logic to update existing field constraints in addUserFields().
2. Fix flaky pocketbase-harness e2e test by adding retry logic with
exponential backoff to createAdminUser() and createTestUser().
Handles SQLite database lock during PocketBase startup migrations.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix race conditions: Set workers: 1 since all tests share test user state
- Fix stale data: GET /api/user and /api/cycle/current now fetch fresh data
from database instead of returning stale PocketBase auth store cache
- Fix timing: Replace waitForTimeout with retry-based Playwright assertions
- Fix mobile test: Use exact heading match to avoid strict mode violation
- Add test user setup: Include notificationTime and update rule for users
All 1014 unit tests and 190 E2E tests pass.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The suggested notifications.spec.ts E2E tests for notification preferences
are already thoroughly covered by existing settings.spec.ts tests which
validate notification time and timezone input, validation, and persistence.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Verifies that the app correctly applies light/dark mode styling based on
the user's system preference (prefers-color-scheme). Tests cover both
light and dark modes using Playwright's emulateMedia API.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
Root cause: The setup-db script was missing user field definitions
(garminConnected, tokens, etc.). Production PocketBase had no such
fields, so updates silently failed to persist.
Changes:
- Add user custom fields to setup-db.ts (matches e2e harness)
- Fix status route to use strict boolean check (=== true)
- Add verification in tokens route with helpful error message
- Add ENCRYPTION_KEY to playwright config for e2e tests
- Add comprehensive e2e tests for Garmin connection flow
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Previously, 15 e2e tests were skipped because TEST_USER_EMAIL and
TEST_USER_PASSWORD env vars weren't set. Now the test harness:
- Starts a fresh PocketBase instance in /tmp on port 8091
- Creates admin user, collections, and API rules automatically
- Seeds test user with period data for authenticated tests
- Cleans up temp directory after tests complete
Also fixes:
- Override toggle tests now use checkbox role (not button)
- Adds proper wait for OVERRIDES section before testing toggles
- Suppresses document.cookie lint warning with explanation
Test results: 64 e2e tests pass, 1014 unit tests pass
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>
Fetch fresh user data from database in status endpoint instead of
relying on auth store cookie, which may be stale after token save.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add playwright-web-flake to flake.nix for NixOS browser support
- Pin @playwright/test@1.56.1 to match nixpkgs version
- Create playwright.config.ts with Chromium-only, auto-start dev server
- Add e2e/smoke.spec.ts with initial smoke tests
- Add .mcp.json for Claude browser control via MCP
- Update .gitignore for playwright artifacts
- Remove E2E test skip from spec.md Known Limitations
- Update specs/testing.md to require three-tier testing approach
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Output expires_at as ISO 8601 date string instead of Unix timestamp.
PocketBase date fields expect ISO format, and the integer was causing
token saves to fail silently.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add scripts/setup-db.ts to programmatically create missing PocketBase
collections (period_logs, dailyLogs) with proper relation fields
- Fix dark mode visibility across settings, login, calendar, and dashboard
components by using semantic CSS tokens and dark: variants
- Add db:setup npm script and document usage in AGENTS.md
- Update vitest config to include scripts directory tests
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Garth's OAuth1Token and OAuth2Token are Pydantic dataclasses, not BaseModel
subclasses, so they require TypeAdapter for serialization instead of model_dump().
Also adds user-friendly error handling for authentication failures.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The garth library uses Pydantic dataclasses for OAuth tokens which don't
have a serialize() method. Use model_dump() instead, and fix expires_at
handling since it's an integer timestamp not a datetime object.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Routes using withAuth were creating new unauthenticated PocketBase
clients, causing 404 errors when trying to update records. Modified
withAuth to pass the authenticated pb client to handlers so they can
use it for database operations.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Users without a lastPeriodDate can now set it via a modal opened from
the onboarding banner. The dashboard now fetches user data independently
so the banner shows even when /api/today fails due to missing period date.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add parseDate helper that safely returns null for empty/invalid date
strings from PocketBase. This prevents RangeError when pino logger
tries to serialize Invalid Date objects via toISOString().
- Make garminTokenExpiresAt and lastPeriodDate nullable in User type
- Filter garmin-sync cron to skip users without required dates
- Add test assertions for null date handling
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>