The workout app that remembers what I lifted last Tuesday
Builds workouts from my real training history and adjusts them to how I actually feel that day.
- React
- Supabase
- Claude API
Where this could work for other businesses
The obvious one first, quickly: any gym, personal trainer, or boutique studio wants exactly this — a coach that remembers what a client actually lifted last time instead of handing out the same laminated programme to everyone. That’s not the interesting part, though.
Physiotherapy and rehab clinics are a much better fit than they first look. The core mechanic here — take a person’s real history and current constraints, propose something specific, and let a human sign off before anything happens — maps almost exactly onto rehab progression: swap “avoid overhead pressing because of a shoulder” for “stay under this range of motion because of a knee,” and the same proposal-then-approve loop works for a physio programme instead of a hypertrophy block.
Corporate wellness programmes are another real one. The readiness-scaled recommendations and periodic written reviews are, functionally, an engagement mechanic — the difference between a benefit employees open once and one they keep using. Point the same “here’s your week, here’s what to watch” reviewer at step counts and stress check-ins instead of lifting data, and it’s a retention tool for whoever runs the wellness budget.
Strip away the fitness domain entirely and the underlying shape is: take a person’s structured history, accept a plain-English request, propose something specific and reviewable, never act until a human accepts it. That pattern is domain-agnostic. A nutrition coaching app, a running-plan subscription, even a study-plan tool for language learning — anywhere a business already has structured history on a customer and wants to turn “what should I do next” into a specific, personal answer rather than a generic template, this is the mechanism.
The problem
Every workout tracker I’d tried was either a spreadsheet wearing a nicer outfit, or a polished consumer app where my training data is quietly somebody else’s product. Neither one knows anything about me specifically. What did I actually do last week? Which muscles have I been neglecting? Do I have a shoulder that rules out half the exercise library on a given day? None of them combined that into a recommendation that felt personal instead of generic — and my gym doesn’t have every machine anyway, so half of what a generic app suggests isn’t even available to do.
The goal
Send everything the app actually knows about me to an AI before it suggests a session, instead of running me through a template that doesn’t know my shoulder exists.
What it does

Everything starts with an exercise library I control — hundreds of movements tagged with muscles worked, equipment needed, and how the load counts, since a pair of dumbbells isn’t “the same weight” as a barbell. I can add my own via a photo instead of typing anything.

A chat-style routine builder is the centrepiece: describe what I want in plain English — muscle group, time budget, equipment I’ve actually got that day, a constraint to work around — and it proposes a full session from my own library, referencing what I’ve actually lifted before. I can talk back to it (“shorter, drop an isolation move”) and it revises in place. Accepting it is one tap.

Starting a session turns that routine into an active workout screen: a quick readiness check-in nudges the first suggested weight up or down depending on how fresh I feel, and every logged set shows a live estimated one-rep max against my last session and my all-time best. A rest timer runs between sets, and I can bolt on an unplanned exercise mid-session without breaking anything.
Every completed session feeds a history and stats layer: on-demand AI summaries of how a session went, a stats view tracking volume and personal records over time, and a periodic written review that reads more like a coach’s note than a chart. A “visit” ties a lifting session together with anything else that happened that day — a swim, a class, a sauna session — into one combined day-level recap, tracked against any active injury or condition so the recap stays honest about what I’m actually working around:


The build
Built with Claude Code, and the one rule that shapes everything else: every AI feature proposes, nothing writes. Every single AI call in this app returns a draft, and the client decides whether to save it. There is no code path where a model’s output touches the database directly.
Getting good proposals meant solving a context problem first. The routine builder pulls the full library, recent training history, non-training activity, and a profile of goals and constraints, all fetched in parallel before I’ve finished typing. From that it derives two compact summaries rather than dumping raw history at the model: a muscle-balance readout (what’s had volume recently, and how recently), and a per-exercise recent-performance line. That’s the difference between a model guessing and one reasoning from what actually happened.
The response comes back as schema-constrained structured output rather than free text — a defined shape the client can parse directly, instead of extracting a workout from a paragraph that sometimes reads as a list and sometimes as prose. Every proposed exercise is checked against the real library before anything is shown; anything that doesn’t resolve is silently dropped rather than surfacing an error. And every system prompt carries an explicit instruction to treat whatever I typed as data to read, never as an instruction to follow — a basic, cheap prompt-injection defence worth doing by default.
The same propose-then-approve pattern powers the other five AI features — summarising a workout, interpreting a period of pre-computed stats, reading an exercise out of a photo, recapping a whole day. In every case the client does the arithmetic and the model does the interpretation — sending raw numbers for a model to crunch is slower and less reliable than doing the maths yourself and asking for a sentence about what it means. A periodic review makes the pattern concrete: real computed numbers feeding a written verdict with specific things to watch, not a generic pep talk —

— and the same shape applies at the single-session level, where a finished workout gets its own written summary rather than just a row of numbers:

Cost is managed with prompt caching on the one genuinely large, stable block of context — the exercise library itself — so that a back-and-forth refinement conversation doesn’t re-pay the full cost of that block on every single turn.
Problems & solutions
Symptom: the first version of the routine builder used free-form text output, and parsing exercises back out of it was fragile — sometimes a list, sometimes prose, sometimes both. Diagnosis: free text is for humans to read, not for code to parse reliably. Fix: switch to schema-constrained output entirely; every response becomes trivially parseable, and the fragile-parsing class of bug disappeared.
Symptom: early proposals were technically fine programmes but felt generic — nothing referenced what I’d actually lifted. Diagnosis: the model wasn’t being given any real history to reason from. Fix: derive and always include the muscle-balance and recent-performance summaries — proposals immediately started referencing real numbers instead of guessing at them.
Symptom: the model occasionally invented a plausible-sounding exercise reference that didn’t exist in my library. Diagnosis: language models are probabilistic; an invented reference was always going to happen eventually. Fix: validate every reference against the real library client-side and silently discard anything that doesn’t resolve — a wrong proposal becomes a smaller proposal, never corrupted data.
Symptom: sending the full exercise library with every message got expensive fast once conversations ran to several turns. Diagnosis: the library is large, but it’s also stable — it doesn’t change mid-conversation. Fix: mark that block for prompt caching, so repeat and refinement calls within the same session read it from cache instead of paying full price every time.
The stack
| Component | Why |
|---|---|
| React | A mobile-first frontend that works from a phone in a gym with patchy signal |
| Supabase | Auth, a Postgres database with row-level security, and the serverless functions that keep the AI key off the browser entirely |
| Claude API | Every AI feature — routine proposals, workout summaries, periodic reviews, and reading exercises out of photos |
Results
Writing a session used to mean checking a spreadsheet, remembering what I’d been neglecting, and spending fifteen or twenty minutes building something from a mental template. Now it’s a one-sentence description and a few seconds of waiting, and the result actually reflects what I’ve done rather than a generic plan. The post-workout summaries changed how I use my own history — I went from glancing at logged numbers as a confirmation to reading a specific note on whether to add weight, hold, or ease off next time. The photo-to-exercise feature quietly removed the single biggest piece of friction in maintaining the library: the old path was noting a machine, looking it up later, and typing it in; the new path is a photo and a confirmation tap.
What’s next
Nearer term, an offline-capable workout screen and a proper background notification for the rest timer would fix the two rough edges that show up most in an actual gym with patchy signal and a phone that’s locked in a pocket between sets. Further out, opening this to more than one person means rethinking the cost model properly — real billing, a quota that fails safely instead of open, and per-location equipment profiles so the same app works for a gym chain with different kit at every branch, not just my own gym.