DEV Community

2x lazymac
2x lazymac

Posted on

How I Built ACWR Sweet Spot Binding (0.85-1.35) Into a Running Coach — RunVault Translation Device

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode
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';
Enter fullscreen mode Exit fullscreen mode

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 = [
  '부상입니다', '디스크입니다', '진단', '처방', '수술',
  '확실히 ', '반드시 ', '단언컨대',
  '더 빠르게', '더 고통스럽게', '한계까지', '죽을 만큼',
];
Enter fullscreen mode Exit fullscreen mode

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."
Enter fullscreen mode Exit fullscreen mode

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:

  1. Role Framing — "This is not a medical device, it's a translation device." Surfaced in onboarding + Settings.
  2. Honest accuracy disclosure — Every card footer says "Trend-based. Single measurements are not trusted." 7-day rolling is the smallest unit we report on.
  3. 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                    │
└─────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

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)