Pro trenéry

Formát plánu Sestavy

Otevřený a jednoduchý formát pro jednotlivé tréninky i vícetýdenní programy. Napište ho ručně, vyexportujte z tabulky nebo ho vygenerujte vlastními nástroji.

Formát sestava.plan/1 · Soubory: .sestava nebo .csv · UTF-8

Přehled

Plán je seznam částí (například Rozcvička, Hlavní část, Zklidnění). Každá část obsahuje položky a běží v jednom ze čtyř režimů. Program navíc přidává týdny a dny, a každý den má vlastní části.

  • Normální (výchozí): položky popořadě, roundskrát. bell: true zazvoní na gong na začátku každé časované položky i odpočinku a warn upozorní tolik sekund před koncem časované položky.
  • Superset: položky se sériemi a opakováními, prováděné série po sérii (A1, A2, pak znovu A1), s rest poslední položky mezi koly.
  • AMRAP: co nejvíc kol seznamu za cap sekund; sportovec si kola počítá sám.
  • Na čas: seznam jednou, co nejrychleji, s koncem po cap sekundách.

Položka je jednoho z pěti druhů, podle svých klíčů:

  • Časovaná položka: nastavte time v sekundách. sides: 2 ji spustí jednou na každou stranu.
  • Silová položka: nastavte sets a reps. Sportovec po každé sérii klepne na Hotovo a pak běží časovač odpočinku. reps a load mohou uvádět hodnotu pro každou sérii zvlášť ([12, 10, 8]); tempo se zobrazí jako pokyn.
  • Vis: repeatery na hangboardu: hang sekund, repeats visů v sérii s off sekundami mezi nimi, sets s setRest mezi nimi, prep před první sérií a grip a edge.
  • Odpočinek: položka pouze s rest v sekundách.
  • Položka seznamu: řádek seznamu AMRAP nebo Na čas: exercise a reps, číslo nebo text jako 400 m.

Soubory

Specifikace formátu je v angličtině, aby odpovídala souborům, které aplikace čte.

An open JSON format for structured training plans: intervals/HIIT, strength (sets × reps), mobility, hangboard repeaters, supersets, AMRAP and For Time blocks, and bell rounds. One parser, one review screen: every import route (file, link, QR, Describe a workout) converts into this format, and so will the Garmin Connect and TrainingPeaks routes planned after launch.

Document

{
  "format": "sestava.plan/1",
  "name": "Strength Foundations",
  "author": "Coach Novak",
  "id": "Sf3nR8wQ2cLx6YtB",
  "version": 2,
  "category": "strength",
  "note": "Log every top set.",
  "weeks": [ { "days": [ { "name": "Full Body A", "blocks": [ … ] } ] } ]
}
FieldTypeRequiredNotes
formatstringyessestava.plan/1 or sestava.plan/1.<minor>
namestringyesnon-blank
authorstring or nullnotrimmed; blank is treated as absent
idstring of 1–64 A–Z a–z 0–9 _ -with versionstable plan identity; Studio generates it once and never changes it
versioninteger 1–9007199254740991with idraised when a plan that was already shared is shared again after edits
categoryone of the values under categorynocover colour in the app and on the athlete page
notestring or null, at most 500 charactersnoa short note from the coach, shown to athletes
progressionobjectnoreserved, see progression; the default for every strength item
weeksarray of weekone ofa program
blocksarray of blockone ofa single workout; exactly one of weeks / blocks

id and version are both present or both absent; a writer must not emit one without the other. version stops at 2^53 − 1 so a JavaScript reader holds every value exactly. Length limits count Unicode code points as written, before trimming (what JSON Schema’s maxLength counts), so a note of 500 emoji is allowed.

A single workout given as top-level blocks is read as one week with one day named after the plan. Writers emit blocks again for such plans. A single workout is a plan with one week and one day named after the plan; a plan with one week and one day whose name differs from the plan is a one-workout program, not a single workout.

category

ValueLabel
strengthStrength
hiitHIIT
mobilityMobility
coreCore
cardioCardio
recoveryRecovery
climbingClimbing
yogaYoga
boxingBoxing

The label is what the review row Category: {Label} and Studio’s category chips show. Writers emit only these values (the JSON Schema enum is strict). The set may grow in a minor, so readers are lenient: an unknown string is treated as absent and the review adds the warning Unknown category “{raw}”; the plan opens without one; the plan is written back without category. "category": null and a non-string value are invalid.

week

FieldTypeRequired
daysarray of day, min 1yes

day

FieldTypeRequired
namestring, non-blankyes
blocksarray of block, min 1yes

block

FieldTypeRequiredNotes
sectionstring, non-blankyesshown as the kicker, e.g. Warm-up, HIIT
modenormal, superset, amrap or fortimenohow the block runs; default normal
capinteger 1–86400in amrap and fortime only, required thereseconds
bellbooleanno, normal onlyrounds start and end on a bell; default false
warninteger 1–86400no, only with bell: truea knock this many seconds before the end of each timed item; shorter than every timed item’s time
roundsinteger 1–1000no, normal onlyrepeats the items; default 1
itemsarray of item, min 1yesat least 2 in a superset
ModeItems it may holdHow it runs
normaltimed, strength, rest, hangthe items in order, rounds times
supersetstrength only, at least two, all with the same setsset by set: A1, A2, … then the last item’s rest, except after the last set
amrapblock exercisesas many rounds of the list as fit in cap; the athlete counts rounds
fortimeblock exercisesthe list once, as fast as possible; stops at cap

A superset’s letter is its position among the day’s superset blocks (A … Z, then AA, AB …); each reps step is tagged with the letter and the item’s 1-based position (“A2”). rest is allowed only on a superset’s last item.

The cap label is {s} s under a minute, {m} min for whole minutes and {m}:{ss} min otherwise (30 s, 6 min, 1:30 min).

item — one kind, decided by keys

In a normal or superset block the kinds are tried in this order: hang makes a hang item; otherwise time a timed item; otherwise sets a strength item; otherwise a lone rest (no exercise) a rest item. An item with both time and sets and no hang, or with exercise but none of hang, time and sets, is invalid. In an amrap or fortime block every item is a block exercise.

Timed (time): counts down and advances automatically.

{ "exercise": "Side Plank", "time": 30, "sides": 2, "cue": "Hips high", "load": "bodyweight" }

Strength (sets + reps): the athlete taps Done Set; then rest seconds of rest start.

{ "exercise": "Bench Press", "sets": 3, "reps": [12, 10, 8], "rest": 90, "tempo": "3-1-1-0", "load": ["60 kg", "65 kg", "70 kg"] }

Rest (rest only):

{ "rest": 20 }

Hang (hang + repeats): hangboard intervals.

{ "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" }

Block exercise (every item of an amrap or fortime block): one line of the work list.

{ "exercise": "Run", "reps": "400 m" }

Allowed keys per kind

✓ required, ○ optional (default in brackets), — not allowed, ignored: writers must not emit it, readers ignore it.

Keytimedstrengthresthangblock exerciseType
exercise✓✓—✓✓non-blank string; compared case- and whitespace-insensitively
time✓————integer 1–86400 (seconds)
sets—✓—○ (1)—integer 1–10000
reps—✓——✓strength: integer 1–10000 or a list of them; block exercise: integer 1–10000 or non-blank string
rest—○✓——integer 1–86400 (seconds)
hang———✓—integer 1–86400; one hang
repeats———✓—integer 1–1000; hangs per set
off———○ (0)—integer 0–86400; between hangs
setRest———○ (0)—integer 0–86400; between sets
prep———○ (0)—integer 0–86400; before the first set
grip———○—string or null (“Half crimp”)
edge———○—string or null (“20 mm”)
sides○ (1)○ (1)———1 or 2
tempo—○———string or null (“3-1-1-0”), shown as “Tempo 3-1-1-0”
cue○○—○○string or null; shown hint (not spoken)
load○○ (single or list)—○○ (single)non-blank string or null, e.g. 16 kg, bodyweight
image○○—○—string or null; absolute http(s) URL, an invalid value is a review warning
progressionignored○ignoredignoredignoredobject, reserved; see progression
  • A key marked — is an error at that key (not allowed on a timed item; in an amrap or fortime block not allowed in an AMRAP block / not allowed in a For Time block), never ignored. “Unknown keys are ignored” holds only for keys the format does not define, and for progression, the one defined key a reader ignores (it is never marked —). Block keys on an item and item keys on a block are unknown there and ignored. A key counts as present when the object has it, even with the value null.
  • grip, edge, tempo, cue, load and image may be null (absent). sides, hang, repeats, off, setRest, prep, a hang item’s sets, a block exercise’s reps and the block keys mode, cap, bell and warn reject null.
  • reps and load on a strength item are one value, or a list with exactly one value per set (the step for set n takes the nth value). List elements follow the single-value rules and may not be null. Lists are kept as written: [8, 8, 8] stays a list.
  • A block exercise’s reps string is trimmed; if only the digits 0–9 remain it is that whole number (" 10 ", "10" and 10 are the same plan, and "0" or "10001" are errors). Anything else is text shown as written ("400 m", "10/side"); readers never parse it.

progression

Reserved in 1.0 (#408); its semantics come with progression rules (#407). No 1.0 reader applies it.

"progression": { "rule": "double", "step": 1, "range": [8, 12], "ladder": "push-up", "stepBackAfter": 2, "deloadEvery": 6 }

It may appear on the document, where it is the default for every strength item of the plan, and on a strength item, where it overrides the document’s block for that item. Nowhere else: on a timed, rest or hang item and on a block exercise the schema forbids it, so writers must not emit it there, and readers ignore it; on a block, week or day it is an unknown key. A later minor may allow it in more places.

FieldTypeReserved for (semantics in #407)
rulenon-blank stringthe rule kind, such as double (double progression)
stepinteger 1–10000reps added per set when the rule advances
rangearray of exactly 2 integers 1–10000the reps range [low, high]; reaching high advances the ladder
laddernon-blank stringa ladder id from the Format library
stepBackAfterinteger 1–1000failed sessions before a step back
deloadEveryinteger 1–1000weeks between deloads

Every field is optional and the object may be empty. Only the six names are reserved; their types are provisional and may widen in a minor. Unknown keys inside progression are ignored, as everywhere.

  • Readers that do not apply it ignore it: its presence, its contents and its validity change nothing about reading, review or execution. A plan with progression reads, reviews and runs exactly as the same plan without it.
  • Readers that apply it never reject a plan over it: an invalid or misplaced progression is a review warning, and the item runs as written.
  • The canonical form leaves progression out until a writer models it.
  • The key never changes how a plan is executed: progression proposes edits the athlete confirms, and the plan that runs is always the plan as stored.

Error order

Readers report the first error in this order, so every implementation names the same one:

  1. Block keys: section, mode, then cap, bell, warn, rounds in that order, each checked for the block’s mode (cap missing in amrap/fortime, or present elsewhere; bell outside normal or not a boolean; warn without bell: true; rounds outside normal).
  2. The items, in order. For each item: its kind; then misplaced keys in the global order exercise, time, sides, cue, load, image, sets, reps, rest, tempo, prep, hang, off, repeats, setRest, grip, edge; then missing required keys; then values in the kind’s canonical key order.
  3. Rules across items. Superset: every item’s sets equals the first item’s; then rest only on the last item; then strength items only; then at least two items. Bell block: warn shorter than every timed item’s time.
RuleError
mode not one of the four wordsInvalid "mode" (circuit) at blocks[0].mode: must be one of normal, superset, amrap, fortime
cap missing in amrap / fortimeMissing "cap" at blocks[0]
cap in normal / superset… cap: allowed only in AMRAP and For Time blocks
bell not a boolean / outside normal… bell: expected true or false / … bell: allowed only in normal blocks
warn without bell: true… warn: allowed only with bell
warn ≥ a timed item’s timeInvalid "warn" (10) at blocks[0].warn: must be shorter than every timed item in the block
rounds outside normal… rounds: not allowed in a Superset block (an AMRAP block, a For Time block)
superset sets differInvalid "sets" (4) at blocks[0].items[1].sets: must equal the first item's sets (3)
superset rest not on the last item… rest: allowed only on the last item of a Superset block
non-strength item in a supersetInvalid "items" at blocks[0].items[1]: a Superset block holds strength items only
superset with one itemInvalid "items" (1) at blocks[0].items: a Superset block needs at least two items
list of another lengthInvalid "reps" (2) at blocks[0].items[0].reps: must list one value per set (3)
sides not 1 or 2… sides: must be 1 or 2

Rules

  • Unknown keys are ignored, so a sestava.plan/1.x document written by a newer app still opens on an older one (with a review warning). Keys the format defines are never ignored, except progression (see progression): one a kind or mode does not allow is an error (see “Allowed keys per kind”).
  • Duplicate JSON keys are undefined: Foundation’s JSONDecoder keeps the first occurrence, where some other parsers keep the last. Do not rely on either behaviour; writers must not emit duplicate keys.
  • Writers emit the canonical form: keys in this order (document format, name, author, id, version, category, note, weeks, blocks; block section, mode, cap, bell, warn, rounds, items; timed exercise, time, sides, cue, load, image; strength exercise, sets, reps, rest, sides, tempo, cue, load, image; rest rest; hang exercise, prep, hang, off, repeats, sets, setRest, grip, edge, cue, load, image; block exercise exercise, reps, cue, load), absent optional keys and keys equal to their default left out (mode: "normal", bell: false, rounds: 1, sides: 1, a hang’s sets: 1, zero prep, off and setRest), lists written as arrays, a block exercise’s whole-number reps written as a number ("010" is written 10), two-space indent exactly as JavaScript’s JSON.stringify(plan, null, 2), and a trailing newline. The same plan always gives the same bytes, so the same plan always gives the same link.
  • Empty weeks, days, blocks or items are invalid: a plan must have something to do.
  • Text values are trimmed: name (plan and day), author, section, exercise, cue, load, image, note, tempo, grip, edge and a block exercise’s text reps. A blank author, cue, note, tempo, grip or edge is treated as absent; a blank name, section, exercise, load or reps is invalid. load, author, note, cue, image, tempo, grip and edge may be null; id, version and category may not.
  • Trimming removes exactly the Unicode White_Space characters, which is Foundation’s whitespacesAndNewlines: U+0009–U+000D, U+0020, U+0085, U+00A0, U+1680, U+2000–U+200A, U+2028, U+2029, U+202F, U+205F and U+3000. U+FEFF (zero-width no-break space, the byte order mark) is not whitespace and is kept, even at the edge of a value. Other implementations must trim with this explicit set rather than their language’s default trim (JavaScript’s String.prototype.trim removes U+FEFF and keeps U+0085, for example). The normalization vector pins this.
  • A trimmed image is valid only as an absolute http or https URL (case-insensitive scheme) with a non-empty host, including an IPv6 literal host in brackets ([::1], not validated as IPv6 beyond the brackets themselves); anything else — no scheme, no host, a space or other character breaking the host — is kept exactly as written and is a review warning, never an error. A valid URL’s path, query and fragment are percent-encoded for two cases only: a literal space (as %20) and any non-ASCII character (as its UTF-8 bytes, each %XX uppercase); the scheme, host, port, userinfo and anything already percent-encoded are kept exactly as written. This is Foundation’s URL(string:) behaviour for these two cases, confirmed identical on macOS and the swift:6.4 Linux container that CI uses, so it is the format’s rule rather than a Foundation quirk; other differences between Foundation’s parser and a strict RFC 3986 reading (for example https:///x, or a userinfo containing @) are out of scope and not pinned. The image-url-normalization vector pins a space, a non-ASCII path and an IPv6 host.
  • Readers reject a day that would flatten to more than 5000 steps; the cap is enforced by readers, not the JSON Schema, because a flattened step count is derived from rounds and each item’s kind (and, for strength items, whether rest is present) and cannot be expressed as a JSON Schema constraint on any single field. The schema also cannot express equal sets in a superset, rest only on a superset’s last item, a list length equal to sets, warn shorter than the block’s timed items, or a block exercise’s digit-string reps bounds; readers enforce those.

Re-importing a plan updates the stored plan it matches instead of creating a duplicate. The match is decided by the first rule that applies:

  1. id. A plan that carries id is identified by it, whatever route it came by (link, QR code or file). Importing it with a higher version replaces the stored plan and keeps its history and progress; the app says “A newer version is available”. The same or a lower version opens the stored plan unchanged.
  2. Fragment payload. A plan without id opened from a fragment link (https://sestava.app/p#<payload>) is identified by the SHA-256 of the decoded payload bytes (the plan’s UTF-8 JSON after base64url decoding and, unless the payload is marked ~, inflating), so two different plans never share an identity.
  3. Canonical link. Otherwise a link’s identity is its canonical form (lowercase scheme and host, no default port, no fragment, no utm_*/fbclid/gclid query items, no trailing slash). Re-importing a link or QR code in a different but canonically equivalent form (e.g. a tracking query added, or a trailing slash) updates the same plan. Path and query keep their percent-encoding, so /p/7K%2FQ2 and /p/7K/Q2 (or ?v=a%26b and ?v=a&b) are different links. User info (user:pass@) is dropped. Remaining query items keep their order and the case of their names, so ?a=1&b=2 and ?b=2&a=1 are different links.

A file import without id is always a new plan. The core computes the rule as PlanIdentity.

Execution semantics (what a player does with a day)

Flatten the day into steps, block by block:

StepProduced by
timeda timed item: one step, or a left then a right step of the full time with sides: 2
repsa strength item: one per set, or a left then a right step per set with sides: 2; the step for set n carries the nth reps and load of a list
resta rest item; a strength item’s rest after each set in a normal block (after the pair with sides: 2); a superset’s last-item rest between rounds only; a hang item’s setRest between sets
prepa hang item with prep > 0: once per round, before its first set
hanga hang item: repeats per set
offa hang item with off > 0: between the hangs of a set, none after the last
amrapan amrap block: one step of cap seconds named AMRAP {cap label} carrying the list
fortimea fortime block: one step with a cap-second limit named For Time carrying the list

A normal block with rounds repeats its items that many times. A superset runs, for set s in 1…sets, each item’s reps step in order (left then right with sides: 2), then the last item’s rest unless s is the last set. A hang item makes, per round, (prep > 0 ? 1 : 0) + sets × repeats + (off > 0 ? sets × (repeats − 1) : 0) + (setRest > 0 ? sets − 1 : 0) steps.

The athlete counts AMRAP rounds ("+1 Round"); reaching the cap ends the block with the rounds counted. A For Time block ends when the athlete taps Done (the time taken, pauses excluded) or at the cap (“capped”). Skipping or ending either block before its cap records it as skipped; once the cap has passed, even with no tick yet (a Live Activity action while suspended), it records the cap result instead. Results are keyed by the block’s 0-based position in its day.

In a bell block the timed and rest steps ring a bell instead of their tones, and with warn a knock sounds that many seconds before the end of each timed step.

Estimated duration (the estimate convention, shared by the app and Studio): timed, rest, prep, hang and off steps count their seconds, amrap and fortime steps their cap, and each reps step counts 45 seconds (both sides count; DurationEstimator.repsStepEstimate in the core). A plan’s estimate is the sum over all of its days.

Examples: examples/. full-body-a.sestava flattens to 25 steps and estimates 1065 seconds; hangboard-repeaters 61 steps and 1070 s, gym-mix 10 and 1110 s, boxing-rounds 9 and 900 s, evening-yoga-flow 14 and 885 s.

Conformance vectors

vectors/ holds one file per example (Format/examples/*.sestava) and per valid test fixture, written by the core’s ConformanceVectorTests. It also covers reader-only fixtures: documents the JSON Schema rejects (so writers must not emit them) that a reader still opens, such as an unknown category. Any other implementation of the format (Studio’s TypeScript module) must reproduce everything but source from source:

{
  "source": { "…": "the input document, object keys sorted" },
  "canonical": { "…": "the canonical form (see Rules), keys in schema order" },
  "steps": 16,
  "durationSeconds": 240,
  "checks": [ { "severity": "ok", "message": "Format sestava.plan/1 is valid" } ]
}

steps and durationSeconds follow “Execution semantics” summed over all days.

vectors/invalid/ holds one error vector per invalid test fixture that parses as JSON. An implementation must report the same first error for source:

{
  "source": { "…": "the fixture, object keys sorted" },
  "error": {
    "path": "blocks[0].items[0].rest",
    "message": "Invalid \"rest\" at blocks[0].items[0].rest: allowed only on the last item of a Superset block"
  }
}

error.path is the error’s location ($ for errors without one, such as a missing format); error.message is the core’s PlanDecodingError.errorDescription, stable per case. checks are the review rows, computed with no bundled exercise images. Each file is exactly JSON.stringify(vector, null, 2) plus a newline. CI fails when a committed vector is stale; regenerate with SESTAVA_UPDATE_VECTORS=1 swift test --package-path Packages/SestavaCore --filter 'ConformanceVectorTests|ErrorVectorTests' (the filter is a regular expression; both suites write vectors).

CSV z tabulky

Naplánujte trénink v tabulce a vyexportujte ho jako CSV. Buňky, které se nepoužijí, nechte prázdné.

Specifikace formátu je v angličtině, aby odpovídala souborům, které aplikace čte.

One row per item. Header row required; columns may appear in any order, header names are trimmed, and extra columns are ignored.

  • Delimiter: ,, ; or tab, sniffed from the header line (the first non-blank line): the candidate occurring most often in the raw header line wins; among candidates tied for most, , beats ; and ; beats tab; no candidate at all means ,. Quotes are not considered while sniffing, so header names must not contain a delimiter.
  • Quoting: fields containing the delimiter, quotes or line breaks are quoted with ", and a quote inside a quoted field is doubled (""). Only a quote at the start of a field opens a quoted field; a quote anywhere else is a literal character, so Hold 5" off floor imports as written. A quoted field may span lines; the error line of a record is the physical line where it starts. Field values are trimmed of surrounding whitespace and line breaks, so a quoted "S\n" reads as S and a value holding only a line break is blank.
  • Lines: a UTF-8 BOM is ignored; CRLF, LF and lone CR all end a line. Blank lines, and lines whose fields are all blank, are ignored.
week,day,day_name,section,rounds,exercise,time,sets,reps,load,rest,cue

These twelve columns are required. The session-format columns are optional: a sheet without one reads as if every cell in it were blank, so older sheets import unchanged.

mode,cap,bell,warn,hang,repeats,off,set_rest,prep,grip,edge,sides,tempo
ColumnRule
week, daypositive integers; empty means 1. They are positions, not labels: weeks are ordered by number and days by number within a week, whatever order the rows come in. Week numbers must be 1…N without gaps, and so must the day numbers within each week. All rows of a day must be contiguous; weeks may be interleaved (week 2’s rows between week 1’s days) as long as each day’s rows are contiguous.
day_nametaken from the first row of a day; empty means Day <day>, except in a sheet with exactly one day, where that day takes the plan name so the sheet imports as a single workout. day_name must be the same on every row of a day: a later row may leave it blank (it is ignored) or repeat the first row’s name, but a different non-blank name is an error; when the first row leaves it blank, every later row of the day must too.
sectiontrimmed; must not be blank. Consecutive rows with the same section form one block
roundssection-level; empty means 1, maximum 1000; must be blank in a superset, AMRAP or For Time section
modesection-level; blank means normal; otherwise normal, superset, amrap or fortime (lowercase, as in JSON)
capsection-level; seconds 1–86400; required in an AMRAP or For Time section, not allowed elsewhere
bellsection-level; yes, no, true, false, 1 or 0, any case; blank means no bell; normal sections only
warnsection-level; seconds 1–86400; only with bell; shorter than every timed row’s time in the section
time, rest, hangseconds, 1–86400
off, set_rest, prepseconds, 0–86400; blank means 0
sets1–10000 (on a hang row, blank means 1)
repeats1–1000
repsstrength rows: split at /, each part 1–10000; more than one part is a list with one value per set (12/10/8). AMRAP and For Time rows: the whole cell, never split; only digits read as a number (1–10000), anything else as text (10/side, 400 m)
loadstrength rows: split at / only when the part count equals sets (60 kg/65 kg/70 kg on a 3-set row is a list; 20/14 kg or RPE 7/8 on a 3-set row stay one load); a list part must not be blank. Other rows: one load, never split
sides1 or 2; blank means 1
grip, edge, tempo, cuetext; blank means absent

Section-level columns (rounds, mode, cap, bell, warn) are set by the first row of a section; later rows leave them blank or repeat the same value, and a different value is an error naming the line. A column the section’s mode never allows (bell and warn outside a normal section, cap outside AMRAP and For Time, rounds outside normal) is an error on every row, not only the first: a redundant bell,no on a later row of an AMRAP section errors exactly as bell,yes would, it does not import silently.

Rows are classified by their non-blank cells, as JSON items are by their keys. In an AMRAP or For Time section every row is a block exercise (exercise + reps, optional load and cue). Elsewhere hang makes a hang row (exercise + hang + repeats), then time a timed row (exercise + time), then sets a strength row (exercise + sets + reps), then a lone rest a rest row. A non-blank cell in a column the row’s kind does not allow is an error naming the line (sets is not allowed on a timed row, time is not allowed in an AMRAP section), checked in the JSON key order. A superset section holds strength rows only, all with the first row’s sets, rest only on its last row, and at least two rows.

Any other combination is an error naming the line number. A gap in the week or day numbers is an error naming the line of the last data row; a day whose rows are split by another day’s rows is an error naming the first row that returns to it. The plan name is not in the CSV; importers use the file name. A day whose rows flatten to more than 5000 steps is rejected, naming the line of the day’s last row.

Sdílení se sportovci

  • Pošlete soubor přes AirDrop, zprávy nebo e-mail. Otevře se přímo v Sestavě.
  • Umístěte soubor na svůj web a sdílejte odkaz. Otevření na iPhonu plán rovnou importuje.
  • Z odkazu udělejte QR kód na nástěnku tělocvičny nebo do tištěného materiálu.

Kontrola

Sestava zkontroluje každý plán před importem a řekne sportovci, co a kde je špatně. Zprávy uvádějí klíč a jeho místo v souboru, například:

  • Položka v blocks[1].items[0] musí mít právě jeden z klíčů „time“, „sets“, „hang“ nebo „rest“
  • Klíč „tempo“ v blocks[0].items[0].tempo není povolen u časované položky (klíč, který formát definuje, u druhu položky, která ho nepřijímá, je chyba, nikdy se neignoruje; jedinou výjimkou je vyhrazený klíč progression, který se ignoruje)
  • Klíč „rest“ v blocks[0].items[0].rest je povolen jen u poslední položky části Superset
  • CSV, řádek 9: hodnota ve sloupci rounds musí být stejná jako na prvním řádku části
  • „Neznámá kategorie {raw}; plán se otevře bez kategorie.“ (upozornění, ne chyba, když je kategorie neznámá)

Aplikace tyto zprávy dosud zobrazuje v angličtině; česká verze aplikace ještě nevyšla.

Verze

format is sestava.plan/<major> or sestava.plan/<major>.<minor>. Missing minor is 0.

  • Same major: compatible. Readers accept any minor of a major they know.
  • Newer minor than the reader knows: read it, ignore unknown keys, show a review warning.
  • Newer major: refuse with a clear error.
  • Older major (once a major 2 exists): readers migrate explicitly.

A minor bump adds optional keys that do not change how a plan is executed. A major bump may change or remove anything. Any key that changes how a plan is executed is a major change once readers ship: an older reader would ignore it and run the plan differently. Closed value sets (category) grow in minors; readers treat an unknown value as absent and warn. The current version is sestava.plan/1.0.

Bounds are part of 1.0; raising a bound is a minor change, lowering one is major.

1.0 is unreleased; bounds, strictness rules and keys added before release are part of 1.0.

Historie změn

1.0 — 2026-09-24

Initial version: programs (weeks/days/blocks/items) and single workouts (top-level blocks); timed, strength and rest items; rounds; cue, load and image; CSV variant.

Tightened before release: a timed item with rest is invalid (JSON and CSV), and in CSV a rounds value on a later row of a section must match the first row (blank inherits).

Bounds: time and rest are 1–86400 seconds, rounds is 1–1000, sets and reps are 1–10000; a day that flattens to more than 5000 steps is rejected. cue and section are trimmed (a blank cue is absent, a blank section is invalid); load, author, cue and image may be null. In CSV, a day’s day_name must match across all of its rows, and a blank section is an error naming the line (as in JSON). A sheet with exactly one day whose first row leaves day_name blank is a single workout: the day takes the plan name. When a day’s first row leaves day_name blank, its later rows must leave it blank too. In CSV, field values are trimmed of surrounding whitespace and line breaks.

CSV reading: the delimiter is ,, ; or tab, sniffed from the header line (the first non-blank line; ties go to ,, then ;, then tab); only a quote at the start of a field opens a quoted field, elsewhere it is literal; header names are trimmed; quoted fields may span lines; CRLF, LF and lone CR all end a line; blank lines are ignored. week and day are positions: weeks and days are ordered by number, numbered 1…N without gaps, and a day’s rows must be contiguous.

Plan identity and presentation, added before release: optional top-level id (1–64 characters from A–Z a–z 0–9 _ -) and version (integer 1 to 2^53 − 1), present together or not at all; category (strength, hiit, mobility, core, cardio, recovery, climbing, yoga or boxing; writers emit only these, readers treat an unknown string as absent and the review warns, null is invalid); note (string or null, at most 500 code points as written, trimmed, blank is absent). Writers emit the canonical form: schema key order, two-space indent, trailing newline. Link identity is now id first, then the SHA-256 of a /p#… link’s decoded payload, then the canonical URL. The duration estimate counts a reps step as 45 seconds (was 40).

Normalisation, settled before release: author is trimmed like every other text field and a blank author is treated as absent. Trimming removes exactly the Unicode White_Space characters (U+0085 and U+00A0 included); U+FEFF is not whitespace and is kept.

Session formats, added before release: a fourth item kind hang (hang, repeats, optional prep, off, setRest, sets, grip, edge, cue, load, image); block mode (normal, superset, amrap, fortime) with cap (seconds, required in amrap and fortime), bell and warn (normal blocks only); the block exercise, the item shape of amrap and fortime blocks (exercise, reps as a whole number or text, cue, load); sides (1 or 2) on timed and strength items; tempo on strength items; list-valued reps and load on strength items (one value per set). A key the format defines but a kind or mode does not allow is now an error at that key. The canonical order and default omission cover the new keys. CSV gains the optional columns mode, cap, bell, warn, hang, repeats, off, set_rest, prep, grip, edge, sides and tempo, and reps on strength rows splits at /. The duration estimate counts prep, hang and off steps at their seconds and amrap and fortime steps at their cap. Error vectors (vectors/invalid/) pin the first error for every invalid fixture.

Names, settled before release (#16, 2026-10-05): the format id is sestava.plan/1 (the earlier working name setlist.plan/1 was never shipped and is not read), the file is name.sestava (one extension, so iOS types it as a Sestava plan), the UTI is app.sestava.plan conforming to public.json, the MIME type is application/vnd.sestava.plan+json, and the schema’s $id is the URL the site serves it at.

Progression, reserved before release (#408): an optional progression object on the document and on strength items (rule, step, range, ladder, stepBackAfter, deloadEvery; semantics in #407). Readers that do not apply progression ignore it; readers that do never reject a plan over it (a review warning). The canonical form leaves it out until a writer models it.