Building the health coach I never had
For 20 years I’ve kept a written log of how I train and eat. The medium evolved (from a composition book, to a generic notepad app, to a parade of underwhelming apps), but the habit held. What none of those tools ever did, however, was reason about the data. I train on a seven-day split, which assumes every week looks identical, yet mine never does: work dinners, travel, and raising a toddler tend to throw a wrench into even the best-laid plans. The regimen says Sunday is legs. Sometimes Sunday is a 12-hour flight.
Every fitness app handles this the same way, by assuming I did whatever the calendar said or (even worse) making me fix it by hand. I wanted the opposite: something that pays attention to what I actually trained, works out where I am in my cycle on its own, and tells me what to do on a given day, accounting for how I slept and recovered. So I built it, and handed the judgment a coach would exercise (get it?) to an AI model. I call it HealthBot.

The mechanics are simple. A Python script runs each morning, pulls my recovery and workout data from my Garmin wearable over MCP, reads my training plan from a markdown file in git, hands all of it to an AI model, and posts a two-part brief to a Discord channel. It has run every day since early May, and most of what makes it indispensable today came out of three implementation details the first version got wrong.
My initial attempt read the calendar. The first time I moved legs from Sunday to Monday, it prescribed the Sunday leg workout anyway, confidently and uselessly. The fix was to stop trusting the calendar and start trusting the history: the model now reads my recent sessions, identifies the last one by its contents (pulling and curling mean a back day; pressing and dips mean chest), and prescribes the next in the cycle. Day of the week is now a sanity check, not the source of truth, and it has evolved to trust what happened, not what was planned; that inversion has become the whole personality of the system.
The second problem surfaced only after daily use. To find the last session, my script pulled recent Garmin activities, but my initial volume estimate (20) barely covered a week, due mostly to my 2x daily dog walks. As a result, a lift I do once a week could fall off the list, and the brief would report no history for something I’d done just days earlier. It now pulls a far wider window and fetches the expensive set-by-set detail only where it’s needed.
The third was the data itself, which Garmin builds for charts, not coaching. Every strength workout carries the same generic label, so the only way to tell a leg day from a back day is to read the movements inside it. Weights arrive as integer grams (thanks, Garmin), sessions are padded with rest periods, bodyweight moves report no load, and Garmin’s exercise names don’t match mine, typos included. A normalization layer now sits between Garmin’s data and my model, so the prompt reads “three clean sets,” rather than a dump of mixed units.
The piece I’m most satisfied with is the least flashy. My plan lists a target weight for each movement, and the naive design prescribes it. But a target is where I want to be, not necessarily where I am (see: travel, above), and prescribing weight I can’t move cleanly is how injuries happen. So HealthBot prescribes what I last lifted, shows the target beside it, and makes a call: hold and sharpen the technique, or add load, with justification either way. The standing instruction is to show me the data and let me decide: it coaches, it doesn’t manage, which is exactly what commercial apps get wrong. And it learns from me over time.
Last but not least, two additional design decisions save me from a missed coaching notification: a failed run announces itself in Discord instead of dying in a log, and the whole service runs on bare metal, not in a container, to avoid any race conditions with other processes that share the same auth session.
As for post-MVP capabilities, HealthBot’s next evolution will transform the brief into a dialogue: I reply to swap a day, flag travel, or cut a session short, and tomorrow’s brief (along with the rest of the week) flexes to absorb it. The hard part here isn’t the plumbing; it’s the design question of how much a coach should let you negotiate before it stops being a coach and becomes a yes-man. I’m pleased with the line I’ve drawn, and I look forward to refining it over the coming months.