feat(store): persist points.board and serve it from GET /points #18

Merged
whitlocktech merged 1 commits from feat/points-board into edge 2026-07-29 07:53:20 +00:00
Member

What & why

Protocol 3.0 §7 (docs/link/v3.md), order 4 of 6. The shard publishes ~25 points/loyalty leaderboards as one points.board frame per system; the sidecar folds each into a projection table and serves them back.

Part of a four-repo change: servuo-plugins #4 → link (this) → website #114 → docs #69.

  • points_boards(system PK, name, json, updated_t) — keyed by the shard's own PointsType name (QueensLoyalty, CleanUpBritannia, …). name is hoisted only for the ORDER BY.
  • main.rs gains a points.board arm keyed on system, alongside the existing champ / guild / governor / house / ruleset projections. There is deliberately no delete counterpart: the shard's set of point systems is fixed at startup, so it emits no points.remove — the same shape the governor board already has.
  • GET /points returns every board ordered by display name. GET /points/:system returns one, or 404 when the shard has never published that system.

404 and "a published board nobody has scored in yet" (200, empty top) are different answers, and the website renders them differently.

Store-backed rather than an RPC for the same reason as the other boards, and it matters more here: these are standings accumulated over months, so blanking them during a shard restart reads as data loss rather than as staleness.

PROTOCOL_VERSION stays at 2. The bump to 3 is the one-time edgemain cutover in v3.md §4, not a per-phase change — a bump 409s every protected route and closes the website's WS, so doing it per phase would break the site repeatedly.

How it was tested

  • cargo build and cargo clippy --all-targetsclean, no warnings.
  • Driver on the loopback link, exercising the projection semantics directly:
    • two systems stored and served independently;
    • a re-emitted system overwriting rather than accumulating (verified players and the top length both replaced, not appended);
    • ordering by display name;
    • 404 for an unknown system, 401 unauthenticated.
  • Against the real ServUO shard (43,011-mobile world) with the real plugin from servuo-plugins #4: five live boards arrived over the loopback link, were persisted, and served from GET /points. This is also the path that surfaced the plugin's maxPoints overflow — the sidecar forwarded it verbatim, which is exactly what a dumb forwarder should do, and re-serving after the plugin fix showed the corrected values.
  • End to end into the website: GET /points backfill (snapshotted points boards from /points {"count":2}) and live WS fan-out both confirmed from the website side in #114.

Checklist

  • I have read CONTRIBUTING.md.
  • The change builds and existing tests/checks pass locally.
  • I have added or updated tests/docs where it makes sense. (Protocol docs in docs #69.)
  • My commits are reasonably scoped with clear messages.

AI-assisted contributions (required)

  • No AI tools were used to produce this contribution.
  • AI tools were used. Tool(s): Claude Code (Opus 5). I have reviewed and understand every change, and take responsibility for it. AI-authored commits are marked with a Co-Authored-By trailer.

License

  • I agree that my contribution is licensed under this project's license (GNU GPL v3.0 or later), and I have the right to contribute it.
## What & why Protocol 3.0 **§7** ([`docs/link/v3.md`](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/edge/link/v3.md)), order 4 of 6. The shard publishes ~25 points/loyalty leaderboards as one `points.board` frame per system; the sidecar folds each into a projection table and serves them back. Part of a four-repo change: servuo-plugins #4 → link (this) → website #114 → docs #69. - **`points_boards(system PK, name, json, updated_t)`** — keyed by the shard's own `PointsType` name (`QueensLoyalty`, `CleanUpBritannia`, …). `name` is hoisted only for the `ORDER BY`. - **`main.rs` gains a `points.board` arm** keyed on `system`, alongside the existing champ / guild / governor / house / ruleset projections. There is deliberately **no delete counterpart**: the shard's set of point systems is fixed at startup, so it emits no `points.remove` — the same shape the governor board already has. - **`GET /points`** returns every board ordered by display name. **`GET /points/:system`** returns one, or **404** when the shard has never published that system. `404` and "a published board nobody has scored in yet" (`200`, empty `top`) are different answers, and the website renders them differently. Store-backed rather than an RPC for the same reason as the other boards, and it matters more here: these are standings accumulated over **months**, so blanking them during a shard restart reads as data loss rather than as staleness. **`PROTOCOL_VERSION` stays at 2.** The bump to 3 is the one-time `edge` → `main` cutover in v3.md §4, not a per-phase change — a bump 409s every protected route and closes the website's WS, so doing it per phase would break the site repeatedly. ## How it was tested - `cargo build` and `cargo clippy --all-targets` — **clean, no warnings**. - **Driver on the loopback link**, exercising the projection semantics directly: - two systems stored and served independently; - a re-emitted system **overwriting** rather than accumulating (verified `players` and the `top` length both replaced, not appended); - ordering by display name; - `404` for an unknown system, `401` unauthenticated. - **Against the real ServUO shard** (43,011-mobile world) with the real plugin from servuo-plugins #4: five live boards arrived over the loopback link, were persisted, and served from `GET /points`. This is also the path that surfaced the plugin's `maxPoints` overflow — the sidecar forwarded it verbatim, which is exactly what a dumb forwarder should do, and re-serving after the plugin fix showed the corrected values. - **End to end into the website**: `GET /points` backfill (`snapshotted points boards from /points {"count":2}`) and live WS fan-out both confirmed from the website side in #114. ## Checklist - [x] I have read [CONTRIBUTING.md](CONTRIBUTING.md). - [x] The change builds and existing tests/checks pass locally. - [x] I have added or updated tests/docs where it makes sense. *(Protocol docs in docs #69.)* - [x] My commits are reasonably scoped with clear messages. ## AI-assisted contributions (required) - [ ] No AI tools were used to produce this contribution. - [x] AI tools were used. Tool(s): `Claude Code (Opus 5)`. I have reviewed and understand every change, and take responsibility for it. AI-authored commits are marked with a `Co-Authored-By` trailer. ## License - [x] I agree that my contribution is licensed under this project's license (**GNU GPL v3.0 or later**), and I have the right to contribute it.
wtclaude added 1 commit 2026-07-29 02:06:33 +00:00
Protocol 3.0 §7 (docs/link/v3.md). The shard publishes ~25 points/loyalty
leaderboards as one points.board frame per system; the sidecar folds each into a
projection table and serves them back, so the site's leaderboards page renders
during a shard outage.

  - points_boards(system PK, name, json, updated_t), keyed by the shard's own
    PointsType name. `name` is hoisted only for the ORDER BY.
  - main.rs gains a points.board arm keyed on `system`, alongside the existing
    champ/guild/governor/house/ruleset projections. There is deliberately no
    delete counterpart: the shard's set of point systems is fixed at startup, so
    it emits no points.remove — the same shape the governor board already has.
  - GET /points returns every board ordered by display name; GET /points/:system
    returns one, or 404 when the shard has never published that system. 404 and
    "a published board nobody has scored in yet" (200, empty top) are different
    answers, and the website renders them differently.

Store-backed rather than an RPC for the same reason as the other boards, and it
matters more here: these are standings accumulated over months, so blanking them
during a shard restart reads as data loss rather than as staleness.

PROTOCOL_VERSION stays at 2 — the bump to 3 is the one-time edge → main cutover
in v3.md §4, not a per-phase change.

cargo build and cargo clippy --all-targets are clean. Smoke-tested against a
driver on the loopback link: two boards stored and served, a re-emitted system
overwriting rather than accumulating, 404 for an unknown system, 401
unauthenticated. Also verified against the real ServUO shard, which fed five
live boards through this path.

Co-Authored-By: Claude <noreply@anthropic.com>
whitlocktech approved these changes 2026-07-29 07:53:13 +00:00
whitlocktech merged commit 2e386a9d5c into edge 2026-07-29 07:53:20 +00:00
whitlocktech deleted branch feat/points-board 2026-07-29 07:53:21 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RunicGateway/link#18
No description provided.