Feature Requests
Planned
[G1] XP ledger, scoring model and level curve (foundation)
Foundation ticket for the whole gamification effort. Everything else plugs into the point economy defined here, so this lands first. No UI in this ticket.
Why first
XP is the currency behind levels, the leaderboard and part of the challenge system. Defining the scale late would mean recalibrating scores that are already public, which is unmanageable. The scale is settled here, then reused.
Scope
XpEntryledger, append-only likeActivityEvent. Reversal of a cancelled action is a negative entry referencing the original, never a delete.- Mandatory
reason+sourceType/sourceIdpointing at the real entity. Uniqueness on(userId, reason, sourceType, sourceId). For repeatable gains, the natural source is the event row (EpisodeWatch,Comment), which is already unique — so a rewatch legitimately earns again while an API retry is rejected. Watch out: Postgres treatsNULLas distinct in a unique index, so gains with no source entity escape the constraint. - Materialised counter on
User(sum of the ledger), needed for leaderboard performance. The ledger stays the source of truth. - Daily caps computed from the ledger, in the user's own timezone (
User.timezone), not UTC. - Level curve as a pure function in
packages/shared. The level is never stored — it derives from XP. Two knobs only: base costBand exponentp, cost of level N→N+1 =B × N^p. Starting point to challenge:B=100,p=1.5(level 2 during onboarding, ~10 after nine months of regular use, 50 out of practical reach). No maximum level. Recalibrating must never require a migration. - Initial scoring table. Indicative starting point, to be argued during implementation: episode 10 · movie 50 · book finished 150 · game finished 200 · album 20 · series finished 100 · review 50 (+30 when written, above a minimum word count) · comment 10 (max 5/day, minimum trimmed length ~15 chars) · helpful vote received 5 · achievement 100–1000 by rarity.
GAMIFICATION_ENABLEDflag, same role asSOCIAL_ENABLED: a self-host choice and a kill switch, not a rollout flag.
Settled decisions
- One single global level. Social is one XP source among others, not a separate level.
- XP, levels and achievements work without
SOCIAL_ENABLED. Only the leaderboard and social-sourced XP are gated. - Imports never grant XP (they do grant achievements — see G2/G3). XP must stay comparable across accounts, and importing ten years of Trakt is comparable to nothing.
- XP is taken back when the underlying action is undone, otherwise an add/remove loop is a farming exploit.
Out of scope
Any UI, achievements, the leaderboard, penalties (see B8).
Open questions for implementation
- Final shape of the scoring table and the curve constants.
- Which reasons need a source entity to make the unique index effective.
0 Comments
Sign in to comment
No comments yet. Be the first to share your thoughts!
