Registro de decisiones de arquitectura (ADR)¶
Cada decisión incluye qué problema resuelve, por qué se elige, alternativas, ventajas, limitaciones y cómo se ha probado.
ADR-001 · Motor en Python puro, sin dependencias¶
- Problema: la lógica debe ser una librería instalable y jugable sin interfaz (Req 1.1).
- Decisión:
musst.enginesolo usa la biblioteca estándar. - Alternativas: numpy para acelerar la evaluación.
- Ventajas: se instala en cualquier sitio; ningún riesgo de dependencias en el sandbox.
- Limitaciones: Monte Carlo más lento (mitigado con un número de muestras configurable por nivel).
- Pruebas:
tests/unit,tests/property; el CI instala el wheel y juega una partida.
ADR-002 · El motor es la única autoridad¶
- Problema: ni la IA ni el frontend deben poder hacer trampas o romper la partida.
- Decisión: toda acción pasa por
MusGame.validate; las ilegales lanzanIllegalActionsin cambiar el estado. - Alternativas: confiar en que el frontend solo muestre acciones legales.
- Pruebas:
test_accion_ilegal_rechazada_y_estado_intacto,test_en_torneo_el_bot_que_falla_pierde.
ADR-003 · Observaciones por lista blanca¶
- Problema: información privada (Req 5.1.1).
- Decisión:
build_observationcopia campos concretos; nunca se serializaMusGame. - Pruebas:
tests/privacy(60 partidas aleatorias × 4 asientos × cada paso).
ADR-004 · Turnos secuenciales¶
- Problema: en la mesa física se habla a la vez (FEM VI.1, VI.4).
- Decisión: el motor da la palabra de uno en uno; "vale el quiero" se modela dando la palabra al compañero tras un "no quiero".
- Limitación: no se modelan las declaraciones simultáneas de una pareja (imposibles en digital).
ADR-005 · Frontend sin framework ni paso de compilación¶
- Problema: la mesa necesita control fino de la interfaz, pero el equipo debe poder explicarlo todo.
- Decisión: HTML + CSS + JavaScript (módulos ES) servidos por FastAPI. Cambia el diseño inicial (TypeScript + Vite) porque el entorno no tiene Node y un paso de compilación no aportaba lo suficiente.
- Alternativas: Streamlit (menos control visual), React/Vite (requiere Node).
- Limitaciones: sin tipado estático en el cliente.
ADR-006 · Persistencia con sqlite3 + repositorio¶
- Decisión:
sqlite3de la biblioteca estándar detrás deRepository. Cambia el diseño inicial (SQLAlchemy + Alembic) para no añadir dependencias. - Migración a PostgreSQL: implementar la misma interfaz con
psycopg; el esquema SQL es estándar. - Pruebas:
tests/integration/test_api.pyusa una BD en memoria.
ADR-007 · Contraseñas con hashlib.scrypt¶
- Decisión: scrypt (N=2^14, r=8, p=1) con sal aleatoria, de la biblioteca estándar. Cambia el diseño inicial (argon2id) para evitar una dependencia con extensiones C.
- Pruebas:
test_registro_login_hash_y_estadisticascomprueba que no se guarda la contraseña.
ADR-008 · Modelo del rival interpretable (Beta + back-off)¶
- Problema: aprender "hace X cuando ocurre Y" con pocas partidas.
- Decisión: tasas Beta por (métrica, lance, marcador) con encogimiento jerárquico hacia el contexto padre.
- Alternativas: clasificadores o RL (la interfaz
ModelStore/Learnerpermite añadirlos). - Pruebas:
tests/unit/test_player_model.pycon jugadores sintéticos.
ADR-009 · Selección adversa en el agente de probabilidad¶
- Problema: las primeras versiones de los bots basados en agentes perdían contra el bot sencillo: lanzaban órdagos y subidas suponiendo que el rival aceptaría con cualquier mano.
- Decisión: el Monte Carlo estima P(acepta) y P(ganar | acepta) con un modelo de política del rival (umbral de fuerza), pondera los mundos por la verosimilitud de sus envites y aplica una probabilidad mínima de aceptación por la incertidumbre sobre su política.
- Pruebas:
experiments/level_ladder.py.
ADR-010 · Mus duplicado en el torneo¶
- Problema: el azar del reparto domina pocas partidas.
- Decisión: cada seed se juega dos veces intercambiando las parejas de asiento.
- Pruebas:
tests/integration/test_arena.py(reproducibilidad y bot roto).
ADR-011 · Web en GitHub Pages con Python en el navegador (Pyodide)¶
- Problema: la web debe publicarse en GitHub Pages, que solo sirve ficheros estáticos.
- Decisión: el frontend habla con el mismo
/api/..., pero en Pages las peticiones las atiendemusst.web.backenddentro de un Web Worker con Pyodide. La lógica de la API se movió amusst.server.service.MusService, que usan tanto FastAPI como el navegador: no hay dos implementaciones. Los datos (perfil, partidas, estadísticas, modelo del jugador) se guardan en SQLite sobre IndexedDB. - Alternativas: servidor en Render/HF Spaces (arranque en frío, cuentas externas); reescribir el motor en JavaScript (dos motores que mantener).
- Ventajas: gratis, sin servidor, el mismo código Python y las mismas pruebas; los bots piensan en un worker y la mesa no se congela.
- Limitaciones (aceptadas):
- Las cartas de los bots están en la memoria del navegador del jugador. El frontend sigue recibiendo solo la observación de su asiento, pero alguien con conocimientos podría leerlas. Solo se engaña a sí mismo: no hay ranking de humanos en esta versión.
- Perfil local sin contraseña; los datos no salen del navegador.
- Sin compañero humano por invitación ni bots externos en subproceso (requieren servidor).
El torneo con todos los bots se ejecuta en GitHub Actions y se publica en
scoreboard/. - Primera carga: unos 10 MB de Pyodide desde jsDelivr (después, caché).
- Pruebas:
tests/integration/test_web_backend.py; el jobwebdel CI genera el sitio y comprueba que el paquete funciona sin FastAPI; prueba manual en Chrome (partida completa, perfil y estadísticas tras recargar).
ADR-012 · Señas con miradas en lugar de probabilidades fijas¶
- Problema: en la mesa real una seña la ve quien está mirando; una probabilidad fija de "ser visto" no permite elegir el momento.
- Decisión: miradas públicas que cambian al azar con el tiempo; la seña la ven exactamente quienes miran al que la hace. El bot profesional solo la hace si le mira su compañero y ningún rival; el medio, en cuanto le mira el compañero; el sencillo, si le mira cualquiera.
- Pruebas:
tests/unit/test_miradas_y_senas.py(veracidad, ventana, privacidad, reproducibilidad, política de los bots, muestreo condicionado).
ADR-013 · Cuentas en Supabase con GitHub Pages¶
- Problema: cuentas con usuario y contraseña, un administrador y datos compartidos entre dispositivos, sin servidor propio.
- Decisión: Supabase Auth (el usuario se traduce a un correo interno) y Postgres con RLS.
Python sigue jugando y aprendiendo en el navegador;
cloud.jssolo sincroniza: al entrar trae el modelo del jugador y la autocrítica de los bots, y al acabar guarda la partida. Las operaciones de administración son funcionesSECURITY DEFINERque compruebanis_admin(). - Limitaciones: la IA corre en el navegador del jugador (ver ADR-011); un usuario podría enviar datos falsos de sus propias partidas o de la memoria de los bots (no afecta a otros usuarios ni a sus datos, que protege RLS).
- Pruebas:
supabase/tests/test_schema.sqlen un PostgreSQL 16 (jobsupabase-schemadel CI);tests/integration/test_web_backend.py::test_sincronizacion_con_la_nube.
ADR-014 · Autocrítica por calibración, no por resultado¶
- Problema: aprender de las derrotas sin aprender supersticiones (se puede perder jugando bien).
- Decisión: tras cada partida el bot revisa sus decisiones con las manos enseñadas; guarda la calibración (probabilidad estimada frente a resultado real, por lance y tramo) y los errores reales (aceptar perdiendo, rechazar ganando). Corrige sus estimaciones encogiendo hacia la original mientras hay pocos datos.
- Pruebas:
tests/unit/test_autocritica.py.
ADR-015 · Cuentas con contraseña dentro del navegador (GitHub Pages)¶
- Problema: usuarios con contraseña y un administrador usando solo GitHub Pages.
- Decisión: el backend que corre en el navegador usa el mismo
Repositoryque el servidor: contraseñas con hash scrypt (paquetehashlibde Pyodide) en SQLite sobre IndexedDB. La primera cuenta registrada en un navegador es la administradora. El panel de administración usa los mismos casos de uso (MusService.admin_*) en el navegador y en el servidor FastAPI. - Limitaciones (aceptadas): cada navegador tiene sus propias cuentas, y quien controla el equipo puede leer o borrar la base de datos local. Separa a quienes comparten un equipo, pero no es seguridad frente al dueño del equipo. Para cuentas compartidas entre dispositivos está preparada la opción con Supabase (ADR-013), que se activa con dos variables del repositorio sin tocar el código.
- Pruebas:
tests/integration/test_web_backend.py(registro, primer administrador, contraseñas, panel, permisos) ytests/integration/test_api.py::test_administracion_en_el_servidor; prueba manual en Chrome.