On this page 00 — Day brief · 2026-08-25 · LOMO 0%

00-day-brief.md

00 — Day brief · 2026-08-25 · LOMO

Decisiones ALS-2 BRIEF

CampoValor
Fecha2026-08-25
NombreLOMO
ComplejidadNivel 2 (no L1 consecutivo tras ONDA)
SectorArchivo / biblioteca de barrio (sala de lectura, Usera)
TipoMesa de ejemplares de hoy + comando de búsqueda + petición de reserva de sala + mesa BIBLIO JWT
PlataformaWeb responsive (mesa de datos en el mostrador · comando en el móvil)
RegistroR-CL — sala de lectura clásica, herramienta de datos
ShellS-CMD — barra «buscar lomo o signatura»
HomeH-DATA — ejemplares de hoy (DISPONIBLE / EN_SALA / PEDIDO); lead métricas + tabla
FlujoF-LEAD — pedir reserva de mesa; no es préstamo a casa
Por qué no ONDANo S-RAIL / H-GALLERY / F-SEARCH; no radio; no cinta amarilla
Por qué no PIZARRAHome no es catálogo search-first; es mesa de ocupación de hoy
Por qué no DERIVANo mapa de paseos; el dato es el lomo en sala

Terna unicidad

R-CL · S-CMD · H-DATA · F-LEAD

0 códigos iguales a N−1 (ONDA: R-HY · S-RAIL · H-GALLERY · F-SEARCH).
≥2 distintos vs N−2 (RAMAL) y N−3 (TEMPLE).

S-CMD, H-DATA y F-LEAD no están en los últimos 5.

Ground (CRON §5.6)

  • Familia: G-STONE
  • Hex: #E4E0D6
  • Por qué: polvo de caliza en la sala, no papel de cartel (ONDA), no hielo (RAMAL), no tinta de flight case (TEMPLE).

Mood visual (Paper)

  • Candidatos: bookish/yeso (primer instinto biblioteca), mineral, chapel, alpine, fog
  • Elegido: mineral — no el primer instinto librería cálida. Un lomo se lee bajo la caliza, con metal oxidado.
  • Paleta:
    • #E4E0D6 caliza (ground)
    • #1C2B28 tinta de lomo
    • #FFFFFF ficha
    • #3E6B5E cobre oxidado (mark)
    • #5F6B67 mute
    • #9A5340 sello (poco)
  • Tipo: Source Serif 4 display · Outfit UI

Alcance L2 must

  1. Home datos: métricas de hoy + 4 ejemplares
  2. Comando de búsqueda (signatura / título)
  3. Ficha de lomo (sala, estado)
  4. Pedir reserva de mesa (pedir ≠ préstamo a casa)
  5. Login JWT BIBLIO
  6. Mesa de hoy (lista + pedidos)
  7. Empty / error / loading / mobile data
  8. ≥12 UX + ≥10 UI

Microcopy duro

  • Reserva de sala ≠ préstamo a casa. No se lleva el libro.
  • Lomo ≠ catálogo municipal. No sustituye a Absys ni al distrito.
  • Pedir ≠ apuntarse al club. No inscribe al taller.
  • LOMO no es la Biblioteca Regional.

Supuestos

  • S1: La sala atiende 2–4 ejemplares/día en el slice; demo 4.
  • S2: Rafa busca sin cuenta. JWT solo BIBLIO.
  • S3: Nora (restauradora) fuera del producto.
  • S4: Sin préstamo a domicilio, sin GPS, sin chat. [DECISIÓN]

Hipótesis

IDSeñal
H1Una mesa de ocupación reduce colas en el mostrador
H2Separar reserva de sala de préstamo a casa evita el conflicto con el distrito
H3Cuatro lomos bastan; no hace falta el fondo completo

00-paper-reference.md

Paper reference · LOMO

URLhttps://app.paper.design/file/01M0VVNT406AK640AK1ECY2DBR
File ID01M0VVNT406AK640AK1ECY2DBR

UX-count: 12 UI-count: 12

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 Mesa datos · UI-02 Mesa mobile · UI-03 Comando · UI-04 Lomo · UI-05 Lomo mobile · UI-06 Pedir · UI-07 Success · UI-08 Login · UI-09 Mesa BIBLIO · UI-10 Empty · UI-11 Error+Loading

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

IDNombreContenido
UX-00CoverLOMO · «El lomo, no el prestado.» · R-CL · S-CMD · H-DATA · F-LEAD · L2 archivo · mineral · 2026-08-25
UX-01StakeholdersBIBLIO Inés, LECTOR Rafa, restauradora Nora fuera, distrito fuera
UX-02PersonasInés Soto 47 · Rafa Díez 63 · Nora Ruiz 39 (docs)
UX-03JTBDJob «saber si hay mesa para este lomo hoy sin hacer cola»
UX-04StoriesMust: datos, comando, ficha, pedir, login, mesa, estados
UX-05JourneyMostrador → mesa de hoy → El río Manzanares → pedir mesa
UX-06BlueprintFrontstage sala · backstage signatura/Neon/JWT
UX-07Site map/ datos · /buscar · /lomo/:code · /pedir/:code · /ok/:code · /login · /mesa
UX-08FlujosF-LEAD público; mesa BIBLIO
UX-09DatosUser BIBLIO · Ejemplar; JWT en /mesa
UX-10MétricasNorth star % de reservas de mesa confirmadas antes de las 11:00
UX-11ResearchComprobado / supuesto / hipótesis / decisión

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

IDNombreFlujo
UI-00TokensDS mineral: limestone, copper oxide
UI-01Mesa datosSplit Deriva: rail de lomos + foto a sangre de la sala
UI-02Mesa mobile390
UI-03ComandoOverlay buscar «manzanares»
UI-04LomoFicha El río Manzanares sala 2
UI-05Lomo mobile390
UI-06PedirForm reserva de mesa
UI-07SuccessPedida. Reserva ≠ préstamo a casa
UI-08LoginJWT BIBLIO
UI-09Mesa BIBLIOLista + pedidos
UI-10EmptySin lomos
UI-11Error+LoadingRed / skeletons

Mapeo Paper → Angular

ArtboardRutaComponente
UI-01 Mesa datos/HoyPage
UI-03 Comando/buscarBuscarPage
UI-04 Lomo/lomo/:codeLomoPage
UI-06 Pedir/pedir/:codePedirPage
UI-07 Success/ok/:codeOkPage
UI-08 Login/loginLoginPage
UI-09 Mesa/mesaMesaPage

01-project-definition.md

01 — Definición de proyecto — LOMO

1. Identidad

CampoValor
NombreLOMO
SignificadoLomo es el ejemplar que se lee en sala, no el libro que se lleva a casa ni el catálogo del distrito. Se entra a la mesa de ocupación; se sale habiendo pedido mesa.
Eslogan«El lomo, no el prestado.»
Una fraseMesa de ejemplares de hoy + comando de búsqueda + petición de reserva de sala + mesa BIBLIO JWT.
SectorArchivo / biblioteca de barrio (sala de lectura, Usera)
TipoWeb L2 — mesa H-DATA + comando S-CMD + lead F-LEAD + mesa JWT — Nivel 2, herramienta de ocupación usable
PlataformaWeb responsive (mesa de datos en el mostrador · comando en el móvil)
Mercado demoEspaña · Madrid · Usera (una sala de barrio, no la Biblioteca Regional)
Idiomaes-ES
Fecha caso2026-08-25
TernaR-CL · S-CMD · H-DATA · F-LEAD

[COMPROBADO] Nombre, terna, nivel, sector, ground y paleta salen de docs/00-day-brief.md.
[SUPUESTO de catálogo] Usera / El río Manzanares / Planos de Usera es atrezzo de mercado, no el fondo de una biblioteca municipal real.

Tras ONDA (L1 radio, 2026-08-23) el día elige L2 archivo, no otro L1 consecutivo. N−1 es ONDA. [DECISIÓN]

2. Problema

Principal (hipótesis de diseño)

[HIPÓTESIS] El lector de Usera no necesita Absys ni la Biblioteca Regional: necesita saber si hay mesa para este lomo hoy sin hacer cola en el mostrador. Hoy eso vive en un cartel, una cola y una expectativa de préstamo a casa. La mesa de ocupación se lee como catálogo municipal; pedir mesa se lee como llevarse el libro; el producto se lee como la Regional. [SUPUESTO] Una sala atiende 2–4 ejemplares/día; no opera un préstamo a domicilio ni un club de lectura.

Secundarios

ProblemaQuién lo sufreEfecto
«¿Está el Manzanares?» en colaLECTOR (Rafa)Espera, se va, no pide mesa
Pedir mesa se lee como préstamo a casaLECTOR + InésEsperan un carnet y una fecha de devolución
La home se lee como catálogo del distritoRafaEsperan talleres, Absys, sedes (eso fue PIZARRA)
Pedir se lee como apuntarse al clubRafa + InésEsperan inscripción, lista de espera, plaza
La mesa BIBLIO se lee como kanbanBIBLIO (Inés)Esperan Recibido / En curso / Cerrado
El dueño pide fondo completo y préstamo el día 1SalaEl L2 se vuelve Regional

Supuestos (no investigación primaria propia)

  • S1: La sala atiende 2–4 ejemplares/día en el slice; demo 4. [SUPUESTO]
  • S2: Rafa busca sin cuenta. JWT solo BIBLIO. [SUPUESTO]
  • S3: Nora (restauradora) fuera del producto. [SUPUESTO]
  • S4: Sin préstamo a domicilio, sin GPS, sin chat. [DECISIÓN]

Hipótesis de producto

IDHipótesisSeñal de validación (futura)
H1Una mesa de ocupación reduce colas en el mostrador% de visitas a / que terminan en POST /pedir
H2Separar reserva de sala de préstamo a casa evita el conflicto con el distrito↓ «pensé que me lo llevaba»; copy visible en acuse
H3Cuatro lomos bastan; no hace falta el fondo completo0 OPAC; Inés decide el hoy sin Absys

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

3. Propuesta de valor

ParaValor
LECTOR (Rafa Díez)Ve 4 ejemplares de hoy, busca «manzanares», abre El río Manzanares, pide mesa, se lleva el código. Nadie le pide cuenta ni carnet.
BIBLIO (Inés Soto)Ve la mesa de hoy, lee estados y pedidos. No mueve columnas. No presta a casa.
RESTAURADORA (Nora Ruiz)Fuera del slice de código. El oficio de conservación no entra en v1.
Sala LOMOUna superficie mínima: ocupación + comando + mesa, sin préstamo y sin Regional.

No es LOMO

ExcluidoPor qué
Préstamo a domicilio / carnet / devoluciónReserva de sala ≠ préstamo a casa
Catálogo municipal / Absys / OPACLomo ≠ catálogo municipal
Club de lectura / inscripción / plazaPedir ≠ apuntarse al club
Biblioteca Regional / red de MadridLOMO no es la Biblioteca Regional
Fondo completo / miles de signaturasH3 · S1
Chat de lectores / kanbanEso fue PREGÓN (H-CHAT / F-KAN)
Quiosco de horas / bloques de 2 hEso fue TEMPLE (H-KIOSK / F-BOOK)
Galería de cubiertas / archivo de radioEso fue ONDA (H-GALLERY / F-SEARCH)
Portada de nodos + checklist de altaEso fue RAMAL (H-FULL / F-ONB)
Home search-first de talleresEso fue PIZARRA (H-SEARCH); LOMO no es el distrito
Hero 2-col + 3 cards de «tu biblioteca»Anti-patrón AP-12; contradice H-DATA
App de 40.000 ejemplares y CMSL2 compacto, no OPAC regional

4. Objetivos

Negocio / caso de estudio

  • Demostrar vertical slice L2 archivo / biblioteca de barrio con Ejemplar + pedido + JWT BIBLIO.
  • Portfolio coherente: Paper (12 UX + 12 UI) + docs + app runnable.
  • Terna R-CL · S-CMD · H-DATA · F-LEAD frente a ONDA (rail/galería/archivo), RAMAL (split/portada/alta) y PIZARRA (search-first/distrito).
  • No encadenar otro L1 tras ONDA. [DECISIÓN]

Usuario

RolObjetivo medible en demo
LECTORVer 4 filas, buscar «manzanares», pedir mesa de El río Manzanares y ver el acuse en < 90 s, sin cuenta
BIBLIOEntrar a mesa, ver 4 ejemplares de hoy, leer LM-2501 y los pedidos en < 45 s
RESTAURADORANo encuentra login; el oficio de conservación no está en la app

No objetivos v1 (explícitos)

  • Préstamo a domicilio, carnet, sanciones, devolución
  • Catálogo municipal, Absys, OPAC, fondo completo
  • Club de lectura, inscripción, lista de espera de taller
  • Chat, kanban, columnas Recibido / En curso
  • Cuenta de lector, socio, newsletter
  • GPS, mapa de sedes, horario de la Regional
  • Multi-sede, multi-bibliotecaria
  • App nativa ni notificaciones push
  • Rol RESTAURADORA autenticado
  • Programas, repeticiones, CONDUCTOR (ONDA es otro día)
  • Nodos, altas, vocal (RAMAL es otro día)

5. Roles y permisos (resumen)

AcciónAnónimo (lector)BIBLIORESTAURADORA
Ver mesa de datos /No hay cuenta
Buscar comando ?q=
Ver ficha /lomo/:code
Ver form /pedir/:codeSí (no es el job)
POST pedir mesaSí (no es el job)No
Ver acuse /ok/:code (código en URL)
Ver mesa /mesaNo (401 / redirect)No
GET /api/mesa401
Ver lista de pedidosNoSí (mesa)
Prestar a casaNo (no existe)NoFuera
Restaurar un documentoCopy: fueraCopy: fueraOficio fuera de app

[DECISIÓN] Un solo rol autenticado: BIBLIO. El lector no es un usuario del sistema: es autor/autora de un pedido. La restauradora no es un rol de sistema el día 1.

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

TipoMétricaDefinición
North Star% de reservas de mesa confirmadas antes de las 11:00Oficio de sala, no pageviews
Activación lector1.er POST /pedir con codeH1
Integridad de expectativaReclamaciones «¿me lo llevo?» / «¿es Absys?» / «¿me apunto al club?»H2 + microcopy
Integridad de authGET /api/mesa sin token → 401; POST /api/ejemplares/:code/pedir sin token → 201Mesa pública de datos + mesa BIBLIO cerrada
SaludError rate API ejemplares / pedidos4xx/5xx

7. Alcance funcional v1 (L2)

MóduloIncluido
Mesa de datosGET /api/ejemplares · métricas de hoy + 4 filas
ComandoGET /api/ejemplares?q= · signatura / título, query «manzanares»
FichaGET /api/ejemplares/:code · título, sala, estado, cubierta
PedirPOST /api/ejemplares/:code/pedir → acuse; reserva ≠ préstamo
AuthPOST /api/auth/login → JWT BIBLIO
Mesa BIBLIOGET /api/mesa JWT · lista + pedidos, no kanban
Estados UIEmpty (0 lomos / 0 resultados), loading skeleton, error de red
MobileMesa datos ~390px · filas apiladas · targets ≥44px
Seed1 BIBLIO, 4 ejemplares (2 DISPONIBLE, 1 EN_SALA, 1 PEDIDO)
SaludGET /api/health{ ok: true, service: "lomo-api" }

8. Criterios de aceptación de producto

  1. / es la mesa de datos de hoy (métricas + 4 filas). No pide cuenta. No es un hero 2-col + 3 cards. No es una galería de cubiertas.
  2. Un lector puede ver 4 ejemplares, tocar El río Manzanares, pedir mesa y obtener acuse en /ok/LM-2501.
  3. /ok/:code dice explícitamente que reserva de sala no es préstamo a casa y que lomo no es catálogo municipal.
  4. La UI dice explícitamente que LOMO no es la Biblioteca Regional y que pedir no es apuntarse al club.
  5. Sin token, GET /api/mesa responde 401. POST /api/ejemplares/:code/pedir y GET /api/ejemplares son públicos.
  6. Un POST de pedir no crea user, no abre préstamo, no inscribe al club, no mueve kanban.
  7. Un BIBLIO inicia sesión y ve 4 ejemplares (2 DISPONIBLE, 1 EN_SALA, 1 PEDIDO) en el seed.
  8. El tablero autenticado es una mesa de lista + pedidos. No es un kanban de 3 columnas. No es un split lista | ficha.
  9. Fotos reales en las cubiertas seed (assets lomos/*).
  10. Chrome S-CMD: barra «buscar lomo o signatura». Mesa de datos sin top-nav de marketing y sin rail de radio.
  11. Rutas solo las del doc 03. No existen /programa, /hoy JWT de ONDA, /nodos, /alta, /playlist, /prestamo.
  12. GET /api/health devuelve service: "lomo-api" (no onda-api, no ramal-api).
  13. La mesa pública no muestra nombres de otros lectores; la mesa BIBLIO sí muestra pedidos con nombre.
  14. /buscar?q=manzanares devuelve El río Manzanares.

9. Stack y artefactos

CapaDetalle
FrontendAngular + Tailwind · puerto 4200
BackendNestJS · puerto 3025
DBNeon PostgreSQL · project dawn-butterfly-79519092
AuthJWT (BIBLIO)
DiseñoPaper 01M0VVNT406AK640AK1ECY2DBR
Repo app/Users/cristian/orca/lomo-app/
Packagepnpm

[COMPROBADO] Puerto, Neon, Paper file ID y path de app constan en el encargo.
El código se escribe en paralelo a esta suite. Este doc es el contrato. No se espera al rewrite.

10. Riesgos y mitigaciones

RiesgoImpactoMitigación v1
Expectativa de préstamo«¿Me lo llevo a casa?»Copy «Reserva de sala ≠ préstamo a casa»
Expectativa de catálogoAbsys, distrito, PIZARRACopy «Lomo ≠ catálogo municipal»
Expectativa de clubInscripción, plaza, tallerCopy «Pedir ≠ apuntarse al club»
Expectativa de RegionalRed de Madrid, carnet únicoCopy «LOMO no es la Biblioteca Regional»
Expectativa de kanbanRecibido / En cursoLista + pedidos, no columnas de estado
Leftover ONDAprogramas, CONDUCTOR, radio, onda-apiContrato LOMO; health lomo-api; 0 copy de antena
Leftover RAMALnodes, altas, VOCAL, RM-Dominio ejemplares + pedidos + BIBLIO
Fondo el día 1Alcance L4 disfrazadoH3: cuatro lomos
Cuenta de lectorRompe S2Sin signup; POST público con nombre de mostrador
Home percibida como landing de bibliotecaPérdida de H-DATA / AP-12Métricas + tabla; no hero 2-col
Home percibida como search-firstConfundir con PIZARRAH-DATA; el comando no sustituye la mesa
Confundir con ONDA (galería + archivo)Expectativa de cubierta / repeticiónMesa de ocupación; no cubiertas 16/10 de radio
Confundir con TEMPLE (kiosk + horas)Expectativa de bloques de 2 hPedir mesa de un lomo; no slots
0 ejemplaresMesa vacía sin oficioEmpty «Aún no hay lomos hoy.»
Cola paralela en mostradorH1 no se mideNorth star etiquetada como modelo
Cobre oxidado como textoContraste WCAGMark pinta CTA/foco; ink escribe

11. Glosario

TérminoDefinición en LOMO
LomoEjemplar físico en sala; el producto, no el prestado ni el OPAC
EjemplarRecurso (ejemplares); no es un título del catálogo municipal
SignaturaCódigo LM-XXXX; se busca en el comando
SalaEspacio de lectura 1 / 2 / 3; no es préstamo a casa
Mesa (ocupación)Superficie H-DATA pública de hoy; filas de ejemplares
Mesa (BIBLIO)Superficie JWT de Inés (lista + pedidos, no kanban)
PedidoPOST 201; no significa préstamo ni club
AcuseCódigo (LM-XXXX); no es un carnet
DisponibleEstado DISPONIBLE; se puede pedir mesa
En salaEstado EN_SALA; alguien lo lee ahora
Pedido (estado)Estado PEDIDO; ya hay mesa pedida
ComandoChrome S-CMD: «buscar lomo o signatura»
BIBLIOUsuario autenticado que opera la mesa
LECTORAutor/autora del pedido; no tiene fila en users
RESTAURADORAOficio de conservación; fuera del slice de código
PréstamoLlevar el libro a casa; fuera
Absys / OPACCatálogo municipal; fuera. Lomo ≠ catálogo
RegionalBiblioteca Regional; fuera. LOMO no es la Regional
MineralMood: caliza + cobre oxidado
GroundCaliza #E4E0D6
InkTinta de lomo #1C2B28
CardFicha #FFFFFF
MarkCobre oxidado #3E6B5E
Mute#5F6B67
SealSello #9A5340, poco

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

IDDecisiónAlternativa descartada
D1Home = mesa de datos (H-DATA)Hero 2-col + 3 cards (AP-12) · galería (ONDA) · search-first (PIZARRA) · portada a sangre (RAMAL)
D2Shell comando «buscar lomo o signatura» (S-CMD)Rail de radio · split mesa · dock · kiosk mínimo
D3Registro clásico de sala de lectura (R-CL)Bookish/yeso (primer instinto biblioteca) · signage ONDA · ice RAMAL
D4Flujo F-LEAD (pedir reserva de mesa)F-SEARCH como home, F-BOOK, F-KAN, F-ONB
D5Mood mineralBookish/yeso (primer instinto), chapel, alpine, fog, signage
D6Un rol autenticado BIBLIOMulti-rol restauradora / lector / distrito
D7Status DISPONIBLE / EN_SALA / PEDIDOKanban de 3 columnas; «prestado»; «en club»
D8Lector sin cuentaSignup, carnet, socio
D9Mesa pública no lista nombres de lectoresTablero público enumerable
D10POST público de pedir con nombre de mostradorFormulario de DNI + préstamo + club
D11Ground G-STONE #E4E0D6G-SIGN, G-ICE, G-INK, G-FOG, G-CHAPEL
D12Success de acuse en /ok/:codeToast único sin pantalla
D13Búsqueda en comando /buscar, no como homeH-SEARCH tipo PIZARRA (rompería H-DATA)
D14Mesa BIBLIO = lista + pedidos, no kanban ni splitRecibido / En curso / Cerrado; lista | ficha RAMAL
D15Datos públicos + mesa JWT (no auth-first total)Todo cerrado (rompería S2) · todo abierto (rompería mesa)
D16Sin préstamo a casaCarnet + fecha de devolución
D17Leftover ONDA no es el productoMantener programas / CONDUCTOR / onda-api
D18Health lomo-api · puerto 3025leftover onda-api · :3023

13. Relación con el día anterior

CaseTernaPor qué LOMO no lo copia
ONDA (N−1)R-HY · S-RAIL · H-GALLERY · F-SEARCHNo radio, no cubiertas, no repetición, no CONDUCTOR, no rail
RAMAL (N−2)R-CL · S-SPLIT · H-FULL · F-ONBNo telecom, no nodos, no altas, no vocal, no split
TEMPLE (N−3)R-HY · S-MIN · H-KIOSK · F-BOOKNo kiosk, no horas, no concierto; mesa ≠ coger sala de ensayo
PREGÓN (N−4)R-IN · S-SIDE · H-CHAT · F-KANNo admin pública, no chat, no kanban, no 112
MATIZ (N−5)R-HY · S-BOTTOM · H-BENTO · F-EDITNo belleza, no PWA de oficio, no editor de gramos
PIZARRAR-CL · S-CMD · H-SEARCH · F-SEARCHComparte S-CMD; no clona H-SEARCH ni el distrito. LOMO no es el distrito

[COMPROBADO] El brief fija terna R-CL · S-CMD · H-DATA · F-LEAD. 0 códigos iguales a N−1 (ONDA). ≥2 distintos vs N−2 (RAMAL) y N−3 (TEMPLE). S-CMD, H-DATA y F-LEAD no están en los últimos 5. Ventana 4 días: R-HY (TEMPLE) + R-CL (RAMAL) + R-HY (ONDA) + R-CL. Ground G-STONE #E4E0D6, no G-SIGN ni G-ICE.

02-ux-research-strategy.md

02 — Estrategia de investigación UX — LOMO

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 salaModelado de stakeholders + personas§3–4
Definir job de «saber si hay mesa para este lomo hoy sin hacer cola»JTBD + stories Must§5–6
Mapear fricción cola / Absys / préstamo → mesa de ocupaciónJourney + service blueprint§7–8
Traducir a requisitos L2 datos + comando + lead + mesaMatriz hallazgo → requisito → feature§9

2. Fuentes y límites

Fuentes admisibles (secundarias / operativas)

  • Conocimiento general de archivos y salas de lectura de barrio (mostrador, signatura, ejemplar que no sale, restauradora que no entra el día 1, bibliotecaria que lee ocupación no un OPAC).
  • Analogía operativa con superficie pública + panel JWT de la serie daily.
  • Restricciones ALS-2: no préstamo a casa, no catálogo municipal, no club, no radio, no nodos/altas, no kiosk de horas.

Límites éticos de verdad

ProhibidoPermitido
«El 58% de lectores de Usera hace cola para un lomo» sin fuente«[SUPUESTO] Rafa abre la mesa de hoy desde el mostrador»
Citas de entrevistas ficticias como campo realQuotes de persona etiquetadas como constructo de diseño
Volumen medido «17 reservas/semana del Manzanares» como KPI de mercadoEjemplares de seed de producto, etiquetados como demo
Fondo oficial de una biblioteca de Usera 2026Ejemplares de seed; Inés, Rafa y Nora son personas de diseño
«Rafa existe y nos dijo…»Rafa Díez es persona de diseño

3. Stakeholders

StakeholderInfluenciaInterésNecesidad principal
BIBLIO (Inés)AltaMuy altaVer el hoy y los pedidos sin kanban ni Absys
LECTOR (Rafa)Alta en la reservaMuy altaSaber si hay mesa para este lomo hoy, sin cuenta
RESTAURADORA (Nora)Media formal, baja en producto día 1AltaOficio de conservación; fuera del slice de código
Sala / archivo de barrioAlta formalAltaQue se vea qué hay hoy; que no se confunda con la Regional ni con Absys
Biblioteca Regional / red de MadridCompetencia / analogía falsaFuera de alcance; no clonar OPAC regional
Absys / catálogo municipalCanal paralelo / analogía falsaFuera; Lomo ≠ catálogo municipal
Distrito / talleres PIZARRAAnalogía falsa de «apuntarse»Fuera; pedir ≠ club
TEMPLE / local de ensayoAnalogía falsa de «reservar sala» de 2 hFuera; mesa de lomo ≠ slot de ensayo
ONDA / radioAnti-patrón N−1Fuera; no programas, no CONDUCTOR, no antena
Grupo de WhatsApp del barrioCanal paraleloNo es el backoffice

Mapa de poder (resumen)

  • Decisor de adopción: la sala (quién pone la mesa de hoy) y, en uso diario, Inés (quien mira pedidos).
  • Usuario frecuente de pedir mesa: LECTOR (datos + comando + ficha + form, sin cuenta).
  • Usuario frecuente de oficio: BIBLIO (mesa /mesa).
  • Riesgo de rechazo: si el acuse parece un préstamo; si la fila parece un título de OPAC; si hay que crear cuenta; si la mesa parece un kanban; si el comando parece el buscador del distrito.

4. Personas

P1 — Inés Soto · BIBLIO

CampoDetalle
Edad / contexto47 años; lleva el mostrador en Usera; mesa de ocupación, no de Absys
DigitalFluida en desktop de mesa; la mesa pública no es su herramienta principal, pero la mira
GoalsVer qué está disponible, qué se lee en sala, qué se ha pedido
PainsCola a deshora; «¿me lo llevo a casa?»; un kanban que no es su oficio; un OPAC que no es su job de hoy
Quote de diseño«Quiero ver el hoy. No un catálogo y no un préstamo.»
Email demoines@lomo.barrio / password123

Escenario: Entra a /login, abre /mesa, lee LM-2501 El río Manzanares DISPONIBLE, ve los pedidos. El copy dice que una reserva no es préstamo a casa.

[COMPROBADO] Nombre, edad, email demo y rol salen del encargo.

P2 — Rafa Díez · LECTOR

CampoDetalle
Edad / contexto63 años; vive en Usera; lee El río Manzanares cuando hay mesa; no quiere «otra cuenta de biblioteca»
DigitalMóvil desde la sala o el portal; no se hace socio si puede evitarlo
GoalsVer si el Manzanares está disponible; buscarlo por signatura o título; pedir mesa; llevarse un código
PainsLa cola dice «creo que está en sala 2»; abre Absys; le venden un préstamo; cree que se apunta al club
Quote de diseño«Solo quiero una mesa para este lomo. No me hagas un carnet.»

Escenario: Abre / en el mostrador, ve la fila de El río Manzanares DISPONIBLE, o busca «manzanares» en el comando, toca la ficha, pide mesa, lee «reserva ≠ préstamo a casa» y el código LM-2501.

[COMPROBADO] Nombre, edad y gesto (lector, 63) salen del encargo. Es constructo de diseño, no entrevistado. [DECISIÓN]
Rafa no tiene fila en users. [DECISIÓN]

P3 — Nora Ruiz · RESTAURADORA (docs)

CampoDetalle
Edad / contexto39 años; restauradora de documentos; oficio de taller y papel, no de mostrador
DigitalNo tiene login en el slice L2
GoalsConservar cuando la sala lo pida (fuera de app)
PainsQue pedir mesa se lea como una orden de restauración; que la app le pida cuadrante
Quote de diseño«La reserva no me programa. Yo no entro aquí.»

[COMPROBADO] Nombre, edad y exclusión de login salen del encargo. [DECISIÓN] no hay cuenta RESTAURADORA en v1.

5. JTBD

Job principal (lector)

Cuando quiero saber si hay mesa para este lomo hoy, quiero ver la ocupación y pedir reserva de sala sin hacer cola, para dejar constancia en el mostrador sin que eso signifique préstamo, cuenta ni club.

Job principal (bibliotecaria)

Cuando hay ejemplares hoy y pedidos nuevos, quiero ver la lista de hoy y los pedidos, para mover el oficio de la sala sin un kanban ni un OPAC.

Jobs que LOMO no hace

JobPor qué no
Prestar a domicilioReserva de sala ≠ préstamo a casa · S4
Consultar el catálogo municipalLomo ≠ catálogo municipal
Apuntarse a un club / tallerPedir ≠ apuntarse al club
Sustituir a la Biblioteca RegionalLOMO no es la Biblioteca Regional
Coger hora de ensayo≠ TEMPLE
Pedir plaza de taller≠ PIZARRA
Pedir repetición de un programa≠ ONDA
Pedir nodo / alta de bloque≠ RAMAL

6. Stories Must (v1)

IDComoQuieroPara
US-01LectorVer métricas de hoy + 4 filasDistinguir ocupación de catálogo
US-02LectorBuscar «manzanares» en el comandoEncontrar el lomo sin la cola
US-03LectorVer ficha (título, sala, estado)Saber si está disponible, en sala o pedido
US-04LectorPedir mesa y ver acuse LM-XXXXDejar constancia, no llevarse el libro
US-05BibliotecariaEntrar con JWTAbrir la mesa de pedidos
US-06BibliotecariaVer lista de hoy + pedidosOficio sin kanban
US-07AmbosVer empty / error / loadingNo quedarse en blanco

Could (fuera de L2): login de Nora, préstamo, fondo completo, chat, cuenta de lector, Absys.

7. Journey (resumen)

  1. Rafa abre /. Ve métricas (2 disponibles, 1 en sala, 1 pedido) y 4 filas. Lead El río Manzanares DISPONIBLE. [H1]
  2. Toca El río Manzanares → /lomo/LM-2501. (O busca «manzanares» en /buscar.)
  3. Lee sala 2, estado Disponible. Copy irrenunciable.
  4. Toca Pedir mesa → /pedir/LM-2501. Escribe su nombre. POST → /ok/LM-2501. Lee las cuatro oposiciones.
  5. Inés entra a /login/mesa. Lista + pedidos.
  6. Lee LM-2501 y el pedido de Rafa. Copy: reserva no es préstamo a casa.

Backstage: Nest + Neon + JWT. Nora no entra. El lomo no sale. No hay Absys.

8. Blueprint (resumen)

CapaQué pasa
Frontstage (sala)Mesa de datos en mostrador / móvil. Comando. Ficha. Form pedir. Acuse.
Backstage (archivo / taller)Inés mira /mesa. Nora está en el taller, fuera de app. La signatura y el estante son oficio, no OPAC.
SoporteNest · Neon dawn-butterfly-79519092 · JWT BIBLIO · Paper mineral

9. Matriz hallazgo → requisito → feature

Hallazgo (etiquetado)RequisitoFeature v1
[HIPÓTESIS H1] Mesa de ocupación reduce colaMétricas + 4 filas en fold/ H-DATA
[HIPÓTESIS H2] Reserva ≠ préstamo evita el distritoCopy irrenunciable + POST que no crea préstamo/pedir/:code + /ok/:code
[HIPÓTESIS H3] Cuatro lomos bastanSin fondo completoSeed 4 · no OPAC
[SUPUESTO S2] Sin cuenta de lectorPOST público + GET ?q= públicoPOST /api/ejemplares/:code/pedir
[SUPUESTO S3] Una bibliotecariaUn rol JWTBIBLIO
[DECISIÓN S4] Sin préstamo / GPS / chatNo carnet, no mapa, no hiloFuera de alcance

10. Preguntas que este case no responde

  • ¿Cuántas reservas reales hay en Usera en 2026?
  • ¿Qué archivo opera El río Manzanares?
  • ¿Rafa preferiría la cola?
  • ¿Nora necesita un cuadrante de restauración?
  • ¿Inés confirmaría la mesa antes de las 11:00 de verdad?

Esas preguntas piden campo. Aquí hay un vertical slice de diseño, no un estudio.

11. Cierre

Investigación = modelado etiquetado + restricciones ALS-2.
No hay entrevistas inventadas.
Las personas son constructos. Usera / El río Manzanares / Planos de Usera son [SUPUESTO de catálogo].
ONDA no se investiga como producto vivo: es N−1; leftover programas / CONDUCTOR no es LOMO.

03-information-architecture.md

03 — Arquitectura de información — LOMO

1. Principios de IA

PrincipioAplicación
Datos firstLa home es métricas + 4 filas. No hay landing de marca por delante. No hay search como home
Público / mesaRafa pide mesa sin cuenta; Inés entra con JWT
Fila → ficha → form → acuseProfundidad 3 desde la mesa de datos hasta el código (F-LEAD)
El lomo se busca en comandoS-CMD vive en la barra y en /buscar; no sustituye H-DATA
Mesa BIBLIO = lista + pedidosUna columna + recuento; no kanban, no split lista | ficha
Lenguaje de dominioLomo, ejemplar, signatura, sala, pedido, mesa — no «track», «programa», «lead CRM», «préstamo»
Pedir no es prestarEl acuse no tiene fecha de devolución ni carnet
Lomo no es catálogoNo hay facetas de distrito, no hay Absys
Chrome de oficioS-CMD «buscar lomo o signatura»; mesa de datos sin top-nav de marketing

2. Sitemap

/                                      Mesa de datos pública (métricas + 4 filas)     H-DATA
/buscar                                Comando · query + resultados                  S-CMD · público
/lomo/:code                            Ficha de lomo                                 público
/pedir/:code                           Form reserva de mesa                          F-LEAD · público
/ok/:code                              Acuse post-pedido                             público
/login                                 Login JWT BIBLIO                              público
/mesa                                  Lista + pedidos                               auth
/**                                    → redirect /

Árbol por audiencia

AudienciaNodos relevantes
LECTORHoy → (Buscar) → Lomo → Pedir → Ok. No ve /mesa
BIBLIOLogin → Mesa · (puede leer la mesa de datos y buscar)
RESTAURADORANinguno como rol de sistema

[COMPROBADO] Siete rutas del encargo L2. No se inventan /programa, /hoy (JWT ONDA), /nodos, /alta, /playlist, /prestamo, /club, /absys.

La búsqueda es ruta (/buscar): el comando se demuestra; la home sigue siendo datos. [DECISIÓN]
La ficha es ruta (/lomo/:code) porque el comando y la mesa necesitan URL compartible. [DECISIÓN]
El form es ruta (/pedir/:code): F-LEAD es un paso, no un POST oculto en la ficha. [DECISIÓN]
/ok/:code usa el código de ejemplar (LM-2501), no un código de petición distinto. L2 compacto. [DECISIÓN]

3. Navegación

Comando (S-CMD · todas las superficies salvo login/ok pueden mostrarlo)

SlotDestinoVisibilidadNotas
Campo/buscar?q=TodosPlaceholder «buscar lomo o signatura». Overlay o página.
Wordmark LOMO/TodosVuelve a la mesa de datos. No es hero de marca
Mesa/mesaBIBLIO; si guest → /login?next=/mesaOficio JWT

[DECISIÓN] El comando es la navegación primaria de hallazgo. La mesa de datos es la de ocupación. No hay rail Estudio / Buscar / Hoy (eso fue ONDA). No hay split (RAMAL), no dock, no kiosk mínimo (TEMPLE).

Desktop ≥960px: cabecera con wordmark + campo comando (~56px) + panel de datos.
El overlay de comando (UI-03) se abre al foco o al pulsar / · ⌘K. Submit navega a /buscar?q=. [DECISIÓN]
Mobile ~390px: campo de comando bajo el wordmark; la tabla se apila. [DECISIÓN] En 390 el comando no compite con un hamburger.

Mesa pública (H-DATA)

ElementoDestinoNotas
Wordmark LOMO/Sello mineral; no es hero 2-col
Eslogan«El lomo, no el prestado.»
Métricas leadDisponibles / En sala / Pedidos
Fila de ejemplar/lomo/:code4 filas; signatura, título, sala, estado
Link Mesa (mute)/login o /mesa si hay tokenSecundario, no CTA de marca

[DECISIÓN] La mesa de datos no tiene tabs de marketing, no tiene «Catálogo / Préstamo / Club». El comando basta para hallar.

Chrome de ficha y lead (F-LEAD)

SlotDestinoNotas
← Hoy/Un paso atrás, no stepper de 5
Campo query/buscar?q=GET ejemplares
Resultado/lomo/:codeMisma ficha que la mesa
Pedir mesa/pedir/:codeForm; no wizard 1—2—3 de fincas
EnviarPOST → /ok/:codeUna acción

Chrome de mesa (/mesa autenticado)

SlotDestinoVisibilidadNotas
Mesa/mesaAuthTitular de oficio
ListaAuthEjemplares de hoy
PedidosAuthNombre + signatura; no columnas
Hoy/AuthSecundario vía wordmark
Salirlimpia token → /AuthCabecera de mesa, no cuarto slot

[DECISIÓN] No hay S-SPLIT (RAMAL). No hay kanban (PREGÓN). No hay /mesa/:code: L2 lee lista + pedidos. Could L+1: ficha de pedido.

4. Etiquetas de IA (UI)

ConceptoLabel
HomeHoy / Mesa de hoy (no «Inicio», no «Catálogo», no «Galería»)
BúsquedaBuscar / Comando
FichaLomo (no «programa», no «track», no «título SKU»)
FormPedir mesa
SuccessPedida / código
StaffMesa (no «Backoffice», no «Hoy» tipo ONDA en UI JWT, no «Kanban»)
AuthBibliotecaria (no «Área cliente», no «Conductora»)
CTAPedir mesa (no «Prestar», no «Reservar 2 h», no «Inscribirse»)

En docs puede decirse «mesa de datos» para H-DATA y «mesa BIBLIO» para el oficio JWT. En UI pública el titular es Hoy. En UI autenticada el titular es Mesa. [DECISIÓN]

5. Contenido de cada nodo de IA

RutaQué hayQué no hay
/Métricas, 4 filas, eslogan, comandoCubiertas de radio, precios, login de lector, OPAC
/buscarCampo, query «manzanares», resultados, emptyFacetas municipales, chips de distrito
/lomo/:codeCubierta, título, sala, estado, copy, CTAPréstamo, club, follow, player
/pedir/:codeNombre, ejemplar en solo lectura, disclaimerDNI, email, carnet, socio
/ok/:codeCódigo + 4 oposicionesContrato, préstamo, «ya está inscrito»
/loginEmail / clave BIBLIOSignup de lector
/mesaLista de 4 + pedidosColumnas Recibido / En curso; split ficha

6. Error / empty / loading (sitio)

Viven en las mismas rutas, no en URLs extra.

EstadoSuperficie
LoadingSkeletons de métricas + 4 filas / resultados / lista de mesa
Empty hoy«Aún no hay lomos hoy.»
Empty búsqueda«Sin lomos.»
Empty mesa«No hay pedidos.»
Error red«La red no responde» + Reintentar
404 lomo«Ese lomo no está» en ficha

7. Profundidad y búsqueda

La búsqueda es Must (S-CMD), pero no es la home (eso rompería H-DATA y copiaría PIZARRA). 4 ejemplares caben en la tabla; el comando se demuestra con q=manzanares → El río Manzanares. [DECISIÓN]
Inés no filtra la mesa en v1: ve el hoy entero + pedidos. Could L+1: filtro por estado.

Query: min 1 carácter tras submit o debounce; case-insensitive sobre title, code, lead. Vacío en /buscar sin q muestra prompt de comando, no los 4 (eso es la mesa de datos). [DECISIÓN]

8. Códigos y URLs

PiezaForma
EjemplarLM-2501LM-2504 (LM versales, guion, 4 dígitos)
Ficha/lomo/LM-2501
Pedir/pedir/LM-2501
Acuse/ok/LM-2501
Búsqueda/buscar?q=manzanares

API puede normalizar lm-2501LM-2501. En UI siempre versales.

9. Lo que no entra en el mapa

/programa, /programa/:code, /hoy (JWT ONDA), /nodos, /alta, /mesa/:code, /playlist, /prestamo, /club, /inbox, /kanban.
Si el rewrite de lomo-app todavía tiene rutas ONDA (/programa, /hoy JWT, GalleryPage), no son el producto. Gana este doc.

04-user-flows.md

04 — Flujos de usuario — LOMO

1. Convenciones

TokenSignificado
F-LEADPedir reserva de mesa; la home no es el form
MesaLista BIBLIO, JWT
201Pedido creado (pedida)
409Ejemplar no DISPONIBLE
401Mesa sin token
404Lomo inexistente

2. F1 — Lector pide mesa desde la ocupación (Must)

/  →  GET /api/ejemplares
   →  tap fila El río Manzanares  →  /lomo/LM-2501
   →  GET /api/ejemplares/LM-2501
   →  tap Pedir mesa  →  /pedir/LM-2501
   →  escribe nombre «Rafa Díez»
   →  POST /api/ejemplares/LM-2501/pedir  { nombre }
   →  201 { code: LM-2501, title, status: PEDIDO, pedida: true }
   →  /ok/LM-2501

Entrada: mesa de datos, sin cuenta.
Salida: código visible + copy «reserva de sala ≠ préstamo a casa».
Errores: red (reintentar), 404 lomo, 409 no disponible, 0 ejemplares (empty).

AC

  • El form no pide email, DNI ni signup.
  • POST no cobra, no crea user, no abre préstamo, no inscribe al club.
  • /ok/LM-2501 muestra las cuatro oposiciones.
  • El paso /pedir/:code existe: F-LEAD no es un POST oculto en la ficha. [DECISIÓN]

3. F2 — Lector busca en el comando (Must)

/buscar  →  escribe «manzanares»  →  GET /api/ejemplares?q=manzanares
         →  resultados (El río Manzanares)
         →  tap  →  /lomo/LM-2501
         →  (sigue F1 desde la ficha)

Desde /, el foco en S-CMD abre overlay (UI-03) o navega a /buscar?q=. [DECISIÓN]

Entrada: comando, sin cuenta.
Salida: al menos El río Manzanares para q=manzanares.
Errores: q=zzzz → empty «Sin lomos.»; red → UI-11.

AC

  • Home sigue siendo datos. /buscar no redirige a / con un hero ni con H-SEARCH.
  • Query visible en el campo. Compartible /buscar?q=manzanares.
  • Cero facetas de distrito / precio (eso fue PIZARRA).
  • Cero archivo de programas (eso fue ONDA).

4. F3 — Bibliotecaria entra a mesa (Must)

/login  →  POST /api/auth/login
        →  JWT BIBLIO
        →  /mesa
        →  GET /api/mesa

AC

  • Sin token, /mesa redirige a /login?next=/mesa.
  • GET /api/mesa sin Bearer → 401.
  • Lista de hoy: 4 filas + pedidos. No columnas Recibido / En curso.
  • Desktop: lista + pedidos en el panel. 390: apilado bajo el comando.

5. F4 — Estados de sistema

EstadoTriggerUI
Loading hoyGET ejemplares en vueloSkeletons de métricas + 4 filas
Empty hoy200 []«Aún no hay lomos hoy.»
Error hoy5xx / redReintentar
Loading buscarGET ?q= en vueloSkeletons de resultado
Empty buscar200 []«Sin lomos.»
Loading fichaGET :codeSkeleton de ficha
404 fichacódigo inexistente«Ese lomo no está.»
Loading pedirForm listo; submit pending
409 pedirEN_SALA o PEDIDO«No se puede pedir esa mesa.»
Loading mesaGET mesa en vueloSkeletons de fila + pedidos
Empty mesa200 { pedidos: [] }«No hay pedidos.» (las 4 filas pueden existir)
Login 401credenciales«Email o contraseña no valen.»

6. Flujos que no existen

FlujoPor qué
Signup lectorS2 · Pedir ≠ club
Préstamo a casaS4 · Reserva ≠ préstamo
Inscribirse al clubPedir ≠ apuntarse al club
Chat≠ PREGÓN
Alta de nodo≠ RAMAL
Pedir repetición≠ ONDA
Wizard 1—2—3 de fincasPedir es un form corto, no onboarding
Login NoraSlice L2
PATCH estado de ejemplarInés lee; no mueve columnas en v1
Fondo completo / AbsysH3 · S4
Coger slot de 2 h≠ TEMPLE

7. Tiempos de demo (modelo)

ActorRecorridoTecho
RafaHoy → Manzanares → pedir → pedida< 90 s
Rafa (comando)Buscar «manzanares» → ficha → pedir< 90 s
InésLogin → mesa → leer LM-2501< 45 s

No son KPIs medidos. Son techos de diseño para el guión. [HIPÓTESIS]

8. Guión de demo canónico

  1. / — métricas + 4 filas. Lead El río Manzanares DISPONIBLE. Eslogan. Comando.
  2. Tap El río Manzanares — ficha DISPONIBLE · sala 2.
  3. Pedir mesa — /pedir/LM-2501 · nombre Rafa Díez.
  4. Enviar — /ok/LM-2501. Copy ×4.
  5. /buscar?q=manzanares — un resultado.
  6. /loginines@lomo.barrio / password123.
  7. /mesa — lista + pedidos; LM-2501; pedido de Rafa si se hizo el POST.
  8. 390: filas apiladas, no carousel; comando visible.
  9. API down: error + Reintentar.

9. Contratos de error de API (flujo)

CasoHTTPUI
POST pedir, code inexistente404«Ese lomo no está.»
POST pedir, no DISPONIBLE409«No se puede pedir esa mesa.»
POST pedir, nombre vacío400«Escribe un nombre.»
POST pedir, redseal + Reintentar; no navega a ok
GET mesa sin JWT401redirect login
Login malo401copy de login
Query vacía en buscar (sin submit)no fetchprompt de comando

05-data-model.md

05 — Modelo de datos — LOMO

1. Visión general

Dominio L2 de mesa de ejemplares + comando + lead autenticable:

EntidadTabla SQLPropósito
UserusersIdentidad de oficio (rol BIBLIO)
SessionsessionsSesión JWT / token persistido
EjemplarejemplaresLomo de hoy (código LM-XXXX, sala, estado, cubierta)
PedidopedidosReserva de mesa anónima de cuenta sobre un ejemplar

Base: PostgreSQL (Neon project dawn-butterfly-79519092) · IDs: serial + texto estable (LM-2501).
[DECISIÓN] ensureSchema es válido si el contrato de tablas se cumple; el brief de stack no exige Prisma.

Contrato de producto: ejemplares + pedidos + JWT BIBLIO.
No programas / repeticiones / nodes / altas / rooms / bookings.

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

Role

ValorDescripción
BIBLIOBibliotecaria de la mesa; único rol autenticado v1

EjemplarStatus

ValorDescripción
DISPONIBLEEn el estante; se puede pedir mesa
EN_SALAAlguien lo lee ahora; no es préstamo a casa
PEDIDOYa hay mesa pedida; no es inscrito al club

Pedir mesa solo está permitido en DISPONIBLE. EN_SALA y PEDIDO responden 409. [DECISIÓN]

3. Diagrama ER (texto)

users
  id, email, password_hash, name, role(BIBLIO)
  created_at
  1 ──< sessions

sessions
  id, user_id → users
  expires_at, revoked_at
  created_at

ejemplares
  id
  code (unique, LM-XXXX)
  title, lead
  status (DISPONIBLE|EN_SALA|PEDIDO)
  sala (INTEGER 1–3)
  cover
  sort_order
  created_at, updated_at
  1 ──< pedidos

pedidos
  id
  ejemplar_id → ejemplares
  nombre
  created_at

v1: una sola BIBLIO opera sobre el conjunto global.
No hay FK de lector-usuario: el lector no se loguea.
pedidos.nombre es el nombre de mostrador (oral), no una cuenta.

4. Tablas

users

ColumnaTipoNotas
idBIGSERIAL PK
emailTEXT UNIQUEines@lomo.barrio
password_hashTEXTbcrypt 12 · seed $2b$12$OEKcNck63neDJ.CrVP0lNOaXp2NG2zd2aMiGmnYY221h4ZAZ58x1m
nameTEXTInés Soto
roleTEXTBIBLIO
created_atTIMESTAMPTZ

sessions

ColumnaTipoNotas
idUUID PKsid del JWT
user_idBIGINT FK
expires_atTIMESTAMPTZ
revoked_atTIMESTAMPTZlogout
created_atTIMESTAMPTZ

ejemplares

ColumnaTipoNotas
idBIGSERIAL PK
codeTEXT UNIQUELM-2501
titleTEXTEl río Manzanares
leadTEXTUna frase. Reserva ≠ préstamo.
statusTEXTDISPONIBLE | EN_SALA | PEDIDO
salaINTEGER1 · 2 · 3
coverTEXT/assets/lomos/estante.jpg
sort_orderINTEGER1 = lead de la tabla
created_at / updated_atTIMESTAMPTZ

pedidos

ColumnaTipoNotas
idBIGSERIAL PK
ejemplar_idBIGINT FK
nombreTEXTNombre de mostrador (Rafa Díez)
created_atTIMESTAMPTZ

Sin email, sin teléfono, sin DNI, sin carnet. Pedir ≠ apuntarse al club. [DECISIÓN]

Índices: ejemplares(code), ejemplares(status), ejemplares(sort_order), users(email), pedidos(ejemplar_id), pedidos(created_at DESC).

Búsqueda q: ILIKE sobre title, code, lead. Trigram Could L+1; 4 filas no lo necesitan.

5. Seed

BIBLIO

ines@lomo.barrio / password123 · Inés Soto · BIBLIO
Hash bcrypt fijo: $2b$12$OEKcNck63neDJ.CrVP0lNOaXp2NG2zd2aMiGmnYY221h4ZAZ58x1m

Ejemplares

codetitlestatussalacover
LM-2501El río ManzanaresDISPONIBLE2/assets/lomos/estante.jpg
LM-2502Planos de UseraEN_SALA1/assets/lomos/mesa.jpg
LM-2503Crónica del mercadilloPEDIDO3/assets/lomos/silla.jpg
LM-2504Voces del barrioDISPONIBLE2/assets/lomos/estante.jpg
codelead (catálogo)sort
LM-2501El río que corta Usera. Se lee en sala 2. No se lleva a casa.1
LM-2502Planos del barrio. Ahora mismo en mesa de sala 1.2
LM-2503Pregón escrito. Ya hay mesa pedida en sala 3.3
LM-2504Voces del patio. Disponible en sala 2.4

[SUPUESTO de catálogo] Títulos, leads y salas son atrezzo. No es un fondo oficial.

Pedidos

Seed una fila: LM-2503 · nombre Carmen Vidal (constructo de mostrador, no cuenta).
El POST de smoke crea la de Rafa sobre LM-2501.
Inés puede ver 1 pedido al entrar en frío y ≥2 tras el guión.

El seed idempotente restaura los 4 estados de catálogo (2 DISPONIBLE, 1 EN_SALA, 1 PEDIDO) para que el demo sea repetible. [DECISIÓN]

6. JSON (camelCase)

API: sortOrder, createdAt, pedidoCount, cover, sala, status, title, code, lead, nombre.
SQL: sort_order, created_at, pedido_count derivado.

GET /api/ejemplares (item)

{
  "code": "LM-2501",
  "title": "El río Manzanares",
  "lead": "El río que corta Usera. Se lee en sala 2. No se lleva a casa.",
  "status": "DISPONIBLE",
  "sala": 2,
  "cover": "/assets/lomos/estante.jpg",
  "sortOrder": 1
}

Lista pública: no incluye nombre de pedidos (evita enumerar lectores). [DECISIÓN]

Métricas de hoy (home puede derivarlas del array):

MétricaSeed
disponibles2
enSala1
pedidos1
total4

GET /api/mesa (JWT)

{
  "date": "2026-08-25",
  "ejemplares": [{ code, title, status, sala, cover, pedidoCount, sortOrder }],
  "pedidos": [{ code, title, nombre, createdAt, status }]
}

Orden ejemplares: DISPONIBLE, EN_SALA, PEDIDO, luego sort_order.
Orden pedidos: created_at DESC.

POST /api/ejemplares/:code/pedir → 201

Cuerpo:

{ "nombre": "Rafa Díez" }

Respuesta:

{
  "code": "LM-2501",
  "title": "El río Manzanares",
  "sala": 2,
  "status": "PEDIDO",
  "pedida": true
}

Nombre obligatorio. No email. Si el ejemplar no está DISPONIBLE409. Si no existe → 404. Si nombre vacío → 400.

El POST pasa el ejemplar a PEDIDO. [DECISIÓN]

7. Generador de código

Los códigos de ejemplar son de catálogo, no se generan en el POST de pedir. Seed LM-2501LM-2504. L+1 que añada ejemplares usa LM- + 4 dígitos correlativos; no es Must.

El acuse reutiliza el code del ejemplar. No hay LM-P-…. [DECISIÓN]

8. Qué no se modela

FueraMotivo
Tabla restorersNora no entra
Tabla readers / socios / carnetsPedir ≠ club · S2
Tabla loans / due_atReserva ≠ préstamo · S4
Tabla slots / rooms / bookings de 2 h≠ TEMPLE
programas / repeticionesDominio ONDA; no es el contrato
nodes / altasDominio RAMAL; no es el contrato
OPAC / fondo / absysH3 · S4

06-tech-stack.md

06 — Stack tecnológico — LOMO

1. Visión

CapaTecnologíaNotas
FrontendAngular standalone + signalsPuerto 4200 · web responsive
EstilosTailwind CSSTokens mineral (ground, ink, card, mark, mute, seal)
BackendNestJS TypeScriptPuerto 3025, prefijo /api
PersistenciaPostgreSQL (ensureSchema)Tablas users, ejemplares, pedidos, sessions
DBNeon PostgreSQLproject dawn-butterfly-79519092
AuthJWTRol BIBLIO (/mesa); datos, comando y POST pedir públicos
DiseñoPaper01M0VVNT406AK640AK1ECY2DBR
Repo/Users/cristian/orca/lomo-app/Rewrite en paralelo; este doc es el contrato
Packagepnpmpnpm --filter · prohibido npm workspaces que rompen Angular (AP-06)

Stack fijo del cron (D-P0-03). No React/Next/Firebase.
[COMPROBADO] Puerto 3025, Neon, Paper y path de app constan en el encargo.

El código se escribe en paralelo a esta suite. Si el repo todavía contiene leftover ONDA (programas, repeticiones, CONDUCTOR, onda-api, puerto 3023) o leftover RAMAL (nodes, altas, VOCAL), no es el estado del producto. Gana el contrato LOMO.

2. Estructura

lomo-app/
├── apps/api/     # Nest (paquete independiente)
├── apps/web/     # Angular + Tailwind (paquete independiente)
└── package.json  # scripts api / start · apps independientes

D-P1-03: pnpm --filter. Prohibido npm workspaces que rompen Angular (AP-06).

Scripts raíz esperados:

ScriptAcción
apiArranca Nest en :3025
start / webArranca Angular en :4200

3. API pública vs JWT

MétodoRutaAuth
GET/api/healthPúblico · { ok: true, service: "lomo-api" }
GET/api/ejemplaresPúblico
GET/api/ejemplares?q=Público · comando
GET/api/ejemplares/:codePúblico
POST/api/ejemplares/:code/pedirPúblico
POST/api/auth/loginPúblico
GET/api/mesaJWT BIBLIO

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

[COMPROBADO] Superficie de dominio del encargo: ejemplares, ?q=, :code, pedir, /mesa JWT. Health y login son el contrato de stack de la serie.
No se añaden Must extra (/programas, /repetir, /nodos, /altas, /prestamo).

Health: leftover onda-apilomo-api. Puerto leftover de ONDA :3023 no se reutiliza: LOMO usa :3025. [DECISIÓN]

4. Justificación

ElecciónRazón
Angular + NestAlineación con la serie daily y el handoff del estudio
Neon serverlessVolumen bajo (S1: 2–4 ejemplares/día; demo 4 × pedidos de mostrador); no cola Redis
JWT BIBLIOMesa interna cerrada; la ocupación sí es pública
Auth en servidor para /mesaSource of truth = guard; el cliente no «esconde» pedidos
Puerto 3025Evitar colisión con ONDA :3023 / RAMAL :3022 / TEMPLE :3021 / PREGÓN :3020
Web (no PWA Must)S2: portal + mesa desktop; install Could L+1
code LM-XXXXAcuse humano, no UUID en el mostrador
POST con nombre, sin emailPedir ≠ apuntarse al club
pnpmEncargo; filtro por app

5. Variables

.env local (nunca git):

VariableServicioDescripción
DATABASE_URLAPINeon dawn-butterfly-79519092
JWT_SECRETAPIFirma tokens
PORTAPIopcional, 3025
SESSION_TTL_SECONDSAPIopcional (default 86400)

URI canónica (demo local, no commitear en frontend):

postgresql://neondb_owner:npg_GDsv4KLefP5d@ep-empty-star-a69kwo3y-pooler.us-west-2.aws.neon.tech/neondb?sslmode=require

Región: us-west-2 · pooler · sslmode=require.
.env.example solo placeholders.
Web: URL de API en ApiService (default http://localhost:3025/api). Proxy Angular proxy.conf.json:3025.

6. Módulos API

health · auth · users · ejemplares · mesa · database · common.

Health: HealthService.name = 'lomo-api'.
main.ts PORT default 3025.

No módulos programas, hoy (JWT ONDA), repeticiones, nodes, altas. Si el rewrite los encuentra, se borran o se sustituyen. [DECISIÓN]

7. Frontend

Páginas: HoyPage / · BuscarPage /buscar · LomoPage /lomo/:code · PedirPage /pedir/:code · OkPage /ok/:code · LoginPage /login · MesaPage /mesa.

Guard JWT en /mesa. Interceptor Bearer. Token key lomo.accessToken.

8. Lo que el stack no es

No es Absys. No es la Biblioteca Regional. No es un OPAC. No es Prisma obligatorio.
No es un leftover de programas/repeticiones: el contrato es ejemplares + pedidos + BIBLIO.
No es un leftover de radio: health no dice onda-api.

07-creative-direction.md

07 — Dirección creativa — LOMO

1. Concepto

LOMO es una mesa de ocupación bajo caliza: se entra a cuatro filas y se sale habiendo pedido una mesa, no una landing de «tu biblioteca». El ground es polvo de caliza. El cobre oxidado puntúa, poco. El sello terracota marca pedido, poco.

Mood elegido: mineral.
Primer instinto descartado: bookish / yeso (librería cálida). Un lomo se lee bajo la caliza, con metal oxidado, no sobre papel de novela.

[COMPROBADO] Mood, ground y paleta salen de docs/00-day-brief.md.

2. Territorio visual

EjeNo
SuperficieCaliza #E4E0D6, tinta #1C2B28, ficha #FFFFFF, cobre #3E6B5EPapel de cartel ONDA, hielo RAMAL, ink TEMPLE, tiza PIZARRA
TipoSource Serif 4 + OutfitDM Serif+IBM Plex, Newsreader+Figtree, Fraunces+Atkinson
FotoEstante de sala, mesa de lectura, silla, archivo de barrioEstantería de cadena, micro con cinta, mástil de operador, aula municipal
GridMesa de datos: métricas + 4 filas; comando en cabeceraHero 2-col + 3 cards (AP-12) · galería 16/10 (ONDA) · search-first (PIZARRA)
AcentoMark #3E6B5E en CTA, foco, comando; seal #9A5340 en pedidoCinta amarilla ONDA, cobre RAMAL, tiza PIZARRA
ChromeComando «buscar lomo o signatura»Rail Estudio/Buscar/Hoy, split, dock, kiosk mínimo

3. Paleta

TokenHexRol
Ground / G-STONE#E4E0D6Polvo de caliza. Fondo de página.
Ink#1C2B28Texto, wordmark, tinta de lomo.
Card#FFFFFFFicha, fila, form. Superficie sobre caliza.
Mark#3E6B5ECobre oxidado: CTA Pedir, foco, filete. Poco.
Mute#5F6B67Meta, labels, sala secundaria, idle
Seal#9A5340Sello «pedido» y error. Poco. No pinta el header.

Jerarquía de color: la caliza carga; el cobre puntúa; el sello marca pedido. Si el mark pinta fondos enteros, deja de ser óxido y se vuelve «brand verde». El seal no decora la cabecera: solo puntual (PEDIDO, error). El texto sobre mark es card/blanco o ink según contraste; nunca mute. [DECISIÓN]

Candidatos de mood descartados: bookish/yeso (primer instinto biblioteca), chapel, alpine, fog.

4. Tipografía

UsoFamiliaTamaños de referencia
WordmarkSource Serif 4 60024–28 desktop · 22 mobile
Titular de hoy / mesaSource Serif 4 60032–44
Código LM-XXXXSource Serif 4 60028–36
Nombre de lomoSource Serif 4 60022–28
Cuerpo / disclaimersOutfit 40016–17 / 26–28 lh
UI, comando, form, listaOutfit 400/500/60014 / 16
Meta (sala, status, signatura)Outfit 50012–13, tracking 0.12em, ink o mute

Prohibido: Source Serif 4 por debajo de 18px (se rompe). Outfit en el wordmark de hoy (se vuelve SaaS). Mark como color de texto de párrafo (contraste).

Fuentes vetadas de la serie reciente: DM Serif Display / IBM Plex Sans / Newsreader / Figtree / Fraunces / Atkinson Hyperlegible / Ibarra Real Nova / Jost / Libre Baskerville / Nunito / Instrument Serif / Sora / Cormorant / Public Sans / Spectral / Manrope / Zilla / Mulish / Lora / Karla.

Outfit entra: el brief lo fija para UI. No es el Outfit vetado de un cooldown antiguo frente a un brief explícito. [COMPROBADO]

5. Fotografía

AssetDebeNo debe
hero.jpgSala de archivo de barrio: caliza, estante, mesa, luz de UseraBiblioteca Regional pulida, micro con cinta, aula de tiza
estante.jpgLomos en estante de sala; polvo, madera, signaturaEstantería IKEA de stock, cubiertas de radio
mesa.jpgMesa de lectura con planos / papel abiertoMesa de mezclas, NOC, kiosk
silla.jpgSilla de sala, ocupada o a punto; oficio de lecturaSilla de club, grada, butaca de concierto

Alt: lomo + lugar, no «imagen1». Ver doc 09.

LM-2501 y LM-2504 comparten estante.jpg (disponibles). El lead y el título distinguen. [COMPROBADO] en seed.

6. Registro R-CL

Herramienta de datos de sala de lectura (clásica de archivo) + comando (oficio). No es un dashboard de seguros. No es un kiosk de horas. No es una galería de cubiertas. No es una librería cálida.

La innovación está en H-DATA + S-CMD + F-LEAD, no en un look «bookish». [DECISIÓN]

7. Anti-moods

MoodPor qué no
Bookish / yesoPrimer instinto; es librería cálida
Signage / cinta amarillaFue ONDA
Ice institucionalFue RAMAL
Vehicle dashboardFue TEMPLE
Highlighter fogFue PREGÓN
Chapel amatistaFue MATIZ
Tiza municipalFue PIZARRA

8. Densidad (AP-21)

En el recorte de mesa de datos: eslogan + 3 métricas (disponibles / en sala / pedidos) + 4 filas con signatura, título, sala y estado visibles sin hover. Headline huérfano + foto = card hueca. [COMPROBADO] CRON §5.8.

08-design-system.md

08 — Sistema de diseño — LOMO

1. Tokens

TokenValorUso
--ground#E4E0D6Fondo de página (caliza)
--ink#1C2B28Texto
--card#FFFFFFFicha, fila, form
--mark#3E6B5ECTA, foco, comando
--mute#5F6B67Meta
--seal#9A5340Pedido, error
--font-displaySource Serif 4Titulares
--font-sansOutfitUI
--target44×44 minToques

Tailwind: bg-ground, text-ink, bg-card, bg-mark, text-mute, text-seal, font-display.
La fila vive sobre --card; el ground es caliza alrededor. [DECISIÓN]

2. Escala de tipo

RolSize / leading
Display hoyclamp(2.2rem, 5vw, 3.6rem) / 0.95
H1 mesa / ficha2.5rem / 1
H2 lomo28px / 32px
Cuerpo16–17 / 26
Meta12–13 / 16, tracking 0.12em, uppercase
Comando16 / 24, Outfit 400

3. Componentes

Fila de ejemplar (mesa de datos)

Thumb 4/3 recortado + signatura · título + sala + estado. Toda la fila es el hit. Destino /lomo/:code. Lead (El río Manzanares) ocupa la primera fila.

Métrica lead

Tres cifras: Disponibles · En sala · Pedidos. Número Source Serif, label Outfit mute. No gráficos.

Resultado de comando (buscar)

Misma anatomía en fila: thumb + título + sala + estado. Hit → ficha.

Overlay de comando (UI-03)

Campo a ancho de cabecera, resultados sobre card, query «manzanares». Esc cierra. Submit → /buscar?q=.

Ficha de lomo

Cubierta, título display, dl sala/estado/signatura, bloque de copy irrenunciable, CTA Pedir mesa (card sobre mark).

Form pedir

Nombre, ejemplar en solo lectura (código, título, sala), disclaimer, submit mark.

Acuse

Código Source Serif enorme + bloque card con título y sala + 4 oposiciones en mute.

Fila de mesa BIBLIO

Código · status · título · sala · recuento. Pedidos debajo: nombre · código · hora. Sin acciones PATCH en v1.

Login

Wordmark + dos inputs + CTA mark. Ayuda mute con demo.

Comando (S-CMD)

Campo «buscar lomo o signatura». Activo: filete mark 2px. Fondo card sobre ground.

Skeleton

animate-pulse bg-card. aria-busy.

Error

Titular display + cuerpo mute + Reintentar mark.

Sello PEDIDO / EN_SALA

Texto «Pedido» / «En sala» + seal o mute. Nunca solo el color. Poco: un sello por vista, no un header terracota.

4. Layouts

SuperficieGrid
Hoy 1440Cabecera comando; métricas en 3 cols; tabla de 4 filas sobre card
Hoy 390Métricas en fila scroll-snap o wrap; filas apiladas
BuscarColumna max-w-3xl
Lomo / Pedir / Ok / loginColumna max-w-lg (ok/login/pedir) · max-w-2xl (ficha)
Mesa BIBLIOLista + pedidos; stacked < lg

Max page 1440px. Padding 20 / 40 / 48.

5. Estados de control

EstadoTratamiento
DefaultCaliza, ink, card
Hover fila desktopRing 2px mark. Sin lift
FocusRing mark 2px
Disabledopacity 50–60
ErrorTexto seal, no solo color
Activo (comando)Filete mark
Pedida (CTA after 201)Navega a ok; no deja el botón en «Prestar»

6. Iconografía

Cero iconos decorativos de «libro abierto / carnet / antena / play». El oficio es tipográfico + foto + cobre. Comando: lupa mínima, stroke ink, 20px, no pack de color. [DECISIÓN]

7. Motion

120–160ms ease-out. Reduced motion: 0ms. Skeletons pulse; no spinners de Regional ni de radio.

8. Densidad (AP-21)

En el recorte de mesa: eslogan + 3 métricas + 4 filas con signatura, título, sala y estado. [COMPROBADO] CRON §5.8.

9. Contraste de cobre

#3E6B5E sobre #E4E0D6 no es texto de párrafo. CTA = text-card o text-ink sobre bg-mark según ratio ≥ 4.5:1. Meta = mute o ink sobre ground/card. Seal sobre ground ≥ 4.5:1 para el rótulo «Pedido» (el sello lleva texto, no solo círculo).

09-content-guide.md

09 — Guía de contenido — LOMO

1. Voz

Cercana de mostrador y de sala. Tuteo en UI al lector. Segunda persona cuando la interfaz habla. Tercera cuando habla el hoy (El río Manzanares ocupa…).

Sin jerga de Regional («tu carnet único», «red de Madrid», «préstamo interbibliotecario»). Sin Absys. Sin «apúntate al club». Sin sirena. Sin kanban en UI. Sin antena ni repetición.

El producto se mira y se pide mesa. No vende préstamo, no es la Regional, no inscribe al club, no crea cuenta.

2. Palabras permitidas / prohibidas

UsarNo usar
Lomo / ejemplar / signaturaPrograma, track, título SKU, OPAC
Pedir mesaPrestar, llevarse a casa, inscribirse, play
Disponible / en sala / pedidoRecibido, En curso, Prestado, En antena
Hoy (público) / Mesa (BIBLIO)Kanban, backoffice, catálogo, galería
BibliotecariaConductora, vocal, coordinadora de distrito, tutor
LectorCliente, lead, socio, oyente
SalaReserva de 2 h, TEMPLE, slot, estudio de radio
LOMO no es la Biblioteca RegionalRed de Madrid, Absys, carnet único
Reserva de sala ≠ préstamo a casaDevolución, sanción, renovación

«Kanban», «F-LEAD» y «H-DATA» son lenguaje de diseño, no de UI: Rafa no lee «lead pattern»; Inés lee Disponible / En sala / Pedido.

«Mesa» en UI autenticada es el oficio. En pública el titular es Hoy. Si hay ambigüedad en ficha, CTA = Pedir mesa, nunca «Reservar sala de 2 h». [DECISIÓN]

3. Microcopy duro (literales irrenunciables)

El brief fija oposiciones. Aparecen donde el usuario puede confundirse.

OposiciónDóndeCopy
Reserva de sala ≠ préstamo a casaFicha · pedir · acuse · pie de mesa«Pedir mesa no es un préstamo. El lomo no se lleva a casa.»
Lomo ≠ catálogo municipalHoy · buscar · acuse«Esto no es el catálogo del distrito. Es la ocupación de hoy.»
Pedir ≠ apuntarse al clubFicha · pedir · acuse«Pedir mesa no te apunta a ningún club.»
LOMO no es la Biblioteca RegionalHoy · acuse«LOMO no es la Biblioteca Regional. Es la sala del barrio.»

[DECISIÓN] Los cuatro textos son obligatorios en UI. No se diluyen a un tooltip.
Pie de hoy puede condensar dos en dos líneas; acuse muestra los cuatro.

4. Microcopy por superficie

SuperficieCopy
EsloganEl lomo, no el prestado.
Línea de lugarUsera
Hoy titularHoy
Métrica 1Disponibles
Métrica 2En sala
Métrica 3Pedidos
Comando placeholderbuscar lomo o signatura
Buscar titularComando
Buscar submitBuscar
Ficha volver← Hoy
Ficha CTAPedir mesa
Pedir titularPedir mesa
Pedir nombreNombre
Pedir submitPedir mesa
Pedir ayudaSolo un nombre de mostrador. No es un carnet.
Login titularEntrar a la mesa
Login ayudaSolo bibliotecaria. El lector pide mesa en el hoy, sin cuenta.
Login submitEntrar
Login errorEmail o contraseña no valen.
Empty hoyAún no hay lomos hoy.
Empty buscarSin lomos.
Empty mesaNo hay pedidos.
Success titularPedida.
Success códigoLM-2501
Success cuerpoPedir mesa no es un préstamo. El lomo no se lleva a casa.
Success extraLOMO no es la Biblioteca Regional. Pedir no te apunta al club. Esto no es el catálogo municipal.
Success CTAVolver al hoy
ErrorLa red no responde.
Error retryReintentar
LoadingCargando
404 lomoEse lomo no está.
409 pedirNo se puede pedir esa mesa.
Mesa titularMesa
SalirSalir
Link Mesa (hoy)Mesa
Status DISPONIBLEDisponible
Status EN_SALAEn sala
Status PEDIDOPedido
SalaSala 1 / Sala 2 / Sala 3

5. Ejemplares seed (tono)

Los ejemplares demo se escriben como lomos de sala de barrio, no como ficha de OPAC ni como anuncio de club.

PiezaTono
El río ManzanaresEstante. Sala 2. Disponible. No se lleva.
Planos de UseraMesa. Sala 1. En sala. Papel abierto.
Crónica del mercadilloSilla. Sala 3. Pedido. Pregón escrito.
Voces del barrioEstante. Sala 2. Disponible. Patio.

No inventar declaraciones de lectores reales. Si hay una voz, es de persona de diseño etiquetada.

En EN_SALA no se reescribe a «prestado hasta el 12». El cierre es el estado. [DECISIÓN]

6. Nombres propios del seed

NombreUso
Inés SotoBIBLIO; ines@lomo.barrio
Rafa DíezLECTOR; sin cuenta; pide El río Manzanares
Nora RuizRESTAURADORA; sin cuenta; (fuera)
Carmen VidalNombre de mostrador del pedido seed de LM-2503; no es cuenta
LOMO · UseraSala demo; El río Manzanares / Planos de Usera

No inventar CIF de biblioteca, código de sede municipal, ni Absys real como si fuera el producto. [SUPUESTO de catálogo demo]
Usera es topónimo reconocible, no un inventario oficial de una biblioteca 2026.

No usar Nerea, Iker, Pilar, Patio 17, CONDUCTOR, programas. [DECISIÓN] leftover ONDA.

7. Tono por estado

StatusFrase UIFrase que Inés podría decir (fuera de app)
DISPONIBLEDisponible«Está en el estante. No te lo llevas a casa.»
EN_SALAEn sala«Alguien lo lee. No es un préstamo.»
PEDIDOPedido«Ya hay mesa. No es el club.»

La app no envía esos mensajes al lector en v1. El copy solo prepara el habla. No hay email de «ya está en tu mesa».

8. Capitalización y números

  • Wordmark: LOMO en versales Source Serif 4.
  • Código: LM-2501 (LM versales, guion, 4 dígitos).
  • Nombres: Inés Soto, Rafa Díez, Nora Ruiz.
  • Lomos: El río Manzanares (artículo en el título, no el-rio en UI).
  • Sala: Sala 2 en UI.
  • Fecha: 25 ago si hay línea larga.
  • No mezclar lm-2501 en minúsculas en UI. API puede normalizar a versales.

9. Idioma

es-ES. Fácil lectura. EN no en v1.
No mezclar «loan» ni «OPAC» en UI. En API sí: ejemplares, sala, status.

«Lector» en UI cuando hablamos de Rafa; «la bibliotecaria» para Inés; «la restauradora» solo en docs. [DECISIÓN]

10. Accesibilidad de copy

  • No transmitir estado solo con color (el seal no «significa» pedido él solo).
  • Filas llevan texto: Disponible / En sala / Pedido.
  • Aviso de no-préstamo lleva palabras, no solo el cobre.
  • Alt de fotos: objeto + lugar, no «imagen1».
    • hero: «Sala de archivo de barrio en Usera; estante, mesa de lectura y caliza.»
    • estante: «Lomos en un estante de sala de lectura. Cubierta de El río Manzanares y Voces del barrio.»
    • mesa: «Mesa de lectura con planos abiertos. Cubierta de Planos de Usera.»
    • silla: «Silla de sala de lectura junto a un lomo. Cubierta de Crónica del mercadillo.»

11. Piezas que no se escriben

NoMotivo
«Llévatelo a casa» / «Préstamo 15 días»Reserva ≠ préstamo · S4
«Inscrito al club» / «Plaza confirmada»Pedir ≠ club
«Como en la Regional» / carnet únicoLOMO no es la Regional
«Catálogo Absys» / facetas de distritoLomo ≠ catálogo municipal
Recibido / En curso / CerradoNo es kanban
«En antena» / «Pedir repetición» / ON-Leftover ONDA
«Nodo aceptado» / «Alta RM-»Leftover RAMAL
Lorem / «lorem ipsum lomo»Prohibido en el case
Declaraciones inventadas de una biblioteca realVerdad de catálogo

10-accessibility.md

10 — Accesibilidad — LOMO

1. Alcance

WCAG 2.2 AA orientada al slice: mesa de datos, comando, ficha, pedir, acuse, login, mesa BIBLIO. No es una auditoría certificada. [DECISIÓN]

2. Contraste

ParRelación esperada
Ink #1C2B28 sobre caliza #E4E0D6≥ 7:1
Ink sobre card #FFFFFF> 10:1
Card/blanco sobre mark #3E6B5ETexto de CTA ≥ 4.5:1
Mute #5F6B67 sobre calizaMeta ≥ 4.5:1
Mute sobre cardMeta ≥ 4.5:1
Seal #9A5340 sobre caliza / card«Pedido» / error ≥ 4.5:1

Prohibido: texto mark sobre ground como párrafo (cobre como cuerpo). El cobre es superficie de CTA/foco, no párrafo.

Texto sobre foto: gradient from-ink/80 bajo el titular de cubierta. No titular suelto sobre estante claro.

3. Teclado y foco

  • Orden: wordmark → comando → métricas (no foco si no son controles) → filas → ficha CTA → form pedir → form login → lista mesa.
  • Foco visible: ring mark 2px (ink si el fondo ya es mark).
  • Filas operables con Enter / Espacio.
  • Pedir mesa no se activa al pasar el foco.
  • Overlay de comando: ⌘K / / abre; Esc cierra; flechas recorren resultados. [DECISIÓN]

4. Targets

≥44×44 en CTA, filas, campo de comando, link Mesa, submit pedir. Campo de búsqueda altura ≥44px.

5. Nombre accesible

ControlNombre
FilaTítulo del ejemplar (h2 o texto de fila)
ComandoBuscar lomo o signatura
Pedir mesaPedir mesa
Buscar submitBuscar
EntrarEntrar
SalirSalir

Fotos: alt de doc 09. Decorative? No: las cubiertas de ficha son contenido. Thumbs de fila tienen alt vacío si el título está al lado (alt="" + nombre en el texto). [DECISIÓN]

aria-current="page" no aplica a un rail; el wordmark no se marca current. El campo de comando en /buscar lleva aria-label.

6. Estados no solo por color

Disponible / En sala / Pedido en texto. Pedida: pantalla de acuse, no solo el botón en mark. Error: texto seal + mensaje. Empty búsqueda: «Sin lomos.»

7. Motion

prefers-reduced-motion: sin pulse. Skeletons estáticos. Cero animación de «páginas pasando» decorativa.

8. Mobile 390

Hoy: métricas wrap; filas apiladas. No carousel.
Buscar: campo sticky bajo el wordmark; resultados en columna.
Ficha: una columna; CTA al fondo del fold.
Pedir: form a columna; submit ≥44px.
Mesa: lista + pedidos a pantalla.
Comando: campo visible, no hamburger, no rail inferior de radio.

9. Autocomplete

Login: username / current-password. Buscar: type="search" · name="q". Pedir nombre: name · autocomplete="name". Sin autocomplete de dirección.

10. Idioma

html lang="es". Números y códigos leídos como texto (LM-2501). Salas «Sala 2», no room 2.

11. Búsqueda

El campo tiene label visible o aria-label="Buscar lomo o signatura". Resultados anuncian recuento (1 lomo / Sin lomos) vía texto, no solo el grid vacío.

12. Tabla de datos

La mesa de hoy es una lista de filas accionables, no una <table> opaca sin caption. Si se usa table: <caption> «Ejemplares de hoy» y encabezados Signatura / Título / Sala / Estado. En 390 las filas se apilan como cards, no se recortan columnas. [DECISIÓN]

11-privacy-security.md

11 — Privacidad y seguridad — LOMO

1. Superficie pública vs cerrada

RecursoAuthPII
GET ejemplaresNoNo (catálogo de ocupación)
GET ejemplares ?q=NoNo
GET ejemplares/:codeNoNo
POST pedirNoNombre de mostrador (mínimo)
GET /ok/:codeNo (solo código en URL)No hidrata pedidos ajenos
GET mesaJWT BIBLIONombres de pedidos

[DECISIÓN] El acuse no lista otros pedidos. Evita enumerar demanda de la sala.
La mesa pública no muestra nombre.

2. Datos de lector

v1 guarda solo un nombre de mostrador por pedido (pedidos.nombre). No email, no teléfono, no DNI, no carnet.
El lector no tiene fila en users. Pedir ≠ apuntarse al club.

El nombre existe para que Inés llame en sala («Rafa, sala 2»). No es una cuenta. [DECISIÓN]

3. Auth

  • JWT HS256. Claims: sub userId, sid session, role.
  • Password bcrypt 12. Demo password123 solo en seed local. Hash canónico $2b$12$OEKcNck63neDJ.CrVP0lNOaXp2NG2zd2aMiGmnYY221h4ZAZ58x1m.
  • Guard: Bearer + sesión no revocada.
  • Role BIBLIO para /mesa. 401 sin token, 403 si no BIBLIO.
  • Rate limit en login.

4. Secretos

DATABASE_URL y JWT_SECRET en apps/api/.env (gitignore). Nunca en Angular.
La URI Neon del encargo es para el rewrite local; no se embebe en el bundle.

5. CORS

Allowlist localhost:4200. Previews Vercel opcionales.

6. Amenazas del slice

AmenazaMitigación
Enumerar /ok/LM-2501 y ver pedidosOk no GET de pedidos; state de navegación + catálogo público
POST spam de pedidosVolumen demo; 409 si ya PEDIDO; rate Could L+1
Token en localStorageXSS = compromiso; slice L2 acepta el tradeoff de SPA [DECISIÓN]
Leftover CONDUCTOR / VOCAL en el mismo JWTSeed solo BIBLIO; claims role=BIBLIO
Login con email de NoraNo existe la fila
Nombre de lector en lista públicaFuera del JSON de GET ejemplares

7. RGPD (modelo, no dictamen)

Base: interés de la sala en gestionar la ocupación y las reservas de mesa. Minimización: solo nombre de mostrador en v1. Sin cesión a la Regional. Derecho de supresión Could L+1 (Inés no borra en v1).

Nora no tiene acceso. No hay logs de «quién restauró el plano».

8. Lo que no hacemos

No OAuth. No cookies de marketing. No analytics de terceros en v1. No cifrado extra más allá de TLS. No cuenta de lector «por si acaso». No carnet municipal.

12-analytics.md

12 — Analítica — LOMO

1. North star

% de reservas de mesa confirmadas antes de las 11:00.

Oficio de sala. No pageviews. No «engagement con el estante». Confirmar = Inés da la mesa (fuera de app en v1; el modelo cuenta el oficio, no un PATCH). [DECISIÓN]

En v1 no hay estado CONFIRMADA: la north star es de producto, no un campo SQL. Could L+1: confirmada_at.

2. Métricas de producto (modelo)

MétricaDefiniciónHipótesis
Activación lectorPOST /api/ejemplares/:code/pedir 201 / visitas a /H1
Uso de comandoGET /api/ejemplares?q= con q no vacío / visitasS-CMD
Tiempo a acuse< 90 s en demoDiseño
Tiempo a lectura de mesaGET mesa desde loginOficio
Integridad auth401 en GET mesa anónimoSeguridad
ExpectativaTickets «¿me lo llevo?» / «¿es Absys?» / «¿me apunto?» (fuera de app)H2

3. Eventos (si se instrumentara)

No hay tracker en v1. Contrato de nombres por si L+1:

EventoProps
hoy_viewejemplares_count, disponibles, en_sala, pedidos
buscar_queryq, results_count
pedir_submitcode
mesa_openejemplares_count, pedidos_count

Prohibido: prestamo, club_signup, play, repetir_submit, alta_submit.

4. Lo que no medimos

Préstamos a domicilio. Fondos de Absys. Aperturas de WhatsApp (no hay dato). NPS de la Regional. Repeticiones de radio.

5. Salud

GET /api/health{ ok: true, service: "lomo-api" }. Error rate 4xx/5xx en ejemplares/pedir/mesa.
Si health dice onda-api o ramal-api, el slice no está cerrado.

13-qa-test-plan.md

13 — Plan de QA — LOMO

1. Smoke API (pnpm test:api)

  1. GET /api/healthlomo-api (no onda-api, no ramal-api)
  2. GET /api/ejemplares → 4 codes LM-2501…LM-2504 + lead + sala + status + cover
  3. GET /api/ejemplares/LM-2501 → El río Manzanares · DISPONIBLE · sala 2
  4. GET /api/ejemplares?q=manzanares → incluye LM-2501; no exige los 4
  5. GET /api/ejemplares?q=zzzz[]
  6. POST /api/ejemplares/LM-2501/pedir { nombre: "Rafa Díez" } → 201 pedida: true · status PEDIDO
  7. POST /api/ejemplares/LM-2502/pedir { nombre: "Rafa Díez" } → 409 (EN_SALA)
  8. POST /api/ejemplares/LM-9999/pedir { nombre: "Rafa Díez" } → 404
  9. GET /api/mesa sin JWT → 401
  10. POST /api/auth/login Inés → BIBLIO
  11. GET /api/mesa → 4 ejemplares; pedidos con nombre; no lista pública de nombres en GET ejemplares

2. Smoke UI

#PasosEsperado
1Abrir /Métricas + 4 filas, lead El río Manzanares, eslogan, comando
2Tap El río Manzanares/lomo/LM-2501 · Disponible · sala 2
3Pedir mesa/pedir/LM-2501 · form nombre
4Enviar Rafa Díez/ok/LM-2501 + 4 oposiciones
5/buscar + «manzanares»El río Manzanares en resultados
6/buscar + «zzzz»«Sin lomos.»
7/login Inés/mesa lista + pedidos
8Leer LM-2501No botones Aceptar/Rechazar RAMAL; no En antena ONDA
9LogoutHoy; /mesa pide login
10/ 390filas apiladas, no carousel; comando visible
11API downError + Reintentar

3. Regresión de unicidad

  • No copy ONDA (programa, repetición, conductora, ON-, en antena, playlist, Patio 17).
  • No copy RAMAL (nodo, alta, vocal, RM-, azotea como producto, cobertura).
  • No copy TEMPLE (hora de ensayo, concierto, conserje, turno, TM-).
  • No copy PREGÓN (chat, kanban, 112, parte).
  • No copy PIZARRA (taller, plaza, distrito, Absys como producto).
  • No préstamo a casa, no club, no Regional como CTA.
  • Health no dice onda-api ni ramal-api.
  • Puerto 3025.

4. Accesibilidad rápida

Tab por comando y filas. Focus ring mark. Alt de fotos. Status en texto. CTA con contraste sobre mark.

5. Datos

Seed idempotente por email y code. Un POST de smoke sobre LM-2501 no borra el ejemplar. El re-seed restaura DISPONIBLE para repetir el demo.
Playwright futuro: esperar El río Manzanares, ines@lomo.barrio, no Patio 17, no nerea@onda.radio, no RM-2201, no «En antena».

6. Criterio de salida

Smoke API verde + guión UI completo + 0 leftover de dominio anterior en UI (ONDA o RAMAL).

14-dev-handoff.md

14 — Handoff de desarrollo — LOMO

1. DoD del slice

  • Health { ok: true, service: "lomo-api" } en :3025
  • 4 ejemplares seed con foto, lead, sala, status
  • GET /api/ejemplares?q=manzanares → El río Manzanares
  • POST pedir público → acuse /ok/:code
  • JWT BIBLIO ines@lomo.barrio / password123
  • /mesa lista + pedidos · GET JWT · no kanban
  • Empty / error / loading en hoy, buscar y mesa
  • Copy ×4 visible
  • Rutas / /buscar /lomo/:code /pedir/:code /ok/:code /login /mesa
  • Páginas HoyPage BuscarPage LomoPage PedirPage OkPage LoginPage MesaPage
  • Paper 12 UX + 12 UI mapeados
  • Docs 01–20 + README + executive
  • 0 leftover programas / repeticiones / CONDUCTOR / onda-api / nodes / altas / VOCAL

2. Contratos

Ver docs 05 y 20. JSON camelCase. SQL snake_case.

POST /api/ejemplares/:code/pedir:

{ "nombre": "Rafa Díez" }

201:

{ code, title, sala, status, pedida: true }

GET /api/mesa (JWT):

{
  date,
  ejemplares: [{ code, title, status, sala, cover, pedidoCount, sortOrder }],
  pedidos: [{ code, title, nombre, createdAt, status }]
}

Login:

{ email: "ines@lomo.barrio", password: "password123" }

3. Mapas de pantalla

PaperRutaComponente
UI-01 Mesa datos/HoyPage
UI-02 Mesa mobile/ 390HoyPage
UI-03 Comando/buscarBuscarPage
UI-04 Lomo/lomo/:codeLomoPage
UI-05 Lomo mobile/lomo/:code 390LomoPage
UI-06 Pedir/pedir/:codePedirPage
UI-07 Success/ok/:codeOkPage
UI-08 Login/loginLoginPage
UI-09 Mesa BIBLIO/mesaMesaPage
UI-10 Emptymismas rutasempty branches
UI-11 Error+Loadingmismas rutaserror / skeletons
UI-00 Tokenstailwind.config.js + styles.scss

4. Tokens en código

tailwind.config.js + styles.scss. Fuentes Google: Source Serif 4 + Outfit.
Colores: ground #E4E0D6 · ink #1C2B28 · card #FFFFFF · mark #3E6B5E · mute #5F6B67 · seal #9A5340.

5. Assets

Copiar 2026-08-25-lomo/assets/lomos/*.jpg y hero.jpg a apps/web/public/assets/.
Cubiertas: lomos/estante.jpg · mesa.jpg · silla.jpg.

6. No hacer

  • No reintroducir programas / repeticiones / CONDUCTOR como dominio vivo.
  • No dejar HealthService.name = 'onda-api' ni puerto 3023.
  • No reintroducir nodes / altas / VOCAL.
  • No documentar leftover ONDA como estado actual.
  • No añadir signup de lector, préstamo, club, chat, kanban, Absys, fondo completo.

7. Arranque

cd /Users/cristian/orca/lomo-app
# apps/api/.env → DATABASE_URL Neon dawn-butterfly-79519092, JWT_SECRET, PORT=3025
pnpm --filter @repo/api start:dev
pnpm --filter @repo/web start

El rewrite corre en paralelo. Este handoff es el contrato; no se espera a Paper ni se arranca aquí.

15-roadmap.md

15 — Roadmap — LOMO

Aquí solo viven ideas de L+1 / L4 y pulido posterior.
El slice L2 del día está cerrado: mesa de datos, comando, ficha, pedir, JWT BIBLIO, mesa de pedidos.

1. Hecho (L2)

  • Mesa H-DATA · métricas + 4 filas
  • Comando S-CMD · q=manzanares
  • Ficha · título, sala, estado
  • Form F-LEAD /pedir/:code
  • Acuse /ok/:code · reserva ≠ préstamo
  • Mesa JWT · lista + pedidos, no kanban
  • Empty / error / loading · datos 390
  • Neon dawn-butterfly-79519092 · API :3025 · lomo-api

2. L+1 (mismo producto, más oficio)

ItemNota
confirmada_at en pedidoNorth star medible en SQL
Marcar EN_SALA cuando se sientaInés PATCH; 4 filas no lo necesitan el día 1
Filtro de mesa por estado4 filas no lo necesitan; 20 sí
Rate limit POST pedirDemo no lo necesita
Recibo por… no: sigue sin cuentaNo email de lector
PWA installMostrador; no es Must
Trigram search4 filas no lo necesitan

3. L4 (requiere brief nuevo de diversidad)

  • Rol RESTAURADORA (Nora) + cuadrante de conservación
  • Préstamo a domicilio / carnet (otro producto; no es LOMO L2)
  • Fondo completo / Absys / OPAC
  • Multi-sede / multi-bibliotecaria
  • Chat / kanban (fue PREGÓN)
  • Club de lectura / inscripción (fue PIZARRA)
  • Cuenta de lector / socio

4. Fuera para siempre en este nombre

La Biblioteca Regional. Préstamo a casa el día de la petición. Catálogo municipal como producto. Programas y repeticiones (ONDA es otro día). Nodos y altas (RAMAL es otro día).

5. Criterio para abrir L+1 / L4

Solo si un día futuro elige archivo L3+ y la diversidad ALS-2 lo permite. No parche silencioso. No reabrir ONDA dentro de LOMO.

16-interaction-specs.md

16 — Especificación de interacción — LOMO

1. Convenciones

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

Loading y empty / error viven con los gestos (UI-10 / UI-11). [DECISIÓN]


2. Mesa de datos (UI-01) + mobile (UI-02) · H-DATA

GestoResultado
Primera cargaLoading: métricas + 4 tiles card; aria-busy
200 con 4Métricas (2 / 1 / 1) + 4 filas. Signatura, título, sala, estado
200 con 0UI-10 empty
Error 5xx / redUI-11 + Reintentar (repite GET ejemplares)
Tap fila/lomo/:code
Tap Mesa (ghost)/login o /mesa si hay token
Hover fila desktopRing 2px mark. Sin lift
Foco comando / ⌘K / /Overlay UI-03 o /buscar
Submit comando/buscar?q=

AC: título, sala y estado visibles sin hover. 3 métricas + 4 filas en el recorte (AP-21). Cero botones Prestar / Inscribirse / Play.

El aviso «Lomo ≠ catálogo municipal» es texto, no un CTA de OPAC. [DECISIÓN]

390: mismas 4 filas apiladas. No carousel. Comando visible. [DECISIÓN]


3. Comando (UI-03) · S-CMD

GestoResultado
/buscar sin qCampo + prompt de comando; no lista los 4 (eso es el hoy)
Overlay desde /Mismos resultados; Esc cierra; Enter abre ficha
Submit / debounce qGET /api/ejemplares?q= · pending
200 con hitsFilas thumb + título + sala + estado
200 []UI-10 «Sin lomos.»
ErrorUI-11
Tap resultado/lomo/:code
Query «manzanares»Incluye El río Manzanares
Query LM-2501Incluye El río Manzanares

AC: no facetas de distrito. No H-SEARCH como home. No archivo de programas.


4. Lomo (UI-04) + mobile (UI-05)

GestoResultado
Primera cargaLoading: skeleton de ficha
200Cubierta, título, sala, estado, copy × oposiciones, CTA
404«Ese lomo no está.»
Tap Pedir mesa/pedir/:code (F-LEAD; no POST aquí)
CTA si no DISPONIBLEDisabled + «No se puede pedir esa mesa.»
Tap ← Hoy/

AC: una pantalla. Cero préstamo. Cero «Inscribirse». Cero «Pedir repetición».


5. Pedir (UI-06) · F-LEAD

GestoResultado
CargaForm + ejemplar en solo lectura + disclaimer
Nombre vacío + submit«Escribe un nombre.» · no POST
Submit válidoPOST · pending en botón
201/ok/:code con state
409seal + «No se puede pedir esa mesa.»
404«Ese lomo no está.»
Error redseal + mensaje; no navega

AC: un campo de nombre. Cero email. Cero DNI. Cero carnet.


6. Acuse (UI-07)

GestoResultado
Con códigoSource Serif LM-XXXX + bloque card + 4 oposiciones
Sin código«No hay ese código» + Volver al hoy
CTA/ hoy

No GET de lista de pedidos. [DECISIÓN] doc 11.


7. Login (UI-08)

GestoResultado
SubmitPOST login · pending
200/mesa o next seguro (/mesa only)
401«Email o contraseña no valen.»
Ya hay tokenRedirect mesa

next open-redirect: allowlist /mesa únicamente. [DECISIÓN]


8. Mesa BIBLIO (UI-09)

GestoResultado
GET mesa loadingSkeletons de fila + pedidos
200 con ejemplaresLista. DISPONIBLE primero
200 pedidosNombre · código · hora
200 pedidos []UI-10 «No hay pedidos.»
ErrorUI-11
Tap filav1: no hay ficha JWT; el gesto no navega salvo Could. [DECISIÓN] lectura
Salirlogout → /

Desktop: lista + pedidos. 390: apilado. [DECISIÓN]
Cero Aceptar / Rechazar (eso fue RAMAL). Cero columnas. Cero En antena.


9. Loading / empty / error (UI-10…11)

Skeletons pulse. Empty usa titular display, no un illustration pack. Error no usa copy de 112 ni de radio.

17-prototype-map.md

17 — Mapa de prototipo — LOMO

Paper file: 01M0VVNT406AK640AK1ECY2DBR
https://app.paper.design/file/01M0VVNT406AK640AK1ECY2DBR

Este mapa es el contrato Paper ↔ docs ↔ app. No se arranca Paper desde esta suite. El archivo ya existe.

UX process → docs

PaperDoc
UX-00 CoverREADME + 01
UX-01 Stakeholders02 §3
UX-02 Personas02 §4
UX-03 JTBD02 §5
UX-04 Stories02 §6
UX-05 Journey02 §7 · 04
UX-06 Blueprint02 §8 · 06
UX-07 Site map03
UX-08 Flujos04
UX-09 Datos+permisos05 · 11
UX-10 Métricas12
UX-11 Research02

UI producto → app

PaperRutaComponente
UI-00 Tokenstailwind.config.js + styles.scss
UI-01 Mesa datos/HoyPage
UI-02 Mesa mobile/ 390HoyPage
UI-03 Comando/buscarBuscarPage
UI-04 Lomo/lomo/:codeLomoPage
UI-05 Lomo mobile/lomo/:code 390LomoPage
UI-06 Pedir/pedir/:codePedirPage
UI-07 Success/ok/:codeOkPage
UI-08 Login/loginLoginPage
UI-09 Mesa BIBLIO/mesaMesaPage
UI-10 Emptymismas rutasempty branches
UI-11 Error+Loadingmismas rutaserror / skeletons

Guión de prototipo

  1. Cover → Hoy (métricas + 4 filas, lead El río Manzanares).
  2. Comando «manzanares».
  3. Ficha El río Manzanares sala 2 Disponible.
  4. Pedir mesa · nombre.
  5. Acuse LM-2501 · reserva ≠ préstamo.
  6. Login Inés.
  7. Mesa BIBLIO · lista + pedidos.
  8. Empty / error / loading.
  9. Hoy 390 + lomo 390.

No hay artboard de préstamo, club, Absys, galería de radio, kanban ni nodos.

18-completeness-audit.md

18 — Auditoría de cierre — LOMO

1. Alcance L2

PiezaEstado
Docs 01–20 + README + executiveOK (este set)
00-day-brief / 00-paper-referenceIntactos (no tocados)
Paper 12 UX + 12 UIInventario en 00 (no se arranca Paper aquí)
App path /Users/cristian/orca/lomo-appOK · rewrite en paralelo
Neon dawn-butterfly-79519092OK
API :3025 · health lomo-apiContrato (leftover onda-api prohibido)
Código ejemplares + pedidos + JWT BIBLIOContrato paralelo
Cierre sin deuda del L2OK · roadmap solo L+1 / L4

2. Terna y diversidad

CheckEvidencia
R-CL · S-CMD · H-DATA · F-LEADBrief + 01 + 07
0 códigos = ONDAONDA era R-HY · S-RAIL · H-GALLERY · F-SEARCH
≥2 vs RAMAL / TEMPLECmd/Data/Lead vs split/full/onb y kiosk/book
S-CMD, H-DATA, F-LEAD no en últimos 5Brief
Ventana 4: R-CLR-HY (TEMPLE) + R-CL (RAMAL) + R-HY (ONDA) + R-CL
Ground G-STONE #E4E0D600 + 07 + Tailwind
No leftover ONDA0 programas / CONDUCTOR / radio en el contrato

3. Copy irrenunciable

Presente en hoy, ficha, pedir, acuse, pie de mesa. Ver doc 09.

  • Reserva de sala ≠ préstamo a casa
  • Lomo ≠ catálogo municipal
  • Pedir ≠ apuntarse al club
  • LOMO no es la Biblioteca Regional

4. Anti-patrones vigilados

APMitigación
AP-12 hero 2-colH-DATA métricas + tabla
AP-21 card huecaMétricas + signatura/sala/estado
Leftover como estadoContrato LOMO; no se anota ONDA como producto vivo
Entrevistas inventadas02 etiquetado
programas/CONDUCTOR/radio0 en dominio; 05 + 20
nodes/alta/VOCAL0 en dominio
H-SEARCH como homeComando en /buscar; home es datos

5. Seed

4 ejemplares · 2 DISPONIBLE · 1 EN_SALA · 1 PEDIDO · Inés BIBLIO · Rafa sin cuenta · Nora fuera.

codetitlestatussala
LM-2501El río ManzanaresDISPONIBLE2
LM-2502Planos de UseraEN_SALA1
LM-2503Crónica del mercadilloPEDIDO3
LM-2504Voces del barrioDISPONIBLE2

6. Huecos conscientes (fuera)

Préstamo, login Nora, email transaccional, instrumentación analytics, fondo Absys, confirmada_at.

7. Veredicto

Slice L2 cerrado a nivel de case. El código se escribe en paralelo con el mismo contrato. Si el repo diverge (leftover ONDA), gana docs 05 + 20 tras alinear ambos. Paper no se abre en esta entrega de docs.

19-backlog-completo.md

19 — Backlog — LOMO

1. Done (Must L2)

IDItem
D01Mesa de datos 4 filas H-DATA
D02Comando S-CMD
D03Ficha /lomo/:code
D04Form /pedir/:code · F-LEAD
D05Acuse /ok/:code · POST pedir
D06Login JWT BIBLIO
D07Mesa lista + pedidos, no kanban
D08Empty / error / loading
D09Mesa datos 390
D10Seed 4 ejemplares
D11Health lomo-api :3025
D12Copy ×4
D13Docs 01–20 + README + executive

2. Could L+1 (no bloquea cierre)

IDItem
C01confirmada_at / marcar confirmada
C02PATCH EN_SALA al sentarse
C03Filtro mesa por estado
C04Rate limit POST pedir
C05PWA
C06Trigram search

3. Explícito L4 / otro brief

IDItem
E01Login Nora / cuadrante de conservación
E02Préstamo a domicilio / carnet
E03Fondo Absys / OPAC
E04Multi-sede
E05Chat / kanban
E06Club de lectura

4. No-backlog (rechazado)

Hero 2-col. Signup lector. leftover ONDA (programas, CONDUCTOR, onda-api) como feature. leftover RAMAL (nodes, altas, VOCAL) como feature. La Regional. Préstamo a casa. «Apúntate al club». Search-first como home.

5. Cómo usar este archivo

Done = el día. Could = L+1. E = otro cron. Nada de esta lista es deuda del L2.

20-implementation.md

20 — Implementación — LOMO

1. Resumen ejecutivo técnico

CampoValor
App path/Users/cristian/orca/lomo-app
APINestJS · puerto 3025 · prefijo /api
WebAngular standalone · puerto 4200
Packageapps independientes (pnpm --filter)
DBNeon PostgreSQL · project dawn-butterfly-79519092
Tablasusers, ejemplares, pedidos, sessions
AuthJWT Bearer · role BIBLIO (/mesa); datos, comando y POST pedir públicos
DominioUser, Ejemplar, Pedido
Fecha2026-08-25
Healthlomo-api (leftover onda-api → sustituir)

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.

El código se escribe en paralelo a esta suite. Contrato: ejemplares + pedidos + JWT BIBLIO. Puerto 3025. Health lomo-api.

[COMPROBADO] Path, puerto, Neon y Paper salen del encargo.
No se arranca la app ni Paper desde esta entrega de docs.

El repo puede contener leftover ONDA (programas, repeticiones, GalleryPage, onda-api, puerto 3023) al inicio del rewrite. Eso no es LOMO. El rewrite borra ese dominio.

2. Cómo arrancar

cd /Users/cristian/orca/lomo-app
# instalar dependencias por app (pnpm --filter; no npm workspaces)

# apps/api/.env
#   DATABASE_URL=postgresql://neondb_owner:npg_GDsv4KLefP5d@ep-empty-star-a69kwo3y-pooler.us-west-2.aws.neon.tech/neondb?sslmode=require
#   JWT_SECRET=
#   PORT=3025

# schema + seed
# api → http://localhost:3025
# web → http://localhost:4200

Credenciales

RolEmailPassword
BIBLIOines@lomo.barriopassword123

Hash seed: $2b$12$OEKcNck63neDJ.CrVP0lNOaXp2NG2zd2aMiGmnYY221h4ZAZ58x1m

Lector / restauradora: sin cuenta. Rafa pide en /pedir/:code. Nora no entra.

3. Schema

Al boot o vía migrate:

  1. CREATE TABLE de users, sessions, ejemplares, pedidos (doc 05).
  2. Índices: ejemplares(code), ejemplares(status), users(email), pedidos(ejemplar_id).
  3. Seed idempotente:
    • upsert BIBLIO ines@lomo.barrio (hash bcrypt canónico)
    • upsert ejemplares por code (LM-2501LM-2504) restaurando status de catálogo
    • upsert pedido seed de LM-2503 · Carmen Vidal
  4. Dominio: solo ejemplares + pedidos + users. [DECISIÓN]

No borrar pedidos nuevos de Rafa en cada boot, salvo el re-seed explícito de demo. Upsert de ejemplares por code restaura status de catálogo para que el guión sea repetible. [DECISIÓN]

No generar códigos en el POST: el acuse reusa LM-2501.

Tablas (recordatorio)

TablaContenido
usersBIBLIO Inés
ejemplares4 lomos
pedidosReservas de mesa (1 seed + las del POST)
sessionssid + expires_at

DROP leftover programas, repeticiones, nodes, altas si existen en el mismo Neon. [DECISIÓN] El project dawn-butterfly-79519092 es de LOMO.

4. Módulos API a implementar

Auth

  • POST /api/auth/login
  • Valida email/password; compara bcrypt; emite JWT con sub, email, role.
  • Guard JWT + role === BIBLIO protege /mesa.
  • Públicos: health, ejemplares, ejemplares?q=, ejemplares/:code, POST pedir, login.

Oficio

MétodoRutaAuthNotas
GET/api/healthNo{ ok: true, service: "lomo-api" }
GET/api/ejemplaresNosort_order asc + sala + status + cover
GET/api/ejemplares?q=NoILIKE title/code/lead
GET/api/ejemplares/:codeNoficha
POST/api/ejemplares/:code/pedirNoINSERT pedido; status → PEDIDO; 409 si no DISPONIBLE; 404 si no hay ejemplar
GET/api/mesaJWT4 ejemplares + pedidoCount + lista pedidos

Health (referencia de implementación)

HealthService.name = 'lomo-api'
main.ts PORT default 3025

Si el rewrite hereda onda-api o puerto 3023, cambiar. El puerto de LOMO es 3025.

Pedir (referencia)

ejemplar = ejemplares.byCode(code) || 404
if status !== DISPONIBLE → 409
if !nombre.trim() → 400
INSERT pedidos (ejemplar_id, nombre)
UPDATE ejemplares SET status = PEDIDO
return { code, title, sala, status: PEDIDO, pedida: true }

POST no cobra, no abre préstamo, no crea user, no inscribe al club, no mueve kanban.

JSON camelCase: sortOrder, createdAt, pedidoCount.
SQL: sort_order, created_at.

Módulos de dominio: ejemplares/, mesa/, auth/, health.
No programas/, hoy/ (JWT ONDA), repeticiones/, nodes/, altas/.

5. Frontend a implementar

PáginaRutaResponsabilidad
HoyPage/H-DATA · GET ejemplares · métricas + 4 filas
BuscarPage/buscarS-CMD · GET ?q=
LomoPage/lomo/:codeFicha · CTA → pedir
PedirPage/pedir/:codeF-LEAD · POST pedir
OkPage/ok/:codeAcuse · state de navegación
LoginPage/loginJWT BIBLIO
MesaPage/mesaLista + pedidos · GET mesa

Estados: loading / ready / empty / error en hoy, buscar y mesa.
Token key: lomo.accessToken.
Proxy: /api:3025.

Tokens Tailwind: ground / ink / card / mark / mute / seal. Fuentes Source Serif 4 + Outfit.

Chrome comando: campo «buscar lomo o signatura» → /buscar. Wordmark → /. Mesa → /mesa.

Renombrar leftover GalleryPage / ProgramaPage / HoyPage JWT ONDA / SearchPage → las siete páginas de arriba. [DECISIÓN]
Nota: HoyPage en LOMO es la home pública H-DATA, no la mesa JWT de ONDA.

6. Seed visible en UI

CódigoTítuloStatusSalaCover
LM-2501El río ManzanaresDISPONIBLE2/assets/lomos/estante.jpg
LM-2502Planos de UseraEN_SALA1/assets/lomos/mesa.jpg
LM-2503Crónica del mercadilloPEDIDO3/assets/lomos/silla.jpg
LM-2504Voces del barrioDISPONIBLE2/assets/lomos/estante.jpg

Playwright: esperar El río Manzanares, El lomo, no el prestado., ines@lomo.barrio.
No esperar Patio 17, nerea@onda.radio, ON-2301, En antena, Azotea Palomeras, RM-2201.

7. Smoke

apps/api/scripts/test-api.mjs · pnpm test:api contra :3025.

Cubre health lomo-api, 4 ejemplares, q=manzanares, POST pedir, 409 EN_SALA, 404 code, 401 mesa, login BIBLIO, lista 4, pedidos con nombre.

8. DoD técnico

Ver doc 14. Este archivo no lista leftover de otro día como estado del producto. El contrato vivo es LOMO. ONDA es N−1, no el dominio de este repo.