twenty-four: building with claude
I’ve spent too much time interacting w/ screens for my fitness life. Not for the workouts themselves - those still require showing up - but everything around them. The calendar juggling, the gym reservation anxiety, the Strava housekeeping. All of it.
The result is a set of services I call twenty-four (because there are only so many hours in a day, and I’d rather not spend them on administrative bullshit).
Here’s what happened, and how Claude became part of the story.
The Before Times
Manual labor:
- Creating Google Calendar events for every Intervals.icu workout
- Moving calendar events, then manually updating ICU to match
- Checking the gym website daily for class openings (they fill up fast)
- Missing reservations because I forgot to check
- Manually adding emojis to Strava activities (π π΄ πͺ)
- Hiding commute rides from the feed
- Assigning the right gear to activities (still some TBD here)
The gym reservations were the worst. Classes fill within minutes of the 22-day window opening. Miss it, and you’re on a long waitlist. But now? I’m always near the top of the reservation list. Cancel if I need to. No stress.
The Build: Four Phases
Phase 1: Foundation (Dec 2024 - May 2025)
Built the core services solo:
- Gym scraper: Selenium-based Gym website scraper, running in k8s with Chrome sidecar
- Calendar sync: Bi-directional sync between Intervals.icu and Google Calendar
- Strava automation: Activity processing, emoji injection, gear assignment
- S3 caching: State persistence across pod restarts
By February I had basic ICU β Google Calendar sync. By April, gym reservations were creating ICU workouts automatically. It worked, but it was fragile.
Phase 2: First Claude Assist (May 17-18, 2025)
First time working with Claude on this. Added health endpoints, logging improvements. The commit messages tell the story:
health site, thanks to claude
claude is everywhere
fuck it, we ball
(twice)
Still figuring out the collaboration rhythm. Claude helped with structure I’d been putting off.
Phase 3: Refinement Era (May 25 - July 2025)
This is where it got useful. Mixed solo/assisted work on the hard problems:
- Pushover notifications for waitlist changes
- Bi-directional deletion sync (delete from calendar β deletes from ICU)
- Auto-reservation with tentative status handling
- S3 caching to prevent duplicate creation
- Waitlist position tracking across multiple HTML formats
The auto-reservation feature took days of iteration. The Gym website doesn’t have an API, so I’m parsing HTML, clicking buttons via Selenium, and praying they don’t change the DOM. It’s held up since July. π€
Phase 4: Heavy Collaboration (Oct 7-11, 2025)
Claude and I went hard:
- Multi-arch builds: Switched all services to ARM (runs on my Apple Silicon k8s nodes)
- Workout generator: New service to create structured workout plans for unstructured ICU entries
- Dashboard: Status monitoring across all services
- READMEs: Documented each service (this was overdue)
- Swim workouts: Six commits iterating on the format. Got it right eventually.
This phase felt different. Less “Claude, fix this bug” and more “here’s what I’m trying to do” followed by working implementations. The workout generator went from idea to deployed service in a day.
The Architecture
Four services, all running in my k8s cluster:
|
|
Strava: Processes activities every 15 minutes. Adds emojis, tags commutes, mutes short walks, assigns gear. Runs in Python.
Gym: Scrapes Gym website for reservations. Auto-reserves and waitlists classes. Returns JSON over HTTP. Python + Selenium + headless Chrome.
Calendar: The orchestrator. Syncs ICU workouts to Google Calendar (with configurable transit buffers), creates ICU workouts from gym reservations, handles bi-directional updates. Sends Pushover notifications for waitlist changes. Go + S3 cache.
Workouts: Generates structured workout plans for swim/bike/run workouts that don’t have them. Go, stateless, REST API.
Everything talks HTTP. Everything has /health
, /status
, /logs
endpoints. Everything uses git SHAs for versioning. Multi-arch builds (arm64 + amd64) via remote BuildKit in the cluster.
Big Features
Gym auto-reservation: No longer care when the 22-day window opens. The service checks hourly, reserves everything that matches my schedule. I get a notification, review it, cancel if needed. Stress eliminated.
Bi-directional sync: Move a calendar event, ICU workout updates automatically. Delete from calendar, ICU workout disappears. Delete from ICU, calendar event vanishes. It Just Worksβ’.
Waitlist notifications: “β€οΈβπ₯ HIIT (Waitlisted 5/4)” β “β€οΈβπ₯ HIIT (Reserved)” with a Pushover notification. No more checking the website.
Strava automation: Haven’t manually edited a Strava activity in months. Everything gets the right emoji, the right gear, commutes are tagged, short walks are hidden.
The Claude collaboration
Phase 4 was notably faster than Phase 1. Not because Claude wrote all the code, but because it handled the tedious parts (Makefiles, READMEs, deployment YAML) while I focused on the logic. The swim workout iteration is a good example - six commits in an hour, each one a small refinement.
What’s Next
This is part one of a series:
- Part 0: The platform (k8s, MetalLB, BuildKit, .home infrastructure) (coming soon)
- Part 1: This post (overview + timeline)
- Part 2: Calendar service deep dive (coming soon)
- Part 3: Gym service deep dive (coming soon)
- Part 4: Strava service deep dive (coming soon)
- Part 5: Workout generator deep dive (coming soon)
Also on the TODO: Auto-cancellation when workouts are removed from ICU. Need a confirmation step - probably Pushover with callback. Soon.
Phase | Dates | Work | Commits |
---|---|---|---|
Solo Foundation | Dec 2024 - May 17, 2025 | Core services, basic sync | ~40 commits |
First Claude | May 17-18, 2025 | Health endpoints, logging | 4 commits |
Refinement Era | May 25 - July 2025 | Notifications, sync reliability | ~60 commits (mixed) |
Heavy Collaboration | Oct 7-11, 2025 | Multi-arch, new services, docs | ~25 commits |
Timeline: Claude’s Involvement
π€ Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com