Member app documentation
NAOS Fitness turns coach-measured benchmark assessments into a ranked season game. This is the engineering reference for the member app: the model it follows, every screen in it, the events it emits, and the decisions NAOS still owes us.
Source of truth
The clickable prototype. Layout, copy and states in these pages describe what it does; fixture values are illustrative.
Scope
Member app, V1. The coach and admin portal appears only where the app depends on it.
Open items
Every pending NAOS decision carries our recommendation, marked Our call. Build against it and keep the seam visible.
Problem
Today assessment results live in a coach's spreadsheet: a member is told a number, forgets it, and has no reason to come back for the next assessment. The gym has a competitive esports brand and no competitive product.
Goals
- Make the assessment worth returning for. Rank only moves on re-assessment, so the assessment is the app's core event and the only thing that changes the hero number.
- Give the between-assessment weeks something live. Gym visits are the only moving part between assessments; each approved tap-in pays XP and counts toward badges, never rank.
- Make standing legible at a glance. Tier, placement total, distance to next tier, season position — in that order, on Home.
- Make a member want to post it. The achievement card is a deliberate share artifact.
Non-goals for V1
In-app payment or plan purchase (membership is bought and linked at the front desk), booking and scheduling, wearables and auto-tracking, team boards, weight-class segmentation, divisions I–IV, and the coach/admin portal itself.
Success signals
Re-assessment rate per season, average approved visits per member per week, and leaderboard opens per member per week. These need instrumenting but no analytics spec is in scope for V1.
Product model
Three progression tracks. They never merge, and every other rule follows from that.
| Track | Answers | Source | Moves | Resets |
|---|---|---|---|---|
| Fitness Rank | How good are you | Placement total from a coach assessment | Only on official assessment; can go down | Per season |
| Account Level | How long have you been here | XP from participation | Any time XP is awarded | Never |
| Achievements | What have you done | Badges from visits, streaks, tiers and seasons | On unlock | Never |
Rules that keep them separate
- XP never scales with score or position. The moment it does, Level becomes a second skill signal and the model collapses.
- Level never shares a container with Fitness Rank. Rank owns the Home hero card; Level lives as a ring around the avatar.
- One full-screen celebration, and it belongs to rank-up. Ordinary level-ups get a toast; milestone levels (10, 20, 30) get their own moment.
- Nothing else attaches to XP. No unlocks, no gating, no rewards.
The engagement gap, stated plainly
Rank, personal bests and the radar are all static between assessments, because they come from coach measurement. Tap-ins are the only live thing in those weeks, and they carry the whole loop alone. Do not hide this — it is the product's real shape.
Personas
| Persona | Context | Needs from V1 |
|---|---|---|
| Rhea · ranked member | Assessed, mid-season, trains three times a week. Competitive, reads numbers fast, screenshots her rank. | A Home hero that answers "where am I", a season board, a quick tap-in at the door, a share card. |
| New sign-up | Downloaded the app and created an account; may not be a gym member yet. No scores. | Sign up → gamertag → welcome with no friction, a clear "link your membership" next step, then an honest empty state with the appointment as hero once linked. |
| Coach Mika | Portal user, never opens the app. | Nothing in-app — but every rank change originates with her. |
| Gym admin | Links app accounts to memberships, approves tap-ins, books slots, closes seasons, resets gamertags. | Same: the app is downstream of admin actions. |
Flows
First run — self-service sign-up
Splash → Get started → Create account → Set password → Choose gamertag → Welcome → Home (not yet a member)
Anyone can create an account with full name, email, date of birth and sex. Gamertag is required and gets a full screen — it is the member's first act of ownership. The account can browse but cannot be ranked until the gym links it to a membership.
Joining the gym
Home "Link your membership" → front desk links by email (portal) → Push "Membership linked" → admin books slot → Push "Assessment scheduled" → Home (unassessed)
The front desk finds the account by email, checks the date of birth and sex the member entered (both drive scoring bands), and attaches the membership. Both lock in the app from that moment. Tap-ins and leaderboard entry unlock at the same time.
First assessment — peak 1
Push "Placement results posted" → Placement results → Rank reveal → Home (assessed)
The notification is the entry point, not something to find in the app. Results count up the five scores, draw the radar, land the total, overlay the coach's season target, and add the flat 500 XP.
Between assessments — the attendance loop
Arrive at the gym → Game on tab "Tap in" (or Home card) → pending → front desk approves in the portal → Push "Visit logged" → +100 XP → level-up toast when a threshold is crossed
One approved visit per day. A declined request shows the reason; the front desk can add a missed visit in the portal. There is no member-side dispute flow.
Reassessment — moving placement mid-season
Game on "Request reassessment" (or Placement results "Reassess") → pick stats → Pending → admin books the slot in the portal → Push "Reassessment booked" → Booked → coach runs it → Push "Placement results posted" → Placement results
Open only while a season is running and only after the member's first placement. One request at a time, at least 4 days after the last assessment, and requests close 3 days before the season ends. The member picks one to five stats; new scores replace the old even if lower, the total, tier and season position recompute, and it pays the flat 500 XP. A pending request can be cancelled in the app; a booked slot cannot ("Can't make it? Tell the front desk"). A missed slot stays on the card as Missed until the front desk or coach cancels it in the portal — only then can the member request again.
Compare and share — peak 2
Leaderboard → Competitor profile → Comparison → Achievement card → native share sheet
Season close
Push "Season ending" → Season → (close) → Push "Final standings" → Season (closed) → Season history
Screen specs
Thirty screens, grouped by tab. The tab bar (Home · Game on · Board · Me) shows on signed-in screens only. Device frame is 402 × 874.
First run — no tab bar
Splash
Purpose. App-launch screen. NF mark, wordmark, loading sliver.
Behaviour. Animates in over ~1.8s, then auto-advances to Get started (signed out) or Home (session restored). No controls, no back.
Edge. If a session restore fails, fall through to Get started rather than holding the splash.
Get started
Purpose. Entry fork. Tagline, primary "Create account", secondary "Log in".
Behaviour. Footnote sets the expectation up front: anyone can sign up; ranking starts once the front desk links the membership.
Create account
Purpose. Full name, email, date of birth and sex. This is the identity the front desk checks when linking a membership.
Behaviour. Continue enables once the name has two or more characters, the email is valid, the member is 13 or older, and a sex is picked. Sex offers Female and Male only, matching the scoring bands.
Edge. Under the minimum age: inline "You need to be 13 or older to sign up." and Continue stays disabled. Minimum age is 13.
Edge. Email already registered: "An account already uses that email. Log in or reset your password." One account per email.
Set password
Purpose. Password and confirm with a strength meter.
Behaviour. Continue stays disabled until both match and the minimum strength is met; the rule is stated up front, never only on failure.
Choose gamertag
Purpose. Required. The name that appears on every board.
Rules. 3–16 characters, letters, numbers and underscore; unique database-wide (not per gym); live availability check as the member types, never submit-then-fail; re-checked on save.
Edge. Taken tag shows a taken state inline and blocks Continue. Admins can reset a gamertag from the portal.
Welcome
Purpose. Confirms the account is live and sets the next step: link the membership at the front desk with the account email; the gym then books the first assessment.
Behaviour. Single CTA into Home. The Iron badge is shown as unranked, not as a score.
Log in
Purpose. Email and password, link to reset.
Edge. Wrong credentials return one combined message; never disclose whether an email exists.
Reset password
Purpose. Email entry → send link → success confirmation → back to Log in.
Edge. Success copy is identical whether or not the email is known.
Membership lapsed
Purpose. Full block when membership ends. Rank badge and gamertag dimmed so it reads as paused, not deleted.
Behaviour. No tab bar, no back out, no payment path — renewal happens at the gym. Log out is the only action. Reachable from any screen when the membership flag flips.
Home and season
Home
Purpose. The standing, answered in one screen.
Elements. Greeting row with avatar level ring; Fitness Rank card (badge, tier, placement total, distance to next tier, season position); season row with week and days left; attribute strip of five axis scores; today's tap-in card with an inline "Tap in"; activity feed.
Behaviour. Rank card opens the Rank ladder; season row opens Season; any axis opens Attribute detail; the level ring opens Level history. Distance-to-next-tier is the progress metric, never a bar out of 45.
Edge. Not yet a member: "Link your membership" card is the hero, showing the account email; no tap-in card. Unassessed member: empty radar outline, Iron badge with no total, appointment card promoted to hero. At NAOS Legends: progress bar drops and the card reads tier · position.
Assessment detail
Purpose. Read-only appointment set by the admin: coach, date, time, location, what to bring.
Behaviour. Secondary action requests a change, which notifies the coach. No booking, no calendar, no slot picking in V1.
Placement results
Purpose. Peak 1. The coach's assessment, revealed.
Elements. Five attribute scores, radar animating in, placement total, resulting tier, the coach's season target as a dashed overlay with a one-line caption, and the +500 XP counting up.
Behaviour. Entered from the push notification; hands off to the rank reveal when the tier changes. Re-placements show movement against the previous assessment, including downward. While a season is running and no request is open, an "Improved since? · Reassess" row sits below the CTA and opens the reassessment picker.
Request reassessment
Purpose. Pick which stats to re-run. Member ticks one to five attributes; current scores shown beside each. Opened from Game on or Placement results; it sits under the Game on tab.
Rules. Live season only, after the first placement, one request at a time, 4-day cooldown from the last assessment, closed in the last 3 days of the season. New scores replace the old, even if lower; total, tier and position recompute; +500 XP. Rank reveal fires only if the tier changes.
Behaviour. CTA reads "Request reassessment · n" (or "Request full reassessment" at five). Sends to the admin request queue and returns to Game on with the card in Pending.
Rank reveal
Purpose. The one full-screen celebration in the product.
Behaviour. Fires on first placement and on any re-placement that changes tier. Tier name, medal, placement total, single continue CTA. Ordinary level-ups get a toast instead; milestone levels (10/20/30) get their own full-screen moment.
Rank ladder
Purpose. The whole climb: every tier, its threshold, and a "you are here" marker.
Behaviour. The only place the full 0–45 scale is shown. Above the top threshold the exact total is shown uncapped.
Season
Purpose. The season in one place: theme, dates, progress, the member's movement, and the top of the board.
Elements. Hero card with countdown; state notice (open / ending soon / closed); four stat tiles (rank movement, XP this season, visits this season, your position); top standings with a link to the full board; last season's final top 20 (top 5 shown, plus the member's row if they placed, "Show all 20" expands); Season history of closed seasons.
Behaviour. Season history lives here and only here — Profile links to it rather than repeating it.
Placement history
Purpose. Progress over time: every assessment with date, per-attribute scores, total and tier, and the movement between them.
Edge. One assessment only: show it with no movement column rather than a comparison of one.
Attribute detail
Purpose. What produced the axis score: the underlying exercises, raw results with units, the 1–9 score each produced, personal best and date.
Behaviour. Reached by tapping any radar axis or strip tile. For Strength, all four lifts are listed with the best-scoring one marked as counting.
Game on
Game on
Purpose. Everything the member asks the gym for: today's tap-in and reassessment requests. Below them: this week (Mon–Sun), total visits, weekly streak, next visit milestone, recent visits.
Tap in. "Tap in" sends a request to the front desk queue: none → pending (cancellable) → approved (+100 XP) or declined (reason shown). One approved visit per day; the button is gone once today is settled.
Reassessment card. Locked before the first placement ("After first placement") and between seasons; locked during the 4-day cooldown ("Available Sun 27 Sep") and in the last 3 days of a season ("Requests closed"). Otherwise "Retest your stats" with "Request reassessment". After sending: Pending with the picked stats and Cancel request. Once the admin books it: Booked with date, time and coach, no cancel — "Can't make it? Tell the front desk." If the slot is missed: Missed, and the member cannot request again until the front desk or coach cancels it.
Counters. Approved visits only. This week fills a box per approved day and reads "n of 3 to keep the streak" until three. Weekly streak counts consecutive Mon–Sun weeks with three or more approved visits; it breaks only when a week closes short. Visits is all time. The milestone bar tracks the next visit badge (10 → 50 → 100) and hides after 100.
Edge. Not yet a member: locked "Members only" state linking to Membership. Missing visits are added by the front desk in the portal; the member has no dispute flow.
Board
Leaderboard
Elements. Board selector (dropdown, Overall pinned first, per-exercise boards grouped by attribute), Season and Sex filters, count line, ranked rows: position, name per display setting, avatar with level ring, rank badge, ranking value. The member's row is highlighted and pinned into view.
Rules. Fewer than 2 entries hides the board. Sex defaults to All on Overall (bands are sex-specific); on per-exercise boards All is allowed but labelled as a mixed board.
Edge. Closed seasons show as a record: no movement indicators, no live position. Non-members can browse boards but never appear on them.
Competitor profile
Purpose. Opened from any leaderboard row: name per their display setting, level ring, rank badge, season position, placement total and radar.
Behaviour. Primary action is "Compare with you". No contact, no follow, no messaging in V1.
Comparison
Purpose. Two radars overlaid plus a per-attribute head-to-head delta and the totals.
Achievement card
Purpose. Peak 2, and the product's only outbound artifact: medal, tier headline, name, gamertag, season, placement total, level and radar snapshot with a brand lockup.
Behaviour. CTA opens the native share sheet. Card renders at a fixed aspect so the exported image is predictable.
Me
Profile
Elements. Avatar with level ring, gamertag, full name, Fitness Rank card, XP bar and level, radar with current and target, badge grid, settings list, log out, app version.
Behaviour. Settings list links to Edit profile, Membership, Season history (on the Season screen), Activity, About and legal, and Delete account.
Edit profile
Purpose. Avatar, gamertag (same live uniqueness component as first run), and the leaderboard name toggle.
Rules. Full name, date of birth and sex are read-only — labelled "Confirmed when you join" before linking, "Held by the gym" after — with a line pointing to the gym — sex is load-bearing for scoring. The name toggle is a display preference, not privacy, and should be described to NAOS as such.
Membership
Purpose. Plan, price, status, renewal date and the benefit list. No payment method and no billing actions — renewal happens at the gym.
Edge. Not yet a member: status "Not linked" and four "How to link" steps including the account email. This screen is the target of every locked state.
Activity
Purpose. The full in-app feed: assessments, rank changes, visits, level-ups, season events, each deep-linking to its target.
Edge. Unassessed members see one entry, account created — not an empty screen.
Level history
Purpose. Every XP award with source, amount and date, plus the current level and distance to the next.
Behaviour. Reached from the avatar ring anywhere it appears. Flat curve: 1,000 XP per level, forever.
About and legal
Purpose. About, Contact, FAQ, Privacy Policy, Terms.
Dependency. Privacy Policy and Terms also need hosted public URLs for store submission.
Delete account
Purpose. Required by both stores. Explains what deletion means when the gym holds the membership: app access ends, assessment records are retained by the gym.
Behaviour. Explicit confirmation step; the request is queued for the gym rather than executed client-side.
Not built yet
Notification settings — per-category toggles — is a V1 requirement and is not drawn. It belongs in the Me settings list between Membership and Activity. Level-up toast and level milestone exist as fired moments, not standalone screens.
States
Member states
Six coherent states drive every screen, reachable from the demo panel. This is the QA matrix.
| Not yet a member | Unassessed | First placement | After visits | After re-placement | Legends | |
|---|---|---|---|---|---|---|
| Rank | Iron, unranked | Iron, unranked | Silver | Silver | Gold | NAOS Legends |
| Total | — | — | 11 | 11 | 17 | 41 |
| Attributes | — | — | 3/2/2/2/2 | 3/2/2/2/2 | 4/3/3/4/3 | 9/8/8/8/8 |
| Level · XP | 1 · 0 | 1 · 0 | 1 · 500 | 2 · 1,100 | 3 · 2,100 | 12 · 11,400 |
| Position | Not on boards | Unranked | #14 | #14 | #9 (jump) | #3, position mode |
| Visits · streak | — | 2 · 0 wk | 3 · 1 wk | 6 · 2 wk | 14 · 4 wk | 112 · 13 wk |
| Unlocked | Browse only | Everything; appointment is hero | Everything | |||
Invariants. Attribute scores drive the radar, the total and the tier together. The leaderboard recomputes positions from totals rather than hard-coding them. Personal bests and attribute detail derive from the same results as the radar. A visit never changes rank.
Season states
| State | What changes |
|---|---|
| Day one | Week 1, full countdown, "Season open" notice, standings empty state, season XP and movement at zero. Every member is unranked again: Home shows the unranked card and empty radar, with "Ranks reset every season" copy. |
| Live | Week n of m with days left, live standings, movement indicators. |
| Ending soon | Warning-toned notice, countdown at two days. Reassessment requests are closed for the last 3 days. |
| Closed | Progress complete, standings locked, reassessment requests locked until the next season, final position stated, Ginto-toned notice, board CTA becomes "View final standings", season moves into Season history. |
Season copy never states a future date — the app has no source for one.
Other states to build
- Leaderboard density. Full, sparse (2–4 ranked with an honest count line), hidden (fewer than 2 entries).
- Tap-in states. Not tapped in, pending, approved, declined — all four reachable from the demo panel.
- Reassessment states. Locked (no placement, season closed, requests closed), cooldown, available, pending, booked, missed — reachable from the demo panel's Reassessment and Season controls.
- Rank down. Results show the movement; no celebration fires and the copy stays factual.
- Membership lapsed. Blocks every screen; log out is the only exit.
- Offline and stale. Last-known state with a quiet stale marker; never a blocking spinner on Home.
- Dead deep link. Land on the nearest parent screen with a one-line explanation.
Scoring and XP
Five attributes — Strength, Power, Endurance, Mobility, Conditioning — each scored 1–9 against sex-specific bands, then summed. Range 5–45. The total sets the tier. Iron is the pre-assessment state, so no assessed member lands in Iron.
| Tier | Label | Min | Tier | Label | Min |
|---|---|---|---|---|---|
| Iron | Newbie | 0 | Diamond | Exceptional | 25 |
| Bronze | Novice | 5 | Master | Pro-level | 30 |
| Silver | Fair | 10 | Grand Master | Elite | 35 |
| Gold | Experienced | 15 | NAOS Legends | Legendary | 40 |
| Platinum | Advanced | 20 |
Nine tiers, not eight. Nine medals exist, so Grand Master fills the 35 band. If NAOS confirms eight, delete Grand Master and reset the top band to 35.
Display rules. Home shows distance to the next tier plus the raw total with no denominator. The ladder is the only place the full scale appears. At the top tier the app switches to position mode: no progress bar, tier · season position, uncapped total on the ladder.
XP economy
| Source | XP | Rule |
|---|---|---|
| Gym visit | 100 | Front desk approves the member's tap-in. Max one per day. |
| Assessment completed | 500 | Flat, whether the member went up a tier or down one. Includes member-requested reassessments, partial or full. |
| Season completed | 1,000 | Flat, for any member with at least one assessment that season. |
| Personal best set | 150 | Per exercise. |
Flat curve: 1,000 XP per level, forever. Level is derived from the XP ledger, never stored. All of these numbers need balancing against real training volume before launch.
Data model
| Entity | Key fields | Notes |
|---|---|---|
| Member | id, fullName, gamertag, sex, dob, email, joinedAt, coachId, membershipStatus, displayNamePref, avatarUrl | Self-created with fullName, email, dob and sex. membershipStatus: none · active · lapsed; dob and sex are checked by the gym at linking and locked in app from then on (dobVerifiedAt). Gamertag unique database-wide. |
| Assessment | id, memberId, coachId, date, scores[5], total, tier, targetScores[5], notes, scope, requestId | Portal-written. Immutable once published; corrections create a new record. scope: full or a list of attributes; unscoped attributes carry over from the previous assessment. |
| AssessmentRequest | id, memberId, seasonId, type, attributes[], status, requestedAt, slotAt, coachId, assessmentId | type: reassessment · change. status: pending · booked · missed · cancelled · done. One open (pending, booked or missed) per member; a new one needs 4 days since the last assessment and more than 3 days left in the season. Only the portal can cancel a booked or missed request. |
| ExerciseResult | id, assessmentId, exerciseId, attribute, rawValue, unit, score1to9, isPersonalBest | Feeds attribute detail and per-exercise boards. |
| Season | id, number, theme, startDate, endDate, weeks, status | upcoming · live · closed. Closing is a portal action. |
| SeasonEntry | seasonId, memberId, total, position, tier, movement | Position derived at read time, not stored as truth mid-season. |
| TapIn | id, memberId, requestedAt, status, reviewedBy, reviewedAt, declineReason, source | status: pending · approved · declined. source: member request or front-desk entry. Unique approved per member per day; approval writes the XpEvent. |
| XpEvent | id, memberId, source, amount, date, refId | Append-only ledger; level derived. |
| Badge | id, memberId, badgeType, earnedAt, refId | Permanent; never revoked on a rank drop. |
| NotificationPref | memberId, category, enabled | One row per push category. |
Notifications matrix
Every notification has a matching activity entry; not every activity entry sends a notification. All types are on by default.
Push and in-app
| Notification | Trigger | Deep-link target | Full-screen |
|---|---|---|---|
| Placement results posted | Coach submits an assessment | Placement results, reveal animation intact | Hands off to reveal |
| Rank up | New total lands a higher tier | Rank reveal celebration | Yes |
| Level up | XP crosses a 1,000 XP threshold | Level history | No — toast |
| Level milestone | Level 10, 20, 30 reached | Level milestone celebration | Yes |
| Visit logged | Front desk approves a tap-in (+100 XP) | Game on | No |
| Tap-in not approved | Front desk declines a tap-in | Game on | No |
| Membership linked | Front desk links the account to a membership | Home | No |
| Assessment scheduled | Admin sets or changes the slot | Assessment detail | No |
| Reassessment booked | Admin books a requested reassessment | Game on (Booked) | No |
| Season ending | Countdown milestone, two days out | Season | No |
| Season final standings | Season closes, standings lock | Season (closed) | No |
Transactional email
| Trigger | Contains | |
|---|---|---|
| Verify email | Member creates an account | One-tap verification link |
| Password reset | Member requests a reset | Reset link, 30-minute expiry |
| Appointment scheduled | Admin sets the assessment slot | Date, time, coach |
Rules
- The two peaks arrive as notifications. Placement results and rank up are met in the notification tray, not found in the app; a tap lands on the moment, never on Home.
- Rank notifications come only from an official coach assessment. No visit, XP award or leaderboard movement produces one.
- One full-screen celebration per event. Rank up and level milestones take the screen; ordinary level-ups are a toast.
- Achievements never notify on their own. A badge rides along with the event that unlocked it.
Open with NAOS
Whether a rank down notifies at all or is only visible in-app; quiet hours or batching for season countdowns; whether a declined tap-in carries the front desk's reason. Our call: no push on a rank down (the member meets it inside the app, framed factually), batch countdowns to one morning send, and carry the decline reason — a declined claim without a reason generates a front-desk conversation.
Activity feed
One event stream, read at two depths. Home · Activity shows the latest four entries with no unread state and no times — it reads as a record. The Activity screen (app bar icon, or Me → Activity) is the full feed with unread markers and timestamps. Tapping any entry deep-links to the screen that owns it.
| Entry | Recorded when | Deep-link target | Notified |
|---|---|---|---|
| Account created | Member signs up in the app | — | Email only |
| Membership linked | Front desk links the account | Membership | Yes |
| Reassessment requested | Member sends a reassessment request | Game on | No |
| Reassessment booked | Admin books the requested slot | Game on | Yes |
| Assessment scheduled | Admin sets or changes the slot | Assessment detail | Yes |
| Assessment completed | Coach submits the assessment (+500 XP) | Placement results | Yes |
| Fitness Rank set | First placement lands a tier | Rank reveal | Yes |
| Rank up | A re-assessment lands a higher tier | Rank reveal | Yes |
| Rank down | A re-assessment lands a lower tier | Placement results | Our call: no push |
| Personal best set | A result beats a stored best (+150 XP) | Attribute detail | No |
| Visit logged | Front desk approves a tap-in (+100 XP) | Game on | Yes |
| Tap-in not approved | Front desk declines a tap-in | Game on | Yes |
| Level reached | XP crosses a 1,000 XP threshold | Level history | Yes |
| Level milestone | Level 10, 20, 30 reached | Level milestone | Yes |
| Achievement unlocked | A badge trigger fires | Profile badge grid | Rides with its event |
| Season ending | Countdown milestone | Season | Yes |
| Season closed | Season ends, standings lock (+1,000 XP) | Season | Yes |
Rules
- Entries are immutable. Activity is a log — a rank that went down stays in the record.
- Announcements about the member count as activity. "Assessment scheduled" is set by the gym and still belongs in the feed.
- XP-bearing entries state the amount ("+100 XP"); rank entries state the placement total.
- Unread lives on the Activity screen only. Home stays clean.
- No filtering or search. Four-week seasons keep the feed short.
Achievements
Permanent and discrete: once unlocked they never expire and never reset with the season. A badge awards a place on the Profile grid and nothing else — never rank, never an attribute score, never a board position. Locked badges stay visible with their name, so the grid is a goal surface. Visit badges use the flag icon and streak badges the flame; both carry their number in a corner pill (10 · 50 · 100, 4W · 8W · 12W) so tiers of the same badge read apart.
In the prototype
| Badge | Trigger | Source of truth | Repeatable |
|---|---|---|---|
| First placement | First official coach assessment is posted | Assessment | No |
| 10 · 50 · 100 visits | Approved visits reach the milestone, all time | TapIn | No |
| 4 · 8 · 12 week streak | Three approved visits a week, that many weeks running | TapIn | No — longest streak kept |
| Gold tier | Placement total reaches the Gold band (15+) | Assessment | No — highest tier kept |
| Season podium | Finish a season in the overall top three | Season close | Once per season |
Tier badges
One badge per tier reached, unlocked when an assessment first lands the member in that band — nine tiers, nine possible badges. Awarded on the highest tier ever reached, so a member who drops from Gold to Silver keeps the Gold badge. Iron is the pre-assessment state and is not awarded.
Proposed additions
| Badge | Trigger | Needs |
|---|---|---|
| Balanced build | Every attribute at 5 or above in one assessment | Assessment scores only |
| Target hit | Reach the coach's season target on every axis | Coach-set target |
| Strength specialist | Strength axis at 7 or above | Assessment scores only |
| Comeback | Improve the total after a re-assessment that went down | Placement history |
| Season regular | Visit in every week of a season | TapIn |
| Referral | A referred member completes a first assessment | Admin action in the portal |
Rules
- Awarded by the system, never by a coach tapping "grant". Every trigger resolves from data the product already holds.
- No XP is attached to a badge. XP comes from participation events and is listed in Level history.
- Unlocks are notified as part of the event that caused them, never as a separate push.
Open with NAOS
One badge per tier (nine) or a single badge showing the highest tier reached; whether Season podium shows a repeat count; which proposed additions survive. Our call: nine tier badges — the grid is the goal surface and nine filled slots read better than one mutable badge — with a repeat count on Season podium.
Portal dependencies
The coach and admin portal is a separate spec, but the member app cannot ship without these writes — none of which the app performs itself.
| Portal action | What breaks without it |
|---|---|
| Find an app account by email, check dob and sex, and link it to a membership | Sign-ups stay browse-only forever: no assessment, tap-ins or board entry. |
| Reassessment request queue: book a slot, reschedule, cancel a booked or missed request | Requests sit in Pending forever; a missed slot blocks the member from requesting again. |
| Publish an assessment (scores, total, season target) | Rank never moves; radar, ladder, history and both peaks stay empty. |
| Schedule or change an assessment slot | Appointment card and the unassessed Home hero have no content. |
| Tap-in queue: approve or decline, and add a missed visit | No visit XP at all — Level stalls and the between-assessment loop is dead. |
| Open and close a season | No season states, no final standings, no history, no season XP. |
| Reset a gamertag | No remedy for an abusive or mistaken tag. |
| Update membership status | The lapsed block never fires. |
Open decisions
NAOS owns these. Each carries the recommendation the prototype already assumes.
| Decision | Our call | Where it bites |
|---|---|---|
| Exercise list: workbook nine, or the agreed eleven | Ship the workbook nine; adding later needs no schema change. | Per-exercise boards, attribute detail rows |
| Body fat: scored, and visible to whom | Record it, show member and coach, exclude from the total and public boards. | Total range, thresholds, board count |
| Explosiveness combination rule | Best of the two exercises, matching how Strength resolves. | Attribute detail for Power |
| 3RM → 1RM estimation | Coach enters 3RM, the system estimates. | Attribute detail for Strength |
| Between-band results | Round down — a band is earned, not approached. | Fixture scores only |
| Divisions I–IV | Drop for V1, keep the component API. | Rank badge, ladder, Home card |
| Challenges | Decided: removed from V1. Replaced by attendance XP: member requests, front desk approves, +100 XP, one a day. | Tab bar, Home, XP economy, badges |
| Season length and carry-over | Decided: every rank resets at season start. The last season's final top 20 is shown on the Season screen as a record. Four-week length still assumed. | Season, history, season XP, Home on day one |
| Reassessment requests | Decided: live season only, after the first placement, any one to five stats, one open request, 4-day cooldown, closed in the last 3 days, +500 XP. Booked slots are cancelled only by the gym; a missed slot must be cancelled by the front desk or coach before a new request. Replaces the one-time first-placement retest. | Game on, Placement results, coach workload |
| Attendance naming | Decided: tab is "Game on", the daily action is "Tap in", a request is a tap-in. | Tab bar, notifications, copy |
| Eight tiers or nine | Nine — nine medals exist and Grand Master fills the 35 band. | Ladder, thresholds, badge art |
| Registration model | Decided: self-service sign-up; front desk links membership by email. No in-app payment. | First run, Membership, store review |
Risks
- The engagement gap. Visits are the only live loop. If the front desk is slow to approve, the app is quiet for weeks at a time.
- Approval queue at peak hours. Every visit needs a front-desk tap. At 6–8 AM this is the busiest queue in the gym; batch approve in the portal is a must.
- Thin boards at launch. One gym, many boards, two filter dimensions. The two-entry rule and the count line are the mitigation.
- Front-desk workload is the real dependency. Under-staffed approval makes the member app look broken.
- Reassessment load. A 4-day cooldown allows up to about six requests per member per season. Coach capacity, not the rule, will be the limit; watch the queue in the first season.
- The name toggle is not privacy. On a small member base the top of a board is identifiable regardless — say so out loud.
- Numbers are unbalanced. Thresholds, XP per level and rewards are illustrative until measured.
Build hygiene
Demo panel
Prototype-only control surface: jump to screen, member state, season state, fire a moment, fire a notification, leaderboard density, tap-in today, reassessment state, display-name toggle, membership status, reset. Shake to open, with a deliberately off-brand magenta handle as the visible fallback.
It ships in App Distribution and TestFlight builds and must be strippable: one folder, one mount point behind a build-time flag, no inbound imports, state changes only through real app actions. Acceptance test: deleting the folder and the single mount line leaves a working app.
App version display
Profile renders Version 1.0.0 · Build 24. The build suffix is for dev, staging and tester builds only — production must render the version alone (Version 1.0.0). The string is built in one place in the profile view model; strip the suffix there behind the same release flag that strips the demo panel.
Platform pass
The system is iOS-derived. Android needs its own treatment for the back button, share sheet, system font fallback and navigation transitions. Do not assume pixel parity.