Skip to main content
Synced from the repo — do not edit here

Canonical source: docs/claude/app-redesign-scores.md. This page is generated by docs/scripts/sync-handbook.mjs. Edit the source file in the repo; changes appear here on the next build.

App Redesign — Key Scores (Biological Age & composite metrics)

Companion to app-redesign-data-contracts.md. Defines the composite scores the redesign surfaces so they're computed from real, sourced formulas — not invented. Figma fileKey 2Vs1e9IZVQh8KpMxc0W3PT (ScoreCard component + Today/Health hero). Renamed "Ready Age" → "Biological Age" everywhere (George, 2026-07-10).


Biological Age — a multi-source composite with a completeness signal

Key design decision (George): Biological Age must NOT be blood-only. Most clients won't have all lab markers, or they'll be stale. It's a weighted composite of blood + wearable data, and the card shows a data-completeness % so the number is honest about how much it's standing on. Wearable-only users still get an estimate (lower completeness); adding labs raises it.

Component A — Blood: Levine PhenoAge (2018)

Yale (Morgan Levine et al.). 9 clinical-chemistry biomarkers + chronological age → a phenotypic age validated against 10-year all-cause mortality (Gompertz model). This is the gold-standard, publicly-documented biological-age formula.

Inputs (units): albumin (g/L), creatinine (µmol/L), glucose (mmol/L), CRP (ln-transformed, mg/dL), lymphocyte %, MCV (fL), RDW (%), alkaline phosphatase (U/L), WBC (1000 cells/µL), + chronological age (years).

Standard published coefficients — ⚠️ VERIFY against the primary source (Levine et al. 2018, Aging, PMID 29676998; Liu 2019 correction) before implementing. Every online calculator defers to the supplement; do not ship from memory.

xb = -19.907
- 0.0336 · albumin
+ 0.0095 · creatinine
+ 0.1953 · glucose
+ 0.0954 · ln(CRP)
- 0.0120 · lymphocyte%
+ 0.0268 · MCV
+ 0.3306 · RDW
+ 0.00188 · alkaline_phosphatase
+ 0.0554 · WBC
+ 0.0804 · age
M = 1 - exp( -exp(xb) · (exp(120·γ) - 1) / γ ), γ = 0.0076927
PhenoAge = 141.50225 + ln( -0.00553 · ln(1 - M) ) / 0.09165

Data source in-app: healthSummaries / lab panels (the same extracted biomarker values the protocol biomarker-progress section reads).

Component B — Wearable: cardiovascular / fitness age

Available continuously even without labs:

  • Cardiovascular age (Oura-style CVA): estimated from resting/sleeping heart rate, HRV, and estimated pulse-wave-velocity (PPG waveform shape). Oura computes this device-side — where a device exposes its own CVA, ingest it directly; otherwise estimate from RHR + HRV (+ VO2max).
  • VO2max / cardio capacity: age-adjusted; from the device estimate or a walking test (distance + HR + anthropometrics). Population-estimate when no test.
  • Resting HR trend vs personal baseline.

Data source: Terra + HealthKit wearable streams (Whoop / Oura / Apple / Garmin).

Completeness & recency weighting (the "% complete" on the card)

Each input carries an availability flag + a recency decay:

  • Labs: full weight if < ~3 months old; decay toward 0 by ~12 months (biomarkers drift).
  • Wearable: full weight if recorded in the last ~14–30 days; else stale/excluded.
  • VO2max: full weight from a recent walking test; reduced weight if population-estimate only.

Completeness % = weighted fraction of required inputs that are present AND recent → surfaced verbatim on the ScoreCard ("78% data complete").

Composite Biological Age = weighted blend of PhenoAge (blood) and cardio-age (wearable); weights ∝ availability × recency × reliability of each component:

  • Full labs + recent wearable → high completeness, tightest estimate.
  • Wearable-only → cardio/fitness-age estimate, lower completeness, prompt "Add labs ›".
  • Labs-only (no device) → PhenoAge, prompt "Connect a device ›". Always show Δ vs the user's personal baseline + trend.

UI surface (ScoreCard — built)

  • Composite number + "years" + Δ-vs-baseline chip.
  • Completeness meter ("78% data complete") + affordance ("Add labs ›" / "Connect device ›").
  • Recency caption ("Labs Jun 12 · wearables live · VO₂max est.").
  • Locked/empty state until minimum inputs exist — unlocks after first lab panel OR a connected wearable (not labs-only).

Backend to build

  • Score service: PhenoAge from healthSummaries + cardio-age from wearable signals.
  • Completeness/recency engine (per-input freshness → weights + overall %).
  • Composite blend + confidence; Δ vs personal baseline.
  • Ingest device-native CVA/VO2max where available (Oura); estimate otherwise.
  • Persist users/{uid} score doc: value, components{blood,cardio}, completeness, asOf, per-input provenance.
  • Reuse the personal-baseline service (see data-contracts doc) for the score and its inputs.

Other scores — surfaced, not recomputed

Sleep Score, Recovery / Readiness, Strain, etc. are computed by the wearable (Whoop Recovery, Oura Readiness/Sleep). We surface the device's score with attribution ("78 · via Whoop") — we do NOT recompute them. With multiple devices, pick the canonical source via the existing source-attribution logic to avoid conflicting numbers.


Open decisions (before backend build)

  • Recency decay curves (lab half-life; wearable staleness threshold).
  • Component weighting when both present (e.g. 60/40 blood/wearable?).
  • Minimum inputs to "unlock" the score.
  • Which device-native scores we trust as-is vs. normalize.

Sources: Levine PhenoAge (longevity-tools) · PhenoAge calculator (Teamz Lab) · Oura Cardiovascular Age · Oura Cardio Capacity (VO2 Max) · primary formula: Levine et al. 2018, Aging, PMID 29676998.