From b4ed3d7a976c2df06dfb4b1d4ff71d010dcf5bd5 Mon Sep 17 00:00:00 2001 From: devRaGonSa Date: Thu, 11 Jun 2026 13:27:03 +0200 Subject: [PATCH] Document teamkills ranking zero audit --- ...51-investigate-teamkills-ranking-zeroes.md | 128 ++++++++++++++++++ docs/TEAMKILLS_RANKING_AUDIT.md | 94 +++++++++++++ 2 files changed, 222 insertions(+) create mode 100644 ai/tasks/done/TASK-251-investigate-teamkills-ranking-zeroes.md create mode 100644 docs/TEAMKILLS_RANKING_AUDIT.md diff --git a/ai/tasks/done/TASK-251-investigate-teamkills-ranking-zeroes.md b/ai/tasks/done/TASK-251-investigate-teamkills-ranking-zeroes.md new file mode 100644 index 0000000..2bc9636 --- /dev/null +++ b/ai/tasks/done/TASK-251-investigate-teamkills-ranking-zeroes.md @@ -0,0 +1,128 @@ +# TASK-251 - Investigate teamkills ranking zeroes + +## Summary + +This task audits why annual `teamkills` ranking data is missing or zeroed in practice. + +No fix was implemented because the issue is upstream from the public ranking UI and needs a source decision first. + +## Files Read First + +- `backend/app/rcon_admin_log_parser.py` +- `backend/app/rcon_admin_log_storage.py` +- `backend/app/rcon_admin_log_materialization.py` +- `backend/app/rcon_historical_player_stats.py` +- `backend/app/rcon_annual_rankings.py` +- `backend/app/routes.py` +- `backend/tests/test_rcon_materialization_pipeline.py` + +## Findings + +### 1. Parser support exists + +The parser already extracts: + +- `killer_team` +- `victim_team` + +The materialization code already increments: + +- `teamkills` +- `deaths_by_teamkill` + +when `killer_team == victim_team`. + +Tests already cover this logic. + +### 2. Local match facts contain no positive teamkills + +Read-only local checks on `backend/data/hll_vietnam_dev.sqlite3` show: + +- `rcon_admin_log_events` has many `kill` rows +- `rcon_match_player_stats where teamkills > 0` returns zero rows +- `rcon_annual_ranking_snapshot_items where teamkills > 0` returns zero rows + +### 3. Local kill events contain no same-team kills + +Read-only checks on parsed event payloads show: + +- kill payloads include `killer_team` and `victim_team` +- but the local dataset contains zero `kill` rows where both teams are equal + +That means the materialized player facts never receive a positive `teamkills` increment in this dataset. + +### 4. Player profile snapshots do contain teamkill totals + +`rcon_player_profile_snapshots` contains positive values for: + +- `teamkills_done` +- `teamkills_received` + +So the broader AdminLog/profile ecosystem does know about teamkills somewhere, but those totals are: + +- lifetime-oriented +- not tied to one exact closed match window +- not directly usable for annual or per-match ranking backfills without a separate design + +### 5. Annual public ranking payload currently returns empty for teamkills + +Production and local checks both show: + +- `/api/ranking?timeframe=annual&metric=teamkills&...` returns `items = []` + +So the user-visible issue is not a frontend parse bug. It is a data-source gap for annual teamkill ranking inputs. + +## Cause Probable + +The annual ranking consumes `rcon_match_player_stats.teamkills`. + +That field is derived from same-team `kill` events in the materialized AdminLog match stream. + +In the audited dataset: + +- same-team `kill` events are not present +- therefore `rcon_match_player_stats.teamkills` stays at zero +- therefore annual `teamkills` ranking has no rows + +Meanwhile, profile snapshots expose lifetime teamkill totals, but they are not an annual closed-match ranking source. + +## Decision + +Do not patch the public ranking UI. + +Do not inject lifetime `teamkills_done` into annual ranking, because that would mix incompatible time models. + +Do not fabricate teamkills from unrelated signals. + +## Follow-up Direction + +The next correct task should choose one of these paths explicitly: + +1. Confirm whether the real RCON/AdminLog kill stream should include same-team kills and why they are absent. +2. If same-team kills are absent by product/API behavior, define a separate ranking source for teamkills. +3. If an alternate raw source exists, backfill `rcon_match_player_stats.teamkills` from that source with tests. + +## Validation + +Read-only checks executed: + +- local SQLite schema audit for: + - `rcon_admin_log_events` + - `rcon_match_player_stats` + - `rcon_annual_ranking_snapshots` + - `rcon_annual_ranking_snapshot_items` +- local count of `event_type='kill'` +- local count of `teamkills > 0` in materialized player stats +- local count of same-team parsed kill events +- local inspection of `parsed_payload_json` samples +- local inspection of `rcon_player_profile_snapshots.teamkills_done/teamkills_received` +- production endpoint checks for: + - annual all-servers teamkills ranking + - annual comunidad-hispana-01 teamkills ranking + - annual comunidad-hispana-02 teamkills ranking + +## Notes + +- No backend code changed. +- No frontend code changed for this audit. +- No migrations or data writes were performed. diff --git a/docs/TEAMKILLS_RANKING_AUDIT.md b/docs/TEAMKILLS_RANKING_AUDIT.md new file mode 100644 index 0000000..e321288 --- /dev/null +++ b/docs/TEAMKILLS_RANKING_AUDIT.md @@ -0,0 +1,94 @@ +# Teamkills Ranking Audit + +## Scope + +This document explains why annual public `teamkills` ranking currently returns no usable rows in the audited dataset. + +## What Was Checked + +Sources reviewed: + +- `rcon_admin_log_events` +- `rcon_match_player_stats` +- `rcon_annual_ranking_snapshots` +- `rcon_annual_ranking_snapshot_items` +- `rcon_player_profile_snapshots` + +Code reviewed: + +- `backend/app/rcon_admin_log_parser.py` +- `backend/app/rcon_admin_log_storage.py` +- `backend/app/rcon_admin_log_materialization.py` +- `backend/app/rcon_annual_rankings.py` +- `backend/app/routes.py` +- `backend/tests/test_rcon_materialization_pipeline.py` + +## What Works Already + +The parser and materialization pipeline already support teamkills when the source event is explicit enough. + +The key condition is: + +```text +killer_team == victim_team +``` + +When that is true, the materializer increments: + +- `teamkills` on the killer +- `deaths_by_teamkill` on the victim + +This behavior is covered by regression tests. + +## What The Local Dataset Shows + +Read-only checks on `backend/data/hll_vietnam_dev.sqlite3` show: + +- many `kill` events exist +- zero `rcon_match_player_stats` rows with `teamkills > 0` +- zero annual ranking snapshot items with `teamkills > 0` +- zero parsed `kill` events where `killer_team == victim_team` + +That means the annual ranking source facts never receive positive teamkill values. + +## What The Profile Snapshots Show + +`rcon_player_profile_snapshots` does contain positive: + +- `teamkills_done` +- `teamkills_received` + +So the broader data ecosystem knows teamkills somewhere, but those values are: + +- cumulative +- profile-oriented +- not scoped to exact closed match windows + +Using them directly for annual ranking would be semantically wrong. + +## Public Endpoint Check + +The current public endpoint returns empty annual teamkill ranking items for: + +- `all` +- `comunidad-hispana-01` +- `comunidad-hispana-02` + +So the problem is not a frontend-only rendering issue. + +## Conclusion + +The current annual teamkills ranking depends on: + +- `rcon_match_player_stats.teamkills` + +In the audited dataset, that field remains zero because the match-level kill stream does not contain same-team kill events that the materializer can classify as teamkills. + +## Safe Next Step + +Do not fake teamkills from unrelated counters. + +The next implementation task should first decide whether: + +1. the event stream is incomplete and needs parser/source correction, or +2. teamkills need a dedicated ranking source different from match-level kill events.