Trace
I could never remember what I had lifted the week before. So I built Trace, a workout tracker I open on my phone in the gym, and it records what I lift set by set and shows me what I did the last time I trained the same exercise.
It started as a Google Sheet. I wrote my sessions down so I could come back the next week and pick up at the right weight, and after a while I noticed the sheet was worth more than that. Every line was a number with a date on it, so I could plot it and see which lifts were moving and which ones had been sitting at the same weight for a month.
I made charts in that spreadsheet for a while. Then I got tired of typing into a spreadsheet between sets, and that is where the app came from. The rest of this page is what ended up underneath it.
Live at trace-training.com, behind a login. There is a demo
account: demo@trace-training.com / TraceDemo2026!
A session is a list of sets
The first thing I built was the shape of the data, before there was an app to put it in. A session is a list of sets, and every set carries the exercise, the load, the reps, how hard it felt, and whether it was a warm-up or a drop set. Everything I wanted to look at later reads from those rows, so adding a view has mostly meant writing a query.

Loads are stored twice, the number I typed and a normalized value in kilograms, and every calculation runs on the second one while the first is what comes back on screen. It is one redundant column, and it keeps a session I logged in pounds from quietly corrupting a volume total three months later.
Three records instead of one
Every exercise carries three records. One for the heaviest single working set, one for the best estimated one rep max, and one for the most total work done in a session. I kept all three because a record means a different thing depending on what I am chasing that month, and holding a single number would have hidden the other two.

Volume counted per session
The muscle view counts a set as one for the muscle an exercise targets and a half for each muscle that assists it, so a session of pull ups shows up on my back and partly on my biceps without me telling it anything.

It is anchored on training sessions rather than calendar weeks. I train on an irregular schedule, and calendar buckets made a heavy week that happened to straddle a Sunday look like two light ones, which is the opposite of what I wanted to see.
Asking it in words
Trace exposes an MCP server, twelve tools over the same rows, and I connect it to my own assistant. Eleven of the tools only read. The twelfth writes one field, which is the note I leave for the assistant about where I am in my training right now, and it writes it only after asking me.
I asked it how my training was going lately, which names no lift and no number, and it read ten of my hundred and twenty four sessions, chose which ten on its own, and came back telling me my quads were on an eight day cadence while everything upper body sat at four. I had not asked about my quads.
Most of the work went into the glossary the server sends with every connection. An assistant doing arithmetic on the wrong column produces confident nonsense and never says so, and the part I like best is that the weekly volume figure carries its own warning:
Loads are stored twice. Each set carries the value the user typed AND a normalized
load_canonical_kg. ALWAYS do math on canonical kg. Mixing units is the single most common mistake.Muscle volume is event-anchored (per training session, not calendar weeks, because weekly buckets mislead irregular trainers). RP weighting: a set counts 1.0 for the exercise's primary muscle, 0.5 for each secondary.
weekly_equivalentis a DERIVED ~weekly set count for MEV/MAV/MRV comparison ONLY. Those landmarks are fuzzy ranges, so don't over-read its precision or read week-to-week noise into it.
Logging with no signal
I log during the session, on my phone, and gyms have bad reception, so writes go to a database in the browser first and sync afterwards through an outbox that retries with backoff. Logging a set never waits on the network. Everything I have added since has had to accept rows that arrive late.
Where it is now
Trace is live and I am the only person using it. I open it on every training day, and what is left on it is bug fixing and deciding whether I put it in front of anyone else. It runs on SvelteKit with Supabase behind it and Vercel in front, and the database migrations are applied by CI on every deploy, because the app I am changing is the one I train with.