--- name: zwift-workout description: Create, preview, edit, validate and publish Zwift .zwo workout files for cycling (bike-only). Use when the user asks to build a Zwift workout, generate a .zwo file, convert a training idea into a Zwift session, preview/adjust an existing workout, or publish a workout for import into Zwift. --- # 🚴 Zwift Workout (.zwo) Builder Builds valid Zwift `.zwo` files from a small JSON spec, renders a human-readable preview for confirmation, and publishes the finished file. **Bike-only.** ## When to use - "Bau mir ein Zwift-Workout …" / "Create a .zwo for 5Γ—3min VO2max" - "Zeig mir das Workout" / "Γ€nder die Intervalle auf 4Γ—5min" - "Lade das Workout in Zwift" (publish to hosting for import) ## Core workflow (always follow this order) 1. **Draft the spec.** Either pick a deterministic template (`zwo.py templates` β†’ `endurance | sweetspot | vo2max | sprint`) or write a JSON spec from the user's request. Keep power as FTP fractions (`0.88` = 88 %, `1.15` = 115 %); durations in **seconds**. 2. **Render the preview and ask for confirmation.** Run `python3 {baseDir}/scripts/zwo.py render ` (or `build … --preview`) and show the table to the user. **Do not publish before the user confirms.** 3. **Apply changes.** Edit the JSON spec and re-render; repeat until confirmed. 4. **Build + publish on confirmation.** Run `python3 {baseDir}/scripts/zwo.py build -o .zwo --publish`. This validates, writes the file, and uploads it via the `hosting` skill (`https://hosting.haumdaucher.de/docs/…`). 5. **Deliver.** Post the hosting link (and the `.zwo` as `MEDIA:` if the user wants the file directly). Optionally offer a QR code (hosting skill rule). 6. **File the result into the wiki** under `syntheses/` or `concepts/` per the Knowledge Wiki protocol (see below). ## JSON spec format ```json { "name": "VO2 Max 5x3", "author": "IT Guy", "description": "5 Γ— 3 min @ 115 % FTP, 3 min Pause.", "sportType": "bike", "tags": ["vo2max", "structured"], "steps": [ {"type": "Warmup", "duration": 600, "powerLow": 0.50, "powerHigh": 0.75}, {"type": "IntervalsT", "repeat": 5, "onDuration": 180, "offDuration": 180, "onPower": 1.15, "offPower": 0.55}, {"type": "Cooldown", "duration": 300, "powerLow": 0.75, "powerHigh": 0.40} ] } ``` ### Supported step types (bike) | `type` | Required fields | Optional | |---|---|---| | `Warmup` | `duration`, `powerLow`, `powerHigh` | `cadence` | | `Cooldown` | `duration`, `powerLow`, `powerHigh` | `cadence` | | `SteadyState` | `duration`, `power` | `cadence` | | `Ramp` | `duration`, `powerLow`, `powerHigh` | β€” | | `IntervalsT` | `repeat`, `onDuration`, `offDuration`, `onPower`, `offPower` | `cadence`, `cadenceResting` | | `FreeRide` | `duration` | `flatRoad` | | `MaxEffort` | `duration` | β€” | | `SolidState` | `duration`, `power` | β€” | | `RestDay` | β€” | β€” | Power values are **fractions of FTP**; duration is in **seconds**. See `references/zwo-format.md` for the full tag/attribute reference. ## CLI ```bash python3 {baseDir}/scripts/zwo.py templates # list templates python3 {baseDir}/scripts/zwo.py from-template vo2max --title "VO2 Max 5x3" --publish python3 {baseDir}/scripts/zwo.py render my.json # preview only python3 {baseDir}/scripts/zwo.py build my.json -o out.zwo --preview --publish python3 {baseDir}/scripts/zwo.py validate existing.zwo # check a file python3 {baseDir}/scripts/zwo.py parse existing.zwo --json # .zwo -> spec (editable) ``` ## Guardrails - **Power range:** `0.20 – 1.50` FTP. Outside β†’ hard error (catches unit mistakes). - **Durations:** integer seconds, `> 0`; `< 5 s` and `> 4 h` produce warnings. - **Structure:** exactly one ``, root ``, `name` required, `sportType` must be `bike`. - **Round-trip check:** output XML is re-parsed with `xml.etree` before writing; text fields are escaped, so umlauts/quotes never corrupt the file. - **Never publish without explicit user confirmation** of the rendered preview. ## Reference & fixtures - `references/zwo-format.md` β€” condensed tag/attribute reference (offline copy). - `assets/example-vo2max.zwo` β€” known-good fixture; use for regression tests. ## Knowledge Wiki filing After a workout is confirmed and published, file it per the Karpathy LLM Wiki protocol: create/update a `concepts/` or `syntheses/` page recording the workout name, structure, power targets, hosting URL, and rationale, then append a `log.md` entry (`ingest`/`synthesis`). Cross-link the Fitness agent page. ## Inter-agent synergy `agents/fitness.md` owns training intent (Tacx Flux 2, FTP 275 W). This skill is the execution layer for structured cycling sessions. For evidence-based protocols (e.g. SIT 30 s / 4 min), consult the Knowledge Wiki first via `wiki_search`. A future refactor can share one spec module with `garmin-workout` (one source spec β†’ Garmin workout + `.zwo`).