Sync external training data and age nutrition weeks into the history layer. Use when the task is to pull fresh Hevy workouts, Strava activities, or archive the current nutrition week as an aged proxy record.
The history skill manages the history/ layer — observed reality.
history/ holds facts. Not plans, not wiki knowledge.
The distinction is ontological: history/ is what happened, programs/ is what is intended.
The gap between the two is where coaching happens.
history/strength/ — Hevy workout data, imported or synced
hevy-workouts.json — raw dump (forensic source of truth)hevy-workouts.csv — canonical agent-readable layer, one row per sethistory/running/ — Strava activity data, imported or synced
activities.json — raw dump (forensic source of truth)activities.csv — canonical agent-readable layer, one row per activity, with laps_text and kindlaps.csv — detailed lap table, one row per lap, with lap_kindlaps/<activity_id>.json — per-activity lap cache (idempotent, fetched once)history/nutrition/ — nutrition weeks aged from programs/nutrition/ as proxy recordshistory/body/ — append-only body metrics log
weight.yaml — body weight entries (date, weight_kg, source, optional note)composition.yaml — body composition snapshots (date, body_fat_pct, lean_mass_kg, method, optional note)Nothing else. No plans, no wiki pages, no preferences.
The CSV files are the source of truth the agent reads for coaching. JSON is the forensic source. YAML in programs/ is a derived view.
| Domain | CSV | Key columns |
|---|---|---|
| Strength | history/strength/hevy-workouts.csv | date, exercise_normalized, set_index, weight_kg, reps |
| Running | history/running/activities.csv | date, kind, distance_km, pace_min_per_km, average_hr_bpm, laps_text |
| Running details | history/running/laps.csv | activity_id, lap_index, lap_kind, distance_m, moving_time_s, pace_min_per_km |
These CSVs are sorted in ascending chronological order:
date, start_time, title, exercise_title, set_indexdatedate, activity_id, lap_index/history status is the canonical read path over this CSV layer.
Use the CSVs directly before opening YAML or JSON.
Regenerate YAML from CSV after a manual CSV edit:
uv run health-coach-import-hevy --input history/strength/hevy-workouts.json
uv run health-coach-import-strava --input history/running/activities.json
Or directly:
from health_coach.history.strength.ingest import import_hevy_file
from health_coach.history.running.ingest import import_strava_file
/history sync hevy/history sync strava/history review nutrition/history age nutrition/history sync all/history capture weight — append a new entry to history/body/weight.yaml (user-reported weight, optional composition)./history statusAll heavy lifting here requires code — OAuth, external APIs, binary format parsing.
| Tool | Purpose |
|---|---|
history.sync_hevy() | Fetch all Hevy workouts via Firefox OAuth, write to history/strength/hevy-workouts.json, import into programs/strength/program.yaml |
history.import_hevy(path) | Manual file fallback — same output, no OAuth |
history.sync_strava() | Fetch Strava run activities, write to history/running/activities.json, import into programs/running/program.yaml |
history.import_strava(path) | Manual file fallback |
history.capture_weight(...) | Append a new body-weight fact to history/body/weight.yaml and optionally history/body/composition.yaml |
history.age_nutrition_week() | Read programs/nutrition/week-draft.yaml, tag as aged proxy, write to history/nutrition/<week_start_date>.yaml |
CLI equivalents:
health-coach-sync-hevyhealth-coach-import-hevy --input <path>health-coach-sync-stravahealth-coach-import-strava --input <path>health-coach-capture-weight --weight-kg <kg> [--date YYYY-MM-DD]health-coach-age-nutrition/history sync hevyFetches all Hevy workouts from the logged-in Firefox session.
Steps:
cookies.sqliteauth2.0-token cookie for the Hevy APIhistory/strength/hevy-workouts.jsonhistory/strength/hevy-workouts.csvprograms/strength/program.yaml/history status, which inspects the fresh CSV rowsNotes:
health-coach-import-hevy --input <path> when syncing is not available/history sync stravaFetches Strava run activities via OAuth.
Steps:
.env or data/running/strava_tokens.json--incremental)history/running/laps/<id>.json already exists)history/running/activities.jsonhistory/running/activities.csv (with kind, laps_text)history/running/laps.csv with one row per lapprograms/running/program.yaml/history status, which inspects the fresh CSV rowsNotes:
.envhealth-coach-sync-strava --setup to perform OAuth setuphealth-coach-import-strava --input <path> for manual imports/history age nutritionAges the current nutrition week draft into the history layer.
Steps:
programs/nutrition/week-draft.yamlweek_start_dateprovenance: aged_proxy and aged_at timestamphistory/nutrition/<week_start_date>.yamlWhy:
Nutrition has no reliable consumption log.
The week plan (programs/nutrition/) represents intended meals, not confirmed consumption.
Aging it into history/nutrition/ preserves it as a proxy record that the planning skill can use for replanning.
The provenance tag ensures downstream tools know it is intention, not measurement.
/history review nutritionCanonical week-closing gesture for nutrition.
Purpose:
Steps:
programs/nutrition/week-draft.yamlmemory/YYYY-MM-DD.mdhistory/nutrition/<week_start_date>.yamlRules:
Notes:
/history age nutrition remains available as the lower-level aging gesture/history capture weightAppends a new body metric entry reported by the user (no external sync path exists yet — weight is manual).
Steps:
health-coach-capture-weight for the append-only writehistory/body/weight.yaml with date, weight_kg, source: user_input, optional notehistory/body/composition.yaml with method (e.g. impedance_scale, dexa)Why:
Body weight is a primary input for strength (volume targets), running (load), and nutrition (TDEE calibration). Without a dedicated log it can only be inferred from scattered memory notes. The append-only file is the canonical factual timeline.
Notes:
source is user_input today; reserve scale_sync for a future integration and inferred only when explicitly derived/history sync allRuns sync hevy + sync strava in sequence.
Nutrition aging is a separate gesture because it requires a deliberate decision to close the week.
/history statusSummarize the current state of the history layer:
Purpose:
history/2-3 weeks) over a tiny snapshot, so irregular days do not dominate the readSteps:
history/strength/hevy-workouts.csvhistory/running/activities.csvhistory/running/laps.csvhistory/nutrition/ aged weeks if relevantDefault horizon:
14 days when the question is tactical21 days when the goal is to infer pattern, rhythm, or adherencecutoff=$(date -v-14d +%F)
awk -F, -v cutoff="$cutoff" 'NR==1 || $1 >= cutoff' history/strength/hevy-workouts.csv
awk -F, -v cutoff="$cutoff" 'NR==1 || $2 >= cutoff' history/running/activities.csv
awk -F, -v cutoff="$cutoff" 'NR==1 || $2 >= cutoff' history/running/laps.csv
21 days or add a targeted filter:cutoff=$(date -v-21d +%F)
awk -F, -v cutoff="$cutoff" 'NR==1 || $1 >= cutoff' history/strength/hevy-workouts.csv
awk -F, -v cutoff="$cutoff" 'NR==1 || $2 >= cutoff' history/running/activities.csv
awk -F, -v cutoff="$cutoff" 'NR==1 || $2 >= cutoff' history/running/laps.csv
rg or a focused awk:rg "bench_press_barbell" history/strength/hevy-workouts.csv
rg "interval_session" history/running/activities.csv
awk -F, '$2 == "2026-04-01"' history/running/laps.csv
history/strength/hevy-workouts.json)history/running/activities.json)2-3 weeks2-3 weekslaps.csvhistory/nutrition/This skill writes only to:
history/strength/history/running/history/nutrition/history/body/programs/strength/program.yaml (as a side effect of import — this is the structured program derived from history)programs/running/program.yaml (same)This skill never writes to:
wiki/memory/programs/nutrition/ — only the planning skill modifies nutrition planshistory/.