Leaderboards¶
Weekly boards for patients and a monthly board for therapists, built around one goal: patients doing the sessions their therapist prescribed, week after week, and therapists noticing and acting when they don't. Per clinic, behind the leaderboards flag (see Feature Flags); the all-clinics boards need leaderboardsCrossClinic as well.
What patients see¶
"Your week", a card on the home screen (web and app) under the streak: a ring that fills as the week's sessions are done, the points, the tier, and one sentence about what comes next ("One session puts you past Quiet Otter.", "You lead your clinic this week."). The card opens the leaderboard.
The leaderboard has the clinic board and, where the clinic takes part, the all-clinics board, each ranked two ways: This week (points) and Streaks (the longest current streak). Every board has the same shape, whatever its size:
- the podium: the top three;
- ahead of you: the three people directly above the patient, with the gap ("83 points ahead", or "2 days ahead" on the streak board);
- the patient's own row, with the sentence about what comes next.
Two people with the same score stand in the same order everywhere (the one who joined the boards first is ahead), so a tie never puts someone both on and off the podium.
Nobody is shown below the patient, there is no rank number beyond the podium and no "of N". A board lists only people who have scored, so until the first session of the week is finished the podium is open ("The podium opens with the first finished session of the week."). Under the board sit the patient's stats (streak, solid weeks, sessions, minutes, reps, best week), a line comparing their last finished week with every patient who has ever been in the same week of a plan ("Week 3 on your plan: better than 78 percent of patients in their week 3"), their tier with the next step, and their nickname settings.
Points¶
A week runs Monday to Sunday in the patient's own time zone, like streaks.
- The sessions due come from the plan's schedule (a Monday/Wednesday/Friday plan is three a week, a daily plan seven), with the sessions that exist as the floor. Where a clinic has switched plan continuation off, only the sessions that exist count. A week with nothing planned is not ranked, and a plan that starts next week is not ranked this week.
- A plan that starts (or starts again after a break) in the middle of the week is due only from its first session day: a Monday/Wednesday/Friday plan that begins on Thursday is one session that week, so finishing Friday is 1000 points, not 333.
- A fourth session in a three-session week earns nothing: the boards never reward doing more than the plan.
- Guided sessions, the logbook and Mark as finished all count. Sessions inferred from an app entrance do not.
- Ties break on the number of days trained (three sessions on three days beat three on Monday), then the current streak, then sessions done on the day the plan said, then who finished earlier.
Points climb as sessions are finished and reset every Monday.
Tiers¶
Tiers are earned against fixed thresholds over the last four scored weeks, never against other people, so a patient in a clinic of four can hold Platinum:
| Tier | Average weekly adherence | Needs |
|---|---|---|
| Starter | fewer than two scored weeks | |
| Bronze | 50 percent or more | 2 weeks |
| Silver | 70 percent or more | 2 weeks |
| Gold | 85 percent or more | 3 weeks |
| Platinum | 95 percent or more | 4 weeks |
Four Gold-or-better weeks in a row earn a laurel next to the nickname. The tier card always says the next step ("Two more weeks at 85 percent for Gold.").
Nicknames and privacy¶
Every onboarded patient of a clinic with the flag is on the board automatically, under a random nickname: an adjective and a cute animal ("Calm Otter"; in Russian «Спокойная выдра», with the adjective agreeing with the animal). The nickname is assigned at random, never from anything about the person, and is unique across the platform (3,600 two-word names; once those are taken a colour goes in the middle, "Calm Blue Otter", for 36,000 more); it never contains a number. A patient can change the nickname once a day and hide themselves from the clinic board, the all-clinics board, or both; their numbers still count for their care team, and they can still look at the boards. A patient hidden from the clinic board gets no Monday recap either: it is the one way to switch that push off.
Other patients see a nickname and numbers: never a name, a clinic, a condition or a body region. A patient's own therapist can see which of their patients is which nickname.
The weekly recap¶
On Monday (or Tuesday) morning in the patient's time zone, one push: "3 of 3 sessions last week, 1000 points. Silver kept. You made the podium!" A week with nothing done gets a fresh start instead of a row of zeros ("One session today puts you on this week's board."), and nothing is sent for a week with nothing planned. In the app, tapping it opens the leaderboard.
What therapists see¶
A Leaderboard tab on the web dashboard and a card at the top of the Patients view in the app, both opening the same view:
- The clinic's therapists by name, for the month, ranked four ways: Adherence (the share of their patients' planned sessions that were done), Starts (the share of patients invited in the last 30 days who finished a session within a week), Comebacks (patients who came back after a silent week, within a week of a message, a plan change, a note or a visit from the therapist) and Replies (median time to answer a patient's message, capped at three days). Adherence ranks once 10 sessions were due across 3 patients. A month is made of the patient weeks that overlap it, the week the 1st falls in included, so the board is never empty on the first days of a month; a patient archived during the month counts for the weeks up to the archiving. Therapists stand on their clinic's board by name as long as the clinic keeps it; their one switch is the all-clinics board.
- Their own tiles for the month, with the platform's first-week start rate as a yardstick.
- Can still make their week and Went quiet: the therapist's own patients, each with a Message button that opens the thread. Went quiet is the alarm: nothing done this week with two or more sessions due so far. Can still make their week is the nudge: patients who have missed at least one session whose day has passed but can still finish the week with a session a day. Someone on track is on neither list, so on Monday both are empty rather than listing the whole caseload; a patient is never on both.
- Your patients on the board: each of their patients with the nickname, points and tier.
Across clinics, therapists appear under a nickname too, with the rate only (no names, no clinic, no counts); each therapist has a switch to stay off it. A clinic can keep patient boards without ranking its staff (leaderboardsStaff: false).
Why the boards look the same at any size¶
The boards never render anything whose size depends on how many people take part: no totals, no "of N", no ranks below the podium, nobody below the viewer, no live percentiles, no lists of clinics. Comparisons beyond the fixed frame are against absolute tiers and against the platform's whole history, which only grows. In-clinic, a podium with an open seat reads as an invitation; the all-clinics board shows only once three people have scored on it, and a clinic that switches off, or an account that is deactivated, leaves the boards at once rather than at the end of the week.
Flags¶
| Flag | Missing value | Meaning |
|---|---|---|
leaderboards |
off | the clinic's patient boards, the home card, the recap |
leaderboardsStaff |
on (!== false) while leaderboards is on |
the clinic's therapist board and tab |
leaderboardsCrossClinic |
off | this clinic's people appear on, and can see, the all-clinics boards |
Turn leaderboardsCrossClinic on for several clinics at once: each all-clinics board needs three people who have scored on it, and until then that board says so in its place ("This board opens once three people are on it") while the All clinics segment itself appears once the week board (patients) or the adherence board (therapists) has three. A clinic with leaderboardsStaff: false has no therapists on the all-clinics therapist board either. Otera Measure's tenant (measureOnly) never has boards, whatever its flags say.
Internals¶
Data model¶
Three models (OktaPT-API/prisma/schema.prisma, migration 20261021024611_leaderboards):
LeaderboardParticipant: one row per person (userIdunique), the nickname key (quiet-otter, unique), the two visibility switches, the tier, the laurel and the last recap sent. Deleting the user deletes it.LeaderboardScore: one snapshot row per participant, board and period (WEEKandSTREAKkeyed by the Monday of the patient's local week;MONTH_ADHERENCE,MONTH_STARTS,MONTH_COMEBACKS,MONTH_REPLIESkeyedYYYY-MM), with the points, a packedrankKey(points and the tie-breaks in one integer, so "ahead of me" is one indexed comparison), the breakdown behind the points,eligible, andcrossClinicfolded in at write time.LeaderboardBenchmark: de-identified histograms of weekly adherence by plan week, and the platform's first-week start rate. Counts only.
Both tenant-owned models are in TENANT_MODELS; the services read through prismaWithoutTenant with explicit conditions because the all-clinics boards cross tenants.
Computation¶
src/services/leaderboards/:
| File | Role |
|---|---|
scoring.ts |
the pure rules: due sessions from the schedule, points, rank keys, tiers, histogram percentiles |
nicknames.ts |
the word lists in both languages and the random keys |
periods.ts |
the patient's local week, month keys, the Mondays of a month |
patientWeek.ts |
a tenant's patients' current week in one pass (four queries), and the row writer, which writes only rows whose numbers changed; the breakdown carries due, dueSoFar (due today or earlier) and dueBefore (due before today) |
therapistMonth.ts |
caseload adherence from the patients' weekly rows (this week prorated to today), starts, comebacks, replies |
tiers.ts |
tiers and laurels from the last four finished weeks |
benchmarks.ts |
the nightly SQL over every finished patient-week |
read.ts |
the fixed frame: podium, three ahead, the viewer; the home card, the stats, the therapist's month and caseload |
enroll.ts |
automatic enrolment and nicknames |
sweep.ts |
the jobs |
Jobs run in the API process from NotificationJobs.startAllJobs with the per-tick Redis lock: the patients' sweep every ten minutes (*/10), the therapists' pass hourly (a monthly board needs no more), the benchmark rebuild nightly at 03:45 UTC, the recap pass hourly. Each sweep writes only the rows whose numbers changed. runCompletionSideEffects recomputes the one patient right after a session and waits for it, so the card the client fetches next is already fresh. A patient's first visit of the week, and a therapist's first visit of the month, compute the caller's rows on the spot. A week that has ended is never written again.
Endpoints¶
All under /v2/leaderboards, behind authentication, the role guard and requireTenantFeature("leaderboards") (404 when off); the therapist routes also need the staff board.
| Method | Path | Who | Answer |
|---|---|---|---|
| GET | /me |
patient | participant, which boards exist, the week, the standing |
| GET | /board?scope=clinic\|all&board=week\|streak |
patient | podium, ahead, me |
| GET | /stats |
patient | stats, tier, benchmark |
| PATCH | /me |
patient | { clinicBoard?, crossClinic? } |
| POST | /me/nickname |
patient | a new nickname; 429 NICKNAME_COOLDOWN within a day |
| GET | /therapist/me |
therapist | participant, boards, the month, the standing |
| GET | /therapist/board?scope=&board=adherence\|starts\|comebacks\|replies |
therapist | podium, ahead, me (names in-clinic, nicknames across) |
| GET | /therapist/caseload |
therapist | can still make their week, went quiet, on the board |
| PATCH | /therapist/me, POST /therapist/me/nickname |
therapist | as for patients |
No board response carries another person's user id. scope=all answers 404 where the clinic does not take part, and 404 BOARD_NOT_AVAILABLE until three people have scored on that particular board (the apps then show a line in the board's place and keep the segment). A patient who cannot be on a board (a sample, never onboarded, deactivated) gets 403 NOT_ELIGIBLE, which the apps show as "no board here".
Every board read (boardWhere in read.ts) checks at request time that each person's account is still eligible and, across clinics, that their clinic still has the flags on, so nobody lingers until the period ends. A board lists people with at least one point (the replies board, ranked in minutes, lists every eligible median). The stats never say how many patient-weeks a benchmark covers; it shows only once there are 100.
Exclusions¶
Sample patients, Otera Measure's tenant, patients who never onboarded, deactivated accounts, templates and app-entrance completions never count; archived connections count for the days before archiving. Self-serve patients have no therapist and count for none.
Tests¶
src/services/leaderboards/__tests__/ (scoring, nicknames, periods, the recap copy, the change-only writer) and src/routes/__tests__/leaderboards.test.ts (flags, roles, the measure-only tenant, the fixed frame over an in-memory score table, ties, hiding, the cooldown and a nickname change, names versus nicknames, the all-clinics threshold, a clinic switching off, deactivated and never-onboarded accounts, the open podium, the therapist's switches, the worklist rules, the benchmark without its size, the recap pass sending once). Web: npm run test:leaderboards (the copy helpers, and the plural forms over the real strings). App: lib/__tests__/leaderboards.test.ts and lib/__tests__/notificationRoutes.test.ts.
Counts in both apps' strings take their plural forms (_one/_few/_many/_other on the web, { one, few, many } in the app through tCount), and Russian sentences keep the nickname in the nominative ("Тихая панда впереди на 333 балла.").