Foundation
The functional groundwork before any visual redesign — six phases in one intense day:
- Schema + decoder. Typed workout model with per-field back-compat
(
decodeIfPresenteverywhere). Full test coverage for every optional field. - iCloud Drive import.
NSMetadataQuerywatchesAski/Documents/workouts/and pulls new files in without an explicit sync trigger. SwiftData persists everything locally. - Runner + session logging. Sets, reps, timer, per-set pain slider (later removed in favor of the session-end pain gate).
- Watch + HealthKit.
HKWorkoutSessionowned by the phone, never the system Workout app — HealthKit would double-count. HR samples stream over WatchConnectivity. - Strava OAuth. PKCE flow, tokens in Keychain, one-way upload after each completed session.
- Exercise library. AVPlayer for local videos, tag filter, search,
cross-references, and a YouTube-search fallback for exercises without
an explicit
videoUrl(opens in an in-app Safari view).
No third-party dependencies at any point.
Quiet Sport — the design system
- Tokens. Three type roles (display / body / data), Motion springs, a component library, and a debug gallery.
- Palette. Quartz canvas, pine and graphite text, chalk hairlines, coral as the single accent.
- Hero numerics scale up to 128 / 160 / 200pt — the runner’s single active set, the 160pt rest-timer glyph, the set-complete bloom.
- Nine new atoms. Caps, DSRule, SegmentedProgressBar, TickRuleBar, PainSlider, StatCell, SpecCell, InlinePickerSheet, HeroNumeric.
- Free font stack bundled. Space Grotesk, Inter, JetBrains Mono.
The default UI wasn’t helping when the phone sat six feet away on a tripod. The redesign codifies what not to build alongside what to ship — bigger numerics for tripod-distance reading, fewer decorative borders, a flatter canvas that keeps coral as the only pull.
Runner + rest timer + watch
- Inline set rows, a “previous” ghost for 2-tap repeat logging, persistent tempo bar with optional metronome, contraction type on every rep set.
- Live Activity rest timer on Lock Screen and Dynamic Island, audible tick on the last ten seconds, gong at zero.
- Watch shows HR + elapsed + rest haptic — nothing else. Protocol
versioned for forward-compat. Tempo haptics are distinct per phase
(
.directionDownfor the eccentric,.clickfor pauses,.directionUpfor the concentric). For duration-based exercises the wrist shows a coral progress ring and the remaining seconds; during rest it shows a numeric counter, the last three tick with.directionUp, zero fires.notification.
Rehab features
- End-of-session pain gate + next-morning reactivity check.
- Silbernagel flag on the summary when the threshold is crossed.
- PR-mute toggle so the rehab user isn’t celebrated on every re-tested lift.
First-workout sweep
- Auto-advance after the last set’s rest.
- “SET N OF M” hero, drag-to-minimize rest timer, propagate reps / weight / duration edits to remaining sets with Undo.
- Working duration editor with a Start button and overtime display; single coral Finish on the last set.
- The phone now owns the canonical
HKWorkout(wall-clock start/end), fixing the 16-second-workout bug. NewStravaUploadSourcesetting (Direct / Via Apple Health / Off) avoids double-counting. - Watch handoff via
startWatchApp; phone audio gated behind a setting when the watch is paired. - Optional in-session form video per set (60s cap, flip camera), stored
under
Documents/Videos/{sessionId}/, surfaced as chips on the summary. Off by default. - Five bundled routines (Beginner A/B, GZCLP Press, PPL Push, Bodyweight Full-body). Three-stage onboarding (welcome → path → six-question quiz), every card skippable, ≤90 seconds to first logged set.
History + per-exercise progression
- TabView shell. Workouts, History, and Settings live in separate tabs with isolated navigation stacks. The tab bar hides during a workout.
- Session history. Reverse-chronological list of finished sessions.
- Pain trend chart. 30 / 60 / 90-day view with three series: session-end pain, pain during load, and next-morning pain. A coral flag marks sessions that crossed the Silbernagel threshold.
- Per-exercise progression. Load-over-time chart (kg for rep-based, longest hold for duration-based) on any exercise in the library.
Rehab loading needs a memory. A quiet, passive view of what the pain did and where the load trended makes week-over-week decisions easier — without streaks, badges, or nudges.
Manage workouts
- Edit and delete a workout directly from the detail view.
- Robust iCloud re-scan with more aggressive recovery from missed file-change events.
- Clipboard paste: copy a JSON you generated in an AI chatbot, open Aski, and import it with one tap without ever touching iCloud.
- Strava upload mode in Settings: Always (default) / Ask / Never.
- Structured
Weighttype (value + unit) across the schema and model, eliminating ambiguous conversions everywhere a weight is read. Shared ±-stepper between the runner and the planner, with haptic feedback on each tap and long-press to scrub. - Muscle groups per exercise. Restricted 20-token enum (
chest,quads,hamstrings,rear-shoulders, …) surfaced in the runner, the detail view, and the library. Schema change is fully back-compat. - Strava description composed from the session — exercise list, total volume, key tempo cues — written at session close.
Polish
- SetRow horizontal overflow on narrow iPhone widths fixed.
- Delete-workout confirmation dialog now behaves correctly.
- Bundled-sample deletions persist across app restarts.
- App icon ships. Ink canvas + coral mark — same language as the app’s Quiet Sport aesthetic.
Renamed to Aski
- New bundle IDs across all four targets:
com.casvanderhoven.aski,…aski.watchkitapp,…aski.tests,…aski.restwidget. URL scheme is nowaski://(Strava OAuth callback included). The iCloud workout container moves toiCloud~com~casvanderhoven~aski. - Website now lives at aski.fit — a custom domain
attached to Cloudflare Pages. The auto-generated
coach-workout-app.pages.devURL still resolves as a fallback. - The on-icon name stays Aski. The App Store listing is Aski
Strength because
Askialone was already taken — search results say one thing, your home screen says another. By design. - “Coach notes” / the
coachNotesJSON field are deliberately kept — those are generic strength-coaching terminology (the program designer’s notes), not stragglers from the rebrand.
“Coach” was generic on the App Store and ironically required a disclaimer (“Yes, the app is called Coach. No, it doesn’t coach you — that’s the point”). “Aski” is short, distinctive, and lets the product stand on its own without the explainer.
Marketing site + design docs
- Public site on Cloudflare Pages: landing page with the core philosophy, Use with an AI chatbot (the workout JSON schema is downloadable so anyone can generate a training plan in any chatbot and drop the result into iCloud), App-Store-ready Privacy policy, a Support page with FAQ, and this changelog.
DESIGN.mdandCLAUDE.mdcapture the invariants that should never silently change — ≤2-tap mid-set logging, resilient rest timer, watch as HR strap, session-end pain gate only, 90-second onboarding, no gamification, personalization over streaks. A change that crosses any of these should surface itself, not slip in quietly.