Files
comunidadhll/ai/tasks/done/TASK-267-investigate-current-match-live-data-staleness-and-clean-copy.md

214 lines
12 KiB
Markdown

---
id: TASK-267
title: Investigate current match live data staleness and clean copy
status: in-progress
type: investigation
team: Backend Senior
supporting_teams: ["Frontend Senior"]
roadmap_item: current-match
priority: high
---
# TASK-267 - Investigate current match live data staleness and clean copy
## Goal
Determine whether the current match killfeed and live player statistics are stale in the API, stale in frontend rendering, or mismatched against CRCON/RCON live data. Apply only the requested visible copy cleanup unless the diagnosis proves a safe code fix is required.
## Context
The current match page can show older live stats than CRCON/RCON live view. For example, CRCON may show a player with many more kills while the page still displays an older count. The killfeed may also appear stale.
Potential causes include stale `/api/current-match/players`, stale `/api/current-match/kills`, AdminLog ingestion issues, current-match window filtering, frontend signature/dedupe behavior, browser cache, or a source mismatch between live RCON scoreboard and derived event-based stats.
## Files to Read First
- `AGENTS.md`
- `ai/architecture-index.md`
- `ai/repo-context.md`
- `ai/orchestrator/frontend-senior.md`
- `ai/orchestrator/backend-senior.md`
- `frontend/assets/js/partida-actual.js`
- `backend/app/payloads.py`
- `backend/app/rcon_admin_log_storage.py`
- `backend/app/postgres_rcon_storage.py`
## Investigation Steps
1. Repeatedly sample killfeed endpoints for `comunidad-hispana-01` and `comunidad-hispana-02` with cache busters and no-cache headers.
2. Repeatedly sample current-match player endpoints for `comunidad-hispana-01` and `comunidad-hispana-02` with cache busters and no-cache headers.
3. Audit frontend polling, fetch cache behavior, render signatures, in-flight guards and dedupe logic.
4. Audit backend payload, storage and query paths for current-match kills and players.
5. Run read-only DB checks for recent AdminLog events where available.
6. Inspect local service/container availability and logs where available.
7. Classify the stale-display cause before applying any staleness fix.
8. Apply requested visible copy cleanup.
9. Validate syntax and document the result.
## Constraints
- Do not execute `ai-platform run`.
- Do not commit or push.
- Do not touch physical assets or `frontend/assets/img/`.
- Do not touch maps, weapons, clans or brands.
- Do not change RCON hosts/ports, `27001`, or server configuration.
- Do not reactivate Elo/MMR.
- Do not reintroduce Comunidad Hispana #03.
- Do not touch `ai/system-metrics.md`.
- Do not include `tmp/`, TASK-204 or unrelated pending changes.
- Do not use `git add .`.
- Do not introduce technical backend wording into visible UI copy.
## Required Copy Changes
- Remove visible text: `Estadisticas derivadas de los eventos recientes.`
- Remove visible text: `Mostrando las ultimas 12 bajas detectadas.`
- Replace visible text: `Leyendo eventos recientes para esta partida.` with `Leyendo los enfrentamientos recientes de la partida.`
## Validation
If frontend changes:
- `node --check frontend/assets/js/partida-actual.js`
If backend changes:
- `python -m compileall backend/app`
- `cd backend; python -m unittest tests.test_current_match_payload`
Functional checks:
- Repeated endpoint samples identify whether kills and players change over time.
- Public endpoints return 200 for kills and players on #01 and #02.
- UI copy no longer contains the removed text.
- No unrelated pending changes are included.
## Findings
### Repeated Production Endpoint Samples
All repeated samples used cache busters and no-cache request headers.
Killfeed samples:
- `GET /api/current-match/kills?server=comunidad-hispana-01&limit=18`
- 10 samples over roughly 100 seconds.
- `scope=open-admin-log-match-window`.
- `count=0` for all samples.
- No first/last event id because the endpoint returned no kill rows.
- `GET /api/current-match/kills?server=comunidad-hispana-02&limit=18`
- 10 samples over roughly 100 seconds.
- `scope=open-admin-log-match-window`.
- `count=18` for all samples.
- First event stayed `rcon-admin-log:comunidad-hispana-02:4246587`.
- Last event stayed `rcon-admin-log:comunidad-hispana-02:4246570`.
- Top rows stayed identical. Top event was `Jack -> AleixCommander` with `SMLE No.1 Mk III`.
Player-stat samples:
- `GET /api/current-match/players?server=comunidad-hispana-01`
- 10 samples over roughly 100 seconds.
- `count=1` for all samples.
- Only visible row stayed `Core.D | Axis | K=0 | D=0`.
- `GET /api/current-match/players?server=comunidad-hispana-02`
- 10 samples over roughly 100 seconds.
- `count=12` for all samples.
- Top row stayed `Jack | Allies | K=16 | D=6 | SMLE No.1 Mk III`.
- Full single follow-up sample confirmed Jack had `kills=16`, `last_seen_at=2026-06-17T15:34:14.312Z`, and source `connected,kill,message,team_switch`.
Public endpoint status validation:
- `current-match-kills-comunidad-hispana-01`: 200.
- `current-match-kills-comunidad-hispana-02`: 200.
- `current-match-players-comunidad-hispana-01`: 200.
- `current-match-players-comunidad-hispana-02`: 200.
### Frontend Analysis
- `CURRENT_MATCH_KILL_FEED_POLL_INTERVAL_MS` is `1500`; killfeed fetch is scheduled every 1.5 seconds.
- `CURRENT_MATCH_PLAYER_STATS_POLL_INTERVAL_MS` is `3000`; player stats fetch is scheduled every 3 seconds.
- `killFeedRefreshInFlight` and `playerStatsRefreshInFlight` are reset in `finally`, so normal request failures should not leave either flag stuck.
- Killfeed rendering consumes `data.items` directly, filters invalid events, dedupes by event id/semantic key, and renders the latest visible window after TASK-262 style dedupe.
- Killfeed visible signature includes event/window key, killer/victim names, teams, weapon and teamkill flag.
- Player stats rendering consumes `data.items`, dedupes rows, sorts by kills/deaths/name, and renders the table from the current endpoint payload.
- Player stats visible signature includes player name, team, kills, deaths, teamkills, deaths by teamkill, favorite weapon and last seen.
- If endpoint data changes in any of those fields, the frontend should re-render.
- `fetchJson` currently uses plain `fetch(url)` and does not set `cache: "no-store"`. The repeated cache-busted production samples still stayed stable, so cache was not proven as the cause in this run.
- No evidence was found that visibleSignature would block a real kills/stat change from the endpoint.
### Backend Analysis
- `/api/current-match/kills` is routed in `backend/app/routes.py` to `build_current_match_kill_feed_payload(...)`.
- `/api/current-match/players` is routed in `backend/app/routes.py` to `build_current_match_player_stats_payload(...)`.
- Both payload builders read from AdminLog storage through:
- `list_current_match_kill_feed(...)`
- `list_current_match_player_stats(...)`
- Neither endpoint queries live RCON directly during the public GET.
- Killfeed source table is `rcon_admin_log_events`.
- Player stat source is also `rcon_admin_log_events`; it derives kills/deaths/teamkills/favorite weapon by replaying current-window AdminLog events.
- Current match window is selected from the latest `match_start` / `match_end` boundary for the target server.
- If the latest boundary is `match_start`, the endpoints use `server_time >= open_start_time`.
- If no open match boundary exists, fallback mode uses recent AdminLog rows and filters by freshness.
- Target matching uses `(target_key = ? OR external_server_id = ?)`, so both target fields are considered for `comunidad-hispana-01` and `comunidad-hispana-02`.
### DB / Service Checks
- Local config reports:
- `use_postgres_rcon_storage=False`
- `database_url_set=False`
- `storage_path=D:\Proyectos\HLL Vietnam\backend\data\hll_vietnam_dev.sqlite3`
- `historical_data_source_kind=rcon`
- Local SQLite schema confirms `rcon_admin_log_events` stores the current-match event source.
- Local SQLite data is not production-current:
- Latest local `comunidad-hispana-01` events are from May 20, 2026.
- Latest local `comunidad-hispana-02` events are from May 20, 2026.
- Production endpoint rows sampled during this task are from June 17, 2026.
- Local DB max samples repeated after 60 seconds did not change, which only proves local ingestion was not running in this workspace.
- Docker is not available locally:
- `docker ps --format "{{.Names}}"` failed because Docker Desktop/Linux engine is not running.
- Container logs could not be inspected from this workspace.
- Production DB/container access was not available from this workspace, so production ingestion status and worker logs could not be directly verified.
### Classification
- During the sample window, both production endpoints were stable. This does not prove a bug by itself because there may have been no new kills/events during the 100-second window.
- A later real-world check found that the server had changed map and the new match had no kills yet, so `/api/current-match/kills` returning `count=0` in that state is expected and does not prove a bug.
- The concrete stale-display example described by the user was not reproduced in the sampled state: production `players` for #02 reported Jack with 16 kills, which is newer than the example of 15 in CRCON and much newer than the stale UI example of 5.
- The active data source for both current-match feed and player stats is stored AdminLog, not direct live scoreboard state.
- If CRCON live changes while these endpoints do not, the cause is backend/source-side: AdminLog ingestion, AdminLog query/window filtering, or mismatch between live scoreboard data and derived event data.
- No evidence was found that frontend visibleSignature/dedupe would ignore changed endpoint values.
- No backend staleness fix was applied because production DB/log access was unavailable and the current endpoint samples did not prove a specific query/window bug.
## Exact Cause
For the stale display reported by the user, the exact confirmed cause is not a frontend render blocker. The current page displays exactly what `/api/current-match/kills` and `/api/current-match/players` return, and the render signatures include the fields needed to refresh changed rows.
The most precise verified cause is source-model mismatch and/or AdminLog read-model freshness: current-match player stats are derived from stored AdminLog events, while CRCON live can show direct live scoreboard state. If stored AdminLog ingestion or the current-match AdminLog window lags behind CRCON live, both killfeed and player stats will appear stale even though the frontend polling continues.
Root-cause confirmation still requires a future validation window where CRCON/RCON live shows new kills after a map change and the current-match endpoints can be sampled at the same time.
## Changes Applied
- `frontend/partida-actual.html`
- Replaced visible copy:
- From `Leyendo eventos recientes para esta partida.`
- To `Leyendo los enfrentamientos recientes de la partida.`
- `frontend/assets/js/partida-actual.js`
- Removed the visible feed status/count line when kill rows exist.
- Removed the visible player-stats line `Estadisticas derivadas de los eventos recientes.` when stat rows exist.
- Empty/error states remain visible.
No backend, scheduler, RCON, server configuration, physical asset or image files were changed.
## Validation Results
- Repeated production endpoint sampling for kills #01/#02 and players #01/#02.
- Public endpoint status checks for kills #01/#02 and players #01/#02 returned 200.
- Frontend polling/render/signature audit completed.
- Backend route/payload/storage audit completed.
- Local SQLite read-only schema/data checks completed.
- Docker/service log inspection attempted; Docker engine was unavailable locally.
- `node --check frontend/assets/js/partida-actual.js` passed.
- Verified the requested removed/replaced visible text with `rg`.
- Reviewed `git diff -- frontend/assets/js/partida-actual.js frontend/partida-actual.html`.