# Reppie Authoring Guide

**Version:** 1.1 · **Last updated:** 2026-05-07 · **Audience:** human authors and AI assistants creating exercises and workouts for Reppie.

This document is the single source of truth for how exercises and workouts must be constructed in Reppie. AI assistants generating content for Reppie should treat every rule below as binding — if a request conflicts with a rule, follow the rule and explain the deviation.

---

## 0. Quickstart for AI assistants

If you're an AI assistant who has been asked by a Reppie user to build them a workout, this is your starting point. The user has copied a short prompt to you, and the full instructions live in this guide. Read it, then reply.

**Your job in 5 steps:**

1. **Understand the request.** Read what the user asked for. Identify the target duration, body focus, equipment, level, and any constraints.
2. **Pick a structure.** See §6.3 (warm-up + main blocks + optional finisher + cool-down) and §6.4 (series, target, rest defaults by training goal).
3. **Choose exercises.** Use canonical English names where possible — Reppie reuses existing public exercises by name (§10.9). Lock `muscles[]` to §5.3 and `equipment[]` to §5.4 (both arrays are always English).
4. **Hit the target time.** Compute total time per §3 / §10.8 and iterate until you are within ±5 % of the user's target. Don't under-deliver: a 60-min request answered with a 39-min plan is a failure.
5. **Format your reply.** Exactly as §10.1 describes: one ` ```json ` code block, then a `⏱ Total: …` audit line, then a `👉` reminder line in the user's language. No preamble, no commentary outside the code block.

**Three rules that catch out almost everyone:**

- 🌐 **Language.** Write everything in the user's language EXCEPT the `muscles[]` array, the `equipment[]` array, and the literal `"Rest"` title for pause entries — those are always English. Full rules in §10.5.
- ⏸  **Rest entries are special.** A pause between exercises has exactly two fields: `{ "title": "Rest", "seconds": N }`. Never add `sets`, `reps`, `muscles`, etc. Never translate the title. Full rules in §10.4.
- ⏱  **Time math is mandatory.** A 30-min request must produce a 30-min plan. The most common AI failure is being 20–40 % short because the time formula was skipped. Self-audit per §10.8 before sending.

**The full worked reply example is in §10.10.** When in doubt, copy its shape exactly.

If your tooling allows fetching URLs, you have already done the most important step — reading this guide. Good luck.

---

## 1. What Reppie is

Reppie is an interval-style workout player. A user opens a **workout**, presses Play, and Reppie walks them through a linear sequence of timed steps with audio cues. Reppie is **not** a long-term programming tool (no week-by-week mesocycles, no progressive overload tracking). Each workout is self-contained and is intended to be played end-to-end in a single session.

Two primitives:

- **Exercise** — a reusable, atomic movement (e.g. *Push-Up*, *Goblet Squat*, *Plank*). It has a title, a description, target muscles, equipment, tags, and optional media. It does **not** carry a duration, rep count, or series count.
- **Workout** — an ordered list of items. Each item is either a reference to an exercise (with series/target/rest configured **at the workout level**) or a standalone break (rest between exercises).

The same exercise can appear in many workouts with different parameters. **Series count, target value (time or reps), and inter-series rest are properties of the workout item, never of the exercise.**

---

## 2. Domain glossary

| Term | Meaning |
|---|---|
| **Exercise** | Reusable movement definition. No timing data. |
| **Workout** | Ordered sequence of items + metadata (title, cover, description, visibility). |
| **Workout Item** | One row in the workout list. Either `type: "exercise"` (links to an exercise, has series/target/rest) or `type: "break"` (standalone rest). |
| **Series** | One round of the exercise. An item with `series_count: 3` plays the exercise 3 times with `rest_seconds` between consecutive series. |
| **Break** (item) | A standalone rest **between** exercises. Two flavours: timed (`target_type: "time"`) and open-ended (`target_type: "pause"` — user must press Continue). |
| **Series rest** | The automatic rest between consecutive series **inside one item** (controlled by `rest_seconds` on that item). Not a separate item in the list. |
| **Get Ready** | A 5 s (configurable) lead-in step inserted automatically before the first exercise. The user does not author it. |
| **Step** | A single segment shown by the player. Steps are derived from items: `get_ready`, `exercise`, `series_rest`, `break`, `paused_rest`. |
| **Target type** | `time` (target_value = seconds), `reps` (target_value = number of reps; duration = `reps × seconds_per_rep`), `pause` (open-ended; only for breaks). |

---

## 3. How total workout time is calculated

This is the formula AI must use to hit a user's time budget.

### 3.1 Step expansion

For each enabled item in order, the player produces steps:

1. **Once** at the very start: `get_ready` (default 5 s; user-overridable in profile).
2. For each item:
   - If the item is a **break** with `target_type: "time"` → one `break` step of `target_value` seconds.
   - If the item is a **break** with `target_type: "pause"` → one `paused_rest` step (duration = 0; **does not count toward total time**).
   - If the item is an **exercise** with `series_count = N`:
     - Series 1 → `exercise` step.
     - If `rest_seconds > 0` and not the last series → `series_rest` step.
     - … repeat …
     - Series N → `exercise` step (no series_rest after the last series).

### 3.2 Per-step duration

| Step kind | Duration |
|---|---|
| `get_ready` | profile setting, default **5 s** |
| `exercise` (target_type `time`) | `target_value` seconds |
| `exercise` (target_type `reps`) | `target_value × seconds_per_rep` (default `seconds_per_rep = 3`) |
| `series_rest` | `rest_seconds` of the parent item |
| `break` (timed) | `target_value` seconds |
| `paused_rest` | **0** (excluded from total) |

### 3.3 Item-level formula

For an exercise item:

```
exerciseDuration =
  target_type == "reps"
    ? target_value * seconds_per_rep
    : target_value

itemTime = series_count * exerciseDuration
         + max(0, series_count - 1) * rest_seconds
```

For a timed break item: `itemTime = target_value`.
For a pause break item: `itemTime = 0`.

### 3.4 Workout total

```
totalTime = get_ready_seconds + Σ itemTime  (over enabled items)
```

`paused_rest` steps are **never** included in totals shown to the user, in stats, or in time-budget planning. If the user asks for "a 25-minute workout," design without relying on pause breaks to fill time.

### 3.5 Worked example

User asks: *"30-minute full-body workout."* Get Ready = 5 s.

| # | Item | Series | Target | Rest | Item time |
|---|---|---|---|---|---|
| 1 | Jumping Jacks (warm-up) | 1 | 60 s | 0 | 60 |
| 2 | Break (timed) | — | 30 s | — | 30 |
| 3 | Goblet Squat | 3 | 12 reps × 3 s/rep | 30 s | 3·36 + 2·30 = 168 |
| 4 | Push-Up | 3 | 10 reps × 3 s/rep | 30 s | 3·30 + 2·30 = 150 |
| 5 | Bent-Over Row | 3 | 10 reps × 3 s/rep | 30 s | 3·30 + 2·30 = 150 |
| 6 | Break (timed) | — | 60 s | — | 60 |
| 7 | Plank | 3 | 40 s | 20 s | 3·40 + 2·20 = 160 |
| 8 | Russian Twist | 3 | 30 s | 20 s | 3·30 + 2·20 = 130 |
| 9 | Break (timed) | — | 30 s | — | 30 |
| 10 | Cool-down stretch (timed) | 1 | 90 s | 0 | 90 |

Sum = 60 + 30 + 168 + 150 + 150 + 60 + 160 + 130 + 30 + 90 = **1028 s**.
Plus Get Ready 5 s ⇒ **1033 s ≈ 17:13**. Below budget — add work.

The AI must iterate on parameters (series, target, rest) until `totalTime` is within ±5 % of the requested time, **without** padding with pause breaks.

### 3.6 Field hard limits (DB constraints)

These will reject the row if violated — never produce values outside these ranges:

| Field | Range |
|---|---|
| Exercise `title` | 1 – 100 chars |
| Exercise `description` | 0 – 2 000 chars |
| Workout `title` | 1 – 100 chars |
| Workout `description` | 0 – 5 000 chars |
| Workout item `target_value` | 1 – 3 600 |
| Workout item `seconds_per_rep` | 1 – 60 (default 3) |
| Workout item `series_count` | 1 – 99 |
| Workout item `rest_seconds` | 0 – 600 |
| General tag `name` | 1 – 30 chars, lowercase, max 10 per exercise/workout |
| Equipment tag `name` | from fixed seed list (§5.4), max 15 per exercise |
| Muscle | from fixed seed list (§5.3), no per-exercise cap but typically 1–4 |

---

## 4. Authoring an Exercise

### 4.1 Title

- 1–100 chars, but aim for **2–5 words**. Title Case.
- Use the canonical English name of the movement. *Push-Up*, not *push ups*.
- **Do not** include numbers (no series, no reps, no time): wrong: *"30s Plank"*; right: *"Plank"*.
- **Do not** include difficulty modifiers unless they actually change the movement: wrong: *"Easy Push-Up"*; right: *"Knee Push-Up"* (different movement).
- **Do not** include equipment unless it changes the movement: right: *"Dumbbell Bench Press"*, *"Barbell Bench Press"* (different exercises). Wrong: *"Push-Up on Yoga Mat"* (the mat is incidental).

### 4.2 Description — required structure

The description is rich text (HTML, rendered with a markdown-style renderer). Maximum **2 000 chars**. Use the following sections in this order. Sections marked **required** must always be present; sections marked optional are added when relevant.

#### 4.2.1 Setup *(required)*

One short paragraph describing the starting position. Body alignment, grip, foot placement, equipment placement. 2–4 sentences.

#### 4.2.2 Execution *(required)*

A numbered list of the movement phases. 3–6 steps. Use imperative voice ("Lower your hips…"). Each step is one sentence.

#### 4.2.3 Breathing *(optional but recommended)*

One sentence: when to inhale, when to exhale.

#### 4.2.4 Common mistakes *(required)*

A bulleted list of 2–4 frequent errors and how to fix them. Format: `**Mistake** — fix`.

#### 4.2.5 Variations *(optional)*

A bulleted list of 1–3 easier or harder variants. **Do not** describe alternative exercises that should be their own entry; only progressions/regressions of the same movement pattern.

#### 4.2.6 Safety *(optional)*

One short paragraph or bulleted list. Include only when there is a real risk (load-bearing, joints, balance). Skip for low-risk exercises.

### 4.3 Description — what NOT to include

- ❌ Number of series, reps, or duration. Those belong on the workout item.
- ❌ "In this workout we will…" — descriptions are reused across workouts.
- ❌ Marketing language ("blast your abs," "torch fat"). Be neutral and instructional.
- ❌ External links.
- ❌ Brand names of equipment unless the equipment is brand-specific (e.g. *TRX*).
- ❌ Footnotes, citations, references.

### 4.4 Media

- One file per exercise, optional but strongly preferred.
- `media_type`: `image`, `gif`, or `youtube`.
- For `image` / `gif`: square or 4:5 portrait, ≤ 5 MB. Show the full body in the active phase of the movement. No watermarks, no text overlays, no logos.
- For `youtube`: paste a youtube.com or youtu.be URL. The clip should be ≤ 60 s and isolated to this exercise (not a workout compilation).

### 4.5 Muscles

- Pick from the fixed seed list (§5.3). No free text.
- Include every muscle that does meaningful work. Typically 1–4 entries.
- No primary/secondary distinction — Reppie does not model that.
- Do **not** add muscles that only stabilize (e.g. don't tag *abs* on every standing exercise).

### 4.6 Equipment (`+` tags)

- Pick from the fixed seed list (§5.4). No free text on new authoring (legacy free-form entries are deprecated).
- Include only equipment that is **required** to perform the exercise. *Yoga mat* counts only when the mat materially affects the exercise (floor work). Shoes, water bottles, etc. never count.
- If the exercise can be done with no equipment at all, set `equipment_free: true` and add no tags.

### 4.7 General tags (`#` tags)

- Lowercase, 1–30 chars, max 10 per exercise.
- Use for cross-cutting attributes: movement pattern (`#push`, `#pull`, `#hinge`, `#squat`, `#carry`, `#rotation`), plane (`#unilateral`, `#bilateral`), pace (`#explosive`, `#isometric`), context (`#warmup`, `#cooldown`, `#mobility`, `#stretch`).
- Do **not** duplicate information already encoded by muscles or equipment.
- Do **not** use tags as full-text descriptions. Three to seven tags is a healthy range.

---

## 5. Reference data

### 5.1 Step kinds (player)

`get_ready`, `exercise`, `series_rest`, `break`, `paused_rest`. Authors do not pick step kinds directly — the player derives them from items.

### 5.2 Item shape (the data AI emits)

```ts
// type "exercise"
{
  type: "exercise",
  exercise_id: "<uuid of the Exercise row>",
  target_type: "time" | "reps",
  target_value: number,             // seconds OR reps, 1..3600
  seconds_per_rep: number | null,   // required if target_type == "reps", default 3
  series_count: number,             // 1..99
  rest_seconds: number,             // 0..600, between series
  order_index: number,
}

// type "break"
{
  type: "break",
  target_type: "time" | "pause",
  target_value: number,             // seconds, ignored when target_type == "pause"
  series_count: 1,
  rest_seconds: 0,
  order_index: number,
}
```

### 5.3 Muscles (fixed seed list, 19 entries)

Names are lowercase identifiers and must be used **exactly as written**, in English, even when the rest of the workout is authored in another language (see §10.5).

| Group | Muscles |
|---|---|
| Chest | chest |
| Back | lats, traps, rhomboids, erector spinae |
| Shoulders | front delts, side delts, rear delts |
| Arms | biceps, triceps, forearms |
| Core | abs, obliques |
| Legs | quads, hamstrings, glutes, calves, adductors, abductors |

### 5.4 Equipment (fixed seed list, ~30 entries)

| Group | Equipment |
|---|---|
| Free Weights | barbell, dumbbells, kettlebell, weight plates, ez bar |
| Machines | cable machine, smith machine, leg press, lat pulldown, pec deck |
| Cardio | treadmill, exercise bike, rowing machine, elliptical, jump rope |
| Bars & Racks | pull-up bar, dip station, bench, squat rack, rings, trx |
| Accessories | resistance bands, yoga mat, foam roller, medicine ball, ab wheel, stability ball, sliders, weighted vest, box |

---

## 6. Authoring a Workout

### 6.1 Title

- 1–100 chars, **3–8 words** is the sweet spot. Title Case.
- Should signal scope and theme: *"Full-Body 25-Minute Conditioning"*, *"Lower-Body Strength · Dumbbells Only"*.
- Avoid clickbait, emojis, and exclamation marks.

### 6.2 Description

- Up to 5 000 chars but aim for **120–500 chars**. Plain paragraph(s).
- Cover: who it's for, expected duration, equipment summary, and any prerequisite (e.g. "Familiarity with hinge pattern"). No coaching cues — those belong on exercises.
- Optional: a one-line "Structure" summary (e.g. "5 min warm-up · 3 strength supersets · 5 min finisher · cool-down").

### 6.3 Item ordering — recommended structure

A well-constructed workout follows this template (skip sections that don't apply):

1. **Warm-up (3–8 min):** dynamic, low-load. Either a single timed exercise (e.g. jumping jacks 60 s) or 2–4 short exercises with no series rest.
2. **Main block(s) (15–40 min):** strength or conditioning sets. Group by movement pattern or muscle. Place a **timed break** (30–90 s) between blocks, never between every single item.
3. **Finisher (optional, 2–5 min):** higher-intensity capstone (AMRAP-style or burnout).
4. **Cool-down (2–5 min):** stretching/mobility, single timed exercises, no series, low rest.

### 6.4 Series, target, rest — rules of thumb

| Goal | Target type | Target value | Series | Rest |
|---|---|---|---|---|
| Strength (compound) | reps | 4–8 reps | 3–5 | 90–180 s |
| Hypertrophy | reps | 8–12 reps | 3–4 | 60–90 s |
| Conditioning / HIIT | time | 20–40 s | 3–5 | 10–30 s |
| Endurance | time | 60–120 s | 1–3 | 30–60 s |
| Mobility / stretch | time | 30–60 s | 1 | 0 |
| Core hold | time | 20–60 s | 2–4 | 20–45 s |

`seconds_per_rep` defaults to **3**. Use 2 for fast/explosive (kettlebell swing, jumping squat) and 4–5 for slow tempo (tempo squat, slow eccentric pull-up).

### 6.5 When to use which break type

- **No break** between sequential items in the same superset/circuit (rest is inside the items via `rest_seconds`, or the next exercise's `get_ready`-like phase serves as transition).
- **Timed break** (`target_type: "time"`) between blocks (e.g. between strength block and conditioning block). Typical 30–120 s.
- **Pause break** (`target_type: "pause"`) only when transition is unpredictable: equipment swap that needs setup, intermission for water, between halves of a long session. Pause breaks do not count toward total time, so do not use them to manage pacing.

### 6.6 Computed vs. extra metadata

These are computed automatically from items and are read-only on the workout:

- **Total duration** — see §3.
- **Workout muscles** — union of muscles across all linked exercises.
- **Workout equipment (computed part)** — union of equipment across all linked exercises.

The workout itself also has its own editable bag of:

- **Workout extra equipment** — e.g. `+towel`, `+water-bottle` that no exercise references but is needed for the session.
- **Workout tags** — `#hiit`, `#strength`, `#mobility`, `#beginner`, `#advanced`, location-style tags like `#home` / `#gym`.

The user sees `computed ∪ extra` for both equipment and muscles; the AI should populate **only** the extra bag, never duplicating equipment that is already on an exercise.

### 6.7 Cover image

- 1:1 (square) for grid thumbnails; the system also stores a "full" 16:9 variant. ≤ 5 MB. No text overlays.
- Optional. If absent, a default placeholder is used.

### 6.8 Visibility

- `private` (default) — owner-only.
- `public` — appears in the public catalogue. Only set public when the workout follows every rule in this guide.

---

## 7. Hitting a target time (algorithm)

When the user requests a workout of duration `T_target`:

1. Pick a structure template (warm-up + N main blocks + optional finisher + cool-down).
2. Allocate budget: e.g. warm-up 10 % of T, cool-down 10 %, transitions/breaks 5 %, main blocks the remainder.
3. For each main block, choose 2–4 exercises. Default to `series_count = 3`, `rest_seconds = 30–60 s` for conditioning, `60–120 s` for strength.
4. Compute the running total using the formula in §3.3–§3.4.
5. If under budget by > 5 %: increase series, lengthen target, or add an exercise.
6. If over budget by > 5 %: reduce series, shorten target, or drop an exercise. **Do not** trim warm-up or cool-down below the minimums in §6.3 unless `T_target < 8 min`.
7. Reject any plan that uses pause breaks to absorb time: pauses don't count and the user will end up over time.

Aim for `|total − T_target| ≤ 5 %` (e.g. ±90 s for a 30-minute workout).

---

## 8. Anti-patterns (do not do this)

- ❌ Placing series/reps/time in the exercise title or description.
- ❌ Putting the same exercise twice in a workout to bump volume — increase `series_count` instead. (Exception: it appears in two distinct blocks for a deliberate reason — be sure.)
- ❌ Relying on pause breaks to reach the target duration.
- ❌ Free-form equipment text. Always pick from the seed list.
- ❌ Tagging every exercise with all stabilizers as muscles.
- ❌ "Combo" exercises that bundle two movements ("Burpee + Push-Up + Squat") — split them or use the canonical compound name (just *Burpee*).
- ❌ Mixing languages within the same description.
- ❌ Empty rest (`rest_seconds: 0`) between strength sets above 4 reps — gives unrealistic pacing.
- ❌ `series_count > 6` for a single exercise — almost always reflects bad structure; split into two items or use a circuit.

---

## 9. Examples

These are full, copy-correct examples. Use them as templates.

### 9.1 Example A — Exercise (`reps`-based, equipped, single muscle group)

**Title:** Goblet Squat

**Description:**

> **Setup**
> Stand with feet shoulder-width apart, toes turned out 10–15°. Hold a single dumbbell or kettlebell vertically against your chest with both hands cupping the top bell.
>
> **Execution**
> 1. Brace your core and pull your shoulder blades down and back.
> 2. Push your hips back and bend your knees, keeping your chest tall.
> 3. Descend until your hip crease is below your knee, or as far as you can without losing the neutral spine.
> 4. Drive through your mid-foot to stand back up, finishing with hips fully extended.
>
> **Breathing**
> Inhale on the way down, exhale as you stand up.
>
> **Common mistakes**
> - **Knees collapsing inward** — actively press your knees out over your second toe.
> - **Heels lifting** — sit your weight back; if your ankles are tight, place small plates under your heels.
> - **Rounding the lower back at the bottom** — stop the descent at the depth where you can keep a neutral spine and progress over time.
>
> **Variations**
> - Easier: bodyweight squat to the same depth.
> - Harder: tempo goblet squat (3 s descent, 1 s pause, 1 s up).

**Muscles:** quads, glutes, adductors, abs
**Equipment (`+`):** dumbbells, kettlebell *(either is acceptable — list both)*
**Tags (`#`):** `#squat`, `#bilateral`, `#strength`, `#beginner-friendly`
**equipment_free:** false
**Media:** GIF, square, full-body side angle.

**Workout-side parameters when used in a workout:** typically `target_type: "reps"`, `target_value: 8–12`, `seconds_per_rep: 3` (or 5 for tempo variant), `series_count: 3–4`, `rest_seconds: 60–90`.

---

### 9.2 Example B — Exercise (`time`-based, no equipment, isometric)

**Title:** Side Plank

**Description:**

> **Setup**
> Lie on your right side with your right forearm on the floor, elbow directly under your shoulder. Stack your feet, knees, hips, and shoulders in one line. Top hand on your hip or extended toward the ceiling.
>
> **Execution**
> 1. Press your forearm into the floor and lift your hips so that your body forms a straight line from ankles to head.
> 2. Squeeze your obliques and glutes; keep your bottom hip from sagging.
> 3. Hold the position; breathe steadily.
> 4. To finish, lower the hip under control. Repeat on the opposite side.
>
> **Breathing**
> Steady, shallow breaths through the nose. Do not hold your breath.
>
> **Common mistakes**
> - **Hips sagging** — actively drive the hip away from the floor.
> - **Shoulder collapsing into the elbow** — push the floor away to keep the shoulder packed.
> - **Head dropping** — keep your neck long and gaze forward.
>
> **Variations**
> - Easier: drop the bottom knee to the floor (modified side plank).
> - Harder: lift the top leg to a hover, or add a hip dip on each rep.
>
> **Safety**
> Skip if you have an unresolved shoulder injury on the supporting side.

**Muscles:** obliques, abs, side delts
**Equipment (`+`):** *(none)*
**Tags (`#`):** `#core`, `#isometric`, `#unilateral`, `#bodyweight`
**equipment_free:** true
**Media:** image, side-on, full-body.

**Workout-side parameters:** `target_type: "time"`, `target_value: 30–60`, `series_count: 2–3` per side, `rest_seconds: 20–30`. Place once per side as two separate items, or one item and rely on the user to switch sides at the next series — prefer two items for clarity.

---

### 9.3 Example C — Workout (30 min, full-body, dumbbells)

**Title:** Full-Body Strength · Dumbbells · 30 min
**Visibility:** public
**Description:**

> A 30-minute full-body strength session for intermediate trainees with a single pair of dumbbells. Three compound supersets bookended by a dynamic warm-up and a stretching cool-down. Expect roughly equal work on push, pull, and lower-body patterns.
>
> **Structure:** 4 min warm-up · 3 strength supersets (~6 min each) · 60 s break · 4 min cool-down.

**Workout extra equipment:** *(none — all equipment is on exercises)*
**Workout tags (`#`):** `#strength`, `#full-body`, `#dumbbells`, `#intermediate`, `#home`

**Items:**

| # | Type | Exercise / Break | target_type | target_value | sec/rep | Series | Rest |
|---|---|---|---|---|---|---|---|
| 1 | exercise | Jumping Jacks | time | 60 | — | 1 | 0 |
| 2 | exercise | Bodyweight Squat | reps | 12 | 3 | 1 | 0 |
| 3 | exercise | Arm Circles | time | 30 | — | 1 | 0 |
| 4 | break (timed) | — | time | 30 | — | — | — |
| 5 | exercise | Goblet Squat | reps | 10 | 3 | 3 | 75 |
| 6 | exercise | Dumbbell Bent-Over Row | reps | 10 | 3 | 3 | 75 |
| 7 | break (timed) | — | time | 60 | — | — | — |
| 8 | exercise | Dumbbell Romanian Deadlift | reps | 10 | 3 | 3 | 75 |
| 9 | exercise | Dumbbell Bench Press | reps | 8 | 3 | 3 | 90 |
| 10 | break (timed) | — | time | 60 | — | — | — |
| 11 | exercise | Dumbbell Push Press | reps | 8 | 3 | 3 | 75 |
| 12 | exercise | Plank | time | 45 | — | 3 | 20 |
| 13 | break (timed) | — | time | 30 | — | — | — |
| 14 | exercise | Cool-down Stretch (timed) | time | 240 | — | 1 | 0 |

Total time check (Get Ready 5 s):

- Warm-up: 60 + 24 + 30 = 114 s
- Break: 30
- Superset 1: 3·30 + 2·75 + 3·30 + 2·75 = 90 + 150 + 90 + 150 = 480
- Break: 60
- Superset 2: 3·30 + 2·75 + 3·24 + 2·90 = 90 + 150 + 72 + 180 = 492
- Break: 60
- Superset 3: 3·24 + 2·75 + 3·45 + 2·20 = 72 + 150 + 135 + 40 = 397
- Break: 30
- Cool-down: 240

Subtotal = 114 + 30 + 480 + 60 + 492 + 60 + 397 + 30 + 240 = **1903 s**
Plus Get Ready 5 ⇒ **1908 s ≈ 31:48**. Within 6 % of 30 min target — acceptable. To tighten, drop one set from Superset 3 (saves ~95 s).

---

## 10. Output contract for external AI assistants

This section is the contract for AI assistants (Claude, ChatGPT, Gemini, …) that receive a Reppie workout request through the user's **Create with AI** flow. The user pastes a short prompt into your chat, you reply in the format below, the user copies your reply back into Reppie, and Reppie's parser materializes the workout.

The contract is deliberately simpler than the internal DB schema (§5.2). The parser handles ID resolution, exercise lookup, and break creation.

### 10.1 Reply layout

Your full reply must consist of, in this exact order:

1. A single fenced ```` ```json ```` code block containing the workout object (see §10.2).
2. One blank line.
3. A time-audit line: `⏱ Total: <hh:mm> (target <hh:mm>, ±<n>%)` — the running total computed per §3 / §10.8.
4. One blank line.
5. One reminder line, starting with the 👉 emoji, **in the same language as the user's request**, telling the user to copy your full message and return to Reppie.
   - English: `👉 Now copy the message above and return to Reppie.`
   - Czech:   `👉 Nyní zkopíruj zprávu výše a vrať se zpět do Reppie.`

Do **not** add a preamble, table of contents, or commentary outside the code block. The user just wants the workout — keep your reply tight.

### 10.2 JSON shape

```json
{
  "workout": {
    "title":       "Workout name in the user's language (1–100 chars)",
    "description": "60–150 words of plain prose, in the user's language (see §10.6)",
    "tags":        ["3–8 lowercase tags, no #/+ prefix, max 30 chars each"]
  },
  "exercises": [
    {
      "title":           "Exercise name in user's language (1–100 chars)",
      "description":     "Optional short note (form cue, modification) — ≤ 2000 chars",
      "equipment_free":  true,
      "equipment":       ["names from §5.4 — only when equipment_free is false"],
      "muscles":         ["names from §5.3 — always English, lowercase"],
      "tags":            ["lowercase tags, max 10, max 30 chars each"],
      "sets":            3,
      "reps":            12,
      "seconds":         45,
      "seconds_per_rep": 2.1,
      "rest_seconds":    60
    },
    { "title": "Rest", "seconds": 90 }
  ]
}
```

Field semantics for an exercise entry:

- `sets` — how many times the block is repeated (1–99).
- `reps` — repetitions per set, for **rep-based** exercises only (1–999). Omit for time-based.
- `seconds` — duration per set in seconds, for **time-based** exercises only (1–3600). Omit for rep-based.
- `seconds_per_rep` — **REQUIRED** with `reps`. Precise duration of one full rep cycle in seconds (0.5–10.0, one decimal). See §10.3.
- `rest_seconds` — rest **between consecutive sets of the same exercise** (0–600). NOT a pause between different exercises — see §10.4.

A regular exercise must have `title`, `sets`, `rest_seconds`, plus EITHER `reps` + `seconds_per_rep` OR `seconds`. A Rest entry has only the two fields shown — no others (see §10.4).

### 10.3 Reps vs time

PREFER TIME (use `seconds`) for anything naturally measured by duration: plank, wall sit, dead hang, farmer's carry, mountain climbers, jump rope, running, cycling. If there's no clean "counting moment," use time.

USE REPS (use `reps` + `seconds_per_rep`) only for movements with discrete countable repetitions: push-up, squat, bicep curl, pull-up, lunge, row.

`seconds_per_rep` is the precise total duration of **one full rep cycle** (one decimal place), counting BOTH directions:

| Movement   | Down | Up  | seconds_per_rep |
|------------|------|-----|-----------------|
| Push-up    | 1.1  | 1.0 | 2.1             |
| Squat      | 1.5  | 1.5 | 3.0             |
| Bicep curl | 1.0  | 1.1 | 2.1             |
| Pull-up    | 1.5  | 1.5 | 3.0             |

Be realistic. Don't round to an integer just because it's neat.

### 10.4 Rest periods (CRITICAL — most common mistake)

There are **two** different ways to express rest. Use the right one.

**(A) `rest_seconds` on an exercise** — automatic rest *between consecutive sets of the same exercise*. NOT a pause between different exercises. If `sets: 3` and `rest_seconds: 60`, the user does the exercise, rests 60 s, repeats, rests 60 s, repeats.

**(B) Rest entry** — a standalone pause *between different exercises*. Special item:

```json
{ "title": "Rest", "seconds": 90 }
```

🚫 A Rest entry has **exactly two fields and nothing else**. No `sets`, `reps`, `muscles`, `equipment`, `tags`, `description`. If you add those, Reppie tries to create a real exercise called "Rest" / "Pauza" / "Odpočinek" / "Descanso" / etc. and the import gets confused.

- ❌ `{ "title": "Pauza", "description": "Krátká pauza", "seconds": 60 }`
- ❌ `{ "title": "Rest", "sets": 1, "seconds": 60, "muscles": [] }`
- ✅ `{ "title": "Rest", "seconds": 60 }`

🌐 **Language exception** for Rest entries: the `title` must always be the literal English word `"Rest"`, even when the rest of the workout is in another language. Reppie uses this exact string as a marker.

### 10.5 Language rules (REQUIRED)

Generate **all content in the same language as the user's request**. This applies to:

- workout title
- workout description
- workout tags
- exercise titles
- exercise descriptions
- exercise tags

Always literal English, never translated:

1. Each item in the `muscles` array — fixed DB identifiers from §5.3.
2. Each item in the `equipment` array — fixed DB identifiers from §5.4.
3. The `title` field of a Rest entry — always literally `"Rest"` (see §10.4).

The reminder line at the end of your reply (the one starting with 👉) is also in the user's language — see §10.1.

### 10.6 Workout description (REQUIRED)

The `workout.description` field is what the user reads on the workout's detail page. **Always provide it.** Make it useful — not generic boilerplate.

Aim for 3–6 sentences (~60–150 words) covering, where relevant:

- WHAT the workout is and its character (full-body strength, HIIT, mobility…)
- WHO it's for (beginner / intermediate / advanced; goals: hypertrophy, fat loss, endurance)
- HOW to use it (recommended frequency, when to rest, when to progress)
- WHAT TO EXPECT physically (intensity, pacing, total time)
- Practical tips: warm-up, cool-down, form cues, common mistakes

Plain prose, friendly second person ("you"), no markdown, no bullet points, no emojis, no exercise-by-exercise breakdown (the exercise list speaks for itself). Don't open with "This workout…" — vary the opening. Don't repeat the title verbatim.

### 10.7 Workout tags (REQUIRED)

The `workout.tags` array is **3–8 short, descriptive labels** — think hashtags. Pick what genuinely fits this specific workout:

- Body focus: `full body`, `upper body`, `lower body`, `push day`, `core`
- Goal: `strength`, `hypertrophy`, `fat loss`, `endurance`, `mobility`, `rehab`
- Style: `hiit`, `circuit`, `supersets`, `amrap`, `emom`, `tabata`, `yoga`
- Level: `beginner`, `intermediate`, `advanced`
- Constraints: `no equipment`, `home`, `gym`, `outdoor`
- Duration: `15 min`, `30 min`, `45 min`, `60 min`

Rules: lowercase, no leading `#` or `+`, max 30 chars each, max 10 tags total. Avoid synonyms of one another.

### 10.8 Total time — mandatory self-audit

Most common failure mode: AI output is 20–40 % shorter than requested. **You MUST compute and verify.**

For each exercise entry:

```
one_set_seconds = (reps used) ? reps * seconds_per_rep : seconds
item_time       = sets * one_set_seconds + (sets - 1) * rest_seconds
```

For each Rest entry: `item_time = seconds`.

```
total_seconds = 5 (built-in Get Ready) + Σ item_time
```

Self-audit:

1. Extract the user's requested duration (e.g. `30 min`, `1 hour`, `20 minut`, `hodina`, `45 минут`, `media hora`).
2. Build the plan, then compute `total_seconds`.
3. If `|total − requested| > 5 %`, REVISE — add/remove sets, lengthen/shorten targets, add/drop exercises — until you are within ±5 %.
4. **Do not under-deliver.** A 60-min request answered with a 39-min plan is a failure — keep iterating.
5. Print the audit line described in §10.1.

Common mistakes: forgetting to multiply `reps × seconds_per_rep`; forgetting `(sets − 1) × rest_seconds`; forgetting that standalone Rest entries also count.

### 10.9 Reuse over creation

The Reppie parser will fuzzy-match exercise titles (case-insensitive, accent-stripped, singularized) against the user's existing library before creating new entries. So:

- Use the canonical English name of common movements (`Push-Up`, `Squat`, `Plank`) when the user's request is in English — they'll match existing public exercises and reuse media/descriptions.
- For non-English requests, use the natural-language name in the user's language — Reppie creates new entries on first use.

### 10.10 Worked example (complete reply)

The following is exactly what your reply should look like — JSON code block, blank line, audit line, blank line, reminder line. Nothing else.

````
```json
{
  "workout": {
    "title": "Full Body Strength",
    "description": "A balanced full-body session that hits the main movement patterns — push, brace, hinge — in three short blocks. Built for intermediate trainees who want measurable strength gains without going to a gym; use it 2–3× per week with at least one rest day between sessions. Expect to feel worked but not destroyed: the rep ranges target hypertrophy and tendon resilience while the rests keep your heart rate up enough to count as a light conditioning stimulus. Warm up for five minutes with mobility before starting and cool down with light stretching afterwards.",
    "tags": ["full body", "strength", "hypertrophy", "intermediate", "16 min"]
  },
  "exercises": [
    {
      "title": "Push-up",
      "description": "Standard push-up, keep core tight and elbows tucked at ~45°.",
      "equipment_free": true,
      "muscles": ["chest", "triceps"],
      "tags": ["bodyweight", "push"],
      "sets": 3,
      "reps": 12,
      "seconds_per_rep": 2.1,
      "rest_seconds": 60
    },
    { "title": "Rest", "seconds": 90 },
    {
      "title": "Plank",
      "equipment_free": true,
      "muscles": ["abs"],
      "tags": ["isometric"],
      "sets": 3,
      "seconds": 45,
      "rest_seconds": 45
    },
    {
      "title": "Barbell Squat",
      "equipment_free": false,
      "equipment": ["barbell", "squat rack"],
      "muscles": ["quads", "glutes"],
      "tags": ["strength"],
      "sets": 4,
      "reps": 8,
      "seconds_per_rep": 3.0,
      "rest_seconds": 120
    }
  ]
}
```

⏱ Total: 16:12 (target 16:00, +1%)

👉 Now copy the message above and return to Reppie.
````

Math for the audit:

- Get Ready: 5
- Push-up: 3 × (12 × 2.1) + 2 × 60 = 75.6 + 120 = 195.6
- Rest: 90
- Plank: 3 × 45 + 2 × 45 = 135 + 90 = 225
- Barbell Squat: 4 × (8 × 3.0) + 3 × 120 = 96 + 360 = 456
- **Total: 5 + 195.6 + 90 + 225 + 456 = 971.6 s ≈ 16:12**

---

## 11. Changelog

| Version | Date | Notes |
|---|---|---|
| 1.1 | 2026-05-07 | Added §0 (AI quickstart) and §10 (output contract for external AI assistants). Reconciled §5.3 with the consolidated `chest` muscle. |
| 1.0 | 2026-05-06 | Initial guide. |
