Build 16 — Thirteen usability fixes in one sweep

A full usability pass over the app: the rest timer survives backgrounding, the editor reorders and creates workouts from scratch, RPE and skip land in the session log, history exports as JSON/CSV, a muscle-volume view and an e1RM trend appear in History, and the watch finally gets personalized heart-rate zones and an honest rest ring.

← All changes

Runner & rest timer

  • The rest timer is now anchored to the wall clock. Backgrounding the app mid-rest used to freeze the in-app countdown (the lock-screen Live Activity was always correct). The countdown now recomputes from an absolute end date on every tick and resyncs the moment the app returns to the foreground. A gong that expired while you were away only plays if it just happened — a late gong is worse than none.
  • RPE, optionally. After logging a set, the rest card offers a quiet 6–10 chip row for the set you just did. Tap to rate, tap again to clear, ignore it entirely — nothing blocks. Ratings show as @8 in the summary and ride along in the Strava description.
  • Add an exercise mid-session. A ghost row in the runner appends any exercise from your library to the running session — seeded with last time’s weights — without touching the template on disk.
  • Skip an exercise, explicitly. A muted “Skip exercise” chip marks the difference between “chose not to today” and “never got there”. The summary shows SKIPPED instead of 0/3; Strava omits it.

Editor & library

  • Reorder everything. Drag exercises within a block; move blocks and sets up/down. No more JSON round-trips to change an order.
  • New workout from scratch. The + menu now creates a blank template straight into the editor — v1 on save, written to iCloud like any import.
  • Quick-start. “Start session” on a workout card’s context menu and “Start again” on any history row skip the detail screen entirely.

History & data

  • Export your history. Settings → Data exports every finished session as JSON (full per-set log) or CSV (one row per set: reps, weight in kg, RPE, pain, skipped). The same ownership the workout templates always had, now for the training log.
  • Sets per muscle group. A second card in History stacks weekly completed sets per muscle group over 4/8/12 weeks. No targets, no deltas — just the bars.
  • Estimated 1RM. The exercise progression screen adds a quiet dashed Epley trend (mass-weighted rep sets, 1–12 reps only). Informational only; no PR celebration anywhere.

Watch (protocol v6)

  • Personalized heart-rate zones. Max heart rate is now a setting (Settings → Watch) and rides to the watch in the start payload. Zone colors were previously hardcoded against 185.
  • An honest rest ring. The watch now receives the planned rest total and fills its ring proportionally instead of guessing.
  • Both fields are optional on the wire — older watch builds simply fall back to the previous behavior.

Under the hood

  • 49 new unit tests (231 → 280): wall-clock rest recovery, back-compat decodes for rpe and skipped, watch protocol v6 round-trips against raw v5 fixtures, watch-message routing without a live WCSession, and Strava token parsing without network or Keychain.
  • TestFlight uploads are self-healing again: the lane now maintains a classic distribution certificate and explicit provisioning profiles via the App Store Connect API key, with no dependency on Xcode’s (silently expiring) account session.