You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
R1: Wire compute-baselines into sync-all-devices orchestrator. After each
successful device sync, baselines are recomputed for that user. Non-fatal
— sync succeeds even if baseline computation fails.
R2: Add daily_history and days_of_data to starter pack response. The engine
already fetches daily data but was discarding it after averaging. Now
preserves per-metric daily values for trend computation and personal
best detection on the client.
R3: Build computeWins() client-side function. Detects personal bests and
all-in-range events from the enriched scorecard. Capped at 3 wins,
ordered by significance. Wired into my-body.tsx.
R4: CycleInsightCard dismiss persistence via AsyncStorage. Card reappears
after 30 days, permanently dismissed after 2 dismissals.
R5: CycleConfirmationPrompt dismiss tracking. Stops asking after 3
consecutive dismissals of the monthly period check-in.
R6: Retroactive phase correction. When user confirms a period date (via
CycleInsightCard or CycleConfirmationPrompt), compute-baselines is
re-triggered to re-tag historical phase labels and recompute baselines.
R7: Profile cycle editing. Female users can now edit cycle preferences
(regular/irregular/none/prefer not to say) and turn off cycle tracking
from the Profile screen edit modal.
Tests: 2807 mobile (0 fail) + 88 Edge Function (0 fail)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/planning/personalized-health-snapshot.md
+282Lines changed: 282 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -824,6 +824,288 @@ Note: Red is never used. Amber is the strongest signal. The personal baseline ap
824
824
825
825
---
826
826
827
+
## Remaining Implementation: Detailed Strategy
828
+
829
+
The following items complete the feature. Each takes a TDD approach: write failing tests, then implement to make them pass. Items are ordered by dependency — later items depend on earlier ones.
830
+
831
+
### R1: Wire compute-baselines into sync-all-devices (server)
832
+
833
+
**Why:** Without this, baselines are never recomputed after the initial backfill. Users who sync new data see stale personal ranges.
**Change:** After all device sync batches complete (after the current results summary loop), collect unique user IDs from successful syncs and call `compute-baselines` for each via HTTP (same pattern as `callSyncFunction`).
838
+
839
+
```typescript
840
+
// After line ~305 (after all batches complete, before logging summary)
logger.warn('baseline', `Failed to recompute baselines for ${userId}: ${err}`);
857
+
// Non-fatal — sync still succeeded
858
+
}
859
+
}
860
+
```
861
+
862
+
**Tests (add to `sync-all-devices/index.test.ts` or `orchestrator.test.ts`):**
863
+
- After successful device syncs, compute-baselines is called for each unique user ID
864
+
- Failed device syncs do not trigger baseline computation for that user
865
+
- Compute-baselines failure does not fail the overall sync response
866
+
- Compute-baselines is called once per user even if user has multiple devices
867
+
868
+
**Test count:**~4
869
+
870
+
---
871
+
872
+
### R2: Add daily history and days_of_data to starter pack response (server)
873
+
874
+
**Why:** The mobile app needs `days_of_data` for the CycleInsightCard and baseline progress text, and `dailyHistory` for trend computation and personal best detection. The starter pack engine already fetches the raw daily data (lines 157-172 of `starter-pack.ts`) but discards it after computing averages.
1.**Preserve daily values** instead of discarding after averaging. Build a `dailyHistory` map: `Record<string, { date: string; value: number }[]>` keyed by metric_key. Each entry is a day's value for that metric.
881
+
882
+
2.**Count days_of_data** from the total unique dates across daily_summary and sleep_sessions.
883
+
884
+
3.**Return both in the response:**
885
+
```typescript
886
+
return {
887
+
...existingFields,
888
+
days_of_data: uniqueDates.size,
889
+
daily_history: dailyHistoryMap, // new field
890
+
};
891
+
```
892
+
893
+
4.**Update mobile type:** Add `daily_history?: Record<string, { date: string; value: number }[]>` to `StarterPackResult` in `types.ts`.
894
+
895
+
5.**Wire in my-body.tsx:** Pass `starterPack.daily_history` to `buildMetricScorecard` as the `dailyHistory` parameter.
896
+
897
+
**Tests (add to starter pack test file or create new):**
898
+
-`days_of_data` equals the number of unique dates in the data
899
+
-`daily_history` contains entries for each metric with actual daily values
900
+
-`daily_history.resting_hr` has one entry per day, not duplicates
901
+
- Empty data returns `days_of_data: 0` and empty `daily_history`
902
+
903
+
**Test count:**~4
904
+
905
+
---
906
+
907
+
### R3: Build wins computation (client-side)
908
+
909
+
**Why:** The `WinsSection` component exists but nothing produces `WinEvent[]`. Wins need to be computed from the scorecard + daily history on the client side, since they depend on the enriched scorecard (with personal baselines) which is built client-side.
2.**Backinrange:**Foreachmetricwhere`personal_status === 'within'`, checkifthepriorday's status was `'outside_bad'`. If so, create a "Back in your range" win.
0 commit comments