Tri-Training Progress Dashboard — Plan (v3)
Decisions (resolved)
- Calorie expenditure model: Both. Whoop kJ as primary (actual burn), TSS-based as secondary comparison line.
- Date range: Default to last 7 days with “show all” toggle.
- Race-day plans: Separate page — daily tracking dashboard only on main view.
- Hosting: Static files in the workspace
files/folder (already served as a static file server). - Language: Python.
- Data architecture: Structured CSV datasets maintained daily — NOT parsing markdown files on every dashboard build. One-time migration script backfills historical data from existing markdown. Going forward, agents append one row per day to each CSV after writing their markdown logs.
- Static site tooling: Plain HTML + Chart.js. Single file, no build step.
Data Architecture (CSV-First)
Three CSV files, one per data domain. Each is the single source of truth for the dashboard. Located in files/analytics/data/.
1. nutrition.csv
| Column | Type | Source |
|---|---|---|
| date | YYYY-MM-DD | filename |
| calories | int | Daily Totals |
| calories_target | int | Daily Totals |
| protein_g | float | Daily Totals |
| protein_target_g | float | Daily Totals |
| carbs_g | float | Daily Totals |
| carbs_target_g | float | Daily Totals |
| fat_g | float | Daily Totals |
| fat_target_g | float | Daily Totals |
| meals | int | count of meal sections |
2. training.csv
One row per session (multiple rows per day possible).
| Column | Type | Source |
|---|---|---|
| date | YYYY-MM-DD | table row |
| discipline | swim/bike/run/other | emoji prefix |
| session_name | str | Session column |
| duration_min | int | Actual column (parsed) |
| tss_planned | int | TSS column |
| tss_actual | int | Actual TSS column |
| rpe | int | RPE column |
| status | completed/skipped/modified | ✅/❌/text |
| week | int | Week header |
3. whoop.csv
| Column | Type | Source |
|---|---|---|
| date | YYYY-MM-DD | filename |
| recovery_score | float | Recovery section |
| hrv_ms | float | Recovery section |
| rhr_bpm | int | Recovery section |
| spo2_pct | float | Recovery section |
| sleep_performance_pct | float | Sleep section |
| sleep_efficiency_pct | float | Sleep section |
| sleep_time_min | int | Sleep section (converted) |
| sws_min | int | Sleep section (converted) |
| rem_min | int | Sleep section (converted) |
| strain | float | Strain section |
| kj | int | Strain section |
| readiness | GREEN/YELLOW/RED | Readiness line |
Daily update flow
Agent writes markdown log
→ Agent appends one row to the relevant CSV
→ Dashboard reads CSVs (no parsing needed)
The nutrition agent already runs daily. After writing files/nutrition/daily/YYYY-MM-DD.md, it should also append a row to nutrition.csv. Same pattern for Whoop reports. Training data is updated less frequently (weekly plan updates), so the training CSV gets updated when sessions are logged.
One-time migration
A Python script (scripts/migrate-to-csv.py) parses all existing markdown files and populates the three CSVs with historical data (Mar 10 onward). This runs once, then the CSVs are maintained incrementally.
What to Extract (Derived Metrics)
Computed at dashboard render time from the CSVs:
- Calorie deficit/surplus = calories - whoop kJ
- TSS-based expenditure estimate = sum(session TSS) * ~4.5 kcal (secondary line)
- Protein compliance = % of days hitting protein target
- Macro periodization = carb intake correlated with strain
- Weekly training load = TSS by discipline (swim/bike/run)
- Training completion rate = completed sessions / total sessions per week
- Recovery trend = HRV and recovery score over time
- Sleep quality trend = sleep performance over time
- Strain vs recovery = high-strain days followed by appropriate recovery?
- Fueling adequacy = calorie intake relative to Whoop kJ on training days
Dashboard Layout (Mobile-First, Single Screen)
Five panels stacked vertically, designed for phone screen (~375px wide). Default view: last 7 days with “show all” toggle.
Panel 1 — Recovery & Readiness (line chart + color band)
- HRV trend line (primary) with recovery score color band (green/yellow/red background)
- RHR as secondary line
- Readiness indicator per day
Panel 2 — Calorie Balance (bar chart)
- Daily vertical bars: intake vs Whoop kJ burn
- Deficit/surplus shading + 7-day rolling average
- TSS-based estimate as dotted comparison line
Panel 3 — Macro Compliance (horizontal stacked bars)
- Protein/carbs/fat as % of daily target per day
- Color coding: green (>90%), amber (70-90%), red (<70%)
Panel 4 — Training Load & Strain (dual-axis chart)
- TSS bars by discipline (swim/bike/run)
- Whoop day strain line overlay (right axis)
- Weekly totals annotated
Panel 5 — Key Stats (summary cards, 2x3 grid)
- Avg daily calorie deficit (kcal) — last 7 days
- Protein hit rate (% of days >= target) — last 7 days
- Total training hours — this week vs last week
- Avg daily TSS — this week vs last week
- Avg HRV — this week vs last week
- Avg sleep performance — this week vs last week
Implementation Steps
- Migration script (
scripts/migrate-to-csv.py) — one-time parse of all existing markdown → populate 3 CSVs - CSV schema validation — simple Python check that CSVs have expected columns, no duplicate dates (nutrition/whoop), reasonable ranges
- Dashboard HTML (
files/analytics/dashboard.html) — single file, Chart.js via CDN, reads CSVs via fetch, responsive mobile layout, 5 panels - Agent integration — update nutrition agent to append CSV row after writing daily markdown log. Document the append format for Whoop and training agents.
- Taskfile entry —
task dashboard:migrate(one-time) andtask dashboard:open(serve/open) - Test with current data — validate migration against all 17+ days across all 3 sources
Output Locations
- Migration script:
scripts/migrate-to-csv.py - CSV data:
files/analytics/data/nutrition.csv,training.csv,whoop.csv - Dashboard HTML:
files/analytics/dashboard.html - Plan (this file):
.ai/projects/tri-training/analytics/plan.md