Repository navigation
Conversation
…r, not stale parser_events The deaths recalc read the per-player death counter from the `parser_events` table, which the bulk full-run never re-persists from `raw_snapshot` (only the single-replay audit path does). For ~90% of current sg replays parser_events has zero `player_counter` rows while `raw_snapshot` carries the compact counters, so the counter death path found nothing. Player resolution and games are built from `raw_snapshot`, so the game was counted while a death with no independent victim kill-row (null-killer / suicide / environmental / bleed-out d>0) was dropped — the platform-wide ~11024 death-game undercount (sg all-time 282720 vs the parser's own 293744; exemplar Zero 621 vs 653). Confirmed by instrumenting the real repository/service code on real staging rows (.planning/quick/260615-f13b-deaths-undercount/instrument.ts): across 16 of Zero's d>0 replays he resolves in all 16 (refuting the nickname-active / F12 hypothesis — he matches via the time-ungated display_name branch), yet pre-fix only 6 deaths are credited and 10 drop because parser_events lacks a counter event and no kill-row names his eid as victim. Fix: read the death counter from the authoritative `raw_snapshot` (the same source already used for resolution and games). `resolvedPlayers` attaches `counterDeaths = artifactCounterDeaths(player.d, player.td)`; the service folds it into the per-replay scratch alongside the event paths. The one-life cap (foldCappedDeaths, <=1 death and <=1 by_teamkills per replay) is unchanged, so the addition is collapse-safe (no inflation) and by_teamkills <= total holds. Games, parser/OCAP contract, schema, and API are untouched. Tests: 4 unit cases in aggregates.test.ts (3 red without the fix) and 1 integration case in postgres.test.ts (raw_snapshot d:1, no counter event, no victim row -> death credited through the real DB). service.ts is 100% unit coverage; the repository change is covered by the integration test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The deaths recalc reads the per-player death counter signal from the
parser_eventstable, but the bulk full-run (full-run-recalculation.ts) never re-persistsparser_eventsfromraw_snapshot— only the single-replay audit path does. For ~90% of current sg replays (1850 / 2059 with artifactdplayers)parser_eventshas 0player_counterrows whileraw_snapshotcarries the compact counters, so the counter death path finds nothing.Player resolution and games are built from
raw_snapshot, so the game is counted, while a death with no independent victim kill-row (null-killer / suicide / environmental / bleed-outd>0) is dropped. This is the platform-wide ~11024 death-game undercount (sg all-time 282720 vs the parser's own 293744; exemplar Zero 621 vs 653).Root cause — confirmed by instrumentation on real staging data
.planning/quick/260615-f13b-deaths-undercount/instrument.tsruns the real repository/service code (buildPlayerIdentityIndex,bestPlayerIdentityIndexed,playerIdentityMatchPriority,calculatePlayerAndSquadAggregates) on real exported rows. Across 16 of Zero's d>0 replays he resolves in all 16 — refuting the F12-timestamp /isNicknameActivehypothesis (he matches via the time-ungateddisplay_namepriority-1 branch even for pre-2026-05-10 replays) — yet pre-fix only 6 deaths are credited (3 counter, 3 victim) and 10 are dropped becauseparser_eventslacks a counter event and no kill-row names his eid as victim. Post-fix: 16/16, 0 dropped.Concrete dropped replay:
9eb1a2ad-…(2021-05-14), prid3662f461-…; raw_snapshot Zero ={d:1, nkd:1, eid:203}; parser_events = 140 kill / 18 destroyed_vehicle / 6 teamkill / 1 diagnostic, 0 player_counter; no kill-row withvictim_entity_id=203. (Diagnostics infindings.sql.)Fix
Read the death counter from the authoritative
raw_snapshot— the same source already used for player resolution and games — instead of the stale events table.service.ts:AggregatePlayerEvidencegains optionalcounterDeaths?: DeathStats; newtallyArtifactCounterDeathsfolds it into the per-replay scratch (player + squad). ExportedartifactCounterDeaths(d, td)builds theDeathStatswith the same semantics as the event-basedcounterDeaths(total = max(d, td),by_teamkills = td);counterDeathsnow delegates to it.repository.ts:resolvedPlayersattachescounterDeaths = artifactCounterDeaths(player.d, player.td).Invariants preserved
foldCappedDeaths): ≤1totaland ≤1by_teamkillsper replay — additive with the event paths, so overlap collapses to 1 (no inflation).by_teamkills ≤ total(themaxcap).Tests
aggregates.test.ts: 4 new unit cases (3 fail without the fix).postgres.test.ts: 1 integration case — raw_snapshotd:1, no counter event, no victim row → death credited through the real DB.Gates
pnpm lint,pnpm typecheck,pnpm format,pnpm test(661 unit, +4) — green.service.ts100% unit coverage. The fullpnpm test:coveragegate needs Docker/testcontainers (unavailable in the authoring env); therepository.tschange is covered by the new integration test, which runs under Docker in CI.🤖 Generated with Claude Code