One format for intervals, sets and repeaters

A hangboard repeater and a Tabata round have little in common on paper. One is seven seconds on an edge and three off, six times, then a long rest and the next set. The other is twenty seconds of all-out work and ten of rest, eight rounds. A coach describing either to an athlete uses completely different words. Sestava runs both with one timer engine, so before we wrote any of the Run screen we had to answer a smaller question: what is a workout once you take the sport out of it?
Our answer is sestava.plan/1, an open JSON format with a CSV variant for spreadsheets. Its full
reference is on the Plan format page; this post is the tour.
Sections, items, and four ways to run them
A plan is either a single workout or a program of weeks and days. A day holds blocks, each with a section name such as Warm-up or Main, and a block holds items. An item is one of four kinds, and its keys decide which:
- timed, with
time: counts down and moves on by itself; - strength, with
setsandreps: the athlete taps Done Set, then thereststarts; - rest, with
restalone; - hang, with
hangandrepeats: hangboard intervals.
A block also has a mode, and there are exactly four. normal, the default, runs the items in
order, rounds times. superset alternates strength items set by set. amrap fits as many
rounds of a list as it can into a time cap while the athlete counts them. fortime runs the list
once, as fast as possible, until the athlete taps Done or the cap is reached. The items of an
amrap or fortime block are a fifth, simpler kind: one line of the work list, such as
“Run, 400 m”.
There is no EMOM mode and no Tabata mode. They are not missing; they do not need one.
A Tabata is a normal block
This is the format’s own example of a single workout, as it is in the repository:
{
"format": "sestava.plan/1",
"name": "Tabata Blast",
"id": "k7Qx2mV9pL4wT8zR",
"version": 2,
"category": "hiit",
"note": "Eight rounds, all out. Scale to squat thrusts if burpees break your form.",
"blocks": [
{
"section": "Tabata",
"rounds": 8,
"items": [
{ "exercise": "Burpees", "time": 20, "cue": "Max effort" },
{ "rest": 10 }
]
}
]
}
One block, eight rounds, a timed item and a rest item. The app turns it into 16 steps and four
minutes. The last ten-second rest runs too, because the format does not treat the last round
differently. A timed item cannot carry its own rest; the rest is an item of its own, which keeps
every item one kind.
The hangboard item
Repeaters are the one shape that did not fit timed items without pages of repetition, so they got a kind of their own:
{
"exercise": "Half Crimp Repeaters",
"prep": 5,
"hang": 7,
"off": 3,
"repeats": 6,
"sets": 5,
"setRest": 180,
"grip": "Half crimp",
"edge": "20 mm",
"load": "+5 kg"
}
Five seconds to get on the board, then five sets of six seven-second hangs with three seconds off between them, and three minutes between sets. The format spells out how many steps that makes: one prep, 30 hangs, 25 offs and 4 set rests, 60 steps from one item. Grip, edge and load are shown to the athlete as the coach wrote them.
Flattening
Whatever the mode, a day ends up as a flat list of steps, and the engine only ever runs that list. A superset’s steps are tagged A1, A2 and so on; an AMRAP becomes one step named “AMRAP 6 min” that carries its work list. Keeping the sports out of the engine is what lets one timer run them all.
From a spreadsheet
Many coaches already keep their plans in a spreadsheet, so the format has a CSV variant with one
row per item. Twelve columns are required
(week,day,day_name,section,rounds,exercise,time,sets,reps,load,rest,cue), the rest are
optional, and a sheet without them reads as if they were blank. The delimiter is found from the
header line: a comma, a semicolon or a tab, so a sheet saved with semicolons, as spreadsheets
set to Czech often do, imports as it is. Per-set values are written with a slash, 12/10/8. The app imports
a .csv file like any other plan.
Strict where it matters
A format that several programs read has to be exact about mistakes. A key the format defines but
an item kind does not allow, such as rest on a timed item, is an error that names its place
(blocks[0].items[0].rest), never silently ignored. A key the format does not know is ignored, so
a plan written by a newer app still opens in an older one, with a warning.
Writers emit one canonical form: the same key order, the same indent, a newline at the end. The same plan therefore always gives the same bytes, and the same bytes give the same link.
Two readers, one set of answers
The format has two readers today: the Swift core in the app, and the TypeScript module in Sestava Studio, the coach’s editor in the browser. To keep them from drifting apart, the repository holds conformance vectors: for 25 plans a reader opens, the canonical form, the number of steps, the estimated duration and the review rows; for 75 invalid ones, the first error, its place and its message. The core writes them, and Studio’s tests reproduce every single workout and every error; Studio edits single workouts only, so it refuses the one program among them, by design. The build fails when a vector is out of date.
What stays out
The format says what happens and when. It does not say why: periodisation, progression and an athlete’s history belong to the coach, not to the file that tells a phone when to beep. It has no audio settings either; voice, beeps and haptics are the athlete’s choice in the app.
A few limits worth knowing:
- The name is settled. The project began as Setlist, which is why early drafts of the format
said
setlist.plan/1; the format shipped assestava.plan/1and files end in.sestava, with no alias for the working name. - “Describe a workout” writes less than the format holds. Sestava’s Apple Intelligence import asks the on-device model for timed, strength and rest items only. The app, not the model, decides whether the result is a valid plan, and you review it like any other import.
- One file format, not every service. The app imports this format from a file, a link or a QR code, and CSV files. It does not import from other training services today.
Format/ folder of the
repository, so it cannot drift from the spec the app reads. The JSON Schema, the CSV column
reference and the changelog are on that page; the conformance vectors are in the folder itself.Cover photo by Isaac Smith on Unsplash.