On this page 00 — Day brief · 2026-08-15 · RONDA 0%

00-day-brief.md

00 — Day brief · 2026-08-15 · RONDA

Decisiones ALS-2 BRIEF

CampoValor
Fecha2026-08-15
NombreRONDA
ComplejidadNivel 2 (no L1 consecutivo tras DERIVA)
SectorGaming / comunidad de mesa local
TipoFeed de mesas + RSVP + inbox HOST JWT
PlataformaWeb responsive (feed + dock · desktop anfitrión)
RegistroR-HY — flyer de caja de juego (clásico) + dock flotante (innovador usable)
ShellS-DOCK — dock flotante Hoy · Mesas · Publicar · Inbox · Yo
HomeH-FEED — cronología de mesas de esta noche / esta semana
FlujoF-BOOK — asientos de una mesa; solicitud ≠ plaza
Por qué no DERIVANo S-SPLIT / H-MAP / F-MAP; no turismo; no plano
Por qué no PIZARRANo S-CMD / H-SEARCH / F-SEARCH; no catálogo municipal

Terna unicidad

R-HY · S-DOCK · H-FEED · F-BOOK

Cumple memory §6.5: 0 códigos iguales a N−1 (DERIVA) y ≥2 distintos vs N−2 (PIZARRA). Ventana de 4 días ya tenía R-IN y R-CL; hoy R-HY. Experimento §8: feed, no mapa ni search.

Directivas aplicadas

  • D-P0-01…09, D-P1-01 (≥12 UX), D-P0-08 (≥10 UI), D-P1-05 JWT HOST
  • Tipografía Zilla Slab + Mulish (evitar Lora, Karla, Newsreader, Atkinson, Outfit, Archivo, Fraunces)
  • Paleta signage — blanco / tinta / amarillo cromo / felt / liner. No dusk, no chalky, no slate/teal, no limestone, no wine
  • Home no es hero 2-col ni mapa (AP-12). Superficie = feed + dock

Alcance L2 must

  1. Home feed cronológico de 5 mesas (foto, juego, hora, local, asientos)
  2. Ficha de mesa (reglas cortas, anfitrión, aforo, punto de encuentro)
  3. Formulario público RSVP (solicitud ≠ plaza confirmada)
  4. Success con código RD-MMDD-NNN
  5. Login + inbox HOST JWT (NEW → CONTACTED → SEATED / WAITLIST / CANCELLED)
  6. Publicar mesa (HOST)
  7. Empty / loading / error / mobile feed + dock
  8. ≥12 UX + ≥10 UI Paper

Mood visual (Paper)

  • Candidatos: taberna candlelit (primer instinto), felt casino, phosphor arcade, signage, alpine
  • Elegido: signage — no el primer instinto taberna (cerca de DERIVA dusk / PLIEGO wine)
  • Paleta: #FFFFFF suelo · #111111 tinta · #F5C400 cromo · #1F4D3A felt · #FFF6C2 liner · #E24B2A ficha
  • Tipo: Zilla Slab 56/36/24 display · Mulish 16/14 UI

Supuestos

  • S1: Un barrio tipo Malasaña–Lavapiés sostiene 5–12 mesas públicas por semana (rango operativo).
  • S2: El jugador llega con “qué se juega esta noche”, no a comprar un juego.
  • S3: El anfitrión necesita cola con estado, no un Discord ni un marketplace.
  • S4: La plaza se confirma en el local; RONDA no cobra.

Hipótesis

IDHipótesisSeñal
H1El feed reduce abandono vs listado de eventos o grupo de WhatsApp% sesiones con tap en mesa
H2Copy “solicitud, no plaza” baja sillas fantasma↓ “pensé que tenía silla”
H3Inbox SEATED nunca supera seatsinvariante de aforo

00-paper-reference.md

Paper reference · RONDA

URLhttps://app.paper.design/file/01M023RM2TZ49CYM2FN1FRRGNY
File ID01M023RM2TZ49CYM2FN1FRRGNY

UX-count: 12 UI-count: 11

UX-00 Cover · UX-01 Stakeholders · UX-02 Personas · UX-03 JTBD · UX-04 Stories · UX-05 Journey · UX-06 Blueprint · UX-07 Site map · UX-08 Flujos · UX-09 Datos+permisos · UX-10 Métricas · UX-11 Research UI-00 Tokens · UI-01 Feed Hoy · UI-02 Ficha mesa · UI-03 RSVP · UI-04 Success · UI-05 Login · UI-06 Inbox HOST · UI-07 Empty · UI-08 Error · UI-09 Loading · UI-10 Mobile feed

UX process (prefijo UX- · mínimo 12)

IDNombreContenido
UX-00CoverPortada RONDA · eslogan «Esta noche hay mesa.» · terna R-HY · S-DOCK · H-FEED · F-BOOK · L2 gaming de mesa local · mood signage · fecha 2026-08-15
UX-01StakeholdersPLAYER anónimo, HOST, local (café/bar/librería), comunidad de mesa, no-Discord
UX-02PersonasLeo Navas (29, jugador, móvil) · Nerea Solís (36, anfitriona, inbox en el bar)
UX-03JTBDJob «qué hay esta noche» + job «llenar la mesa sin sillas fantasma»
UX-04StoriesMust: feed Hoy, ficha, RSVP público, código RD-, login HOST, inbox 5 estados, publicar, empty/error/loading
UX-05JourneyScroll feed → ficha → solicitud honesta → acuse → contacto HOST → SEATED / WAITLIST
UX-06BlueprintFrontstage feed/dock/form · backstage mesa física · sistemas Nest/Neon/JWT
UX-07Site mapPúblico feed-first + auth HOST; dock Hoy · Mesas · Publicar · Inbox · Yo
UX-08FlujosF-BOOK: card → ficha → POST RSVP → success; F-HOST inbox + publicar
UX-09Datos+permisosGameTable · SeatRequest · User HOST · Session; JWT en list/patch/publish
UX-10MétricasNorth star contacto antes de hora de mesa; tap mesa; invariante SEATED ≤ seats
UX-11ResearchComprobado / supuesto / hipótesis / decisión — sin entrevistas de campo inventadas

UI producto (prefijo UI- · mínimo 10)

IDNombreFlujo
UI-00TokensDS signage: suelo blanco, tinta, cromo, felt, liner, ficha; Zilla Slab + Mulish
UI-01Feed HoyH-FEED: cronología de mesas de esta noche + dock flotante
UI-02Ficha mesaFoto real, juego, hora, local, aforo, anfitrión, reglas cortas, CTA
UI-03RSVPForm público; disclaimer solicitud ≠ plaza confirmada
UI-04SuccessAcuse + código RD-MMDD-NNN + próximos pasos
UI-05LoginAcceso JWT HOST al inbox
UI-06Inbox HOSTLista + estados NEW → CONTACTED → SEATED / WAITLIST / CANCELLED
UI-07EmptyCero mesas esta noche / inbox vacío
UI-08ErrorFallo de red / API; reintentar
UI-09LoadingSkeleton feed / inbox
UI-10Mobile feedHome feed ~390px + dock inferior flotante

Regla: wizard de N pasos → N boards UI solo para esos pasos, además de success/empty/error/loading/staff. RONDA no es wizard: el núcleo es feed → ficha → form de 1 pantalla.


1. Mapa de canvas (bandas)

§BandaPropósitoArtboards clave
1UX PROCESSModelo de servicio de mesa localUX-00…UX-11
2DESIGN SYSTEMTokens signage + dock + badgesUI-00
3PUBLIC FEEDCara jugador feed-firstUI-01, UI-02, UI-03, UI-04, UI-07, UI-10
4HOSTFlujos autenticadosUI-05, UI-06
5STATESResilienciaUI-08, UI-09

Layout canvas (referencia de diseño): origen (0,0) · gaps ~100–120px · UX en 3×4 · UI pública en fila feed → ficha → RSVP → success · mobile debajo del feed. [DECISIÓN] Bandas jerárquicas D-P0-04 / D-P1-04.


2. Mapeo Paper → Angular

ArtboardRuta appComponente
UI-00 Tokenstokens Tailwind + fuentes
UI-01 Feed Hoy/FeedPage
UI-02 Ficha mesa/mesas/:slugTablePage
UI-03 RSVP/mesas/:slug/asientoRsvpPage
UI-04 Success/okSuccessPage
UI-05 Login/loginLoginPage
UI-06 Inbox HOST/inboxInboxPage
UI-07 Empty/ 0 mesas · /inbox 0 itemsempty en feed / inbox
UI-08 Errorfeed / inbox / formbanner error + retry
UI-09 Loadingfeed / inboxskeletons
UI-10 Mobile feed/ viewport 390mismo FeedPage + dock

Rutas HOST adicionales (no artboard UI dedicado v1, sí en IA): /inbox/:id detalle solicitud · /publicar crear mesa · /yo sesión.


3. Tokens de diseño en Paper

TokenValorUso
Suelo / bg#FFFFFFFondo de página, cartelería
Tinta#111111Texto fuerte, wordmark, CTA ink
Cromo#F5C400Acento dock activo, focus, “esta noche”
Felt#1F4D3ASuperficie de mesa, SEATED, acciones positivas
Liner#FFF6C2Cards, rails, fondo dock
Ficha#E24B2AWAITLIST, urgencia, asientos restantes bajos
DisplayZilla Slab 56/36/24Titulares, wordmark, hora de mesa
UIMulish 16/14Body, labels, form, dock labels

4. Checklist de densidad (anti thin-frames)

CriterioUXDSPublic feedHOSTStates
Jerarquía tipográfica visible
Microcopy real (no lorem)
Tokens signage aplicados
Datos de seed creíblesPersonas5 mesas + fotosCódigos RD-Empty realista
Media / iconografíaCoverFotos mesaBadges estadoSkeletons
Dock flotante presenteSpecUI-01, UI-10UI-06

5. Media

  • assets/hero.jpg — atmósfera de mesa / signage de local
  • assets/tables/azulejos.jpg — Azulejos de Lavapiés
  • assets/tables/cartas.jpg — Cubilete y cartas
  • assets/tables/minis.jpg — Mazmorra de Chamberí
  • assets/tables/cooperativo.jpg — Farol cooperativo
  • assets/tables/coleccion.jpg — Colección de patio

[COMPROBADO] Los seis archivos existen en el case 2026-08-15-ronda/assets/.


6. Enlaces

01-project-definition.md

01 — Definición de proyecto — RONDA

1. Identidad

CampoValor
NombreRONDA
SignificadoRonda de juego / ronda de copas en la mesa: el turno que se abre esta noche
Eslogan”Esta noche hay mesa.”
Una fraseFeed cronológico de mesas de juego locales + RSVP público + inbox HOST JWT.
SectorGaming / comunidad de mesa local
TipoWeb L2 — feed + RSVP + inbox HOST — Nivel 2
PlataformaWeb responsive (móvil-first jugador + desktop anfitrión)
Mercado demoEspaña · Madrid · Malasaña, Lavapiés, Chamberí (y Embajadores como local de seed)
Idiomaes-ES
Fecha caso2026-08-15
TernaR-HY · S-DOCK · H-FEED · F-BOOK

[COMPROBADO] Nombre, eslogan, terna, nivel y sector salen de docs/00-day-brief.md.

2. Problema

Principal (hipótesis de diseño)

[HIPÓTESIS] Quien quiere jugar esta noche no abre un marketplace ni un Discord: pregunta “qué hay” en un grupo de WhatsApp, un cartel del bar o un hilo que se pierde. [SUPUESTO] El anfitrión confirma sillas por mensaje suelto, sin cola, sin código y sin tope de aforo visible.

Secundarios

ProblemaQuién lo sufreEfecto
Oferta enterrada en chats y cartelesPLAYER (Leo)Llega tarde o no llega; no sabe qué se juega
“¿Quedan asientos?” por DMAmbosCola informal, sillas fantasma
Mensaje enviado sin acusePLAYERNo sabe si llegó; escribe otra vez
Excel / WhatsApp sin estadoHOST (Nerea)No sabe a quién ya contestó
Expectativa de “ya tengo silla”AmbosNo-shows y mesa descompensada

Supuestos (no investigación primaria propia)

  • S1: Un barrio tipo Malasaña–Lavapiés sostiene 5–12 mesas públicas por semana (rango operativo). [SUPUESTO]
  • S2: El jugador llega con “qué se juega esta noche”, no a comprar un juego. [SUPUESTO]
  • S3: El anfitrión necesita cola con estado, no un Discord ni un marketplace. [SUPUESTO]
  • S4: La plaza se confirma en el local; RONDA no cobra. [SUPUESTO]

Hipótesis de producto

IDHipótesisSeñal de validación (futura)
H1El feed reduce abandono vs listado de eventos o grupo de WhatsApp% sesiones con tap en mesa
H2Copy “solicitud, no plaza” baja sillas fantasma↓ “pensé que tenía silla”
H3Inbox SEATED nunca supera seatsinvariante de aforo

No se afirman estadísticas de demanda de ocio de mesa inventadas. Todo lo anterior es razonamiento de diseño etiquetado.

3. Propuesta de valor

ParaValor
PLAYER (Leo Navas)Ve qué mesas hay esta noche; pide asiento en un minuto; se lleva un código.
HOST (Nerea Solís)Ve solicitudes nuevas, marca contacto antes de abrir, sienta o pone en espera sin WhatsApp.
Local (café / bar / librería)Canal mínimo: mesa visible + pipeline de gente, sin TPV ni evento de ticket.

No es RONDA

ExcluidoPor qué
Mapa / plano de ciudadTerna H-FEED, no H-MAP (eso fue DERIVA)
Search-first / catálogo facetadoTerna no es H-SEARCH (eso fue PIZARRA)
Kanban de solicitudesF-BOOK es cola de asiento, no tablero
E-commerce / TPV / entrada de pagoS4: la plaza se confirma en el local; no cobra
Discord / chat in-app / matchingS3: cola con estado, no comunidad persistente
Hero 2-col + 3 cards de “juegos estrella”Anti-patrón AP-12; contradice H-FEED

4. Objetivos

Negocio / caso de estudio

  • Demostrar vertical slice L2 gaming feed-first con GameTable + SeatRequest + JWT HOST.
  • Portfolio coherente: Paper (12 UX + 11 UI) + docs + app runnable.
  • Terna R-HY · S-DOCK · H-FEED · F-BOOK frente a DERIVA (mapa) y PIZARRA (search).

Usuario

RolObjetivo medible en demo
PLAYERVer “esta noche” en el primer viewport y enviar RSVP en < 2 min
HOSTMarcar CONTACTED / SEATED / WAITLIST en < 3 taps desde el inbox

No objetivos v1 (explícitos)

  • Pagos, fianza, TPV
  • Cuenta de jugador / “mis solicitudes”
  • Chat, matching o reputación
  • Mapa de locales
  • Notificación email / WhatsApp transaccional
  • Inventario de juegos en venta
  • Multi-anfitrión con roles editoriales

5. Roles y permisos (resumen)

AcciónPúblico (PLAYER)HOST
Ver feed Hoy / Mesas
Ver ficha de mesa
POST solicitud RSVPSí (mismo form)
Login JWTNo (no cuenta jugador)
Listar solicitudesNo (401)
Cambiar statusNo
Publicar mesa POST /api/tablesNo (401)
Ver / editar sesión (Yo)No

[DECISIÓN] Un solo rol autenticado: HOST. El jugador es anónimo en captura.

6. Métricas (modelo, no instrumentadas en v1 salvo base)

TipoMétricaDefinición
North Star% solicitudes válidas CONTACTED antes de startAt de la mesaCalidad operativa del contacto, no “entradas vendidas”
Activación1ª solicitud públicaPOST create 2xx
Feed% sesiones con tap en card de mesaH1
ExpectativaReclamaciones “ya tenía silla”H2 (cualitativa)
IntegridadSEATED ≤ seats por mesaH3
SaludError rate API tables/create/list4xx/5xx

7. Alcance funcional v1 (L2)

MóduloIncluido
Home feedCronología de 5 mesas (foto, juego, hora, local, asientos) + dock
FichaReglas cortas, anfitrión, aforo, punto de encuentro, CTA
RSVPForm público + POST /api/requests
SuccessCódigo RD-MMDD-NNN + próximos pasos
AuthPOST /api/auth/login → JWT HOST
InboxGET list, PATCH status (NEW → CONTACTED → SEATED / WAITLIST / CANCELLED)
PublicarPOST /api/tables (HOST)
Estados UIEmpty, loading skeleton, error de red
Mobile feedMisma home a ~390px + dock flotante
Seed1 HOST, 5 mesas, solicitudes de ejemplo

8. Criterios de aceptación de producto

  1. Un visitante puede ver el feed cronológico sin autenticación y abrir una ficha.
  2. Un visitante puede enviar una solicitud sin cuenta y recibir un código RD-….
  3. La UI dice explícitamente que la solicitud no confirma plaza.
  4. Sin token, GET/PATCH /api/requests* y POST /api/tables responden 401.
  5. GET /api/tables devuelve las 5 mesas seed ordenadas por startAt ascendente.
  6. Un HOST puede iniciar sesión y ver el inbox ordenado (createdAt desc).
  7. El detalle permite transicionar NEW | CONTACTED | SEATED | WAITLIST | CANCELLED.
  8. La home es el feed: no hay hero 2-col + 3 cards como superficie principal.
  9. Barrios cubiertos en seed: Lavapiés, Embajadores, Chamberí, Malasaña.
  10. Fotos de mesa reales en fichas (assets del case).
  11. Si los asientos visibles están cubiertos, el CTA ofrece WAITLIST, no “reserva tu silla”.
  12. Un HOST autenticado puede publicar una mesa (POST /api/tables).

9. Stack y artefactos

CapaDetalle
FrontendAngular + Tailwind · puerto 4200
BackendNestJS · puerto 3015
DBNeon PostgreSQL · ensureSchema · project curly-lab-77015594
AuthJWT (HOST) + tabla sessions
DiseñoPaper 01M023RM2TZ49CYM2FN1FRRGNY
Repo app/Users/cristian/orca/ronda-app/ · GitHub Criscode2022/ronda-app

[COMPROBADO] Puerto, Neon, Paper file ID y path de app constan en el brief de este encargo.

10. Riesgos y mitigaciones

RiesgoImpactoMitigación v1
Spam en form públicoInbox ruidosoValidación server; honeypot / rate-limit en L2+
Expectativa de silla instantáneaNo-show y mesa rotaCopy “solicitud ≠ plaza”; SEATED solo HOST
OverbookingConflicto en la mesa físicaWAITLIST + regla H3 documentada; HOST no debe SEATED > seats
Confundir con tienda / evento de ticketExpectativa de pagoCopy “se confirma en el local”; sin precio
PII de jugadores en solicitudesPrivacidadSolo HOST lista; doc 11
Feed vacío un martesAbandonoEmpty state “esta noche no hay mesa” + CTA ver semana
Home percibida como landing de marcaPérdida de craftSignage + feed como superficie; dock, no hero 2-col
Confusión Discord / “únete al server”Expectativa de chatNo hay canal; hay cola

11. Glosario

TérminoDefinición en RONDA
Mesagame_tables: juego, hora, local, aforo, anfitrión
Solicitud / RSVPseat_requests; no es plaza confirmada
Plaza / asientoSolo cuando HOST marca SEATED
Lista de esperaWAITLIST cuando seats están cubiertos
HOSTUsuario autenticado que publica y sienta
PLAYERVisitante anónimo que pide asiento
Código RD-Identificador corto oral (RD-0815-001)
DockShell S-DOCK: Hoy · Mesas · Publicar · Inbox · Yo
HoyRecorte del feed: mesas con startAt en el día civil Europe/Madrid
MesasRecorte semanal / todas las publicadas próximas

12. Decisiones de diseño (cierre de brief)

IDDecisiónAlternativa descartada
D1Home = feed cronológico (H-FEED)Hero 2-col + 3 cards (AP-12) · mapa (DERIVA) · search (PIZARRA)
D2Shell dock flotante (S-DOCK)Top-nav sticky · sidebar CRM · command search
D3Registro híbrido flyer + dock (R-HY)Taberna candlelit (primer instinto) · dusk · wine
D4Flujo F-BOOK (asiento)Wizard, kanban, search, mapa
D5Mood signageFelt casino, phosphor arcade, alpine, taberna
D6Un rol autenticado HOSTMulti-rol jugador / local / admin
D7Tabla game_tables (no tables)Nombre SQL reservado
D8Estado SEATED (no ENROLLED)Vocabulario municipal de PIZARRA
D9RONDA no cobraTPV / fianza / Eventbrite

13. Relación con el día anterior

CaseTernaPor qué RONDA no lo copia
DERIVA (N−1)S-SPLIT / H-MAP / F-MAPNo turismo, no plano, no split mapa
PIZARRA (N−2)S-CMD / H-SEARCH / F-SEARCHNo catálogo municipal, no search-first

[COMPROBADO] El brief exige 0 códigos iguales a N−1 y ≥2 distintos vs N−2. Terna de hoy: R-HY · S-DOCK · H-FEED · F-BOOK.

02-ux-research-strategy.md

02 — Estrategia de investigación UX — RONDA

Importante: este documento contiene artefactos de diseño y razonamiento secundario.
No hay entrevistas de campo primarias ni estadísticas inventadas presentadas como dato medido.
Etiquetas: [COMPROBADO] en producto, [SUPUESTO], [HIPÓTESIS], [DECISIÓN].

1. Objetivos de investigación (del caso)

ObjetivoMétodo en este casoSalida
Entender actores de la mesa localModelado de stakeholders + personas§3–4
Definir job de “esta noche” y de aforoJTBD + stories Must§5
Mapear fricción chat → sillaJourney + service blueprint§6–7
Traducir a requisitos L2 feed-firstMatriz hallazgo → requisito → feature§8

2. Fuentes y límites

Fuentes admisibles (secundarias / operativas)

  • Conocimiento general de bares de juego, cafés con mesa reservable y grupos de WhatsApp de barrio.
  • Analogía operativa con feeds + RSVP + inbox JWT de la serie daily.
  • Restricciones ALS-2: no mapa, no search-first, no kanban, no e-commerce, no Discord.

Límites éticos de verdad

ProhibidoPermitido
“El 68% de jugadores abandona el grupo de WhatsApp” sin fuente[SUPUESTO] el jugador llega con ‘qué hay esta noche’”
Citas de entrevistas ficticias como campo realQuotes de persona etiquetadas como constructo de diseño
SLA medido “contacto en 2 h en Lavapiés” como KPI realNorth star modelo “CONTACTED antes de startAt
Aforo real de un local concretoAforos de seed de producto
“Nerea existe y nos dijo…”Nerea Solís es persona de diseño

3. Stakeholders

StakeholderInfluenciaInterésNecesidad principal
PLAYER / jugador de mesaBaja formalMuy altaVer qué hay esta noche y dejar solicitud con acuse
HOST / anfitrión (Nerea)AltaMuy altaCola con estado y código oral
Acompañante / pareja de juegoMediaAltaHora, local, aforo claros
Local (café, bar, librería, terraza)Alta adopciónMediaMesa llena a su hora, sin overbooking de sillas
Comunidad de mesa del barrioMediaMediaRitmo semanal visible, no un foro eterno
Tienda / editorial de juegosBaja v1BajaFuera: no es canal de venta
Discord / Meetup / WhatsAppCompetencia / canal paraleloNo clonar el chat; sí sustituir la cola informal

Mapa de poder (resumen)

  • Decisor de adopción: HOST habitual del local (quien abre mesa).
  • Usuario frecuente de captura: PLAYER (feed + form) y HOST (inbox diario).
  • Riesgo de rechazo: si la home no enseña “esta noche” en el primer viewport, o si el form implica “ya tienes silla”.

4. Personas

P1 — Leo Navas · PLAYER

CampoDetalle
Edad / contexto29 años; vive cerca de Tribunal; juega de semana cuando sale del curro
DigitalAlta; móvil primero; no quiere otra app de chat
GoalsVer qué hay esta noche, si queda hueco, dejar datos y un código
PainsGrupos muertos, “¿alguien para Azul?”, no saber si el mensaje llegó
Quote de diseño“Dime qué hay esta noche y si de verdad me van a guardar silla.”
Seed demoleo.navas@example.com · solicitud azulejos RD-0815-001

Escenario: Abre RONDA en el metro, ve Azulejos de Lavapiés a las 20:30 en La Palma 12, pide asiento, guarda RD-0815-001.

[DECISIÓN] Leo no tiene cuenta. Crear usuario jugador sería fricción y no aporta al job de captura.

P2 — Nerea Solís · HOST

CampoDetalle
Edad / contexto36 años; anfitriona habitual; abre mesa en café/bar antes de que llegue gente
DigitalMedia–alta; móvil en la barra; desktop si publica la semana
GoalsVer NEW del día, contactar antes de la hora, sentar o pasar a WAITLIST sin WhatsApp
PainsMensajes sueltos, no sabe quién ya fue avisado, sillas fantasma
Quote de diseño“Si está en NEW, es mía. Si está SEATED, hay silla. Si no, WAITLIST.”
Email demonerea@ronda.club / password123

Escenario: Login en el bar → inbox → Leo NEW → escribe / llama → CONTACTED → si hay silla SEATED; si la mesa está llena WAITLIST.

Anti-personas

QuiénPor qué no es target v1
Comprador de marketplace de juegosEso es e-commerce, no ronda de mesa
Organizador de torneo federado con rankingOtro producto (liga, no feed de esta noche)
Turista que busca “escape room”Experiencia de ticket, no comunidad de barrio
Moderador de Discord que quiere un cloneRONDA no es chat

5. JTBD y user stories

Job principal (PLAYER)

Cuando quiero saber qué se juega esta noche cerca,
quiero ver las mesas en un feed y dejar mis datos en un solo paso,
para que el anfitrión me contacte sin perder el mensaje y sin creer que ya tengo silla.

Job principal (HOST)

Cuando abro mesa y empiezan a llegar peticiones,
quiero verlas con juego, hora y estado,
para contactar antes de la hora y sentar o poner en espera sin overbooking.

Jobs secundarios

JobRol
Distinguir “hoy” de “esta semana”PLAYER
Referir una solicitud por código corto en la barraAmbos
Ver cuántas NEW / WAITLIST hay antes de abrirHOST
Publicar una mesa nueva desde el dockHOST
Cancelar duplicado o desistimientoHOST

Stories Must (v1)

IDStoryAC
US1Como jugador, quiero ver las mesas de esta noche en un feedGET /api/tables ordenado por startAt; cards con foto, hora, local, asientos
US2Como jugador, quiero abrir la ficha con reglas, aforo y punto de encuentroGET :slug; foto real; CTA
US3Como jugador, quiero enviar RSVP sin cuentaPOST 201 + redirect success
US4Como jugador, quiero un código y saber que no tengo silla todavíacode en /ok + copy disclaimer
US5Como HOST, quiero entrar con email/passwordJWT + redirect /inbox
US6Como HOST, quiero listar solicitudesGET list JWT
US7Como HOST, quiero cambiar estado incl. WAITLIST y SEATEDPATCH status
US8Como HOST, quiero publicar una mesaPOST /api/tables JWT
US9Como cualquiera, quiero ver empty / error / loadingUI-07, UI-08, UI-09
US10Como jugador en móvil, quiero el feed + dock usable a ~390pxUI-10; targets ≥44px

MoSCoW (v1 L2)

PrioridadÍtems
MustFeed Hoy, ficha, RSVP, success, login, inbox, 5 estados, publicar, empty/loading/error, mobile feed + dock
ShouldRecorte Mesas (semana); asientos restantes visibles; labels ES de enums; detalle /inbox/:id
CouldCopiar código al portapapeles; click-to-call; persistir scroll del feed
Won’tPagos, cuenta jugador, mapa, Discord, email transaccional, search facetado

6. Journey (PLAYER → HOST)

FaseActorAcciónTouchpointEmoción [HIPÓTESIS]
1 MirarLeoAbre RONDA; ve “esta noche”Feed Hoy + dockCuriosidad baja fricción
2 ElegirLeoToca Azulejos de Lavapiés 20:30Card → fichaControl
3 EntenderLeoLee local, aforo 4, tile-laying, Nerea/mesas/azulejosConfianza o duda de cupo
4 PedirLeoForm + disclaimer/mesas/azulejos/asientoPrudencia
5 AcuseLeoVe RD-0815-001/okAlivio (no euforia de “ya estoy”)
6 ContactoNereaVe NEW, escribeInbox + detalleControl operativo
7 CierreNereaSEATED o WAITLISTPATCHCierre honesto

Momentos de verdad

  1. Feed en el primer viewport — si hay que “descubrir la marca”, se rompe S2. [HIPÓTESIS]
  2. Disclaimer en form y success — si falta, H2 falla.
  3. Código RD- — prueba de “ha llegado” para la barra y el mensaje.
  4. WAITLIST visible — evita la mentira social del “ya te apunto”.
  5. Dock no esconde Publicar / Inbox — el HOST en el bar no busca un menú hamburger. [HIPÓTESIS]

7. Service blueprint (resumen)

CapaElementos
Frontstage PLAYERFeed, dock, ficha, form, success
Frontstage HOSTLogin, inbox, detalle, publicar, Yo
BackstageMensaje / llamada / silla física en el local (fuera de app)
SistemasNest API :3015, Neon curly-lab-77015594, JWT + sessions, Angular feed
SoportesSeed 5 mesas + fotos, Paper, docs
Fallos0 mesas hoy; 401 sin token; 404 slug; red caída → UI error

Fallos de servicio y respuesta de diseño

FalloEvidencia de UIRecuperación
Noche sin mesasUI-07 Empty feed“Esta noche no hay mesa” + ver semana
Inbox vacíoUI-07 Empty inbox“Cuando alguien pida asiento, aparece aquí”
API caídaUI-08 ErrorReintentar
Latencia feedUI-09 LoadingSkeleton de 5 cards
Form inválidoInline fieldNo navegar a success
Cupo lleno (ops)HOST ve seats vs countForzar WAITLIST, no SEATED [H3]

8. Matriz hallazgo → requisito → feature

HallazgoTipoRequisitoFeature v1
Llega con “qué hay esta noche”[SUPUESTO] S2Feed es la homeUI-01 / H-FEED
Chat no ordena por hora[HIPÓTESIS] H1Cronología startAtGET tables ordenado
Sin acuse[HIPÓTESIS]Código oralSuccess + RD-
Confunden solicitud con silla[HIPÓTESIS] H2Copy irrenunciableDisclaimer form/success
Overbooking informal[HIPÓTESIS] H3Estado WAITLIST + seatsEnum + campo seats
HOST opera en el bar[SUPUESTO]Dock + inbox móvilS-DOCK / UI-06 / UI-10
PII de jugadores[DECISIÓN]Auth JWT HOSTLogin + guards API
L2 compacto, no Discord[DECISIÓN]Un rol HOST; mesas seedSin chat, sin cuenta PLAYER

9. Preguntas abiertas (no bloquean v1)

IDPreguntaCómo se resolvería después
Q1¿Un HOST por local o cola compartida de barrio?venueId + scope de User L2+
Q2¿Email automático “hemos recibido tu solicitud”?Hook post-create; copy ya promete contacto
Q3¿Checkbox RGPD + política?Legal L2+; minimización ya aplicada
Q4¿Sincronizar aforo con sillas físicas del local?Fuente de verdad única; hoy es HOST
Q5¿Permitir party_size > 1 y descontar asientos?Campo en request + regla ops; v1 default 1
Q6¿“Hoy” corta a medianoche o a las 04:00?[DECISIÓN] día civil Europe/Madrid 00:00–24:00; L2+ madrugada

10. Plan de research futuro (si hubiera campo real)

MétodoMuestra orientativaPregunta
Test de usabilidad feed5–6 jugadores 22–40¿Encuentran mesa de esta noche en < 30 s?
Shadowing HOST 1 noche1–2 locales¿El inbox sustituye el WhatsApp de la barra?
Revisión de no-showsMesas con SEATEDValidar H2 (sillas fantasma)
Card sort de mecánicas6–8 participantes¿Azulejos / minis / coop se entienden?

Estos métodos no se han ejecutado. No se reportan hallazgos como si lo hubieran sido.

11. Síntesis

RONDA se diseña como feed de mesas con cola de asiento, no como tienda, no como mapa y no como Discord.
La investigación del caso es constructiva y etiquetada: personas, JTBD y journey son artefactos de diseño; las métricas de validación quedan para uso real futuro.

03-information-architecture.md

03 — Arquitectura de información — RONDA

1. Principios de IA

PrincipioAplicación
Feed firstLa home es la cronología de esta noche. No hay landing de marca por delante.
Card → ficha → asientoProfundidad 2 desde el feed hasta el form
Público vs HOSTFeed y form abiertos; inbox y publicar solo autenticados
Lenguaje de dominioMesa, asiento, solicitud, ronda, local — no “evento SKU”, “lead CRM”, “ticket”
Código visibleRD-… en success e inbox para referencia oral / barra
Honestidad de estadoSolicitud ≠ sentado; WAITLIST es ciudadano de primera
Dock siempre a manoS-DOCK: cinco destinos, no mega-menú

2. Sitemap

/                                 Feed Hoy (público) — mesas del día civil, startAt ASC
/mesas                            Feed semanal / próximas (público)
/mesas/:slug                      Ficha de mesa (público)
/mesas/:slug/asiento              Formulario RSVP (público)
/ok                               Success post-solicitud (público)
/login                            Login JWT HOST
/inbox                            Lista de solicitudes (auth HOST)
/inbox/:id                        Detalle + estado (auth HOST)
/publicar                         Crear mesa (auth HOST)
/yo                               Sesión HOST: nombre, salir (auth HOST)
/**                               → redirect /

Árbol por audiencia

AudienciaNodos relevantes
PLAYERFeed Hoy → (Mesas) → Ficha → Asiento → Ok
HOSTLogin → Inbox → Detail · Publicar · Yo (y feed público si consulta oferta)
AmbosWordmark → / (HOST autenticado: wordmark puede ir a /inbox)

[DECISIÓN] /mesas no es un buscador: es el mismo patrón de feed con recorte temporal más amplio.

3. Navegación

Dock flotante (S-DOCK)

SlotDestinoVisibilidadNotas
Hoy/TodosRecorte día civil; estado activo en home
Mesas/mesasTodosPróximas / semana
Publicar/publicarHOST; si guest → /login?next=/publicarCentro del dock, acento cromo
Inbox/inboxHOST; si guest → /login?next=/inboxBadge de NEW opcional v1 Should
Yo/yo si auth; /login si guestTodosSesión o entrada HOST

[DECISIÓN] El dock es la única navegación primaria. No hay top-nav de 7 ítems ni sidebar CRM.

Pública (además del dock)

ElementoDestinoNotas
Wordmark RONDA/Zilla Slab; no “club marketplace”
Card de mesa/mesas/:slugResultado del feed
Pedir asiento/mesas/:slug/asientoPrimary en ficha
Entrada HOST discreta/loginTambién vía Yo

HOST (autenticado)

ElementoDestinoVisibilidad
Inbox/inboxHOST
Badge rolHOST
NombreNerea SolísHOST
Publicar/publicarHOST
Salirlimpia token + session → /loginHOST
Card solicitud/inbox/:idHOST
Volver (detalle)/inboxHOST

4. Inventario de contenido

PantallaContenidos
Feed HoyWordmark, fecha del día, lista cronológica (foto, título, hora, local, barrio, asientos), empty, loading, dock
Feed MesasMismo patrón; agrupación por día; recorte semana
FichaHero foto, título, mecánica, hora, local, dirección, aforo, anfitrión, reglas cortas, disclaimer, CTA
RSVPTítulo mesa, hora, disclaimer, campos form, submit
SuccessMensaje, código RD-, “no es plaza”, siguiente paso
LoginTítulo anfitrión, email, password, submit, error
InboxLista (code, nombre, mesa, status, createdAt), empty, error
DetailCódigo, jugador, mesa, aforo vs demanda, notas, acciones estado
PublicarTítulo, mecánica, barrio, local, dirección, fecha/hora, asientos, resumen, submit
YoNombre, email, rol HOST, salir

5. Taxonomía

RequestStatus

Status APILabel UISemántica
NEWNuevaAcaba de llegar; sin contacto
CONTACTEDContactadaHOST inició mensaje / llamada
SEATEDSentadaPlaza confirmada en el local (o compromiso firme del HOST)
WAITLISTLista de esperaCupo cubierto o reserva de hueco
CANCELLEDCanceladaDesiste / duplicado / no localizable

Orden de inbox: createdAt descendente (más reciente primero).
[DECISIÓN] No se ordena por “prioridad de socio del local” en v1.

Mechanic

APILabel UIMesas seed
TILEAzulejos / tile-layingAzulejos de Lavapiés
CARDSCartas y dadosCubilete y cartas
MINISMiniaturasMazmorra de Chamberí
COOPCooperativoFarol cooperativo
COLLECTIONColecciónColección de patio

Neighborhood

APILabel UISeed
LAVAPIESLavapiésazulejos
EMBAJADORESEmbajadorescartas
CHAMBERIChamberíminis
MALASANAMalasañacooperativo, coleccion

Barrios y locales del seed son contenido de producto demo, no una agenda real publicada. [SUPUESTO de catálogo]

RecorteCriterio
HoystartAt ∈ día civil Europe/Madrid
MesasstartAt ≥ now − 30 min (aún alcanzable) y ≤ now + 7 días

[DECISIÓN] No hay query q ni chips de mecánica en v1. Eso sería H-SEARCH (PIZARRA). El feed se recorre.

6. Modelo mental vs UI

Modelo mentalRepresentación
“¿Qué hay esta noche?”Feed Hoy ordenado por hora
“¿Dónde quedamos?”Ficha: local + dirección
“¿Quedan sillas?”Asientos restantes / CTA WAITLIST
“Me apunto”Form /asiento + disclaimer
“Me dieron un número”code RD-
“Lista de gente nueva”Inbox + badge NEW
“Ya le escribí”CONTACTED
“Tiene silla”SEATED
“La mesa está llena”WAITLIST
“Abro mesa el jueves”/publicar

7. Query string (contrato mínimo)

ParamEjemploEfecto
codeRD-0815-001Success: muestra el código
next/inboxLogin: redirect post-auth
fromhoy | mesasFicha: volver al recorte

[DECISIÓN] El feed no serializa filtros de mecánica/barrio en URL en v1. Evita disfrazar un search.

8. Rutas API alineadas a IA

UIAPI
Feed Hoy / MesasGET /api/tables
FichaGET /api/tables/:slug
Submit RSVPPOST /api/requests
LoginPOST /api/auth/login
Inbox listGET /api/requests
Cambiar estadoPATCH /api/requests/:id/status
Publicar mesaPOST /api/tables

Detalle inbox: GET /api/requests/:id (JWT) — Should de implementación, AC de F7.

9. Decisiones de IA descartadas

IdeaPor qué no en L2 v1
/explorar editorial + feed secundarioRompe H-FEED
Área “mis solicitudes” por email mágicoCuenta de facto; authz delicada
Mapa de locales con pinsH-MAP de DERIVA
Search + chips mecánica/barrioH-SEARCH de PIZARRA
Nested nav “Barrios → Locales → Mesas”Directorio, no ronda
Kanban de solicitudesF-KAN; aquí la cola es inbox
Wizard de 4 pasos de RSVPF-ONB; el form es 1 pantalla
Foro / Discord embebidoS3: no es chat

04-user-flows.md

04 — Flujos de usuario — RONDA

Convenciones

  • Actor: Guest (PLAYER) | HOST
  • Éxito: resultado observable
  • Errores: UI + código HTTP cuando aplica
  • Flujo canónico: F-BOOK (pedir asiento de una mesa)

F1 — Descubrimiento por feed (PLAYER)

/  → feed Hoy (H-FEED)
   → GET /api/tables
   → lista cronológica  |  empty  |  error  |  skeleton
   → click card → /mesas/:slug
   → dock Mesas → /mesas (recorte semana)
   → dock Yo → /login
PasoAcciónSistema
1Aterriza; la primera card de esta noche es el primer focoRender home
2Recorre hora, local, asientosGET tables
3Abre ficha o cambia a MesasRouter
4Dock permanece visibleShell S-DOCK

Éxito: al menos una ficha alcanzable, o empty accionable.
AC: no hay que hacer scroll de “marca” para llegar a la primera mesa.

Errores / estados

CasoComportamiento
0 mesas hoyUI-07: “Esta noche no hay mesa.” + CTA “Ver la semana” → /mesas
0 mesas en semanaUI-07: “Aún no hay mesas publicadas.”
Red / 5xxUI-08 + Reintentar
Primera cargaUI-09 skeleton de 5 cards

F2 — Entender mesa (PLAYER)

/mesas/:slug → GET /api/tables/:slug
             → foto, juego, hora, local, dirección, aforo, anfitrión, reglas, disclaimer
             → CTA "Pedir asiento" → /mesas/:slug/asiento
             → si asientos cubiertos → CTA "Apuntarme a la lista de espera"
             → 404 slug → mensaje + volver al feed
PasoAcciónSistema
1Lee oferta presencialGameTable
2Contrasta aforo y horaCampos seats, startAt
3Decide pedir o volver al feedRouter

Éxito: CTA visible; disclaimer “pedir asiento no reserva silla” visible antes del form.
Error: 404 si slug inexistente.

AC ficha

#Criterio
1Foto real del imageKey (no color sólido)
2Hora en Europe/Madrid, formato HH:mm
3Local + barrio + dirección corta
4Asientos totales visibles
5Nombre del HOST
6Mecánica en label humano (tile-laying → Azulejos)

F3 — Pedir asiento / RSVP (PLAYER, público)

/mesas/:slug/asiento
  → validación cliente
  → POST /api/requests {
        tableId | slug,
        fullName, email, phone?,
        partySize?,
        notes?
     }
  → 201 SeatRequest { code, status: NEW, ... }
  → /ok?code=RD-…
CampoValidación cliente (mín.)API
tableId / slugrequired (de la ruta)existe en DB
fullNamerequired, min 2min 2
emailrequired, emailemail válido
phoneoptionaloptional
partySizeoptional, 1–2, default 11–2
notesoptionaldefault ""

Éxito: registro status=NEW, código RD-MMDD-NNN, pantalla success.
No hay cuenta de jugador en v1.

Errores

CasoComportamiento
Validación DTO400 + mensaje de campo
slug / tableId inexistente400 / 404
Red caídaError de red en form; no navegar a /ok
Email mal formado400
Doble submitBotón disabled mientras pending

[DECISIÓN] No se bloquea el POST aunque seats estén cubiertos: el jugador puede entrar en WAITLIST después, por decisión HOST. Bloquear en cliente mentiría sobre la barra. El CTA cambia de copy, no de permiso.


F4 — Success (PLAYER)

/ok?code=RD-… → copy de acuse + code
              → “Esto no confirma la silla”
              → CTA volver al feed / ir a la mesa
PasoAcciónSistema
1Lee acuse y códigoUI (query/state)
2Conserva el código (captura, nota)Fuera de app
3Espera mensaje / llamada del HOSTFuera de app

AC: el código es seleccionable; el disclaimer es visible sin scroll en desktop.


F5 — Login JWT (HOST)

/login → POST /api/auth/login { email, password }
      → 200 { accessToken, user } → localStorage → crea session
      → redirect `next` o /inbox
      → 401 → mensaje error en form
CampoValidación clienteAPI
emailrequired, emailemail válido
passwordrequired, min 6min 6

Éxito: token guardado; user role=HOST, nombre Nerea Solís.
Credencial demo: nerea@ronda.club / password123. [COMPROBADO]

CasoComportamiento
Credenciales inválidas401 + mensaje
Red caídaError de red en UI
Token caducado en inbox401 en GET → re-login
next abiertoSolo paths internos (/inbox, /publicar, /yo)

F6 — Inbox (HOST)

/inbox (token en cliente)
  → GET /api/requests
  → Render cards
CasoComportamiento
Lista con itemsCards: code, nombre, mesa, status, createdAt
Lista vacíaEmpty: “Nadie ha pedido asiento todavía.”
Fallo red / 401Error + reintentar / re-login

[DECISIÓN] Inbox sin filtros de status en v1 (filtro = L2+). Orden createdAt desc.


F7 — Detalle y cambio de estado (HOST)

/inbox/:id
  → GET /api/requests/:id   (si el endpoint existe; si no, item del list)
  → UI: contacto + mesa + seats + nº solicitudes de la mesa + notas
  → PATCH /api/requests/:id/status { status }
  → 200 SeatRequest actualizado

Transiciones típicas (máquina simple L2)

DesdeHaciaIntención
NEWCONTACTEDNerea inició contacto
CONTACTEDSEATEDHay silla; compromiso firme
CONTACTEDWAITLISTCupo cubierto / pendiente de baja
NEWWAITLISTMesa ya llena al abrir la solicitud
NEWSEATEDConfirmación inmediata en barra (atajo)
*CANCELLEDDesiste, duplicado, no localizable
WAITLISTSEATEDSe libera silla
SEATEDCANCELLEDBaja posterior / no-show
**Corrección operativa (API acepta enum)

Éxito: badge actualizado; lista refleja al volver.
Errores: 404 id; 401 sin token; 400 status inválido.

Regla de integridad H3 (ops, no trigger DB v1): HOST no debe marcar SEATED si seatedCount >= seats. La UI avisa; el API L2 v1 no rechaza (corrección humana). Rechazo duro = L2+.


F8 — Publicar mesa (HOST)

/publicar
  → validación cliente
  → POST /api/tables {
        title, mechanic, neighborhood,
        venue, address, startAt, seats,
        summary, description?
     }
  → 201 GameTable { slug, ... }
  → /mesas/:slug
CampoValidación clienteAPI
titlerequired, min 3min 3
mechanicrequired enumenum Mechanic
neighborhoodrequired enumenum Neighborhood
venuerequiredrequired
addressrequiredrequired
startAtrequired, datetime futuro o hoyISO-8601
seatsrequired, 2–12int 2–12
summaryrequired, max 180max 180
descriptionoptionaloptional
imageKeyoptionaldefault placeholder

Éxito: mesa aparece en GET /api/tables y en el feed.
Error: 401 sin token; 400 DTO.

[DECISIÓN] El slug se genera en servidor (kebab del título + sufijo si colisión). El HOST no elige slug.


F9 — Logout (HOST)

Click "Salir" en /yo o inbox
  → borra token cliente
  → (opcional) invalida fila en sessions
  → /login

Matriz de errores global

CódigoCuándoUI
400DTO inválidoMensaje campo / genérico
401Sin/mal tokenRe-login
404id o slug no existeMensaje + volver
5xx / networkAPI caídaError + retry

Eventos de dominio (para analytics / blueprint)

EventoDispara
table_openedClick card o deep-link ficha
rsvp_submittedPOST 201
host_loginPOST login 200
status_changedPATCH 200
table_publishedPOST tables 201

Flujos fuera de alcance v1

  • Registro / recuperación de password de HOST
  • “Mis solicitudes” por código público
  • Notificación email / WhatsApp automática
  • Filtros multi-criterio en inbox
  • Asignación multi-HOST por local
  • Pago / fianza
  • Edición / baja de mesa publicada por UI (solo alta)
  • Check-in QR en la puerta

05-data-model.md

05 — Modelo de datos — RONDA

1. Visión general

Dominio L2 de feed de mesas + cola de asiento:

EntidadTabla SQLPropósito
UserusersIdentidad de anfitrión (rol HOST)
SessionsessionsSesión JWT / token persistido
GameTablegame_tablesMesa publicada (oferta de esta noche)
SeatRequestseat_requestsSolicitud de asiento ligada a una mesa

Base: PostgreSQL (Neon project curly-lab-77015594) · Bootstrap: ensureSchema al arrancar la API · IDs: cuid() o uuid.

[DECISIÓN] Se llama game_tables y no tables (palabra reservada SQL).
[DECISIÓN] ensureSchema (CREATE TABLE IF NOT EXISTS + seed idempotente) en v1; no se exige Prisma para el slice.

2. Enums (aplicación; persistidos como TEXT + check)

Role

ValorDescripción
HOSTOperador del inbox y alta de mesas; único rol autenticado v1

RequestStatus

ValorDescripción
NEWRecién creada por el form público
CONTACTEDHOST ha iniciado contacto
SEATEDPlaza confirmada (silla en la mesa)
WAITLISTEn espera de silla
CANCELLEDAnulada

Mechanic

TILE | CARDS | MINIS | COOP | COLLECTION

Neighborhood

LAVAPIES | EMBAJADORES | CHAMBERI | MALASANA

3. Diagrama ER (texto)

users
  id, email, password_hash, name, role(HOST)
  created_at, updated_at
  1 ──< sessions
  1 ──< game_tables          -- host_id

sessions
  id, user_id → users
  token_hash, expires_at
  created_at

game_tables
  id, slug (unique), title, summary, description
  mechanic, neighborhood, venue, address
  start_at, seats, image_key
  host_id → users
  created_at, updated_at
  1 ──< seat_requests

seat_requests
  id, code (unique)
  table_id → game_tables
  full_name, email, phone?
  party_size (default 1)
  notes
  status (default NEW)
  created_at, updated_at

No hay FK entre seat_requests y users en v1: el form es anónimo. HOST opera sobre el conjunto global.

4. Tablas (ensureSchema)

users

CampoTipoConstraints
idTEXTPK
emailTEXTUNIQUE NOT NULL
password_hashTEXTNOT NULL (bcrypt)
nameTEXTNOT NULL
roleTEXTNOT NULL DEFAULT 'HOST'
created_atTIMESTAMPTZDEFAULT now()
updated_atTIMESTAMPTZDEFAULT now()

sessions

CampoTipoConstraints
idTEXTPK
user_idTEXTNOT NULL FK → users(id) ON DELETE CASCADE
token_hashTEXTNOT NULL
expires_atTIMESTAMPTZNOT NULL
created_atTIMESTAMPTZDEFAULT now()

[DECISIÓN] El JWT viaja en Authorization: Bearer. sessions permite invalidar (logout) sin esperar expiración del JWT.

game_tables

CampoTipoConstraints
idTEXTPK
slugTEXTUNIQUE NOT NULL, kebab-case
titleTEXTNOT NULL
summaryTEXTcard (~140)
descriptionTEXTficha; default ''
mechanicTEXTNOT NULL
neighborhoodTEXTNOT NULL
venueTEXTnombre del local
addressTEXTdirección corta
start_atTIMESTAMPTZhora de la mesa
seatsINTEGER> 0
image_keyTEXTfilename en assets (azulejos.jpg …)
host_idTEXTNOT NULL FK → users(id)
created_at / updated_atTIMESTAMPTZ

Índice recomendado: (start_at ASC) para el feed.

seat_requests

CampoTipoConstraints
idTEXTPK
codeTEXTUNIQUE NOT NULL, formato RD-MMDD-NNN
table_idTEXTNOT NULL FK → game_tables(id)
full_nameTEXTNOT NULL
emailTEXTNOT NULL
phoneTEXTNULL
party_sizeINTEGERDEFAULT 1, 1–2
notesTEXTDEFAULT ''
statusTEXTDEFAULT 'NEW'
created_at / updated_atTIMESTAMPTZ

Índice recomendado: (created_at DESC) para inbox; (table_id, status) para conteo SEATED.

5. Reglas de integridad y negocio

ReglaImplementación
Create públicoPOST /api/requests sin guard
List/statusJwtAuthGuard → 401 sin token
Publicar mesaPOST /api/tables con guard HOST
Catálogo públicoGET /api/tables y :slug sin guard
Código únicocode unique; RD-MMDD- + secuencia 001–999 del día [DECISIÓN] (retry NNN+1 si colisión, máx. 5)
Status defaultNEW al crear
PasswordNunca en claro; solo password_hash
NotasCoalesce a "" si omitidas
Orden inboxcreated_at desc
Orden feedstart_at asc (esta noche primero; no “destacados”)
Timezone códigos y “Hoy”Europe/Madrid [DECISIÓN]
SEATED vs seatsAviso UI; no check DB v1 (H3 operativa)
LogoutDELETE o expire de sessions + borrar token cliente

6. Contratos API

GET /api/tables (público)

Response 200: GameTable[] ordenado por start_at asc. Incluye campos de card + ficha; sin PII de solicitudes.

Query opcional L2+ (no Must): from, to ISO. v1 puede filtrar “hoy” en cliente.

GET /api/tables/:slug (público)

GameTable + hostName + seatedCount (agregado) o 404.

POST /api/tables (JWT HOST)

Body

CampoTipoReq
titlestring
mechanicMechanic
neighborhoodNeighborhood
venuestring
addressstring
startAtstring ISO
seatsnumber
summarystring
descriptionstringno
imageKeystringno

Response 201: GameTable con slug generado.

POST /api/requests (público)

Body

CampoTipoReq
tableId o slugstringsí (uno de los dos)
fullNamestring
emailstring
phonestringno
partySizenumberno (default 1)
notesstringno

Response 201: SeatRequest + table embebido mínimo (title, slug, startAt, venue).

POST /api/auth/login

Body: { email, password }
Response 200: { accessToken, user: { id, email, name, role } }
Efecto: inserta fila en sessions.

GET /api/requests (JWT HOST)

Array SeatRequest + table, created_at desc.

PATCH /api/requests/:id/status (JWT HOST)

Body: { status: RequestStatus }
Response 200: SeatRequest actualizado.

GET /api/requests/:id (JWT HOST) — Should

SeatRequest + table (seats) + seatedCount o 404.

7. Seed de referencia (2026-08-15)

HOST

NombreEmailPassword
Nerea Solísnerea@ronda.clubpassword123

[COMPROBADO] Credencial demo del brief.

game_tables

slugTítuloMecánicaBarrioLocalHoraSeatsAsset
azulejosAzulejos de LavapiésTILELAVAPIESCafé La Palma 1220:304azulejos.jpg
cartasCubilete y cartasCARDSEMBAJADORESBar El Sur19:005cartas.jpg
minisMazmorra de ChamberíMINISCHAMBERILocal asociativo18:005minis.jpg
cooperativoFarol cooperativoCOOPMALASANALibrería Tipos Infames21:004cooperativo.jpg
coleccionColección de patioCOLLECTIONMALASANATerraza Malasaña17:306coleccion.jpg

[COMPROBADO] Títulos, locales, horas y aforos salen del brief.

Direcciones extendidas, textos de reglas y start_at concreto (fecha del seed) son contenido demo. [SUPUESTO de catálogo]

Textos seed recomendados (no lorem):

slugSummaryReglas cortas (description)
azulejosTile-laying a las 20:30 en La Palma 12. Cuatro sillas, café y azulejos.Enseñamos la primera ronda. Trae ganas, no caja. Mesa de 4; si llegas tarde avisa.
cartasDados y baza en El Sur, Embajadores, 19:00. Cinco sillas.Cubilete compartido. Reglas en servilleta. No es cash game.
minisMazmorra corta en Chamberí, 18:00. Cinco sillas, minis de la casa.One-shot 2 h. Fichas y dados en la mesa. Nivel 1, sin ficha de personaje previa.
cooperativoFarol cooperativo en Tipos Infames, 21:00. Cuatro sillas.Perdemos juntos o no. Tráiler de reglas 5 min. Silencio de librería hasta las 21:10.
coleccionColección al sol de terraza en Malasaña, 17:30. Seis sillas.Sets y cartas al aire. Si llueve, se cancela en inbox.

seat_requests

CódigoNombreMesaEstadoNotas demo
RD-0815-001Leo NavasazulejosNEW“Llego del metro hacia Lavapiés; primera vez con azulejos.”
RD-0815-002Marta GilcartasCONTACTED
RD-0815-003Iago FreirecoleccionSEATED
RD-0815-004Youssef AmranicooperativoWAITLISTMesa de 4 ya cubierta en barra
RD-0815-005Pilar SotominisCANCELLEDDuplicada

El sufijo de fecha del código de seed es el día del caso; la generación runtime usa Europe/Madrid.

Idempotencia seed: upsert HOST por email; upsert mesas por slug; upsert requests por code. No duplicar al re-arrancar.

8. Generación de código

RD- + MMDD (Europe/Madrid) + - + NNN

NNN = siguiente entero del día, padded 3. Si unique falla, reintentar NNN+1 (máx. 5).
Ejemplo el 15 de agosto: RD-0815-001.

9. Evolución posible (no implementada)

CambioNivel
Check DB SEATED + party_size ≤ seatsL2+
Tabla venues normalizadaL3
Soft delete + audit log de statusL2+
User PLAYER con historialL3
party_size > 2 y reservas de grupoL3
Edición / cancelación de mesaL2+

06-tech-stack.md

06 — Stack tecnológico — RONDA

1. Visión

CapaTecnologíaNotas
FrontendAngular standalone + signalsPuerto 4200 · script pnpm start
EstilosTailwind CSSTokens signage (suelo/tinta/cromo/felt/liner/ficha)
BackendNestJS TypeScriptPuerto 3015, prefijo /api · script pnpm api
PersistenciaSQL + ensureSchemaTablas users, sessions, game_tables, seat_requests
DBNeon PostgreSQLproject curly-lab-77015594
AuthJWT + tabla sessionsRol HOST
DiseñoPaper01M023RM2TZ49CYM2FN1FRRGNY
Repo/Users/cristian/orca/ronda-app/GitHub Criscode2022/ronda-app
Package managerpnpmapps independientes vía pnpm --filter

Stack fijo del cron (D-P0-03). No React/Next/Firebase.
[COMPROBADO] Puerto 3015, Neon y pnpm constan en el brief de implementación.

2. Estructura

ronda-app/
├── apps/api/     # Nest + ensureSchema (paquete independiente)
├── apps/web/     # Angular + Tailwind (paquete independiente)
├── package.json  # scripts api / start + pnpm filter
└── pnpm-workspace.yaml

D-P1-03 (adaptado): apps independientes. [DECISIÓN] pnpm workspaces + --filter (no npm workspaces que rompen Angular; no npm --prefix si el brief pide pnpm).

Scripts raíz esperados:

ScriptAcción
pnpm apiArranca Nest en :3015
pnpm startArranca Angular en :4200
pnpm --filter api …Comandos solo API
pnpm --filter web …Comandos solo web

3. API pública vs JWT

MétodoRutaAuth
GET/api/tablesPúblico
GET/api/tables/:slugPúblico
POST/api/requestsPúblico
POST/api/auth/loginPúblico
GET/api/requestsJWT HOST
PATCH/api/requests/:id/statusJWT HOST
POST/api/tablesJWT HOST
GET/api/requests/:idJWT HOST (Should)

CORS: origen http://localhost:4200 en local.

4. Justificación

ElecciónRazón
Angular + NestAlineación con la serie daily y el handoff del estudio
Neon serverlessVolumen bajo (5–12 mesas/semana, S1); no hace falta cola Redis
ensureSchemaBootstrap diario rápido; 4 tablas; seed idempotente al boot
JWT + sessionsD-P1-05: panel interno cerrado; logout invalidable
Feed en servidor, recorte Hoy en cliente o querySource of truth = start_at; no Elasticsearch
Puerto 3015Evitar colisión con DERIVA :3014 y PIZARRA :3013

5. Variables

.env local (nunca git):

VariableServicioDescripción
DATABASE_URLAPINeon curly-lab-77015594
JWT_SECRETAPIFirma tokens
PORTAPIopcional, 3015
JWT_EXPIRESAPIopcional (p. ej. 12h)

.env.example solo placeholders.
Web: URL de API en ApiService (default http://localhost:3015/api).

6. Dependencias de producto (mínimas)

API

  • @nestjs/common / core / platform-express
  • @nestjs/jwt + passport-jwt (o verificación manual)
  • bcrypt (o bcryptjs)
  • cliente Postgres (pg)
  • class-validator + class-transformer

Web

  • Angular standalone
  • Tailwind 3
  • fuentes: Zilla Slab + Mulish (Google Fonts o self-host)

7. Arranque (contrato)

cd /Users/cristian/orca/ronda-app
pnpm install
# apps/api/.env → DATABASE_URL + JWT_SECRET
pnpm api      # http://localhost:3015
pnpm start    # http://localhost:4200

Al boot, la API ejecuta ensureSchema y seed si las tablas están vacías o por upsert.

8. Lo que este stack no es

NoPor qué
Prisma obligatorioBrief pide ensureSchema
npm workspacesAP-06
Firebase AuthStack fijo JWT + Neon
Next.js / ReactD-P0-03
Mapa / tilesFuera de producto

07-creative-direction.md

07 — Dirección creativa — RONDA

1. Mood

Signage — cartelería de local de barrio: pizarra de tiza sustituida por placa esmaltada, número de mesa, hora en negro y un amarillo de semáforo que dice “abierto esta noche”.

No es taberna candlelit (primer instinto). No es dusk cartográfico (DERIVA). No es wine de editorial (PLIEGO). No es chalky de aula (PIZARRA).

Candidatos del brief y veredicto:

CandidatoVeredicto
Taberna candlelitDescartado — cerca de DERIVA dusk / PLIEGO wine
Felt casinoDescartado — implica apuesta
Phosphor arcadeDescartado — 8-bit, no mesa de café
SignageElegido
AlpineDescartado — outdoor, no barrio

[COMPROBADO] Mood signage y paleta salen de 00-day-brief.md.
[DECISIÓN] R-HY = flyer clásico de caja de juego (titular Zilla, foto de mesa, hora grande) + dock flotante innovador usable.

2. Paleta

RolTokenHexReferente
Suelobg#FFFFFFPlaca / papel de carta del local
Tintaink#111111Serigrafía de cartel
Cromochrome#F5C400Amarillo de “abierto” / dock activo
Feltfelt#1F4D3APaño de mesa, SEATED
Linerliner#FFF6C2Reverso de caja, cards
Fichachip#E24B2AFicha naranja, WAITLIST, urgencia
Tinta suaveink-muted#3A3A3AMeta (barrio, mecánica)
Peligrodanger#E24B2AMisma ficha; no introducir rojo extra

[COMPROBADO] Hex literales del brief: #FFFFFF #111111 #F5C400 #1F4D3A #FFF6C2 #E24B2A.

Combinaciones legales

SuperficieTextoUso
#FFFFFF#111111Body, feed
#FFF6C2#111111Cards
#F5C400#111111CTA primario, dock activo
#1F4D3A#FFFFFFBadge SEATED, superficie de mesa
#111111#FFFFFFCTA secundario, wordmark invertido
#E24B2A#FFFFFFBadge WAITLIST (texto ≥14px bold)

No usar cromo 13px sobre blanco como único indicador (contraste de acento, no de texto).

3. Tipo

RolFamiliaCortesUso
DisplayZilla Slab56 / 36 / 24 · 600–700Wordmark, hora, título de mesa
UIMulish16 / 14 · 400/600/700Body, labels, form, dock

[COMPROBADO] Familias y escalas del brief.

Evitar (cooldown / contaminación de serie): Lora, Karla, Newsreader, Atkinson, Outfit, Archivo, Fraunces.

4. Principios

  1. La cronología es el monumento. Hora grande, no hero 2 columnas.
  2. Radio 0–6px. Cartel, no píldora atlética.
  3. Foto real de la mesa, nunca color sólido ni image_gen como captura de producto (D-P0-09).
  4. Copy honesto: solicitud ≠ plaza.
  5. Dock flotante siempre. Cinco slots, cromo en el activo.
  6. Contraste AA tinta sobre blanco/liner; labels ≥14px.
  7. Flyer + dock: el card parece recorte de caja; la navegación no parece caja.

5. Referentes visuales (no clonar)

ReferenteQué se tomaQué se deja
Cartel de bar de copasHora, dirección, tinta/amarilloFoto de copas, script caligráfico
Inserto de caja de juegoFoto de componentes, título slabRating BGG, precio, código de barras
Señal de metro MadridClaridad, pocos coloresPictogramas de línea
Casino feltVerde profundo puntualFichas por todas partes

6. Art direction de media

AssetDebe verseNo debe verse
hero.jpgAtmósfera de mesa / local, luz realMockup de UI, stock de dados genérico recortado mal
azulejos.jpgAzulejos / tiles en mesa de caféPackaging de Amazon
cartas.jpgCartas y cubilete, barraPoker cash agresivo
minis.jpgMiniaturas, mazmorra de salónWargame militar realista bélico
cooperativo.jpgMesa compartida, libreríaEscape room de ticket
coleccion.jpgTerraza, tarde, colecciónClub de playa

7. Motion

GestoSpec
Entrada feedFade 160ms, stagger ≤40ms por card
DockTranslateY 0; no hide-on-scroll en v1
Badge estadoCrossfade 120ms
ProhibidoParallax, scroll-jacking, confetti de “reserva”

8. Anti-dirección

NoPor qué
Dusk teal / mapaDERIVA
Chalky pizarra escolarPIZARRA
Slate/teal SaaSCooldown de serie
Hero 2-col + 3 stepsAP-12
Rounded-full limeApp de fitness
Emoji como icono de mecánicaSignage usa palabra, no sticker

08-design-system.md

08 — Design system — RONDA

1. Tokens

Ver Paper UI-00 y apps/web/tailwind.config.js.

Token TailwindValorUso
ronda-bg#FFFFFFFondo de página
ronda-ink#111111Texto, bordes fuertes
ronda-chrome#F5C400Acento, CTA, dock activo
ronda-felt#1F4D3ASEATED, superficies de mesa
ronda-liner#FFF6C2Cards, rails, fondo dock
ronda-chip#E24B2AWAITLIST, asientos bajos
ronda-muted#3A3A3AMeta
font-displayZilla SlabTitulares
font-sansMulishUI
radius-sm2pxChips de mecánica
radius-md6pxCards, inputs
radius-lg12pxDock container

Tipo — escala

EstiloFamiliaSize / linePesoUso
Display XLZilla Slab56 / 60700Portada, hora hero en ficha
Display LZilla Slab36 / 40700Título mesa, “Esta noche”
Display MZilla Slab24 / 28600Sección, código RD-
BodyMulish16 / 24400Párrafos, form
MetaMulish14 / 20600Barrio, mecánica, dock label
MicroMulish12 / 16600Badge; no usar en cromo sobre blanco

Espaciado

Escala 4: 8 / 12 / 16 / 24 / 32 / 48.
Feed: gap 16 entre cards. Dock: padding 8, gap 4 entre slots. Ficha: bloque 24.

2. Componentes

PiezaSpec
DockFijo bottom; fondo liner 92% + blur; 5 slots 56×48; icono 20 + label 11–12; activo = cromo + tinta
Wordmark“RONDA” Zilla 24/700 tracking amplio; subtítulo “Esta noche hay mesa.” Mulish 14
Card de mesaFoto 16/10 object-top; hora Zilla 24; título; local · barrio; asientos n/seats; mecánica label
Button primaryFondo cromo, texto tinta, h-44, radius 6
Button secondaryFondo tinta, texto blanco
Button ghostBorde tinta 1px, fondo transparente
Button waitlistFondo ficha, texto blanco; label “Lista de espera”
Badge NEWcromo / tinta
Badge CONTACTEDliner / tinta
Badge SEATEDfelt / blanco
Badge WAITLISTficha / blanco
Badge CANCELLEDliner / muted; line-through opcional
Form fieldLabel Mulish 14/700 visible; borde tinta 1px; h-44; focus outline cromo 2px offset 2
DisclaimerFondo liner, borde izquierda cromo 4px, texto 14
SkeletonBloques liner, 5 filas feed; sin shimmer agresivo
EmptyTitular Zilla 24 + 1 acción
ErrorBanner tinta + CTA Reintentar cromo

3. Layouts

Feed (UI-01 / UI-10)

[ wordmark + fecha ]
[ card ]
[ card ]
[ card ]
[ … ]
[ dock flotante ]

Desktop ≥960px: cards a 640–720px centradas (columna de cartel), no grid 3×N de marketplace.
[DECISIÓN] Una columna refuerza H-FEED y evita “catálogo de SKU”.

Ficha (UI-02)

Foto full-bleed de la columna → hora + título → meta (local, barrio, asientos, HOST) → reglas → disclaimer → CTA sticky sobre el dock.

Inbox (UI-06)

Lista densa: código · nombre · mesa · badge. Desktop puede añadir columna de acciones.

4. Estados

EstadoSuperficieContenido
Empty feed hoyUI-07“Esta noche no hay mesa.” + Ver la semana
Empty inboxUI-07“Nadie ha pedido asiento todavía.”
LoadingUI-095 skeletons
Error redUI-08“No hemos podido cargar las mesas.” + Reintentar
Validación emailFormTexto bajo campo, no solo borde
401 inboxLoginRedirect /login?next=/inbox
Aforo 0Ficha / formCTA WAITLIST; disclaimer igual

5. Iconografía del dock

SlotSignificado visualNo
HoyReloj / sol bajoEmoji 🔥
MesasStack de cartas / mesa vista cenitalPin de mapa
Publicar+ en placaFAB Material genérico desligado del dock
InboxBandejaBurbuja de chat Discord
YoFigura simpleAvatar foto real v1

Iconos 20×20 stroke 1.75, tinta. Activo: trazo tinta sobre disco cromo.

6. Breakpoints

NombreAnchoComportamiento
Mobile390Feed + dock; CTA 100%
Tablet768Columna 600, dock igual
Desktop960+Columna 680; inbox puede 2 col

Targets táctiles ≥44px (dock slots 56×48 cumple).

7. No usar

ProhibidoMotivo
Rounded-full lime / tealFitness / SaaS
Hero 2-col + 3 cardsAP-12
Badges “SaaS pill” pastelRompe signage
Emoji como icono de mecánicaVer 07
Top-nav sticky de 7 linksRompe S-DOCK
Grid marketplace 3×NRompe H-FEED
Confetti al successMentiría “ya tienes silla”

09-content-guide.md

09 — Guía de contenido — RONDA

1. Voz

Cercana de barrio, concreta, de barra. Tuteo. Segunda persona.
Sin jerga de marketplace (“experiencia premium”), sin Discord (“únete al server”), sin sede electrónica.

El producto informa y recoge, no vende una noche mágica.

2. Palabras permitidas / prohibidas

UsarNo usar
Solicitud / pedir asientoReserva confirmada, booking, “ya estás dentro”
El anfitrión te escribe“Plaza garantizada”, “ticket emitido”
Lista de esperaOverbooking, sold out (tono concierto)
MesaEvento, sesión, experience, lobby
Local / café / bar / terrazaVenue, campus, store
Esta noche hay mesa“Descubre nuestro ecosistema”
Código RD-Referencia UUID, localizador de vuelo
Sentada / SEATEDInscrita, matriculada, enrolled
Anfitrión / HOSTCommunity manager, dungeon master (salvo mesa minis)

3. Microcopy clave (literales de producto)

SuperficieCopy
EsloganEsta noche hay mesa.
HomeHoy en Malasaña, Lavapiés y Chamberí.
Card CTA implícitoToda la card es el enlace; no “Ver más”
Disclaimer fichaPedir asiento no confirma la silla. El anfitrión te escribe.
Form títuloPedir asiento en {título}
Form submit (hay hueco)Enviar solicitud
Form submit (lleno)Pedir lista de espera
Form disclaimerEsto es una solicitud, no una plaza confirmada.
Success titularSolicitud enviada.
Success cuerpoGuarda este código. No tienes silla todavía. Nerea (o el anfitrión) te contacta antes de la hora.
Success códigoRD-0815-001 (seleccionable)
Empty hoyEsta noche no hay mesa.
Empty hoy CTAVer la semana
Empty inboxNadie ha pedido asiento todavía.
Error feedNo hemos podido cargar las mesas.
Error retryReintentar
Login titularAnfitrión
Login submitEntrar
Login errorEmail o contraseña no valen.
Inbox titularSolicitudes
PATCH SEATEDSentar
PATCH WAITLISTLista de espera
PATCH CONTACTEDMarcar contactada
PATCH CANCELLEDCancelar
Publicar titularAbrir mesa
Publicar submitPublicar esta noche
Yo / salirSalir

[DECISIÓN] El disclaimer aparece tres veces: ficha, form, success. No es opcional.

4. Códigos

Formato RD-MMDD-NNN. Se lee en voz alta en la barra: “erre de, ocho quince, cero cero uno”.
No UUID. No QR obligatorio en v1.

5. Nombres propios del seed

NombreUso
Leo NavasPLAYER; solicitud azulejos
Nerea SolísHOST; copy de “te escribe Nerea” en success de seed
Café La Palma 12Local azulejos
Bar El SurLocal cartas
Local asociativo (Chamberí)Local minis
Librería Tipos InfamesLocal cooperativo
Terraza MalasañaLocal colección

No inventar reseñas de Google ni horarios oficiales de esos locales. [SUPUESTO de catálogo demo]

6. Tono por estado

StatusFrase inbox (HOST)Frase que Nerea podría mandar (fuera de app)
NEWSin contacto
CONTACTEDEscrita / llamada“He visto tu RD-0815-001. ¿Llegas a las 20:30?”
SEATEDSilla“Tienes silla. Trae el código.”
WAITLISTEspera“La mesa de 4 está llena. Te aviso si hay baja.”
CANCELLEDFuera“Cancelamos esta solicitud.”

La app no envía esos mensajes en v1. El copy solo prepara el habla.

7. Capitalización y números

  • Wordmark: RONDA en versales.
  • Horas: 20:30 (24 h, Europe/Madrid), no 8:30 PM.
  • Asientos: 2/4 sillas (ocupadas estimadas / total) cuando hay dato; si no, 4 sillas.
  • Barrios: Malasaña, Lavapiés, Chamberí, Embajadores (tilde correcta).

8. Idioma

es-ES. Fácil lectura. EN no en v1.
No mezclar “table” en UI. En API sí: game_tables, tableId.

9. Accesibilidad de copy

  • No transmitir estado solo con color (“el amarillo significa nueva”).
  • Badge lleva texto: Nueva, Contactada, Sentada, Lista de espera, Cancelada.
  • Alt de fotos: “Azulejos sobre mesa de café en Lavapiés”, no “imagen1”.

10. Piezas que no se escriben

NoMotivo
Precios, “consumición mínima”RONDA no cobra; el local es otro contrato
“Plazas confirmadas al instante”Rompe H2
Ranking de anfitrionesReputación, otro producto
Lorem / “lorem ipsum mesa”Prohibido en el case

10-accessibility.md

10 — Accesibilidad — RONDA

Objetivo: WCAG 2.2 AA. No se declara conformidad legal certificada.
Este documento cubre la UI (feed, form, dock, inbox). No audita la accesibilidad física de los locales seed. [SUPUESTO]

1. Decisiones

TemaDecisión
Tipo UIMulish ≥16px body
DisplayZilla Slab no se usa por debajo de 24px
ContrasteTinta #111111 sobre #FFFFFF / #FFF6C2
CTA cromoTexto tinta sobre #F5C400 (no cromo como texto pequeño)
Felt / fichaTexto blanco ≥14px bold
FocoOutline 2px cromo, offset 2px, visible (no outline-none global)
Dock5 botones reales (<a> / <button>), no divs clicables
Imágenesalt descriptivo de la mesa
FormLabels visibles, no placeholder-only
ErroresTexto, no solo color
MobileTargets ≥44px (dock 56×48, CTA h-44)
MovimientoFade 160ms; prefers-reduced-motion: reduce → 0
Live regionsaria-live="polite" en recuento empty/error del feed

2. Contraste (comprobación de diseño)

ParUsoNota
#111111 / #FFFFFFBodyPasa AA y AAA cuerpo
#111111 / #FFF6C2CardsPasa AA
#111111 / #F5C400CTA / dock activoPasa AA para texto ≥14px bold
#FFFFFF / #1F4D3ABadge SEATEDPasa AA
#FFFFFF / #E24B2ABadge WAITLISTVerificar ≥14px bold; no usar en 12px
#F5C400 / #FFFFFF como textoProhibido para body

3. Teclado

FlujoOrden
FeedWordmark → primera card → … → dock (Hoy, Mesas, Publicar, Inbox, Yo)
FichaBack → CTA → dock
FormNombre → email → teléfono → notas → submit → dock
InboxLista (cada card) → detalle acciones → dock
LoginEmail → password → submit
  • Enter en card = navegar a ficha.
  • Escape no cierra el dock (no es modal).
  • Focus trap solo si hubiera modal; v1 no tiene modal salvo confirmación CANCELLED (Should).

4. Semántica

PiezaMarkup
Feed<main> + lista <ul> de cards; cada card un <article> o <li> con un único <a>
Hora<time datetime="…">
Dock<nav aria-label="Principal">
Slot activoaria-current="page"
Form<form> + <label for>
DisclaimerNo es alert; es texto normal + borde
Success código<p><code> seleccionable
Badgestexto visible; no solo icono
Skeletonsaria-busy="true" en el contenedor; aria-hidden en placeholders

5. Lector de pantalla — copy

SituaciónAnuncio
Card“Azulejos de Lavapiés, veinte treinta, Café La Palma 12, Lavapiés, cuatro sillas, tile-laying”
CTA lleno“Apuntarme a la lista de espera, Azulejos de Lavapiés”
Success“Solicitud enviada. Código erre de ocho quince cero cero uno. No tienes silla todavía.”
ErrorAnuncio en live region; foco al banner

6. Producto vs local físico

Lo que RONDA puede hacerLo que no afirma
Hora, dirección, aforo de sillas de juegoRampa, baño accesible, bucle magnético del café
Form usable con teclado y zoom 200%Que La Palma 12 sea un local accesible

[DECISIÓN] v1 no incluye faceta a11y de sede (eso era PIZARRA). Un campo libre notes permite “voy en silla” sin fingir auditoría.

7. Riesgos

RiesgoMitigación
Dock tapa el CTA en móvilPadding-bottom del main ≥ 80px; CTA de ficha por encima
Zilla Slab en meta 12pxProhibido; meta = Mulish 14
Cromo como único estado activo+ aria-current + label “Hoy”
Foto sin altChecklist seed: 5 alts
Status bar iOS en PaperNo es producto
Motion del dock bounceNo bounce; reduced-motion = estático

8. QA a11y (mínimo)

#PruebaPasa si
1Teclado feed → ficha → form → successSin trampa; foco visible
2Teclado login → inbox → PATCHAcciones alcanzables
3Zoom 200% móvil 390Dock usable; no solapa inputs
4Lighthouse a11y ≥ 90 en /Sin contrast fails de tokens
5VoiceOver/NVDA: legend/hora anunciadosCard comprensible
6prefers-reduced-motionSin stagger
7Contraste badges WAITLIST / SEATEDTexto blanco bold ≥14

9. Criterios de aceptación

  1. Ningún control del dock o CTA mide menos de 44×44 CSS px.
  2. El disclaimer no depende del color cromo para ser entendido.
  3. Las 5 fotos seed tienen alt no vacío y no genérico.
  4. El foco no se pierde al reintentar un error de red.

11-privacy-security.md

11 — Privacidad y seguridad — RONDA

No es un dictamen legal ni un DPIA. Es el contrato de producto para el vertical slice.

1. Datos

DatoTablaClasificaciónUso
Nombre, email, teléfonoseat_requestsPIIContactar solicitud
Notasseat_requestsPII opcionalContexto HOST
party_sizeseat_requestsOperativoAforo
Mesa, estado, códigoseat_requests + game_tablesOperativoCola y sillas
Email HOST, password hashusersCredencialSolo HOST
token_hash, expires_atsessionsSecreto de sesiónLogout / caducidad

Sin datos de salud, menores con tutor, geolocalización continua, ni pagos.

[DECISIÓN] El jugador no crea cuenta. Menos superficie de credenciales.

2. Base (supuesto de diseño)

[SUPUESTO] Interés del HOST en organizar una mesa privada/pública de ocio + consentimiento de contacto al enviar el form.
El texto del form debe mencionarlo:

Usaremos tu nombre y email (y teléfono si lo dejas) solo para escribirte por esta mesa.

No se cede a editoriales, no se usa para marketing de terceros.

3. Minimización

RecogemosNo recogemos en v1
fullName, emailDNI, fecha de nacimiento
phone opcionalDirección de casa
notes libresFoto de perfil
partySize 1–2Lista de juegos que posee

[DECISIÓN] Teléfono opcional: el email basta para el acuse; el HOST prefiere WhatsApp en la práctica [HIPÓTESIS], pero no exigimos el número.

4. Retención

DatoHipótesis operativa
Solicitudes12 meses desde start_at de la mesa; luego borrado o anonimización (full_name → “—”, email hash)
SessionsHasta expires_at o logout
MesasMientras el HOST no las archive (archivo = L2+)
Demo NeonNo es producción; se puede resetear

[HIPÓTESIS] 12 meses cubre una temporada de mesa y reclamaciones de “yo escribí”. No es plazo legal afirmado.

5. Autorización

RecursoPúblicoHOST JWT
GET tables / :slug
POST requests
GET / PATCH requests401
POST tables401
Password hashesNunca en responseNunca

Guards en API son la autoridad. La UI solo esconde.
[COMPROBADO] D-P1-05: panel interno cerrado desde el día 1.

JWT + sessions

  1. Login válido → bcrypt compare → insert sessions → emite JWT (sub, email, role, sid).
  2. Request protegida → verifica firma + sid no expirado.
  3. Logout → expire/delete session; el JWT deja de valer aunque no haya caducado.

[DECISIÓN] Bearer header, no cookie de primer partido en v1 (demo localhost). CSRF residual bajo; XSS sigue siendo el riesgo (no innerHTML de notas sin escape).

6. Seguridad v1

ControlDetalle
TLS hacia NeonConnection string sslmode
Validación DTOsclass-validator
Passwordbcrypt cost ≥ 10
SecretosFuera de git; .env local
Público no lista PIIGET tables sin emails de requests
Rate limit POSTL2+; documentado como hueco
HoneypotL2+
CORSOrigen web conocido

7. Amenazas y respuesta

AmenazaImpactoRespuesta v1
Enumeración de inboxPII de jugadores401 sin token
Spam de RSVPCola inútilValidación; rate-limit L2+
Token robado en localStorageSuplantación HOSTExpiración corta; sessions invalidables
Overposting statusCaos de aforoEnum cerrado; H3 operativa
IDOR :idVer PII ajenaUn solo HOST en v1; igual exigir JWT
Seed en producciónCredencial conocidaRotar JWT_SECRET y password si se publica

8. Credencial demo

CampoValor
Emailnerea@ronda.club
Passwordpassword123

[COMPROBADO] Brief. No reutilizar en un deploy público sin rotar.

9. Riesgos residuales (aceptados L2)

  • No hay checkbox RGPD formal (Q3).
  • No hay cifrado de columna PII (TLS en tránsito + Neon en reposo).
  • localStorage XSS.
  • Un HOST ve todas las solicitudes (cola única de demo).

10. Criterios de aceptación

  1. GET /api/requests sin Authorization401.
  2. PATCH status sin token → 401.
  3. POST /api/tables sin token → 401.
  4. GET /api/tables no incluye emails de jugadores.
  5. Response de login no incluye password_hash.
  6. .env no se commitea.

12-analytics.md

12 — Analítica — RONDA

Instrumentación modelo. v1 puede no emitir eventos reales; este doc es el contrato para cuando se encienda.
No se presentan tasas de conversión inventadas.

1. North star

% de solicitudes válidas (nombre + email + mesa existente) que pasan a CONTACTED antes de startAt de esa mesa.

Por qué no “mesas llenas” ni “pageviews”:

Métrica vanidosaPor qué no es éxito
Visitas al feedUn cartel de WhatsApp también se mira
Mesas con 0 huecos SEATEDPuede ser overbooking (falla H3)
Cuentas creadasEl jugador no tiene cuenta

La north star mide contacto honesto a tiempo, alineada a S3/H2.

2. Hipótesis ↔ señales

IDHipótesisEvento / invariante
H1El feed reduce abandono vs listado o WhatsApp% sesiones con table_opened
H2Copy “solicitud ≠ plaza” baja sillas fantasma↓ tickets “pensé que tenía silla”; ratio SEATED que no aparecen (fuera de app)
H3SEATED nunca supera seatsseated_count ≤ seats por mesa

3. Eventos

EventoDóndeProps (sin PII en claro)
feed_viewed/ o /mesasrecorte (hoy/semana), result_count
table_openedCard / fichaslug
rsvp_startedFormslug
rsvp_submittedPOST 201code, slug, waitlist_cta (bool)
rsvp_failedPOST errorreason (validation/network/4xx)
host_loginLogin 200
host_login_failed401
inbox_viewed/inboxcount
status_changedPATCH 200from, to, slug
table_publishedPOST tables 201slug, seats

[DECISIÓN] No enviar email ni teléfono a analytics. El code es el identificador.

4. Funnel

Land feed → table_opened → rsvp_started → rsvp_submitted
         → host_login → status_changed(CONTACTED) ≤ startAt
         → status_changed(SEATED)  |  WAITLIST
PasoDefinición
Landfeed_viewed
Interéstable_opened / feed_viewed → H1
Intenciónrsvp_started / table_opened
Capturarsvp_submitted / rsvp_started
OpsCONTACTED antes de startAt → North star
CierreSEATED o WAITLIST; SEATED ≤ seats → H3

5. Invariantes (salud, no vanity)

CheckQuery conceptualAlerta
H3COUNT(*) FILTER (status='SEATED') > seatsCualquier mesa
Códigos únicosduplicate code> 0
401 inesperadosGET tables 401No debe ocurrir
5xxcualquier ruta> 1%

6. Dimensiones

DimensiónValores
recortehoy, semana
mechanicTILE, CARDS, MINIS, COOP, COLLECTION
neighborhoodLAVAPIES, EMBAJADORES, CHAMBERI, MALASANA
viewportmobile ≤480, desktop

Sin user-id de jugador.

7. Privacidad de analítica

  • Sin fingerprinting.
  • Sin email en claro.
  • IP no se guarda en el plano de producto.
  • Eventos de demo pueden quedarse en consola.

8. Qué no medimos en v1

NoMotivo
HeatmapsCraft, no research de campo fingido
NPSMuestra nula
“Minutos en mesa”No hay check-in físico
Followers de HOSTNo es red social

9. Criterios de aceptación (si se implementa tracker)

  1. Cada evento de la tabla §3 tiene nombre estable snake_case.
  2. rsvp_submitted incluye code con prefijo RD-.
  3. Ningún payload de analytics incluye email o phone.
  4. El dashboard interno (si existe) no es Must del L2.

13-qa-test-plan.md

13 — Plan de pruebas — RONDA

1. Smoke obligatorio (D-P1-06)

El brief de implementación fija el mínimo:

#AcciónEsperado
1GET /api/tables200 · array length ≥ 5 · slugs seed presentes
2POST /api/requests (slug real, nombre + email)201 · code con prefijo RD- · status=NEW
3POST /api/auth/login nerea@ronda.club / password123200 · accessToken · user.role=HOST
4GET /api/requests con Bearer200 · incluye el POST reciente
5pnpm start / ng buildWeb arranca o build OK

[COMPROBADO] Smoke = GET tables + POST request + login JWT.

2. Casos funcionales

IDCasoEsperado
S1Feed ordenstart_at ascendente; coleccion 17:30 antes que minis 18:00
S2Slug azulejosGET /api/tables/azulejos 200 · venue La Palma 12 · seats 4
S3Slug inexistente404
S4RSVP email inválido400; form no navega a /ok
S5RSVP mesa llena (copy)CTA “lista de espera”; POST sigue 201 NEW
S6SuccessCódigo visible; copy “no tienes silla todavía”
S7Inbox sin JWT401 / redirect login
S8PATCH CONTACTED200 · badge Contactada
S9PATCH SEATED200
S10PATCH WAITLIST200
S11PATCH CANCELLED200
S12POST /api/tables sin token401
S13POST /api/tables con JWT201 · aparece en GET tables
S14Login malo401 · mensaje en form
S15Empty feed (DB sin mesas de hoy)UI-07 + CTA semana
S16API caídaUI-08 + Reintentar conserva la vista
S17LoadingUI-09 skeletons antes del primer paint de datos
S18Mobile 390Dock visible; CTA ≥44px; no hero 2-col
S19Fotos seed5 image_key resuelven a assets
S20Código formatoregex ^RD-\d{4}-\d{3}$
S21GET requests no filtra PII al públicosin token 401 (no 200 [])
S22Password no viaja de vueltalogin body response sin hash

3. Datos de prueba

UsoValor
HOSTnerea@ronda.club / password123
PLAYERLeo Navas · leo.navas@example.com
Mesa felizazulejos
Mesa waitlist democooperativo (seed YA WAITLIST)
Código seedRD-0815-001

4. Regresión de diversidad / craft

CheckFalla si
Home es feedHay hero 2-col + 3 cards de marca (AP-12)
Shell es dockHay sidebar CRM o command search como nav primaria
Copy honestoSuccess dice “reserva confirmada”
PaletaSe cuelan dusk/teal/chalky de días previos
TipoNewsreader / Atkinson / Outfit

5. Hipótesis (no se “prueban” en la demo)

H1, H2, H3 requieren uso real. El QA verifica que existen las superficies que permitirían medirlas (tap en card, disclaimer, badge SEATED + seats).

6. Criterios de salida QA v1

  • Smoke §1 en verde.
  • S2, S6, S7, S12, S18, S20 en verde.
  • Cero blockers de copy deshonesto.
  • Sin .env en el repo.

7. Fuera de este plan

Playwright e2e completo, carga, fuzzing, auditoría de locales físicos.

14-dev-handoff.md

14 — Handoff desarrollo — RONDA

1. Arranque

cd /Users/cristian/orca/ronda-app

# apps independientes · pnpm filter
pnpm install

# apps/api/.env
#   DATABASE_URL=  # Neon curly-lab-77015594
#   JWT_SECRET=
#   PORT=3015

pnpm api      # Nest → http://localhost:3015
pnpm start    # Angular → http://localhost:4200

Al boot: ensureSchema crea users, sessions, game_tables, seat_requests y semilla idempotente.

Demo: nerea@ronda.club / password123.

2. Paridad Paper

Debe verse en AngularArtboard
Tokens signage + Zilla/MulishUI-00
Feed cronológico + dockUI-01, UI-10
Ficha con foto realUI-02
RSVP + disclaimerUI-03
Success RD-UI-04
Login HOSTUI-05
Inbox estadosUI-06
Empty / error / loadingUI-07…09

No clonar DERIVA (mapa split) ni PIZARRA (command search).

3. Contratos

TemaContrato
Login{ accessToken, user } camelCase (no access_token)
FeedGET /api/tables → array ordenado por startAt
FichaGET /api/tables/:slug
RSVPPOST /api/requests body slug o tableId + fullName + email
InboxGET /api/requests Bearer
StatusPATCH /api/requests/:id/status { status }
PublicarPOST /api/tables Bearer
CódigoRD-MMDD-NNN Europe/Madrid
Status enumNEW | CONTACTED | SEATED | WAITLIST | CANCELLED

Campos JSON en camelCase hacia el web aunque SQL sea snake_case. [DECISIÓN]

4. Mapa de rutas web

PathGuard clientePágina
/noFeed Hoy
/mesasnoFeed semana
/mesas/:slugnoFicha
/mesas/:slug/asientonoRSVP
/oknoSuccess
/loginnoLogin
/inboxsoft (redirect)Inbox
/inbox/:idsoftDetalle
/publicarsoftAlta mesa
/yosoftSesión

Soft guard: si no hay token, /login?next=. La API es la autoridad.

5. Assets

Copiar desde el case:

2026-08-15-ronda/assets/hero.jpg
2026-08-15-ronda/assets/tables/*.jpg
  → apps/web/src/assets/tables/

image_key del seed = filename (azulejos.jpg, …).

6. DoD implementación L2

CheckOK
pnpm api sirve :3015
ensureSchema 4 tablas
Seed 1 HOST + 5 mesas
Smoke GET + POST + login
JWT protege requests y POST tables
Web feed + dock + ficha + form + success + inbox
Disclaimer ×3
Tokens Tailwind = paleta brief
ng build o equivalente OK
README del repo app con credencial

7. No hacer

ProhibidoMotivo
npm workspacesAP-06
Commit de .envSecreto
Prometer plaza en copyH2 / AP-09
Tabla SQL tablesReservada → game_tables
Estado ENROLLEDVocabulario PIZARRA
Search bar como homeRompe H-FEED
Mapa LeafletRompe terna
image_gen como captura de productoD-P0-09

8. Contactos de diseño (artefactos)

ArtefactoPath
Briefux-projects/2026-08-15-ronda/docs/00-day-brief.md
Paperhttps://app.paper.design/file/01M023RM2TZ49CYM2FN1FRRGNY
Datosdocs/05-data-model.md
Flujosdocs/04-user-flows.md
IXdocs/16-interaction-specs.md
Builddocs/20-implementation.md

15-roadmap.md

15 — Roadmap — RONDA

1. Hecho en L2 (esta ejecución documental)

EntregaEvidencia
Definición producto + ternadocs 00-brief, 01
Personas Leo / Nerea, JTBD, journeydocs 02
IA feed + dockdocs 03
Flujos F-BOOK + HOSTdocs 04
Modelo 4 tablas + seed 5 mesasdocs 05
Stack Angular/Nest/Neon/JWT/pnpm :3015docs 06, 20
Dirección signage + DSdocs 07, 08
Copy honesto solicitud ≠ plazadocs 09
Paper ref 12 UX + 11 UIdocs 00-paper-reference
Suite 00–20 + README + executivecase folder

2. Implementación L2 (misma complejidad, código)

Contrato en docs 14 y 20. No es deuda silenciosa del case documental: es el build del repo ronda-app.

ÍtemPrioridad
ensureSchema + seedP0
Endpoints Must + smokeP0
Web feed/ficha/RSVP/success/login/inboxP0
POST /api/tables + /publicarP0 (brief)
Dock + tokensP0
Guards Angular canActivateP1

3. L2+ (misma complejidad, polish)

ÍtemNotas
Rate limit + honeypot POST requestsAnti-spam
Checkbox privacidadQ3
Aviso / bloqueo suave SEATED ≥ seatsH3
Filtros de status en inboxQuery API
Playwright S1–S22CI
Copy code clipboardCould
mailto / tel en detalleOps barra
Editar / cancelar mesa publicadaHOST
API URL por environmentDeploy
prefers-reduced-motion verificadoa11y
Badge NEW en dockShould

Estas son subidas de pulido, no parches del L2 documental.

[DECISIÓN] No dejar backlog del vertical slice como “mañana”. Lo de arriba es polish o subida de nivel.

4. L3 (requiere brief nuevo de diversidad)

  • Multi-HOST / un local varios anfitriones
  • Cuenta PLAYER y “mis solicitudes”
  • Email / WhatsApp transaccional
  • Check-in QR en puerta
  • Venues normalizados + calendario iCal
  • Check DB transaccional de aforo
  • Audit log
  • Facetas search (rompería H-FEED si se convierten en home)

5. Explícitamente fuera (no backlog disfrazado)

ÍtemPor qué
Pagos / TPV / EventbriteS4
Discord / chatS3
Mapa de ciudadTerna ≠ DERIVA
Search-first homeTerna ≠ PIZARRA
Kanban≠ PRIMA
E-commerce de juegosOtro producto
App nativaFuera de stack daily
Matching ELOOtro producto (liga)

6. Orden de ataque si hay continuidad de código

  1. Contratos 05–06 + seed 05 + smoke 13
  2. Web paridad UI-01…10
  3. Guards + H3 UI + contacto tel
  4. Rate limit si hay tráfico
  5. Evaluar brief L3 (no parche silencioso)

16-interaction-specs.md

16 — Especificación de interacción — RONDA

1. Convenciones

TokenValor
Duración corta120–160ms
Easingease-out
Reduced motion0ms
Target≥44×44
Pendingcontrol disabled + aria-busy

2. Dock (S-DOCK)

GestoResultado
Tap slotNavega; aria-current se mueve
Tap Publicar / Inbox sin sesión/login?next=…
Scroll del feedDock no se esconde en v1
TecladoTab cíclico de slots al final del documento
Rotación landscape 390Dock permanece; main padding-bottom 80

Estado activo: disco cromo + label tinta.
Estado default: tinta sobre liner.
No: badge numérico obligatorio; si se implementa, aria-label="Inbox, 3 nuevas".

Slots: Hoy / · Mesas /mesas · Publicar /publicar · Inbox /inbox · Yo /yo o /login.


3. Feed Hoy / Mesas

GestoResultado
Tap card/mesas/:slug (fila completa clicable, un solo enlace)
Pull-to-refreshNo en v1; Reintentar solo en error
Primera carga5 skeletons; no spinner centrado de marca
0 resultados HoyEmpty + CTA “Ver la semana”
ErrorBanner fijo bajo wordmark + Reintentar (refetch)
Hover desktopTranslación 0; borde tinta 1px extra opcional

AC: la primera card de esta noche está en el viewport inicial a 390 y a 1280 (sin hero de 60vh).

Orden visual = startAt ASC. Si dos mesas comparten hora, desempate title ASC.


4. Ficha

GestoResultado
CTA primario si hay hueco estimado“Pedir asiento” → /mesas/:slug/asiento
CTA si seatedCount >= seats“Apuntarme a la lista de espera” → mismo form
Back / wordmarkFeed de origen (from=hoy|mesas)
FotoNo lightbox v1
DisclaimerVisible antes del CTA (no debajo del fold en desktop)

Hueco estimado = seats - seatedCount (agregado). Si el API no manda seatedCount, mostrar seats y CTA estándar. [DECISIÓN]


5. Form RSVP

CampoInteracción
fullNameAutocomplete name
emailtype=email, autocomplete email
phonetype=tel, opcional
notestextarea 3 filas
partySizedefault 1; rango 1–2
GestoResultado
Submit inválidoFoco al primer campo error; no POST
Submit válidoBotón disabled; POST; 201 → /ok?code=
Error redBotón “Reintentar”; no limpia campos
Doble tapIgnorado mientras pending

Submit deshabilitado si nombre < 2 o email inválido.


6. Success

GestoResultado
Long-press / selectCódigo seleccionable
Copiar (Could)navigator.clipboard; toast 1.5s “Código copiado”
CTA primarioVolver al feed /
CTA secundarioVolver a la ficha

Sin animación de confetti. Sin “¡reserva confirmada!”.

Si se entra a /ok sin code: mensaje “Solicitud enviada” genérico + CTA feed. No inventar un código.


7. Login

GestoResultado
SubmitPOST login; pending en botón
200Guarda token + user; navega next sanitizado o /inbox
401Texto bajo el form; password no se limpia (sí se puede seleccionar)
EnterSubmit

next permitido: paths que empiezan por / y no // ni http.


8. Inbox

GestoResultado
Sin tokenRedirect login
Tap card/inbox/:id
EmptyIlustración no obligatoria; titular + 1 línea
Error 401 mid-sessionRedirect login
Error redBanner + Reintentar

No swipe-to-archive en v1.


9. Detalle + estados

AcciónConfirmaciónResultado
ContactadaNoPATCH CONTACTED
SentarNo, salvo seatedCount >= seats → avisoPATCH SEATED
Lista de esperaNoPATCH WAITLIST
Cancelar (“¿Cancelar RD-…?”)PATCH CANCELLED

Aviso H3 (no bloqueo API v1):
“Hay tantas o más solicitudes sentadas que sillas. ¿Seguro?”

Optimistic UI: badge cambia al tap; si PATCH falla, rollback + toast error.


10. Publicar

GestoResultado
startAtdatetime-local; min = hoy 00:00 Europe/Madrid
seatsstepper 2–12 o input number
SubmitPOST tables; 201 → /mesas/:slug
401login
Validaciónmismos criterios doc 04 F8

No hay preview Paper-like. El HOST ve la ficha real.


11. Yo / logout

Tap “Salir” → limpia storage → (opcional DELETE session) → /login.
No confirmación.


12. Motion

SuperficieSpec
Cards feedFade 160ms, stagger ≤40ms (off si reduced-motion)
Cambio de badgeCrossfade 120ms
DockSin hide; sin bounce
SkeletonsEstáticos o pulso 1.2s opacity 0.6–1; off si reduced-motion

Prohibido: scroll-jacking, page transitions de 400ms, parallax en foto.


13. Criterios de aceptación de interacción

  1. Un PLAYER en 390px llega de feed a success en ≤ 4 taps (card, CTA, submit → success).
  2. Un HOST en 390px marca CONTACTED en ≤ 3 taps desde inbox (card, acción).
  3. Ningún submit permite doble POST.
  4. El dock no cubre el CTA de ficha (padding / sticky por encima).
  5. Reduced-motion elimina stagger.
  6. La home no es hero 2-col (AP-12).

17-prototype-map.md

17 — Mapa de prototipo — RONDA

Paper no es clicable vía MCP. El prototipo vivo es la app Angular en /Users/cristian/orca/ronda-app/ (pnpm start :4200).

1. Paper → rutas

RutaPaperRolMust
/UI-01 Feed Hoy, UI-07 Empty, UI-08 Error, UI-09 Loading, UI-10 MobilePLAYER
/mesasmismo patrón UI-01 (recorte semana)PLAYERShould
/mesas/:slugUI-02 Ficha mesaPLAYER
/mesas/:slug/pedirUI-03 RSVPPLAYER
/okUI-04 SuccessPLAYER
/loginUI-05 LoginHOST
/inboxUI-06 Inbox HOSTHOST
/inbox/:id(extensión de UI-06)HOSTShould
/publicar(no artboard UI dedicado; spec 04 F8 / 16 §10)HOSTSí (API + pantalla)
/yo(sesión)HOSTShould
UI-00 TokensSistema
UX-00…UX-11Proceso

2. Guion de demo (click-through)

PasoActorRutaQué decir
1Leo/“Esta noche hay mesa” — ve 17:30…21:00
2Leo/mesas/azulejosLa Palma 12, 4 sillas, tile-laying
3Leo/mesas/azulejos/asientoDisclaimer visible
4Leo/ok?code=RD-0815-00NCódigo; no es plaza
5Nerea/loginnerea@ronda.club
6Nerea/inboxNEW de Leo
7NereadetalleCONTACTED → SEATED o WAITLIST
8Nerea/publicarAbre una mesa extra

3. Estados en el mismo prototipo

EstadoCómo provocarlo
Empty HoySemilla con start_at fuera del día, o flag demo
ErrorParar pnpm api y recargar /
LoadingThrottle red DevTools
Mobile390×844
URL localDestino
http://localhost:4200/Feed
http://localhost:4200/mesas/azulejosAzulejos de Lavapiés
http://localhost:4200/mesas/cartasCubilete y cartas
http://localhost:4200/mesas/minisMazmorra de Chamberí
http://localhost:4200/mesas/cooperativoFarol cooperativo
http://localhost:4200/mesas/coleccionColección de patio
http://localhost:4200/loginHOST

API :3015 · Web :4200 · proxy /api opcional.

5. Qué no es prototipo

  • El file Paper no sustituye flujos clicables.
  • Un HTML estático en presentation/ no es el vertical slice.
  • Capturas image_gen no son evidencia de producto (D-P0-09).

18-completeness-audit.md

18 — Auditoría de completitud — RONDA (2026-08-15)

1. Alcance auditado

Vertical slice L2: feed cronológico de mesas de juego locales + RSVP público + panel HOST JWT, con docs, referencia Paper, contratos Angular + Nest + ensureSchema + Neon.

Este encargo cubre la suite documental. Paper hi-fi y el repo ronda-app pueden existir en paralelo; la auditoría marca lo que esta suite cierra.

2. Checklist CRON / ALS-2

RequisitoEstadoEvidencia
Diversidad sector/tipo/nivelOKGaming mesa L2; no mapa; no search; no kanban; no Discord
Terna ≥2 códigos vs N−1/N−2OKR-HY · S-DOCK · H-FEED · F-BOOK (brief)
Day brief + anti-patronesOKdocs/00-day-brief.md (no sobrescrito)
Paper ≥12 UX + ≥10 UIOKUX-count 12 · UI-count 11 · file 01M023RM2TZ49CYM2FN1FRRGNY
Docs 00–20OKsuite en docs/ + executive + README
JWT HOSTOKRole HOST · D-P1-05 · tabla sessions
API + seed + NeonOKcontratos curly-lab-77015594, port 3015
Web tokensOKZilla Slab + Mulish · paleta signage
Feed-first (no AP-12)OKUI-01 / docs 03, 07, 08
Hipótesis no fake fieldOKetiquetas en doc 02
Copy solicitud ≠ plazaOKdocs 01, 09, 16

3. Cobertura funcional

Feature briefSpecUI PaperAPIDocs
Home feed cronológico 5 mesasUI-01, UI-10GET tables03, 04, 16
Ficha + fotoUI-02GET :slug05, 09
Form RSVPUI-03POST requests04, 05
Success + RD-UI-04code04, 09
Login HOSTUI-05POST login04, 06
Inbox 5 estadosUI-06GET + PATCH04, 05
Publicar mesa(pantalla spec)POST tables04, 20
EmptyUI-07200 []16
ErrorUI-085xx/red16
LoadingUI-09pending16
Design systemUI-0008
Seed 5 mesasensureSchema05
DockUI-01, UI-1003, 08

4. Cobertura Paper (literales del brief)

IDNombreEn 00-paper-reference
UX-00Cover
UX-01Stakeholders
UX-02Personas
UX-03JTBD
UX-04Stories
UX-05Journey
UX-06Blueprint
UX-07Site map
UX-08Flujos
UX-09Datos+permisos
UX-10Métricas
UX-11Research
UI-00Tokens
UI-01Feed Hoy
UI-02Ficha mesa
UI-03RSVP
UI-04Success
UI-05Login
UI-06Inbox HOST
UI-07Empty
UI-08Error
UI-09Loading
UI-10Mobile feed

[COMPROBADO] Nombres coinciden con el encargo. UX-count: 12 y UI-count: 11 literales para el gate check-paper-reference.mjs.

5. Rúbrica de calidad (auto SCORE orientativo · docs)

EjeScore 1–5Comentario
Diversidad5Feed+dock vs mapa DERIVA y search PIZARRA
Craft visual (spec)4–5Signage + Zilla/Mulish + fotos mesa documentadas
Densidad UX docs5Suite L2 con tablas, AC, etiquetas
Completitud código L2App fuera o en paralelo a este encargo de docs
Authz5Contrato: guard en list/status/publish
Verdad investigación5Sin entrevistas ni stats de campo falsas

6. Huecos aceptados (no regresiones de cierre documental)

HuecoClasificación
Pagos / Discord / mapa / search-homeFuera L2
e2e automatizadoL2+
Route guards formales AngularL2+
Check DB aforoL2+ / L3
Checkbox RGPDL2+ documentado
Artboard UI dedicado de /publicarSpec en 04/16; no bloquea count UI ≥10
Paper/app no necesariamente tocados aquíEncargo = documentación

7. Veredicto

COMPLETO para entrega documental del caso 2026-08-15.
La suite define producto L2 usable (tablas, AC, riesgos, contratos) sin presentar investigación de campo como hecho.
Cualquier ampliación mapa / search-first / pagos / Discord requiere nuevo brief de diversidad, no parche silencioso.

19-backlog-completo.md

19 — Backlog completo — RONDA

Inventario de ítems. Los del alcance L2 documental del día están Done.
El resto es opcional / siguiente nivel, no deuda oculta del cierre.

1. Done — L2 case documental (2026-08-15)

IDÍtemCapa
D01Definición producto RONDA + esloganDocs
D02Day brief diversidad gaming L2Docs (preexistente)
D03Terna R-HY · S-DOCK · H-FEED · F-BOOKDocs
D04Personas Leo Navas / Nerea SolísDocs + Paper ref
D05JTBD + stories MustDocs
D06IA feed-first + dockDocs
D07Flujos F1–F9 + ACDocs
D08Modelo users / sessions / game_tables / seat_requestsDocs
D09Contratos API :3015Docs
D10Auth JWT HOST + sessionsDocs
D115 estados incl. WAITLIST y SEATEDDocs
D12Código RD-MMDD-NNNDocs
D13Disclaimer solicitud ≠ plazaDocs
D14North star CONTACTED antes de startAtDocs
D15Tokens signage + Zilla Slab / MulishDocs
D16Paper UX-00…11 + UI-00…10Docs
D17Suite docs 00–20 + README + executiveDocs
D18Seed 5 mesas + solicitudes especificadoDocs
D19Assets listadosREADME
D20QA smoke + S1–S22Docs
D21Publicar mesa (HOST) especificadoDocs
D22Neon curly-lab-77015594 + pnpm arranqueDocs

2. Backlog L2 implementación (repo ronda-app)

IDÍtemPrioridadNotas
I01Scaffold apps/api + apps/web pnpmP0filter independientes
I02ensureSchema 4 tablasP0
I03Seed Nerea + 5 mesas + requestsP0
I04GET tables / :slugP0
I05POST requests + código RD-P0
I06POST login JWT + sessionsP0
I07GET requests + PATCH statusP0
I08POST tables HOSTP0Must del brief, no L3
I09Feed + dock AngularP0
I10Ficha / RSVP / success / login / inboxP0
I11Tokens Tailwind + fotosP0
I12Smoke curl + buildP0D-P1-06

3. Backlog L2+ (polish)

IDÍtemPrioridadNotas
B01canActivate guards AngularP1UX auth
B02Filtros por status en inboxP2Query API
B03Aviso / bloqueo suave SEATED ≥ seatsP1H3
B04Captcha / rate limit POSTP1anti-spam
B05Checkbox privacidadP1RGPD
B06Playwright smokeP1CI
B07Skeleton UI-09 en códigoP2
B08mailto/tel en detailP2ops barra
B09Copy code clipboardP3
B10API URL por environmentP1deploy
B11Badge NEW en dockP2
B12Página 404 amigableP3
B13Editar / cancelar mesaP2
B14Recorte Hoy server-sideP2query from/to

4. Backlog L3 (requiere brief nuevo)

IDÍtemDependencia
C01Multi-HOST / scope por localvenues + membership
C02Cuenta PLAYERauthz
C03Email / WhatsApp transaccionalprovider
C04Lookup público por códigoauthz
C05Check DB aforotransacción
C06Audit logEvent
C07Check-in QRstartAt window
C08Calendario iCalschedule

5. Backlog explícitamente fuera

IDÍtem
E01Pagos / TPV / fianza
E02Discord / chat / matching
E03Mapa de locales
E04Search-first como home
E05Kanban de solicitudes
E06E-commerce de juegos
E07App nativa
E08Matching ELO

6. Explicitamente no-backlog

IdeaRazón
Hero 2-col + 3 cardsAP-12 / rompe H-FEED
Sidebar CRMS-SIDE
Wizard RSVP 4 pasosF-ONB
Command search municipalPIZARRA
Split mapaDERIVA

7. Orden de ataque recomendado (continuidad de código)

  1. I01–I12 (vertical slice runnable)
  2. B01 + B06 + B10
  3. B03 + B08
  4. B04 + B05 si hay tráfico
  5. Evaluar brief L3 — no parche silencioso

8. Trazabilidad

OrigenÍtems
Day brief must-haveD01–D22, I01–I12
Doc 15 L2+B01–B14
Doc 15 L3 / fueraC01–C08, E01–E08

20-implementation.md

20 — Implementación — RONDA

1. Resumen ejecutivo técnico

CampoValor
App path/Users/cristian/orca/ronda-app
APINestJS · puerto 3015 · prefijo /api
WebAngular standalone · puerto 4200
Packagepnpm · apps/api + apps/web independientes (pnpm --filter)
DBNeon PostgreSQL · ensureSchema · project curly-lab-77015594
Tablasusers, sessions, game_tables, seat_requests
AuthJWT Bearer · role HOST · persistencia sessions
DominioUser, Session, GameTable, SeatRequest
Fecha2026-08-15
GitHubhttps://github.com/Criscode2022/ronda-app (se creará)

Este documento es la especificación de build alineada al case. No sustituye al código: si el repo diverge, gana el contrato de docs 05 + este archivo tras actualizar ambos.

[COMPROBADO] Path, puertos, tablas, Neon y scripts pnpm api / pnpm start salen del brief de implementación.

2. Cómo arrancar

cd /Users/cristian/orca/ronda-app
pnpm install

# apps/api/.env
#   DATABASE_URL=   # Neon curly-lab-77015594
#   JWT_SECRET=
#   PORT=3015

pnpm api      # http://localhost:3015
pnpm start    # http://localhost:4200

Apps independientes: no npm workspaces. Usar:

pnpm --filter api add <pkg>
pnpm --filter web add <pkg>

Credenciales

RolEmailPassword
HOSTnerea@ronda.clubpassword123

3. ensureSchema

Al boot de Nest (onModuleInit o main.ts antes de listen):

  1. CREATE TABLE IF NOT EXISTS de las 4 tablas (doc 05).
  2. Índices: game_tables(start_at), seat_requests(created_at DESC), seat_requests(table_id, status), users(email), sessions(token_hash).
  3. Seed idempotente:
    • upsert HOST nerea@ronda.club (bcrypt de password123)
    • upsert 5 game_tables por slug
    • upsert seat_requests por code

No borrar datos de usuario en cada boot si ya existen filas de requests distintas al seed. El seed solo garantiza las filas canónicas.

Tablas (recordatorio)

TablaContenido
usersHOST Nerea
sessionssid + token_hash + expires_at
game_tables5 mesas seed + altas HOST
seat_requestsRSVP públicos

4. Módulos API a implementar

Auth

  • POST /api/auth/login
  • Valida email/password; compara bcrypt; inserta sessions; emite JWT con sub, email, role, sid.
  • Guard JWT protege lectura/escritura de solicitudes y alta de mesas.

Tables (público lectura · HOST escritura)

MétodoRutaAuthNotas
GET/api/tablesNoorden start_at ASC
GET/api/tables/:slugNo404 si no existe; incluir hostName, seatedCount
POST/api/tablesJWT HOSTslug generado; host_id = sub

Requests

MétodoRutaAuthNotas
POST/api/requestsNocreate; status NEW; code RD-
GET/api/requestsJWTlist + table; created_at desc
PATCH/api/requests/:id/statusJWTbody { status }
GET/api/requests/:idJWTShould; + table + seatedCount

Generación de código

RD- + MMDD (Europe/Madrid) + - + NNN

NNN = siguiente entero del día, padded 3. Si unique falla, reintentar NNN+1 (máx. 5).

[DECISIÓN] Timezone Europe/Madrid, no UTC, para que “Hoy” y el código coincidan con la barra.

5. Frontend a implementar

PáginaRutaResponsabilidad
FeedPage/GET tables, recorte Hoy, empty/loading/error, dock
TablesWeekPage/mesasmismo GET, recorte 7 días
TablePage/mesas/:slugGET slug, foto, disclaimer, CTA
RsvpPage/mesas/:slug/pedirform create público + disclaimer
SuccessPage/okconfirmación + código + “no es plaza”
LoginPage/loginform → ApiService.login → inbox
InboxPage/inboxlist + empty/error + logout
RequestDetailPage/inbox/:idget + patch status + aviso aforo
PublishPage/publicarform POST tables
MePage/yonombre, salir

Shell: DockComponent persistente.
ApiService centraliza base URL http://localhost:3015/api, token storage (ronda_token, ronda_user), métodos tipados (GameTable, SeatRequest, User).

6. Decisiones de implementación

DecisiónRazón
Puerto API 3015Evitar colisión con DERIVA 3014 / PIZARRA 3013
game_tablestables es reservada
ensureSchema, no Prisma obligatorioBrief de build
pnpm filterBrief; apps independientes
Soft auth en páginasSimple; API es autoridad
start_at TIMESTAMPTZFuente de verdad del feed
Sin FK User–SeatRequestForm anónimo
Código RD-MMDD-NNNReferencia oral corta
Templates standaloneVelocidad daily
No bloquear POST por aforoH2/H3: HOST decide WAITLIST
Home no usa hero como layoutAP-12 / H-FEED
SEATED no ENROLLEDDominio de mesa, no municipal
JSON camelCaseConvenio web serie daily

7. Variables de entorno

VariableServicioDescripción
DATABASE_URLAPINeon curly-lab-77015594
JWT_SECRETAPIFirma tokens
PORTAPIopcional, 3015
JWT_EXPIRESAPIopcional, default 12h

Web: URL de API en ApiService (default localhost:3015).

8. Smoke de implementación (mínimo)

  1. ensureSchema OK (tablas existen).
  2. GET /api/tables length ≥ 5, incluye azulejos, cartas, minis, cooperativo, coleccion.
  3. POST /api/requests 201 + code RD-.
  4. POST /api/auth/login 200 + accessToken.
  5. GET /api/requests Bearer incluye el POST.
  6. GET /api/requests sin token 401.
  7. POST /api/tables sin token 401.
  8. Web: feed dominante, dock, disclaimer, fotos.

9. Estructura de ficheros clave

ronda-app/
├── pnpm-workspace.yaml
├── package.json                  # scripts: api, start
├── apps/api/
│   ├── package.json
│   ├── src/
│   │   ├── main.ts
│   │   ├── app.module.ts
│   │   ├── db/
│   │   │   ├── ensure-schema.ts
│   │   │   └── seed.ts
│   │   ├── auth/
│   │   │   ├── auth.controller.ts
│   │   │   ├── auth.service.ts
│   │   │   ├── jwt.strategy.ts
│   │   │   └── jwt-auth.guard.ts
│   │   ├── tables/
│   │   │   ├── tables.controller.ts
│   │   │   └── tables.service.ts
│   │   └── requests/
│   │       ├── requests.controller.ts
│   │       └── requests.service.ts
│   └── .env.example
├── apps/web/
│   ├── package.json
│   ├── tailwind.config.js
│   └── src/app/
│       ├── app.routes.ts
│       ├── core/api.service.ts
│       ├── shell/dock.component.ts
│       └── pages/
│           ├── feed/
│           ├── table-detail/
│           ├── rsvp/
│           ├── success/
│           ├── login/
│           ├── inbox/
│           ├── request-detail/
│           ├── publish/
│           └── me/
└── README.md

Nombres de fichero orientativos; el contrato es de rutas y tablas, no de filenames exactos.

10. Seed — checklist de implementación

#Check
1User Nerea Solís / nerea@ronda.club / password bcrypt de password123
25 game_tables con slugs de doc 05 y image_key alineado a assets
3Barrios: Lavapiés, Embajadores, Chamberí, Malasaña
4Horas 17:30 / 18:00 / 19:00 / 20:30 / 21:00 Europe/Madrid
5Asientos 6 / 5 / 5 / 4 / 4 según seed (coleccion 6, minis 5, cartas 5, azulejos 4, cooperativo 4)
6Requests RD-0815-001…005 cubriendo NEW, CONTACTED, SEATED, WAITLIST, CANCELLED
7Leo → azulejos NEW
8Youssef → cooperativo WAITLIST
9Idempotencia: upsert por email/slug/code

11. Alineación case ↔ app

DocEvidencia esperada en código
05 data modelensureSchema columnas y enums TEXT
03–04 IA/flowsroutes + controllers
08 DStailwind colors + fontFamily
09 contentstrings en templates (disclaimer ×3)
00 paperURLs en README case
11 securityGuard en GET/PATCH requests y POST tables

12. Notas de cierre técnico

  • El case documental L2 está especificado (docs + Paper ref + contratos).
  • Must-have de producto del brief están escritos con AC.
  • Mejoras (guards Angular, e2e, rate limit, check aforo) viven en backlog L2+, no como deuda silenciosa.
  • Repo GitHub Criscode2022/ronda-app se creará; este doc no lo asume ya pusheado.