---
name: prokope
description: Build and review strength training programs for Prokope, an Apple Watch and iPhone workout logger. Use when someone wants a training program for Prokope (from scratch, from notes or a PDF, or by changing their current one), or shares a Prokope export (prokope-latest.json, prokope-YYYY-MM.json) to review progress, stalls, attendance, body weight and measurements.
---

# Prokope

Prokope logs workouts on Apple Watch and iPhone. It imports a **program JSON** and exports an **export JSON**. Both formats are in [references/schema.md](references/schema.md); read it before writing or reading either file. The exercise catalog is [references/exercises.json](references/exercises.json).

On a Mac with the source repo (`~/GitHub/prokope`), the same files live there, along with the bundled programs in `programs/`. The latest export from the iPhone is at `~/Library/Mobile Documents/iCloud~dev~apvl~prokope/Documents/prokope-latest.json` when iCloud Drive is on. Otherwise, ask for it: Prokope › Settings › Export data.

## Make a program

The person usually isn't a coach and may not know the vocabulary. Your job is to ask the few questions that matter, fill in the rest with good defaults, and show them a plan they understand before writing any JSON.

### 1. Interview, a little at a time

Ask one or two questions per message, never a form. Offer short choices with a suggested default, and accept "you decide". Skip anything they already told you (a PDF, notes, an old program, an export). Stop asking once you can make a sensible plan.

1. **Goal**: get stronger, build muscle, lose fat while keeping muscle, general fitness, or a skill (muscle-up, handstand, L-sit, front lever).
2. **Experience**: new to lifting, under a year, 1 to 3 years, more than 3.
3. **Schedule**: how many days a week, which days if fixed, and roughly how long a session can be.
4. **Where and with what**: full gym, home gym (list it), dumbbells only, bodyweight only.
5. **Limits**: injuries, pain, movements to avoid.
6. **Taste** (one quick question): exercises they love or won't do. Optional: current working weights on the big lifts.

### 2. Decide with defaults

| They said | Default |
|---|---|
| New or under a year | Full body, 3 days (Mon, Wed, Fri) |
| 4 days | Upper / lower twice |
| 5 to 6 days | Push / pull / legs, or a split that fits their goal |
| About 45 min | 4 to 5 exercises a day; 60 min: 5 to 6; 75+: 6 to 7 |
| Get stronger | `repCap` 6, `lowRepThreshold` 3, `restSeconds` 150, big compound lifts first |
| Build muscle or lose fat | `repCap` 10 to 12, `lowRepThreshold` 6, `restSeconds` 90 |
| General fitness | `repCap` 10, `lowRepThreshold` 6, `restSeconds` 90 to 120 |

- 3 working sets per exercise unless there's a reason. `stallSessions` 3.
- Every main movement pattern across the week: squat, hinge, horizontal and vertical push, horizontal and vertical pull, plus a little arms, core and calves if time allows.
- Give each exercise 1 to 3 **alternatives** for when equipment is taken, using only equipment they have.
- A skill goal becomes the day's optional skill slot (see below), not a whole program.
- Respect limits: swap the movement, don't just add a note.

### 3. Show the plan, then write it

Before any JSON, show the plan in plain language: the split, each day with its exercises (sets, rep range, the alternatives) and one line on how progress works. Ask "Anything to change?" Iterate until they're happy.

How progress works, in words they'll get: "Use a weight that's hard around the middle of the rep range. When a set goes past the top of the range, the next set (and next session) goes one step heavier. If the first set falls below the bottom, go one step lighter. The app tracks this for you and shows the weight to start with."

### 4. Map it onto the schema

- One `day` per training day. `weekdays` are ISO: 1 = Monday ... 7 = Sunday; each weekday at most once across days.
- One `slot` per exercise position. The first `variant` is the default; the rest are swaps.
- Use **catalog ids** from `references/exercises.json` wherever the exercise exists (`{"id": "bench-press"}` is enough). For anything new, give `name`, `equipment` and `primary`/`secondary` muscles.
- `kind`: `reps` for lifts, `hold` (seconds) and `carry` (meters) with `cap`, `cardio` for runs and rides.
- **Skill**: at most one `"skill": true` slot per day, for something they're learning. It's optional in the workout and never auto-jumps load. List the whole ladder as its variants, easiest first, and set `targetReps` (or `cap` for holds).
- `loading` only when the default is wrong: one-arm or one-leg work is `perLimb`.
- Variant `id` is a stable kebab-case slug of the exercise, not of the day. Reuse the same id when the same exercise appears on several days: history and progression are keyed by it. When changing an existing program, never rename an id that has history; add a new one instead.
- `equipment` sets the default weight step (barbell 2.5, dumbbell 2, machine and cable 5, bodyweight 2.5 added load). Set `step` only to override. Assistance machines use a negative step (less assistance is progress).
- Short coaching cues go in `note` fields: one line, practical ("Elbows under the bar", "Pause at the bottom").
- Program `id`: kebab-case of the name. `revision` 1 for a new program; bump it when you change one they already have.

### 5. Check it and hand it over

- If you can run code, validate: `python3 scripts/validate.py <file.json>` and fix every error. If you can't, check by hand against the schema: unique slot ids, every weekday used once at most, every slot has at least one variant, catalog ids spelled exactly.
- Give them a **file** named `<id>.json` if you can create files. If you can't, give exactly one JSON code block and tell them to save it as a `.json` file.
- Tell them how to import:
  1. On the iPhone, share the file and pick **Prokope** (from Files, Messages, AirDrop or this chat app), or save it to Files and use Prokope › Get a program › Import a program file.
  2. Look it over and tap **Add**. It becomes the active program; the Watch gets it on its own.
  3. Importing again with the same program `id` replaces it, and history carries over for every exercise id that stays.

## Review progress

1. Load the export (schema 2 has `programs` + `activeProgramId`; schema 1 has `program`). For one program's state (working weights, next suggestions, stalls), Settings › Export program with progress gives a smaller file (format in schema.md). If you can run code, `python3 scripts/summarize.py <export.json> [--weeks 4]` gives a compact digest; then dig into the raw JSON for specifics.
2. Report, in this order and briefly:
   - **Attendance**: workouts per week against the planned days, missed days.
   - **Moving**: exercises whose top weight went up, with from → to.
   - **Stalled**: same top weight for 3+ sessions with no set past the cap. Suggest the program's own fix (keep the weight and add reps, or swap to an alternative) rather than inventing a new scheme.
   - **Regressing**: top weight dropped, or set 1 fell under the low threshold repeatedly.
   - **Body**: 7-day average weight trend (ignore `source: carried` entries), waist and other measurements.
3. Ground every claim in the data with dates and numbers. Don't moralize. With under 2 weeks of data, say so and keep it short.
4. For program changes, edit the program JSON (same ids where the exercise stays), validate, and hand it over as above.


---

Exercise catalog for this file: https://prokope.apvl.dev/skill/references/exercises.json

# Prokope JSON formats

Source of truth (in the Prokope repo): `Packages/ProkopeKit/Sources/ProkopeKit/` (`Program.swift`, `Exercise.swift`, `Muscle.swift`, `Log.swift`, `AppData.swift`). Dates are ISO 8601. Weights are kg. Unknown fields are ignored; missing optional fields take defaults. Schema 2; schema 1 files still import.

## Exercise catalog

[exercises.json](exercises.json), next to this file (in the repo: `Packages/ProkopeKit/Sources/ProkopeKit/Resources/exercises.json`, generated by `scripts/make-exercises.py`), lists ~170 exercises with `id`, `name`, `equipment`, `loading`, `kind` (reps | hold | carry | cardio), `primary` and `secondary` muscles, and for skills `skill: {family, level}` (easy → hard, e.g. muscle-up 1 explosive pull-up … 7 muscle-up). **Prefer catalog ids**: a variant can then be just `{"id": "bench-press"}` and gets its name, equipment, loading and muscles from the catalog. Only exercises missing from the catalog need `name` and `primary`.

Muscles: chest, frontDelts, sideDelts, rearDelts, lats, upperBack, traps, lowerBack, biceps, triceps, forearms, abs, obliques, glutes, quads, hamstrings, adductors, calves.

Loading (how the logged weight reads): `total` (stack or bar total, bodyweight added load), `perSide` (barbell plates per side; stored as bar + 2 × side), `perHand` (one dumbbell), `perLimb` (one arm or leg working alone). Defaults: barbell → perSide, dumbbell → perHand, rest → total.

## Program (import)

```jsonc
{
  "schemaVersion": 2,
  "id": "get-lean",                 // stable program id
  "name": "Get Lean",
  "revision": 2,                    // bump when you change a program the athlete already has: the app replaces older saved copies of a bundled program
  "summary": "optional one-liner",
  "rules": {
    "repCap": 9,                    // past this, next set +1 step
    "lowRepThreshold": 5,           // set 1 under this, next set -1 step
    "stallSessions": 3,             // same weight this many sessions → "keep it, add reps"
    "restSeconds": 90
  },
  "notes": ["program-wide cues"],
  "days": [
    {
      "id": "d1-pull",
      "title": "Pull",
      "subtitle": "Lats, biceps",
      "weekdays": [1],              // ISO 1=Mon..7=Sun; each weekday at most once across days
      "notes": ["day cues"],
      "warmup": [                   // optional and currently unused: the app runs a single warm-up timer (Settings › Warm-up)
        { "name": "Band dislocates", "reps": 15 },
        { "name": "Scap pulls", "seconds": 30, "note": "optional" }
      ],
      "sections": [
        {
          "title": "Skill first",   // optional
          "note": "optional",
          "slots": [
            {
              "id": "d1-pull-up",   // unique across the program
              "kind": "reps",       // reps | hold | carry | cardio (default reps); "skill" is the v1 spelling of reps + skill
              // cardio: "cap" = target minutes, or "intervals": {"work": 30, "rest": 30, "rounds": 8} (seconds); sets usually 1.
              // Use a catalog cardio id (outdoor-run, treadmill-run, outdoor-cycle, indoor-cycle, rowing-machine, stair-climber, elliptical, jump-rope, intervals).
              // A day of only cardio runs on the Watch as that Health workout (running, cycling...), with distance and pace.
              "skill": false,       // the day's skill (muscle-up, L-sit): at most ONE per day, optional by default, never auto-jumps load
              "optional": false,    // can be skipped without leaving the day unfinished (defaults to the value of "skill")
              "sets": 3,            // default 3
              "targetReps": 3,      // skill: int or {"min":1,"max":3}
              "cap": 20,            // hold: seconds, carry: meters
              "note": "optional cue",
              "variants": [         // first = default, others = swaps
                {
                  "id": "pull-up",  // stable exercise slug, shared across days; prefer a catalog id
                  "name": "Pull-up", // optional for catalog ids
                  "equipment": "bodyweight", // barbell|dumbbell|machine|cable|bodyweight|band|kettlebell|other
                  "step": 2.5,      // optional override; negative for assistance
                  "url": "https://www.strengthlog.com/pull-up/",
                  "note": "optional",
                  "loading": "total",       // optional override, see Loading
                  "barWeight": 20,          // perSide only, default 20
                  "primary": ["lats"],      // optional override; required for non-catalog exercises
                  "secondary": ["biceps"]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}
```

Validation (also enforced by the app on import): at least one day; weekdays in 1...7 and unique; each day has slots; at most one skill slot per day; slot ids unique; each slot has ≥1 variant and ≥1 set; every variant has a name (its own or the catalog's); a variant id always maps to the same name.

A skill slot's variants are the skill's ladder, easiest first (all muscle-up steps in one slot), so the athlete picks the step they're on and the app can suggest the next.

## Export

```jsonc
{
  "schemaVersion": 2,
  "programs": [ /* programs as above */ ],
  "activeProgramId": "get-lean",           // v1 exports have a single "program" instead
  "profile": { "sex": "male", "heightCm": 180 },
  "customExercises": [ /* catalog-shaped entries the athlete added */ ],
  "sessions": [
    {
      "id": "UUID",
      "programId": "get-lean",
      "dayId": "d1-pull",
      "dayTitle": "Pull",
      "startedAt": "2026-10-22T16:02:11Z",
      "endedAt": "2026-10-22T17:01:40Z",   // null while running
      "updatedAt": "...",
      "source": "watch",                     // watch | phone
      "discarded": false,                    // true = deleted, ignore
      "exercises": [
        {
          "id": "UUID",
          "slotId": "d1-pull-up",
          "variantId": "pull-up",
          "variantName": "Pull-up",
          "kind": "reps",
          "sets": [
            {
              "id": "UUID",
              "loggedAt": "...",
              // first segment = the working set; more segments = drop set
              "segments": [{ "weight": 10, "amount": 8 }, { "weight": 0, "amount": 4, "halfRep": true }] // halfRep: a last half rep (8½); never counts past the cap
            }
          ]
        }
      ]
    }
  ],
  "bodyWeights": [
    { "id": "UUID", "date": "...", "kg": 80.4, "source": "manual" } // manual | health | carried (no scale; ignore for trends)
  ],
  "measurements": [
    { "id": "UUID", "date": "...", "values": { "waist": 84, "chest": 101, "arms": 36.5, "thighs": 58, "hips": 97, "neck": 38 } } // cm
  ],
  "settings": {
    "restSeconds": 90,
    "variantChoices": { "slotId": "variantId" },
    "stepOverrides": { "variantId": 2.5 },
    "reminders": { /* weigh-in, measurements, report times */ }
  }
}
```

`amount` is reps for `reps`/`skill`, seconds for `hold`, meters for `carry`, seconds for `cardio` (with optional `distance` in meters). Weight on bodyweight moves is added load (0 = bodyweight); on assisted machines it is the assistance.

Useful derived numbers:
- **Top weight** of an exercise in a session: max of `segments[0].weight` across sets.
- **Estimated 1RM** (Epley): `weight * (1 + reps / 30)` on `segments[0]`.
- **Volume**: Σ weight × amount (+0.5 for a half rep) over all segments of `reps` exercises.

## Program export (Settings › Export program with progress)

`prokope-program-<id>-<date>.json`: the active program plus where each exercise stands. Use it to review a program or plan the next block.

```jsonc
{
  "schemaVersion": 1,
  "exportedAt": "...",
  "program": { /* the program, as above */ },
  "exercises": [
    {
      "variantId": "bench-press", "name": "Bench press", "slotIds": ["d2-bench"], "primary": ["chest"],
      "sessions": 6,
      "workingWeight": 60,          // heaviest working set last time
      "nextWeight": 62.5, "nextReps": 8, "nextChange": "up", // what the app prefills next session: start | same | up | down
      "hint": "Last set passed 9 · start one step up",
      "stalled": false,             // same weight for stallSessions sessions without passing the cap
      "recent": [{ "date": "...", "sets": ["60×8", "60×10"] }] // newest first, up to 5; "→" joins drop segments, "½" marks a half rep
    }
  ]
}
```

