Document spec gaps: auth, phase scaling, observability, testing

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>
This commit is contained in:
2026-01-11 07:49:56 +00:00
parent 97a424e41d
commit 6a8d55c0b9
9 changed files with 596 additions and 29 deletions

68
spec.md
View File

@@ -425,14 +425,18 @@ interface PeriodLog {
}
```
### Data Retention
All logs (DailyLog, PeriodLog) are retained indefinitely. No automatic deletion or archival. SQLite handles large datasets efficiently for single-user scenarios.
---
## API Routes
```
# Auth (PocketBase handles user auth)
POST /api/auth/login
POST /api/auth/register
# Auth (PocketBase handles via OIDC)
GET /api/auth/login - Initiate OIDC flow
GET /api/auth/callback - OIDC callback handler
POST /api/auth/logout
# Garmin Token Management
@@ -450,7 +454,8 @@ GET /api/cycle/current - Current phase info
# Daily
GET /api/today - Today's decision + all data
GET /api/history - Historical logs
GET /api/history - Historical logs (cursor-based pagination)
Query params: ?cursor=<lastId>&limit=20
# Calendar
GET /api/calendar/:userId/:token.ics - ICS feed (public, token-protected)
@@ -460,6 +465,10 @@ POST /api/calendar/regenerate-token - Generate new calendar token
POST /api/overrides - Set active overrides
DELETE /api/overrides/:type - Remove override
# Observability
GET /api/health - Health check for monitoring
GET /metrics - Prometheus metrics endpoint
# Cron (internal, protected)
POST /api/cron/garmin-sync - Fetch Garmin data (6 AM)
POST /api/cron/notifications - Send emails (7 AM)
@@ -773,7 +782,7 @@ EOF
|----------|----------|
| Garmin API unavailable | Use last known values, note in email |
| Garmin tokens expired | Email user with re-auth instructions |
| No Garmin data yet | Use phase-only decision |
| Garmin not connected | Block app usage, show onboarding prompt |
| User hasn't set period date | Prompt in dashboard, block email until set |
| Email delivery failure | Log, retry once |
| Invalid ICS request | Return 404 |
@@ -785,11 +794,51 @@ EOF
- Garmin tokens encrypted at rest (AES-256)
- ICS feed URL contains random token (not guessable)
- Cron endpoints protected by secret header
- PocketBase handles user auth
- PocketBase handles user auth via OIDC
- HTTPS enforced via Traefik
---
## Backup & Recovery
PocketBase stores all data in a single SQLite database file.
**Backup Procedure:**
```bash
# Stop PocketBase or use SQLite backup API
cp /path/to/pb_data/data.db /path/to/backups/data-$(date +%Y%m%d).db
```
**What to backup:**
- `pb_data/data.db` - Main database (users, logs, settings)
- `pb_data/storage/` - Any uploaded files (if applicable)
**Recovery:**
```bash
# Stop PocketBase
cp /path/to/backups/data-YYYYMMDD.db /path/to/pb_data/data.db
# Restart PocketBase
```
Backups are manual. Set up your own cron job or backup solution as needed.
---
## Known Limitations
The following are **out of scope** for MVP:
| Limitation | Notes |
|------------|-------|
| Phase-only mode | Garmin connection required; no fallback without biometrics |
| Pregnancy/menopause | Cycle tracking assumes regular menstrual cycles |
| Hormonal birth control | May disrupt natural cycle phases |
| API versioning | Single version; breaking changes via deprecation |
| Formal API documentation | Endpoints documented in spec only |
| E2E tests | Unit + integration tests only (authorized skip) |
---
## File Structure
```
@@ -821,9 +870,12 @@ phaseflow/
│ │ │ ├── [userId]/[token].ics/route.ts
│ │ │ └── regenerate-token/route.ts
│ │ ├── overrides/route.ts
│ │ ├── health/route.ts
│ │ └── cron/
│ │ ├── garmin-sync/route.ts
│ │ └── notifications/route.ts
│ │ └── metrics/
│ │ └── route.ts # Prometheus metrics
│ ├── components/
│ │ ├── dashboard/
│ │ │ ├── decision-card.tsx
@@ -843,7 +895,9 @@ phaseflow/
│ │ ├── nutrition.ts
│ │ ├── email.ts
│ │ ├── ics.ts # ICS feed generation
│ │ ── encryption.ts
│ │ ── encryption.ts
│ │ ├── logger.ts # Structured JSON logging
│ │ └── metrics.ts # Prometheus metrics
│ └── types/
│ └── index.ts
├── flake.nix