Your plan, not someone else's.
The first thing every fitness app asks you is some version of "what's your level?" Then it gives you the same three buttons no matter what you tap. ZenMotion did this too. v1.7.2 throws that out and rebuilds onboarding around two ideas: ask people what they actually train, then respect the answer, and if they're an existing user, the app already knows — don't make them prove it twice.
This release is the biggest content + flow change since v1.6.4. It introduces a 10-week adaptive cycle for people who can't do a push-up yet, a calibration-aware path for everyone else, and a way to redo any of it from your Profile when something feels off.
Three tiers, two paths
Onboarding now ends with one routing question — "What brings you here?" — and three honest options:
- I'm starting from zero. No assumed strength. No "30s active hang to qualify." We meet you at wall push-ups and walk you up.
- I train sometimes. Some gym or sports background, ready to put structure on it.
- I know what I'm doing. Experienced calisthenics — give me the tools, get out of the way.
The first option routes into Foundations — a 10-week adaptive cycle built specifically for beginners. The other two route into the existing custom-plan structure (Mon/Wed/Fri default, fully editable, focus skill, the strength splits you already know). The tier you pick decides which Home view you see, which content gets prescribed, and how progression decisions get made on your behalf.
Foundations — 10 weeks, 3 sessions a week, ~20 minutes each
A complete beginner opening a calisthenics app is asked to commit to something every other app gets wrong. They get a 45-minute workout with strict pull-ups on Day 1 and either quit or hurt themselves. Foundations is the opposite end of the dial:
- 3 sessions a week, Mon / Wed / Fri. Short enough to not skip. Long enough to matter.
- 15–20 minutes per session. Two warm-ups, three main movements, one core finisher. Nothing more.
- Adaptive prescription per movement family. Push, pull, squat, hinge, core — each has its own "rung ladder" of progressions. You're on whichever rung the diagnostic placed you on, and you stay there until you crush it twice in a row with good form.
- No fixed weekly schedule of exercises. The system picks today's exercises based on which rungs you're on. It changes per session.
- Built-in graduation. After 10 weeks, or earlier if you hit all three benchmarks (8 floor push-ups, 30s active hang, full-depth bodyweight squat), you get a graduation sheet with three branches: pick a focus skill, train more often, or keep building Foundations.
A push rung 1 user trains Wall Push-Ups. Rung 3 trains Desk Push-Ups. Rung 7 trains Pike or Diamond Push-Ups. The same template — "today is upper body intro" — produces different sessions for different rung levels. The app never says "you should be doing real push-ups by now"; it just keeps loading the right variant for who you actually are right now.
When you do crush a rung two sessions in a row, a celebration toast lands when you finish that workout: "Push rung 3 → 4." Big haloed badge. Single-tap dismiss. The mechanism is finally visible — without it, beginners had no idea the system was adapting around them. This was the highest-leverage UX miss in the original Foundations spec, and the fix landed in this release.
The diagnostic respects what you've already done
Here's the part nobody seems to bother building. If you're updating from v1.7.1, you already have a calibration baseline. You have a UserMax for strict pull-ups. You have 30 sessions logged. You have a strength profile in the Profile tab. The new onboarding reads all of that and pre-fills its answers.
When you reach the tier question, the option that matches your actual training history is already highlighted with a small "BASED ON YOUR HISTORY" pill. When you reach the diagnostic step — "can you do a push-up?" — the radio is already on the right answer if your UserMax for push_up says yes. You're tapping Continue twice to confirm the system already knows you, not re-proving you can do a thing you've done 200 times.
The pre-fill logic is conservative — it won't suggest a tier too high or pre-select an answer you didn't earn. A user with one logged push-up max of 0 reps gets the radio left blank, not auto-filled to "FEW". The bias is consistently toward "don't insult them by guessing wrong."
Brand-new users (no signal at all) see exactly the same flow as before: clean form, no pre-selection, no history pill.
Press to Handstand
Small one but it's been bothering us for two releases: the skill family was named "Push to Handstand" in the app despite the standard calisthenics term being "Press to Handstand" — you press up into the handstand, you don't push. Catalog content, accessory pools, and the Skill Focus Split day-programs all carry the new name. Historical data (your logged "Push to Handstand" runs from before the rename) keeps working — Stats just shows them under the new name retroactively, since program rows are referenced by ID, not name.
Recalibrate from Profile
If the app guessed your tier wrong, or if you crossed a boundary mid-cycle (graduated from Foundations and now want a real split, or the opposite — bumped into a wall and want to drop back), there's a "Recalibrate my plan" button under Profile → Plan. Tap it, confirm the destructive intent, and the onboarding coordinator re-presents — diagnostic and all, pre-filled from your current maxes. Your run history, achievements, calibration baselines, and tracked maxes stay intact. Only the plan structure resets.
This used to be a "delete some rows in the database" operation — quietly possible by support, invisible to users. Now it's a button.
Programs tab uses the real artwork now
Programs tab cards used to render with monochrome SF Symbols — a dumbbell glyph for strength, a gymnast pose for skills. Replaced with the curated hero artwork for each program. Each Skill Focus Family (Handstand Focus, Front Lever Focus, Press to Handstand Focus, etc.) now shows its dedicated skill art instead of the generic strength placeholder. Custom programs you've authored keep the tinted person glyph since they don't have curated artwork — a clear visual signal that the program is yours, not ours.
The onboarding welcome carousel also got a cleanup pass: it used to render every single program in the catalog (50+ cards including all 5 day-programs per Skill Focus Family). Now it shows one card per learnable skill, with the cleaned name — "Handstand", not "Handstand Focus: Foundation". Much closer to the "here's what's possible" feel the carousel is supposed to convey.
What's running underneath
Implementation-side, this release introduces a few model changes worth noting if you're curious about the architecture:
UserDiagnostic,UserPlan,UserMovementRung— new Prisma tables. Diagnostic captures your tier + soft assessment answers; plan owns kind (FOUNDATIONS vs CUSTOM) plus pacing fields likefoundationsDayandfoundationsWeek; movement rung tracks per-family progression (push: rung 3, pull: rung 1, etc.).rungResolverservice. Pure function. Takes(template, rungs)and returns the resolved exercise list for today. No DB writes, all derived. The 6 templates × ~7 rungs × 5 families produces ~thousands of possible session variants without storing any of them — they're computed on demand.rungContenton UserExerciseProgress. When a Foundations user starts a session, the resolved exercise content is written into a JSON column on the UEP row instead of being FK-joined to a staticProgramExercise. Keeps the catalog clean (no synthetic rows accumulating) while letting/completereconstruct the prescription for the bump check.evaluateRungBumps. The bump-on-crush logic runs after/completelands. Bump rule: hit top of rep range across all sets, form intact (UEP.isComplete), and at least one prior session on the same rung. Conservative on purpose — we'd rather miss a bump and let the user crush twice than promote them off a movement they're not stable on.
The full architecture lives in docs/my_plan_schema.md and docs/foundations_plan.md in the API repo if you're a returning early-adopter who likes reading source.
Smaller wins
- Onboarding "starting from zero" tier no longer reads as pre-selected. Earlier internal test caught this: the visual emphasis on the primary tier (filled background, teal stroke) made it look like it was already chosen, but Continue stayed disabled until you actually tapped. Now selected and primary are clearly distinct: filled checkmark + saturated stroke = selected; subtle teal tint = primary suggestion.
- OnboardingCompleteView is tier-aware. Tier 1 users see a Foundations preview card with the 10-week framing. Tier 2/3 users see a "Your plan is ready" card with the days-per-week summary. No more "Here are 3 recommended programs" cards for users who already have their own split.
- Programs tab filters out the Foundations Library. The library (program id 100) exists as a backing catalog for the rung resolver — never user-visible. Filtered out of every list it could appear in.
- PlanManager refreshes after every workout finish. Foundations users now see their day counter and next session update immediately on Home after saving a workout, instead of having to background and re-foreground the app.
This release is the one where the answer to "what kind of person is this app for?" stops being "people who already know what they're doing" and becomes "anyone who wants to get strong with calisthenics, including someone who's never trained before." Foundations is a real thing now. The architecture's in place for the same adaptive treatment to spread to skill programs in a future release — which is the long-term plan.
If you've been using ZenMotion since the early releases, this is the biggest change you'll feel since the active-workout sheet refactor in v1.7.1. Existing splits keep working, history is preserved, your focus skill carries over. The diagnostic just runs once on next launch, and onwards it knows you.
As always — support@zenmotion.app reaches me directly. Every message still gets a real reply from the person who wrote the code.
— Jacob