Address 21 previously undefined behaviors across specs: - Authentication: Replace email/password with OIDC (Pocket-ID) - Cycle tracking: Add fixed-luteal phase scaling formula with examples - Calendar: Document period logging behavior (preserve predictions) - Garmin: Clarify connection is required (no phase-only mode) - Dashboard: Add UI states, dark mode, onboarding, accessibility - Notifications: Document timezone batching approach - New specs: observability.md (health, metrics, logging) - New specs: testing.md (unit + integration strategy) - Main spec: Add backup/recovery, known limitations, API updates Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
148 lines
3.9 KiB
Markdown
148 lines
3.9 KiB
Markdown
# Cycle Tracking Specification
|
|
|
|
## Job to Be Done
|
|
|
|
When I log my period start date, I want the app to calculate my current cycle day and phase, so that training and nutrition guidance is accurate.
|
|
|
|
## Core Concepts
|
|
|
|
### Cycle Day
|
|
|
|
Day 1 = first day of menstruation.
|
|
|
|
**Calculation:**
|
|
```
|
|
cycleDay = ((currentDate - lastPeriodDate) mod cycleLength) + 1
|
|
```
|
|
|
|
Default cycle length: 31 days (configurable per user).
|
|
|
|
### Cycle Phases
|
|
|
|
Phase boundaries scale based on cycle length using a **fixed luteal, variable follicular** approach. The luteal phase (14 days) is biologically consistent; the follicular phase expands or contracts with cycle length.
|
|
|
|
**Phase Calculation Formula:**
|
|
|
|
Given `cycleLength` (user-configurable, default 31):
|
|
|
|
| Phase | Days | Weekly Limit | Training Type |
|
|
|-------|------|--------------|---------------|
|
|
| MENSTRUAL | 1-3 | 30 min | Gentle rebounding only |
|
|
| FOLLICULAR | 4 to (cycleLength - 16) | 120 min | Strength + rebounding |
|
|
| OVULATION | (cycleLength - 15) to (cycleLength - 14) | 80 min | Peak performance |
|
|
| EARLY_LUTEAL | (cycleLength - 13) to (cycleLength - 7) | 100 min | Moderate training |
|
|
| LATE_LUTEAL | (cycleLength - 6) to cycleLength | 50 min | Gentle rebounding ONLY |
|
|
|
|
**Examples by Cycle Length:**
|
|
|
|
| Phase | 28-day | 31-day | 35-day |
|
|
|-------|--------|--------|--------|
|
|
| MENSTRUAL | 1-3 | 1-3 | 1-3 |
|
|
| FOLLICULAR | 4-12 | 4-15 | 4-19 |
|
|
| OVULATION | 13-14 | 16-17 | 20-21 |
|
|
| EARLY_LUTEAL | 15-21 | 18-24 | 22-28 |
|
|
| LATE_LUTEAL | 22-28 | 25-31 | 29-35 |
|
|
|
|
## API Endpoints
|
|
|
|
### POST `/api/cycle/period`
|
|
|
|
Log a new period start date.
|
|
|
|
**Request Body:**
|
|
```json
|
|
{
|
|
"startDate": "2024-01-10"
|
|
}
|
|
```
|
|
|
|
**Behavior:**
|
|
1. Update `user.lastPeriodDate`
|
|
2. Create `PeriodLog` record for history
|
|
3. Recalculate today's cycle day and phase
|
|
|
|
### GET `/api/cycle/current`
|
|
|
|
Returns current cycle information.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"cycleDay": 12,
|
|
"phase": "FOLLICULAR",
|
|
"daysUntilNextPhase": 3,
|
|
"phaseConfig": {
|
|
"weeklyLimit": 120,
|
|
"dailyAvg": 17,
|
|
"trainingType": "Strength + rebounding"
|
|
}
|
|
}
|
|
```
|
|
|
|
## Cycle Utilities (`src/lib/cycle.ts`)
|
|
|
|
**Functions:**
|
|
- `getCycleDay(lastPeriodDate, cycleLength, currentDate)` - Calculate day in cycle
|
|
- `getPhase(cycleDay)` - Determine current phase
|
|
- `getPhaseConfig(phase)` - Get phase configuration
|
|
- `getPhaseLimit(phase)` - Get weekly intensity limit
|
|
|
|
**Constants:**
|
|
- `PHASE_CONFIGS` - Array of phase definitions
|
|
|
|
## Period History
|
|
|
|
### PeriodLog Schema
|
|
|
|
```typescript
|
|
interface PeriodLog {
|
|
id: string;
|
|
user: string;
|
|
startDate: Date;
|
|
created: Date;
|
|
}
|
|
```
|
|
|
|
### History Display (`/history`)
|
|
|
|
- List of all logged period dates
|
|
- Calculated cycle lengths between periods
|
|
- Average cycle length over time
|
|
- Ability to edit/delete entries
|
|
|
|
## Configurable Cycle Length
|
|
|
|
Users with irregular cycles can adjust:
|
|
- Default: 31 days
|
|
- Range: 21-45 days
|
|
- Affects phase day boundaries per formula above
|
|
|
|
## Known Limitations
|
|
|
|
The following scenarios are **out of scope** for MVP:
|
|
|
|
- **Pregnancy** - Cycle tracking pauses; app not designed for pregnancy
|
|
- **Menopause** - Irregular/absent cycles not supported
|
|
- **Hormonal birth control** - May suppress or alter natural cycle phases
|
|
- **Anovulatory cycles** - App assumes ovulation occurs; no detection for skipped ovulation
|
|
|
|
Users in these situations should consult healthcare providers for personalized guidance.
|
|
|
|
## Success Criteria
|
|
|
|
1. Cycle day resets to 1 on period log
|
|
2. Phase transitions at correct day boundaries
|
|
3. Weekly limits adjust per phase automatically
|
|
4. History shows all logged periods
|
|
|
|
## Acceptance Tests
|
|
|
|
- [ ] `getCycleDay` returns 1 on period start date
|
|
- [ ] `getCycleDay` handles cycle rollover correctly
|
|
- [ ] `getPhase` returns correct phase for each day range
|
|
- [ ] `getPhase` scales correctly for 28-day cycle
|
|
- [ ] `getPhase` scales correctly for 35-day cycle
|
|
- [ ] POST `/api/cycle/period` updates user record
|
|
- [ ] GET `/api/cycle/current` returns accurate phase info
|
|
- [ ] Days beyond cycle length default to LATE_LUTEAL
|