You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Build Nutrition Challenges — time-bound, opt-in challenges (7 / 14 / 30 days) with daily progress bars, completion badges, and an optional Squad leaderboard. Direct adaptation of Strava Challenges, the single feature with the strongest published retention number in consumer fitness.
Strava's "Challenges" feature lifted 90-day retention from 18% to 32% (+14pp), drove a 28% DAU lift, and a 15% premium subscription lift. Source: StriveCloud / Trophy Strava case study.
This is week 1, feature 3 of 3 (alongside Squads + Behavior Insights). Challenges are the connective tissue: they consume Behavior Insights as suggested challenges and use Squads as the social leaderboard layer.
Why this fits AuraFitness
Stacks with the other two features: Insights surface "you do X often" → suggest challenge "X for 14 days" → Squads compete on it.
US-3.1 — As a user, I want to browse a curated list of challenges (e.g., "21-day Protein Streak", "14-day Breakfast Before 10am") so I have a clear short-term goal.
US-3.2 — As a user, I want to join a challenge with one tap and see daily progress on my home dashboard so I'm reminded of my commitment without opening a separate screen.
US-3.3 — As a user in a Squad, I want my Squad mates to join the same challenge so we compete on a shared leaderboard.
US-3.4 — As a user, I want a completion badge added to my profile when I finish so the achievement is durable, not just a transient streak.
US-3.5 — As a Premium user, I want access to AI-generated personalized challenges based on my Behavior Insights so the challenge actually targets my weak spot.
US-3.6 — As a user mid-challenge, I want a "rest day" allowance (1 per 14 days) so a single bad day doesn't kill my progress (Duolingo Streak Freeze pattern).
Acceptance Criteria
AC-1 Catalog & lifecycle
Catalog has ≥10 seeded challenges across categories: Protein, Fiber, Hydration, Timing, Whole Foods, Squad-only.
Status transitions: active → completed (all required days hit) or active → failed (failed days > duration - max_rest_days).
AC-3 Rest day
If user fails a day but rest_days_used < max_rest_days, the failure is converted to a rest day (not counted against success).
User notified in-app: "Rest day used. X of Y remaining."
AC-4 Squad leaderboard
If user is in a Squad and the challenge has squad_leaderboard_enabled=true, all Squad members who joined appear on a per-challenge leaderboard ranked by days_succeeded desc, joined_at asc.
Cold-start guard: members with 0 success days hidden from leaderboard (avoids public "0%").
AC-5 Completion
On status → completed, server creates user_badge row with timestamp + challenge metadata.
ChallengeProgressCard — a Bento card surfaced on home dashboard for each active challenge. Shows day X of Y + progress bar + today's status (✅ done / 🟡 pending / ❄️ rest day used).
BadgeUnlockModal — celebratory full-screen on completion: badge image, AI-generated congratulation copy, "Share to Squad" CTA.
Summary
Build Nutrition Challenges — time-bound, opt-in challenges (7 / 14 / 30 days) with daily progress bars, completion badges, and an optional Squad leaderboard. Direct adaptation of Strava Challenges, the single feature with the strongest published retention number in consumer fitness.
This is week 1, feature 3 of 3 (alongside Squads + Behavior Insights). Challenges are the connective tissue: they consume Behavior Insights as suggested challenges and use Squads as the social leaderboard layer.
Why this fits AuraFitness
User Stories
US-3.1 — As a user, I want to browse a curated list of challenges (e.g., "21-day Protein Streak", "14-day Breakfast Before 10am") so I have a clear short-term goal.
US-3.2 — As a user, I want to join a challenge with one tap and see daily progress on my home dashboard so I'm reminded of my commitment without opening a separate screen.
US-3.3 — As a user in a Squad, I want my Squad mates to join the same challenge so we compete on a shared leaderboard.
US-3.4 — As a user, I want a completion badge added to my profile when I finish so the achievement is durable, not just a transient streak.
US-3.5 — As a Premium user, I want access to AI-generated personalized challenges based on my Behavior Insights so the challenge actually targets my weak spot.
US-3.6 — As a user mid-challenge, I want a "rest day" allowance (1 per 14 days) so a single bad day doesn't kill my progress (Duolingo Streak Freeze pattern).
Acceptance Criteria
AC-1 Catalog & lifecycle
id, slug, title, description, duration_days, success_predicate_key, category, tier (free|pro|premium), badge_image_url, max_rest_days.POST /challenges/{id}/join— creates achallenge_participationrow withstart_date = today,end_date = today + duration_days.AC-2 Daily evaluation
success_predicate(user, day) -> booleanusing same predicate registry as Behavior Insights (DRY).days_succeeded,days_failed,rest_days_used.active → completed(all required days hit) oractive → failed(failed days > duration - max_rest_days).AC-3 Rest day
rest_days_used < max_rest_days, the failure is converted to a rest day (not counted against success).AC-4 Squad leaderboard
squad_leaderboard_enabled=true, all Squad members who joined appear on a per-challenge leaderboard ranked bydays_succeeded desc, joined_at asc.AC-5 Completion
completed, server createsuser_badgerow with timestamp + challenge metadata.{title}🎉".ProfileBadgesGrid.AC-6 AI-generated personalized challenges (Premium)
POST /challenges/personalizedcalls Gemini LLM with prompt template:AC-7 Tier gating
UI / UX Spec
Inspired by: Strava Challenges (catalog grid + progress card), Apple Fitness+ Awards (durable badges), Duolingo Streak Freeze (rest day mechanic), Linear Cycles (time-bound progress bar).
Screens
ChallengesScreen— top tabsActive | Browse | Completed. Browse = grid ofChallengeCard. Active = stackedActiveChallengeCardwith progress bar.ChallengeDetailScreen— hero badge image, description, predicate explainer, "Join" CTA, optional Squad section showing teammates' progress.ChallengeProgressCard— a Bento card surfaced on home dashboard for each active challenge. Shows day X of Y + progress bar + today's status (✅ done / 🟡 pending / ❄️ rest day used).BadgeUnlockModal— celebratory full-screen on completion: badge image, AI-generated congratulation copy, "Share to Squad" CTA.Components
ChallengeCard— glass morphism, badge thumbnail (top-right), title, duration chip, category accent stripe.ChallengeProgressBar— segmented bar (one segment per day), filled = success, hatched = rest day, empty = pending/failed.RestDayPill—❄️ Rest Day Usedchip on day cell, animated entrance.SquadChallengeLeaderboard— reusesSquadLeaderboardRowfrom feature 1.Theming
successtokeninfotokenAnimations
0 → 180deg, 400ms spring.0 → 1.2 → 1.0with 60ms haptic medium then heavy.0% → first day cell pulse.Database Schema (PostgreSQL + Flyway)
Predicate registry is shared with feature 2 (Behavior Insights) — both use the same
BehaviorPredicateinterface, avoiding duplicate logic.API Contract
GET/api/v1/challenges?category=&tier=Challenge[](catalog)GET/api/v1/challenges/{id}ChallengeDetailPOST/api/v1/challenges/{id}/joinParticipationPOST/api/v1/participations/{id}/abandon204GET/api/v1/participations?status=activeParticipation[]withdayResultsGET/api/v1/challenges/{id}/squad-leaderboardLeaderboardEntry[]POST/api/v1/challenges/personalized(Premium)Challenge[](3 AI-generated, ephemeral until joined)GET/api/v1/users/me/badgesBadge[]Caching: catalog cached in Redis
challenges:catalog:{tier}1h TTL; participations not cached (read after write).Tests
Unit (Spring Boot)
ChallengeServiceTestjoin_rejectsWhenUserHas3Active.join_rejectsWhenTierInsufficient.abandon_setsStatusButPreservesHistory.ChallengeEvaluationJobTestevaluatesAllActiveDaysSinceLastRun.restDay_convertsFailToRest_whenWithinAllowance.restDay_doesNotConvert_whenAllowanceExhausted.transitionsToCompleted_whenAllRequiredDaysSucceeded.transitionsToFailed_whenFailuresExceedAllowance.BehaviorPredicateRegistryTest— shared registry returns same value asBehaviorDeriver(feature 2) for identical input — guards against drift.PersonalizedChallengeServiceTestgemini_responseValidatedAgainstSchema_invalidDropped.freeTier_endpointReturns403.Unit (Frontend)
ChallengeProgressBar.test.tsx— segments render correct outcome state; rest day hatched.ActiveChallengeCard.test.tsx— shows correct day X of Y; today's status pill correct.BadgeUnlockModal.test.tsx— firesshare_to_squadcallback on CTA tap.useChallenges.test.ts— join optimistically adds to active list; rolls back on API error.E2E
e2e/challenges.join-and-complete.e2e.tse2e/challenges.rest-day.e2e.tse2e/challenges.squad-leaderboard.e2e.tse2e/challenges.tier-gating.e2e.ts/challenges/personalized(403); upgrade to Premium → 3 AI challenges returned with valid schema.e2e/challenges.ai-disclaimer.e2e.tsDay-by-day plan (5 working days)
BehaviorPredicateRegistryextracted (shared with feature 2)ChallengeCard,ChallengeProgressBarcomponentsChallengeServicejoin/abandon + tier gating; controller + DTOsChallengesScreen(Browse + Active tabs),ChallengeDetailScreenChallengeEvaluationJob(@Scheduled03:30); rest day logicChallengeProgressCardon home dashboardBadgeUnlockModal+ confetti animation;ProfileBadgesGridDependencies / sequencing notes
BehaviorPredicateRegistry— extract this on Day 1 to avoid duplicate logic.Definition of Done
withErrorBoundaryon every new screen.feature.challenges.enabled.challenge_browsed,challenge_joined,challenge_completed,challenge_failed,rest_day_used,badge_awarded,personalized_challenge_generated.