The first workout finally fits — onboarding activation, honestly measured
Onboarding was 91% completion. The next number was 29% — the share of installs that actually started their first workout. Somewhere between "I'm done setting up my plan" and "I'm doing the first session", most people walked away. This release goes after that gap.
The three fixes
One less tap between finish and start. The old flow: complete the setup questions, land on a celebration screen with a "Go to Home 🏠" button, tap, land on your first workout preview, tap Start. The celebration screen served no purpose beyond the tap itself — the copy said "Home", the button was indigo, the mental model it built was "go browse", which is the exact opposite of what onboarding just tried to set up. v1.22 removes it. After the last onboarding question, ZenMotion shows a clean "Setting up your plan…" beat while the background write finishes, then goes straight to your first workout prompt with a caliLightblue "Start now". One less tap, and the verb finally points forward.
Diagnostic answers stop lying to the recommender. If you told the app you were advanced and picked Press to Handstand as your focus, the first workout it would recommend was Wall Push-Ups Level 1. The reason was mechanical: onboarding diagnostic answers seeded your progression rungs (correctly, high) but never populated the calibration data (UserMax) that the first-workout recommender was checking. No calibration data equals "prerequisites not met" equals fallback to the beginner strength program. Which is completely wrong for someone who just said "I want to learn press to handstand."
Fixed: the fallback destination is now the picked skill's own calibration test. Pick Press to Handstand — land in Press to Handstand Calibration. Pick Front Lever — land in Front Lever Calibration. Pick Planche — Planche Calibration. Same for all 11 skill families. The framing is honest: "let's measure where you actually are on this skill" instead of "here are some knee push-ups". Complete the calibration, and your rung state populates for real, which unlocks the Focus Split on the next Home load.
The calibration checkmark actually toggles again. A follow-on from v1.21's calibration fix. Committing a set at 0 on a calibration ("I can't do a single pull-up right now, and that's the honest answer") worked in v1.21 but the checkmark got stuck — once filled, tapping it again didn't clear it. v1.22 makes the un-check path truncate the array back to pre-commit length instead of writing a zero, so the state actually returns to "not logged." Both directions work now.
What changed under the hood
The onboarding coordinator's .complete step lost its tappable button and became an auto-advance loading beat. The saveProfile() + POST + advance-to-.ready sequence now runs on view appear, so the user sees a spinner for the network round-trip (usually 500 ms – 2 s) instead of a celebration screen they can't read before it disappears.
The first-workout resolver's routing table grew a calibrationProgramId per skill family. When the user picks a focus skill and has no UserMax history to satisfy the Focus Split's prerequisites, the resolver now hands back that calibration instead of the generic Foundations-tier strength program. iOS's OnboardingReadyView already knew how to render kind: "calibration" — the eyebrow reads "LET'S MEASURE YOUR STARTING POINT" and the support copy is "A quick honest-effort test. Your results tune every future workout to your actual level." No iOS change needed; the backend just started sending the right pointer.
The calibration set-row's un-check path branches on isCalibration and calls a new clearSet(for:set:) helper that truncates both the reps and weight arrays back to set - 1. Non-calibration exercises stay on the classic zero-write path — that one flips isLogged correctly because the check is arr[idx] > 0. Calibration needs the truncation because for calibration, arr[idx] == 0 is a legitimate committed value ("I couldn't do a single rep, log that honestly"), so length is the only signal that distinguishes "committed" from "cleared".
Privacy policy — HRV and readiness check-ins
Small housekeeping catch-up from v1.20. The privacy policy now explicitly covers the readiness check-in log (score + tag + timestamp) and the HRV read from HealthKit that powers the readiness suggestion. HRV data itself stays on device — only the resulting score, if you tap to confirm it, reaches our servers.
Feedback
Getting the first-workout number up is the whole point of this release, so if you saw the onboarding flow this week and it still didn't fit — support@zenmotion.app, we want to hear about it.
Train well.