A structured workout is a sequence of intervals with defined targets. The targets might be watts, a percentage of FTP, a heart rate zone, or an open effort. Each device vendor has its own format: Garmin uses FIT workout messages, Zwift uses .zwo XML. pacelore stores workouts in a common schema and exports to both.
The step schema
Every workout is an array of steps. Each step has:
kind: one ofwarmup,work,rest,cooldown, orrepeatdurationSec: duration in seconds (required for all kinds except open-ended steps)targetType: one ofwatts,ftp_pct,hr_pct, oropentargetLow/targetHigh: the target range (watts, percentage, or bpm depending on type)steps: nested array forrepeatkind, plus arepeatCountfield
A 2x20 at 95% FTP workout looks like this in the schema:
{
name: "2x20 Sweet Spot",
sport: "cycling",
steps: [
{ kind: "warmup", durationSec: 600, targetType: "ftp_pct", targetLow: 50, targetHigh: 65 },
{
kind: "repeat",
repeatCount: 2,
steps: [
{ kind: "work", durationSec: 1200, targetType: "ftp_pct", targetLow: 88, targetHigh: 95 },
{ kind: "rest", durationSec: 300, targetType: "ftp_pct", targetLow: 50, targetHigh: 55 },
],
},
{ kind: "cooldown", durationSec: 600, targetType: "ftp_pct", targetLow: 40, targetHigh: 55 },
],
}
The repeat kind handles nested intervals cleanly. Garmin's FIT format has a native repeat message; Zwift's .zwo has an IntervalsT element. The schema maps to both without needing special cases.
FIT export for Garmin
FIT is a binary format defined by Garmin's FIT SDK. A workout FIT file contains a sequence of messages: one file_id message, one workout message with the workout name, and then a series of workout_step messages.
Each step message specifies:
wkt_step_name: display labelduration_type:time(most common),open,repeat_until_steps_completeduration_value: milliseconds for time-based stepstarget_type:power,heart_rate,opentarget_value: the target, encoded as watts or bpm. FTP percentage targets are pre-resolved using the athlete's stored FTP at export time.custom_target_value_low/custom_target_value_high: range bounds
The repeat step kind becomes a special FIT step with duration_type = repeat_until_steps_complete and a duration_value pointing back to the first step of the repeat block by step index.
Garmin devices display the workout in their structured training view. The device counts down the step duration, shows the target range, and alerts when you drift outside the zone.
Zwift .zwo export
Zwift workouts are XML files with a .zwo extension. The root element is <workout_file>, and the workout steps are child elements with types like Warmup, SteadyState, IntervalsT, Cooldown, and FreeRide.
A SteadyState element looks like:
<SteadyState Duration="1200" PowerLow="0.88" PowerHigh="0.95" />
Zwift encodes power as a fraction of FTP (0.0–1.0). pacelore's ftp_pct targets map directly — divide by 100. Watts targets are divided by the athlete's FTP at export time to produce the fraction.
IntervalsT handles repeats:
<IntervalsT Repeat="2" OnDuration="1200" OffDuration="300"
OnPower="0.92" OffPower="0.52" />
Zwift's IntervalsT is simpler than the FIT repeat model — it only supports a single on/off pattern. For workouts with more complex repeat structures (e.g., 4x(30s on / 15s off / 2min recovery)), the FIT export handles them cleanly, but the .zwo flattens the outer repeat into separate elements. This is a Zwift format limitation, not a pacelore one.
The 60 workouts
The library is organized by sport (cycling, running, swimming), duration (30min, 45min, 60min, 90min), and intensity (recovery, aerobic, threshold, VO2max, anaerobic). Within each cell of that grid there are one to three sessions. The categorization is displayed as filter tags in the UI.
Running workouts use pace targets (targetType: "pace_pct", a percentage of threshold pace) and heart rate targets. There's no power meter on most runners. The FIT export for running uses HR targets; the .zwo export skips running workouts since Zwift is a cycling platform.
Compliance scoring
After an athlete completes a workout from the library, pacelore matches the resulting activity against the workout definition. The match uses the activity's start time to find a workout assigned to that date, or falls back to the nearest workout within ±2 hours.
The compliance score checks three things:
- Duration compliance: Did the athlete complete at least 90% of the planned duration?
- Target compliance: For each work step, what fraction of the step duration was spent within the target zone? Averaged across all work steps.
- Step completion: Were all required steps completed (not counting cooldown as mandatory)?
The three components are weighted 20/60/20 and produce a 0–100 score. A 95+ means the athlete nailed it. Below 70 usually means the target was too ambitious or the athlete bonked.