No description
  • TypeScript 78.4%
  • JavaScript 12.7%
  • CSS 5.4%
  • HTML 2%
  • Python 1.3%
  • Other 0.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Viscid 80eaa16879 Debrief celebrates: highlights section in the script, a flashier skill
scripts/debrief.ts gains a highlights section computed against the rider's
own history up to the debriefed ride: personal bests (longest ride, most
kcal, longest steady block, hardest interval power, most hard minutes,
cadence lock, lowest settled HR at a wattage, fastest 1-min recovery),
milestones crossed, week streak, lifetime totals, the week's minutes bar,
the VO2 ladder position, and the fitness trend across checks.

The debrief skill is rewritten around it: headline, wins, a stats table,
honest watch items, what's next, a send-off tied to the long game. Wins
come only from what the script printed; the standing rules (no fan talk,
meds only for a flat line, never push intensity) stay.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NvknhqL86maTxZE4Ga4sgQ
2026-09-01 15:12:11 -04:00
.claude/skills/debrief Debrief celebrates: highlights section in the script, a flashier skill 2026-09-01 15:12:11 -04:00
assets Start-menu app: npm run install-shortcut; npm start works on every platform 2026-08-25 20:51:06 -04:00
docs Consult log 2026-08-29: HR drift is normal physiology, the line gap is drift itself 2026-08-29 12:41:47 -04:00
electron Start-menu app: npm run install-shortcut; npm start works on every platform 2026-08-25 20:51:06 -04:00
relay Relay: strap drops were failed pairing, not radio contention 2026-08-18 10:47:54 -04:00
scripts Debrief celebrates: highlights section in the script, a flashier skill 2026-09-01 15:12:11 -04:00
server Session kind is stored with the ride, not guessed from its name 2026-09-01 14:56:46 -04:00
src Split main.ts into src/app/ modules, one concern each 2026-09-01 15:06:16 -04:00
.gitignore Weekly riding time is a setting (riding h/week, 2-6, default 4) 2026-08-19 12:13:14 -04:00
index.html Settings: choose how many months the Calendar projects 2026-08-28 23:43:50 -04:00
package-lock.json BikeBeater: cadence-coaching app for the Rogue Echo Bike 2026-08-16 19:21:33 -04:00
package.json Start-menu app: npm run install-shortcut; npm start works on every platform 2026-08-25 20:51:06 -04:00
README.md Start-menu app: npm run install-shortcut; npm start works on every platform 2026-08-25 20:51:06 -04:00
spike.html BikeBeater: cadence-coaching app for the Rogue Echo Bike 2026-08-16 19:21:33 -04:00
tsconfig.json BikeBeater: cadence-coaching app for the Rogue Echo Bike 2026-08-16 19:21:33 -04:00
vite.config.ts BikeBeater: cadence-coaching app for the Rogue Echo Bike 2026-08-16 19:21:33 -04:00

BikeBeater

Ride your Rogue Echo Bike (V3 connected console) to a program by ear: the app plays a metronome — one beep per pedal revolution — at the tempo that produces the target console RPM, and distinct audio cues when your actual cadence drifts outside tolerance. The console broadcasts live cadence/power over Bluetooth FTMS; the app compares what you're doing to what the program asks and nudges you back.

A one-time rhythm calibration (Sound card → Calibrate rhythm) maps beep tempo to console RPM: the console reports flywheel RPM, which is belt-geared faster than the cranks by an unpublished ratio, so the mapping must be measured, not assumed. You pedal one revolution per beep through three tempos (~90 s); the median transmitted cadence per tempo over the beat rate gives the gear ratio, cross-checked for consistency and snapped to a simple mapping when it is one (src/engine/calibrate.ts). Until calibrated, the app assumes 1:1 and the metronome will feel wrong — calibrate first.

Run

npm install
npm start          # server + desktop overlay, one terminal; close the overlay to stop both

npm start starts the Vite dev server (with the SQLite API) if nothing is on :5173, waits for it, then launches the overlay; a server already running is reused. For the pieces separately: npm run dev (server) and npm run app (overlay).

npm run install-shortcut puts BikeBeater in the Start menu (Windows / WSL — pin it to the taskbar from there) or in the app launcher (Linux); one click runs npm start. The repo is the app: a code change is live on the next launch, nothing to rebuild or reinstall.

Platforms: on macOS, Linux and native Windows the overlay is Electron from node_modules. Under WSL the overlay must run on the Windows side (a transparent window doesn't survive WSLg), so install the Windows Electron build into WSL — npm_config_platform=win32 npm install — and npm start launches it through scripts/app.sh while the server stays in WSL.

Or open http://localhost:5173 in Chrome or Edge on the Windows side (Web Bluetooth doesn't exist in WSL, and localhost forwarding makes the WSL dev server reachable from Windows). Press the console's Bluetooth/connect state if it needs one, click Connect Echo bike, pick a program, Start ride.

  • ?demo=1 — desk demo: fake bike + short program, straight to the ride screen (click the sound chip once; browsers block audio until a gesture).

Hardware spike (do this first on the real bike)

http://localhost:5173/spike.html — connect, pedal a few minutes including sprints and a full stop, Export JSON. The export answers the open hardware questions (real notify rate/jitter, flag layout, 0-RPM behavior, reconnect quirks) and becomes test fixtures in src/fixtures/.

Facts the design leans on

  • The Echo's cadence is genuinely measured (reed switch on the flywheel) but the number is flywheel RPM, not crank RPM — fixed belt gearing links them by a constant, unpublished ratio (confirmed on-bike: a 1-beep-per- transmitted-RPM metronome is audibly out of sync with the pedals). Watts are derived from RPM via a fan curve — console-relative, not calibrated power. Program targets and drift detection stay in console units; only the metronome tempo is divided by the calibrated ratio.
  • Because watts are a function of RPM, programs target cadence; expected watts are shown as an estimate once the app has passively learned enough (rpm, watts) pairs (src/engine/powermodel.ts, localStorage).
  • FTMS Indoor Bike Data arrives ~1 Hz as a scalar — beeps set the tempo but cannot phase-lock to crank position.
  • Drift alerting is a state machine (src/engine/drift.ts): EMA smoothing, outer/inner hysteresis bands, dwell times, re-alert cooldown, grace period after interval changes, and separate "stopped" and "stale" states.

Tests

npm test

Covers FTMS frame parsing, the drift state machine (alert/clear/grace/ stopped/stale/overshoot timing), and program stepping. Recorded rides (Download recording / spike export) can be replayed through the detector to tune thresholds without re-riding.