TL;DR — I shipped a running app that refuses to say "run harder". Instead it computes Acute:Chronic Workload Ratio (ACWR), lnRMSSD 7-day moving average, and gait asymmetry — then translates those numbers into prescriptions like "shorten your stride by 5 cm so your foot lands under your hip." This post is the engineering + governance writeup.
The problem with "go faster, hurt more"
Open Strava and you see PRs. Open NRC and an audio coach tells you to dig deeper. Open Runday and you get an ad before your warmup. None of them tell you the most important truth your body is broadcasting through your heart rate variability:
"You haven't recovered. Don't run today."
That's the gap RunVault tries to close. We modeled the AI coach as two personas in one body:
| Persona | Job |
|---|---|
| Computational Sports Scientist | Strict numeric analysis of HRV, GRF, asymmetry |
| Empathetic Coach | Translate scientific output into warm, prescriptive language |
This duality is now governed by brand/GOVERNANCE_PROMPT_PROTOCOL.md — the document that sits above brand bible, color tokens, and even the LLM system prompt.
1. HRV — single point is noise, 7-day rolling is signal
Single morning RMSSD readings are statistically useless. Sleep, hydration, last night's wine, mental stress — all of them push the value around. So we follow the Altini / Kiviniemi / Plews protocol:
lnRMSSD = ln(RMSSD)
RMSSD_MEAN_7d = mean(lnRMSSD_1 .. lnRMSSD_7)
RMSSD_CV = (std_7d / RMSSD_MEAN_7d) × 100
When RMSSD_MEAN_7d drops below the user's personal baseline and RMSSD_CV simultaneously collapses, we flag Functional Overreaching (FOR) and downgrade the day's plan automatically. No willpower required from the runner.
2. ACWR Sweet Spot — [0.85, 1.35] is your friend
The Acute:Chronic Workload Ratio comes from professional team sport (Catapult Vector, Zephyr BioHarness). It's defined as:
ACWR = AcuteLoad(last 7 days) / ChronicLoad(28-day average)
| ACWR | Status | Action |
|---|---|---|
| < 0.85 | Low volume | Gentle build, +10-15% next week |
| 0.85 – 1.35 | Sweet Spot | Sustain |
| 1.35 – 1.50 | High caution | Taper next session |
| ≥ 1.50 | Danger | Reset volume + intensity |
In the RunVault Worker (Cloudflare Workers + D1) it's literally:
const acwr = chronic > 0 ? acute / chronic : 0;
let status: 'low' | 'sweet_spot' | 'high' | 'danger' | 'no_data';
if (chronic <= 0) status = 'no_data';
else if (acwr < 0.85) status = 'low';
else if (acwr <= 1.35) status = 'sweet_spot';
else if (acwr < 1.5) status = 'high';
else status = 'danger';
And it surfaces in the UI as a horizontal gauge with the sweet-spot band painted green. No raw number stands alone — every value gets a verbal label.
3. Language translation — raw numbers are banned
We literally banned a list of phrases at the LLM system prompt level:
const BUDDY_BANNED_PHRASES = [
'부상입니다', '디스크입니다', '진단', '처방', '수술',
'확실히 ', '반드시 ', '단언컨대',
'더 빠르게', '더 고통스럽게', '한계까지', '죽을 만큼',
];
And the system prompt enforces:
== Language translation (mandatory) ==
Raw number-only output is forbidden. Every number must be translated
into "an actionable gait directive the runner can perform right now".
Example:
Raw → "Right foot impact 12% higher than left"
Translated → "Right foot impact is 12% stronger.
Your right hip may be slightly behind.
Straighten your upper body and gently push
your sternum forward as you run."
This is the part where the Computational Sports Scientist hands the mic to the Empathetic Coach.
4. Trust Stack — three layers, always visible
Privacy/health-data products fall apart on trust. Three UX rules now live at the top of the brand governance doc:
- Role Framing — "This is not a medical device, it's a translation device." Surfaced in onboarding + Settings.
- Honest accuracy disclosure — Every card footer says "Trend-based. Single measurements are not trusted." 7-day rolling is the smallest unit we report on.
- Visible Data Autonomy — Privacy Center is on the main home screen, not buried in settings. Delete + on-device-processing toggles are first-class buttons.
The Oura playbook ("Live Fast, Die Old" + Translation Device framing) is the explicit benchmark.
5. Architecture — 3-layer Perception → Network → Application
Borrowed from BoxingPro and ported to running:
┌─ 1. PERCEPTION ─────────────────────┐
│ IMU + insole pressure + PPG HRV │
│ + rear-cam MoveNet (17 keypoints) │
└──────────────┬──────────────────────┘
▼
┌─ 2. NETWORK ────────────────────────┐
│ 5th-order Butterworth LPF │
│ Timestamp sync + threshold judging │
└──────────────┬──────────────────────┘
▼
┌─ 3. APPLICATION ────────────────────┐
│ Governance-prompt semantic mapping │
│ LLM coach output │
└─────────────────────────────────────┘
The application layer is where the LLM (currently Claude Haiku with a Pollinations free fallback) takes the kinetic data, walks through the banned-phrase scanner, and emits Korean coaching prose.
6. Why this is a competitive moat
Five running apps benchmarked. Same table from brand/USP_v3.md:
| NRC | Strava | Runna | Runday | RunVault | |
|---|---|---|---|---|---|
| Tracks | Pace/HR | Segments | Mileage | GPS | GRF / Braking / Symmetry / lnRMSSD / ACWR |
| Persona | Direct coach | Social rival | Plan generator | Korean pacer | Computational Scientist + Empathetic Coach |
| Price | Free + IAP | $11.99/mo | $99.99/yr | Free + ads | $5.99/mo |
| Weakness | Watch detached | English-first | Premium price | Ad clutter | (early) sensor bundle barrier |
7. Try it
RunVault Closed Beta APK (Android, ~160 MB):
👉 https://api.lazy-mac.com/runvault.apk
Privacy policy:
👉 https://api.lazy-mac.com/runvault/privacy
The AI coach Tab is the first thing you see when you open the app — graph view + voice mic + prompt. Ask it something like "어제 무릎이 약간 아팠는데 오늘 10km 가도 될까?" ("My knee hurt a bit yesterday, can I do 10k today?") and watch the persona kick in.
8. What's next
- Health Connect HRV records SDK wire (today the worker computes ACWR from real D1 activities; HRV is still a stub waiting on Phase F SDK integration).
- NURVV Run / Moticon insole compatibility for real gait asymmetry detection.
- Whoop Unite-style B2B integration for corporate wellness pilots.
If you're building a health-data product and tempted to just dump sensor numbers on a chart, please don't. Build a translation device.
— Daniel (lazymac2x)
Top comments (0)