Meet your athlete.
Two releases ago I wrote — in the footer of a patch note, half-promised — that Athlete Profile was "coming later this year, as a read-only stats view: per-family + per-skill tier derived live from your rungs." Later turned out to be now.
v1.12.0 ships two things you'll actually feel: Athlete Profile on the Stats tab, and a home-screen widget that shows your next workout without opening the app. The rest is scaffolding — a decode-error crash we finally caught, a second crash reporter running in parallel, and a bug fix for History detail that should have shipped weeks ago.
Who are you as an athlete?
A single number for "how strong am I" was always going to lie. A person who front-lever holds 12 seconds but can barely squat their bodyweight isn't Intermediate at some average level — they're a specialist. Level pickers papered over that, then quietly stopped mattering when we started deriving experience from rung state (v1.10). Now we replace it with something that actually tells the truth.
Athlete Profile is a read-only section on Stats Overview that answers three questions at a glance:
- What kind of athlete are you right now? — an archetype title composed from your dominant training pattern. "The Puller · Lever Specialist" if pull is your strongest family and Front Lever is your best skill. "The Presser · Straight-Arm Specialist" if push dominates and Planche is on top. "The Generalist" if all five universal families sit within one tier of each other.
- How balanced is your training? — a 5-dot spread visualization. Well-rounded, balanced, specialist, extreme specialist. Computed from the variance of your family percentages, not from a self-assessment picker.
- What's your ceiling right now? — six tiers (Newcomer, Beginner, Novice, Intermediate, Advanced, Elite) applied per-family and per-skill, plus an overall tier averaged across universal families. The palette warms from muted white through teal into gold as you climb — Elite doesn't scream, it just reads as different.
If you've calibrated at least one skill, your hero card shows that skill's image behind the archetype title. Planche users see the planche photograph. Front Lever users see the FL photograph. Skill without image? Muscle-Up borrows the pull-up asset, V-Sit borrows core — proxies until we author dedicated art.
Below the hero: a Strength and Growth area callout (top + bottom family), then the full per-family list, then every skill you've touched sorted by percentage. Nothing to configure. It updates when your rungs update.
The point isn't a badge to show off. It's a mirror. If Growth area = Hinge every time you open Stats, that's the app telling you what to do with the next unfocused training slot.
Your next workout, on the home screen
The other headline: a home-screen widget ("Next workout") you can add via Add Widget on iOS. Medium size — the 4x2 tile with the same visual language as the Home tab. It shows exactly what your Home would if you tapped into the app: which skill day you're on, which rung it's building toward, streak, duration estimate, cycle-position dots.
Three states based on what's true right now:
- Ready — normal case. Teal accent, play icon, tap opens straight into StartProgram (skips Home entirely for single-level programs).
- Continue session — you paused mid-workout. Warm orange accent, forward icon, progress bar, tap resumes. Matches Home's Continue banner exactly — if the banner hides, the widget hides too.
- Rest day — moon icon, dim accent, "Recovery is part of the plan" framing. Never scolds. Rest is programmed, not a failure to log.
Refreshes automatically after each Home load and after finishing a workout. In practice you glance at your phone in the morning and the widget already has the right session queued.
Why medium and not small. Small (2x2) can't fit "Advanced Tuck Front Lever · Rung 8 of 23" without truncating. The one number that matters most (which exercise, which rung) got squeezed out at that size. Medium isn't the flashiest choice — it's the one where the widget actually earns its space.
Under the hood
The rest of this build is quality-of-life and reliability. A stack of quiet fixes, all of which would have been "someday" issues without this release cycle to bundle them into.
The history-detail crash we finally caught
Some users saw "Something went wrong!" when tapping any Foundations workout or handstand session in History. The screenshot came in from one user, then another. Server logs showed a clean 200 response — the crash was on the iOS side, decode error, no error report because it fell into a SwiftUI error boundary.
Root cause: since we moved Foundations and skill splits to run on rung-based sessions (no programExercise FK), the History-detail response was returning programExercise: null for those runs. iOS's decoder expected a non-optional value there. Boom, generic error screen.
Fix: the backend now synthesizes a programExercise-shaped object from the rung content when the FK is null. iOS decodes normally, History detail renders, the user sees their session breakdown instead of a triangle icon. Retroactive — all your old Foundations and handstand runs are visible again.
API errors that hit the ground
Related — the reason the crash above went unreported for weeks is that JSON decode failures and non-2xx responses in the API client were thrown but never surfaced to error tracking. The APIClient now captures both to Sentry with full context: endpoint, method, status code, expected type, decode-error path (which key failed), body preview. If a backend response ever again returns a null iOS wasn't expecting, we see it on the first user hit.
Firebase Crashlytics running alongside Sentry
Sentry stays primary (better breadcrumbs, richer UI, we're used to it). But native crashes now also go to Crashlytics — free unlimited quota, catches things Sentry occasionally misses, and lets us verify parity between two independent crash sources. Non-fatal errors bridge to Crashlytics automatically so both dashboards agree. Debug builds are excluded so test crashes don't pollute production.
Belt and braces. If the app is quiet for a week, that's the truth reported twice.
Slot caps on skill sessions
If you had, say, push_rung=12 from advanced push training, but stepped into a Planche Focus Split session, the "push accessory" slots were resolving to One-Arm Push-Ups — technically correct given your rung, but not what the session actually wants. Planche day is about planche skill work; the accessory pushing should build the pattern, not deplete you.
Templates now carry a maxRung cap per family slot. Push accessories on skill days cap at rung 8 (Diamond Push-Up), pull at rung 8 (Archer Pull-Up), core at rung 4, hinge at rung 3. Cap only kicks in for users above the ceiling — anyone below sees zero change. Advanced trainers get sane accessory prescriptions on skill days; beginners aren't affected.
Press to Handstand ladder, reordered
The old Press to Handstand ladder had you attempt the unassisted pike press before you'd built the mobility and eccentric strength — pedagogically backwards. Reordered to:
- Freestanding Handstand Hold
- Pancake Compression Depth Hold (mobility gate)
- Negative Press to Handstand (eccentric first)
- Band-Assisted (Thick Band)
- Band-Assisted (Thin Band)
- Pike Press to Handstand (first unassisted)
- Stalder → Straddle → Press → Consecutive → Weighted
If you'd already crushed calibration and got seeded into the old rung positions, that's reset on this deploy so you recalibrate cleanly on the new ordering.
For the record
- App Store version: 1.12.0 (up from 1.11.0)
- Backend deploy: history-detail fix, PtH calibration seeding, slot caps — all shipping alongside the iOS binary
- Deploy script: skill rungs get reset for the seven skills whose ladders were reordered or extended without a per-skill migration; the backfill script then reseeds from your existing calibration data automatically. No user action required.
- Widget requires iOS 17+ (as does everything else in the app)
If your Athlete Profile reads "The Generalist · well-rounded" that's a compliment — most people specialize accidentally. If it reads "Extreme specialist" and you know you skip pulling day, the growth callout is Yesterday's Jacob telling Today's Jacob to fix that.
Feedback goes to support@zenmotion.app or Instagram.
Train well.