43 lines
2.2 KiB
Markdown
43 lines
2.2 KiB
Markdown
# ADR 0003: Personal guild profile read model
|
|
|
|
- Status: Accepted
|
|
- Date: 2026-08-06
|
|
|
|
## Context
|
|
|
|
Signed-in members need one private page that combines their Discord identity with XP,
|
|
level progress, voice time, and leaderboard standing. The Discord bot remains the
|
|
authoritative writer for `user_levels` and `voice_time`, while the website has only a
|
|
read-only connection to those tables. XP values use `numeric(40,0)`, which may exceed
|
|
JavaScript's safe integer range.
|
|
|
|
## Decision
|
|
|
|
1. `/profile` identifies the current member from the server-side NextAuth session and
|
|
Discord provider account. Anonymous visitors receive a Discord login prompt that
|
|
returns them to `/profile`; profiles cannot be selected by URL parameter.
|
|
2. Discord identity, XP, and voice data load independently. Session identity is the
|
|
fallback when Discord member metadata is unavailable, and unavailable activity
|
|
sources report section-level errors.
|
|
3. The stored bot level remains authoritative. Next-level progress duplicates the
|
|
bot's documented XP formula and performs all XP arithmetic with `bigint` before
|
|
converting the bounded percentage to a number. Level 200 is the maximum and does
|
|
not display a progress bar.
|
|
4. XP and voice ranks use the public leaderboards' shared competition ranking. XP
|
|
rank excludes level-0 members. Voice time and rank include only voice and Stage
|
|
channels that currently exist in the guild.
|
|
5. Missing activity rows display zero activity and no rank. The website does not
|
|
create rows or write inferred values back to the bot database.
|
|
6. One public leaderboard SSE subscription refreshes an open profile after either an
|
|
XP event or a voice-time event. Redis remains a notification path rather than the
|
|
source of truth.
|
|
|
|
## Consequences
|
|
|
|
- A signed-in member sees only the profile associated with their own Discord account.
|
|
- Profile totals and public leaderboard values share ranking and voice-channel scope.
|
|
- The bot formula is a cross-service contract. Formula or maximum-level changes must
|
|
update the bot integration document and website helper together.
|
|
- Large cumulative XP values remain exact through database reads, calculations, and
|
|
formatting.
|