Show total score in match results when available, with a historical balance fallback #147

Closed
opened 2026-10-12 16:01:54 -04:00 by goddard · 1 comment
Collaborator

Match results still show the highest reported points balance even when cumulative
score has been saved. Select one score presentation for each match: total score
for matches with recorded totals, and the existing balance display for historical
matches without totals.

  • src/routes/matches/[id]/+page.svelte:34 always selects bestScore with the
    heading Highest reported score. The cells at line 163 render that selected
    field, so scoreTotal is never displayed.
  • src/lib/server/fleet/match-history.ts:128 already returns scoreTotal for
    each participant alongside bestScore.
  • src/lib/server/db/player-score-total.ts:9 preserves old records with null
    totals. Their balances cannot reconstruct cumulative points earned.
  • src/routes/matches/[id]/+page.svelte:182 still explains only the balance;
    this text must follow the selected score presentation.

Verified against c296769b33392c564d787451844853f31e0c7b80 on 2026-10-12. Production match #1276
has a participant with scoreTotal: 8470 and bestScore: 2760; the rendered page
shows Highest reported score: 2,760, with no Total score column.

Screenshots

Both screenshots use the same disposable synthetic fixture, with totals/balances
staged as 8,470/2,760 and 500/500. The after image is a DOM mockup, not an
implemented application change. Production data and application source were not
modified.

Before:
Current match results

After (mockup):
Match results with Total score

Fix

  • Select the score column at match level based on recorded data, not a date
    cutoff. If any participant has a non-null scoreTotal, show Total score
    and the participants' scoreTotal values. Zero is a valid recorded total.
  • If all participants have null/missing totals, preserve Highest reported
    score
    and bestScore for historical/legacy matches. Show exactly one score
    column in either case, in the current score column's position.
  • Handle mixed coverage explicitly: in a match using Total score, show a dash
    for a participant without a total. Never substitute a points balance beneath
    the Total score heading. Explain that the dash means no total was recorded.
  • Make the column selection react to match navigation/data updates so moving
    between historical and newer matches updates the label, values and explanation.
  • Update Player results/About these results copy and the match-results guide.
    Describe total score as the highest recorded cumulative points earned in this
    match; players absent from the final report retain their latest recorded total.
    Keep the existing balance explanation only for the historical fallback.
  • Preserve stored balances and totals, existing historical records, and
    leaderboard/profile ranking semantics. Do not backfill totals from balances,
    add repeated telemetry snapshots together, or require a schema migration for
    this display change.
  • Add UI regression coverage for totals differing from balances, all-legacy
    matches, zero totals, mixed/null totals, participants without stats, and
    navigation between score presentations. Assert that newer matches show the
    total and no Highest reported score heading, while historical matches retain
    their original label and balance.
  • Run the relevant UI tests, the existing score ingestion/migration/match-detail
    tests, npm run check, and the full application suite.

Validation already performed

The existing player-score-total-migration, keyed-telemetry and match-detail
test files pass: 23 tests. This validates storage and historical preservation;
it does not validate the proposed UI behavior. A search through all 135 existing
open and closed issues found no ticket covering this score-display change.

Match results still show the highest reported points balance even when cumulative score has been saved. Select one score presentation for each match: total score for matches with recorded totals, and the existing balance display for historical matches without totals. - `src/routes/matches/[id]/+page.svelte:34` always selects `bestScore` with the heading **Highest reported score**. The cells at line 163 render that selected field, so `scoreTotal` is never displayed. - `src/lib/server/fleet/match-history.ts:128` already returns `scoreTotal` for each participant alongside `bestScore`. - `src/lib/server/db/player-score-total.ts:9` preserves old records with null totals. Their balances cannot reconstruct cumulative points earned. - `src/routes/matches/[id]/+page.svelte:182` still explains only the balance; this text must follow the selected score presentation. Verified against `c296769b33392c564d787451844853f31e0c7b80` on 2026-10-12. Production [match #1276](https://ec.thewrightserver.net/matches/1276) has a participant with `scoreTotal: 8470` and `bestScore: 2760`; the rendered page shows **Highest reported score: 2,760**, with no Total score column. **Screenshots** Both screenshots use the same disposable synthetic fixture, with totals/balances staged as 8,470/2,760 and 500/500. The after image is a DOM mockup, not an implemented application change. Production data and application source were not modified. Before: ![Current match results](https://gitea.thewrightserver.net/attachments/8df8d812-c7f3-4c0b-bd92-8b0d0baa58b0) After (mockup): ![Match results with Total score](https://gitea.thewrightserver.net/attachments/101c4275-f2e5-4ce4-8854-b5805600c35e) **Fix** - [x] Select the score column at match level based on recorded data, not a date cutoff. If any participant has a non-null `scoreTotal`, show **Total score** and the participants' `scoreTotal` values. Zero is a valid recorded total. - [x] If all participants have null/missing totals, preserve **Highest reported score** and `bestScore` for historical/legacy matches. Show exactly one score column in either case, in the current score column's position. - [x] Handle mixed coverage explicitly: in a match using Total score, show a dash for a participant without a total. Never substitute a points balance beneath the Total score heading. Explain that the dash means no total was recorded. - [x] Make the column selection react to match navigation/data updates so moving between historical and newer matches updates the label, values and explanation. - [x] Update Player results/About these results copy and the match-results guide. Describe total score as the highest recorded cumulative points earned in this match; players absent from the final report retain their latest recorded total. Keep the existing balance explanation only for the historical fallback. - [x] Preserve stored balances and totals, existing historical records, and leaderboard/profile ranking semantics. Do not backfill totals from balances, add repeated telemetry snapshots together, or require a schema migration for this display change. - [x] Add UI regression coverage for totals differing from balances, all-legacy matches, zero totals, mixed/null totals, participants without stats, and navigation between score presentations. Assert that newer matches show the total and no Highest reported score heading, while historical matches retain their original label and balance. - [x] Run the relevant UI tests, the existing score ingestion/migration/match-detail tests, `npm run check`, and the full application suite. **Validation already performed** The existing `player-score-total-migration`, `keyed-telemetry` and `match-detail` test files pass: **23 tests**. This validates storage and historical preservation; it does not validate the proposed UI behavior. A search through all 135 existing open and closed issues found no ticket covering this score-display change.
goddard added the
priority
medium
type/bugtype/ux
labels 2026-10-12 16:01:54 -04:00
josh was assigned by goddard 2026-10-12 16:01:54 -04:00
Author
Collaborator

Reviewed ffda04f276 against current main 2f52dcb1ef. All eight fix items are done, so I'm closing this.

Checklist

  • Choose totals from recorded data. src/routes/matches/[id]/+page.svelte:27 selects totals if any participant has a non-null value. Zero qualifies, and no date cutoff is involved.
  • Preserve the legacy column. The derived column list at :28-39 selects exactly one score field in the original position. All-null/missing totals retain Highest reported score and bestScore.
  • Handle mixed coverage. Cells at :172-175 render null or missing totals as a dash without substituting balances. The explanation at :191-195 states what that dash means.
  • React to navigation and data updates. Match, total availability, and columns are derived from page data. The mounted-page regression test navigates legacy → totals → legacy and then refreshes the same match with a zero total, checking labels, cells, and explanations.
  • Update explanations and documentation. Player results and About these results use the selected score meaning (:126-133,190-202). docs/guides/leaderboard.md:81-89 covers cumulative totals, departed players, mixed coverage, and the historical balance fallback.
  • Preserve storage and ranking semantics. The fix changes presentation, documentation, and test infrastructure; it adds no migration or ingestion/ranking changes. match-history.ts:127-128 still returns both values, and leaderboard/profile queries continue using best_score.
  • Add UI regressions. All eight tests in tests/client/ui-match-results.test.ts pass, covering different balances/totals, legacy and missing values, zero, mixed coverage, departed players, participants without stats, empty matches, navigation, and refreshed data. They assert exactly one score column and the matching explanation.
  • Run required validation. VITEST_MAX_WORKERS=2 npm test passed all 3,055 tests in 227 files, including client UI, score ingestion, score/report migrations, match detail, and migration-memory coverage. npm run check passed with zero errors and zero warnings.

Review notes

  • Reviewed and tested current main at 2f52dcb; no application source changes were made during review.
  • The client test project is included in the default full-suite command, so these rendered-component regressions run with the application tests.
Reviewed ffda04f276306d6de07c60c9ad6722a8a9ef9051 against current main 2f52dcb1ef444b2c0a0e0684afea5a9ed269ed1b. All eight fix items are done, so I'm closing this. **Checklist** - [x] **Choose totals from recorded data.** `src/routes/matches/[id]/+page.svelte:27` selects totals if any participant has a non-null value. Zero qualifies, and no date cutoff is involved. - [x] **Preserve the legacy column.** The derived column list at `:28-39` selects exactly one score field in the original position. All-null/missing totals retain Highest reported score and `bestScore`. - [x] **Handle mixed coverage.** Cells at `:172-175` render null or missing totals as a dash without substituting balances. The explanation at `:191-195` states what that dash means. - [x] **React to navigation and data updates.** Match, total availability, and columns are derived from page data. The mounted-page regression test navigates legacy → totals → legacy and then refreshes the same match with a zero total, checking labels, cells, and explanations. - [x] **Update explanations and documentation.** Player results and About these results use the selected score meaning (`:126-133,190-202`). `docs/guides/leaderboard.md:81-89` covers cumulative totals, departed players, mixed coverage, and the historical balance fallback. - [x] **Preserve storage and ranking semantics.** The fix changes presentation, documentation, and test infrastructure; it adds no migration or ingestion/ranking changes. `match-history.ts:127-128` still returns both values, and leaderboard/profile queries continue using `best_score`. - [x] **Add UI regressions.** All eight tests in `tests/client/ui-match-results.test.ts` pass, covering different balances/totals, legacy and missing values, zero, mixed coverage, departed players, participants without stats, empty matches, navigation, and refreshed data. They assert exactly one score column and the matching explanation. - [x] **Run required validation.** `VITEST_MAX_WORKERS=2 npm test` passed all 3,055 tests in 227 files, including client UI, score ingestion, score/report migrations, match detail, and migration-memory coverage. `npm run check` passed with zero errors and zero warnings. **Review notes** - Reviewed and tested current main at `2f52dcb`; no application source changes were made during review. - The client test project is included in the default full-suite command, so these rendered-component regressions run with the application tests.
Sign in to join this conversation.