# Plataforma Juegoland v2: modelo y contratos de producto **Estado:** propuesta revisada el 6 de octubre de 2026. `games2` implementa únicamente la base de salud, bootstrap y catálogo descrita en el [handoff](HANDOFF-2026-10-06.md); los flujos de este modelo siguen pendientes. Los registros de hallazgos del [5](platform/review-2026-10-05.md) y [6 de octubre](platform/review-2026-10-06.md) conservan defectos, correcciones documentales y comprobaciones pendientes; no certifican un servidor desplegado. ## 1. Alcance y autoridad de los documentos La plataforma organiza identidad, acceso, catálogo, salas, comunicación y operación. El motor ejecuta reglas y proyecta información de partidas. La protección del menor es un requisito transversal: **chat desactivado para menores; para habilitarlo se exige mayoría de edad acreditada y teléfono verificado por SMS**, además de los permisos de sala. El usuario confirma también **juegos propios publicados exclusivamente por el equipo**. Ha encargado desarrollar las demás políticas contrastando referencias; su base concreta está en [políticas de producto](platform/product-policies.md), diferenciada de capacidades ya implementadas. | Documento | Autoridad | | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | Este modelo | Entidades, invariantes, estados, permisos y decisiones de producto. | | [API y eventos](platform/api-and-events.md) | Rutas, sobres, recibos, errores y recuperación de los flujos de plataforma. | | [Modelos de datos compartidos](platform/shared-data-models.md) | Fuente JSON Schema y DTOs TypeScript/Go/Dart; primitivas y núcleo de error comunes, separados de entidades privadas. | | [Protección del menor](platform/child-safety.md) | Elegibilidad, verificación de teléfono/edad, restricciones de contenido, moderación y datos de protección. | | [Políticas de producto](platform/product-policies.md) | Valores de P1–P8, cuentas, admisión, publicación interna, moderación, conservación y requisitos de activación. | | [Arquitectura de tres capas](platform/architecture.md) | API y servidor/motor Go, frontend y adaptación del runtime. | | [Modelo del motor](game-engine-proposal.md) y [formato de juego](game-engine/format-and-protocol.md) | Paquetes, SDK de reglas y protocolo `game.*`; mantienen sus versiones propias. | | [Multidispositivo](game-engine/multi-device-profile.md), [sincronización](game-engine/synchronization-profile.md) y [web/Flutter](platform/client-communication-profile.md) | Extensiones v2 propuestas: proyecciones, mandos, rondas, transporte y renderers; no amplían los esquemas v1 implícitamente. | | [Jugadores virtuales](game-engine/virtual-player-profile.md) | Propiedades/instrucciones por juego, autorización y límites de los controladores IA. | En conflicto, la autorización más restrictiva prevalece mientras se corrige el contrato. Una capacidad del paquete no concede permiso de plataforma. Las propuestas de producto no se convierten en requisitos aprobados solo por aparecer en un ejemplo. OpenAPI, esquemas de plataforma y pruebas de conformidad siguen pendientes; estos documentos no los sustituyen. Las capas fijadas son frontend Svelte, API Go y servidor/motor propio Go. Se propone un monolito modular con PostgreSQL; API y dominio son capas de código que inicialmente comparten proceso y transacción. La producción Node corresponde a `games` v1. El SDK de diseño conservado aquí es TypeScript; la implementación Go exige contratos y equivalencia de reglas según [arquitectura](platform/architecture.md). Una sala organiza miembros y contiene como máximo una partida; una revancha crea otra sala. `roomId` y `matchId` son distintos. No se crean microservicios por cada dominio. ```mermaid flowchart LR UI[Frontend Svelte] --> API[API Go] API --> AUTH[Servidor: sesión y autorización] AUTH --> SAFE[Política de protección] SAFE --> PLATFORM[Catálogo, salas y comunicación] SAFE --> ENGINE[Motor de partidas] PLATFORM --> DB[(PostgreSQL)] ENGINE --> DB DB --> OUT[Outbox y publicación autorizada] OUT --> UI RELEASE[Equipo: publicación de juegos propios] --> ASSETS[Paquetes y recursos inmutables] ASSETS --> UI ``` El lanzamiento inicial excluye espectadores remotos con cuenta propia, mensajes privados, torneos, ranking competitivo, pagos, apuestas, publicación de juegos de terceros y grupos sociales permanentes. Las relaciones privadas de supervisión y contactos autorizados sí forman parte de protección. El [perfil multidispositivo](game-engine/multi-device-profile.md) propone para una fase posterior una pantalla compartida pasiva, vinculada a una sala y sin plaza ni permiso de chat; no equivale a habilitar espectadores. Moderación, bloqueo, reportes y cobertura operativa **son requisitos para habilitar chat y encuentros públicos**, aunque no haya moderación automática. ## 2. Diferencias frente a la versión actual | Área | Código actual | Objetivo v2 | | ------------ | ------------------------------------------------ | --------------------------------------------------------------------------------------------- | | Sala/partida | Objeto `room` y contador compartidos | Agregados y revisiones separados, transacciones coordinadas. | | Invitaciones | Código de sala reutilizable | Token con caducidad, revocación y consumo atómico. | | Tiempo real | Acuses y `room:state` | Contrato versionado, recibos duraderos, barreras y cursores. | | Chat | Array en sala, máximo 1000 mensajes | Flujo paginado, moderación recuperable y acceso condicionado por protección. | | Cuenta | Invitado convertible, PIN y correo sin verificar | Estados de cuenta, recuperación, revocación y comprobaciones independientes de teléfono/edad. | | Catálogo | Registro manual | Proyección de paquetes publicados y política de disponibilidad/idoneidad. | | Operación | Un Node, PostgreSQL y proxy | Outbox, límites, salud diferenciada, restauración y migración compatibles. | Estas diferencias proceden de los módulos históricos `server/service.mjs`, `server/platform.mjs`, `server/auth.mjs` y `shared/room-lifecycle.mjs` de la aplicación anterior. La nueva implementación vive en este proyecto y no importa esos módulos. La comparación no prueba por sí sola un incidente en producción. ## 3. Modelo canónico | Entidad | Identidad y datos | Invariantes | | ------------------- | -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | Principal/cuenta | `userId`, tipo invitado/cuenta, estado, credenciales privadas | El invitado tiene identidad estable. Convertirlo conserva `userId`; iniciar sesión en otra cuenta no fusiona identidades automáticamente. | | Perfil | `profileRevision`, alias, avatar, preferencias | Vista propia distinta de vista pública. No publica correo, teléfono, edad exacta ni clasificación de menor. | | Sesión | Hash de token, principal, expiraciones, revocación y versión de autorización | Una cookie no es evidencia de aceptación vigente ni de permiso de chat. | | Protección | `safetyRevision`, comprobaciones de edad/teléfono, restricciones | Privada. Autoridad única para juego, comunicación, descubrimiento y publicación de contenido. | | Aceptación | Principal o contexto anónimo, versión y fecha, aceptación/revocación | Registro de hechos; no se borra el hecho de aceptar al revocar. No equivale a consentimiento parental o verificación de edad. | | Publicación | `gameId`, versión, digests, disponibilidad y evaluación de contenido | El contenido es inmutable; la política de distribución puede cambiar sin alterar el paquete. | | Sala | `roomId`, revisión, anfitrión, capacidad, visibilidad, configuración y política fijada | A lo sumo un `matchId`. No incorpora estado privado del juego. | | Membresía | `membershipId`, `userId`, fecha de entrada, estado y asiento | Una membresía activa por principal/sala. `membershipId` no cambia si se reorganizan plazas en espera. | | Instancia virtual (propuesta) | Identificador interno, sala/partida, asiento, perfil/digest y dificultad del agente | Solo existe si el paquete declara el perfil; no es cuenta humana ni obtiene permisos sociales. Su persistencia y API están pendientes. | | Invitación | `invitationId`, revisión, sala, hash de token, vencimiento y usos | No es membresía ni habilitación de chat. Token y UUID de sala son diferentes. | | Acceso QR (propuesta) | Finalidad, sala/paquete o preparación, generación, hash y vencimiento | En mesa `screen-qr` sustituye la invitación; un QR de mando personal solo continúa la admisión convencional y vincula dispositivos. | | Vínculo de pantalla (propuesta) | Dispositivo, sala, alcance común/personal, propietario, generación y revocación | Sin cuenta ni plaza; proyección pública. La TV personal depende además de la membresía propietaria. | | Ronda temporal (propuesta) | Perfil fijado, época de reloj, generación, plazos, fuentes e ingresos duraderos | El orden de commit no determina reacción; recogida/resolución y salida neutral siguen reglas publicadas. | | Partida | `matchId`, asientos `p0…`, versiones y estado del motor | El vínculo asiento-cuenta pertenece a plataforma. El perfil virtual propuesto añade un vínculo a instancia de agente; ambos quedan fijados al iniciar. | | Mensaje/chat | `messageId`, autor, texto/estado y secuencia de creación; flujo de eventos | Crear y ocultar un mensaje son eventos distintos y ordenados. | | Resultado | `matchId`, participantes, finalización o cancelación | Un resultado por partida; estadísticas reconstruibles y con procedencia. | | Notificación | `notificationId`, destinatario, destino, lectura y revisión de bandeja | Dedupe incluye destinatario. Nunca copia chat ni datos ocultos del juego. | | Reporte/restricción | Caso, sujeto/recurso, estado, motivo y auditoría de acceso | Permisos de moderación separados de jugar o ser anfitrión. | | Outbox | Evento, agregado, revisión, destinatario/proyección y entrega | Garantía de publicación recuperable; no sustituye un registro de auditoría. | | Auditoría | Actor, operación, motivo, fecha y acceso restringido | Retención y permisos propios; evita payloads privados por defecto. | UUID para entidades de plataforma y partidas. Excepciones: `gameId`/`packageId` son identificadores del formato de paquetes, los asientos del motor son `p0…`, las versiones son las del contrato del motor y los digests son SHA-256. Fechas UTC con `Z`; contadores enteros seguros JSON no negativos, sin reutilización ni vuelta a cero dentro de un agregado. El servidor deriva actor, asiento, rol, fecha y resultado. Un cliente puede identificar un **destinatario** en una operación autorizada, por ejemplo `targetMembershipId` al transferir anfitrión; eso no le permite elegir el actor ejecutor. ## 4. Ciclo de sala y partida | Estado de sala | Significado | Operaciones | | -------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------- | | `waiting` | Sin partida todavía | Entrar/salir, configurar, listo, colores, cancelar; invitar solo en entrada convencional; chat solo si el principal es elegible. | | `active` | Partida activa asociada | Reglas, retirada y cancelación por política; chat elegible. | | `completed` | Partida completada con resultado | Lectura autorizada e historial; chat de solo lectura para adultos elegibles. | | `cancelled` | Cancelada antes o durante el juego | Motivo y lectura autorizada; no victorias/derrotas inventadas. | Estados terminales no se reabren. Al crear se fijan paquete, dependencias y política de sala. El cliente indica el digest que eligió; si ya no se puede crear con él se rechaza, en lugar de cambiarle las reglas. Antes del inicio se puede cambiar capacidad/configuración admitida por el paquete, pero no el juego o su versión; eso exige otra sala. El [perfil multidispositivo propuesto](game-engine/multi-device-profile.md) fija también `modeId` y `entryMethod: standard|screen-qr`. Cada jugador selecciona una presentación compatible `TVBoard`, `Desktop` o `Mobil`; esa elección no modifica admisión ni reglas. La mesa presencial `TVBoard` se crea con pantalla común y entrada QR, sin invitaciones. En una sala convencional, una invitación introducida en Smart TV puede enlazar TV personal y móvil mando; ese recorrido exige poder pasar a móvil completo conservando la plaza. El [perfil temporal propuesto](game-engine/synchronization-profile.md) fija reloj, rondas, fuente de estímulo y punto seguro de cambio, con empates/neutralización cuando no se puede establecer una comparación válida. La capacidad debe cumplir `players.min ≤ capacity ≤ players.max`; un paquete de 1–4 jugadores también puede iniciar una sala de uno. La base P1 exige todas las plazas configuradas ocupadas y todos listos. Cada entrada, salida o cambio de configuración invalida los «listo» anteriores; un cambio de color invalida al menos el del afectado. Las asignaciones se validan con el paquete. El [perfil de jugadores virtuales](game-engine/virtual-player-profile.md), todavía pendiente de contratos ejecutables, amplía las propiedades del juego con perfiles IA e instrucciones incluidas. Humanos y agentes cuentan como plazas; las membresías humanas conservan sus permisos y las instancias virtuales solo pueden actuar en su asiento. Configurar IA exige autorización en espera y confirmación de los humanos mediante «listo»; la plataforma comprueba disponibilidad de cada instancia al iniciar. Ninguna IA acredita edad, supervisa menores ni sustituye al anfitrión responsable. No se reemplaza a un participante desconectado o retirado durante la partida. La admisión de práctica contra IA se definirá expresamente antes de habilitarla. Inicio atómico: comprobar permisos para jugar, recursos/versiones disponibles y condiciones de inicio; fijar asientos/configuración; crear partida y posición inicial; guardar revisiones y publicaciones. Un fallo de `setup` revierte toda la operación, incluido el último «listo». La revisión inicial de partida es 0. La sala aumenta su revisión una vez por transacción con cambio efectivo. Salida del anfitrión en espera: P2 transfiere al miembro elegible restante con menor `joinedAt`, desempate por `membershipId`, o cancela si no queda elegible. En salas protegidas solo puede ser anfitrión un responsable adulto autorizado. La transferencia revoca las invitaciones activas del anterior anfitrión. Cancelar deliberadamente y salir son acciones distintas. En partida el anfitrión no cambia reglas ni expulsa a un rival. En una mesa `screen-qr`, la transferencia invalida su acceso QR y exige reautorizar la pantalla común y emitir otra generación antes de nuevas altas. No crea invitaciones como sustitución. Las TVs personales mantienen su vínculo solo mientras siga vigente la autorización propia y la de protección. Una retirada ordinaria usa `onLifecycle(participant.withdrawn)` del motor. La plataforma no decide que el último jugador gana. Excepción de protección explícita: la salida voluntaria del anfitrión responsable en una partida protegida activa cancela mediante el servicio de núcleo sin resultado competitivo, igual que una revocación de tutela/contacto que impida continuar. Ese responsable ocupa una plaza; supervisión sin plaza no está incluida en el primer perfil. Un participante retirado no se reemplaza ni reingresa; conserva solo las proyecciones históricas que autoricen reglas y protección. Una salida en espera revoca la membresía; una entrada posterior crea otra membresía. Cerrar pestaña o perder conexión nunca es una retirada. ### Concurrencia y enlace con el motor Una mutación de sala exige revisión de sala; **retirada y cierre en partida exigen además revisión de partida**. Una jugada concurrente invalida la confirmación obsoleta de salida/cierre. Chat, presencia y lectura de notificaciones no cambian revisiones de sala o juego. Un no-op y un rechazo tampoco las incrementan. Toda escritura sobre una sala, incluida `game.command`, entra por la misma unidad de trabajo: contexto de autorización coherente, bloqueo de sala, partida si existe, invitación/chat y demás registros en orden fijo. La revocación de autorización se serializa con esas operaciones; una operación posterior a la revocación confirmada no se admite. No se hace red ni se espera al usuario dentro de la transacción. El orden debe ser idéntico en cron, HTTP y sockets para reducir interbloqueos; PostgreSQL documenta esta [disciplina de bloqueo](https://www.postgresql.org/docs/current/explicit-locking.html). Resultado terminal, estado de sala, estado del motor, recibo, plazos y outbox se confirman juntos. Una acción puede cambiar la partida sin modificar la sala; al finalizar o retirarse alguien cambia también la sala. El adaptador de persistencia del motor debe participar en esta misma transacción, no abrir otra que confirme por separado. La cancelación de plataforma requiere un servicio interno del núcleo que cierre la posición y los plazos de forma válida; no se añade de forma implícita un evento nuevo al SDK existente. Los mensajes de ambos flujos pueden llegar en distinto orden. El cliente puede ver temporalmente sala activa y partida terminada sin que la base esté corrupta. Usa `matchRef.minimumRevision` como barrera de coordinación y sincroniza el flujo atrasado; nunca modifica resultados para hacerlos coincidir. ### Inactividad y fallos operativos La política se fija con versión y plazos al crear la sala. P3 adopta 10 minutos sin progreso para cierre solicitado y 24 horas para automático en el perfil casual; espera pública 30 minutos y privada 24 horas desde creación. La publicación de cada juego comprueba su adecuación y fija perfil. Se excluyen incidentes de servicio confirmados y se reconcilian plazos antes de reanudar cierres, como especifican las [políticas](platform/product-policies.md#5-salas-invitaciones-y-continuidad--p1p4). En espera, `expiresAt` se calcula desde la creación; chat/presencia no lo prolongan. En partida, `lastProgressAt` cambia solo con transiciones efectivas del motor, no por chat, consultas, errores o conexiones. Para cierre solicitado, el núcleo comprueba bajo bloqueo que el solicitante activo no está habilitado en `flow.actors` y que queda al menos otro actor habilitado; una fase automática no habilita ese cierre. Solo se publica al solicitante la capacidad de cerrar, nunca actores o etapas privados. Juegos con otra semántica requieren una política explícita, sin consultar campos particulares como `state.turn`. Un trabajo vencido revalida fecha, generación, revisión y estado bajo bloqueo. Si una jugada se confirmó antes, no aplica un cierre antiguo. Los plazos propios del juego pertenecen al motor. Falta de módulo, corrupción o indisponibilidad se expresan como estado operativo `blocked` separado del resultado; no convierten una avería en derrota. ## 5. Cuenta, sesión y autorización Tipo de identidad, rol en sala y protección son ejes independientes. Un invitado puede ser anfitrión de su juego individual; el perfil inicial reserva multijugador a adultos acreditados o cuentas tuteladas con admisión protegida. Los permisos se calculan por principal + estado de cuenta + aceptación + protección + recurso + rol/fase, nunca por una columna «cuenta» que anule otra de «anfitrión». | Operación | Condición mínima | | ----------------------------- | ------------------------------------------------------------------------------------------------ | | Catálogo/resumen de sala | Publicable y apto para el perfil de protección; no incluye personas menores identificables. | | Crear/entrar/jugar | Sesión válida, aceptación vigente, cuenta habilitada, juego y entorno permitidos. | | Vista de sala/partida | Membresía actual o participación histórica autorizada; proyección por destinatario. | | Chat, incluido historial | Mayoría de edad acreditada + teléfono SMS vigente + sin restricción + autorización de sala. | | Configurar/transferir/invitar | Anfitrión en espera y política de protección; la invitación no permite saltar estas condiciones. | | Consultar recibo de salida | El principal que lo emitió, aunque ya no sea miembro; solo resultado mínimo de su operación. | | Moderar/publicar paquetes | Permisos administrativos específicos, auditoría y acceso mínimo; ser anfitrión no los concede. | Estados de cuenta: `pending-email`, `active`, `restricted`, `deletion-pending`, `deleted`. Cuentas infantiles creadas por responsable usan el canal verificado de este y su flujo de acceso propio. Las restricciones son capacidades concretas, por ejemplo no comunicar o no crear salas. El bloqueo total revoca sesiones. La eliminación no hace desaparecer asientos de partidas de otros usuarios: desvincula/seudonimiza lo necesario según política, sin presentar esa seudonimización como anonimato. Conversión invitado-cuenta conserva identidad después de acreditar la nueva credencial, evita correos duplicados con restricción única y rota/revoca sesiones. Iniciar sesión en una cuenta existente conserva dos identidades separadas hasta un eventual flujo explícito de vinculación; nunca se apropian partidas solo por escribir el mismo correo. Sesiones web: token opaco, hash servidor, cookie `HttpOnly; Secure; SameSite=Lax; Path=/` sin dominio compartido; expiración absoluta e inactiva impuestas en servidor. El [perfil nativo futuro](platform/client-communication-profile.md#3-autenticación-web-y-nativa) usa otro adaptador de credencial emitida por la misma plataforma, con iguales límites y permisos. [P6](platform/product-policies.md#3-identidad-credenciales-y-recuperación--p6) define credenciales, recuperación y plazos por tipo de sesión. Login/recuperación, cambio de credencial/teléfono, suspensión, logout y revocación de condiciones tienen efectos explícitos sobre sesiones y sockets. Los latidos no prolongan indefinidamente una sesión. Véase la referencia de [gestión de sesiones OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html). Aceptar condiciones, acreditar edad, verificar teléfono y obtener autorización parental son registros distintos. Revocar condiciones invalida las capacidades protegidas en todas las sesiones, pero mantiene disponibles logout, nueva aceptación, gestión de privacidad y reporte de seguridad. Una aceptación anónima se vincula a un contexto de navegador; al crear identidad se registra la atribución, sin tomar cualquier cookie como consentimiento del titular. La política de [protección](platform/child-safety.md) revalida acceso en peticiones, suscripción y antes de publicar. Una restricción cancela suscripciones afectadas y ordena eliminar cachés privadas. Los datos ya vistos no pueden retirarse de la memoria de una persona: se garantiza impedir nuevas entregas y retirar copias controladas por la aplicación. ## 6. Invitaciones y admisión Las reglas de invitación siguientes corresponden a `entryMethod: standard`. La mesa presencial `screen-qr` usa un acceso de finalidad propia visible en TV y rechaza emisión/consumo de invitaciones. Preparar la mesa en TV no crea una plaza: el anfitrión autorizado confirma desde su móvil la creación, membresía, vínculo común y QR de entrada atómicos. Todos los escaneos revalidan admisión, capacidad, contactos y tutela; la presencia física o el QR no concede esos permisos. En acceso convencional desde Smart TV, introducir la invitación prepara un QR personal de mando sin consumirla. La confirmación autenticada desde móvil admite o recupera una única membresía y vincula su TV; los reintentos no duplican plaza/usos. Ese QR no sirve para admitir a otras personas. «Jugar solo en el móvil» cambia a `Mobil` mediante barrera de sincronización y revoca la TV personal, sin otra invitación. Véanse las [rutas y finalidades propuestas](platform/api-and-events.md#preparación-tvboard-qr-de-entrada-y-enlace-de-mando-propuesta-v2). Token aleatorio de al menos 128 bits; hash para validación, fecha, revisión, revocación y `maxUses`. P4 fija 24 h y un uso por defecto, máximo siete días y siempre limitada por vida de sala y plazas disponibles. Cuotas en las políticas. Admitir a una persona consume un uso en la misma transacción que crea su membresía; reintentar o resolver la invitación no consume usos. Entrar ya siendo miembro no ocupa otra plaza. No se reutiliza un token revocado. Capacidad, prohibiciones entre personas, protección, anfitrión y token se revalidan bajo bloqueo. Una invitación es una capacidad para solicitar entrada, no consentimiento parental, identidad verificada ni permiso para chat. Al empezar/terminar/cancelar se deshabilitan nuevas entradas. Una transferencia de anfitrión sigue P2. Los enlaces no llevan tokens al registro de acceso: la propuesta usa fragmento en la página de invitación y resolución por POST con cuerpo excluido de logs, `no-store` y `no-referrer`. No se cargan analíticas o terceros en esa pantalla. Un token inválido devuelve un error genérico; razones detalladas de revocación se reservan al propietario autorizado. Mostrar el token una vez y guardar solo su hash impedía recuperar una respuesta perdida. Se corrige con una copia cifrada de la respuesta secreta, asociada al principal/comando y con ventana de recuperación de 24 h; fuera de esa ventana solo se revela el identificador, y el anfitrión puede rotar mediante una intención nueva. El hash permanente no se sustituye por texto claro. Esto no habilita recuperar tokens de otra cuenta ni renovar su vigencia. ## 7. Catálogo, publicación y medios Solo el equipo incorpora y publica juegos propios mediante el flujo interno de [publicación](platform/product-policies.md#8-publicación-interna-de-juegos). No hay publicadores externos ni endpoint de subida de juegos para usuarios. La validación del paquete del motor se mantiene: archivos, rutas, dependencias, hashes, licencias, recursos y pruebas. La plataforma añade revisión de idoneidad de contenido, idioma, accesibilidad y exposición de texto/medios de usuarios. No se consideran secretos los archivos públicos de un paquete; reglas privadas tienen `url: null` en el lock. En el alcance inicial solo se publican en distribución pública recursos revisados y aptos para menores. Ocultar una ficha por edad no protege archivos accesibles por URL. Incorporar contenido restringido por edad exigiría otro perfil de distribución con autorización real y caché adecuado; no se habilita mediante una simple etiqueta del catálogo. `published` permite crear; `retired` impide nuevas salas y permite completar las ya fijadas si los permisos siguen vigentes. Metadatos y recursos antes públicos no pasan a ser privados por retirar la versión. `quarantined` bloquea nuevas ejecuciones por incidencia; las partidas afectadas quedan operativamente bloqueadas hasta una resolución explícita, sin sustitución silenciosa de reglas. La plataforma nunca sirve una distribución privada de reglas como recurso del navegador. Avatares subidos son un flujo independiente: límite de bytes/píxeles, formatos permitidos, decodificación real, eliminación de metadatos, nueva codificación y revisión antes de publicar. No se aceptan URLs remotas arbitrarias ni SVG/HTML de usuarios. El cliente referencia un `mediaId` propio en estado `ready`; menores y edad desconocida usan presets aprobados. El contenido publicado queda sujeto a retirada/moderación aunque su URL haya estado cacheada. ## 8. Chat, presencia y avisos Menores y edad desconocida no pueden enviar, leer, suscribirse, recuperar ni recibir fragmentos o audio del chat. Verificar SMS nunca habilita por sí solo la comunicación. La base de políticas desactiva comunicación para todos en salas protegidas/mixtas; nombres, avatares e invitaciones visibles a menores usan proyecciones seguras. El tipo de admisión/comunicación se fija al crear la sala y no se eleva al salir un menor. El chat usa texto plano y no adjuntos en esta versión. Cada creación y cada ocultación produce un evento con secuencia nueva; una ocultación mantiene `messageId` y sustituye su texto por una lápida. La sincronización entrega una cola reciente acotada; el historial se pagina aparte. La revisión de moderación impide que una página antigua resucite texto ocultado. La membresía fija la secuencia de entrada; no da acceso a chat anterior. Salir en espera elimina acceso al chat de esa membresía. En una retirada en partida, si el adulto sigue siendo elegible, su lectura queda limitada al intervalo entre admisión y retirada, con redacciones posteriores aplicadas; no recibe el flujo vivo. El chat terminal es de solo lectura; mensajes se purgan a los 30 días desde su creación según P5. El acceso histórico también se revoca si cambia la protección. Presencia es por sala y principal, agregada entre pestañas, sin autoridad sobre abandono, listo o plazos. No incluye menores/edad desconocida en listados públicos. Una observación caducada se muestra como desconocida; los valores de TTL/latido figuran en el contrato de red. Avisos personales se derivan de cambios confirmados. La clave de deduplicación incluye destinatario, tipo, recurso y transición semántica: «te toca» al pasar a poder actuar, no en cada revisión ajena. Cada cambio de lectura también incrementa `inboxRevision` para sincronizar pestañas. El aviso no contiene texto de chat, teléfonos, edad ni información secreta; abrir el destino vuelve a autorizar. Correo/push de actividad y marketing están deshabilitados inicialmente; correo transaccional de cuenta/seguridad tiene finalidad propia. `lobby.changed` solo se genera por cambios en la proyección pública, nunca por actividad de salas privadas. ## 9. Datos, moderación y resultados El resultado registrado es inmutable para operaciones ordinarias; una corrección administrativa excepcional añade una enmienda auditable y reconstruye estadísticas, sin reescribir silenciosamente el historial. El resultado público se obtiene por proyección: no copia métricas/equipos privados del motor. Cancelaciones no cuentan como victoria/derrota; resultados `neutral` y cooperativos se conservan correctamente. La [matriz de conservación P8](platform/product-policies.md#9-datos-conservación-y-borrado--p8) fija plazos, purga y excepciones por categoría. «Inmutable» no significa conservar identificadores personales para siempre. Las purgas se propagan a cachés y medios; una restauración reaplica marcas de borrado/restricción antes de abrir tráfico. Faltan ejecución, responsables y validación de las bases por finalidad antes de producción. Reportes y bloqueos están descritos en [protección](platform/child-safety.md). Son accesibles aunque el usuario no tenga chat, ni SMS, o haya revocado condiciones. La consola restringida necesita responsables, estados, tiempos de atención, apelación y auditoría antes de habilitar comunicación. Un botón sin personal/procedimiento no satisface este requisito. ## 10. Operación, rendimiento y experiencia PostgreSQL confirma cambios y outbox juntos. El publicador entrega al menos una vez; los clientes deduplican y detectan pérdidas por revisión/cursor. Socket.IO preserva orden de mensajes que llegan, pero no garantiza por defecto recuperar todos los perdidos: la recuperación es obligación de nuestra aplicación ([documentación oficial](https://socket.io/docs/v4/delivery-guarantees/)). La outbox conserva la proyección de su revisión, no recompone un estado nuevo etiquetándolo como antiguo. Antes de enviar se vuelven a aplicar permisos y redacciones vigentes. Se acotan mensajes, páginas, conexiones, suscripciones y buffers. Un cliente lento recibe exigencia de resincronización o desconexión recuperable; nunca hace crecer memoria sin límite. El almacén de archivos sigue siendo solo de desarrollo. Varias instancias requieren coordinación de autorización, locks, outbox y fanout; si se mantiene long-polling, también su estrategia de afinidad. HTTP mutante del perfil web exige origen permitido y CSRF vinculado a sesión/contexto; su binding Socket.IO verifica ambos en handshake. El WSS propuesto para web/nativo usa un ticket ligado a desafío/conexión y adaptador de sesión, con validación de origen web y autorización común; omitir `Origin` no valida una cookie ni acredita una app. La IP reenviada solo se confía a proxies configurados. Logs excluyen tokens, teléfono/OTP, texto del chat, pruebas de edad y estado privado. CSP, carga de medios, redacción de logs y límites de SMS se comprueban por entorno. `/live` comprueba proceso; `/ready` comprueba base de datos, migración compatible y recursos imprescindibles. Al drenar se deja de admitir trabajo, se completan transacciones y se conserva la outbox; el reinicio no ejecuta de nuevo reglas confirmadas. Proxy, backend, frontend/SSR, PostgreSQL y verificador externo tienen métricas separadas. P8 propone RPO ≤5 min, RTO ≤60 min, copia diaria + WAL continuo, ventana de 35 días y simulacro mensual; deben configurarse, medirse y asignarse responsables antes del despliegue v2. Objetivos propuestos: disponibilidad 99,9 % mensual medida desde fuera del proxy; p95 de operaciones dentro del servidor <500 ms para lectura y <1 s para recibo; tras 30 s de corte, sincronización <5 s desde restablecer red. Son objetivos, no resultados observados. Antes de aceptarlos se fija carga reproducible, concurrencia, volumen, hardware y criterios de error; la latencia del SMS se mide aparte. El [perfil de capacidad](platform/capacity-and-scaling.md) propone ensayar 100.000 personas jugando simultáneamente **repartidas entre salas**. Define conexiones adicionales, trabajo por acción, latidos, fanout y fallos que deben medirse. No es un resultado actual ni implica que una sala admita 100.000 participantes. La interfaz mantiene navegación a 320 CSS px, zoom, orientación, teclado virtual y diálogos dentro del viewport. El tablero puede necesitar navegación bidimensional accesible, pero no estrechar toda la página. Objetivo [WCAG 2.2 AA](https://www.w3.org/TR/WCAG22/), con teclado, foco, alternativa textual, movimiento reducido y control de sonido. Los avisos para menores usan lenguaje comprensible, sin inducir a aportar teléfono/edad/documentos para desbloquear una función prohibida. Mensajes localizables y datos UTC, sin reglas basadas en textos. La [política de idiomas](platform/localization.md) desarrolla el alcance multilingüe; la identidad visual y el sistema de tokens quedan pendientes de definición. ## 11. Decisiones confirmadas y base de políticas Confirmado por el usuario: protección transversal; chat desactivado para menores; SMS obligatorio para cualquier persona habilitada para chat; solo juegos propios y publicación interna; tres capas con API y servidor/motor propio Go. También quedan fijados jugadores virtuales declarados por juego con sus instrucciones, `TVBoard`/`Desktop`/`Mobil`, mesa presencial por QR sin invitaciones, TV personal tras invitación con paso a móvil completo y futura app Flutter **solo de controles**. Tableros y vistas completas siguen en la web; desde Flutter el paso a móvil completo abre `Mobil` web conservando membresía/recibos y transfiriendo el mando. Requisito técnico derivado: edad desconocida no cuenta como adulta; SMS acredita posesión de número, no edad o identidad civil. Las estructuras v2, el mando con generación y los algoritmos temporales son propuestas técnicas, pendientes de esquemas y ensayos; no equivalen a implementación ni garantizan orden humano exacto bajo cualquier lag. | ID | Base desarrollada en políticas | Dependencia real antes de activar | | --- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | | P1 | Inicio automático con capacidad llena y todos listos. | Concurrencia/configuración probadas por juego. | | P2 | Transferencia a miembro elegible más antiguo y revocación de invitaciones anteriores. | Migración de comportamiento v1 y permisos de salas protegidas. | | P3 | Espera 30 min pública/24 h privada; inactividad casual 10 min/24 h, excluyendo incidentes confirmados. | Perfil validado por juego y reconciliación de tareas. | | P4 | Invitación 24 h, un uso por defecto, máximo 7 días acotado a sala; cuotas y contactos autorizados. | Implementación atómica de admisión y protección. | | P5 | Chat solo adulto en sala adulta, solo lectura al terminar; mensajes 30 días. | Moderación, cobertura, SMS y purga comprobados. | | P6 | Correo/contraseña robusta, recuperación, sesiones acotadas, MFA administrativo; sin PIN remoto. | Proveedor de correo, contratos ejecutables y migración segura. | | P7 | Perfil España; propuesta de cuentas tuteladas 6–17 y alta autónoma adulta; prueba de edad separada de tutela/SMS. | Verificador, procedimiento de tutela, revisión territorial y pruebas de accesibilidad/corrección. | | P8 | Matriz de datos, objetivos RPO/RTO/carga, presupuesto SMS limitado y responsabilidades. | Recursos/personas/proveedores configurados, costes autorizados y ensayos medidos. | Hasta implementar y validar las dependencias de P7, la modalidad segura no habilita cuentas infantiles sociales ni comunicación por suposición. No hay un interruptor de interfaz que convierta una declaración en mayoría de edad acreditada. Las referencias de protección sirven para diseño; no se afirma conformidad legal sin determinar ámbito y revisar la implementación. ## 12. Migración y aceptación 1. Resolver políticas bloqueantes y amenazas de privacidad/abuso; producir esquemas de plataforma, OpenAPI y casos de conformidad con una única fuente de tipos. 2. Migrar con mapeo duradero `oldRoomId → roomId + matchId`, conservando versiones de reglas y participantes. No ejecutar de nuevo `setup` sobre partidas existentes. 3. API v1 y v2 escriben a través de la misma autoridad/transacción; no hay doble escritura independiente. Invitaciones antiguas pasan por autorización v2; clientes antiguos sin protección suficiente deben actualizar antes de comunicar. 4. Ensayar migraciones de expansión/contracción con copia anonimizada: datos activos, historial, privacidad, chat y consentimientos. El rollback de código requiere esquema compatible; nunca deshace a ciegas una migración destructiva. 5. Migrar juegos gradualmente y retirar adaptador solo con criterio de uso/capacidad de recuperación, no por fecha supuesta. | Caso de aceptación | Resultado obligatorio | | -------------------------------------------------------- | ------------------------------------------------------------------------------------- | | Dos entradas para la última plaza o invitación de un uso | Una admisión; consumo y membresía atómicos. | | Listo frente a cambio de configuración/salida | Sin inicio con aceptación obsoleta; una sola partida. | | Jugada frente a retirada/cierre | Se revalida también la revisión de partida; resultado coherente. | | Commit seguido de caída antes del ack | Mismo recibo y una sola aplicación al reintentar. | | Consulta del recibo tras salir | El autor recupera el resultado sin reabrir acceso a sala/chat. | | Moderación frente a página de historial retrasada | El texto oculto no reaparece; nueva secuencia/revisión. | | Revocación de teléfono/edad/permiso en otra pestaña | Cese de entregas, cierre de suscripción y eliminación de caché controlada. | | Menor llama HTTP/WS o recupera historial/avisos | Ningún contenido de chat; incluye clientes v1. | | Rotación de invitación con respuesta perdida | Recuperación acotada del mismo secreto o rotación explícita, sin duplicar invitación. | | Flujos sala/partida llegan en orden inverso | Recuperación con barrera, sin falso error de corrupción. | | Reinicio, cliente lento o pérdida del último evento | Buffers limitados, cursores y resincronización verificables. | | Restauración de copia anterior a un borrado/restricción | Se reaplican marcas antes de servir información. | Las pruebas anteriores son requisitos pendientes de implementación. La comprobación actual de estos Markdown verifica enlaces, estructura y ejemplos, no comportamiento del servicio.