Exercise is the second worked case of the catalog, beside Diet and Nutrition. Exercises are definitions, items of a catalog; a set is what is logged, an entry of a trackable. The Strength module (strengthModule in packages/core/src/modules.ts) makes the Exercise catalog, the Set trackable, Daily Volume and the Workout routine, filed under the Fitness tag. The Cardio module (activityModule) makes the Activity catalog, Cardio and Exercised (Modules).
The Exercise catalog
Each movement is an item of the Exercise catalog, whose basis unit is the rep and whose fields every exercise carries:
| field | label | kind |
|---|---|---|
bodyweight_factor | Bodyweight × | number |
muscle_groups | Muscles | tags |
secondary_muscles | Also works | tags |
equipment | Equipment | Category |
category | Type | Category |
force, mechanic, level | Force, Mechanic, Level | Category |
area | Body area | Category, the bundle’s grouping |
- Muscles and Also works are tag fields (
{kind: "text", list: true}): comma text that the union rule folds and the UI shows as a chip per value. Being a list is the field’s own declaration, never guessed from a comma in the value, so a single muscle shows as a chip like several. - Equipment is the modality: barbell, dumbbell, body only, kettlebells, and the rest.
- Bodyweight × is the share of bodyweight a movement lifts, and no public dataset carries it, so it is Datler’s own. It is set only where it is defensible and only for body-only movements: 1 for a pull-up, a chin-up and a dip, which lift the whole body by definition, and 0.7 for a push-up, the one measured figure. Everywhere else it is absent, so the weight fill does not fire and the weight is typed (
scripts/db/gen-exercises.ts).
The Categories come from the values the bundle uses (Catalog Categories). Like a food, an exercise is never logged itself; an entry names it.
Where exercises come from
873 movements from free-exercise-db, shaped by pnpm run db:gen-exercises (scripts/db/gen-exercises.ts) in the same way as the USDA foods: the raw dump is gitignored and the shaped bundle (scripts/db/exercises.generated.json) is committed, so the starter set can be pruned again without downloading anything. Its starter set is 124 exercises, each with its Body area (packages/core/src/exercises.starter.json); how a bundle becomes a starter set and reaches a profile is in Catalog Entities.
The bundle buys names, muscles and equipment, which the picker and the muscle coverage use. It is not knowledge a person could not otherwise have: what matters about an exercise is what they lifted, and that they log.
Two fields are left out on purpose, since a field nothing reads is worse than none. The instructions were most of the raw data and nothing computes, filters or folds on them; their only place would be a table cell, the wrong shape for a paragraph. The images are paths inside the source repository: storing them points at nothing, linking to the repository’s host contradicts local-first, and bundling about 1,700 photographs is a size cost. Either comes back by adding the field to gen-exercises and to the Exercise catalog’s fields.
Licence. free-exercise-db is released under the Unlicense, into the public domain, but its README says the data was restructured from Ollie Jennings’ exercises.json without saying where that came from, so the chain of title is unverified. wger’s exercise data is CC-BY-SA 4.0 with a licence and author on each record, at the cost of share-alike and of having no force, mechanic or level.
Logging: a Set, in a Workout
Set is a record of three required fields: the Exercise (an item field on the Exercise catalog), Reps (a whole number of at least 1) and Weight (kg). One entry is one set, and its own Reading is a Count. Logging a set at a time is what makes a sets count or a list field unnecessary: a drop set or an AMRAP is just its sets, each as it went, and a routine’s page has Add another, which adds a set like the last.
{ exercise: Zottman Curl, reps: 10, weight: 16 }
- Volume and Estimated 1RM are computed fields (
computedon a record field): worked out from weight and reps when read, never typed or stored, so changing a formula reaches every set. Volume is weight × reps; Estimated 1RM is Brzycki’s, weight × 36 / (37 − reps), exact at 1 rep and read from sets of 1 to 10 reps only (within), past which every estimate overshoots. Labels leave them out; tables, charts and Readings read them like any field. - Starts as last time. Set has Prefill on: a new set starts as the last one did, and picking an exercise starts its reps and weight from that exercise’s own latest set (Frictionless Capture).
- The weight fill. Picking an exercise sets the weight to its Bodyweight × times the latest entry of a trackable measuring mass, rounded to one decimal (Capture Model). The number is stored with the entry, not worked out again later, so a set keeps the bodyweight of the day it was done.
- A template line plans, and leaves the weight to last time. A field a line sets is the plan; one it leaves blank starts as that exercise’s latest set did, and the weight fill wins over that for a bodyweight movement (
useLineFills). A planned weight would be out of date by design: the point of training is that it grows. - The Workout is a Routine: one entry per session, holding its stretch, its runs and its sets, each an ordinary entry of its own trackable.
- Set’s summary strip counts the sessions and the sets, sums the volume and takes the top set.
- Rest days, from its own module, is yes on a day with no Set.
Cardio over an Activity catalog
Cardio is one trackable over a catalog, as Set is over Exercise. The Activity catalog holds Walk, Run, Swim, Bike and Hike, each only a name. Cardio has a start and a stop, the activity (required) and a distance in the person’s unit; its own value is how long it lasted, and a day’s cardio is the sum. Exercised is a yes/no, Any over everything under the Fitness tag, Cardio and Set included, so a gym day counts as having exercised; it is the tag’s aggregator. Because the module makes a catalog, Cardio does not follow its module’s updates (Modules). In the demo profiles Chad’s walks and runs are Cardio entries, and a jog inside a Workout is a Cardio entry held by it.
Volume
Volume is reps times weight, worked out per set, the same shape as a food’s calories being its amount times a value. Daily Volume is a derived trackable: Set’s entries mapped to reps times weight and summed by day (Derived Trackables). A week or a month of it is a period or a window over that series, and a running total across all time is an achievement’s total measure (Achievements). One exercise’s volume narrows Set to that exercise, and a session’s volume reads each Workout as one (Each event as one), both offered wherever a Reading is narrowed (Readings).
Muscle coverage is a fold, not a series. The Set’s own summary strip joins the Muscles of the exercises a set of entries holds (summaryValue’s Union), on a template’s card, an Arm Circuit or a Workout. There is no “muscles trained this week” trackable: a Reading reaches an item’s values only through an item field that carries an amount, and Set’s Exercise carries none (How an entry reads an item). Coverage also counts the primary muscles only, since the union folds one field: a pull-up’s lats count, its biceps (Also works) do not.
A circuit is a template on the Workout
A circuit is not an exercise: Arm Circuit has no Bodyweight × and cannot be one entry. It is a Template bound to the Workout, whose lines are Sets, three rounds of:
| exercise | reps |
|---|---|
| Zottman Curl | 10 |
| Overhead Press | 10 |
| Romanian Deadlift | 20 |
| Lateral Raise | 10 |
Each set’s weight is left blank, so it starts at what was lifted last time. Chad’s Push, Pull and Legs are the same: a stretch, then each lift’s sets in a row, the reps planned and the weight left to last time. Started, a routine takes its template as its plan, each set a row ticked as it is done; logged after the fact, it holds them all. A template’s card summary (the volume, the muscles covered) is a preview: the entries are the record.
One exercise’s history
There is no trackable per movement. One exercise’s history is Set’s entries filtered to it, which the entries table does with its facet on a record’s item field (recordRef in EntryFilter, packages/core/src/entry-repository.ts; Data Table).
What a lifter calls a personal best is a Reading of one exercise over all time, Max or Min of a field with no judgement word (readItem in packages/core/src/item-readings.ts, the one copy). Set declares which it shows (presentation.item_readings): Max weight, Max e1RM, Max set volume, Max session volume (Volume summed within each Workout, then the max across them), Max reps, and Max weight by Reps (a 5-rep max, an 8-rep max). Beside each exercise in a Workout and under an exercise being logged, a line reads its last time and the first two: “Last 7 reps · 75 kg, Sun · Max weight 75 kg · Max e1RM 96.4 kg”; the exercise’s page lists them all with the day each was reached, and draws one of them day by day, picked from those that are one value (Max weight by day: each day’s heaviest), as a Visualization of that Reading narrowed to the exercise. Nothing suggests an increase: the progress is read from the data. A new maximum is announced by an Achievement set up beside its Reading on the exercise’s page: the bell beside Max weight sets up a new highest of Max of the weight for that exercise, read per Workout where the Reading is.
Related
Catalog Entities · Templates · Events · Value Types · Derived Trackables · Diet and Nutrition