feat(measurements): add WHOOP source, native score types, and source-priority ladders

Add the WHOOP integration data layer (migration 0111): seven new
MeasurementType values (HRV_RMSSD, DAY_STRAIN, WORKOUT_STRAIN,
SLEEP_PERFORMANCE, SLEEP_EFFICIENCY, SLEEP_CONSISTENCY, SLEEP_NEED,
ENERGY_EXPENDITURE_KJ), a WHOOP MeasurementSource, the WhoopConnection
and WhoopOAuthState models, and the per-user encrypted BYO-key columns
on User. Day-strain takes DAY_STRAIN (not the COMPUTED STRAIN_SCORE) and
RMSSD takes HRV_RMSSD (not the SDNN HEART_RATE_VARIABILITY) so a native
value never shares a bucket with a derived proxy.

Carry the new types through the completeness walls (units, value ranges,
chart tokens, PR direction, categories, list-meta colours/icons/labels,
HealthKit-mapping exclusions) and feed the cross-source picker: recovery
ladder (WHOOP native above the COMPUTED proxy), WHOOP inserted into the
overlapping vital ladders and the workout ladder, plus skinTemperature
and respiratoryRate metric keys.
This commit is contained in:
Marc-André Bombeck committed 2026-06-04 00:49:31 +02:00
1 parent 747a62516a
commit 6db3bef413
21 files changed
+649 -31

No files matched your search

@@ -79,6 +79,10 @@ const METRIC_LABEL_KEYS: Record<SourcePriorityMetricKey, string> = {
hrv: "settings.sections.sources.metrics.hrv",
restingHeartRate: "settings.sections.sources.metrics.restingHeartRate",
vo2Max: "settings.sections.sources.metrics.vo2Max",
// v1.11.0 — WHOOP-overlapping metric classes + native-vs-derived recovery.
skinTemperature: "settings.sections.sources.metrics.skinTemperature",
respiratoryRate: "settings.sections.sources.metrics.respiratoryRate",
recovery: "settings.sections.sources.metrics.recovery",
};
const SOURCE_LABEL_KEYS: Record<string, string> = {
@@ -86,6 +90,10 @@ const SOURCE_LABEL_KEYS: Record<string, string> = {
APPLE_HEALTH: "settings.sections.sources.sourceLabels.APPLE_HEALTH",
MANUAL: "settings.sections.sources.sourceLabels.MANUAL",
IMPORT: "settings.sections.sources.sourceLabels.IMPORT",
// v1.11.0 — WHOOP native source + the COMPUTED proxy (surfaces in the
// `recovery` ladder for native-vs-derived ordering).
WHOOP: "settings.sections.sources.sourceLabels.WHOOP",
COMPUTED: "settings.sections.sources.sourceLabels.COMPUTED",
};
const DEVICE_TYPE_LABEL_KEYS: Record<DeviceType, string> = {