twenty-four: AI training plans
The fitness service generates multi-week training plans using Claude AI.
Plans are stored in S3 with a full feedback loop - generate, review, iterate, then sync to Intervals.icu when satisfied.
The Problem
Training plans are rigid. They assume:
- You have perfect availability
- You never travel
- You don’t have other commitments
- You can do exactly what the plan says when it says to do it
Real life doesn’t work that way.
I needed a plan generator that could:
- Understand my calendar - Don’t schedule hard workouts when I’m traveling
- Respect weekly volume limits - Don’t jump from 20 miles/week to 40 miles/week
- Place long workouts intelligently - Move 90+ minute efforts from Wednesday to Saturday
- Generate structured workouts - Warmup, main set, cooldown with specific zones
- Allow iteration - Let me improve the plan if something doesn’t look right
- Work around existing workouts - Import what’s already on my calendar
How It Works
POST /plans - Create a new training plan:
|
|
Response (202 Accepted):
|
|
The API returns immediately - generation continues in the background. Open the plan URL to watch weeks appear in real-time.
GET /plans/:id - View the plan in a calendar UI, or add ?format=json for data.
POST /plans/:id/feedback - Regenerate with adjustments:
|
|
POST /plans/:id/week/3/feedback - Fix just one week:
|
|
POST /plans/:id/sync - Push to Intervals.icu when satisfied:
|
|
Two Generation Methods
The system supports two approaches:
| Method | Parameter | Speed | Quality |
|---|---|---|---|
| Week-by-week (default) | use_full_plan: false |
~1-2 min/week | Higher - each week generated with context from previous weeks |
| Full-plan | use_full_plan: true |
~30 seconds | Good - entire plan generated in one pass |
Progressive Rendering
A 10-week plan takes 10-20 minutes to generate with week-by-week mode. Rather than waiting with no feedback, the system now shows weeks as they’re produced.
How it works:
- Immediate response - POST returns 202 with
generation_status: "generating" - Background processing - Each week generates and saves to S3 independently
- Live UI updates - The plan page polls every 2 seconds and displays new weeks
- Progress indicator - Animated progress bar shows “Generating week N of M”
|
|
Poll for status programmatically:
|
|
Response while generating:
|
|
Response when complete:
|
|
The UI automatically reloads when generation completes or fails. Actions (feedback, sync) are disabled during generation.
Week-by-Week Generation
The default approach generates each week sequentially with full context:
|
|
Each week’s prompt includes:
- Prior weeks - What was generated before (load, duration, workout types)
- Current fitness - ATL, CTL, TSB, ramp rate from Intervals.icu
- Plan progress - Completed load, miles, hours, rest days used
- Holidays - Fetched from ICU calendar, enforced as rest days
- Imported workouts - Existing ICU workouts to work around
This context awareness means Week 5 knows exactly what happened in Weeks 1-4 and can make intelligent adjustments.
Two-Pass Validation
Before accepting each week, the system runs validation:
|
|
Example violations detected and fed back:
|
|
The model gets one chance to fix violations. If that fails, auto-fixes are applied (moving workouts, capping durations).
Sport-Specific Templates
The system uses different prompt templates based on race type:
Running:
weekly_prompt_v1_half_marathon.txt- Half marathon plansweekly_prompt_v1_50k.txt- Ultramarathon plans
Triathlon:
weekly_prompt_v1_him.txt- Half Ironman (70.3) plansweekly_prompt_v1_im.txt- Full Ironman plans
Each template includes sport-specific rules:
|
|
The validation system enforces these rules and rejects weeks that violate them.
Importing Existing Workouts
Setting import_icu_workouts: true fetches existing calendar entries and includes them in the prompt:
|
|
This lets you keep gym classes, personal training, and other commitments while generating endurance training around them.
Holiday Awareness
The system fetches holidays from Intervals.icu and enforces rest:
|
|
If Claude accidentally schedules a workout during a holiday, validation catches it and either:
- Asks Claude to regenerate with feedback
- Auto-removes the workout
Structured Workouts
Every workout uses Intervals.icu structured format:
|
|
Key format rules:
- Steps start with
- - Capitalize
Rampfor progressive efforts - Include zone notation (Z1-Z5) alongside percentages
- Add descriptive section headers (not just “Main Set”)
Gym Plan Generator
Separate from race training, the system also generates repeating gym schedules:
|
|
Weekly pattern:
- Monday: HIIT @ 7:00 AM
- Tuesday: Strength @ 6:00 AM
- Thursday: Strength @ 6:00 AM
- Friday: Personal Training @ 6:00 AM
- Saturday: HIIT @ 8:00 AM
Automatically skips holidays and creates plan templates in ICU library.
The Feedback Loop
The full workflow:
- Create plan - POST to
/planswith race details - Review - Open the web UI calendar at
/plans/:id - Iterate - Use feedback endpoints:
/plans/:id/feedback- regenerate entire plan/plans/:id/week/:num/feedback- regenerate one week
- Version history - Each regeneration increments version number
- Sync - When satisfied, POST to
/plans/:id/sync
Example feedback prompts:
Full plan:
- “Make the plan less aggressive, reduce weekly mileage by 15%”
- “Add more recovery weeks, I want a recovery week every 2 weeks instead of 3”
- “Move all long runs to Sunday instead of Saturday”
Week-specific:
- “This week has too much intensity, replace the intervals with an easy run”
- “Add a tempo run on Wednesday”
- “Make this a recovery week with only easy runs”
Storage & Checkpoints
Plans are stored in S3 with progressive saves after each week. This enables both the live UI updates and failure recovery.
|
|
Generation states:
| Status | Meaning |
|---|---|
generating |
Plan creation in progress, weeks appearing progressively |
complete |
All weeks generated successfully |
failed |
Generation failed (error message in generation_error field) |
If generation fails mid-plan, the partial plan remains in S3. You can view completed weeks and decide whether to delete and retry, or provide feedback to regenerate from that point.
Prompt Templates
Download the Claude prompt templates used for each race type:
- Half Marathon - 13.1 mile race training
- 50K Ultra - 31 mile ultramarathon training
- Half Ironman (70.3) - 1.2mi swim + 56mi bike + 13.1mi run
- Full Ironman (140.6) - 2.4mi swim + 112mi bike + 26.2mi run
These are Go text templates with placeholders (e.g., {{.RaceName}}, {{.WeekNumber}}) that get filled in with context about your race, fitness level, and prior weeks before being sent to Claude.
See also: Part 0: The Platform | Part 1: Building with Claude | Part 2: Calendar Service | Part 3: Gym Service | Part 4: Strava Service | Part 5: Workout Generator | Part 6: AI Recommendations | Part 7: Service Consolidation | Part 8: System Architecture
Co-Authored-By: Claude noreply@anthropic.com