health

star 35

Personal health knowledge graph — local-first ingest of Garmin (Apple Health, Whoop, Oura, CGM in v2+) traces into SQLite, projected to Obsidian daily-note frontmatter, synthesized into validated longevity-proxy metrics (HRV-CV, CTL/ATL/TSB, VO2max arc). Hex architecture so new sources drop in as adapters. NOT a coaching surface in v1.

MarceloClaro By MarceloClaro schedule Updated 6/7/2026

name: health version: 0.2.3 primitive_candidate: P22 # not promoted; candidate per bstack-engine rule-of-three description: Personal health knowledge graph — local-first ingest of Garmin (Apple Health, Whoop, Oura, CGM in v2+) traces into SQLite, projected to Obsidian daily-note frontmatter, synthesized into validated longevity-proxy metrics (HRV-CV, CTL/ATL/TSB, VO2max arc). Hex architecture so new sources drop in as adapters. NOT a coaching surface in v1. author: broomva license: MIT tags: [health, garmin, apple-health, whoop, oura, hrv, training-load, vo2max, longevity, local-first, knowledge-graph] compounding: - bookkeeping # P6 — high-signal health insights promote into research/entities/concept/health-*.md via Nous gate ≥ 5/9 - persist # P12 — persist iterate health-sync.md for daily cron loop - telos # GOALS.md / MISSION.md / MODELS.md cross-links (Attia/Galpin longevity frames) trigger_keywords: - health, my health, sync garmin, garmin sync, garmin connect - training readiness, hrv trend, hrv-cv, recovery, sleep last night - vo2max, body battery, training load, ctl, atl, tsb, race readiness - my fitness, workout history, longevity, peter attia metric, andy galpin metric, stacy sims - daily health note, health dashboard, healthos

Health — Personal Health Knowledge Graph

The Health skill turns your wearables into a queryable, local-first knowledge graph with validated longevity-proxy synthesis on top. It is a substrate, not a coach. The agent reads from it; the user reads the projections; humans interpret. v1 ships the Garmin adapter and the hex architecture that lets Apple Health, Whoop, Oura, and CGM drop in as single-file adapters with zero application-core changes.

This is the substrate-shaped answer to the questions "how did I sleep last night?", "am I overreached?", "what's my VO2max arc this year?" — answered against a trace store you own, not a vendor cloud.


When to invoke

Invoke this skill on any trigger in the frontmatter trigger_keywords list. The router is:

User intent Workflow Command
"Where do I stand?" / health snapshot Status health status
"Pull my latest" / fresh data Sync health sync --source garmin
"Backfill history from " Backfill health backfill --source garmin --from <date>
"Today's daily note" DailyNote health daily-note
"Am I overreached?" / training-load read TrainingLoad health context --metric ctl,atl,tsb --days 90
"Recovery review" / 7-day rollup RecoveryReview health context --window 7d --metric hrv_cv,rhr,sleep
"VO2max arc" / longevity check VO2maxArc health context --metric vo2_max --bucket quarter
"Coach me / what should I do" Coaching NOT IMPLEMENTED in v1

Default discipline: any health-domain conversation opens with health status (a P15 Snapshot reflex). The agent never answers a health question from training-data priors when the live trace store could answer it.


Architecture

Five-layer hex, dependency arrow points inward:

cli/         ─→  application/  ─→  ports/  ←─  adapters/
                                                 ├─ sources/      (Garmin v1; Apple, Whoop, Oura, CGM ≥ v2)
                                                 ├─ repositories/ (SQLite default; SQLCipher v1.1)
                                                 ├─ projections/  (Obsidian daily-note; healthOS feed)
                                                 ├─ token_stores/ (filesystem default; Keychain optional)
                                                 ├─ rate_limiters/ (token-bucket)
                                                 └─ mfa/          (prompt / env / Keychain)
domain/      pure Pydantic v2 — sample shapes, metric registry, results, errors
synthesis/   derived views — HRV-CV, CTL/ATL/TSB, VO2max arc (stdlib-only)
migrations/  numbered SQL files + idempotent runner
config/      paths + settings (TOML + env, env wins)
  • domain has zero I/O. It defines QuantitySample, CategorySample, CorrelationSample, Workout, Device, the MetricCode registry with canonical units, the Source enum, all Result types, and the HealthError hierarchy.
  • ports are Protocol-typed seams: TraceSource, TraceRepository, ProjectionTarget, RateLimiter, TokenStore, MFAProvider, Clock.
  • application holds the four v1 use cases: SyncSourceUseCase, BackfillSourceUseCase, HealthStatusUseCase, RenderDailyNoteUseCase. Each is a frozen dataclass with constructor-injected ports.
  • adapters are the only place concrete dependencies live.
  • cli wires concrete adapters into use cases at startup. The CLI is the only impure entry point.

This separation is what lets a new source (e.g. Whoop) drop in as a single file in adapters/sources/ without touching anything else. The application layer never imports an adapter.

Deep-dive: References/architecture.md.


Workflows

Name Command When Output
Status health status P15 Snapshot reflex; entry to any health convo list[SourceStatus] (token validity, last sync, rate budget)
Sync health sync --source garmin Foreground / cron; pulls since last sample ts SyncResult (samples_ingested, workouts_ingested, duration_s)
Backfill health backfill --source garmin --from YYYY-MM-DD Cold start, after gap BackfillResult
DailyNote health daily-note [--date YYYY-MM-DD] After successful sync Path to ~/broomva-vault/07-Health/YYYY-MM-DD.md
TrainingLoad health context --metric ctl,atl,tsb --days 90 When asking about freshness/fatigue CTL, ATL, TSB time series
RecoveryReview health context --window 7d --metric hrv_cv,rhr,sleep Weekly review; after illness 7-day rollup
VO2maxArc health context --metric vo2_max --bucket quarter Quarterly check; longevity tracking {quarter_key: mean_vo2max}
Coaching (not implemented in v1)

Each workflow doc starts with a one-line When invoked rule and ends with example output. See Workflows/*.md.


CLI surface

# Authentication
health auth login [--source garmin] [--profile NAME]   # one-time MFA login
health auth status [--source garmin]                    # token validity per source
health auth logout [--source garmin]                    # delete token bundle

# Sync & backfill
health sync [--source garmin] [--since ISO_DATETIME]   # incremental pull
health backfill --source garmin --from YYYY-MM-DD [--to YYYY-MM-DD]

# Reflexive
health status                                           # snapshot across all sources
health doctor                                           # verify install + paths + perms

# Projection
health daily-note [--date YYYY-MM-DD]                  # emit Obsidian daily-note frontmatter
health context [--metric M1,M2,...] [--days N|--window 7d] [--bucket month|quarter|year]

# Output formatters (applies to every subcommand)
health <cmd> --format {json,jsonl,csv,tsv,human}       # default: json

Exit codes: 0 success · 1 error · 2 auth-required (re-run health auth login) — matches eddmann/garmin-connect-cli convention so wrapping scripts can branch on $?.

Output formatters: every CLI subcommand routes user-visible output through cli/formatters.py::format_value. Direct print(json.dumps(...)) calls inside subcommands are an explicit anti-pattern.


Composition with bstack primitives

The skill is designed to compound with the existing bstack primitives, not replace any of them.

Primitive How Health composes
Bookkeeping (P6) High-signal health observations (e.g. "CTL crossed 60 for the first time"; "HRV-CV jumped > 0.15 — illness onset?") get promoted into research/entities/concept/health-*.md via the Nous gate (≥ 5/9). The Health skill does NOT compete with the entity graph — it feeds it.
Persist (P12) Daily sync loop runs as persist iterate skills/Health/PROMPT.md. Each iteration spawns a fresh agent context; state lives in the trace DB + a tiny state.jsonl. Backpressure is from the sync SyncResult + the rate-limiter, not model self-grading.
Snapshot (P15) health status is the P15 reflex for the health domain. Every health-domain agent response opens with it.
Audience (P18) Daily-note projections are agent-and-human-readable (markdown rendered by Obsidian) — Category B per the format-discernment rule. Plans/specs about the skill itself are HTML under docs/plans/, docs/specs/.
Cross-Review (P20) Validation-evidence calls are MED/LOW confidence by default and require a second-model pass before being baked into agent prose. Vendor "recovery scores" never escape the trace layer un-flagged.
Telos Cross-links to USER/TELOS/GOALS.md (longevity/fitness goals), MISSION.md (the underlying why), MODELS.md (Attia/Galpin/Sims mental models). The Health skill can emit a health context --format json blob suitable for direct Telos GOALS-progress queries.

The skill is not a primitive — it is a substrate that several primitives compose against.


Privacy invariants

The Health skill is the most privacy-sensitive workspace component. The invariants are hard:

  1. SQLite by default; SQLCipher upgrade path documented — v1 ships unencrypted SQLite at ~/broomva/health/traces/<source>.db. v1.1 will add the [encrypted] extra (pysqlcipher3) and a one-shot migration to SQLCipher with the encryption key stored in macOS Keychain. See References/privacy-architecture.md.
  2. Tokens at 0o600; directory at 0o700~/.config/broomva-health/tokens/ directory is mode 0o700 (only owner can read), token files are mode 0o600. The filesystem token store enforces this at write time.
  3. Never log PII — no log statement may contain email, password, MFA code, raw token bytes, sample values, or device serials. The agent never echoes these to the conversation log either. PII redaction is enforced at the formatter layer.
  4. Local-first; no telemetry; no third-party calls beyond the source's own API — the Health skill makes network calls only to the source vendors (Garmin Connect, in v1). No analytics, no crash reporters, no "phone home".
  5. Reconciliation is a projection, not a column — samples carry source + device; which-source-wins for a given metric/window is computed at projection time, not stored as a flag in the trace. This preserves provenance permanently.
  6. Apple HealthKit's local-only model is the reference — Apple keeps every HKSample on-device unless the user explicitly shares with an app. We aspire to the same posture for everything we ingest.

Failure to uphold these invariants is a P20 blocker — Cross-Review must reject any PR that touches adapters/repositories/ or adapters/token_stores/ without re-verifying them.


What this skill explicitly is NOT

  • Not a coaching surface in v1. It does not tell you to take a rest day, hit zone 2, or sleep more. Coaching is the v2 vision (see Workflows/Coaching.md) and requires Telos + calendar + context integration that is intentionally out-of-scope here.
  • Not Apple Health in v1. The Apple Health adapter is designed-for (the APPLE_HEALTH Source enum member exists; the port shape is HealthKit-compatible) but unimplemented. Adding it is a P5 fan-out task per References/extension-guide.md.
  • Not a Garmin Health API client. Garmin Health is enterprise-only and not viable for a single-user knowledge graph. We use python-garminconnect (widget+cffi bypass; active maintenance) — see References/garmin-api-landscape-2026.md.
  • Not a replacement for apps/healthOS/. healthOS is the platform; this skill produces the substrate that healthOS can read from. The [ProjectionTarget] port exists precisely so healthOS can subscribe without coupling.
  • Not a Strava / Wahoo / Polar middle-tier. Strava has no sleep/HRV/body-battery; Wahoo has no API; Polar's API is gated. These are not viable substrates for a health KG.
  • Not a vendor "recovery score" passthrough. Body Battery, Training Readiness, Whoop Recovery are stored as opaque metric codes (BODY_BATTERY, TRAINING_READINESS) for completeness but synthesis recomputes its own composites. Reasoning: Altini and others have documented that vendor recovery scores are unvalidated "made up scores" — see References/validation-evidence.md.

Self-evolution hooks

The Health skill is a P22 primitive candidatenot promoted. Per the L3 stability budget in CLAUDE.md, promotion requires the rule-of-three: three concrete failures the skill prevents, with citations, logged in research/entities/pattern/bstack-engine.md. Current count: 0. Track here:

  • Failure 1: unrecorded — the daily "did I sync Garmin?" ritual that produced no persistent answer.
  • Failure 2: unrecorded — repeated re-asking of "what's my VO2max trend?" because no skill owned the question.
  • Failure 3: unrecorded — agent answering health questions from training-data priors when the live trace store could answer.

When each failure is logged with citation (session ID + date + concrete artifact), the skill graduates from primitive_candidate to a numbered primitive. Until then it remains a skill — useful, composable, but not in the table at CLAUDE.md §Bstack Core Automation Primitives.

The L3 cadence is deliberate: skills compose; primitives stabilize. Don't promote until the substrate has earned its row.


Related skills / references

Workspace skills:

  • /bookkeeping (P6) — promote high-signal health observations into the entity graph
  • /persist (P12) — persist iterate health-sync.md for daily sync loop
  • /cross-review (P20) — mandatory gate before merging health substrate changes
  • /telos — health metrics feed Telos GOALS/MISSION/MODELS

Per-workflow docs:

References:

External:

  • Linear BRO-1235
  • Design plan (HTML, human-readable): docs/plans/2026-05-22-health-skill.html
  • Trace schema spec (HTML, human-readable): docs/specs/2026-05-22-health-trace-schema.html
Install via CLI
npx skills add https://github.com/MarceloClaro/OpenCode_Ecosystem --skill health
Repository Details
star Stars 35
call_split Forks 17
navigation Branch main
article Path SKILL.md
More from Creator
MarceloClaro
MarceloClaro Explore all skills →