Web Platform · Completed
Floofy
A personal Garmin workout builder and Claude training-plan importer. It replaces Garmin Connect's clunky step editor with a manual builder and a deterministic pipeline for expanding Claude-generated plans into pushable sessions.
Client
Personal Project
Year
2025
Duration
MVP + Backend Phase
Role
Solo Developer

What it does
Floofy is a solo side project built to get away from Garmin Connect's clunky step editor. You can hand-build structured workouts with steps, repeats, and targets, or paste a Claude-generated `floofy_plan_v1` plan. The app deterministically expands the plan into concrete sessions, classifies them, and lets you push the structured ones to Garmin. It runs on a layered seam architecture with a typed DI container, so in-memory mocks swap for real Supabase and Garmin backends without touching the UI.
The problem
Garmin Connect's step editor is clunky. Building structured workouts by hand is tedious, and importing an LLM-generated training plan usually means manually translating prose into individual sessions. Floofy fixes this with a manual builder and a deterministic importer for Claude-generated plans.
How it works
The architecture uses a layered seam design with a lightweight typed DI container. Flow: ui → viewmodels → repository interfaces → concrete implementation. Concrete implementations bind via DI, so in-memory mocks stand in for the MVP and swap for real backends without touching UI, viewmodel, or domain code. Zod schemas are the single source of truth for domain types, shared unmodified between client and server.
The core is a deterministic transformation pipeline. Plan JSON goes through expand (weekly_template[day] ⊕ progression[week]), resolve (attach scheduledDate), classify (structured | open | display_only), select, compile (machine block → Garmin payload), and push (addWorkout/scheduleWorkout). No prose parsing anywhere in this path. Only the machine "structured" block Claude attaches to each session is ever compiled.
Garmin I/O is isolated behind a single server-side adapter over the unofficial @flow-js/garmin-connect package, invoked only from Next.js API routes. Garmin credentials and tokens never reach the client. With zero backend configuration, the app runs entirely on in-memory mocks. Configuring Supabase swaps in a real, RLS-scoped PlanRepository. Configuring real-mode swaps the Garmin push/session seams to the real adapter with an encrypted, per-user token vault in Supabase.
Status
The MVP is complete and running end-to-end on mock data. The backend phase is done: Supabase schema/RLS/RPCs, Supabase auth, the encrypted per-user Garmin token vault, and the real adapter are implemented and env-gated. The real Garmin adapter has been live-verified end-to-end against a real account — from login to createWorkout to read back and deleteWorkout, leaving no residue behind.
Not yet real-verified: scheduleWorkout (the actual calendar write) and on-watch confirmation are unit-tested only. The Garmin API used is unofficial — an internal, session-authenticated workout-service endpoint, not Garmin's official developer program. It can change without notice and carries real ToS risk if used for anyone other than the account owner. The project has ~339 tests across domain, pipeline, DI, features, ui, and infra. There is an optional unobtrusive tip jar toward the author's Absa Cape Epic mountain bike stage race entry.
Built with
The screens
The build log
Development updates
No development updates yet. Check back soon for progress updates and insights.
