An honest first run

A UX audit walked through ZenMotion as a genuine beginner earlier this week and caught things we couldn't see anymore. Most were fixable in an afternoon. The two big ones — one that shipped Dead Hang to a bar-less user, and one that celebrated the wrong milestones first — needed structural rethinks. Both landed in v1.42.

The Dead Hang problem

A brand-new user tells the app "Bodyweight only — wall and floor, no gear needed" during onboarding. The app promises "We'll adjust every session to match." Then the first Foundations session prescribes a Dead Hang: three sets of 30 seconds on a pull-up bar they don't own.

Root cause: pull-family rung 1 in the Foundations ladder IS Dead Hang, and Dead Hang IS bar-required. When Equipment Mode was designed we assumed "rung 1 of every ladder is bodyweight-only in practice" — which is true for push (Wall Push-Up), squat (Chair Squat), core (Dead Bug), hinge (Glute Bridge), and every skill ladder. But pull is different: even the base of the ladder requires you to hang from something.

Old behaviour: no rung matches → defensive fallback to rung 1 anyway → Dead Hang shows up. New behaviour: no rung matches → skip the entire slot. Session becomes shorter — a bar-less Upper Body day trains push, core, and mobility only, no pull work — but nothing shows up you physically cannot do. The user's underlying pull rung is untouched; the moment they add a bar, the next session's pull slot resolves at their true rung again.

If you were on v1.41 and saw content that didn't match your gear, this is fixed. Sessions from v1.42 forward will only prescribe movements you can actually perform.

The celebration ordering fix

Same audit run: user finished their first workout and got three sheets in a row — 30-Second Dead Hang milestone, 60-Second Dead Hang milestone, First Workout — for holds they never physically did. Because we drain remaining celebrations after the first dismiss (v1.41's "one sheet per workout" invariant), First Workout — the one legitimate unlock — was buried third and never seen.

Fix: First Workout is now always promoted to the front of the celebration queue. Backend can send unlocks in any order; iOS always shows First Workout first when it's in the batch. Everything else drains silently and shows up on the Achievements tab where you can find it later.

The bigger question — whether we should be unlocking hold-milestones at all when the user didn't physically perform the hold — is a verify-vs-trust design conversation for a later release.

Skill-clamped sessions no longer bump the wrong ladder

If your planche session gets equipment-clamped down to push-family basics because you're training without parallettes today, we no longer bump your skill:planche rung when you crush that clamped content. The design intent, in your own words when this came up during self-testing: "man ska kunna bibehålla sina muskler, och göra utmanande träning för att på sikt klara samma mål."

Progression on a ladder you're not actually training that session pauses. Underlying muscle groups still build. Add the parallettes back and the next session bumps normally.

Copy polish

Three things the audit caught that read as sloppy:

  • "Signed in with Apple 🍎" on the welcome screen showed the Apple label even after a Google sign-in (Google shipped as a second path in v1.15). Now provider-neutral — shows your email when known, plain "Signed in" otherwise.
  • "Program saved successfully!" after a workout was misleading because Create Program is a separate feature. Now "Workout saved."
  • "WELCOME BACK" greeted brand-new accounts on their first Home visit. Now shows "Welcome" the first time and "Welcome back" from session 2 onwards.

None of these change how the app works. They stop it from lying about small things.

Under the hood

Nothing you'll notice, but future-me appreciates the note: applyEquipmentClamp now returns { rung: null, unsatisfiable: true } instead of defaulting to rung 1, rungEvaluator has a new priority-0 equipment-clamp brake that beats even the max-effort brake, and PostWorkoutInbox.push() runs a pure promoteFirstWorkout sort before setting the pending queue. All three ship with regression tests locking in the specific audit scenarios.

Coming next

The audit surfaced more Wave 2 material we're keeping on the shelf for v1.43 — a unified progress indicator through onboarding, consolidating the three overlapping goal questions, a "here's your plan" reveal at the end of onboarding so the effort you put into 9 answers feels rewarded. That release will be about making the reasoning behind ZenMotion visible instead of buried.

Available in v1.42.0 on TestFlight now. Rolling out to the App Store this week. Feedback welcome at support@zenmotion.app.