Skip to content

Health Center

The Health Center is vita's unified health surface: a read-only web app at /health that composes Garmin telemetry, lab/DEXA extracts, a clinical sub-agent's working memory, and an open-loops board into one "command center." Eleven tabs render a single read-model contract; every concrete number, band, and status is computed below the API boundary so the React client never parses a raw data file.

Scope note. This page documents the engine β€” the data model, normalization, banding math, and where state lives β€” not anyone's actual health contents. Example markers, ranges, and values below are illustrative placeholders. For the predictive sleep model see Sleep Guardian; for the daily input path see Check-in; for model routing see Model Guidance.

What it is (and isn't)

The Health Center is the headline health overhaul shipped mid-2026. A few orienting facts a reimplementer needs up front:

  • It is read-only. The web UI never mutates health data. Writes happen elsewhere (the clinical sub-agent, the loops CLI, Garmin sync, the Check-in flow, Goals).
  • There is no /health slash command. The "command center" is the web home β€” the index tab is literally labeled Command. (Slash commands like /checkin, /goal, /insights, /sleep-risk, /sleep-guardian-tune are separate touchpoints, documented on their own pages.)
  • Each tab is backed by a thin FastAPI router that delegates to a service; all services share one HealthRepository normalization layer. The client uses react-query caching.
web (/health, 11 tabs)            api (port 33800)              on-disk state
─────────────────────────         ────────────────────         ──────────────────────────
HealthLayout (tabs)               routers/health_*.py          data/garmin/YYYY-MM-DD.json
  ↳ react-query hooks   ── GET ─▢   ↳ services/health_*.py      memory/extracted/medical/*
  (useHealth.ts)                      ↳ repositories/             memory/extracted/performance/*
  typed read-model                      health_center.py          data/health/{metric_registry,loops,events}.json
  (features/health/types.ts)            (HealthRepository)        data/doctor/* Β· data/sleep/* Β· data/checkins/*
                                                                  profile/medications.json Β· goals/active/*

The 11 tabs

Each browser route under /health mounts one React view and calls one (or two) endpoints. Routes are TanStack-Router file routes under web/src/routes/health/.

Tab Route View component Endpoint(s)
Command /health HealthDashboard GET /api/v1/health/overview
Explore /health/explore MetricExplorer GET /api/v1/health/metrics, /metrics/{key}/series
Rhythm /health/rhythm RhythmView GET /api/v1/health/rhythm/{metric}
Sleep /health/sleep SleepView GET /api/v1/health/sleep
Body /health/body BodyCompView GET /api/v1/health/body-comp
Regimen /health/regimen RegimenView GET /api/v1/health/regimen
Doctor /health/doctor DoctorReadView GET /api/v1/health/doctor-read
Check-ins /health/checkins CheckinsView GET /api/v1/health/checkins
Visits /health/visits VisitsView GET /api/v1/health/visits
Genetics /health/genetics GeneticsView GET /api/v1/health/genetics
Prep /health/prep AppointmentPrep GET /api/v1/health/prep

HealthLayout.tsx owns the tab bar and a dark/light theme toggle (persisted to localStorage); every tab renders inside its <Outlet/>.

API surface

All endpoints live under the /api/v1/health prefix and are registered in api/src/main.py. They are spread across eight routers (the bare /api/v1/health healthcheck lives in a ninth, routers/health.py, and does not collide because the center uses only subpaths).

Method Β· Path Router Query params Returns
GET /overview health_center days (7–365, default 30) HealthOverview
GET /metrics health_center days MetricTile[] (every registry metric with data)
GET /metrics/{key}/series health_center days (default 90) MetricSeriesResponse
GET /rhythm/{metric} health_center days (3–60, default 14) RhythmResponse
GET /prep health_center β€” PrepPacket
GET /source-pdf health_center path (required) FileResponse (PDF)
GET /regimen health_regimen β€” RegimenResponse
GET /doctor-read health_doctor β€” DoctorReadResponse
GET /body-comp health_bodycomp β€” BodyCompResponse
GET /checkins health_checkins days (14–365, default 60) CheckinsResponse
GET /visits health_visits β€” VisitsResponse
GET /genetics health_genetics β€” GeneticsResponse
GET /sleep health_sleep days (14–365, default 90) SleepResponse

Dependency injection wires two @lru_cache singletons (api/src/dependencies_data.py): get_health_center_repository() constructs a HealthRepository(settings.data_dir), and get_health_center_service() wraps it in a HealthCenterService. All endpoints are synchronous def (single-user personal app β€” sync I/O is fine; the client caches).

The normalization layer (HealthRepository)

api/src/repositories/health_center.py is the heart of the system. It reads heterogeneous on-disk sources and projects them into clean dicts the services compose. A reimplementer should treat this file as the contract for "how messy real health data gets coerced into something chartable."

Source directories

Logical source On-disk location
Garmin daily data/garmin/YYYY-MM-DD.json
Lab extracts memory/extracted/medical/labs_*.json (~22 files)
Performance extracts (DEXA / VO2max) memory/extracted/performance/performance_*.json (7 files)
Original lab/DEXA PDFs memory/sources/medical/, data/medical/labs/
Metric registry data/health/metric_registry.json
Open loops data/health/loops.json
Health-event annotations data/health/events.json
Sleep Guardian data/sleep/{risk-model,guardian_config}.json, risk-log.jsonl, outcomes.jsonl
Clinical sub-agent data/doctor/{working-memory.md, protocols.jsonl, interventions.jsonl, research-queue.jsonl, observations.jsonl}
Medications profile/medications.json
Check-ins data/checkins/YYYY-MM-DD.json
Calendar data/calendar/events.json (mtime-cached; ~11 MB)
Live task mirror Precedent at http://localhost:5424 (1.5 s timeout, 30 s cache)

Lab extract parsing β€” one recursive walk for many shapes

Lab/DEXA extracts arrive in at least five on-disk shapes across vendors and years:

A) categories β†’ { markers: { key: {value...} } }
B) flat top-level marker keys  { marker_x: {value...} }
C) panels: [ { panel_name, results: [ {marker, value} ] } ]
D) nested  { marker_x: { marker_x_assay: {value...} } }
E) history { marker, results|history: [ {date, value} ] }

A single recursive walk() handles all of them: descend the tree, carry the nearest marker-name seen, and emit any leaf with a value whose own-or-inherited name resolves (via the registry alias map) to a canonical metric. Key behaviors:

  • Key normalization (_norm_key): drops a trailing assay qualifier β€” "Marker Name (Direct)", "(LC/MS)", "(calc)" β€” and lowercases/underscores, so a labeled assay still resolves to its base marker alias.
  • Reference-range parsing (parse_ref_range): turns range strings into (low, high) β€” e.g. "52-328", "82–237", "25.8~60.7", "4 to 15", "<0.5" β†’ (None, 0.5), ">1500" β†’ (1500, None).
  • Censored values: a bounded assay result (">358.86", "<0.5") is an inequality, not a point. It is flagged censored=True, excluded from baselines/averages, and the UI marks it as a bound rather than plotting it as exact.
  • Per-marker PDF linking (_match_pdf): when one lab file holds several markers and several PDFs, link each marker to the PDF whose filename tokens best overlap the marker name. A single candidate is used directly; ties with no real overlap link to nothing β€” "a wrong source link is a lying instrument; no link is the honest fallback."
  • Dedup: one record per (marker, date), preferring the richest (has reference range, then PDF, then status). A global second pass dedups across files.

Source-PDF gatekeeper

GET /source-pdf?path=... serves an original lab/DEXA PDF as a FileResponse. It is path-traversal-safe and internal-only: resolve_medical_pdf() resolves the request and returns it only if the resolved path is a real .pdf file living inside one of the medical archive dirs. Crucially, the PDF producer (_pdf_candidates) enforces the same archive-membership rule, so it never advertises a path the gatekeeper would 404 β€” the two halves agree on the trust boundary.

Metric registry

data/health/metric_registry.json is the single source of truth for "what /health can chart, how to band it, and how to parse it from labs." It is read-only from the web UI.

{
  "_comment": "READ-ONLY from the web UI ...",
  "updated_at": "YYYY-MM-DD",
  "metrics": [
    {
      "key": "example_marker",
      "label": "Example Marker",
      "unit": "mg/dL",
      "domain": "metabolic",
      "source": "labs",
      "directionality": "lower_better",
      "decimals": 0,
      "optimal_min": null, "optimal_max": 99,
      "reference_min": 70, "reference_max": 140,
      "gate": null,
      "aliases": ["raw lab key 1", "raw lab key 2"]
    }
  ]
}
Field Meaning
key / label / unit / decimals Canonical id, display label, units, render precision
domain Grouping (cardio, endocrine, metabolic, sleep, recovery, training, body_comp, inflammation, liver, kidney, …)
source garmin | labs | performance β€” where the data comes from
directionality higher_better | lower_better | range β€” drives status logic
optimal_* Opinionated functional target band (a clinical goal, not just population reference)
reference_* Population/lab reference band (the softer fallback)
target / gate Optional goal line / hard threshold
aliases[] Raw lab keys that the normalizer maps onto this canonical marker

The registry currently defines on the order of two dozen metrics spanning Garmin recovery/training signals and lab/DEXA markers. Count them from the file rather than hard-coding a number.

Metric tiles: banding, baseline, z-score, status

The Command and Explore tabs render MetricTiles. The service (api/src/services/health_center.py) builds each tile from the metric's data points:

  • latest / previous / delta β€” last two points.
  • baseline β€” the mean of the windowed points (statistics.fmean).
  • z-score β€” (latest βˆ’ baseline) / pstdev, only when there are β‰₯ 4 points and stdev > 0 (else null). This is the falsifiable "how far from your own normal is today" signal.
  • status β€” good | watch | bad | neutral from value + bands + directionality (_status_for).
  • stale_days β€” days since the latest point; attention is True when status is bad.

_status_for logic by directionality (simplified):

Directionality good watch bad
lower_better ≀ optimal_max ≀ reference_max above reference
higher_better β‰₯ optimal_min β‰₯ reference_min (or within 90% of optimal) below
range inside optimal band inside reference band outside both

A gate below the value short-circuits to bad.

Lab tiles judge against the point's own reference range

This is the subtle correctness feature. For lab/performance markers, the tile's mini-trend and status stay within the latest source/assay (never plot a cross-assay jump as a trend), and status is computed by _lab_status:

  1. If the registry defines a functional optimal band (an intentional clinical target), use that.
  2. Otherwise judge against the latest point's own reference range parsed from the lab itself β€” never a stale single-assay registry band. A value can read green against the assay that produced it even if a registry band copied from a different assay would call it out of range. When status is judged this way, the tile caption shows that range so the colored dot and the "ref …" label agree.
  3. Fall back to the registry reference band only if the point has no two-sided parseable range.

_unit_consistent also drops points whose unit differs from the latest non-null unit, preventing a false trend/z when a marker's unit changed across labs (e.g. ng/dL β†’ pg/mL).

Metric series (drill-down)

GET /metrics/{key}/series returns the full history for one marker. For multi-assay lab markers it returns all sources as LabMarkerPoints β€” each carrying its parsed ref_low/ref_high, a normalized position within its own range ((value βˆ’ low) / (high βˆ’ low), so different-assay values become comparable as "% of range"), the censored flag, the source label, and the linked PDF path. multi_source + sources[] let the UI segment by assay. annotations[] overlays health-event markers from data/health/events.json that fall inside the displayed range.

The Command overview

GET /overview composes the whole command screen in one payload (HealthOverview):

Section How it's built
hero[] Headline HeroStates: sleep gate, sleep risk, open-loops count, next-up event, data-freshness.
loops[] Ranked open clinical loops (see below).
upcoming[] Health-relevant calendar events + protocol-scheduled panels, de-duped, next 8.
freshness[] Per-source live | stale | offline for Garmin, CGM, Labs, and the sub-agent's working memory.
metrics[] A curated pinned grid of tiles (clinical markers first, then recovery/training).
goals[] Active physical/fitness goals with progress (read from goals/active/*.json).

Upcoming events are filtered by a health regex over event titles (lab draws, scans, endocrine/cardiology visits, etc.) and classified lab | test | appointment. Freshness deliberately watches the sub-agent's own instruments too: a stale working-memory file is surfaced as stale with a "working memory Nd stale" detail rather than silently trusted β€” making a lying instrument legible.

Open clinical loops board

A Tier-0 "needs attention" board of open clinical action items (a draw to schedule, a scan to book, a result to chase, a follow-up). It is read-only on the dashboard; the write side is owned by scripts/health/loops.py (launcher bin/health-loops), and the clinical sub-agent maintains it through that CLI.

./bin/health-loops list [--all]                 # current board (or incl. done)
./bin/health-loops show <id>
./bin/health-loops add <id> --title "..." --importance high --urgency this_week \
    --next-action "..." [--due YYYY-MM-DD] [--consequence "..."] [--kids] \
    [--precedent-task <key>] [--source "..."]
./bin/health-loops update <id> [--status ...] [--due ...]
./bin/health-loops close <id> --reason "..."    # resolved β†’ drops off the board
./bin/health-loops snooze <id> --until YYYY-MM-DD --reason "..."
# equivalently: python -m health.loops <cmd>

Stored in data/health/loops.json. Per-loop schema and enums:

Field Values
importance critical \| high \| medium \| low
urgency now \| this_week \| this_month \| eventual
status open \| waiting \| scheduled \| monitoring \| snoozed \| done
owner me \| doctor \| lab \| pcp
plus id, title, category, opened, due, next_action, consequence, kids_relevant, sources[], precedent_task

Ranking (service-side): importance weight + urgency weight + +25 overdue + +15 if relevant to dependents + +10 if waiting/open. Precedent mirroring: a loop with a precedent_task key has its status/due mirrored live from Precedent (read-through overlay, 30 s cache) β€” if the linked task is completed, the loop drops off the board; if Precedent is unreachable, it falls back to the curated fields. Loops with status: done are filtered out entirely.

Long-horizon clinical commitments can be represented as monitoring loops and paired with a high-tier proactive reminder ahead of the due date. The standing six-month lab-panel cadence uses this pattern: the loop is the durable clinical expectation shown by Health Center and the doctor digest, while the deduplicated reminder is delivery plumbing. Health cadence reminders have an isolated admission tier so ordinary outreach volume cannot starve a rare, high-value health prompt.

Rhythm: the "typical day" envelope

GET /rhythm/{metric} builds an AGP-style typical-day envelope (median + percentile bands) over a window for an intraday Garmin metric β€” so you see the shape of a normal day rather than a single daily number.

  • Supported metrics: heart_rate, stress, body_battery (an unknown metric 404s).
  • Intraday samples are bucketed into 30-minute bins of local minute-of-day (UTC offset derived per day from sleep_start_local vs sleep_start_utc), with Garmin's negative no-data sentinels dropped.
  • Each RhythmBin carries median, p25/p75, p10/p90, and n (sample count).

Sleep page

GET /sleep unifies four sleep layers the rest of vita keeps in separate places, into one SleepResponse:

  1. Nightly architecture β€” per-night SleepNight: duration, score, deep/light/rem/awake (hours + % of asleep), bedtime/waketime, a trailing 7-night rolling mean, HRV/RHR/respiration, and a below_gate flag.
  2. Training sleep-gate β€” GateState: the 7-day average over the last 7 calendar days vs the 6.5 h board-resolution threshold, with margin, blocked, and days_with_data. The window matches scripts/scheduling/sleep_gate.py exactly so the page never claims "blocked" when the real gate isn't (e.g. during a sync gap). The gate suspends max-effort intervals only, not all training.
  3. Consistency/regularity β€” bed/wake midpoint stdev computed here, labeled very regular | regular | variable | irregular, plus median typical bed/wake clock times. A monotonic "minutes after 18:00" axis sorts evening bedtimes below morning wakes.
  4. Sleep Guardian β€” the predictive risk model and its calibration.

Nightly values are override-aware. A correction in data/sleep/overrides.jsonl replaces a bad Garmin duration without mutating the raw instrument file; a night marked invalid is excluded rather than counted as zero. If the recording itself was broken, stage/score/bedtime fields are suppressed for that night while independently measured HRV/RHR/respiration can remain. This is the same read contract used by the gate and risk scorer, so the web page cannot disagree with the training decision.

Guardian calibration (honest scoring)

Calibration scores the Sleep Guardian over its last ~30 resolved nights against its own recorded outcomes (prediction_correct):

  • accuracy_pct β€” the Guardian's hit-rate.
  • base_rate_pct β€” how often nights were actually poor.
  • trivial_pct β€” accuracy of the trivial baseline (always guess the common outcome).
  • precision_pct β€” of risk calls (yellow/red), the fraction that were truly poor.
  • has_edge β€” accuracy > trivial. A risk model is only useful if it beats the trivial baseline; the summary says so in plain language and tells you to treat a flag as a prompt, not a verdict, when there's no edge. The displayed nights are exactly the ones the accuracy is computed over.

The page reconciles the gate as a single source of truth: the Guardian's stored gate_margin (a different snapshot) is overwritten with the live gate's margin.

Body composition (DEXA / VO2max)

GET /body-comp parses the DEXA and VO2max performance extracts into a longitudinal read-model (api/src/services/health_bodycomp.py). Two correctness features dominate:

Defensive label matching. Vendor panel/marker names drift across report years (a ratio called "A/G Ratio" in one, "Android/Gynoid Ratio" in another; "Visceral Fat Content (VAT)" vs "Visceral Fat (VAT)"). Every scan is flattened to a {marker: value} map and matched by case-insensitive substring with exclusions, never exact label β€” so whole-body "Lean Tissue" isn't confused with "Trunk Lean Tissue."

LSC noise-gating β€” the headline correctness feature. DEXA has precision error, so a delta smaller than the Least Significant Change is measurement noise, not a trend. Every headline delta carries a real_change flag set False when |delta| ≀ LSC, and the LSC threshold itself is returned to the UI as a footnote. Per-metric thresholds (illustrative method constants, in each metric's own units):

Metric LSC
Total body-fat % 0.8 pct-pts
Lean mass (whole body) 1.5 lb
Regional lean 1.0 lb
Regional fat % 1.5 pct-pts
Visceral fat 0.05 lb
RSMI 0.30 kg/mΒ²
A/G ratio 0.05
Total BMD 0.020 g/cmΒ²

The payload also includes a regional composition table (arms/legs/trunk, noise-gated), limb symmetry (left/right lean gap banded by injury-risk thresholds: <5% good, 5–10% watch, >10% bad), bone BMD by site leading with the age-matched Z-score (T-score deliberately omitted), and a VAT trend series across scans.

Regimen & Research

GET /regimen composes the active-interventions view from three sources, normalized in api/src/services/health_regimen.py:

  • Medications ← profile/medications.json prescriptions (name, dose, frequency, status, prescriber, reason).
  • Supplements / topicals ← the sub-agent's protocols.jsonl, bucketed by keyword (topicals vs supplements; diagnostics/test-kits/subscriptions are excluded by name).
  • Research verdicts ← the sub-agent's interventions.jsonl β€” each an ADOPT | MONITOR | SKIP evaluation with an evidence grade, relevance, overall score, summary, and a linked report slug.

Two notable behaviors: a protocol that merely restates an existing prescription is folded into that medication's detail rather than duplicated (with near-duplicate notes suppressed so you don't get a redundant tail); and a research verdict whose compound name-matches an active medication is annotated with prescribed_match β€” a SKIP on a drug already prescribed is a net-new-adoption question, not advice to stop taking it. The updated date is the max ISO date across medications.json and every surfaced protocol change, so the surface doesn't read stale when a supplement changed without a meds-file edit.

Doctor's Read + the clinical sub-agent

The Health Center surfaces a domain-expert clinical sub-agent. Two layers:

The sub-agent (data/agents/doctor.json, prompt data/agents/prompts/doctor.md)

A personal longevity researcher β€” explicitly not a clinician. It does not diagnose or prescribe; it observes trends in Garmin/CGM/check-in/medical data, maintains working memory, tracks a protocol stack, queues research questions for a real doctor, and maintains the open-loops board. State lives in data/doctor/:

File Role
working-memory.md Current signals / open questions / data gaps (starts with a Last updated: line)
observations.jsonl Notable measurements (timestamp, metric, value, baseline, note)
protocols.jsonl Active interventions (name, started, reason, source, status)
interventions.jsonl Research verdicts (ADOPT/MONITOR/SKIP)
research-queue.jsonl Questions to bring to a real clinician (priority, rationale)
intake-log.jsonl Audit log of data consumed each run

Triggers (from doctor.json): a weekly digest (scheduled, Saturday evening PT) that reviews the week and reconciles the loops board, a per-garmin-sync daily review (event-triggered) for anomaly scanning, and an on-demand Telegram topic. A staleness.max_age_hours of 168 (7 days) marks the agent's state stale past a week.

The Doctor's Read surface (GET /doctor-read)

api/src/services/health_doctor.py parses the agent's working-memory.md markdown into three lists β€” current signals, open questions, data gaps β€” by matching ##/### headers case-insensitively (header wording is allowed to drift). It pulls the newest ~10 observations.jsonl entries into a newest-first timeline, and computes a stale_days freshness from the file's Last updated: line (falling back to mtime). The working memory is deliberately allowed to drift stale; stale_days is passed straight through so the UI shows a visible badge β€” an instrument made honest rather than hidden.

Check-ins (subjective vs measured)

GET /checkins reads the daily check-in files and aligns each day's self-report (energy, sleep quality, mood, notes) with same-day Garmin recovery (overnight HRV, measured sleep) so felt-vs-measured divergence is chartable. It handles two on-disk shapes (a modern snapshot{...} form and a legacy responses[] Q&A form), maps qualitative ratings (high/good/fair) to a coarse 0–3 scale, and is honest about provenance:

  • Auto-generated entries (synthesized from telemetry, e.g. source: conversation_exhaust or non-human depth) are flagged inferred=True. They still chart (they're the only signal available) but are labeled "inferred (auto)."
  • confident_days counts only genuine self-reports (a real deep/quick/standard depth and a non-auto source), so machine-synthesized entries can't inflate self-report coverage β€” a guard against the chart's subjective line silently tracking the very telemetry it's plotted against.

The Garmin overlay spans the actual calendar range of the returned check-in points (not a fixed today βˆ’ days window), because the repository returns the last N check-in files, which can be sparse and reach further back.

The read-only Check-ins view is distinct from the interactive Check-in command/skill that writes data/checkins/YYYY-MM-DD.json.

Visits

GET /visits composes a chronological (newest-first) provider visit history from heterogeneous on-disk visit records. The source fields are irregular β€” some strings, some lists, some nested dicts β€” so the service coerces everything into one typed Visit (date, provider, type, summary, recommendations, action items, nutrition guidance, follow-up). Unparseable/missing dates sink to the bottom rather than floating up via lexical comparison.

Genetics (anticipatory)

GET /genetics renders an anticipatory "what's coming" state for an ordered whole-genome panel until results land. The contract is the same shape in both states so the frontend never special-cases a missing payload:

  • available tracks parsed results, not raw file presence β€” a stray/partial JSON in a genetics dir can't flip the header to "results in" while the body shows none.
  • pending[] describes the ordered panel (vendor, status pulled from the protocol stack, expected results window, and the report categories it will return).
  • Result parsing is wired-on-arrival: _results_present() scans the genetics/genomics/dna extract directories; when a results file appears, parsing populates results[] and available flips True naturally.

Appointment prep

GET /prep builds a doctor-facing prep packet for the next clinical appointment so a real visit starts from current data. The service picks the next upcoming appointment, derives a focus from its title (endocrine vs body-composition vs general β†’ a different relevant marker set), and assembles:

  • markers[] β€” the latest value + reference range + delta for each focus marker (status via _lab_status).
  • protocols[] β€” currently active interventions.
  • open_questions[] β€” the top research-queue items by priority.
  • observations[] β€” recent sub-agent observations.
  • a one-line summary (focus, appointment, marker count, count outside optimal, open-question count).

File locations

Path Role
web/src/features/health/ All 11 view components + HealthLayout, types.ts
web/src/routes/health/*.tsx TanStack file routes (one per tab)
web/src/hooks/useHealth.ts react-query hooks (overview/metrics/series/rhythm/prep)
api/src/routers/health_*.py Eight thin routers (+ health.py healthcheck)
api/src/services/health_*.py Read-model composition
api/src/repositories/health_center.py The HealthRepository normalization layer
api/src/schemas/health_*.py Typed read-model contracts
api/src/dependencies_data.py @lru_cache repo/service factories
data/health/metric_registry.json Chartable-metric source of truth
data/health/loops.json Open clinical loops board (write via bin/health-loops)
scripts/health/loops.py Β· bin/health-loops Loops write-side CLI
data/agents/doctor.json Β· data/agents/prompts/doctor.md Clinical sub-agent config + prompt
data/doctor/ Sub-agent state (working memory, observations, protocols, interventions, research queue)
memory/extracted/{medical,performance}/ Lab + DEXA/VO2max JSON extracts
memory/sources/medical/, data/medical/labs/ Original lab/DEXA PDFs

Where to go next

  • Sleep Guardian β€” the predictive risk model and learning loop the Sleep tab surfaces.
  • Check-in β€” the interactive flow that writes the data the Check-ins tab reads.
  • Goals β€” the active-goals store the overview pulls progress from.
  • Garmin β€” the daily telemetry that feeds metrics, rhythm, sleep, and recovery overlays.
  • Model Guidance β€” model routing for sub-agents (the clinical agent runs on the configured frontier model).