# Technical Decisions

## Decision 001: frontend simple HTML/CSS/JS

Se adopta una base estatica con HTML, CSS y JavaScript puro para priorizar simplicidad, velocidad de arranque y compatibilidad total al abrir el frontend directamente en navegador.

## Decision 002: backend previsto en Python

La estructura del repositorio reserva desde el inicio una carpeta de backend porque la implementacion futura se realizara en Python.

## Decision 003: estructura preparada para orquestacion por agentes

Se incluye una carpeta `ai/` y un documento `AGENTS.md` para facilitar una futura organizacion del trabajo por roles, tareas y orquestacion.

## Decision 004: branding militar Vietnam

La direccion visual inicial se alinea con una estetica sobria, tactica y militar inspirada en el contexto Vietnam para mantener coherencia tematica desde la primera iteracion.

## Decision 005: AI Development Platform integrada de forma adaptada

Se integra una capa de orquestacion por tasks inspirada en la plantilla de AI Development Platform, pero adaptada al contexto real de HLL Vietnam y sin arrastrar supuestos genericos de otros stacks. La plataforma se usa como soporte operativo del repositorio, no como funcionalidad del producto.

## Decision 006: contrato API pequeno antes de integraciones reales

Antes de implementar endpoints de comunidad o integraciones externas, se fija un contrato JSON minimo entre frontend y backend para evitar que la landing y el backend evolucionen con supuestos incompatibles.

La unica ruta implementada hoy es `GET /health`. Las rutas `/api/community`, `/api/trailer`, `/api/discord` y `/api/servers` quedan definidas como contrato previsto o placeholder en `docs/frontend-backend-contract.md`, manteniendo el backend en Python y sin introducir todavia Discord real, servidores reales ni base de datos.

## Decision 007: estrategia por fases para Discord y servidores

Los datos de Discord y de servidores de juego se incorporaran por fases para evitar dependencias prematuras de credenciales, APIs externas o consultas de red todavia no validadas.

La fase inicial debe usar datos manuales o placeholder controlados por el backend para mantener estable el contrato del frontend. Una fase intermedia podra anadir una integracion limitada con fuentes publicas o consultas tecnicas de bajo riesgo. Solo una fase posterior evaluara integraciones mas reales, siempre que queden claras las restricciones de seguridad, disponibilidad, latencia y mantenimiento.

La estrategia detallada de bloques de datos, fuentes posibles, riesgos y orden recomendado de implementacion queda documentada en `docs/discord-and-server-data-plan.md`.

## Decision 008: consumo frontend progresivo con fallback estatico

El frontend no debe depender de datos dinamicos para renderizar la landing base mientras el proyecto siga en fase fundacional.

Cuando se incorporen endpoints del backend, el consumo debe hacerse con `fetch` y JavaScript simple, priorizando bloques independientes y manteniendo contenido estatico o placeholders visuales si falla una llamada. `GET /health` queda reservado para comprobaciones tecnicas y no debe bloquear el render principal.

La estrategia detallada de prioridades de endpoints, estados de carga, errores y orden de migracion queda en `docs/frontend-data-consumption-plan.md`.

## Decision 009: servidores actuales de HLL como referencia provisional

Mientras no existan datos reales o representativos de HLL Vietnam, la web puede
mostrar un bloque provisional con servidores actuales de Hell Let Loose siempre
que quede claramente etiquetado como referencia temporal.

La primera version de ese bloque debe salir de un payload controlado del backend
Python, no de una integracion directa desde frontend ni de scraping prematuro.
Esto permite fijar campos utiles, preservar el tono del producto y evitar que la
landing dependa de una fuente externa aun no validada.

La estrategia de campos, riesgos, fases y sustitucion futura queda documentada
en `docs/current-hll-servers-source-plan.md`.

## Decision 010: ingesta por snapshots y adaptadores desacoplados

La evolucion desde payloads placeholder hacia datos mas realistas debe hacerse
con una arquitectura de snapshots de servidor, no conectando el frontend a una
fuente externa ni acoplando el backend a una integracion unica desde el inicio.

La unidad tecnica base sera un snapshot con `captured_at` y campos normalizados
como estado, jugadores, capacidad y mapa actual cuando exista. La lectura de
fuente, la normalizacion y la produccion del snapshot deben quedar separadas
para poder sustituir mocks por una fuente publica o consulta tecnica posterior
sin romper el contrato interno.

La estrategia detallada de fuentes, riesgos, fases y limites queda documentada
en `docs/current-hll-data-ingestion-plan.md`.

## Decision 011: modelo de almacenamiento logico antes de fijar tecnologia

Antes de introducir una base de datos concreta, el proyecto debe fijar un
modelo logico minimo para identidad de servidores y snapshots historicos.

La base inicial se apoya en entidades genericas como `game_sources`, `servers`
y `server_snapshots`. Las metricas iniciales deben derivarse primero de esos
snapshots en vez de materializar agregados prematuros. Esto mantiene el diseno
reutilizable para HLL actual y para futuras fuentes mas cercanas a HLL Vietnam.

El modelo base y las preguntas abiertas quedan documentados en
`docs/stats-database-schema-foundation.md`.

## Decision 012: historico de partidas desde CRCON scoreboard JSON

El historico reutilizable para estadisticas por partida y por jugador debe
salir de la capa JSON publica expuesta por los scoreboards CRCON de la
comunidad, no de A2S ni del HTML renderizado de `/games`.

La discovery tecnica confirma que ambos scoreboards sirven una SPA cuya fuente
real de datos usa `baseURL: "/api"` y endpoints como
`/get_scoreboard_maps` y `/get_map_scoreboard`. Esa capa permite obtener listas
de partidas, detalle por `map_id` y metricas por jugador suficientes para una
futura agregacion semanal por servidor.

A2S se mantiene como fuente de estado actual de servidores. El historico de
partidas y rankings debe construirse en una linea separada basada en CRCON. La
discovery detallada queda en `docs/historical-crcon-source-discovery.md`.

## Decision 013: persistencia historica local separada del flujo live

El backend mantiene el estado live de servidores y el historico CRCON en el
mismo SQLite local de desarrollo para no introducir infraestructura prematura,
pero ambas lineas quedan separadas por tablas y contratos distintos.

El flujo live sigue usando `server_snapshots` via A2S. El flujo historico usa
tablas `historical_*` para:

- servidores historicos configurados
- partidas
- mapas
- jugadores
- estadisticas por jugador y partida
- ejecuciones de ingesta

Las claves estables son:

- servidor: `historical_servers.slug`
- partida: `(historical_server_id, external_match_id)`
- jugador: `stable_player_key`
- estadistica por partida: `(historical_match_id, historical_player_id)`

Esto permite bootstrap, refresco incremental e idempotencia sin mezclar
semanticas de estado actual con historico persistido. El modelo detallado queda
en `docs/historical-domain-model.md`.

## Decision 014: despliegue normal simplificado sin servidor #03

El despliegue operativo normal vuelve a quedar reducido a `backend` +
`frontend`. Los servicios `historical-runner` y `rcon-historical-worker` se
mantienen disponibles solo para uso avanzado y explicito mediante el perfil
Compose `advanced`.

Comunidad Hispana #03 deja de formar parte de los targets RCON por defecto
porque ya no es una fuente operativa vigente. El codigo historico, los datos
persistidos, las migraciones y las piezas Elo/MMR no se eliminan; quedan
pausadas operativamente para esta fase y pueden reintroducirse mediante una
task futura si se valida de nuevo la fuente y el coste de mantenimiento.

## Decision 015: historico RCON-first con fallback publico

La politica por defecto para historico vuelve a ser RCON-first:
`HLL_BACKEND_HISTORICAL_DATA_SOURCE=rcon`. El scoreboard publico de CRCON se
mantiene como fallback controlado cuando RCON falla, no tiene cobertura util o
no soporta todavia una operacion competitiva concreta.

La arquitectura historica RCON-first se compone de captura de sesiones RCON,
ingesta de AdminLog, parser de eventos, almacenamiento de eventos/snapshots y
materializacion de partidas y estadisticas por jugador. Los snapshots de perfil
procedentes de `MESSAGE` enriquecen lecturas de jugador, pero no sustituyen los
hechos de partida derivados de eventos RCON.

Comandos operativos manuales:

```powershell
docker compose exec backend python -m app.rcon_admin_log_ingestion --minutes 1440
docker compose exec backend python -m app.rcon_historical_worker capture
```

Esta decision no reactiva Elo/MMR dentro del arranque normal del backend. Las
piezas Elo/MMR, migraciones, datos persistidos y modulos historicos se
conservan, pero su operativa compleja sigue pausada y desacoplada salvo task
explicita.

## Decision 016: catalogo confiable de scoreboards publicos activos

Los origenes publicos de scoreboard que el backend puede exponer o validar se
centralizan en un catalogo explicito de servidores activos. En esta fase solo
son confiables `comunidad-hispana-01`, con origen
`https://scoreboard.comunidadhll.es`, y `comunidad-hispana-02`, con origen
`https://scoreboard.comunidadhll.es:5443`.

`comunidad-hispana-03` no forma parte de ese catalogo ni de los seeds por
defecto nuevos. Los datos historicos ya persistidos no se eliminan, pero las
URLs publicas de partidas solo se aceptan si el `raw_payload_ref` usa HTTP(S),
apunta al origen confiable del servidor activo y mantiene una ruta `/games/`.

## Decision 017: PostgreSQL phase 1 for RCON historical persistence

La primera migracion de persistencia a PostgreSQL cubre el camino que sufria
contencion SQLite entre `backend`, `historical-runner` y
`rcon-historical-worker`:

- captura prospectiva RCON, muestras y ventanas competitivas
- eventos AdminLog deduplicados y snapshots de perfil derivados
- partidas RCON materializadas y estadisticas por jugador
- candidatos confiables de URL de scoreboard que puedan poblarse para
  correlacion de detalle

Docker Compose configura `HLL_BACKEND_DATABASE_URL` y usa PostgreSQL como
backend autoritativo para esas tablas. La ejecucion local sin esa variable sigue
usando SQLite como fallback temporal para preservar comandos y tests locales.

Quedan SQLite-backed en esta fase porque no forman parte del lock-prone writer
path migrado y siguen cubriendo fallback publico o caches locales:

- snapshots live y cache de `/api/servers`
- tablas `historical_*` de scoreboard publico, rankings y correlacion legacy
- snapshots historicos precalculados, ledger player-event y Elo/MMR pausado

La correlacion de URL publica en detalle usa primero candidatos PostgreSQL
confiables cuando existan y puede seguir leyendo filas `historical_*`
persistidas en SQLite durante la transicion. El diagnostico operativo se expone
con `python -m app.storage_diagnostics`.

## Decision 018: PostgreSQL phase 2 for displayed historical data

PostgreSQL pasa a ser la fuente de lectura para los datos historicos visibles:

- fallback publico `historical_*` de partidas, detalle y rankings
- snapshots historicos precalculados que consume `historico.html`
- cache live de servidores que consume `/api/servers`
- ledger player-event usado para reconstruir snapshots visibles
- tablas RCON de AdminLog, perfiles, ventanas, partidas materializadas,
  estadisticas y candidatos seguros ya migradas en phase 1

La migracion se ejecuta de forma idempotente con:

```powershell
cd backend
python -m app.sqlite_to_postgres_migration
python -m app.storage_diagnostics
```

El comando conserva IDs y `external_match_id` del scoreboard publico, claves
`match_key` materializadas y URLs seguras existentes. Copia SQLite y los JSON
historicos de `backend/data/snapshots` como fuentes legacy; no los vuelve a
usar como read model visible cuando `HLL_BACKEND_DATABASE_URL` esta definido.
Las filas legacy de `comunidad-hispana-03` se omiten en el read model visible
de esta migracion para no reactivar ese target.

Permanecen fuera de phase 2:

- checkpoints y runs operativos del import publico que no aparecen en frontend
- Elo/MMR pausado y oculto en la UI actual

`app.storage_diagnostics` muestra conteos PostgreSQL, ultimas partidas
materializadas, ultimos `match_end`, dominios restantes y un resumen de paridad
para verificar la migracion antes de retirar fuentes legacy.
