I ride structured training workouts on an indoor bike several times a week, and I now spend a fair amount of that time working: mostly elaborating GitHub issues and exploring new ideas by talking with Claude Code. Indoor workouts can be hard, tedious, or both, and it recently occurred to me that there might be something better to do with that time than block out the pain with music or numb the boredom with Netflix. I can manage about an hour at the moment before the wrinkles in my setup overpower my patience: my goal is to comfortably sustain a two to three hour ride while doing meaningful work.
The basic loop: left button starts and stops dictation, right button submits the prompt. I lack both the mental and physical dexterity needed to actually ride while filming this. Sorry about the dirty Mac!
I ride a Wahoo KICKR Bike, in front of which sits my MacBook on a cheap music stand. I run Zwift on the Mac, though I largely ignore it in favour of the Zwift Companion app on my phone tracking which set I'm in and how long is left. Claude Code runs in one or more terminal panes or tabs on my screen. The KICKR Bike's handlebar hoods house two small inset buttons, which are intended for Zwift's steering feature but are disabled during workouts. I wrote Claude wrote a small Python script that connects to the bike over Bluetooth, intercepts those button presses, and remaps them to keyboard shortcuts:
| Left button | Right button | |
|---|---|---|
| Tap | Toggle voice dictation (F1) | Submit prompt (Return) |
| Double tap | Navigate up (Up arrow) | Navigate down (Down arrow) |
| Hold | — | Cancel prompt (Escape) |
The Python script is a hack: all it does is synthesise keypresses. Enter submits a prompt, Escape cancels one, arrow keys navigate option lists — pretty standard terminal behaviour. The only custom mapping is F1, which I've bound to toggle Fluid Voice dictation and is a completely arbitrary hotkey choice. Left button starts and stops dictation, right button hits Enter/Return to submit whatever I've said to Claude. If I fluffed the prompt, a long press of right triggers Escape to clear it. On the odd occasion Claude needs me to choose from a list of options, double tap left/right navigates to the answer I want and right button submits it.
The buttons are recessed into the top of each hood, designed to be hit with your thumbs without shifting your grip:

With this setup, I very rarely need to lift my hands off the handlebars. The main exception is switching between terminal tabs or panes — I typically run multiple Claude Code sessions in iTerm2, and I haven't found a keybinding that cycles to the next or previous tab or pane (only absolute hotkeys like "jump to tab 1", which would need statefulness in the Python script that I'm not comfortable with). So for now, switching sessions means reaching for the keyboard or trackpad. If you know of a terminal emulator with "next tab" / "previous tab" keybindings, let me know.
Not coding, but thinking
I briefly tried to code from the bike, but I found I just couldn't quite work within the confines of a terminal window; I'd need to see code in more detail, or view the results in a browser tab. It was doable on a shorter, less intense workout, but it irked me every time I had to take a hand off the handlebars to command-tab, move the trackpad, or click something, and became progressively more difficult and distracting as I fatigued over the course of a workout. The ergonomics weren't there, and I found my mental model of the code in question became increasingly fragile as the workout progressed: I just couldn't concentrate at the right level for long enough.
As luck would have it, I recently rediscovered how powerful it is to invest time in ticket elaboration — something I've mostly abandoned as a solo developer. I knew how valuable a well-maintained backlog could be, but I'd just stopped bothering to do it. Getting back into the habit of elaborating tickets, and finding I could do it from the bike, was a happy coincidence.
A typical session
A session on the bike starts with me asking Claude to pull up my open issues using the GitHub CLI — usually the ones with little or no elaboration, the brain farts I created earlier. We pick one that seems like a good candidate to talk through, and I start rambling. If I've been really organised, I'll have chucked a few one-liner tickets in ahead of the workout to make sure there's something fresh to work with.
My dictation is seldom clean. It's fragmented, interrupted, half a sentence at a time. It doesn't matter if I need to stop mid-thought because I can't breathe. Unfiltered, unabridged, full-blown waffle, huffed and puffed in disjointed fragments. Claude patiently asks questions, suggests approaches, flags things I haven't considered. We go back and forth — all via ragged voice on my end, all captured in the GitHub issue when ready. It doesn't take long before a ticket is in a good enough shape that future me and/or future Claude Code can pick up and run with.
So much of my work — past and present — already exists on GitHub, not to mention the oceanic expanse of open source software to use as occasional reference. The one simple gh tool is a huge unlock: Claude can read issues, update them, cross-reference other tickets, browse external repos, all without me touching anything. Don't mistake Claude's usage of gh for just another MCP server: its knowledge of gh is baked into its training data, whereas it knows nothing about each MCP connection (that's the whole point of MCP). That level of innate understanding makes its use of gh almost frictionless.
In a good session I can elaborate five or more issues (and sometimes a lot more than that). Five-plus pieces of work fully scoped, documented, and ready to implement — created as a byproduct of a workout I was going to do anyway. The state of the frontier models is so good now (if you believe Boris Cherny, coding is "largely solved") that most of these tickets can be handed over to a future Claude Code agent which I'm increasingly confident will few-shot the implementation with minimal supervision.
Not all intervals are equal
The profile and overall intensity of the workout matters. Here's a session from Zwift's sub-60 minute catalogue:

You can bet I'm not doing anything other than surviving during those red bits, but the blue and green steadier sections after allow me to dictate freely, think through a ticket, review what Claude's come back with, and then repeat for as long as I've got. Starting a thought at the wrong time doesn't matter either; Fluid Voice has no limit on dictation length nor any cut-off if you've been idle (or just trying to breathe) for too long.
Voice is the backbone
Speaking of Fluid Voice: none of this works without voice dictation, and Fluid Voice is absolutely amazing. It's free, open source, and does real-time transcription entirely on-device. The transcription is fast and accurate. I've tried various dictation apps recently and nothing else comes close for my use case.
You can optionally pass each dictation through an AI model for enhancement — tidying up the stream of consciousness into something more coherent. I flip flop on this; right now I don't, because Claude Code is the layer of AI enhancement and it handles my messy transcriptions just fine. But when I'm dictating into a browser (writing issue descriptions directly in GitHub, for example) I'll usually turn it on.
Fluid Voice has small touches that add up, too. It can pause your system media when you start dictating and resume it when you stop, which I personally don't use on the bike but is a lifesaver when I'm back at my desk later blasting out tunes. It's customisable, transparent in its behaviour, and it's open source. It is so good.
Tickets are the right abstraction
I've already mentioned why I gave up trying to code on the bike earlier. Ticket creation and elaboration hits a sweet spot because:
- It's high-value work. A well-elaborated ticket saves significant time during implementation, because Claude has clear context, constraints, and direction to work from.
- It's purely conversational. No visual feedback loops, no compilation, no running applications.
- It's… doable. Workouts can be really hard, but I can usually wheeze my way to a well-formed ticket with some back-and-forth conversation. I previously mentioned that the mental context required to code felt too delicate, too easily lost on the bike, whereas tickets feel like a higher-level abstraction that are easier to juggle.
- Issues are permanent, portable artefacts. Everything lands in GitHub — issues with comment histories, linked pull requests, cross-referenced tickets. A corpus of thinking that outlives any single session and is accessible from any device, by any agent or human.
- It's low risk. If I don't like the look of a ticket later, I can edit it or junk it. No code gets committed. No databases get dropped.
I used to pour all my thinking directly into Claude Code's plan mode, which worked in the moment but left nothing behind. The prompts were ephemeral — they lived and died within the session, and when I had multiple ideas competing for attention, I'd end up running parallel sessions that burned through usage limits as Claude went head first into implementation after planning. Those plans are kept on a local folder on disk, but in a way which feels internal to Claude, rather than something you're meant to get your hands on and hack around with. I trialed having Claude produce planning documents in the codebase (which isn't the same as the built-in plan mode), but I just didn't like it: over time the cruft accumulated and Claude would sometimes find and reference old plans long after implementation. The lifecycle management and cross-linking just wasn't as smooth. GitHub issues work better for me, personally.
Why this matters to me
The voice-driven ticket elaboration workflow — Claude Code, GitHub CLI, Fluid Voice — is just as valuable at my desk. I use it there too. But there's something about doing it from the bike that I find unbelievably exciting. I don't know where AI is taking us, and some of the possible futures scare me, but the point we're at right now feels incredibly empowering and exciting.
Physical well-being matters enormously to me, both for its own sake and for the mental balance it provides me with. Time on the bike was never dead time because getting fit is productive in the most fundamental sense. But with this setup, I'm taking time that was already valuable and layering serious, real work on top of it. I'm sweating through an interval set and somehow also producing a backlog of well-scoped, well-documented tickets that future me — or a colleague, or a Claude Code session — can pick up and implement. The output has permanence. Issues with comment histories, linked pull requests, cross-referenced tickets: an ever-growing record of decisions and context that lives permanently in GitHub, which has been my home for the last 17 years.
What's next
The target is completely hands-free. It sounds absurd to say that moving your hands a few inches from handlebars to laptop is tiring, but trust me, it really is, and it really adds up over the course of a workout. I'm not there yet for the reasons already mentioned, which mainly boil down to needing to move focus between apps or terminal panes.
There's also a lot of room for improvement in the cobbled-togetherness of the whole thing. Bike → Python → fake keypress → Fluid Voice → Claude Code. That's a lot of duct tape, a lot of interfaces kludged together in the crudest possible way under layers of hackstraction.
Lastly, the final link in the chain is a terminal window, running Claude Code. Issues are plain text, so a TUI is mostly okay, but there are still pain points: long issues or long conversations require scrolling my trackpad (gross) to read fully. It's hard to get a full view of which tickets have and haven't been covered or their status.
I have a hunch that Claude can write me a homebrewed application which will remove a lot of these rather unsightly seams, but we'll see. Improvements are definitely possible, but whether or not they'll get me to the magic two or three hour mark remains to be seen. I'll report back with any progress!