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
{{ message }}
Repository navigation
Body composition and Healthspan contributors: manual/imported trends and local drivers #763
WHOOP now surfaces Healthspan/body-composition-style features such as biological/fitness age framing and body composition integrations. NOOP has local Fitness Age/Vitality work, but needs a clean-room plan for body composition inputs and contributor explanations.
Add local body composition and healthspan contributor support:
manual and imported entries for weight, body-fat percentage, lean mass, waist/measurement fields where appropriate, and VO2/cardio-fitness estimates where available;
trend charts and contributor explanations for Fitness Age/Vitality-style surfaces;
source labeling for manual, Apple Health/Health Connect, CSV/imported, or device-derived inputs;
calibration/insufficient-data states;
transparent formulas and confidence labels, not proprietary biological-age claims.
Non-goals / safety constraints
NOOP must not invent body-fat, lean-mass, or VO2 values from strap data alone.
No medical diagnosis or clinical risk scoring.
No cloud sync/account requirement.
No WHOOP/Sage proprietary logic or UI assets.
Acceptance criteria
User can store and review local body composition entries.
Imported/manual values have source labels and timestamps.
Fitness Age/Vitality contributors can explain which local inputs moved a trend.
Missing/old/stale inputs are clearly labeled.
Swift/Kotlin storage and analytics remain parity-compatible.
Backup/export behavior is specified for any new fields.
Verification
Migration tests cover new storage.
Analytics tests cover trend and contributor calculations.
Import tests cover supported Health/Health Connect/CSV fields when implemented.
Scope reads right — the non-goals line up with docs/SCOPE.md, and "must not invent body-fat/lean-mass/VO2 from strap data alone" is the same rule as the unproven-derivation clause in CLAUDE.md. Nothing here needs a scope change.
More of this exists than the issue implies:
FitnessAgeEngine.estimateVO2max(age:sex:waistCm:restingHR:paIndex:) — VO2max estimate already ships, gated on waist
profile.waistCm is already stored and user-enterable; HealthView already treats bodyMetrics / waist as fixable checklist items and gates on hasWaist
So the genuinely new inputs are body-fat %, lean mass, and additional measurements.
The real work is a storage-shape change, not analytics. Weight/height/waist are currently Profile scalars — one current value, no history. "Trend charts" needs them as a dated series (metricSeries or a new table), plus migrations that agree across Room and GRDB. That's the bulk of the effort and worth scoping first; the contributor explanations are cheap once the series exists.
On the export acceptance criterion — worth pinning concretely, because #718 just hit the failure mode. Cycle data landed in metricSeries, which means it rides .noopbak (whole-file copy) but is excluded from the CSV export, since the sidecar is built from strap-derived source ids only and is never re-imported. Result: it survives a same-platform restore and is lost on an Android↔iOS move. Any new body-comp series will inherit exactly that unless the export path is part of the design.
Suggest splitting: (1) dated body-composition storage + migrations + source labels, (2) trends UI, (3) contributor explanations. (1) is the dependency and the only part with parity/migration risk.
Activity
ryanbr commentedon Jul 25, 2026
Scope reads right — the non-goals line up with
docs/SCOPE.md, and "must not invent body-fat/lean-mass/VO2 from strap data alone" is the same rule as the unproven-derivation clause in CLAUDE.md. Nothing here needs a scope change.More of this exists than the issue implies:
FitnessAgeEngine.estimateVO2max(age:sex:waistCm:restingHR:paIndex:)— VO2max estimate already ships, gated on waistprofile.waistCmis already stored and user-enterable;HealthViewalready treatsbodyMetrics/waistas fixable checklist items and gates onhasWaistLabMarkerCsvImport.ktalready mapswaist_circumference→waistSo the genuinely new inputs are body-fat %, lean mass, and additional measurements.
The real work is a storage-shape change, not analytics. Weight/height/waist are currently Profile scalars — one current value, no history. "Trend charts" needs them as a dated series (metricSeries or a new table), plus migrations that agree across Room and GRDB. That's the bulk of the effort and worth scoping first; the contributor explanations are cheap once the series exists.
On the export acceptance criterion — worth pinning concretely, because #718 just hit the failure mode. Cycle data landed in
metricSeries, which means it rides.noopbak(whole-file copy) but is excluded from the CSV export, since the sidecar is built from strap-derived source ids only and is never re-imported. Result: it survives a same-platform restore and is lost on an Android↔iOS move. Any new body-comp series will inherit exactly that unless the export path is part of the design.Suggest splitting: (1) dated body-composition storage + migrations + source labels, (2) trends UI, (3) contributor explanations. (1) is the dependency and the only part with parity/migration risk.
feat: add dated weight logging and history
feat(health): add local Healthspan and Stress Monitor details
Clarify the profile birthdate behind the local age estimate
Gate age history to adults and retain stress navigation state
Document guarded activity demo fixture and record its parity
Consume owner navigation handoff for Healthspan and breathwork
Add Healthspan calibration and contributor drilldowns
Record Healthspan helper twins and preserve mass units
Record Healthspan parity inventory and preserve reference navigation
Merge latest shell milestone and refresh combined parity inventory
Consume the owned Healthspan helper authority handoff
Merge approved test dependency and verification handoffs
Refresh Healthspan civil-day and committed contributor snapshots
Record Healthspan refresh validation and parity inventory
Bind Healthspan supporting readings to the selected date
Align stress timeline labels with civil-day timestamps
Merge verified design shell corrections