Metrics measures demand only as player-hours, although every match is recorded with its map and players. Operators can't see how many groups played a map or how big they were.
fleet/metrics.ts:76-81,297-303readMetrics reads only metric_samples (and registered names at fleet/metrics.ts:96-102). It never reads leaderboard_matches or leaderboard_match_players.
metrics.ts:101-120MetricsData has no match fields, so neither the summary nor the maps table can show them.
On live, 265 matches started in the last 7 days across 19 maps, and 116 of them (44%) were solo.
Player-hours can't tell one four-player group playing for two hours from four solo players. Matches started per map is the demand that each empty spare lobby exists to serve.
Screenshots
Same preview data as #137. Match counts come from the fixture's match records.
Before, summary cards:
After (mockup), matches started:
Before, maps table:
After (mockup), Matches column:
Fix
Add a matches-started card with average group size and solo share
Add a Matches column, with average group size, to the maps table
Apply the report's range, game and map filters to the match counts
Document the new measures in metrics.md
Metrics measures demand only as player-hours, although every match is recorded with its map and players. Operators can't see how many groups played a map or how big they were.
- `fleet/metrics.ts:76-81,297-303` `readMetrics` reads only `metric_samples` (and registered names at `fleet/metrics.ts:96-102`). It never reads `leaderboard_matches` or `leaderboard_match_players`.
- `metrics.ts:101-120` `MetricsData` has no match fields, so neither the summary nor the maps table can show them.
- On live, 265 matches started in the last 7 days across 19 maps, and 116 of them (44%) were solo.
Player-hours can't tell one four-player group playing for two hours from four solo players. Matches started per map is the demand that each empty spare lobby exists to serve.
**Screenshots**
Same preview data as #137. Match counts come from the fixture's match records.
Before, summary cards:

After (mockup), matches started:

Before, maps table:

After (mockup), Matches column:

**Fix**
- [x] Add a matches-started card with average group size and solo share
- [x] Add a Matches column, with average group size, to the maps table
- [x] Apply the report's range, game and map filters to the match counts
- [x] Document the new measures in `metrics.md`
Reviewed 616b1937ed. All four fix items are done, so I'm closing this.
Checklist
Matches-started card.MetricsSummary.svelte:68 adds "Matches started" with the average group size, the solo count and the solo share. When some matches have no recorded players, the card says so ("Players recorded for X of Y matches"). On the autoscaled preview fixture it reads 268 matches, average group 2.4, 48 solo (17.9%). An independent SQL count over the same window gives the same totals.
Matches column.MetricsTables.svelte:26 adds a sortable Matches column with "avg group" under each count, and the row details add matches with recorded players and solo share. On the preview, Kino Der Toten shows 81 matches with an average group of 2.1.
Range, game and map filters.fleet/metrics.ts:270-279 limits matches to started_at within the report window and to the selected game and exact map_name. Participants are counted per match before maps are summed, so roster size never inflates the start count. Covered by "filters matches by game and exact recorded map label…" and "weights group sizes by matches and counts unique participants per match…".
Docs.metrics.md:33 adds "Matches and groups", covering what counts as a start, how distinct players are counted, how matches with no recorded players are handled, and match retention. The API section lists the new fields. I checked the retention claims: matchKeepDays defaults to 0, meaning kept indefinitely, under Matches & players (fleet-settings.ts:50,219-222), and expiry only applies to completed or interrupted matches (retention.ts:146-150).
Review notes
Good extra work: maps that only have match history now appear in the filter options (fleet/metrics.ts:210) and in the table. Migration 14 refreshes cached reports when matches or participants change but ignores score and heartbeat updates (revisions.ts:4). The empty table state now tells "no history in this window" apart from "no search results".
The map join relies on match records and occupancy samples using the same label. Both come from selectedZombieMap(...).friendly (leaderboard-ingest.ts:35-45 and worker.ts). On live, all 19 map labels in the 577 saved matches exactly match the 19 occupancy labels, so no map will split into two rows.
The four-card summary fits at 1440 px and stacks cleanly at 390 px on the preview.
npm run check is clean. The 8 metrics test files pass locally (105 tests), and so do the 23 leaderboard, match, retention and telemetry test files (283 tests), which exercise the new triggers.
Minor and non-blocking: About this report now says "Occupancy history defaults to 31 days" (+page.svelte:404). That's no longer wrong, but it still doesn't show the configured value, so #141 stays open.
Reviewed 616b1937edaab482cfadc973f6f484d5d0b60e3a. All four fix items are done, so I'm closing this.
**Checklist**
- [x] **Matches-started card.** `MetricsSummary.svelte:68` adds "Matches started" with the average group size, the solo count and the solo share. When some matches have no recorded players, the card says so ("Players recorded for X of Y matches"). On the autoscaled preview fixture it reads 268 matches, average group 2.4, 48 solo (17.9%). An independent SQL count over the same window gives the same totals.
- [x] **Matches column.** `MetricsTables.svelte:26` adds a sortable Matches column with "avg group" under each count, and the row details add matches with recorded players and solo share. On the preview, Kino Der Toten shows 81 matches with an average group of 2.1.
- [x] **Range, game and map filters.** `fleet/metrics.ts:270-279` limits matches to `started_at` within the report window and to the selected `game` and exact `map_name`. Participants are counted per match before maps are summed, so roster size never inflates the start count. Covered by "filters matches by game and exact recorded map label…" and "weights group sizes by matches and counts unique participants per match…".
- [x] **Docs.** `metrics.md:33` adds "Matches and groups", covering what counts as a start, how distinct players are counted, how matches with no recorded players are handled, and match retention. The API section lists the new fields. I checked the retention claims: `matchKeepDays` defaults to 0, meaning kept indefinitely, under Matches & players (`fleet-settings.ts:50,219-222`), and expiry only applies to completed or interrupted matches (`retention.ts:146-150`).
**Review notes**
- Good extra work: maps that only have match history now appear in the filter options (`fleet/metrics.ts:210`) and in the table. Migration 14 refreshes cached reports when matches or participants change but ignores score and heartbeat updates (`revisions.ts:4`). The empty table state now tells "no history in this window" apart from "no search results".
- The map join relies on match records and occupancy samples using the same label. Both come from `selectedZombieMap(...).friendly` (`leaderboard-ingest.ts:35-45` and `worker.ts`). On live, all 19 map labels in the 577 saved matches exactly match the 19 occupancy labels, so no map will split into two rows.
- The four-card summary fits at 1440 px and stacks cleanly at 390 px on the preview.
- `npm run check` is clean. The 8 metrics test files pass locally (105 tests), and so do the 23 leaderboard, match, retention and telemetry test files (283 tests), which exercise the new triggers.
- Minor and non-blocking: About this report now says "Occupancy history defaults to 31 days" (`+page.svelte:404`). That's no longer wrong, but it still doesn't show the configured value, so #141 stays open.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Metrics measures demand only as player-hours, although every match is recorded with its map and players. Operators can't see how many groups played a map or how big they were.
fleet/metrics.ts:76-81,297-303readMetricsreads onlymetric_samples(and registered names atfleet/metrics.ts:96-102). It never readsleaderboard_matchesorleaderboard_match_players.metrics.ts:101-120MetricsDatahas no match fields, so neither the summary nor the maps table can show them.Player-hours can't tell one four-player group playing for two hours from four solo players. Matches started per map is the demand that each empty spare lobby exists to serve.
Screenshots
Same preview data as #137. Match counts come from the fixture's match records.
Before, summary cards:

After (mockup), matches started:

Before, maps table:

After (mockup), Matches column:

Fix
metrics.mdReviewed
616b1937ed. All four fix items are done, so I'm closing this.Checklist
MetricsSummary.svelte:68adds "Matches started" with the average group size, the solo count and the solo share. When some matches have no recorded players, the card says so ("Players recorded for X of Y matches"). On the autoscaled preview fixture it reads 268 matches, average group 2.4, 48 solo (17.9%). An independent SQL count over the same window gives the same totals.MetricsTables.svelte:26adds a sortable Matches column with "avg group" under each count, and the row details add matches with recorded players and solo share. On the preview, Kino Der Toten shows 81 matches with an average group of 2.1.fleet/metrics.ts:270-279limits matches tostarted_atwithin the report window and to the selectedgameand exactmap_name. Participants are counted per match before maps are summed, so roster size never inflates the start count. Covered by "filters matches by game and exact recorded map label…" and "weights group sizes by matches and counts unique participants per match…".metrics.md:33adds "Matches and groups", covering what counts as a start, how distinct players are counted, how matches with no recorded players are handled, and match retention. The API section lists the new fields. I checked the retention claims:matchKeepDaysdefaults to 0, meaning kept indefinitely, under Matches & players (fleet-settings.ts:50,219-222), and expiry only applies to completed or interrupted matches (retention.ts:146-150).Review notes
fleet/metrics.ts:210) and in the table. Migration 14 refreshes cached reports when matches or participants change but ignores score and heartbeat updates (revisions.ts:4). The empty table state now tells "no history in this window" apart from "no search results".selectedZombieMap(...).friendly(leaderboard-ingest.ts:35-45andworker.ts). On live, all 19 map labels in the 577 saved matches exactly match the 19 occupancy labels, so no map will split into two rows.npm run checkis clean. The 8 metrics test files pass locally (105 tests), and so do the 23 leaderboard, match, retention and telemetry test files (283 tests), which exercise the new triggers.+page.svelte:404). That's no longer wrong, but it still doesn't show the configured value, so #141 stays open.