22 KiB
Player achievements and visual refresh implementation plan
Status: Proposed October 7, 2026. Implementation has not started.
Make Electric Cherry more rewarding to revisit by showing recent player achievements, making match results easy to share, and giving maps a recognizable visual identity. Deliver player achievements first, then refresh the presentation across Fleet, Autoscaling, and operations. Each workstream should be useful on its own.
Intended experience
- A player can compare this week's or this month's results, open the match that earned a record, and see how it improved on their previous best.
- An operator can export a readable match or player result card without manually assembling a screenshot.
- Maps and games are recognizable at a glance. Player counts, rounds, health, and required actions have a clear visual order.
- Routine states look calm. Failures, stale observations, and safety blockers remain visible without opening technical details.
Scope and defaults
| Area | First release |
|---|---|
| Leaderboard periods | All time, This week, and This month, with previous/next period navigation. Keep All time as the default. |
| Calendar | Weeks start Monday at 00:00 UTC; months start on the first at 00:00 UTC. Show the boundaries explicitly. Personal timezone preferences format timestamps without changing competition membership. |
| Personal bests | Highest round, best game kills, and highest reported score, compared within the same game and map variant. |
| Sharing | A preview and PNG download for a match summary or one player's result, plus an authenticated match link. Native device sharing is an optional enhancement with download/copy fallbacks. |
| Visual direction | Existing dark surfaces, electric-blue accent, typography, and brand mark, extended with original SVG map emblems and consistent game/status treatments. |
| Initial visual coverage | Fleet, server detail, Autoscaling, provisioning and host operation history, player highlights, and match/share cards. |
Season rewards, player-account linking, public profile/match pages, notification subscriptions, rank-change tracking, custom themes, and photographic map artwork are outside this release. These features need separate product decisions.
Existing foundations
The leaderboard guide defines the current qualification rules, player identity, record evidence, and access model. Completed matches and player participation persist independently of live servers and operational history retention. Reuse these records rather than requiring reporter changes.
The leaderboard already has game/map/metric filters, shareable filter URLs, player profiles, source-match links, and refresh behavior that protects someone inspecting a row. Fleet already has search, pins, grouping, cards/table views, and a live match spotlight. Preserve those behaviors.
Extend the shared UI patterns, semantic tokens,
ServerIdentity, StatusBadge, and existing disclosure controls. The visual
work should refine their use across pages.
Player achievements
Calendar rankings
Add validated period=all|week|month and optional start=YYYY-MM-DD query
parameters. An omitted start selects the current period; an explicit start must
be a Monday for a week or the first day for a month. Reject malformed dates,
misaligned boundaries, and a start supplied with All time. Disable navigation
into future periods. Existing URLs without these parameters retain their behavior.
Return resolved period metadata, including inclusive start, exclusive end, and the fixed UTC timezone. Use the same resolved boundaries for rankings, summary totals, eligibility thresholds, exact-player rank, selected profile history, and record evidence. Apply a period to a match's completion time, so a match crossing midnight belongs entirely to the period in which it completed.
Retain the existing rules: only completed games reaching round 2 with qualifying player participation contribute; ties share rank; search preserves actual rank; GUIDs combine across T5/T6; map variants remain distinct. Efficiency thresholds apply to matches inside the selected period. Keep known map options available when a selected period has no results.
On the profile, label lifetime totals separately from the selected competition and filtered match history. Carry period/start through profile links, record links, pagination, and return navigation. Reset pagination when the period changes. A copy-link action should resolve the current period to an explicit start so the shared link still identifies the same competition next week.
Add an additive nullable first_completed_at field to historical matches. Set it
once when a match first becomes completed. The current ingestion path can update
ended_at when a changed final report arrives; a correction must not move a
match into another competition. Preserve the existing ended_at behavior.
Backfill completed rows from valid ended_at values as the best available
historical estimate. Unknown completion times remain eligible for All time but
are excluded from dated competitions, with that limitation explained in report
help.
Create any index on the new field after the additive column migration. Startup currently executes the base schema before adding columns to existing databases. Use a dedicated, idempotent migration that normalizes valid values to ISO UTC; existing timestamp-normalization markers must not cause upgrades to skip it. Evaluate a partial completion-time index for completed matches using query plans and the existing synthetic benchmark before selecting the final index. Period membership stays stable after correction, while corrected counters can update the rankings for a past period.
Resolve the period before report submission, and include its bounds in all in-flight, worker-response, and ranked-scope cache keys. A week or month must roll over without requiring a database write. Extend evidence requests with the same bounds so a period's record never links to an equal result from another period. Preserve the current all-time evidence tie-breaking behavior.
Primary changes:
- Leaderboard types, API validation, page and profile loaders, and navigation context.
- Ranking queries, record evidence, player profile queries, and report worker/cache inputs.
- Database schema/migration and completion ingestion.
Personal best highlights
Show a compact Recent personal bests section on player profiles and annotate the relevant results on completed match pages. Use copy such as “Highest round on Town Survival: 31, up from 27.” Identify whose record it is; dashboard accounts are not currently linked to player GUIDs.
A milestone is a strict improvement over all earlier qualifying results for the
same GUID, game, map key, and metric. Order by first_completed_at, then match ID
for deterministic simultaneous completions. An equal result is not a new record.
A first qualifying result is labeled First recorded result rather than an
improvement. A later record does not remove the earlier match's “Personal best
at the time” annotation. Current record evidence remains a separate concept.
Derive milestones in the report worker from retained history for the requested player or bounded match roster. Compare against the complete earlier history before applying the displayed date range or result limit. Do not make the first match of each week a false personal best by limiting the comparison to that week. Batch the requested player/map scopes to avoid one query per metric or row.
Use undated historical values as a conservative comparison floor and do not emit dated milestones for them. Explain in methodology that incomplete older timestamps can suppress a highlight. Recompute derived milestones after counter corrections using the existing report revision invalidation. Duplicate reports must not create duplicate highlights. No new event journal, notification history, or full-history startup backfill is needed.
Initially show at most five grouped match highlights on a profile, with a link to its existing match history. Follow the selected game/map and competition period for displayed highlights while comparing against all earlier history. Group multiple improved metrics from one match. Keep the existing qualification caveats near the evidence: highest round can include late joins, score is the highest observed balance, and first observation does not prove continuous participation. Add no confetti or repeating animation.
Add a dedicated milestone query/presentation module alongside the existing leaderboard records and pass only the needed results to profile and match pages. Use explicit report-worker job handling for these reads rather than expanding the worker's current player-report fallback implicitly.
Shareable match results
Add Share result to completed match pages, and a player-specific action to each eligible result. Open an accessible dialog with two layouts: Match summary and Player result. The preview and exported image use the same bounded presentation model.
The summary includes map, game, final round, completion date, and known duration. The player layout adds display name, recorded player statistics, and applicable personal-best improvements. Use the player's observed highest round for their result, which may differ from the match's final round. Include relevant late-join or missing-start context. Omit unknown values rather than rendering them as zero.
Render a 1200 × 630 PNG locally using bundled fonts and assets. Share-card layout code must wrap or deliberately truncate long names without dropping essential qualifications. Provide semantic HTML for the preview; the canvas is an export implementation detail. Wait for fonts/assets before export and show a retryable error if generation fails. Native sharing, when supported, must run only from an explicit user action.
Construct an explicit export model. Exclude chat, GUIDs, internal instance IDs, addresses, keys, and raw diagnostics; the current match detail object includes chat and must not be exported wholesale. Offer a plain match link labeled as requiring Electric Cherry access. Do not change route permissions or introduce an unauthenticated image endpoint. Downloads remain useful to recipients without a dashboard account.
Implement the first version with the current brand styles. Apply the new map emblems during the visual workstream without changing result semantics.
Visual refresh
Map and game identity
Add a presentation resolver keyed by game and selected map slug, backed by the existing map catalog. Use original small SVG emblems: a theater marquee for Kino, a rocket for Ascension, a moon for Moon, prison bars for Mob, and excavation/gears for Origins. Complete the catalog with the same simple line or silhouette style and a neutral custom-map fallback.
Maps sharing an environment may share artwork, while supported Survival
variants retain explicit names. Historical views resolve identity from
their stored game/map key and variant, never from a replacement live server.
An ambiguous engine map must use a fallback rather than inventing a variant.
Extend MatchDetail, PlayerMatch, and their queries with the stored map key;
they currently expose friendly map names without that identity. Do not infer
historical variants from display names.
Introduce MapEmblem and a shared game label/badge. Keep Black Ops I/II readable
without color, using distinct I/II marks where space is limited. Put larger
emblems on map headings and result cards, smaller ones on lobby cards, and keep
table mode compact. Artwork is decorative when adjacent text names the map.
Use locally shipped, dimensioned SVG assets or components. Keep the current fonts and dark/electric-blue palette. Game decoration must not reuse danger or warning tones. Add only the semantic tokens needed for identity and achievement styling, and document their use.
Information order and status consistency
| Surface | Presentation changes |
|---|---|
| Fleet cards | Map/lobby/game first; health, player count, and round next; roster and match duration below; lower-priority timing and technical identity in supporting details. Preserve compact empty cards, search, pins, table mode, and the match spotlight. |
| Server detail | Add map/game identity to the shared header with health. Keep player counts, round, and timing in the Live content so Configuration and Diagnostics remain focused on their task. |
| Autoscaling | Use shared identity and mode badges, a compact count strip, and a prominent current decision/blocker. Keep per-map targets, Off/On setting, ownership, global pause, and unsettled operations visible. Reuse existing evidence/history disclosures. |
| Provisioning and host operations | Use consistent labels and status components while preserving action, target, result, elapsed time, and specific wait reasons. Put long IDs, source paths, and low-level metadata behind explicit details/copy controls. |
| Player and match results | Reuse map identity and achievement treatment across profile highlights, match results, and downloaded cards. Distinguish a current record from a record earned at that time. |
Align status presentation by meaning, while preserving domain-specific labels.
For example, StatusPip currently colors starting/provisioning amber while shared
badges use an informational treatment. Normal queued work and waiting for players
should not look like failures. Keep the specialized provisioning status helpers
that explain why an operation is waiting.
Do not collapse freshness warnings, unknown player counts, failed operations, safety blockers, pending destructive work, or unsettled commands. Raw identities must remain reachable through keyboard-accessible details and copy controls. Preserve navigation context, permission checks, confirmations, and existing polling/store behavior.
Delivery sequence
Complete, validate, and commit each phase before starting the next. Finish the player-achievement workstream before starting the visual refresh. Commit any unrelated fix separately.
| Phase | Reviewable result | Acceptance gate |
|---|---|---|
| A1 Completion time and period queries | Stable completion field/migration, period parsing, scoped ranking/evidence/profile queries, and cache keys. | Boundary, correction, qualification, and all-time compatibility tests pass; query plans and representative timing are recorded. |
| A2 Period navigation | Week/month controls, history navigation, period metadata, profile context, and stable share URLs. | Direct links, back/forward, rollover, empty periods, and inspection-preserving refresh work on desktop and mobile. |
| A3 Personal best highlights | Derived milestones, profile section, and match annotations. | Strict improvements, ties, first results, variants, corrections, and undated history behave as specified; long player histories remain practical. |
| A4 Result sharing | Accessible preview, PNG download, authenticated copy-link action, and supported native sharing. | Export matches the preview, contains only selected result data, handles long/incomplete data, and works after live-server deletion. Update leaderboard documentation and commit the completed workstream. |
| B1 Shared visual identity | Original emblems, resolver, game labels, achievement tokens, and usage guidance. | Every map selection resolves correctly or falls back; no misleading variant, color-only meaning, or missing-asset layout shift. |
| B2 Fleet and operational presentation | Apply identity/hierarchy/status rules to Fleet, server detail, Autoscaling, provisioning, and host operation history. | Existing actions and critical states remain visible and usable; no new polling or behavior changes. |
| B3 Player presentation and review | Apply the shared visual identity to player highlights, match pages, and export layouts; refresh documentation images. | Complete the visual/accessibility review and update UI guidance, then commit the completed workstream. |
Validation
Use an isolated synthetic database. Extend existing fixture helpers; the current
seed:fixture package script points to a missing script and must not be assumed
to provide a working preview dataset.
For achievements, cover Monday/month/year boundaries, leap years, viewer timezone differences, cross-boundary matches, unknown completion timestamps, and corrections after completion. Test upgrade from an existing database, timestamp normalization, and restart after the completion migration. Verify that an idle database still changes the current period at rollover. Test period-scoped record sources, ties, efficiency eligibility, GUID identity across games, T6 variants, deleted servers, profile pagination, and all-time response compatibility. Match/player qualification must be shared with existing leaderboard logic.
Test personal-best comparison against full prior history, simultaneous completion ordering, duplicate final reports, first results, later-surpassed records, and grouping several improvements from one match. Benchmark a player with a long history and a full roster. Bound request scopes and response sizes; do not hide query costs by silently ignoring older matches. Keep report work off the main request thread. Extend the existing leaderboard benchmark for period queries and record before/after cold and warm timings under the same fixture.
For sharing, verify the explicit export model, missing values, long/Unicode names, font/asset readiness, downloaded-image readability, and browser fallbacks. Confirm that anonymous access to match/profile pages remains unchanged and that sharing requires an explicit action.
Review representative screens at 1440, 390, and 320 pixels, with 200% zoom, keyboard navigation, reduced motion, and long custom names. Include empty, occupied, stale, offline, starting, failed, waiting, and paused states. Check contrast, focus order, non-color status labels, readable export text, and absence of nested interactive targets. Capture before/after screenshots using identical fixtures; verify that map, players/round, health, and required action are easy to identify without opening details.
During implementation, run npm run check, focused Vitest tests for changed
behavior, and npm run build for each relevant phase. Before completing each
workstream, run the full controller test suite and browser review. Agent tests
are needed only if agent code or its protocol changes; neither is planned here.
Use meaningful behavior/query tests for new rules and manual visual checks for
decorative changes.
After delivery, fold lasting behavior into the leaderboard guide and UI patterns, refresh the Fleet documentation image, and remove this completed plan according to the repository documentation convention.