# Reracked routine file format (version 1)

A routine file holds workout **routines** (plans), not logged history. Reracked writes one from
**Routines → ⋮ → Export routines** and reads one from **Routines → ⋮ → Import routines**. It is plain
UTF-8 JSON, so it can be kept, edited by hand, or written by another tool.

Code: `RoutineFile` / `WorkoutExporter.toRoutineFile` / `WorkoutImporter` in
`app/src/main/java/com/bhamilton/workoutloggerui/data/export/`. Tests that pin this document:
`RoutineFileTest`, `WorkoutImportRoundTripTest`. **If this page and the code disagree, the code wins.**

## Example

```json
{
  "format": "reracked.routines",
  "schema_version": 1,
  "routines": [
    {
      "name": "Push A",
      "folderName": "PPL",
      "notes": "Heavy day",
      "exercises": [
        {
          "order": 0,
          "name": "Bench Press",
          "notes": null,
          "supersetGroup": 0,
          "restSeconds": 150,
          "repRangeMin": 6,
          "repRangeMax": 10,
          "sets": [
            { "setNumber": 1, "type": "WARMUP", "reps": 8, "weightKg": 40, "rpe": null,
              "completed": false, "isPersonalRecord": false, "notes": null },
            { "setNumber": 2, "type": "NORMAL", "reps": 8, "weightKg": 80, "rpe": 8,
              "completed": false, "isPersonalRecord": false, "notes": null }
          ]
        },
        {
          "order": 1,
          "name": "Cable Fly",
          "notes": null,
          "supersetGroup": 0,
          "restSeconds": 0,
          "repRangeMin": null,
          "repRangeMax": null,
          "sets": [
            { "setNumber": 1, "type": "NORMAL", "reps": 12, "weightKg": 15, "rpe": null,
              "completed": false, "isPersonalRecord": false, "notes": null }
          ]
        }
      ]
    }
  ]
}
```

## Top level

| Key | Meaning |
| :-- | :-- |
| `format` | `"reracked.routines"`. Absent means a full Reracked backup, which is also accepted (its routines are imported; its workouts are ignored). Another `reracked.` value was written by a newer version of the app and is refused with "update the app"; any other value is refused as not a routine file. |
| `schema_version` | `1`. A higher number is refused with "update the app". Absent is read as 1. |
| `routines` | The routines, in any order. |

## A routine

| Key | Meaning |
| :-- | :-- |
| `name` | Required, not blank, at most 255 characters. |
| `folderName` | Optional folder, at most 120 characters. |
| `notes` | Optional, at most 2,000 characters. |
| `exercises` | Required: 1–200 exercises, at most 100 sets each and 1,000 exercises plus sets in all. |

## An exercise

| Key | Meaning |
| :-- | :-- |
| `order` | Position in the routine, from 0. |
| `name` | Required. |
| `notes` | Optional. |
| `supersetGroup` | Optional small number. Exercises with the same number in one routine form a superset; it is not an id. |
| `restSeconds` | Optional planned rest, 0–900. `null` = the app's default rest; `0` = no rest timer. |
| `repRangeMin`, `repRangeMax` | Optional rep range for the working sets. Both or neither, `1 ≤ min ≤ max`. |
| `sets` | The prescribed sets. |

## A set

| Key | Meaning |
| :-- | :-- |
| `setNumber` | Required. |
| `reps` | Required. |
| `weightKg` | Required, in **kilograms** whatever unit the app displays. |
| `type` | `NORMAL`, `WARMUP`, `DROP` or `FAILURE`; anything else reads as `NORMAL`. |
| `rpe` | Optional target effort on the RPE scale (RIR = 10 − RPE). |
| `notes` | Optional. |
| `completed`, `isPersonalRecord` | Written for compatibility with the backup; not meaningful for a plan. |

## How an import behaves

- **Nothing in the file is an identity.** There are no database or sync ids, and nothing about the
  account that exported it. Every imported routine, exercise and set gets a new id, and is saved on the
  phone first, then synced like any routine you build yourself.
- **An import never replaces a routine.** A routine identical to one you already have is skipped.
  One whose name is already taken is imported as "Name (2)", "Name (3)" and so on. The preview lists
  every routine the import would add, including any renames, and nothing is written until you confirm.
- **Invalid routines are skipped and counted.** A routine is invalid if it breaks a limit a routine
  built in the app must meet: no name, no exercises, too many exercises or sets, or numbers out of
  range. Smaller problems drop only the part affected: an exercise with no name or a set missing its
  `setNumber`, `reps` or `weightKg` is left out, and a bad optional value (a rest of 5000 s, half a
  rep range) is ignored.
- **File limits:** 32 MB, 10,000 routines, 200,000 exercises plus sets, and 32 levels of JSON nesting.

## Compatibility rules

- Adding a key is not a version change: readers ignore keys they don't know.
- Removing a key or changing what one means raises `schema_version`. Older builds then refuse the
  file and tell the user to update the app, instead of importing something the file doesn't say.
- The routine element is shared with the full JSON backup (**Settings → Export workouts → JSON**),
  under the same `schema_version`. A routine file can also be restored through **Settings → Import
  workouts**.
