Set a baseline. Train against it.
Shipped the strength-split rotation a couple of weeks ago. The next two weeks ate the gap between "the app holds your workout" and "the app knows what you can do". This release closes it.
Three big threads landed: a workout summary that wraps every session with real numbers, a Strength Calibration test that gives the app your bodyweight strength baseline, and a personalized Strength Profile that tracks how you grow from there. Plus a long list of polish that finally takes the keyboard out of logging.
Strength Calibration
A new program category called Tests, with one program inside it for now: Strength Calibration. Seven movements, ~20 minutes:
- Strict pull-ups (max reps)
- Strict push-ups (max reps)
- Dips (max reps)
- Active hang (max seconds)
- L-sit (max seconds)
- Bodyweight squats (max reps)
- Bodyweight rows (max reps)
Each one is a single all-out set logged in the same inline interface as any other workout. The difference: when you finish, the app stores your value per movement as a baseline — a known capability point you can train against.
The recommended cadence is monthly. Once your latest calibration is more than 30 days old, the app surfaces a "time for a strength test" card on Home. Brand-new users see "set your baseline" instead — same card, different framing.
Strength Profile
A new section in your Profile tab lists every movement you've measured, with your current best plus when it was recorded. Tap a row and you get a per-movement detail view:
- A line chart of every recorded max over time
- Calibration points (circles) and session PRs (diamonds) rendered distinctly so you can see which records were earned during a test workout vs hit organically in a normal session
- A history list below the chart
Same-day records collapse to the day's best — if you re-tested a movement to double-check, the chart shows your better attempt, not a flatline.
Session PRs and progression
ZenMotion now watches your regular workouts for personal records, not just your calibration tests. If "Strict Pull-Ups" shows up in Day 5 Pull & Skill and you hit 12 reps — beating your calibration of 10 — the app silently records a session PR.
When you finish the workout, the summary screen shows it in a Personal Records block:
🏆 Pull-Ups — 12 reps (+2 from calibration)
For calibration runs the same block becomes Test Results and lists every tested movement with the delta vs your last calibration. Improved, same, regressed, or first-time-logged — all four are valid outcomes; the app names them without judgment.
Prerequisites on skill programs
The skill programs (Planche, Front Lever, Muscle-Up, etc.) now declare what bodyweight strength they assume. If your maxes are below the recommended baseline, the program card shows a small hint:
ⓘ Recommended: 8+ pull-ups, 8+ dips
This is a soft gate. Tap still works, you can still start the program. The hint exists because most skill progressions have implicit prereqs anyway — without 5+ strict pull-ups, the Front Lever progression is going to feel impossible no matter how much you grind. The app just makes the implicit explicit.
Ready to graduate
When you finish a workout where every prescribed set hit its target, the summary screen now suggests moving up a level next time:
🏆 Level 3 crushed. Try Level 4 next time to keep progressing.
It's a suggestion, not an auto-promotion. The level picker still lives where it always did; the hint just makes sure you notice you've earned the option.
A workout summary that lands somewhere
The big UX shift this release: workouts end with a screen, not a toast.
- Header — program name + actual duration
- Test results / Personal records — described above
- Progression — for every exercise where you beat your previous-run's volume, a percent gain
- Exercises — each attempted movement with a one-line summary
- Save / Continue / Discard — three explicit choices. Save commits the run. Continue takes you back to the workout in the same state. Discard throws it away, with a confirmation when there's actually data to lose.
The point: finishing a workout should feel like landing somewhere, not closing a tab.
The keyboard rarely shows up anymore
A pattern this release codifies: tapping a text field should be the backup path, not the primary one.
Reps now use a [−] 10 [+] stepper. Tap the number to type if you need a big jump; otherwise just nudge.
Holds have a Start/Stop timer right on the row. Tap ▶︎ → the field becomes a live counter. Tap ⏹ → the elapsed value commits as the set's logged amount. Wall-clock based, so it stays correct if your screen sleeps mid-hold. The number turns ZenMotion-teal and you get a haptic when you cross your target.
Weights are still a TextField. Decimal pad is the right tool for 80 kg vs 82.5 kg.
Side effect: five sets of Bench Press now logs in ~30 seconds with zero keyboard pop-ups until you change the weight.
Smarter finish-or-next
Two improvements to the bottom-button logic:
- "Finish workout" surfaces from anywhere once every exercise has at least one set logged — not just on the last exercise of the last phase. You're never trapped in a flow that forces you to walk through completed work to call it done.
- "Next exercise" prioritizes the unattempted — if you've finished what you're on and there's an untouched exercise earlier or later in the program, the button routes you there instead of just incrementing by one. The app actively walks you past skipped work rather than letting it slip past unnoticed.
A progress strip across the top
Above the exercise content, you now see a row of small dots — one per work unit. A circuit collapses to one dot, since it lives on one screen anyway. Solid ZenMotion-teal = unit done. Half-opacity = partially logged. Outlined = untouched. The current unit gets a ring around it.
Glance answer to two questions: "How many units am I from done?" and "Did I skip any?" Tap a dot to jump.
Rest timer that survives backgrounding
The old rest timer was a counter that ticked once a second. If you backgrounded the app for 30 seconds — incoming text, glancing at music — the timer paused there. You came back and it kept counting from where it stopped.
Now it's wall-clock based. Start stores an end-time. Background for 30 seconds, foreground again, the timer immediately reads the actual elapsed value. 60 seconds of rest means 60 seconds.
Smaller fixes
- Pause auto-commits a running hold timer. If you tap Pause mid-hold, the elapsed value commits to that set and the timer stops. Without this, pause time was bleeding into the elapsed.
- Adaptive stepper step size. Reps step by ±1. Holds step by ±1 under 60 seconds, ±5 under 10 minutes, ±30 above that.
- Toolbar chevrons skip the whole circuit. Tapping right in a 3-exercise circuit used to take 3 taps to leave; it now jumps straight to the next phase.
- Sets are ordered. Set 3 doesn't accept input until Set 2 is logged. Prevents weird
[0, 0, 8]arrays in your history. - mm:ss display for long holds. A 20-minute Run hold reads "20:00" instead of "1200" — above 60 seconds the field uses
mm:sswhenever it isn't focused for typing. - No more sheets for logging. The old log-a-set sheet is gone; everything happens inline on the row, including supersets and accessory circuits.
- Best-ever maxes, not latest. The Strength Profile shows your highest recorded value per movement, not the most recent measurement. A tired-day calibration won't make it look like you got weaker overnight.
- Blog archive. All release notes now live at /blog with an Updates link in the nav, so old posts don't disappear behind the latest one.
The thread tying this release together: ZenMotion now has an opinion about what you can do, and it uses it to guide the next session. Calibration is the input, the Strength Profile is the memory, prereqs and graduation are how it shapes the prescription. Most of the underlying autoregulation is still ahead — the prescribed numbers don't yet adapt to your performance — but the foundation is in place.
Email me if anything feels off — support@zenmotion.app. Every message still reaches the person who wrote the code.
— Jacob