2026live
armchair.run
Choose “engineer” and your Sunday ride comes back written up as a day in the life of a developer. Nobody asked for this. It runs unattended, seconds after you finish, and it is the most over-engineered joke I have shipped.
- role
- solo, end to end
- built
- 2026, still tinkering
- runs on
- python · cloud run · claude api
- live at
- armchair.run
// the idea
Strava records a great deal about an activity — where you stopped, which kilometre hurt, how your heart rate drifted over the last half hour — and shows almost none of it back in a form anyone actually reads.
So this reads the finished activity, works out what happened in it, and writes a short piece of commentary into the description. Automatically, in whichever voice you picked, without anyone looking at it first.
There is no business case. It exists so that somebody scrolling Strava gets a small laugh.
// what it wrote
Front-loaded the merge conflicts early, cleared them, and then somehow got slower on flat, unchanged code for the rest of the ride — CPU usage climbing on a workload that didn’t. One stretch had a wall in it and I crawled through at a pace that would embarrass a linter. Fastest commit of the day was a revert. Runtime did the work, I filed the summary.
// the bit I got too serious about
Telling a language model not to quote numbers does not work. Not showing it any does.
The whole thing depends on commentary being publishable with nobody checking it first — which means it cannot get the facts wrong. So I tested the obvious approach first: instruct the model not to quote figures. Across six samples it quoted 47 of them.
Removing the numbers from its input produced none — with no instruction at all. Every fact is now computed in Python before the model is involved: stopped time, per-kilometre splits carrying their own elevation, sustained gradient, heart-rate drift, and where the effort ranks against your recent history. Those become plain sentences — “They stopped once for longer than they spent moving” — and only the sentences reach the model. It never sees a number, so it has nothing to misquote.
The rules for what counts as notable live in code rather than in the prompt, which moves the judgement somewhere it can be tested. It is the same rule that runs Roos Research, arrived at independently and for considerably less serious reasons.
// how it works
Strava calls a webhook, which validates the event and hands it straight to a queue — Strava retries unless you answer inside two seconds, so none of the slow work happens there. A worker then fetches the activity and its sensor streams, computes the facts, asks for prose, and writes it back.
The store doubles as the idempotency guard: a transactional claim means an activity gets commented on exactly once, even if two workers are handed it at the same moment. Voices and tones are markdown files rather than code, so writing a new one is a diff and needs no release.
// what I deliberately didn't build
- No multi-tenancy, no scale work. The target was me, and later a few friends. Building for volume would have been building for nothing.
- Access is invite-only, and not by choice. Strava’s default API tier caps how many athletes can connect at once. Going wider means applying to have the app published, which I have not decided about yet — so for now the allowlist admits nobody by default.
- Three sports only. Racquet sports log a couple of hundred metres over an hour and would be described badly, so they are skipped rather than covered poorly.
- Search is client-side, and the app says so. The two things worth searching live in different systems, so a server implementation would run the same scan one round trip further away. Better an honest limit than a slower dishonest one.
- One cloud project, no separate environments. Local development runs the same code against files instead, with two environment variables as the only difference.
- No analytics of any kind, which is precisely why I cannot tell you how many activities it has commented on. I decided I would rather not know than add tracking to a joke.