Merge branch 'codex/task-120-adminlog-worker-ingestion'
This commit is contained in:
@@ -0,0 +1,99 @@
|
|||||||
|
---
|
||||||
|
id: TASK-120
|
||||||
|
title: Integrate AdminLog ingestion into RCON historical worker
|
||||||
|
status: done
|
||||||
|
type: backend
|
||||||
|
team: Backend Senior
|
||||||
|
supporting_teams:
|
||||||
|
- Arquitecto Python
|
||||||
|
roadmap_item: rcon-full-data
|
||||||
|
priority: high
|
||||||
|
---
|
||||||
|
|
||||||
|
# TASK-120 - Integrate AdminLog ingestion into RCON historical worker
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Automatically ingest RCON AdminLog events during the periodic RCON historical worker capture flow.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
HLL Vietnam is moving to an RCON-first historical/live data platform. Manual AdminLog ingestion already exists and has been validated with duplicate-safe canonical message storage. The periodic worker should keep collecting this data automatically after or alongside existing session/gamestate capture.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
- Do not reactivate Elo/MMR.
|
||||||
|
- Do not reintroduce Comunidad Hispana #03.
|
||||||
|
- Do not store secrets, runtime DB files or `backend/runtime`.
|
||||||
|
- Do not modify Docker behavior.
|
||||||
|
- Do not change frontend files.
|
||||||
|
- Preserve existing writer lock behavior.
|
||||||
|
- If AdminLog ingestion fails for one target, normal session capture for other targets must continue.
|
||||||
|
- Public scoreboard remains optional enrichment/fallback only.
|
||||||
|
|
||||||
|
## Allowed Changes
|
||||||
|
|
||||||
|
- `backend/app/rcon_historical_worker.py`
|
||||||
|
- tests only if needed for worker payload behavior
|
||||||
|
- this task file when moving it through the workflow
|
||||||
|
|
||||||
|
## Implementation Requirements
|
||||||
|
|
||||||
|
- Work from a dedicated branch for this task.
|
||||||
|
- Read first:
|
||||||
|
- `AGENTS.md`
|
||||||
|
- `ai/architecture-index.md`
|
||||||
|
- `ai/repo-context.md`
|
||||||
|
- `ai/orchestrator/backend-senior.md`
|
||||||
|
- `backend/app/rcon_historical_worker.py`
|
||||||
|
- `backend/app/rcon_admin_log_ingestion.py`
|
||||||
|
- `backend/app/rcon_admin_log_storage.py`
|
||||||
|
- Reuse existing AdminLog ingestion and storage modules.
|
||||||
|
- Add a configurable AdminLog lookback window, defaulting to 60 minutes.
|
||||||
|
- Prefer project-consistent naming such as `HLL_BACKEND_RCON_ADMIN_LOG_LOOKBACK_MINUTES=60`.
|
||||||
|
- Do not hardcode `1440` for the periodic worker.
|
||||||
|
- Keep the manual backfill command able to support `--minutes 1440`.
|
||||||
|
- Extend worker results with:
|
||||||
|
- `admin_log_events_seen`
|
||||||
|
- `admin_log_events_inserted`
|
||||||
|
- `admin_log_duplicate_events`
|
||||||
|
- `admin_log_failed_targets`
|
||||||
|
- Keep changes deterministic and idempotent.
|
||||||
|
|
||||||
|
## Validation Commands
|
||||||
|
|
||||||
|
- `python -m compileall backend/app`
|
||||||
|
- `docker compose up -d --build backend rcon-historical-worker`
|
||||||
|
- `docker compose exec backend python -m app.rcon_historical_worker capture`
|
||||||
|
- Run AdminLog ingestion twice and confirm dedupe still reports duplicates on the second run.
|
||||||
|
|
||||||
|
## Manual Verification Steps
|
||||||
|
|
||||||
|
- Confirm worker capture returns normal session/gamestate data plus AdminLog metrics.
|
||||||
|
- Confirm an AdminLog failure for one target is reported without breaking all target capture.
|
||||||
|
- Confirm `/health` still works.
|
||||||
|
- Confirm `git diff --name-only` matches the allowed scope.
|
||||||
|
|
||||||
|
## Git Requirements
|
||||||
|
|
||||||
|
- Create a dedicated branch for this task, for example `codex/task-120-adminlog-worker-ingestion`.
|
||||||
|
- Run relevant validation before committing.
|
||||||
|
- Stage only intended files.
|
||||||
|
- Commit the completed implementation.
|
||||||
|
- Push the branch to origin.
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
- Integrated AdminLog ingestion into the RCON historical capture worker using the existing ingestion/storage path.
|
||||||
|
- Added `HLL_BACKEND_RCON_ADMIN_LOG_LOOKBACK_MINUTES`, defaulting to 60 minutes.
|
||||||
|
- Added worker result totals for AdminLog events seen, inserted, duplicated and failed targets.
|
||||||
|
- Verified a temporary bad RCON target reports one session failure and one AdminLog failure while a valid target still captures successfully.
|
||||||
|
|
||||||
|
## Validation Result
|
||||||
|
|
||||||
|
- `python -m compileall backend/app` passed.
|
||||||
|
- `docker compose up -d --build backend rcon-historical-worker` passed.
|
||||||
|
- `docker compose exec backend python -m app.rcon_historical_worker capture` passed and returned AdminLog metrics.
|
||||||
|
- `docker compose exec backend python -m app.rcon_admin_log_ingestion --minutes 1440` run twice; second run reported 143 duplicate events and 0 inserted events.
|
||||||
|
- `/health` returned `status: ok`.
|
||||||
|
- `git diff --name-only` matched the expected task scope.
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
---
|
||||||
|
id: TASK-121
|
||||||
|
title: Add RCON AdminLog storage read tests
|
||||||
|
status: pending
|
||||||
|
type: backend
|
||||||
|
team: Backend Senior
|
||||||
|
supporting_teams:
|
||||||
|
- Arquitecto Python
|
||||||
|
roadmap_item: rcon-full-data
|
||||||
|
priority: high
|
||||||
|
---
|
||||||
|
|
||||||
|
# TASK-121 - Add RCON AdminLog storage read tests
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Add deterministic regression tests for AdminLog persistence, reads and deduplication.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
AdminLog storage and manual ingestion exist, and real validation confirmed canonical message deduplication. The storage layer now needs offline tests so future RCON pipeline work can depend on it safely.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
- Do not require real RCON.
|
||||||
|
- Do not use the real runtime database.
|
||||||
|
- Do not reactivate Elo/MMR.
|
||||||
|
- Do not reintroduce Comunidad Hispana #03.
|
||||||
|
- Do not commit runtime DB files or `backend/runtime`.
|
||||||
|
- Keep tests deterministic and offline.
|
||||||
|
|
||||||
|
## Allowed Changes
|
||||||
|
|
||||||
|
- a new backend test file under `backend/tests/`
|
||||||
|
- possibly small test-only helpers if already consistent with the test suite
|
||||||
|
- this task file when moving it through the workflow
|
||||||
|
|
||||||
|
## Implementation Requirements
|
||||||
|
|
||||||
|
- Work from a dedicated branch for this task.
|
||||||
|
- Read first:
|
||||||
|
- `AGENTS.md`
|
||||||
|
- `ai/architecture-index.md`
|
||||||
|
- `ai/repo-context.md`
|
||||||
|
- `ai/orchestrator/backend-senior.md`
|
||||||
|
- `backend/app/rcon_admin_log_storage.py`
|
||||||
|
- `backend/app/rcon_admin_log_parser.py`
|
||||||
|
- `backend/tests/test_rcon_admin_log_parser.py`
|
||||||
|
- Use a temporary SQLite database path.
|
||||||
|
- Test table initialization.
|
||||||
|
- Test that first insert inserts events.
|
||||||
|
- Test that a second insert of the same events returns duplicates.
|
||||||
|
- Test that canonical message dedupes repeated AdminLog reads with changing relative prefixes.
|
||||||
|
- Test `event_counts` grouping by target and event type.
|
||||||
|
- If pytest is unavailable locally, document the docker-based pytest command in the task outcome.
|
||||||
|
|
||||||
|
## Validation Commands
|
||||||
|
|
||||||
|
- `python -m compileall backend/app`
|
||||||
|
- `python -m pytest backend/tests/test_rcon_admin_log_parser.py backend/tests/<new_admin_log_storage_test>.py`
|
||||||
|
|
||||||
|
## Manual Verification Steps
|
||||||
|
|
||||||
|
- Confirm no real RCON credentials or runtime DB paths are used.
|
||||||
|
- Confirm tests create and clean temporary data only.
|
||||||
|
- Confirm `git diff --name-only` matches the allowed scope.
|
||||||
|
|
||||||
|
## Git Requirements
|
||||||
|
|
||||||
|
- Create a dedicated branch for this task, for example `codex/task-121-adminlog-storage-tests`.
|
||||||
|
- Run relevant validation before committing.
|
||||||
|
- Stage only intended files.
|
||||||
|
- Commit the completed implementation.
|
||||||
|
- Push the branch to origin.
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
---
|
||||||
|
id: TASK-122
|
||||||
|
title: Materialize RCON matches from AdminLog events
|
||||||
|
status: pending
|
||||||
|
type: backend
|
||||||
|
team: Backend Senior
|
||||||
|
supporting_teams:
|
||||||
|
- Arquitecto de Base de Datos
|
||||||
|
- Arquitecto Python
|
||||||
|
roadmap_item: rcon-full-data
|
||||||
|
priority: high
|
||||||
|
---
|
||||||
|
|
||||||
|
# TASK-122 - Materialize RCON matches from AdminLog events
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Create a materialization layer that converts RCON AdminLog and useful session/gamestate data into durable RCON match records.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
Recent matches need consistent RCON-first scores. AdminLog contains `MATCH START` and `MATCH ENDED` records, while session/gamestate capture can provide partial in-progress scores. A materialized read model should make this data reliable and idempotent before UI changes consume it.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
- Do not delete or replace existing competitive-window logic.
|
||||||
|
- No UI changes.
|
||||||
|
- Do not reactivate Elo/MMR.
|
||||||
|
- Do not reintroduce Comunidad Hispana #03.
|
||||||
|
- Do not store secrets, runtime DB files or `backend/runtime`.
|
||||||
|
- Preserve RCON as the source of truth; public scoreboard is optional enrichment/fallback only.
|
||||||
|
|
||||||
|
## Allowed Changes
|
||||||
|
|
||||||
|
- backend storage/model modules for materialized RCON matches
|
||||||
|
- backend tests for materialization
|
||||||
|
- small wiring needed to initialize/read the new table
|
||||||
|
- this task file when moving it through the workflow
|
||||||
|
|
||||||
|
## Implementation Requirements
|
||||||
|
|
||||||
|
- Work from a dedicated branch for this task.
|
||||||
|
- Read first:
|
||||||
|
- `AGENTS.md`
|
||||||
|
- `ai/architecture-index.md`
|
||||||
|
- `ai/repo-context.md`
|
||||||
|
- `ai/orchestrator/backend-senior.md`
|
||||||
|
- `ai/orchestrator/database-architect.md`
|
||||||
|
- `backend/app/rcon_admin_log_storage.py`
|
||||||
|
- `backend/app/rcon_admin_log_parser.py`
|
||||||
|
- `backend/app/rcon_historical_read_model.py`
|
||||||
|
- existing historical storage modules
|
||||||
|
- Derive RCON matches from:
|
||||||
|
- `match_start`
|
||||||
|
- `match_end`
|
||||||
|
- session/gamestate samples where useful
|
||||||
|
- Store materialized records in SQLite, using a table such as `rcon_materialized_matches`.
|
||||||
|
- Include fields for `id`, `target_key`, `external_server_id`, `match_key` or `session_key`, `map_name`, `map_pretty_name`, `game_mode`, server/event start/end times, scores, winner, `confidence_mode`, `source_basis`, `created_at` and `updated_at`.
|
||||||
|
- Make materialization idempotent.
|
||||||
|
- Treat `MATCH ENDED` as authoritative result when present.
|
||||||
|
- Treat session/gamestate scores as partial or in-progress when no `MATCH ENDED` exists.
|
||||||
|
- Parse and test results including 5-0, 2-2 and 0-5.
|
||||||
|
|
||||||
|
## Validation Commands
|
||||||
|
|
||||||
|
- `python -m compileall backend/app`
|
||||||
|
- `python -m pytest backend/tests/<new_or_relevant_materialized_match_tests>.py`
|
||||||
|
|
||||||
|
## Manual Verification Steps
|
||||||
|
|
||||||
|
- Confirm repeated materialization does not duplicate matches.
|
||||||
|
- Confirm a sample `MATCH ENDED \`ST MARIE DU MONT Warfare\` ALLIED (5 - 0) AXIS` produces the expected score and winner.
|
||||||
|
- Confirm existing competitive-window reads still work.
|
||||||
|
- Confirm `/health` still works.
|
||||||
|
- Confirm `git diff --name-only` matches the allowed scope.
|
||||||
|
|
||||||
|
## Git Requirements
|
||||||
|
|
||||||
|
- Create a dedicated branch for this task, for example `codex/task-122-rcon-match-materialization`.
|
||||||
|
- Run relevant validation before committing.
|
||||||
|
- Stage only intended files.
|
||||||
|
- Commit the completed implementation.
|
||||||
|
- Push the branch to origin.
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
---
|
||||||
|
id: TASK-123
|
||||||
|
title: Materialize RCON player match stats
|
||||||
|
status: pending
|
||||||
|
type: backend
|
||||||
|
team: Backend Senior
|
||||||
|
supporting_teams:
|
||||||
|
- Arquitecto de Base de Datos
|
||||||
|
- Arquitecto Python
|
||||||
|
roadmap_item: rcon-full-data
|
||||||
|
priority: high
|
||||||
|
---
|
||||||
|
|
||||||
|
# TASK-123 - Materialize RCON player match stats
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Build first-pass per-match player statistics from RCON AdminLog kill, team and presence events.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
The internal match detail page should eventually show a simplified scoreboard-like view backed by RCON. AdminLog includes `KILL`, `TEAMSWITCH`, `CONNECTED` and `DISCONNECTED` events that can produce kills, deaths, teamkills, weapon counts and player presence.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
- No UI changes.
|
||||||
|
- Do not expose raw player IDs in frontend work.
|
||||||
|
- Do not reactivate Elo/MMR.
|
||||||
|
- Do not reintroduce Comunidad Hispana #03.
|
||||||
|
- Do not store secrets, runtime DB files or `backend/runtime`.
|
||||||
|
- Keep rematerialization deterministic and idempotent.
|
||||||
|
|
||||||
|
## Allowed Changes
|
||||||
|
|
||||||
|
- backend materialization/storage code for RCON player match stats
|
||||||
|
- optional structured event timeline table or read logic
|
||||||
|
- backend tests and fixtures
|
||||||
|
- this task file when moving it through the workflow
|
||||||
|
|
||||||
|
## Implementation Requirements
|
||||||
|
|
||||||
|
- Work from a dedicated branch for this task.
|
||||||
|
- Read first:
|
||||||
|
- `AGENTS.md`
|
||||||
|
- `ai/architecture-index.md`
|
||||||
|
- `ai/repo-context.md`
|
||||||
|
- `ai/orchestrator/backend-senior.md`
|
||||||
|
- `ai/orchestrator/database-architect.md`
|
||||||
|
- `backend/app/rcon_admin_log_parser.py`
|
||||||
|
- `backend/app/rcon_admin_log_storage.py`
|
||||||
|
- materialized match code from TASK-122
|
||||||
|
- Use parsed events from `rcon_admin_log_events`.
|
||||||
|
- Associate events to matches by target and server-time range between `match_start` and `match_end`.
|
||||||
|
- Create a table such as `rcon_match_player_stats` with target, match, player identity/display, team, kills, deaths, teamkills, deaths by teamkill, weapon JSON, death-by weapon JSON, most-killed JSON, death-by JSON and first/last seen server time.
|
||||||
|
- Optionally create `rcon_match_events` for structured timeline rows, or read directly from AdminLog events.
|
||||||
|
- For `KILL`, add one kill to the killer when teams differ, add one death to the victim, and count same-team kills as teamkills plus victim deaths by teamkill.
|
||||||
|
- Track weapon counts and killer-victim counts.
|
||||||
|
- Use connected/disconnected/team switch events to improve presence and team attribution.
|
||||||
|
- Handle missing or non-Steam-style player IDs robustly.
|
||||||
|
|
||||||
|
## Validation Commands
|
||||||
|
|
||||||
|
- `python -m compileall backend/app`
|
||||||
|
- `python -m pytest backend/tests/<new_or_relevant_player_stats_tests>.py`
|
||||||
|
|
||||||
|
## Manual Verification Steps
|
||||||
|
|
||||||
|
- Validate offline fixtures containing `KILL`, `TEAMSWITCH`, `CONNECTED` and `DISCONNECTED`.
|
||||||
|
- Confirm kills, deaths, teamkills, weapons, most killed and death-by summaries.
|
||||||
|
- Confirm repeated materialization does not duplicate or inflate stats.
|
||||||
|
- Confirm `/health` still works.
|
||||||
|
- Confirm `git diff --name-only` matches the allowed scope.
|
||||||
|
|
||||||
|
## Git Requirements
|
||||||
|
|
||||||
|
- Create a dedicated branch for this task, for example `codex/task-123-rcon-player-stats`.
|
||||||
|
- Run relevant validation before committing.
|
||||||
|
- Stage only intended files.
|
||||||
|
- Commit the completed implementation.
|
||||||
|
- Push the branch to origin.
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
---
|
||||||
|
id: TASK-124
|
||||||
|
title: Add internal RCON match detail API
|
||||||
|
status: pending
|
||||||
|
type: backend
|
||||||
|
team: Backend Senior
|
||||||
|
supporting_teams:
|
||||||
|
- Arquitecto Python
|
||||||
|
- Frontend Senior
|
||||||
|
roadmap_item: rcon-full-data
|
||||||
|
priority: high
|
||||||
|
---
|
||||||
|
|
||||||
|
# TASK-124 - Add internal RCON match detail API
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Expose internal match detail data from materialized RCON matches and player stats.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
`frontend/historico-partida.html` needs a reliable internal backend payload when an external scoreboard link is unavailable or not safely correlated. The API should be RCON-first and graceful for partial or old data.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
- No UI changes in this task.
|
||||||
|
- Preserve existing query params and fallback behavior.
|
||||||
|
- If no materialized RCON detail exists, return a controlled empty or partial payload, not a 500.
|
||||||
|
- Do not reactivate Elo/MMR.
|
||||||
|
- Do not reintroduce Comunidad Hispana #03.
|
||||||
|
- Do not store secrets, runtime DB files or `backend/runtime`.
|
||||||
|
- Public scoreboard URL is optional enrichment only.
|
||||||
|
|
||||||
|
## Allowed Changes
|
||||||
|
|
||||||
|
- `backend/app/routes.py` or existing route modules
|
||||||
|
- backend read-model modules for historical match detail
|
||||||
|
- backend tests for response builder/read model
|
||||||
|
- this task file when moving it through the workflow
|
||||||
|
|
||||||
|
## Implementation Requirements
|
||||||
|
|
||||||
|
- Work from a dedicated branch for this task.
|
||||||
|
- Read first:
|
||||||
|
- `AGENTS.md`
|
||||||
|
- `ai/architecture-index.md`
|
||||||
|
- `ai/repo-context.md`
|
||||||
|
- `ai/orchestrator/backend-senior.md`
|
||||||
|
- `backend/app/routes.py`
|
||||||
|
- `backend/app/rcon_historical_read_model.py`
|
||||||
|
- materialized match/player stats code from TASK-122 and TASK-123
|
||||||
|
- `frontend/historico-partida.html`
|
||||||
|
- `frontend/assets/js/historico-partida.js`
|
||||||
|
- Extend or add `/api/historical/matches/detail?server=...&match=...`.
|
||||||
|
- Prefer an existing endpoint if present and do not break current query params.
|
||||||
|
- Response should include server, match id, map, game mode, start/end, duration, result, winner, confidence/source basis, optional external `match_url`, player rows and timeline/event summary.
|
||||||
|
- Player rows should include display name, team, kills, deaths, teamkills, K/D, top weapons, most killed and death-by summary.
|
||||||
|
- Keep old data fallbacks controlled and backwards-compatible.
|
||||||
|
|
||||||
|
## Validation Commands
|
||||||
|
|
||||||
|
- `python -m compileall backend/app`
|
||||||
|
- `python -m pytest backend/tests/<new_or_relevant_match_detail_api_tests>.py`
|
||||||
|
- `docker compose up -d --build backend`
|
||||||
|
- `Invoke-WebRequest "http://localhost:8000/api/historical/matches/detail?server=<server>&match=<match>"`
|
||||||
|
- `Invoke-WebRequest "http://localhost:8000/health"`
|
||||||
|
|
||||||
|
## Manual Verification Steps
|
||||||
|
|
||||||
|
- Confirm known materialized match detail returns the expected summary and players.
|
||||||
|
- Confirm missing materialized data returns a controlled partial payload.
|
||||||
|
- Confirm `/health` still works.
|
||||||
|
- Confirm no server #03, Elo/MMR or secrets were introduced.
|
||||||
|
- Confirm `git diff --name-only` matches the allowed scope.
|
||||||
|
|
||||||
|
## Git Requirements
|
||||||
|
|
||||||
|
- Create a dedicated branch for this task, for example `codex/task-124-rcon-match-detail-api`.
|
||||||
|
- Run relevant validation before committing.
|
||||||
|
- Stage only intended files.
|
||||||
|
- Commit the completed implementation.
|
||||||
|
- Push the branch to origin.
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
---
|
||||||
|
id: TASK-125
|
||||||
|
title: Prefer materialized RCON recent matches
|
||||||
|
status: pending
|
||||||
|
type: backend
|
||||||
|
team: Backend Senior
|
||||||
|
supporting_teams:
|
||||||
|
- Frontend Senior
|
||||||
|
roadmap_item: rcon-full-data
|
||||||
|
priority: high
|
||||||
|
---
|
||||||
|
|
||||||
|
# TASK-125 - Prefer materialized RCON recent matches
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Update the recent matches API to prefer materialized RCON match records over approximate competitive windows and public-scoreboard fallback.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
End users should see correct scores as consistently as possible. RCON AdminLog `MATCH ENDED` data should be the primary source when available, with active RCON sessions next and public scoreboard only as fallback or degraded enrichment.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
- No frontend changes unless strictly necessary for contract compatibility.
|
||||||
|
- Do not show stale public scoreboard as selected source when RCON is healthy.
|
||||||
|
- Do not reactivate Elo/MMR.
|
||||||
|
- Do not reintroduce Comunidad Hispana #03.
|
||||||
|
- Do not store secrets, runtime DB files or `backend/runtime`.
|
||||||
|
- Preserve RCON-first policy.
|
||||||
|
|
||||||
|
## Allowed Changes
|
||||||
|
|
||||||
|
- backend recent matches read model modules
|
||||||
|
- backend route tests or read-model tests
|
||||||
|
- minimal compatibility-only frontend changes if unavoidable and justified
|
||||||
|
- this task file when moving it through the workflow
|
||||||
|
|
||||||
|
## Implementation Requirements
|
||||||
|
|
||||||
|
- Work from a dedicated branch for this task.
|
||||||
|
- Read first:
|
||||||
|
- `AGENTS.md`
|
||||||
|
- `ai/architecture-index.md`
|
||||||
|
- `ai/repo-context.md`
|
||||||
|
- `ai/orchestrator/backend-senior.md`
|
||||||
|
- `backend/app/routes.py`
|
||||||
|
- `backend/app/rcon_historical_read_model.py`
|
||||||
|
- existing recent matches read-model/storage modules
|
||||||
|
- materialized match code from TASK-122
|
||||||
|
- Use priority:
|
||||||
|
- materialized RCON matches with `MATCH ENDED` result
|
||||||
|
- active/partial RCON session windows with current gamestate
|
||||||
|
- public-scoreboard fallback only if RCON is unavailable or explicitly degraded
|
||||||
|
- Recent match items should expose result scores/winner when available.
|
||||||
|
- Add `result_source` values: `admin-log-match-ended`, `rcon-session`, `public-scoreboard-fallback`, `unavailable`.
|
||||||
|
- Include `match_url` only when safe external link exists.
|
||||||
|
- Include internal detail link data whenever match id and server allow it.
|
||||||
|
|
||||||
|
## Validation Commands
|
||||||
|
|
||||||
|
- `python -m compileall backend/app`
|
||||||
|
- `python -m pytest backend/tests/<new_or_relevant_recent_matches_tests>.py`
|
||||||
|
- `docker compose up -d --build backend`
|
||||||
|
- `docker compose exec backend python -m app.rcon_admin_log_ingestion --minutes 1440`
|
||||||
|
- `Invoke-WebRequest "http://localhost:8000/api/historical/recent-matches?server=all-servers&limit=10"`
|
||||||
|
|
||||||
|
## Manual Verification Steps
|
||||||
|
|
||||||
|
- Confirm recent matches show RCON/AdminLog scores where available.
|
||||||
|
- Confirm source priority uses AdminLog over stale public scoreboard.
|
||||||
|
- Confirm internal detail data exists for match cards.
|
||||||
|
- Confirm `/health` and `frontend/historico.html` are not broken.
|
||||||
|
- Confirm `git diff --name-only` matches the allowed scope.
|
||||||
|
|
||||||
|
## Git Requirements
|
||||||
|
|
||||||
|
- Create a dedicated branch for this task, for example `codex/task-125-rcon-recent-matches-priority`.
|
||||||
|
- Run relevant validation before committing.
|
||||||
|
- Stage only intended files.
|
||||||
|
- Commit the completed implementation.
|
||||||
|
- Push the branch to origin.
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
---
|
||||||
|
id: TASK-126
|
||||||
|
title: Correlate scoreboard links with RCON materialized matches
|
||||||
|
status: pending
|
||||||
|
type: backend
|
||||||
|
team: Backend Senior
|
||||||
|
supporting_teams:
|
||||||
|
- Arquitecto Python
|
||||||
|
roadmap_item: rcon-full-data
|
||||||
|
priority: medium
|
||||||
|
---
|
||||||
|
|
||||||
|
# TASK-126 - Correlate scoreboard links with RCON materialized matches
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Safely correlate materialized RCON matches with public scoreboard game URLs when a high-confidence match exists.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
Match cards should link to the public scoreboard only when safe correlation exists. RCON remains the primary data source; public scoreboard links are optional enrichment and should never open arbitrary domains.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
- No UI changes.
|
||||||
|
- Do not reactivate Elo/MMR.
|
||||||
|
- Do not reintroduce Comunidad Hispana #03.
|
||||||
|
- Do not store secrets, runtime DB files or `backend/runtime`.
|
||||||
|
- Do not treat public scoreboard as source of truth for RCON data.
|
||||||
|
- If correlation fails, internal detail links must still work.
|
||||||
|
|
||||||
|
## Allowed Changes
|
||||||
|
|
||||||
|
- existing scoreboard origin catalog/correlation code if present
|
||||||
|
- backend safe URL/correlation storage or cache code
|
||||||
|
- backend tests
|
||||||
|
- this task file when moving it through the workflow
|
||||||
|
|
||||||
|
## Implementation Requirements
|
||||||
|
|
||||||
|
- Work from a dedicated branch for this task.
|
||||||
|
- Read first:
|
||||||
|
- `AGENTS.md`
|
||||||
|
- `ai/architecture-index.md`
|
||||||
|
- `ai/repo-context.md`
|
||||||
|
- `ai/orchestrator/backend-senior.md`
|
||||||
|
- `backend/app/scoreboard_origin_catalog.py` if present
|
||||||
|
- existing scoreboard link correlation modules
|
||||||
|
- materialized match code from TASK-122
|
||||||
|
- Correlate by server, map, end time/server time where possible, duration where possible and player count/peak players where available.
|
||||||
|
- Support known origins:
|
||||||
|
- Comunidad Hispana #01: `https://scoreboard.comunidadhll.es`
|
||||||
|
- Comunidad Hispana #02: `https://scoreboard.comunidadhll.es:5443`
|
||||||
|
- Only return external `match_url` for expected origins and `/games/<id>` paths.
|
||||||
|
- Store or cache correlation result if appropriate.
|
||||||
|
- Include a manual check for `https://scoreboard.comunidadhll.es/games/1561515`.
|
||||||
|
|
||||||
|
## Validation Commands
|
||||||
|
|
||||||
|
- `python -m compileall backend/app`
|
||||||
|
- `python -m pytest backend/tests/<new_or_relevant_scoreboard_correlation_tests>.py`
|
||||||
|
|
||||||
|
## Manual Verification Steps
|
||||||
|
|
||||||
|
- Confirm safe URL allowlist rejects arbitrary domains and paths.
|
||||||
|
- Confirm #01 and #02 origin selection works.
|
||||||
|
- Confirm no server #03 is added to catalogs, defaults or tests.
|
||||||
|
- Confirm failed correlation still leaves internal detail links available.
|
||||||
|
- Confirm `git diff --name-only` matches the allowed scope.
|
||||||
|
|
||||||
|
## Git Requirements
|
||||||
|
|
||||||
|
- Create a dedicated branch for this task, for example `codex/task-126-rcon-scoreboard-correlation`.
|
||||||
|
- Run relevant validation before committing.
|
||||||
|
- Stage only intended files.
|
||||||
|
- Commit the completed implementation.
|
||||||
|
- Push the branch to origin.
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
---
|
||||||
|
id: TASK-127
|
||||||
|
title: Update recent match link actions
|
||||||
|
status: pending
|
||||||
|
type: frontend
|
||||||
|
team: Frontend Senior
|
||||||
|
supporting_teams:
|
||||||
|
- Experto en interfaz
|
||||||
|
- Backend Senior
|
||||||
|
roadmap_item: rcon-full-data
|
||||||
|
priority: medium
|
||||||
|
---
|
||||||
|
|
||||||
|
# TASK-127 - Update recent match link actions
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Update recent match cards so internal details and external scoreboard links are prioritized clearly and safely.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
Recent match cards should always offer internal details when supported and only show public scoreboard links when the backend provides a safe `match_url`. RCON remains primary, and the UI must not expose stale wording or paused Elo/MVP content.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
- Do not reintroduce Comunidad Hispana #03.
|
||||||
|
- Do not show paused Elo/MVP blocks.
|
||||||
|
- Do not expose public "snapshot" wording.
|
||||||
|
- Do not show broken external links.
|
||||||
|
- Do not change backend behavior unless strictly required and justified.
|
||||||
|
- Preserve HLL Vietnam tactical, sober styling.
|
||||||
|
|
||||||
|
## Allowed Changes
|
||||||
|
|
||||||
|
- `frontend/assets/js/historico.js`
|
||||||
|
- `frontend/assets/js/historico-recent-live.js` if needed
|
||||||
|
- minimal CSS only if needed for the action layout
|
||||||
|
- this task file when moving it through the workflow
|
||||||
|
|
||||||
|
## Implementation Requirements
|
||||||
|
|
||||||
|
- Work from a dedicated branch for this task.
|
||||||
|
- Read first:
|
||||||
|
- `AGENTS.md`
|
||||||
|
- `ai/architecture-index.md`
|
||||||
|
- `ai/repo-context.md`
|
||||||
|
- `ai/orchestrator/frontend-senior.md`
|
||||||
|
- `ai/orchestrator/ui-expert.md`
|
||||||
|
- `frontend/historico.html`
|
||||||
|
- `frontend/assets/js/historico.js`
|
||||||
|
- `frontend/assets/js/historico-recent-live.js`
|
||||||
|
- `frontend/assets/css/historico.css`
|
||||||
|
- Show score when result exists.
|
||||||
|
- Show `Ver detalles` internal link for every match with internal detail support.
|
||||||
|
- Show `Ver scoreboard` or `Ver partida externa` only when `match_url` exists.
|
||||||
|
- If a match has no score yet, show `En curso` or `Resultado no disponible` instead of `---` when a better status exists.
|
||||||
|
- Keep cards compatible with existing API fallback payloads.
|
||||||
|
|
||||||
|
## Validation Commands
|
||||||
|
|
||||||
|
- `node --check frontend/assets/js/historico.js`
|
||||||
|
- `node --check frontend/assets/js/historico-recent-live.js` if changed
|
||||||
|
- `docker compose up -d --build backend frontend`
|
||||||
|
|
||||||
|
## Manual Verification Steps
|
||||||
|
|
||||||
|
- Open `http://localhost:8080/historico.html`.
|
||||||
|
- Confirm scores appear where available.
|
||||||
|
- Confirm internal detail link is visible when supported.
|
||||||
|
- Confirm external scoreboard link appears only when `match_url` exists.
|
||||||
|
- Confirm there is no Elo/MVP/server #03/snapshot wording.
|
||||||
|
- Confirm `git diff --name-only` matches the allowed scope.
|
||||||
|
|
||||||
|
## Git Requirements
|
||||||
|
|
||||||
|
- Create a dedicated branch for this task, for example `codex/task-127-recent-match-actions`.
|
||||||
|
- Run relevant validation before committing.
|
||||||
|
- Stage only intended files.
|
||||||
|
- Commit the completed implementation.
|
||||||
|
- Push the branch to origin.
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
---
|
||||||
|
id: TASK-128
|
||||||
|
title: Build simplified internal match detail page
|
||||||
|
status: pending
|
||||||
|
type: frontend
|
||||||
|
team: Frontend Senior
|
||||||
|
supporting_teams:
|
||||||
|
- Experto en interfaz
|
||||||
|
- Backend Senior
|
||||||
|
roadmap_item: rcon-full-data
|
||||||
|
priority: high
|
||||||
|
---
|
||||||
|
|
||||||
|
# TASK-128 - Build simplified internal match detail page
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Make `frontend/historico-partida.html` useful as a lightweight internal scoreboard-style match detail page backed by the internal RCON detail API.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
When a safe public scoreboard link is unavailable, users should still be able to inspect the match through HLL Vietnam. The page should show summary, score, winner, factions/teams if available, player stats and a relevant event timeline without advanced charts.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
- Do not add advanced charts or graphs.
|
||||||
|
- Do not expose raw player IDs unless required by an already approved API contract.
|
||||||
|
- Do not reintroduce Comunidad Hispana #03.
|
||||||
|
- Do not show paused Elo/MVP blocks.
|
||||||
|
- Do not expose public "snapshot" wording.
|
||||||
|
- Preserve HLL Vietnam styling.
|
||||||
|
|
||||||
|
## Allowed Changes
|
||||||
|
|
||||||
|
- `frontend/historico-partida.html`
|
||||||
|
- `frontend/assets/js/historico-partida.js`
|
||||||
|
- `frontend/assets/css/historico.css` if needed
|
||||||
|
- this task file when moving it through the workflow
|
||||||
|
|
||||||
|
## Implementation Requirements
|
||||||
|
|
||||||
|
- Work from a dedicated branch for this task.
|
||||||
|
- Read first:
|
||||||
|
- `AGENTS.md`
|
||||||
|
- `ai/architecture-index.md`
|
||||||
|
- `ai/repo-context.md`
|
||||||
|
- `ai/orchestrator/frontend-senior.md`
|
||||||
|
- `ai/orchestrator/ui-expert.md`
|
||||||
|
- `frontend/historico-partida.html`
|
||||||
|
- `frontend/assets/js/historico-partida.js`
|
||||||
|
- `frontend/assets/css/historico.css`
|
||||||
|
- backend match detail API code from TASK-124
|
||||||
|
- Use the internal match detail API.
|
||||||
|
- Display map, server, result, winner, game mode, start/end time, source/confidence and external scoreboard link when available.
|
||||||
|
- Add player stats table with player, team, kills, deaths, teamkills, K/D, top weapons, most killed and death by.
|
||||||
|
- Add event/timeline section for match start, match end, kill sample/recent kills and useful team switches.
|
||||||
|
- Handle partial or missing data gracefully with controlled empty states.
|
||||||
|
- Keep implementation in vanilla JavaScript.
|
||||||
|
|
||||||
|
## Validation Commands
|
||||||
|
|
||||||
|
- `node --check frontend/assets/js/historico-partida.js`
|
||||||
|
- `docker compose up -d --build backend frontend`
|
||||||
|
|
||||||
|
## Manual Verification Steps
|
||||||
|
|
||||||
|
- Open a match detail from the recent matches list.
|
||||||
|
- Verify a match with AdminLog kill data shows at least one kill/weapon row.
|
||||||
|
- Verify a match without player stats shows a controlled empty state.
|
||||||
|
- Confirm no Elo/MVP/server #03/snapshot wording appears.
|
||||||
|
- Confirm `git diff --name-only` matches the allowed scope.
|
||||||
|
|
||||||
|
## Git Requirements
|
||||||
|
|
||||||
|
- Create a dedicated branch for this task, for example `codex/task-128-internal-match-detail-page`.
|
||||||
|
- Run relevant validation before committing.
|
||||||
|
- Stage only intended files.
|
||||||
|
- Commit the completed implementation.
|
||||||
|
- Push the branch to origin.
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
---
|
||||||
|
id: TASK-129
|
||||||
|
title: Parse and materialize player profile MESSAGE blocks
|
||||||
|
status: pending
|
||||||
|
type: backend
|
||||||
|
team: Backend Senior
|
||||||
|
supporting_teams:
|
||||||
|
- Arquitecto de Base de Datos
|
||||||
|
- Arquitecto Python
|
||||||
|
roadmap_item: rcon-full-data
|
||||||
|
priority: medium
|
||||||
|
---
|
||||||
|
|
||||||
|
# TASK-129 - Parse and materialize player profile MESSAGE blocks
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Extract useful long-term player profile snapshots from RCON AdminLog `MESSAGE` blocks generated by the server bot.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
Observed AdminLog `MESSAGE` blocks can include profile-like stats such as first seen, sessions, matches played, play time, sanctions, averages, kills, deaths, teamkills, K/D, victims, nemesis and favorite weapons. These are profile snapshots, not authoritative per-match stats.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
- Do not use profile snapshots as authoritative per-match stats.
|
||||||
|
- No frontend changes unless adding backend API contract tests requires fixtures.
|
||||||
|
- Do not reactivate Elo/MMR.
|
||||||
|
- Do not reintroduce Comunidad Hispana #03.
|
||||||
|
- Do not store secrets, runtime DB files or `backend/runtime`.
|
||||||
|
- Do not expose raw full `MESSAGE` content through public APIs by default.
|
||||||
|
|
||||||
|
## Allowed Changes
|
||||||
|
|
||||||
|
- backend parser/materialization/storage code for profile snapshots
|
||||||
|
- backend tests with anonymized examples
|
||||||
|
- this task file when moving it through the workflow
|
||||||
|
|
||||||
|
## Implementation Requirements
|
||||||
|
|
||||||
|
- Work from a dedicated branch for this task.
|
||||||
|
- Read first:
|
||||||
|
- `AGENTS.md`
|
||||||
|
- `ai/architecture-index.md`
|
||||||
|
- `ai/repo-context.md`
|
||||||
|
- `ai/orchestrator/backend-senior.md`
|
||||||
|
- `ai/orchestrator/database-architect.md`
|
||||||
|
- `backend/app/rcon_admin_log_parser.py`
|
||||||
|
- `backend/app/rcon_admin_log_storage.py`
|
||||||
|
- existing AdminLog ingestion/materialization tests
|
||||||
|
- Add a parser for profile-like `MESSAGE` content only.
|
||||||
|
- Store extracted snapshots in a table such as `rcon_player_profile_snapshots`.
|
||||||
|
- Include fields for target, player id/name, source server time, event timestamp, sessions, matches played, total kills/deaths, teamkills done/received, K/D, favorite weapons JSON, victims JSON, nemesis JSON, averages JSON, sanctions JSON and raw content.
|
||||||
|
- Deduplicate by `target_key + player_id + source_server_time`.
|
||||||
|
- Validate Spanish labels including `bajas`, `muertes`, `Víctimas`, `Némesis`, `Armas favoritas`, `Promedios` and `Sanciones`.
|
||||||
|
|
||||||
|
## Validation Commands
|
||||||
|
|
||||||
|
- `python -m compileall backend/app`
|
||||||
|
- `python -m pytest backend/tests/<new_or_relevant_profile_message_tests>.py`
|
||||||
|
|
||||||
|
## Manual Verification Steps
|
||||||
|
|
||||||
|
- Confirm tests use anonymized MESSAGE examples.
|
||||||
|
- Confirm non-profile `MESSAGE` entries are ignored.
|
||||||
|
- Confirm duplicate snapshots are not inserted twice.
|
||||||
|
- Confirm `/health` still works.
|
||||||
|
- Confirm `git diff --name-only` matches the allowed scope.
|
||||||
|
|
||||||
|
## Git Requirements
|
||||||
|
|
||||||
|
- Create a dedicated branch for this task, for example `codex/task-129-profile-message-snapshots`.
|
||||||
|
- Run relevant validation before committing.
|
||||||
|
- Stage only intended files.
|
||||||
|
- Commit the completed implementation.
|
||||||
|
- Push the branch to origin.
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
---
|
||||||
|
id: TASK-130
|
||||||
|
title: Add player profile enrichment API
|
||||||
|
status: pending
|
||||||
|
type: backend
|
||||||
|
team: Backend Senior
|
||||||
|
supporting_teams:
|
||||||
|
- Arquitecto Python
|
||||||
|
roadmap_item: rcon-full-data
|
||||||
|
priority: medium
|
||||||
|
---
|
||||||
|
|
||||||
|
# TASK-130 - Add player profile enrichment API
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Expose optional player profile summaries from RCON profile snapshots for future UI use without coupling them to per-match stats.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
Profile MESSAGE snapshots can enrich player rows later, but they are historical profile snapshots rather than authoritative match facts. The API should expose safe display summaries and remain resilient when no snapshot exists.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
- No mandatory frontend changes.
|
||||||
|
- Do not return raw full `MESSAGE` content by default.
|
||||||
|
- Do not make match detail fail if no profile snapshot exists.
|
||||||
|
- Do not reactivate Elo/MMR.
|
||||||
|
- Do not reintroduce Comunidad Hispana #03.
|
||||||
|
- Do not store secrets, runtime DB files or `backend/runtime`.
|
||||||
|
|
||||||
|
## Allowed Changes
|
||||||
|
|
||||||
|
- backend read model/route code for profile enrichment
|
||||||
|
- API tests
|
||||||
|
- this task file when moving it through the workflow
|
||||||
|
|
||||||
|
## Implementation Requirements
|
||||||
|
|
||||||
|
- Work from a dedicated branch for this task.
|
||||||
|
- Read first:
|
||||||
|
- `AGENTS.md`
|
||||||
|
- `ai/architecture-index.md`
|
||||||
|
- `ai/repo-context.md`
|
||||||
|
- `ai/orchestrator/backend-senior.md`
|
||||||
|
- `backend/app/routes.py`
|
||||||
|
- profile snapshot code from TASK-129
|
||||||
|
- match detail API code from TASK-124
|
||||||
|
- Add a backend read-model endpoint or include optional `profile_summary` in match detail player rows if available.
|
||||||
|
- Return only safe display data.
|
||||||
|
- Keep no-data behavior empty or omitted, not failing.
|
||||||
|
- Keep implementation deterministic and testable offline where possible.
|
||||||
|
|
||||||
|
## Validation Commands
|
||||||
|
|
||||||
|
- `python -m compileall backend/app`
|
||||||
|
- `python -m pytest backend/tests/<new_or_relevant_profile_api_tests>.py`
|
||||||
|
|
||||||
|
## Manual Verification Steps
|
||||||
|
|
||||||
|
- Make a manual request against a known stored profile snapshot.
|
||||||
|
- Confirm missing snapshots do not break match detail or profile responses.
|
||||||
|
- Confirm raw full MESSAGE content is absent by default.
|
||||||
|
- Confirm `/health` still works.
|
||||||
|
- Confirm `git diff --name-only` matches the allowed scope.
|
||||||
|
|
||||||
|
## Git Requirements
|
||||||
|
|
||||||
|
- Create a dedicated branch for this task, for example `codex/task-130-profile-enrichment-api`.
|
||||||
|
- Run relevant validation before committing.
|
||||||
|
- Stage only intended files.
|
||||||
|
- Commit the completed implementation.
|
||||||
|
- Push the branch to origin.
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
---
|
||||||
|
id: TASK-131
|
||||||
|
title: Add RCON data pipeline validation script
|
||||||
|
status: pending
|
||||||
|
type: platform
|
||||||
|
team: Backend Senior
|
||||||
|
supporting_teams:
|
||||||
|
- Arquitecto Python
|
||||||
|
- Frontend Senior
|
||||||
|
roadmap_item: rcon-full-data
|
||||||
|
priority: medium
|
||||||
|
---
|
||||||
|
|
||||||
|
# TASK-131 - Add RCON data pipeline validation script
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Add one lightweight validation script for the full RCON data pipeline.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
The RCON data pipeline now spans parsing, storage, AdminLog ingestion, materialized matches, player stats and backend APIs. A focused script should help implementation workers and reviewers validate the pipeline without requiring real RCON for unit tests.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
- Do not require real RCON for unit tests.
|
||||||
|
- Do not modify Docker behavior.
|
||||||
|
- Do not reactivate Elo/MMR.
|
||||||
|
- Do not reintroduce Comunidad Hispana #03.
|
||||||
|
- Do not store secrets, runtime DB files or `backend/runtime`.
|
||||||
|
- Document when real RCON or Docker smoke checks are skipped.
|
||||||
|
|
||||||
|
## Allowed Changes
|
||||||
|
|
||||||
|
- `scripts/run-rcon-data-pipeline-tests.ps1`
|
||||||
|
- possibly `scripts/run-integration-tests.ps1` only if wiring the new script is appropriate and low risk
|
||||||
|
- documentation only if needed for the new command
|
||||||
|
- this task file when moving it through the workflow
|
||||||
|
|
||||||
|
## Implementation Requirements
|
||||||
|
|
||||||
|
- Work from a dedicated branch for this task.
|
||||||
|
- Read first:
|
||||||
|
- `AGENTS.md`
|
||||||
|
- `ai/architecture-index.md`
|
||||||
|
- `ai/repo-context.md`
|
||||||
|
- `ai/orchestrator/backend-senior.md`
|
||||||
|
- `scripts/run-integration-tests.ps1`
|
||||||
|
- existing backend tests for AdminLog/parser/materialization
|
||||||
|
- Add `scripts/run-rcon-data-pipeline-tests.ps1`.
|
||||||
|
- Validate Python compile.
|
||||||
|
- Run parser tests.
|
||||||
|
- Run storage/materialization tests.
|
||||||
|
- Include optional backend endpoint smoke checks if Docker is already running.
|
||||||
|
- Skip real RCON-dependent checks gracefully with a clear message.
|
||||||
|
|
||||||
|
## Validation Commands
|
||||||
|
|
||||||
|
- `powershell -ExecutionPolicy Bypass -File scripts/run-rcon-data-pipeline-tests.ps1`
|
||||||
|
- `powershell -ExecutionPolicy Bypass -File scripts/run-integration-tests.ps1`
|
||||||
|
|
||||||
|
## Manual Verification Steps
|
||||||
|
|
||||||
|
- Confirm the script can run without real RCON credentials.
|
||||||
|
- Confirm skipped Docker/RCON smoke checks explain why they were skipped.
|
||||||
|
- Confirm existing integration script still passes.
|
||||||
|
- Confirm no product behavior changed.
|
||||||
|
- Confirm `git diff --name-only` matches the allowed scope.
|
||||||
|
|
||||||
|
## Git Requirements
|
||||||
|
|
||||||
|
- Create a dedicated branch for this task, for example `codex/task-131-rcon-pipeline-validation`.
|
||||||
|
- Run relevant validation before committing.
|
||||||
|
- Stage only intended files.
|
||||||
|
- Commit the completed implementation.
|
||||||
|
- Push the branch to origin.
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
---
|
||||||
|
id: TASK-132
|
||||||
|
title: Document RCON-first historical architecture
|
||||||
|
status: pending
|
||||||
|
type: documentation
|
||||||
|
team: PM
|
||||||
|
supporting_teams:
|
||||||
|
- Backend Senior
|
||||||
|
- Arquitecto Python
|
||||||
|
roadmap_item: rcon-full-data
|
||||||
|
priority: medium
|
||||||
|
---
|
||||||
|
|
||||||
|
# TASK-132 - Document RCON-first historical architecture
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Document the new RCON-first historical architecture and update AI context so future tasks preserve the intended data-source policy.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
HLL Vietnam is building a historical/live data platform for Comunidad Hispana servers with RCON as the primary source. AdminLog ingestion, parsing, event storage, materialized matches/player stats and optional public-scoreboard enrichment should be documented clearly for future workers.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
- Documentation-only task.
|
||||||
|
- Do not change product behavior.
|
||||||
|
- Do not modify backend/frontend/Docker code.
|
||||||
|
- Do not reactivate Elo/MMR.
|
||||||
|
- Do not reintroduce Comunidad Hispana #03.
|
||||||
|
- Do not store secrets, runtime DB files or `backend/runtime`.
|
||||||
|
- Public scoreboard must be described only as optional enrichment/link source or fallback.
|
||||||
|
|
||||||
|
## Allowed Changes
|
||||||
|
|
||||||
|
- `README.md` or backend README/docs as appropriate
|
||||||
|
- `docs/decisions.md`
|
||||||
|
- `ai/repo-context.md`
|
||||||
|
- `ai/architecture-index.md`
|
||||||
|
- this task file when moving it through the workflow
|
||||||
|
|
||||||
|
## Implementation Requirements
|
||||||
|
|
||||||
|
- Work from a dedicated branch for this task.
|
||||||
|
- Read first:
|
||||||
|
- `AGENTS.md`
|
||||||
|
- `ai/architecture-index.md`
|
||||||
|
- `ai/repo-context.md`
|
||||||
|
- `ai/orchestrator/pm.md`
|
||||||
|
- `ai/orchestrator/backend-senior.md`
|
||||||
|
- `README.md`
|
||||||
|
- `backend/README.md`
|
||||||
|
- `docs/decisions.md`
|
||||||
|
- Document:
|
||||||
|
- RCON session capture
|
||||||
|
- AdminLog ingestion
|
||||||
|
- AdminLog parser
|
||||||
|
- event storage
|
||||||
|
- materialized matches/player stats
|
||||||
|
- public scoreboard only as optional enrichment/link source or fallback
|
||||||
|
- Elo/MMR paused
|
||||||
|
- Comunidad Hispana #03 disabled
|
||||||
|
- Include manual commands:
|
||||||
|
- `docker compose exec backend python -m app.rcon_admin_log_ingestion --minutes 1440`
|
||||||
|
- `docker compose exec backend python -m app.rcon_historical_worker capture`
|
||||||
|
- Keep wording repository-specific and avoid generic platform-template replacement.
|
||||||
|
|
||||||
|
## Validation Commands
|
||||||
|
|
||||||
|
- `powershell -ExecutionPolicy Bypass -File scripts/run-integration-tests.ps1`
|
||||||
|
|
||||||
|
## Manual Verification Steps
|
||||||
|
|
||||||
|
- Review docs for no secrets or credentials.
|
||||||
|
- Confirm docs consistently preserve RCON-first policy.
|
||||||
|
- Confirm Elo/MMR remains documented as paused.
|
||||||
|
- Confirm Comunidad Hispana #03 remains documented as disabled from defaults.
|
||||||
|
- Confirm `git diff --name-only` matches the allowed scope.
|
||||||
|
|
||||||
|
## Git Requirements
|
||||||
|
|
||||||
|
- Create a dedicated branch for this task, for example `codex/task-132-rcon-architecture-docs`.
|
||||||
|
- Run relevant validation before committing.
|
||||||
|
- Stage only intended files.
|
||||||
|
- Commit the completed implementation.
|
||||||
|
- Push the branch to origin.
|
||||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import time
|
import time
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
@@ -14,6 +15,7 @@ from .config import (
|
|||||||
get_rcon_historical_capture_retry_delay_seconds,
|
get_rcon_historical_capture_retry_delay_seconds,
|
||||||
get_rcon_request_timeout_seconds,
|
get_rcon_request_timeout_seconds,
|
||||||
)
|
)
|
||||||
|
from .rcon_admin_log_ingestion import ingest_rcon_admin_logs
|
||||||
from .rcon_client import (
|
from .rcon_client import (
|
||||||
RconQueryError,
|
RconQueryError,
|
||||||
build_rcon_target_key,
|
build_rcon_target_key,
|
||||||
@@ -38,6 +40,10 @@ class RconHistoricalCaptureStats:
|
|||||||
samples_inserted: int = 0
|
samples_inserted: int = 0
|
||||||
duplicate_samples: int = 0
|
duplicate_samples: int = 0
|
||||||
failed_targets: int = 0
|
failed_targets: int = 0
|
||||||
|
admin_log_events_seen: int = 0
|
||||||
|
admin_log_events_inserted: int = 0
|
||||||
|
admin_log_duplicate_events: int = 0
|
||||||
|
admin_log_failed_targets: int = 0
|
||||||
|
|
||||||
|
|
||||||
def run_rcon_historical_capture(
|
def run_rcon_historical_capture(
|
||||||
@@ -60,12 +66,14 @@ def run_rcon_historical_capture_unlocked(
|
|||||||
"""Capture one prospective RCON sample assuming the shared writer lock is already held."""
|
"""Capture one prospective RCON sample assuming the shared writer lock is already held."""
|
||||||
initialize_rcon_historical_storage()
|
initialize_rcon_historical_storage()
|
||||||
selected_targets = _select_targets(target_key)
|
selected_targets = _select_targets(target_key)
|
||||||
|
admin_log_lookback_minutes = get_rcon_admin_log_lookback_minutes()
|
||||||
captured_at = utc_now().isoformat().replace("+00:00", "Z")
|
captured_at = utc_now().isoformat().replace("+00:00", "Z")
|
||||||
target_scope = target_key or "all-configured-rcon-targets"
|
target_scope = target_key or "all-configured-rcon-targets"
|
||||||
run_id = start_rcon_historical_capture_run(mode="capture", target_scope=target_scope)
|
run_id = start_rcon_historical_capture_run(mode="capture", target_scope=target_scope)
|
||||||
stats = RconHistoricalCaptureStats()
|
stats = RconHistoricalCaptureStats()
|
||||||
items: list[dict[str, object]] = []
|
items: list[dict[str, object]] = []
|
||||||
errors: list[dict[str, object]] = []
|
errors: list[dict[str, object]] = []
|
||||||
|
admin_log_errors: list[dict[str, object]] = []
|
||||||
timeout_seconds = get_rcon_request_timeout_seconds()
|
timeout_seconds = get_rcon_request_timeout_seconds()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -108,6 +116,17 @@ def run_rcon_historical_capture_unlocked(
|
|||||||
)
|
)
|
||||||
errors.append(_serialize_capture_error(target, exc, timeout_seconds=timeout_seconds))
|
errors.append(_serialize_capture_error(target, exc, timeout_seconds=timeout_seconds))
|
||||||
|
|
||||||
|
admin_log_result = _ingest_target_admin_log(
|
||||||
|
target_key=str(target_metadata["target_key"]),
|
||||||
|
minutes=admin_log_lookback_minutes,
|
||||||
|
)
|
||||||
|
_merge_admin_log_result(
|
||||||
|
stats=stats,
|
||||||
|
admin_log_errors=admin_log_errors,
|
||||||
|
target=target_metadata,
|
||||||
|
result=admin_log_result,
|
||||||
|
)
|
||||||
|
|
||||||
status = "success" if not errors else ("partial" if items else "failed")
|
status = "success" if not errors else ("partial" if items else "failed")
|
||||||
finalize_rcon_historical_capture_run(
|
finalize_rcon_historical_capture_run(
|
||||||
run_id,
|
run_id,
|
||||||
@@ -135,14 +154,20 @@ def run_rcon_historical_capture_unlocked(
|
|||||||
"run_status": status,
|
"run_status": status,
|
||||||
"captured_at": captured_at,
|
"captured_at": captured_at,
|
||||||
"target_scope": target_scope,
|
"target_scope": target_scope,
|
||||||
|
"admin_log_lookback_minutes": admin_log_lookback_minutes,
|
||||||
"targets": items,
|
"targets": items,
|
||||||
"errors": errors,
|
"errors": errors,
|
||||||
|
"admin_log_errors": admin_log_errors,
|
||||||
"storage_status": list_rcon_historical_target_statuses(),
|
"storage_status": list_rcon_historical_target_statuses(),
|
||||||
"totals": {
|
"totals": {
|
||||||
"targets_seen": stats.targets_seen,
|
"targets_seen": stats.targets_seen,
|
||||||
"samples_inserted": stats.samples_inserted,
|
"samples_inserted": stats.samples_inserted,
|
||||||
"duplicate_samples": stats.duplicate_samples,
|
"duplicate_samples": stats.duplicate_samples,
|
||||||
"failed_targets": stats.failed_targets,
|
"failed_targets": stats.failed_targets,
|
||||||
|
"admin_log_events_seen": stats.admin_log_events_seen,
|
||||||
|
"admin_log_events_inserted": stats.admin_log_events_inserted,
|
||||||
|
"admin_log_duplicate_events": stats.admin_log_duplicate_events,
|
||||||
|
"admin_log_failed_targets": stats.admin_log_failed_targets,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,6 +256,72 @@ def _select_targets(target_key: str | None) -> list[object]:
|
|||||||
return selected
|
return selected
|
||||||
|
|
||||||
|
|
||||||
|
def get_rcon_admin_log_lookback_minutes() -> int:
|
||||||
|
"""Return the AdminLog lookback window used by periodic RCON capture."""
|
||||||
|
configured_value = os.getenv("HLL_BACKEND_RCON_ADMIN_LOG_LOOKBACK_MINUTES", "60")
|
||||||
|
lookback_minutes = int(configured_value)
|
||||||
|
if lookback_minutes <= 0:
|
||||||
|
raise ValueError("HLL_BACKEND_RCON_ADMIN_LOG_LOOKBACK_MINUTES must be positive.")
|
||||||
|
return lookback_minutes
|
||||||
|
|
||||||
|
|
||||||
|
def _ingest_target_admin_log(
|
||||||
|
*,
|
||||||
|
target_key: str,
|
||||||
|
minutes: int,
|
||||||
|
) -> dict[str, object]:
|
||||||
|
try:
|
||||||
|
return ingest_rcon_admin_logs(minutes=minutes, target_key=target_key)
|
||||||
|
except Exception as exc: # noqa: BLE001 - worker reports per-target AdminLog failures
|
||||||
|
return {
|
||||||
|
"status": "error",
|
||||||
|
"errors": [
|
||||||
|
{
|
||||||
|
"target_key": target_key,
|
||||||
|
"status": "error",
|
||||||
|
"error_type": type(exc).__name__,
|
||||||
|
"message": str(exc),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"totals": {
|
||||||
|
"events_seen": 0,
|
||||||
|
"events_inserted": 0,
|
||||||
|
"duplicate_events": 0,
|
||||||
|
"failed_targets": 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _merge_admin_log_result(
|
||||||
|
*,
|
||||||
|
stats: RconHistoricalCaptureStats,
|
||||||
|
admin_log_errors: list[dict[str, object]],
|
||||||
|
target: dict[str, object],
|
||||||
|
result: dict[str, object],
|
||||||
|
) -> None:
|
||||||
|
totals = result.get("totals")
|
||||||
|
if isinstance(totals, dict):
|
||||||
|
stats.admin_log_events_seen += int(totals.get("events_seen") or 0)
|
||||||
|
stats.admin_log_events_inserted += int(totals.get("events_inserted") or 0)
|
||||||
|
stats.admin_log_duplicate_events += int(totals.get("duplicate_events") or 0)
|
||||||
|
stats.admin_log_failed_targets += int(totals.get("failed_targets") or 0)
|
||||||
|
|
||||||
|
errors = result.get("errors")
|
||||||
|
if isinstance(errors, list):
|
||||||
|
for error in errors:
|
||||||
|
if isinstance(error, dict):
|
||||||
|
admin_log_errors.append(
|
||||||
|
{
|
||||||
|
"target_key": target["target_key"],
|
||||||
|
"external_server_id": target.get("external_server_id"),
|
||||||
|
"name": target.get("name"),
|
||||||
|
"status": "error",
|
||||||
|
"error_type": error.get("error_type"),
|
||||||
|
"message": error.get("message"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _serialize_target(target: object) -> dict[str, object]:
|
def _serialize_target(target: object) -> dict[str, object]:
|
||||||
return {
|
return {
|
||||||
"target_key": build_rcon_target_key(target),
|
"target_key": build_rcon_target_key(target),
|
||||||
|
|||||||
Reference in New Issue
Block a user