---
title: "Leaderboards rankings and persistent high scores"
description: "Players want to see who is on top across sessions: highest score, fastest time, most wins. A leaderboard is durable per-player records plus a ranking sort you do in a script. There is no `StatsComponent` (stripped) and no built-in ranked board."
engineVersion: v1.0.234
date: 2026-09-28
license: "(c) Gessa, proprietary. Cite with attribution to https://gessa.ai/docs/. Terms: https://gessa.ai/terms/."
canonical: https://gessa.ai/docs/knowledge/playbooks/leaderboards-and-rankings/
---

# Leaderboards rankings and persistent high scores

## When to use

Players want to see who is on top across sessions: highest score, fastest time,
most wins. A leaderboard is durable per-player records plus a ranking sort you do
in a script. There is no `StatsComponent` (stripped) and no built-in ranked
board.

## The recipe

Author with `project_apply_script_semantic_patch` using the `ai_safe` op pack;
the warehouse reads happen in a GessaScript body.

1. Persist each player's best: on run end, compare the new score to the stored
   best and `writeTypedStateValue` the larger (or `incrementNumericState` a
   running total) into a durable record keyed by the durable player id, in a
   leaderboard store (for example `storeKey:"leaderboard"`, one record per
   player).
2. Track live scores in a match with `ctx.match.addScore` for a subject and read
   with `ctx.match.getScore`; mirror the current value to the HUD with
   `wireHudStateBinding`.
3. Build the ranked list: in a GessaScript body, `ctx.warehouse.query` the
   leaderboard store to read the records, then SORT them by the score field
   yourself. Records come back ordered by recordKey in canonical collation, not
   by value, so the ranking order is your computation, not the query.
4. Page large boards: pass the query result `nextCursor` back verbatim to walk
   pages; take the top N after sorting rather than assuming the first page is the
   top.
5. Show it: push the ranked names and scores into a UI panel (see
   ui-hud-panels-and-widgets) with `ctx.ui.setWidgetValue`, or bind a single
   headline stat with `wireHudStateBinding`.

## Pitfalls

- Warehouse query does NOT sort by value; do not expect an orderBy. Read the
  records and rank them in the script.
- Key each record by the DURABLE player id, not a per-session id, or the board
  resets every match (the same rule as a persistent scoreboard subject key).
- Warehouse writes are prefetch-then-drain and authority-gated; update the best
  on the server at run end, not per tick.
- A giant board is costly to read every frame; recompute the ranking on score
  change or on a timer, not each tick.

## Verify

- `simulation_run` (`qa.run.start`) to prove a new best updates the record and the
  ranked list reorders.
- `project_get_graph_snapshot` to confirm the score-write handlers and the HUD
  binding landed.
