On this page 00 — Day brief · 2026-08-22 · RAMAL 0%

00-day-brief.md

00 — Day brief · 2026-08-22 · RAMAL

Decisiones ALS-2 BRIEF

CampoValor
Fecha2026-08-22
NombreRAMAL
ComplejidadNivel 3 (no L2 consecutivo tras TEMPLE)
SectorTelecomunicaciones / comunidad vecinal (Vallecas)
TipoPortada de red de bloque + checklist de alta + mesa VOCAL JWT (split)
PlataformaWeb responsive (portada pública · mesa split desktop)
RegistroR-CL — institucional de asociación, no NOC cian (ventana 4 pedía clásico)
ShellS-SPLIT — lista de altas | ficha; la portada pública no lleva top-nav de marketing
HomeH-FULL — portada a sangre; lead + 3 nodos en el recorte 16/10
FlujoF-ONB — checklist de alta (vivienda, planta, azotea); no wizard chrome
Por qué no TEMPLENo S-MIN / H-KIOSK / F-BOOK; no ensayo
Por qué no PREGÓNNo chat ni kanban; alta ≠ parte municipal
Por qué no operadorRAMAL no es Movistar ni un speedtest

Terna unicidad

R-CL · S-SPLIT · H-FULL · F-ONB

0 códigos iguales a N−1 (TEMPLE: R-HY · S-MIN · H-KIOSK · F-BOOK).
≥2 distintos vs N−2 (PREGÓN) y N−3 (MATIZ).
Ventana 4: R-HY + R-IN + R-HY + R-IN → hoy R-CL.

S-SPLIT, H-FULL y F-ONB no están en los últimos 5.

Ground (CRON §5.6)

  • Familia: G-ICE
  • Hex: #E7F0F5
  • Por qué: escarcha en teja a las 8, no negro de flight case (TEMPLE), no fotocopia (PREGÓN), no yeso (MATIZ), no hormigón (ZAGUÁN), no blanco de kiosco (MARGEN).

Mood visual (Paper)

  • Candidatos: cyan SaaS / NOC (primer instinto telecom), ice, mineral, tidal, alpine
  • Elegido: ice — no el primer instinto tablero de operador. Una red de bloque se ve desde la azotea un martes con escarcha, no desde un NOC.
  • Paleta:
    • #E7F0F5 hielo (ground)
    • #1A2A32 mástil mojado
    • #FFFFFF ficha
    • #3D7A96 cobre oxidado del mástil
    • #5C6B73 galvanizado
    • #B42318 precinto (poco)
  • Tipo: Newsreader display · Figtree UI

Alcance L3 must

  1. Home H-FULL: portada + 3 nodos (lead + 3 datos)
  2. Checklist de alta (4 ítems, una pantalla, no wizard)
  3. Acuse con código (alta ≠ cobertura)
  4. Login JWT VOCAL
  5. Mesa split: lista de altas | ficha + aceptar/rechazar
  6. Empty / error / loading / mobile portada
  7. ≥12 UX + ≥10 UI

Microcopy duro

  • Alta ≠ cobertura del operador. RAMAL no sustituye a Movistar.
  • Nodo ≠ router de casa. El nodo está en la azotea del bloque.
  • RAMAL no instala hoy. Aceptar no sube a la azotea.
  • Checklist ≠ técnico. Completar el alta no programa una visita.

Supuestos

  • S1: Una asociación atiende 4–8 altas/semana; la demo tiene 3 nodos y 5 altas (3 PENDIENTE, 2 ACEPTADA).
  • S2: La vecina pide alta desde el salón; no crea cuenta.
  • S3: Un VOCAL (Pablo) acepta o rechaza; el técnico no entra en el slice L3 de código (sí en docs).
  • S4: Sin pago, sin radio planning, sin mapa GIS. [DECISIÓN]

Hipótesis

IDSeñal
H1Una portada de 3 nodos reduce “esto es un operador”
H2El checklist deja claro que no hay técnico mañana
H3Split lista|ficha basta; no hace falta kanban

00-paper-reference.md

Paper reference · RAMAL

URLhttps://app.paper.design/file/01M0M4FJGXPG315PDNT7PCAY3J
File ID01M0M4FJGXPG315PDNT7PCAY3J

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 Portada · UI-02 Portada mobile · UI-03 Alta · UI-04 Alta mobile · UI-05 Success · UI-06 Login · UI-07 Mesa split · UI-08 Ficha · UI-09 Empty · UI-10 Error · UI-11 Loading

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

IDNombreContenido
UX-00CoverRAMAL · «La red del bloque, no el operador.» · R-CL · S-SPLIT · H-FULL · F-ONB · L3 telecom comunidad · ice · 2026-08-22
UX-01StakeholdersVECINA (Inés), VOCAL (Pablo), técnico fuera del slice, operador comercial fuera
UX-02PersonasInés Soto 52 · Pablo Ruiz 38 · Nerea Gil 29 (técnica, docs)
UX-03JTBDJob «pedir nodo en mi bloque sin llamar a un comercial»
UX-04StoriesMust: portada, checklist, acuse, login, split, aceptar
UX-05JourneyLee 3 nodos → checklist → código → (Pablo) acepta
UX-06BlueprintFrontstage portada · backstage junta · Nest/Neon/JWT
UX-07Site map/ portada · /alta · /ok/:code · /login · /mesa
UX-08FlujosF-ONB público; split VOCAL
UX-09DatosUser VOCAL · Node · Alta; JWT en mesa
UX-10MétricasNorth star % altas que Pablo mueve en 7 días
UX-11ResearchComprobado / supuesto / hipótesis / decisión

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

IDNombreFlujo
UI-00TokensDS ice: hielo, mástil, ficha, cobre, galvanizado
UI-01PortadaH-FULL 1440: foto a sangre + 3 nodos. Lead + 3 datos
UI-02Portada mobile390
UI-03AltaChecklist 4 ítems
UI-04Alta mobile390
UI-05SuccessCódigo. Alta ≠ cobertura
UI-06LoginJWT VOCAL
UI-07Mesa splitLista | ficha
UI-08FichaAceptar / rechazar
UI-09EmptySin altas
UI-10ErrorRed
UI-11LoadingSkeletons

Mapeo Paper → Angular

ArtboardRutaComponente
UI-01 Portada/HomePage
UI-03 Alta/altaAltaPage
UI-05 Success/ok/:codeOkPage
UI-06 Login/loginLoginPage
UI-07 Mesa/mesaMesaPage
UI-08 Ficha/mesa/:codeAltaFichaPage

01-project-definition.md

01 — Definición de proyecto — RAMAL

1. Identidad

CampoValor
NombreRAMAL
SignificadoRamal es el tramo de red que baja del mástil al bloque, no el operador comercial. Ni la cobertura ni el router de casa. Se entra a la portada; se sale con un alta.
Eslogan«La red del bloque, no el operador.»
Una frasePortada de nodos + checklist de alta + mesa VOCAL JWT (split lista | ficha).
SectorTelecomunicaciones / comunidad vecinal (Vallecas)
TipoWeb L3 — portada H-FULL + checklist F-ONB + mesa split — Nivel 3, herramienta de altas usable
PlataformaWeb responsive (portada pública · mesa split desktop)
Mercado demoEspaña · Madrid · Vallecas · Palomeras (una asociación, no una operadora)
Idiomaes-ES
Fecha caso2026-08-22
TernaR-CL · S-SPLIT · H-FULL · F-ONB

[COMPROBADO] Nombre, terna, nivel, sector, ground y paleta salen de docs/00-day-brief.md.
[SUPUESTO de catálogo] Vallecas / Palomeras / Sierra Gádor es atrezzo de mercado, no el inventario de una red real.

2. Problema

Principal (hipótesis de diseño)

[HIPÓTESIS] La vecina de un bloque de Palomeras no necesita un comercial de fibra: necesita pedir nodo en su bloque sin llamar a un operador. Hoy eso vive en un grupo, una junta y una expectativa de cobertura. El alta se lee como contrato de Movistar; el nodo se lee como router de casa; el producto se lee como speedtest. [SUPUESTO] Una asociación atiende 4–8 altas/semana; no opera un NOC ni un chat municipal.

Secundarios

ProblemaQuién lo sufreEfecto
«¿Hay fibra en el 12?» por WhatsAppVECINA (Inés)Espera, llama a un comercial, pide cobertura
Alta se lee como coberturaVECINA + juntaEsperan instalación, velocidad, operador
Nodo se lee como router de casaVECINA«¿Me cambian el Wi-Fi?»
La mesa se lee como kanbanVOCAL (Pablo)Esperan Recibido / En curso / Cerrado
El checklist se lee como visita técnicaVECINA + NereaEsperan que alguien suba a la azotea mañana
La dueña pide speedtest y TPV el día 1AsociaciónEl L3 se vuelve operador

Supuestos (no investigación primaria propia)

  • S1: Una asociación atiende 4–8 altas/semana; la demo tiene 3 nodos y 5 altas (3 PENDIENTE, 2 ACEPTADA). [SUPUESTO]
  • S2: La vecina pide alta desde el salón; no crea cuenta. [SUPUESTO]
  • S3: Un VOCAL (Pablo) acepta o rechaza; el técnico no entra en el slice L3 de código (sí en docs). [SUPUESTO]
  • S4: Sin pago, sin radio planning, sin mapa GIS. [DECISIÓN]

Hipótesis de producto

IDHipótesisSeñal de validación (futura)
H1Una portada de 3 nodos reduce “esto es un operador”% de visitas a / que terminan en POST con código
H2El checklist deja claro que no hay técnico mañana↓ «pensé que veníais a instalar»; copy visible en acuse
H3Split lista|ficha basta; no hace falta kanban0 columnas Recibido / En curso; Pablo decide sin chat

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

3. Propuesta de valor

ParaValor
VECINA (Inés Soto)Ve 3 nodos, marca 4 ítems, escribe planta y nombre, se lleva un código. Nadie le pide cuenta ni cobertura.
VOCAL (Pablo Ruiz)Ve la lista, abre la ficha, acepta o rechaza. No mueve columnas. No instala.
TÉCNICA (Nerea Gil)Fuera del slice de código. El oficio de azotea no entra en v1.
Asociación RAMALUna superficie mínima: nodos + altas + mesa, sin operador y sin chat.

No es RAMAL

ExcluidoPor qué
Cobertura / speedtest / MbpsAlta ≠ cobertura del operador
Router de casa / Wi-Fi / ONTNodo ≠ router de casa
Instalación el día 1RAMAL no instala hoy
Visita técnica programadaChecklist ≠ técnico
Movistar / operadora comercialRAMAL no es el operador
Chat de incidencias / kanbanEso fue PREGÓN (H-CHAT / F-KAN)
Quiosco de horas / bloques de 2 hEso fue TEMPLE (H-KIOSK / F-BOOK)
Wizard de fincas + comparadorEso fue ZAGUÁN (H-WIZARD / F-CMP)
Hero 2-col + 3 cards de «tu fibra»Anti-patrón AP-12; contradice H-FULL
Pago / pasarela / cuotaS4
Radio planning / mapa GISS4
App de 40 nodos y NOCL3 compacto, no OSS/BSS

4. Objetivos

Negocio / caso de estudio

  • Demostrar vertical slice L3 telecomunicaciones / comunidad vecinal con Node + Alta + JWT VOCAL.
  • Portfolio coherente: Paper (12 UX + 12 UI) + docs + app runnable.
  • Terna R-CL · S-SPLIT · H-FULL · F-ONB frente a TEMPLE (kiosk/horas), PREGÓN (chat/kanban) y MATIZ (bento/editor).

Usuario

RolObjetivo medible en demo
VECINAVer 3 nodos, completar checklist y ver el código en < 90 s, sin cuenta
VOCALEntrar a mesa, ver 5 altas, abrir Inés Soto RM-2201 y aceptar en < 45 s
TÉCNICANo encuentra login; el oficio de azotea no está en la app

No objetivos v1 (explícitos)

  • Cobertura, speedtest, Mbps, SLA de operador
  • Router de casa, Wi-Fi, ONT, radio planning
  • Instalación, visita técnica, cuadrante de Nerea
  • Chat, kanban, columnas Recibido / En curso
  • Cuenta de vecina, seguimiento con login, push
  • Pago, cuota, pasarela
  • Mapa GIS / cadastro
  • Multi-asociación, multi-vocal
  • App nativa ni notificaciones push
  • Rol TÉCNICA autenticado

5. Roles y permisos (resumen)

AcciónAnónimo (vecina)VOCALTÉCNICA
Ver portada / nodosNo hay cuenta
POST altaSí (no es el job)No
Ver acuse /ok/:code (código en URL)
Ver mesaNo (401 / redirect)No
GET /api/mesa401
Aceptar / rechazarNoNo
Ver lista de vecinas en portadaNoSí (mesa)
Vender coberturaNo (no existe)NoFuera
Subir a la azoteaCopy: fueraCopy: fueraOficio fuera de app

[DECISIÓN] Un solo rol autenticado: VOCAL. La vecina no es un usuario del sistema: es autora/autor de un alta. La técnica 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 altas que Pablo mueve en 7 díasOficio de junta, no pageviews
Activación vecina1.er POST con codeH1
Integridad de expectativaReclamaciones «¿cuándo instaláis?» / «¿es Movistar?» / «¿es un speedtest?»H2 + microcopy
Integridad de authGET /api/mesa sin token → 401; POST /api/altas sin token → 201Portada pública + mesa cerrada
SaludError rate API nodes / altas4xx/5xx

7. Alcance funcional v1 (L3)

MóduloIncluido
PortadaGET /api/nodes · 3 nodos (lead + 3 datos) a sangre
Checklist/alta · 4 ítems, una pantalla, no wizard
Acuse públicoPOST /api/altasRM-XXXX PENDIENTE
AuthPOST /api/auth/login → JWT VOCAL
MesaGET /api/mesa JWT · lista | ficha
DecisiónPATCH /api/mesa/:code · ACEPTADA / RECHAZADA
Estados UIEmpty (0 nodos / 0 altas), loading skeleton, error de red, 409 ya decidida
MobilePortada ~390px · checklist · targets ≥44px
Seed1 VOCAL, 3 nodos, 5 altas (3 PENDIENTE, 2 ACEPTADA)
SaludGET /api/health{ ok: true, service: "ramal-api" }

8. Criterios de aceptación de producto

  1. / es la portada de 3 nodos a sangre. No pide cuenta. No es un hero 2-col + 3 cards.
  2. Una vecina puede ver 3 nodos en el recorte 16/10 (lead + 3 datos), tocar Azotea Palomeras, completar el checklist y obtener un código RM-XXXX.
  3. /ok/:code dice explícitamente que alta no es cobertura del operador y que RAMAL no instala hoy.
  4. La UI dice explícitamente que nodo no es router de casa y que checklist no es técnico.
  5. Sin token, GET /api/mesa responde 401. POST /api/altas y GET /api/nodes son públicos.
  6. Un POST de alta crea status=PENDIENTE. Completar no programa una visita.
  7. Un VOCAL inicia sesión y ve 5 altas (3 PENDIENTE, 2 ACEPTADA) en el seed.
  8. El tablero es un split lista | ficha. No es un kanban de 3 columnas.
  9. Fotos reales en los 3 nodos seed (assets nodos/*).
  10. Checklist visible: vivienda · planta · azotea · no instala.
  11. Chrome S-SPLIT en mesa; portada pública sin top-nav de marketing.
  12. Rutas solo las del doc 03. No existen /cobertura, /speedtest, /chat, /turno, /kanban.
  13. GET /api/health devuelve service: "ramal-api".
  14. La portada no muestra nombres de otras vecinas; la mesa sí.

9. Stack y artefactos

CapaDetalle
FrontendAngular + Tailwind · puerto 4200
BackendNestJS · puerto 3022
DBNeon PostgreSQL · project gentle-bonus-75761237
AuthJWT (VOCAL)
DiseñoPaper 01M0M4FJGXPG315PDNT7PCAY3J
Repo app/Users/cristian/orca/ramal-app/ · GitHub Criscode2022/ramal-app

[COMPROBADO] Puerto, Neon, Paper file ID, repo y path de app constan en el encargo.
El código se escribe en paralelo a esta suite.

10. Riesgos y mitigaciones

RiesgoImpactoMitigación v1
Expectativa de cobertura«¿Cuándo me ponéis fibra?»Copy «Alta ≠ cobertura del operador»
Expectativa de routerWi-Fi, ONT, speedtestCopy «Nodo ≠ router de casa»
Expectativa de instalaciónTécnico mañanaCopy «RAMAL no instala hoy»
Expectativa de kanbanRecibido / En cursoSplit lista|ficha, no columnas de estado
Pago el día 1Alcance L4 disfrazadoS4: sin pasarela
Cuenta de vecinaRompe S2 / H3Sin signup; nombre libre en el alta
Home percibida como landing de operadoraPérdida de H-FULL / AP-123 nodos a sangre; no hero 2-col
Confundir con TEMPLE (kiosk + horas)Expectativa de bloques de 2 hPortada de nodos; no slots
Confundir con PREGÓN (chat + kanban)Expectativa de parteChecklist; no hilo
Confundir con MovistarExpectativa de coberturaMicrocopy ×4
Doble decisión de la misma altaDos estados409 si no está PENDIENTE
Enumeración de altas por códigoPII de vecina en públicoGET mesa JWT; /ok/:code no hidrata ficha ajena
0 nodosPortada vacía sin oficioEmpty «Aún no hay nodos.»
WhatsApp paraleloH1 no se mideNorth star etiquetada como modelo

11. Glosario

TérminoDefinición en RAMAL
RamalTramo de red de bloque; el producto, no un operador
NodoPunto físico (nodes); no es el router de casa
RouterEquipo de salón; fuera
AltaPOST 201 + código; no significa cobertura
AcuseCódigo (RM-XXXX); no es un contrato de operador
CoberturaPromesa comercial de Mbps; fuera. RAMAL no es el operador
PortadaSuperficie H-FULL pública
MesaSuperficie S-SPLIT del VOCAL (lista | ficha)
KanbanLenguaje de diseño de otros días; no hay columnas aquí
VOCALUsuario autenticado que opera la mesa
VECINAAutora/autor del alta; no tiene fila en users
TÉCNICAOficio de azotea; fuera del slice de código
Checklist4 ítems en una pantalla; no es un técnico
HieloGround #E7F0F5
InkMástil #1A2A32
CardFicha #FFFFFF
MarkCobre oxidado #3D7A96
MuteGalvanizado #5C6B73
SealPrecinto #B42318, poco
OperadorMovistar y similares; fuera

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

IDDecisiónAlternativa descartada
D1Home = portada a sangre + 3 nodos (H-FULL)Hero 2-col + 3 cards (AP-12) · kiosk (TEMPLE) · chat (PREGÓN)
D2Shell split en mesa (S-SPLIT)Rail de estados · bottom tabs · kiosk mínimo · wizard chrome
D3Registro clásico institucional (R-CL)Cyan NOC (primer instinto; operador) · vehicle dashboard TEMPLE
D4Flujo F-ONB (checklist de alta)F-BOOK, F-KAN, F-EDIT, F-CMP, F-CHAT
D5Mood iceCyan SaaS / NOC (primer instinto), ink dashboard, highlighter
D6Un rol autenticado VOCALMulti-rol técnica / vecina / operador
D7Status PENDIENTE / ACEPTADA / RECHAZADAKanban de 3 columnas; «instalada»; «en cobertura»
D8Vecina sin cuentaSignup, seguimiento con login
D9Portada no lista nombres de otras vecinasTablero público enumerable
D10POST público + checklist 4 ítemsFormulario de DNI + pago + radio planning
D11Ground G-ICE #E7F0F5G-INK, G-FOG, G-CHAPEL, G-CONCRETE, G-GALLERY
D12Success de acuse en /ok/:codeToast único sin pantalla
D13Checklist una pantalla, no wizardPasos 1—2—3; chrome F-WIZ
D14Mesa = split, no kanbanRecibido / En curso / Cerrado
D15Portada pública + mesa JWT (no auth-first total)Todo cerrado (rompería S2) · todo abierto (rompería mesa)
D16Sin pagoPasarela en alta
D17409 si el alta ya no está PENDIENTEReabrir «por si acaso»

13. Relación con el día anterior

CaseTernaPor qué RAMAL no lo copia
TEMPLE (N−1)S-MIN / H-KIOSK / F-BOOKNo kiosk, no horas, no concierto, no turno
PREGÓN (N−2)S-SIDE / H-CHAT / F-KANNo admin pública, no chat, no kanban, no 112
MATIZ (N−3)S-BOTTOM / H-BENTO / F-EDITNo belleza, no PWA de oficio, no editor de gramos
ZAGUÁN (N−4)S-WIZ / H-WIZARD / F-CMPNo inmobiliaria, no wizard, no comparador

[COMPROBADO] El brief fija terna R-CL · S-SPLIT · H-FULL · F-ONB. 0 códigos iguales a N−1 (TEMPLE). ≥2 distintos vs N−2 (PREGÓN) y N−3 (MATIZ). Ventana 4 días: R-HY + R-IN + R-HY + R-CL. Ground G-ICE #E7F0F5, no G-INK.

02-ux-research-strategy.md

02 — Estrategia de investigación UX — RAMAL

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 del bloqueModelado de stakeholders + personas§3–4
Definir job de «pedir nodo en mi bloque» y de decidir altasJTBD + stories Must§5
Mapear fricción WhatsApp / operador / router → portadaJourney + service blueprint§6–7
Traducir a requisitos L3 portada + mesaMatriz hallazgo → requisito → feature§8

2. Fuentes y límites

Fuentes admisibles (secundarias / operativas)

  • Conocimiento general de redes comunitarias y asociaciones de vecinos (nodo en azotea, alta en junta, técnico que no entra el día 1, distinción red de bloque vs operador comercial).
  • Analogía operativa con superficie pública + panel JWT de la serie daily.
  • Restricciones ALS-2: no Movistar, no speedtest, no chat, no kanban, no kiosk de horas.

Límites éticos de verdad

ProhibidoPermitido
«El 62% de vecinas de Vallecas pide alta por WhatsApp» sin fuente«[SUPUESTO] Inés abre la portada desde el salón»
Citas de entrevistas ficticias como campo realQuotes de persona etiquetadas como constructo de diseño
Volumen medido «17 altas/semana en Palomeras» como KPI de mercadoAltas de seed de producto, etiquetadas como demo
Inventario oficial de una red comunitaria 2026Nodos de seed; Inés, Pablo y Nerea son personas de diseño
«Inés existe y nos dijo…»Inés Soto es persona de diseño

3. Stakeholders

StakeholderInfluenciaInterésNecesidad principal
VECINA (Inés)Alta en el altaMuy altaPedir nodo en su bloque sin cuenta ni comercial
VOCAL (Pablo)AltaMuy altaVer altas y decidir sin kanban
TÉCNICA (Nerea)Media formal, baja en producto día 1AltaOficio de azotea; fuera del slice de código
Asociación / juntaAlta formalAltaQue entren altas; que no se confunda con operador
Operador comercialCompetencia / analogía falsaFuera de alcance; no clonar cobertura
Chat / 112 / parteCanal paralelo / analogía falsaFuera; no clonar PREGÓN
Grupo de WhatsApp del bloqueCanal paraleloNo es el backoffice

Mapa de poder (resumen)

  • Decisor de adopción: la junta (quién pone la portada) y, en uso diario, Pablo (quien mira la mesa).
  • Usuario frecuente de pedir alta: VECINA (portada + checklist, sin cuenta).
  • Usuario frecuente de decisión: VOCAL (mesa).
  • Riesgo de rechazo: si el acuse parece un contrato de cobertura; si el nodo parece router; si hay que crear cuenta; si la mesa parece un kanban.

4. Personas

P1 — Inés Soto · VECINA

CampoDetalle
Edad / contexto52 años; vive en Palomeras; 4º B; no quiere «otra cuenta de operadora»
DigitalMóvil desde el salón; no llama a un comercial si puede evitarlo
GoalsVer si hay nodo en su bloque; enviar un alta; llevarse un código
PainsEl grupo dice «creo que hay fibra»; llama a Movistar; le venden cobertura
Quote de diseño«Solo quiero el nodo del bloque. No me vendas un operador.»

Escenario: Abre / en el salón, toca Azotea Palomeras, marca los 4 ítems, escribe Inés Soto / 4º B, lee «Alta ≠ cobertura» y el código RM-XXXX.

[COMPROBADO] Nombre, edad y gesto (vecina, 52) salen del encargo. Es constructo de diseño, no entrevistada. [DECISIÓN]

P2 — Pablo Ruiz · VOCAL

CampoDetalle
Edad / contexto38 años; vocal de la asociación; mesa de altas, no de instalación
DigitalFluido en desktop de mesa; la portada pública no es su herramienta
GoalsVer las altas, abrir la ficha, aceptar o rechazar
PainsWhatsApp a deshora; «¿cuándo instaláis?»; un kanban que no es su oficio; speedtest que no es su job
Quote de diseño«Quiero ver las altas y decidir. No un kanban.»
Email demopablo@ramal.vallecas / password123

Escenario: Entra a /login, abre /mesa, toca RM-2201 Inés Soto, lee la ficha, pulsa Aceptar. El copy dice que aceptar no sube a la azotea.

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

P3 — Nerea Gil · TÉCNICA (docs)

CampoDetalle
Edad / contexto29 años; técnica de azotea; oficio de mástil, no de mesa
DigitalNo tiene login en el slice L3
GoalsSubir cuando la junta lo pida (fuera de app)
PainsQue el checklist se lea como una visita suya
Quote de diseño«El alta no me programa. Yo no entro aquí.»

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

5. JTBD

Job principal (vecina)

Cuando quiero saber si hay nodo en mi bloque, quiero pedir alta sin llamar a un comercial, para dejar constancia en la junta sin que eso signifique cobertura ni técnico mañana.

Job principal (vocal)

Cuando hay altas nuevas, quiero ver lista y ficha y decidir, para mover el oficio de la asociación sin un kanban ni un NOC.

Jobs que RAMAL no hace

JobPor qué no
Contratar cobertura de operadorAlta ≠ cobertura
Medir MbpsNo es speedtest
Programar visita de NereaChecklist ≠ técnico
Chat con la junta≠ PREGÓN
Coger hora de ensayo≠ TEMPLE

6. Stories Must (v1)

IDComoQuieroPara
US-01VecinaVer 3 nodos (lead + 3 datos)Distinguir red de bloque de operador
US-02VecinaCompletar checklist en una pantallaPedir alta sin wizard ni cuenta
US-03VecinaVer un código RM-XXXXTener acuse, no cobertura
US-04VocalEntrar con JWTAbrir la mesa
US-05VocalVer lista | fichaDecidir sin kanban
US-06VocalAceptar o rechazarMover PENDIENTE

Could (fuera de L3): login de Nerea, mapa GIS, radio planning, pago, speedtest, historial de vecina.

7. Journey (resumen)

  1. Inés abre /. Ve foto a sangre y 3 nodos. [H1]
  2. Toca Azotea Palomeras → /alta?nodo=azotea.
  3. Marca vivienda, planta, azotea, no instala. Escribe nombre y 4º B.
  4. POST → /ok/RM-XXXX. Lee las cuatro oposiciones.
  5. Pablo entra a /login/mesa. Lista a la izquierda.
  6. Abre /mesa/RM-2201. Acepta. Copy: no sube a la azotea.

Backstage: Nest + Neon + JWT. Nerea no entra. El mástil no se mueve.

8. Matriz hallazgo → requisito → feature

Hallazgo (etiquetado)RequisitoFeature v1
[HIPÓTESIS H1] Portada de 3 nodos reduce “operador”3 nodos en fold, lead + 3 datos/ H-FULL
[HIPÓTESIS H2] Checklist evita técnico mañana4 ítems + copy irrenunciable/alta F-ONB
[HIPÓTESIS H3] Split bastaLista | ficha, no columnas/mesa S-SPLIT
[SUPUESTO S2] Sin cuenta de vecinaPOST públicoPOST /api/altas
[SUPUESTO S3] Un vocalUn rol JWTVOCAL
[DECISIÓN S4] Sin pago / GISNo pasarela, no mapaFuera de alcance

9. Preguntas que este case no responde

  • ¿Cuántas altas reales hay en Palomeras en 2026?
  • ¿Qué asociación opera un mástil en Sierra Gádor?
  • ¿Inés preferiría WhatsApp?
  • ¿Nerea necesita un cuadrante?

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

10. Cierre

Investigación = modelado etiquetado + restricciones ALS-2.
No hay entrevistas inventadas.
Las personas son constructos. Vallecas / Palomeras / Sierra Gádor son [SUPUESTO de catálogo].

03-information-architecture.md

03 — Arquitectura de información — RAMAL

1. Principios de IA

PrincipioAplicación
Portada firstLa home son 3 nodos a sangre. No hay landing de marca por delante
Público / mesaLa vecina pide alta sin cuenta; Pablo entra con JWT
Nodo → checklist → acuseProfundidad 2 desde la portada hasta el código
Mesa = splitLista a la izquierda, ficha a la derecha; no kanban
Lenguaje de dominioNodo, alta, acuse, mesa — no «cobertura», «lead», «expediente», «parte»
Alta no es coberturaEl acuse no tiene Mbps ni contrato
Nodo no es routerEl mástil está en la azotea, no en el salón
Chrome de oficioS-SPLIT en mesa; portada sin top-nav de marketing

2. Sitemap

/                                      Portada pública (3 nodos)                    H-FULL
/alta                                  Checklist 4 ítems                            F-ONB · público
/ok/:code                              Acuse post-alta                              público
/login                                 Login JWT VOCAL                              público
/mesa                                  Split lista | vacío de ficha                 auth
/mesa/:code                            Split lista | ficha aceptar/rechazar         auth
/**                                    → redirect /

Árbol por audiencia

AudienciaNodos relevantes
VECINAPortada → Alta → Ok. No ve mesa
VOCALLogin → Mesa · (puede leer la portada)
TÉCNICANinguno como rol de sistema

[COMPROBADO] Seis rutas del encargo L3. No se inventan /cobertura, /speedtest, /chat, /turno, /kanban, /tecnico.

El checklist es ruta (/alta): una pantalla, no wizard chrome. [DECISIÓN]
La ficha es ruta (/mesa/:code) porque S-SPLIT necesita URL compartible para Pablo. [DECISIÓN]

3. Navegación

Portada pública (H-FULL · sin top-nav de marketing)

ElementoDestinoNotas
Wordmark RAMAL/Sello sobre la foto; no es hero 2-col
Eslogan«La red del bloque, no el operador.»
Card nodo/alta?nodo=:slug3 tiles con lead + 3 datos
Link Vocal/login o /mesa si hay tokenSecundario, mute, no CTA de marca

[DECISIÓN] La portada no tiene tabs, no tiene hamburger, no tiene «Cobertura / Tarifas / Área cliente». Un link «Vocal» discreto basta para Pablo.

Chrome de mesa (S-SPLIT autenticado)

SlotDestinoVisibilidadNotas
Wordmark / Mesa/mesaAuthTitular de oficio
Lista/mesa/:codeAuthColumna izquierda
FichaAuthColumna derecha
Portada/AuthSecundario
Salirlimpia token → /AuthCabecera, no rail

[DECISIÓN] No hay rail S-SIDE (eso fue PREGÓN). No hay kiosk S-MIN (TEMPLE). No hay bottom tabs (MATIZ). La mesa es un split a pantalla.

Chrome de alta (F-ONB)

SlotDestinoNotas
← Nodos/Un paso atrás, no stepper 1—2—3
FormularioPOST /api/altasUna pantalla
No hay «Siguiente / Anterior»El checklist no es wizard

4. Etiquetas de IA (UI)

ConceptoLabel
HomePortada / Nodos (no «Inicio», no «Cobertura»)
AltaPedir alta / Pedir nodo en el bloque
SuccessAlta enviada / código
StaffMesa (no «Backoffice», no «Turno», no «Kanban»)
AuthVocal (no «Área cliente»)

5. Contenido de cada nodo de IA

RutaQué hayQué no hay
/Foto a sangre, 3 nodos, lead + 3 datosPrecios, Mbps, login de vecina
/altaNodo, nombre, vivienda, 4 checksDNI, pago, fecha de visita
/ok/:codeCódigo + 4 oposicionesContrato, cobertura, «ya está instalado»
/loginEmail / clave VOCALSignup de vecina
/mesaLista de 5 altasColumnas Recibido / En curso
/mesa/:codeFicha + Aceptar / RechazarCuadrante de Nerea

6. Error / empty / loading (sitio)

Viven en las mismas rutas, no en URLs extra.

EstadoSuperficie
LoadingSkeletons de 3 nodos / 5 filas / ficha
Empty portada«Aún no hay nodos.»
Empty mesa«No hay altas.»
Error red«La red no responde» + Reintentar
404 alta«Esa alta no está» en ficha
409«Esa alta ya no está pendiente»

7. Profundidad y búsqueda

Sin buscador en v1. 3 nodos y 5 altas caben en una lista. [DECISIÓN]
Pablo ordena por PENDIENTE primero, no filtra por nodo. Could L+1.

04-user-flows.md

04 — Flujos de usuario — RAMAL

1. Convenciones

TokenSignificado
F-ONBChecklist de alta, una pantalla
MesaSplit VOCAL, JWT
201Alta creada PENDIENTE
401Mesa sin token
409Alta ya decidida

2. F1 — Vecina pide alta (Must)

/  →  GET /api/nodes
   →  tap nodo  →  /alta?nodo=azotea
   →  marca 4 ítems + nombre + vivienda
   →  POST /api/altas
   →  201 { code: RM-XXXX, status: PENDIENTE }
   →  /ok/RM-XXXX

Entrada: portada, sin cuenta.
Salida: código visible + copy «alta ≠ cobertura».
Errores: red (reintentar), checklist incompleto (400, no POST), 0 nodos (empty).

AC

  • 4 checks obligatorios. El botón permanece disabled hasta completar.
  • No hay paso 2. No hay «Siguiente».
  • POST no cobra, no crea user, no llama a Nerea.

3. F2 — Vocal entra a mesa (Must)

/login  →  POST /api/auth/login
        →  JWT VOCAL
        →  /mesa
        →  GET /api/mesa
        →  tap RM-2201  →  /mesa/RM-2201
        →  GET /api/mesa/RM-2201

AC

  • Sin token, /mesa redirige a /login?next=/mesa.
  • GET /api/mesa sin Bearer → 401.
  • Lista a la izquierda, ficha a la derecha (desktop). Mobile: lista o ficha, no las dos a la vez si no cabe.

4. F3 — Vocal acepta / rechaza (Must)

ficha PENDIENTE
  →  PATCH /api/mesa/:code { status: ACEPTADA | RECHAZADA }
  →  200 ficha actualizada
  →  lista refleja el status

Si ya no está PENDIENTE → 409. Copy: «Aceptar no sube a la azotea.»

AC

  • Aceptar no cambia el nodo, no crea visita, no envía email.
  • Rechazar no es un parte municipal.
  • Los botones desaparecen cuando el status no es PENDIENTE.

5. F4 — Estados de sistema

EstadoTriggerUI
Loading portadaGET nodes en vuelo3 skeletons
Empty portada200 []«Aún no hay nodos.»
Error portada5xx / redReintentar
Loading mesaGET mesa en vuelo5 skeletons + ficha
Empty mesa200 []«No hay altas.»
Loading fichaGET :codeSkeleton de ficha
404 fichacódigo inexistente«Esa alta no está.»

6. Flujos que no existen

FlujoPor qué
Signup vecinaS2
PagoS4
SpeedtestAlta ≠ cobertura
Chat≠ PREGÓN
Coger hora≠ TEMPLE
Wizard 1—2—3F-ONB es una pantalla
Login NereaSlice L3

7. Tiempos de demo (modelo)

ActorRecorridoTecho
InésPortada → alta → código< 90 s
PabloLogin → mesa → aceptar RM-2201< 45 s

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

05-data-model.md

05 — Modelo de datos — RAMAL

1. Visión general

Dominio L3 de portada de nodos + mesa autenticada:

EntidadTabla SQLPropósito
UserusersIdentidad de oficio (rol VOCAL)
SessionsessionsSesión JWT / token persistido
NodenodesNodo de bloque (azotea / bloque / arqueta)
AltaaltasPetición de vecina (código RM-XXXX)

Base: PostgreSQL (Neon project gentle-bonus-75761237) · IDs: serial + texto estable (azotea, RM-2201).
[DECISIÓN] ensureSchema es válido si el contrato de tablas se cumple; el brief de stack no exige Prisma.

Contrato de producto: nodes + altas + JWT VOCAL. No rooms / bookings / bandos / reports.

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

Role

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

AltaStatus

ValorDescripción
PENDIENTEAlta enviada; no implica cobertura ni visita
ACEPTADAPablo aceptó; no implica instalación
RECHAZADAPablo rechazó; no es un parte

NodeKind

azotea | bloque | arqueta

3. Diagrama ER (texto)

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

sessions
  id, user_id → users
  expires_at, revoked_at
  created_at

nodes
  id
  slug (unique)
  name, kind, street, lead
  data_1, data_2, data_3
  cover
  sort_order
  created_at, updated_at
  1 ──< altas

altas
  id
  code (unique, RM-XXXX)
  node_id → nodes
  neighbor_name
  dwelling
  status (PENDIENTE|ACEPTADA|RECHAZADA)
  created_at, decided_at, updated_at

v1: un solo VOCAL opera sobre el conjunto global.
No hay FK de vecina-usuaria: la vecina no se loguea.
altas no apunta a users: el alta es anónima de cuenta.

4. Tablas

users

ColumnaTipoNotas
idBIGSERIAL PK
emailTEXT UNIQUEpablo@ramal.vallecas
password_hashTEXTbcrypt
nameTEXTPablo Ruiz
roleTEXTVOCAL
created_atTIMESTAMPTZ

sessions

ColumnaTipoNotas
idUUID PKsid del JWT
user_idBIGINT FK
expires_atTIMESTAMPTZ
revoked_atTIMESTAMPTZlogout
created_atTIMESTAMPTZ

nodes

ColumnaTipoNotas
idBIGSERIAL PK
slugTEXT UNIQUEazotea / bloque / arqueta
nameTEXTAzotea Palomeras
kindTEXTazotea | bloque | arqueta
streetTEXTPalomeras
leadTEXTUna frase. Nodo ≠ router.
data_1TEXTDato 1 del tile
data_2TEXTDato 2
data_3TEXTDato 3
coverTEXT/assets/nodos/azotea.jpg
sort_orderINTEGER1, 2, 3
created_at / updated_atTIMESTAMPTZ

altas

ColumnaTipoNotas
idBIGSERIAL PK
codeTEXT UNIQUERM-2201
node_idBIGINT FK
neighbor_nameTEXTInés Soto
dwellingTEXT4º B
statusTEXTPENDIENTE por defecto
created_atTIMESTAMPTZ
decided_atTIMESTAMPTZnull hasta PATCH
updated_atTIMESTAMPTZ

Índices: nodes(slug), altas(code), altas(status), altas(created_at DESC), users(email).

5. Seed

VOCAL

pablo@ramal.vallecas / password123 · Pablo Ruiz · VOCAL

Nodos

slugnamekind3 datos
azoteaAzotea PalomerasazoteaMástil · Palomeras · 18 viviendas al alcance
bloqueBloque Sierra GádorbloquePatio · Sierra Gádor · 24 viviendas
arquetaArqueta Pablo NerudaarquetaAcera · Pablo Neruda · 2 bloques

Altas

codeneighbornodedwellingstatus
RM-2201Inés Sotoazotea4º BPENDIENTE
RM-2202Luis Herrerobloque2º APENDIENTE
RM-2203Carmen Vidalarqueta1º CPENDIENTE
RM-2204Rosa Méndezazotea6º AACEPTADA
RM-2205Toni Casasbloque3º DACEPTADA

[SUPUESTO de catálogo] Nombres de vecinas secundarias, calles y conteos de viviendas son atrezzo.

6. JSON (camelCase)

API: neighborName, nodeSlug, nodeName, nodeKind, sortOrder, createdAt, decidedAt, data1, data2, data3.
SQL: neighbor_name, node_id, sort_order, created_at, decided_at, data_1.

7. Generador de código

SELECT max(code) numérico + 1, formateado RM- + 4 dígitos. Seed máximo 2205 → siguiente POST = RM-2206 (si no hay altas nuevas). Si se agota 9999, L+1 amplia a 5 dígitos; no es Must.

8. Qué no se modela

FueraMotivo
Tabla techniciansNerea no entra
Tabla paymentsS4
Tabla coverage / speedtestsAlta ≠ cobertura
GIS / lat-lngS4
rooms / bookingsDominio de otro día; no es el contrato

06-tech-stack.md

06 — Stack tecnológico — RAMAL

1. Visión

CapaTecnologíaNotas
FrontendAngular standalone + signalsPuerto 4200 · web responsive
EstilosTailwind CSSTokens ice (hielo, ink, card, mark, mute, seal)
BackendNestJS TypeScriptPuerto 3022, prefijo /api
PersistenciaPostgreSQL (ensureSchema)Tablas users, nodes, altas, sessions
DBNeon PostgreSQLproject gentle-bonus-75761237
AuthJWTRol VOCAL (mesa); portada y POST altas públicos
DiseñoPaper01M0M4FJGXPG315PDNT7PCAY3J
Repo/Users/cristian/orca/ramal-app/GitHub Criscode2022/ramal-app

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

El código se escribe en paralelo a esta suite. Este doc es el contrato.

2. Estructura

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

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

Scripts raíz esperados:

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

3. API pública vs JWT

MétodoRutaAuth
GET/api/healthPúblico · { ok: true, service: "ramal-api" }
GET/api/nodesPúblico
GET/api/nodes/:slugPúblico
POST/api/altasPúblico
POST/api/auth/loginPúblico
GET/api/mesaJWT VOCAL
GET/api/mesa/:codeJWT VOCAL
PATCH/api/mesa/:codeJWT VOCAL

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

[COMPROBADO] Superficie API del encargo. No se añaden Must extra (/cobertura, /speedtest, /reports).

4. Justificación

ElecciónRazón
Angular + NestAlineación con la serie daily y el handoff del estudio
Neon serverlessVolumen bajo (S1: 4–8 altas/semana; demo 3 nodos × 5 altas); no cola Redis
JWT VOCALD-P1-05: mesa interna cerrada; la portada sí es pública
Auth en servidor para mesaSource of truth = guard; el cliente no «esconde» nombres
Puerto 3022Evitar colisión con TEMPLE :3021 / PREGÓN :3020 / MATIZ :3019
Web (no PWA Must)S2: salón + mesa desktop; install Could L+1
code RM-XXXXAcuse humano, no UUID en el salón
409 si no PENDIENTEUn alta no se decide dos veces

5. Variables

.env local (nunca git):

VariableServicioDescripción
DATABASE_URLAPINeon gentle-bonus-75761237
JWT_SECRETAPIFirma tokens
PORTAPIopcional, 3022
SESSION_TTL_SECONDSAPIopcional (default 86400)

.env.example solo placeholders.
Web: URL de API en ApiService (default http://localhost:3022/api). Proxy Angular proxy.conf.json:3022.

6. Módulos API

health · auth · users · nodes · altas · mesa · database · common.

Health: HealthService.name = 'ramal-api'.
main.ts PORT default 3022.

7. Frontend

Páginas: HomePage / · AltaPage /alta · OkPage /ok/:code · LoginPage /login · MesaPage /mesa y /mesa/:code.

Guard JWT en mesa. Interceptor Bearer. Token key ramal.accessToken.

8. Lo que el stack no es

No es un NOC. No es un OSS/BSS. No es un speedtest. No es Prisma obligatorio. No es un leftover de horas de ensayo: el contrato es nodes + altas + VOCAL.

07-creative-direction.md

07 — Dirección creativa — RAMAL

1. Concepto

RAMAL es una portada de red de bloque con escarcha en la teja: se entra a tres nodos y se sale con un alta, no una landing de «tu operadora». El ground es hielo de las ocho. El cobre oxidado es el mástil. El precinto sella lo urgente, poco.

Mood elegido: ice.
Primer instinto descartado: cyan SaaS / NOC. Una red de bloque se ve desde la azotea un martes, no desde un centro de operaciones.

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

2. Territorio visual

EjeNo
SuperficieHielo #E7F0F5, ficha #FFFFFF, mástil #1A2A32Ink TEMPLE, fog PREGÓN, yeso MATIZ, hormigón ZAGUÁN
TipoNewsreader display + Figtree UIFraunces+Atkinson, Ibarra+Jost, Instrument+Sora
FotoAzotea, bloque, arqueta; mástil, no rack de operadorNOC, speedtest, comercial de fibra, «familia sonriente con router»
GridPortada a sangre 16/10; 3 nodos con lead + 3 datos; split mesaHero 2-col + 3 cards de features (AP-12)
AcentoMark #3D7A96 en CTA, nodo activo, selloAmber dashboard, lima highlighter, amatista chapel
ChromeSplit en mesa; portada sin nav de marketingRail de kanban, kiosk mínimo, wizard 1—2—3

3. Paleta

TokenHexRol
Hielo / G-ICE#E7F0F5Ground de página. Escarcha en teja, no fotocopia.
Ink#1A2A32Texto, wordmark, mástil mojado.
Card#FFFFFFSuperficie de ficha / tile / form. Nunca el fondo de página.
Mark#3D7A96Cobre oxidado del mástil: CTA Enviar, aceptar, foco
Mute#5C6B73Meta, labels, calle secundaria, idle
Seal#B42318Precinto secundario, poco: error, no 112-como-envío

Jerarquía de color: el hielo carga; el mark puntúa; la card encuadra el nodo. Si el mark pinta fondos enteros, deja de ser mástil y se vuelve «brand teal». El seal no decora el header: solo puntual (error). [DECISIÓN]

4. Tipografía

UsoFamiliaTamaños de referencia
WordmarkNewsreader 600/70024–28 desktop · 22 mobile
Titular de portada / mesaNewsreader 60032–44
Código RM-XXXXNewsreader 70028–36
Nombre de nodoNewsreader 60022–28
Cuerpo / disclaimersFigtree 40016–17 / 26–28 lh
UI, chips, form, listaFigtree 400/500/60014 / 16
Meta (kind, calle, status)Figtree 60012–13, tracking 0.12em, mark o mute

Prohibido: Newsreader por debajo de 18px (se rompe). Figtree en el wordmark de portada (se vuelve SaaS).

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

5. Fotografía

AssetDebeNo debe
hero.jpgAzotea, teja, mástil, frío de mañanaRack, NOC, familia con router
azotea.jpgMástil de bloqueAntena 5G de operador como marca
bloque.jpgPatio / fachada de bloqueRender inmobiliario
arqueta.jpgArqueta de aceraArmario corporativo de operadora

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

6. Registro R-CL

Institucional de asociación: márgenes serios, tipo con contraste, foto a sangre, ficha blanca. No es un dashboard de red. No es un kiosk de horas. No es un chat municipal.

La innovación está en H-FULL + S-SPLIT + F-ONB, no en un look «telecom cian». [DECISIÓN]

7. Anti-moods

MoodPor qué no
Cyan NOCPrimer instinto; es operador
Vehicle dashboardFue TEMPLE
Highlighter fogFue PREGÓN
Chapel amatistaFue MATIZ
Hormigón oxidadoFue ZAGUÁN

08-design-system.md

08 — Sistema de diseño — RAMAL

1. Tokens

TokenValorUso
--ground#E7F0F5Fondo de página
--ink#1A2A32Texto
--card#FFFFFFSuperficie
--mark#3D7A96CTA, foco, status
--mute#5C6B73Meta
--seal#B42318Error
--font-displayNewsreaderTitulares
--font-sansFigtreeUI
--target44×44 minToques

Tailwind: bg-ground, text-ink, bg-card, bg-mark, text-mute, text-seal, font-display.

2. Escala de tipo

RolSize / leading
Display portadaclamp(2.4rem, 6vw, 4.4rem) / 0.95
H1 mesa3rem / 1
H2 nodo28px / 32px
Cuerpo16–17 / 26
Meta12–13 / 16, tracking 0.12em, uppercase

3. Componentes

Tile de nodo (portada)

Foto 16/10 recortada + kind · calle + nombre + lead + 3 datos. Toda la card es el hit. Destino /alta?nodo=:slug.

Checklist (alta)

4 labels con checkbox ≥20px + texto. Fieldset. Disabled CTA hasta complete.

Acuse

Código Newsreader enorme + bloque mark con nodo y nombre + 4 oposiciones en mute.

Fila de mesa

Código · status · vecina · nodo · vivienda. Ring mark si seleccionada.

Ficha

Código, nombre, dl nodo/vivienda, copy irrenunciable, Aceptar (mark) / Rechazar (ground).

Login

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

Skeleton

animate-pulse bg-card (o bg-ground sobre card). aria-busy.

Error

Titular display + cuerpo mute + Reintentar mark.

4. Layouts

SuperficieGrid
PortadaFoto 16/10 a sangre; debajo 3 cols desktop, 1 col 390
AltaColumna max-w-2xl
Ok / loginColumna max-w-lg centrada
Mesaminmax(280px,360px) 1fr · stacked < lg

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

5. Estados de control

EstadoTratamiento
DefaultCard blanca, ink
Hover tileSin lift; el oficio no flota
FocusRing mark 2px
Disabledopacity 50–60
ErrorTexto seal, no solo color
Selected (split)Ring mark

6. Iconografía

Cero iconos decorativos de «wifi / 5G / cohete». El oficio es tipográfico + foto. [DECISIÓN]

7. Motion

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

8. Densidad (AP-21)

En el recorte 16/10 de portada: eslogan + 3 nodos con lead + 3 datos. Headline huérfano + foto = card hueca. [COMPROBADO] CRON §5.8.

09-content-guide.md

09 — Guía de contenido — RAMAL

1. Voz

Cercana de bloque y de mesa. Tuteo en UI a la vecina. Segunda persona cuando la interfaz habla. Tercera cuando habla la mesa (Inés Soto ocupa…).

Sin jerga de operador («tu cobertura», «hasta 600 Mb», «contrata ahora»). Sin speedtest. Sin «reserva tu slot». Sin sirena. Sin kanban en UI.

El producto se pide y se decide. No vende cobertura, no es router, no instala hoy, no es técnico.

2. Palabras permitidas / prohibidas

UsarNo usar
Nodo / mástil / azoteaRouter de casa, ONT, Wi-Fi (como producto)
Alta / pedir altaContratar, cobertura, alta de operador
Acuse / códigoContrato, línea, SIM
Pendiente / aceptada / rechazadaRecibido, En curso, Instalada, En cobertura
MesaKanban, NOC, backoffice, turno
VocalComercial, operador, conserje
Vecina / vivienda / plantaCliente, lead, abonado
RAMAL no instala hoyTécnico mañana, visita programada

«Kanban» y «F-ONB» son lenguaje de diseño, no de UI: Inés no lee «onboarding»; Pablo lee Pendiente / Aceptada.

«Alta» en UI es la petición de nodo. Si hay ambigüedad, preferir pedir nodo o código. [DECISIÓN]

3. Microcopy duro (literales irrenunciables)

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

OposiciónDóndeCopy
Alta ≠ cobertura del operadorChecklist · acuse · pie de portada«Alta no es cobertura del operador. RAMAL no sustituye a Movistar.»
Nodo ≠ router de casaTile de nodo · checklist · acuse«El nodo está en la azotea, no en el router de casa.»
RAMAL no instala hoyChecklist · acuse · ficha · pie de mesa«RAMAL no instala hoy. Aceptar no sube a la azotea.»
Checklist ≠ técnicoAlta · acuse«Completar el alta no programa una visita.»

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

4. Microcopy por superficie

SuperficieCopy
EsloganLa red del bloque, no el operador.
Línea de lugarVallecas · Palomeras
Portada titularRAMAL
Tile dato 1Mástil / Patio / Acera
Tile dato 2Palomeras / Sierra Gádor / Pablo Neruda
Tile dato 318 viviendas al alcance / 24 viviendas / 2 bloques
Alta volver← Nodos
Alta titularPedir nodo en el bloque
Check 1Vivo en el bloque. Esta es mi vivienda.
Check 2Sé mi planta y portal.
Check 3El nodo está en la azotea, no en el router de casa.
Check 4Alta no es cobertura. RAMAL no instala hoy. El checklist no es un técnico.
Alta submitEnviar alta
Login titularEntrar a la mesa
Login ayudaSolo vocal. La vecina pide alta en la portada, sin cuenta.
Login submitEntrar
Login errorEmail o contraseña no valen.
Empty portadaAún no hay nodos.
Empty mesaNo hay altas.
Success titularAlta enviada.
Success códigoRM-2206
Success cuerpoAlta no es cobertura del operador. RAMAL no instala hoy.
Success nodoNodo ≠ router de casa. Checklist ≠ técnico.
Success CTAVolver a los nodos
ErrorLa red no responde.
Error retryReintentar
Error 409Esa alta ya no está pendiente. Aceptar no sube a la azotea.
Loading portadaCargando
Loading mesaCargando
404 nodoEse nodo no está.
Mesa titularMesa
Ficha vacíaElige un alta
AceptarAceptar
RechazarRechazar
SalirSalir
Link Vocal (portada)Vocal

5. Nodos y altas seed (tono)

Los nodos demo se escriben como infraestructura de bloque, no como ficha de operador ni como anuncio.

PiezaTono
Azotea PalomerasMástil. Teja. Sin rack.
Bloque Sierra GádorPatio. Cuarto de contadores.
Arqueta Pablo NerudaAcera. No armario de marca.
RM-2201 Inés SotoNombre de vecina; no abonada.
Marisa Ortiz (POST)Vecina de smoke; dos palabras, sin biografía.

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

En ACEPTADA no se reescribe a «ya estáis en cobertura». El cierre es la decisión. [DECISIÓN]

6. Nombres propios del seed

NombreUso
Inés SotoVECINA; sin cuenta; 4º B, RM-2201
Pablo RuizVOCAL; pablo@ramal.vallecas
Nerea GilTÉCNICA; sin cuenta; docs
Luis Herrero, Carmen Vidal, Rosa Méndez, Toni CasasVecinas catálogo
Marisa OrtizPOST de smoke
RAMAL · VallecasAsociación demo; Palomeras / Sierra Gádor / Pablo Neruda

No inventar CIF de asociación, teléfono de operador, ni cobertura real como si fuera el producto. [SUPUESTO de catálogo demo]
Vallecas / Palomeras / Sierra Gádor son topónimos reconocibles, no un inventario oficial de una red 2026.

7. Tono por estado

StatusFrase UIFrase que Pablo podría decir (fuera de app)
PENDIENTEPendiente«Está en mesa. No es cobertura.»
ACEPTADAAceptada«La acepté. No subo a la azotea hoy.»
RECHAZADARechazada«No entra. No es un parte.»

La app no envía esos mensajes a la vecina en v1. El copy solo prepara el habla. No hay email de «ya tenéis fibra».

8. Capitalización y números

  • Wordmark: RAMAL en versales Newsreader.
  • Código: RM-2201 (RM versales, guion, 4 dígitos).
  • Nombres: Inés Soto, Pablo Ruiz, Nerea Gil.
  • Nodos: Azotea Palomeras (mayúscula de kind + lugar).
  • Vivienda: 4º B en UI.
  • Fecha: 22 ago si hay línea larga.
  • No mezclar rm-2201 en minúsculas en UI. API puede normalizar a versales.

9. Idioma

es-ES. Fácil lectura. EN no en v1.
No mezclar «onboarding» ni «coverage» en UI. En API sí: nodes, neighborName, status.

«Vecina» en UI cuando hablamos de Inés; «el vocal» para Pablo; «la técnica» solo en docs. [DECISIÓN]

10. Accesibilidad de copy

  • No transmitir estado solo con color (el mark no «significa» pendiente él solo).
  • Filas llevan texto: Pendiente / Aceptada / Rechazada.
  • Aviso de no-cobertura lleva palabras, no solo el seal.
  • Alt de fotos: nodo + lugar, no «imagen1».
    • hero: «Azotea de un bloque en Vallecas; mástil de red de comunidad, no un operador.»
    • azotea: «Mástil en azotea de Palomeras; nodo de bloque.»
    • bloque: «Fachada y patio del bloque Sierra Gádor.»
    • arqueta: «Arqueta de acera en Pablo Neruda.»

11. Piezas que no se escriben

NoMotivo
«Tu cobertura ha sido activada»Alta ≠ cobertura
«Ya está instalado»RAMAL no instala hoy
«Contratar ahora» / «Mbps»S4 · no operador
«Si es urgente, abre aquí» (como 112)RAMAL no es 112
Recibido / En curso / CerradoNo es kanban
Ranking de velocidad del bloqueSpeedtest, otro producto
Lorem / «lorem ipsum nodo»Prohibido en el case
Declaraciones inventadas de una asociación realVerdad de catálogo

10-accessibility.md

10 — Accesibilidad — RAMAL

1. Alcance

WCAG 2.2 AA orientada al slice: portada, checklist, acuse, login, mesa split. No es una auditoría certificada. [DECISIÓN]

2. Contraste

ParRelación esperada
Ink #1A2A32 sobre hielo #E7F0F5> 7:1
Ink sobre card #FFFFFF> 10:1
Card sobre mark #3D7A96Texto de CTA ≥ 4.5:1
Mute #5C6B73 sobre hieloMeta ≥ 4.5:1
Seal #B42318 sobre cardError ≥ 4.5:1

Texto sobre foto: gradient from-ink/80 bajo el titular. No titular suelto sobre teja clara.

3. Teclado y foco

  • Orden: wordmark → Vocal → tiles de nodo → form → mesa lista → ficha.
  • Foco visible: ring mark 2px.
  • Radio de nodo y checks operables con teclado.
  • Aceptar / Rechazar no se activan al pasar el foco.

4. Targets

≥44×44 en CTA, checks, filas de mesa, link Vocal. Checks 20px visual + label amplia.

5. Nombre accesible

ControlNombre
Tile nodoNombre del nodo (h2)
ChecksTexto del ítem
Enviar altaEnviar alta
Aceptar / RechazarAceptar / Rechazar
SalirSalir

Fotos: alt de doc 09. Decorative? No: las tres son contenido.

6. Estados no solo por color

Pendiente / Aceptada / Rechazada en texto. Completo del checklist: botón disabled + copy. Error: texto seal + mensaje.

7. Motion

prefers-reduced-motion: sin pulse. Skeletons estáticos.

8. Mobile 390

Portada: foto + 3 nodos apilados. No carousel.
Alta: una columna.
Mesa: lista o ficha (master-detail). Back a lista = /mesa.

9. Autocomplete

Login: username / current-password. Nombre: name. Vivienda: sin autocomplete de dirección de operador.

10. Idioma

html lang="es". Números y códigos leídos como texto (RM-2201).

11-privacy-security.md

11 — Privacidad y seguridad — RAMAL

1. Superficie pública vs cerrada

RecursoAuthPII
GET nodesNoNo (infra de bloque)
POST altasNoNombre + vivienda de quien envía
GET /ok/:codeNo (solo código en URL)No hidrata ficha ajena desde API
GET/PATCH mesaJWT VOCALLista de vecinas

[DECISIÓN] El acuse no hace GET público por código. Evita enumeración de Inés Soto.

2. Datos de vecina

neighbor_name + dwelling + node_id. Sin email, sin DNI, sin teléfono en v1.
La vecina no tiene fila en users.

3. Auth

  • JWT HS256. Claims: sub userId, sid session.
  • Password bcrypt 12. Demo password123 solo en seed local.
  • Guard: Bearer + sesión no revocada.
  • Role VOCAL para mesa. 401 sin token, 403 si no VOCAL.
  • Rate limit en login.

4. Secretos

DATABASE_URL y JWT_SECRET en apps/api/.env (gitignore). Nunca en Angular.

5. CORS

Allowlist localhost:4200. Previews Vercel opcionales.

6. Amenazas del slice

AmenazaMitigación
Enumerar /ok/RM-2201 y ver a InésOk no GET; solo state de navegación
POST spam de altasVolumen demo; rate Could L+1
Reabrir alta aceptada409
Token en localStorageXSS = compromiso; slice L3 acepta el tradeoff de SPA [DECISIÓN]

7. RGPD (modelo, no dictamen)

Base: interés de la asociación en gestionar altas. Minimización: nombre + vivienda. Sin cesión a operador. Derecho de supresión Could L+1 (Pablo no borra en v1).

Nerea no tiene acceso. No hay logs de «quién subió a la azotea».

8. Lo que no hacemos

No OAuth. No cookies de marketing. No analytics de terceros en v1. No cifrado de dwelling más allá de TLS.

12-analytics.md

12 — Analítica — RAMAL

1. North star

% de altas que Pablo mueve (ACEPTADA o RECHAZADA) en 7 días.

Oficio de junta. No pageviews. No «engagement con el mástil». [DECISIÓN]

2. Métricas de producto (modelo)

MétricaDefiniciónHipótesis
Activación vecinaPOST /api/altas 201 / visitas a /H1
Compleción checklistSubmit / inicios de /altaH2
Tiempo a código< 90 s en demoDiseño
Tiempo a decisiónPATCH desde GET mesaH3
Integridad auth401 en GET mesa anónimoSeguridad
ExpectativaTickets «¿cuándo instaláis?» (fuera de app)H2

3. Eventos (si se instrumentara)

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

EventoProps
portada_viewnodes_count
alta_submitnode_slug, code
mesa_opencode
alta_decidecode, status

Prohibido: purchase, coverage_unlocked, speedtest_run.

4. Lo que no medimos

Mbps. Tiempo en azotea. Aperturas de WhatsApp (no hay dato). NPS de operadora.

5. Salud

GET /api/health{ ok: true, service: "ramal-api" }. Error rate 4xx/5xx en nodes/altas/mesa.

13-qa-test-plan.md

13 — Plan de QA — RAMAL

1. Smoke API (pnpm test:api)

  1. GET /api/healthramal-api
  2. GET /api/nodes → 3 slugs azotea,bloque,arqueta + lead + 3 datos
  3. GET /api/nodes/azotea → kind azotea
  4. POST /api/altas completo → RM- PENDIENTE
  5. POST incompleto → 400
  6. GET /api/mesa sin JWT → 401
  7. POST /api/auth/login Pablo → VOCAL
  8. GET /api/mesa → ≥5 e incluye RM-2201…2205
  9. GET /api/mesa/RM-2201 → Inés Soto PENDIENTE
  10. PATCH ACEPTADA → 200; segundo PATCH → 409

2. Smoke UI

#PasosEsperado
1Abrir /Foto a sangre, 3 nodos, eslogan
2Tap Azotea/alta?nodo=azotea
3Enviar sin checksCTA disabled
4Completar + Marisa 5º C/ok/RM-XXXX + 4 oposiciones
5/login Pablo/mesa split
6Tap RM-2201Ficha Inés · Aceptar
7AceptarStatus Aceptada; botones fuera
8LogoutPortada; /mesa pide login
9/ 3903 nodos apilados, no carousel
10API downError + Reintentar

3. Regresión de unicidad

  • No copy TEMPLE (hora, concierto, conserje, turno, TM-).
  • No copy PREGÓN (chat, kanban, 112, parte).
  • No Movistar, no Mbps, no speedtest.
  • Health no dice temple-api.
  • Puerto 3022.

4. Accesibilidad rápida

Tab por tiles y checks. Focus ring. Alt de 4 fotos. Status en texto.

5. Datos

Seed idempotente por email y code. Un POST de smoke no borra RM-2201.
Playwright futuro: esperar Inés Soto, no Lola ni Los Yesos.

6. Criterio de salida

Smoke API verde + guión UI completo + 0 leftover de dominio anterior en UI.

14-dev-handoff.md

14 — Handoff de desarrollo — RAMAL

1. DoD del slice

  • Health { ok: true, service: "ramal-api" } en :3022
  • 3 nodos seed con foto, lead, 3 datos
  • POST alta pública → RM-XXXX PENDIENTE
  • JWT VOCAL pablo@ramal.vallecas / password123
  • Mesa split lista | ficha · PATCH ACEPTADA / RECHAZADA
  • Empty / error / loading en portada y mesa
  • Copy ×4 visible
  • Rutas / /alta /ok/:code /login /mesa /mesa/:code
  • Paper 12 UX + 12 UI mapeados
  • Docs 01–20 + README + executive

2. Contratos

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

POST /api/altas:

{ nodeSlug, neighborName, dwelling, vivienda, planta, azotea, noInstala }

Los cuatro booleanos deben ser true.

PATCH /api/mesa/:code:

{ status: "ACEPTADA" | "RECHAZADA" }

3. Mapas de pantalla

PaperRutaComponente
UI-01 Portada/HomePage
UI-03 Alta/altaAltaPage
UI-05 Success/ok/:codeOkPage
UI-06 Login/loginLoginPage
UI-07 Mesa/mesaMesaPage
UI-08 Ficha/mesa/:codeMesaPage

4. Tokens en código

tailwind.config.js + styles.scss. Fuentes Google: Newsreader + Figtree.

5. Assets

Copiar 2026-08-22-ramal/assets/nodos/*.jpg y hero.jpg a apps/web/public/assets/.

6. No hacer

  • No reintroducir rooms / bookings como dominio vivo.
  • No documentar leftover TEMPLE como estado actual.
  • No añadir signup de vecina, speedtest, chat, kanban, GIS, pago.

7. Arranque

cd /Users/cristian/orca/ramal-app
# apps/api/.env → DATABASE_URL Neon gentle-bonus-75761237, JWT_SECRET, PORT=3022
pnpm --filter @repo/api start:dev
pnpm --filter @repo/web start

15-roadmap.md

15 — Roadmap — RAMAL

Aquí solo viven ideas de L+1 / L4 y pulido posterior.
El slice L3 del día está cerrado: portada, checklist, acuse, JWT VOCAL, mesa split.

1. Hecho (L3)

  • Portada H-FULL · 3 nodos · lead + 3 datos
  • Checklist F-ONB · 4 ítems · una pantalla
  • Acuse /ok/:code
  • Mesa S-SPLIT · aceptar / rechazar
  • Empty / error / loading · portada 390
  • Neon gentle-bonus-75761237 · API :3022 · ramal-api

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

ItemNota
Filtro de mesa por nodo5 filas no lo necesitan; 40 sí
Comentario de rechazoUna línea, no chat
Recibo por email a la vecinaSigue sin cuenta
PWA installSalón; no es Must

3. L4 (requiere brief nuevo de diversidad)

  • Rol TÉCNICA (Nerea) + cuadrante de azotea
  • Radio planning / mapa GIS
  • Pago / cuota de asociación
  • Multi-vocal / multi-bloque
  • Speedtest (otro producto; no es RAMAL)
  • Chat / kanban (fue PREGÓN)

4. Fuera para siempre en este nombre

Movistar. Cobertura como promesa. Router de casa como nodo. Instalación el día del alta.

5. Criterio para abrir L4

Solo si un día futuro elige telecom L4 y la diversidad ALS-2 lo permite. No parche silencioso.

16-interaction-specs.md

16 — Especificación de interacción — RAMAL

1. Convenciones

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

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


2. Portada (UI-01) + mobile (UI-02) · H-FULL

GestoResultado
Primera cargaLoading: 3 tiles card; aria-busy
200 con 3Foto a sangre + 3 nodos. Lead + 3 datos
200 con 0UI-09 empty
Error 5xx / redUI-10 + Reintentar (repite GET nodes)
Tap tile/alta?nodo=:slug
Tap Vocal (ghost)/login o /mesa si hay token
Hover tile desktopRing 2px mark. Sin lift

AC: nombre, lead y 3 datos visibles sin hover. 3 nodos en el recorte 16/10 (AP-21). Cero botones Cobertura / Mbps / Crear cuenta.

El aviso «RAMAL no instala hoy» es texto, no un CTA de instalación. [DECISIÓN]

390: mismas 3 nodos apilados. No carousel. [DECISIÓN]


3. Alta checklist (UI-03) + mobile (UI-04) · F-ONB

GestoResultado
Primera cargaLoading: form skeleton
2003 radios de nodo + nombre + vivienda + 4 checks
Query ?nodo=Preselecciona slug
Check incompletoCTA disabled
Submit completoPOST · pending en botón
201/ok/:code con state
400Copy checklist; no navega
Error redseal + mensaje
Tap ← Nodos/

AC: una pantalla. Cero stepper. Los 4 checks son el job, no un modal.


4. Acuse (UI-05)

GestoResultado
Con códigoNewsreader RM-XXXX + bloque mark + 4 oposiciones
Sin código«No hay ese código» + Pedir alta
CTA/ nodos

No GET público por código. [DECISIÓN] doc 11.


5. Login (UI-06)

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

6. Mesa split (UI-07) + ficha (UI-08)

GestoResultado
GET mesa loading5 skeletons + ficha
200 con altasLista izq. PENDIENTE primero
200 []UI-09
ErrorUI-10
Tap fila/mesa/:code · ring mark
Sin :code«Elige un alta»
Ficha loadingSkeleton derecho
404«Esa alta no está»
AceptarPATCH ACEPTADA · botones fuera
RechazarPATCH RECHAZADA
409seal · ya decidida
Salirlogout → /

Desktop: dos columnas. 390: ficha a pantalla; volver a lista = /mesa. [DECISIÓN]


7. Loading / empty / error (UI-09…11)

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

17-prototype-map.md

17 — Mapa de prototipo — RAMAL

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

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 · 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 Portada/HomePage
UI-02 Portada mobile/ 390HomePage
UI-03 Alta/altaAltaPage
UI-04 Alta mobile/alta 390AltaPage
UI-05 Success/ok/:codeOkPage
UI-06 Login/loginLoginPage
UI-07 Mesa split/mesaMesaPage
UI-08 Ficha/mesa/:codeMesaPage
UI-09 Emptymismas rutasempty branches
UI-10 Errormismas rutaserror branches
UI-11 Loadingmismas rutasskeletons

Guión de prototipo

  1. Cover → Portada (3 nodos).
  2. Alta checklist Inés.
  3. Acuse RM-XXXX.
  4. Login Pablo.
  5. Mesa · RM-2201 · Aceptar.
  6. Empty / error / loading.

No hay artboard de speedtest, chat, kiosk de horas ni kanban.

18-completeness-audit.md

18 — Auditoría de cierre — RAMAL

1. Alcance L3

PiezaEstado
Docs 01–20 + README + executiveOK (este set)
00-day-brief / 00-paper-referenceIntactos (no tocados)
Paper 12 UX + 12 UIInventario en 00
App path /Users/cristian/orca/ramal-appOK
Neon gentle-bonus-75761237OK
API :3022 · health ramal-apiContrato
Código nodes + altas + JWT VOCALContrato paralelo
Cierre sin deuda del L3OK · roadmap solo L+1 / L4

2. Terna y diversidad

CheckEvidencia
R-CL · S-SPLIT · H-FULL · F-ONBBrief + 01 + 07
0 códigos = TEMPLETEMPLE era R-HY · S-MIN · H-KIOSK · F-BOOK
≥2 vs PREGÓN / MATIZSplit/Full/ONB vs chat/kanban y bento/edit
Ventana 4: R-CLR-HY + R-IN + R-HY + R-CL
Ground G-ICE #E7F0F500 + 07 + Tailwind

3. Copy irrenunciable

Presente en alta, acuse, ficha, pie de portada. Ver doc 09.

4. Anti-patrones vigilados

APMitigación
AP-12 hero 2-colH-FULL a sangre
AP-21 card huecaLead + 3 datos
Leftover como estadoContrato RAMAL documentado; no se anota TEMPLE como producto vivo
Entrevistas inventadas02 etiquetado

5. Seed

3 nodos · 5 altas (3 PENDIENTE, 2 ACEPTADA) · Pablo VOCAL · Inés sin cuenta.

6. Huecos conscientes (fuera)

GIS, pago, login Nerea, email transaccional, instrumentación analytics.

7. Veredicto

Slice L3 cerrado a nivel de case. El código se escribe en paralelo con el mismo contrato. Si el repo diverge, gana docs 05 + 20 tras alinear ambos.

19-backlog-completo.md

19 — Backlog — RAMAL

1. Done (Must L3)

IDItem
D01Portada 3 nodos H-FULL
D02Checklist 4 ítems F-ONB
D03Acuse /ok/:code
D04Login JWT VOCAL
D05Mesa split lista | ficha
D06PATCH ACEPTADA / RECHAZADA
D07Empty / error / loading
D08Portada 390
D09Seed 3 nodos + 5 altas
D10Health ramal-api :3022
D11Copy ×4
D12Docs 01–20

2. Could L+1 (no bloquea cierre)

IDItem
C01Filtro mesa por nodo
C02Nota de rechazo
C03Email de acuse
C04PWA
C05Rate limit POST altas

3. Explícito L4 / otro brief

IDItem
E01Login Nerea / cuadrante
E02GIS / radio planning
E03Pago / cuota
E04Multi-asociación
E05Speedtest
E06Chat / kanban

4. No-backlog (rechazado)

Hero 2-col. Signup vecina. leftover TEMPLE como feature. Movistar. «Instalar hoy».

5. Cómo usar este archivo

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

20-implementation.md

20 — Implementación — RAMAL

1. Resumen ejecutivo técnico

CampoValor
App path/Users/cristian/orca/ramal-app
APINestJS · puerto 3022 · prefijo /api
WebAngular standalone · puerto 4200
Packageapps independientes (npm --prefix o pnpm --filter)
DBNeon PostgreSQL · project gentle-bonus-75761237
Tablasusers, nodes, altas, sessions
AuthJWT Bearer · role VOCAL (mesa); portada y POST altas públicos
DominioUser, Node, Alta
Fecha2026-08-22
GitHubhttps://github.com/Criscode2022/ramal-app

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: nodes + altas + JWT VOCAL. Puerto 3022. Health ramal-api.

[COMPROBADO] Path, puerto, Neon, Paper y GitHub salen del encargo.

2. Cómo arrancar

cd /Users/cristian/orca/ramal-app
# instalar dependencias por app (no npm workspaces)

# apps/api/.env
#   DATABASE_URL=   # Neon gentle-bonus-75761237
#   JWT_SECRET=
#   PORT=3022

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

Credenciales

RolEmailPassword
VOCALpablo@ramal.vallecaspassword123

Vecina / técnica: sin cuenta. Inés pide alta en /alta. Nerea no entra.

3. Schema

Al boot o vía migrate:

  1. CREATE TABLE de users, sessions, nodes, altas (doc 05).
  2. Índices: nodes(slug), altas(code), altas(status), users(email).
  3. Seed idempotente:
    • upsert VOCAL pablo@ramal.vallecas (bcrypt de password123)
    • upsert nodes por slug (azotea, bloque, arqueta)
    • upsert altas por code (RM-2201RM-2205)
  4. Dominio: solo nodes + altas + users. [DECISIÓN]

No borrar altas nuevas de Inés/Marisa en cada boot si ya existen filas distintas al seed (upsert por code no pisa status de altas ya decididas).

Generador de código: SELECT max(code) numérico + 1, formateado RM- + 4 dígitos. Seed máximo 2205 → siguiente POST = RM-2206. Si se agota 9999, L+1 amplia a 5 dígitos; no es Must.

Tablas (recordatorio)

TablaContenido
usersVOCAL Pablo
nodes3 nodos
altas5 altas 3 PENDIENTE / 2 ACEPTADA
sessionssid + expires_at

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 === VOCAL protege mesa.
  • Públicos: health, nodes, nodes/:slug, POST altas, login.

Oficio

MétodoRutaAuthNotas
GET/api/healthNo{ ok: true, service: "ramal-api" }
GET/api/nodesNosort_order asc + lead + data1..3
GET/api/nodes/:slugNoficha de nodo
POST/api/altasNocrea PENDIENTE + code; 400 si checklist incompleto
GET/api/mesaJWTlista PENDIENTE primero
GET/api/mesa/:codeJWTficha
PATCH/api/mesa/:codeJWT{ status } · 409 si no PENDIENTE

Health (referencia de implementación)

HealthService.name = 'ramal-api'
main.ts PORT default 3022

Alta (referencia)

require vivienda && planta && azotea && noInstala
node = nodes.bySlug(nodeSlug)
code = next RM-
INSERT PENDIENTE
return Alta camelCase con nodeName

JSON camelCase: neighborName, nodeSlug, nodeName, nodeKind, sortOrder, createdAt, decidedAt, data1.
SQL: neighbor_name, node_id, sort_order, created_at, decided_at, data_1.

POST no cobra, no instala, no llama a Nerea, no mueve kanban.

Módulos de dominio: nodes/, altas/, mesa/, auth/, health.

5. Frontend a implementar

PáginaRutaResponsabilidad
HomePage/H-FULL · GET nodes · 3 tiles
AltaPage/altaChecklist · POST altas
OkPage/ok/:codeAcuse · state de navegación
LoginPage/loginJWT VOCAL
MesaPage/mesa · /mesa/:codeSplit · PATCH

Estados: loading / ready / empty / error en portada y mesa.
Token key: ramal.accessToken.
Proxy: /api:3022.

Tokens Tailwind: ground / ink / card / mark / mute / seal. Fuentes Newsreader + Figtree.

6. Seed visible en UI

CódigoVecinaNodoStatus
RM-2201Inés SotoAzotea PalomerasPENDIENTE
RM-2202Luis HerreroBloque Sierra GádorPENDIENTE
RM-2203Carmen VidalArqueta Pablo NerudaPENDIENTE
RM-2204Rosa MéndezAzotea PalomerasACEPTADA
RM-2205Toni CasasBloque Sierra GádorACEPTADA

Playwright: esperar Inés Soto y Azotea Palomeras. No esperar Lola, Los Yesos, turno.

7. Smoke

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

Cubre health, 3 nodos, POST alta, 400 checklist, 401 mesa, login VOCAL, lista ≥5, ficha RM-2201, PATCH + 409.

8. DoD técnico

Ver doc 14. Este archivo no lista leftover de otro día como estado del producto. El contrato vivo es RAMAL.