00-day-brief.md
00 — Day brief · 2026-08-17 · MARGEN
Decisiones ALS-2 BRIEF
| Campo | Valor |
|---|---|
| Fecha | 2026-08-17 |
| Nombre | MARGEN |
| Complejidad | Nivel 2 (no L1 consecutivo tras CESTA) |
| Sector | Editorial local / familias (semanario vecinal) |
| Tipo | Lookbook de números + lectura de crónicas + agenda familiar + mesa EDITOR JWT |
| Plataforma | Web responsive (galería editorial desktop · lector mobile · mesa redacción) |
| Registro | R-CL — kiosco / revista de barrio, rigor tipográfico |
| Shell | S-TOP — cabecera de periódico (masthead + secciones) |
| Home | H-GALLERY — portadas y piezas como lookbook, no hero 2-col |
| Flujo | F-STORY — hojear número → leer crónica (no cola de leads) |
| Por qué no CESTA | No S-RAIL / H-TABLE / F-LEAD; no ONG; no tabla de necesidades |
| Por qué no RONDA | No S-DOCK / H-FEED / F-BOOK; no gaming |
Terna unicidad
R-CL · S-TOP · H-GALLERY · F-STORY
0 códigos iguales a N−1 (CESTA). ≥2 distintos vs N−2 (RONDA) y N−3 (DERIVA). Ventana 4 días ya tenía R-IN (DERIVA) y R-HY (RONDA); hoy R-CL editorial.
Ground (CRON §5.6)
- Familia: G-GALLERY
- Hex:
#FFFFFF - Por qué: kiosco / papel prensa. No es G-MOSS (CESTA), G-CHROME (RONDA), G-WATER (DERIVA), G-MUNICIPAL (PIZARRA) ni G-ICE (PRIMA).
Mood visual (Paper)
- Candidatos: bookish plaster×ink (primer instinto), chapel, candlelit, gallery / newsstand
- Elegido: gallery / newsstand — no el primer instinto bookish
- Paleta:
#FFFFFFpapel ·#16141Atinta ·#C41E3Acadmio ·#5C5866grafito ·#F4F1EApliego (solo cards) ·#E8E4DCfilete - Tipo: Cormorant Garamond display · Public Sans UI (evita Newsreader / Outfit / Spectral / Manrope / Zilla / Mulish / Lora / Karla)
Alcance L2 must
- Home lookbook: número en curso + ≥3 portadas/piezas con lead (visibles en recorte 16:10)
- Ficha de crónica (kicker, título, cuerpo, crédito)
- Agenda familiar de la semana (3 eventos reales)
- Login + mesa EDITOR JWT (DRAFT → PUBLISHED)
- Alta de pieza + success
- Empty / loading / error / mobile lector
- ≥12 UX + ≥10 UI
Microcopy duro
- Publicar ≠ imprimir. El número digital no es el tiraje de kiosco.
- Borrador ≠ publicado. La familia no ve DRAFT.
- Agenda ≠ inscripción. El evento se lee; no se reserva plaza (≠ PIZARRA).
Supuestos
- S1: Un semanario de barrio cierra 1 número / semana y 4–8 piezas (demo: 1 número + 4 piezas + 3 eventos).
- S2: La lectora llega por “qué hay esta semana”, no a suscribirse a un paywall.
- S3: La redacción es 1–2 personas; la mesa es lista de piezas, no un CMS enterprise.
- S4: No hay comentarios ni likes. El producto es lectura + publicación.
Hipótesis
| ID | Señal |
|---|---|
| H1 | El lookbook de portadas reduce “¿dónde está el PDF del cole?” vs hilos de WhatsApp |
| H2 | Separar agenda de inscripción evita la expectativa de reserva |
| H3 | JWT solo en redacción basta; el lector no necesita cuenta |
00-paper-reference.md
Paper reference · MARGEN
| URL | https://app.paper.design/file/01M078GFW7QQF5RRA2XGKQ8BD9 |
| File ID | 01M078GFW7QQF5RRA2XGKQ8BD9 |
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 Lookbook · UI-02 Crónica · UI-03 Agenda · UI-04 Login · UI-05 Mesa · UI-06 Alta pieza · UI-07 Success · UI-08 Empty · UI-09 Error · UI-10 Loading · UI-11 Mobile lector
UX process (prefijo UX- · mínimo 12)
| ID | Nombre | Contenido |
|---|---|---|
| UX-00 | Cover | Portada MARGEN · eslogan «La semana del barrio, en un número.» · terna R-CL · S-TOP · H-GALLERY · F-STORY · L2 editorial local familias · mood gallery/newsstand · fecha 2026-08-17 |
| UX-01 | Stakeholders | LECTORA anónima (Elena), EDITOR (Nuria), AMPA (Raúl), cole, biblioteca, vecinos de Usera / Carabanchel / Villaverde — no paywall, no inscripción |
| UX-02 | Personas | Elena Vargas (38, lectora, Usera, dos hijos) · Nuria Ceballos (44, editora, mesa de 1–2) · Raúl Méndez (42, AMPA Carabanchel) |
| UX-03 | JTBD | Job «qué hay esta semana en el barrio» + job «cerrar el número y publicar la crónica» + job «que el cole se lea, no se reserve» |
| UX-04 | Stories | Must: lookbook del número + ≥3 portadas, ficha de crónica, agenda 3 eventos, login EDITOR, mesa DRAFT→PUBLISHED, alta + success, empty/error/loading, mobile lector |
| UX-05 | Journey | Hojear lookbook → leer crónica → consultar agenda → (EDITOR) login → mesa → nueva pieza → publicar |
| UX-06 | Blueprint | Frontstage masthead/galería/ficha/agenda · backstage cierre de número y WhatsApp del AMPA · sistemas Nest/Neon/JWT |
| UX-07 | Site map / IA | Público gallery-first + auth EDITOR; S-TOP Número · Agenda · Mesa |
| UX-08 | Flujos | F-STORY: lookbook → crónica → agenda; F-DESK: login → lista → alta → PATCH PUBLISHED |
| UX-09 | Datos + permisos | User EDITOR · Issue · Piece · Event; JWT en GET mesa / POST / PATCH; público no ve DRAFT |
| UX-10 | Métricas | North star % sesiones que abren ≥1 pieza y ven agenda; publicar no cuenta como tiraje |
| UX-11 | Research notes | Comprobado / supuesto / hipótesis / decisión — sin entrevistas de campo inventadas |
UI producto (prefijo UI- · mínimo 10)
| ID | Nombre | Flujo |
|---|---|---|
| UI-00 | Tokens | DS gallery/newsstand: papel, tinta, cadmio, grafito, pliego, filete; Cormorant Garamond + Public Sans |
| UI-01 | Lookbook | H-GALLERY: número en curso + ≥3 portadas/piezas con lead, recorte 16:10 visible |
| UI-02 | Crónica | Ficha: kicker, título, cuerpo, crédito, cover |
| UI-03 | Agenda | Semana familiar, 3 eventos; se leen, no se inscriben |
| UI-04 | Login | Acceso JWT EDITOR a la mesa |
| UI-05 | Mesa | Lista de piezas DRAFT / PUBLISHED + CTA nueva |
| UI-06 | Alta pieza | Form de crónica (título, kicker, cuerpo, sección, cover) |
| UI-07 | Success | Acuse de publicación + aviso «no es el tiraje de kiosco» |
| UI-08 | Empty | Número sin piezas publicadas |
| UI-09 | Error | Fallo de red, reintentar |
| UI-10 | Loading | Cerrando el número |
| UI-11 | Mobile lector | Home galería 390px + 3 leads |
Regla: wizard de N pasos → N boards UI solo para esos pasos, además de success/empty/error/loading/staff. MARGEN no es wizard: el núcleo es lookbook → crónica → agenda y mesa → alta → publicar.
Paper se construye en paralelo a esta suite. Los nombres de la tabla son el inventario planificado; el file ID ya existe.
1. Mapa de canvas (bandas)
| § | Banda | Propósito | Artboards clave |
|---|---|---|---|
| 1 | UX PROCESS | Modelo de servicio de semanario vecinal | UX-00…UX-11 |
| 2 | DESIGN SYSTEM | Tokens gallery + masthead + card pliego | UI-00 |
| 3 | PUBLIC GALLERY | Cara lectora lookbook / lectura / agenda | UI-01, UI-02, UI-03, UI-08, UI-10 |
| 4 | EDITOR | Flujos autenticados | UI-04, UI-05, UI-06, UI-07 |
| 5 | STATES | Resiliencia | UI-08, UI-09 |
Layout canvas (referencia de diseño): origen (0,0) · gaps ~100–120px · UX en 3×4 · UI pública en fila lookbook → crónica → agenda · mesa en fila login → lista → alta → success · mobile debajo del lookbook. [DECISIÓN] Bandas jerárquicas D-P0-04 / D-P1-04.
2. Mapeo Paper → Angular
| Artboard | Ruta app | Componente |
|---|---|---|
| UI-00 Tokens | — | tokens Tailwind + fuentes |
| UI-01 Lookbook | / | HomePage (galería del número) |
| UI-02 Crónica | /piezas/:slug | PiecePage |
| UI-03 Agenda | /agenda | AgendaPage |
| UI-04 Login | /login | LoginPage |
| UI-05 Mesa | /mesa | DeskPage |
| UI-06 Alta pieza | /mesa/nueva | NewPiecePage |
| UI-07 Success | /mesa/ok/:slug | OkPage |
| UI-08 Empty / Error | / 0 piezas · /agenda 0 eventos · /mesa 0 filas | empty / banner error + retry |
| UI-09 Loading | lookbook / mesa / agenda | skeletons de cards / filas |
| UI-10 Mobile lector | / y /piezas/:slug viewport 390 | mismos pages + tipografía de lectura |
[COMPROBADO] Rutas del encargo. No hay /inscribir, /comentarios, /suscribir, search-first ni hero 2-col.
3. Tokens de diseño en Paper
| Token | Valor | Uso |
|---|---|---|
| Papel / ground | #FFFFFF | Fondo de página, kiosco |
| Tinta | #16141A | Texto fuerte, wordmark, cuerpo |
| Cadmio | #C41E3A | Acento, filete de masthead, PUBLISHED, CTA |
| Grafito | #5C5866 | Meta, kicker, crédito, nav idle |
| Pliego | #F4F1EA | Cards de pieza / evento (no el ground) |
| Filete | #E8E4DC | Bordes, rules, skeletons |
| Display | Cormorant Garamond 56/40/28 | Titulares, wordmark, título de crónica |
| UI | Public Sans 16/14 | Body UI, labels, form, nav, agenda |
[COMPROBADO] Hex y tipo salen de docs/00-day-brief.md. Mood elegido: gallery / newsstand, no el primer instinto bookish plaster×ink.
4. Checklist de densidad (anti thin-frames)
| Criterio | UX | DS | Public gallery | EDITOR | States |
|---|---|---|---|---|---|
| Jerarquía tipográfica visible | Sí | Sí | Sí | Sí | Sí |
| Microcopy real (no lorem) | Sí | Sí | Sí | Sí | Sí |
| Tokens gallery aplicados | — | Sí | Sí | Sí | Sí |
| Datos de seed creíbles | Personas | — | 4 piezas + 3 eventos + fotos | 1 DRAFT en mesa | Empty realista |
| Media / iconografía | Cover | — | Fotos de barrio | Badges DRAFT/PUBLISHED | Skeletons |
| Masthead S-TOP presente | — | Spec | UI-01, UI-02, UI-03, UI-10 | UI-05 | — |
| Galería (no tabla, no feed) | — | Spec | UI-01, UI-10 | — | — |
| Recorte 16:10 visible en portadas | — | Spec | UI-01 | — | — |
5. Media
assets/hero.jpg— atmósfera de kiosco / semanario abierto en mesa de cocina, café y cuaderno de niñosassets/merienda.jpg— Patio de manzana, familias en banco de piedra, meriendaassets/colegio.jpg— Puerta de CEIP, mochilas en la valla, espera de madres y padresassets/piscina.jpg— Vaso municipal de verano, bloques de ladrillo al fondoassets/biblioteca.jpg— Sala de lectura municipal, patio y cartel «Biblioteca Municipal»
[COMPROBADO] Los cinco archivos existen en el case 2026-08-17-margen/assets/.
6. Enlaces
- Paper: https://app.paper.design/file/01M078GFW7QQF5RRA2XGKQ8BD9
- Case:
/Users/cristian/orca/ux-projects/2026-08-17-margen/ - App:
/Users/cristian/orca/margen-app/ - GitHub: https://github.com/Criscode2022/margen-app
- Neon project:
morning-truth-64427459 - API:
http://localhost:3017 - Web:
http://localhost:4200
01-project-definition.md
01 — Definición de proyecto — MARGEN
1. Identidad
| Campo | Valor |
|---|---|
| Nombre | MARGEN |
| Significado | El margen de la página (filete, blanco, kicker) y el margen sur de Madrid: Usera, Carabanchel, Villaverde. Semanario de familias, no revista de centro. |
| Eslogan | ”La semana del barrio, en un número.” |
| Una frase | Lookbook del número semanal + lectura de crónicas + agenda familiar + mesa EDITOR JWT. |
| Sector | Editorial local / familias (semanario vecinal) |
| Tipo | Web L2 — galería + crónica + agenda + mesa EDITOR — Nivel 2, craft alto |
| Plataforma | Web responsive (galería editorial desktop · lector mobile · mesa redacción) |
| Mercado demo | España · Madrid · Usera / Carabanchel / Villaverde (un solo semanario, no red multi-distrito) |
| Idioma | es-ES |
| Fecha caso | 2026-08-17 |
| Terna | R-CL · S-TOP · H-GALLERY · F-STORY |
[COMPROBADO] Nombre, terna, nivel, sector, ground y paleta salen de docs/00-day-brief.md.
2. Problema
Principal (hipótesis de diseño)
[HIPÓTESIS] La familia del sur de Madrid no abre un paywall ni un PDF institucional: pregunta “qué hay esta semana” en el grupo del cole, el del patio y el de la AMPA. El recado se pierde, el cartel se desactualiza y el “PDF del cole” circula en tres versiones. [SUPUESTO] La redacción de un semanario de barrio es 1–2 personas; no opera un CMS enterprise ni una mesa de inscritos.
Secundarios
| Problema | Quién lo sufre | Efecto |
|---|---|---|
| “¿Dónde está el PDF del cole?” enterrado en hilos | LECTORA (Elena) | Llega tarde a la merienda, al vaso, a la hora del cuento |
| Número digital confundido con tiraje de kiosco | Ambos | “¿Cuándo lo imprimís?”; expectativa de papel |
| Borrador visible en home | EDITOR (Nuria) + lectora | Crónica a medias se lee como publicada |
| Agenda leída como inscripción | AMPA (Raúl) + lectora | “Ya estoy apuntada” cuando solo había un horario |
| Mesa de redacción = Excel + Drive | EDITOR | Cierra el número en chats, no en una lista |
Supuestos (no investigación primaria propia)
- S1: Un semanario de barrio cierra 1 número / semana y 4–8 piezas (demo: 1 número + 4 piezas + 3 eventos). [SUPUESTO]
- S2: La lectora llega por “qué hay esta semana”, no a suscribirse a un paywall. [SUPUESTO]
- S3: La redacción es 1–2 personas; la mesa es lista de piezas, no un CMS enterprise. [SUPUESTO]
- S4: No hay comentarios ni likes. El producto es lectura + publicación. [SUPUESTO]
Hipótesis de producto
| ID | Hipótesis | Señal de validación (futura) |
|---|---|---|
| H1 | El lookbook de portadas reduce “¿dónde está el PDF del cole?” vs hilos de WhatsApp | % sesiones con tap en card de pieza |
| H2 | Separar agenda de inscripción evita la expectativa de reserva | ↓ “pensé que ya estaba apuntada” |
| H3 | JWT solo en redacción basta; el lector no necesita cuenta | 0 cuentas de lectora; lectura completa sin login |
No se afirman estadísticas de lectura vecinal inventadas. Todo lo anterior es razonamiento de diseño etiquetado.
3. Propuesta de valor
| Para | Valor |
|---|---|
| LECTORA (Elena Vargas) | Ve el número de la semana en un lookbook; lee la crónica; consulta la agenda sin crear cuenta. |
| EDITOR (Nuria Ceballos) | Lista las piezas, escribe una nueva, publica DRAFT → PUBLISHED sin un CMS de 40 tipos. |
| AMPA (Raúl Méndez) | La crónica del cole y la cita de la semana se leen en un sitio; no convierte MARGEN en matrícula. |
| Barrio / semanario | Canal mínimo: número visible + lectura + cierre editorial, sin paywall ni comentarios. |
No es MARGEN
| Excluido | Por qué |
|---|---|
| Paywall / suscripción / cuenta lectora | S2 + H3: llega a leer, no a darse de alta |
| Inscripción / reserva de plaza | Agenda ≠ inscripción (≠ PIZARRA) |
| Tabla de necesidades / compromiso de caja | Eso fue CESTA (H-TABLE / F-LEAD) |
| Feed / cronología de “historias” | Terna H-GALLERY, no H-FEED (eso fue RONDA) |
| Mapa de paseos | Terna no es H-MAP (eso fue DERIVA) |
| Search-first / catálogo facetado | Terna no es H-SEARCH (eso fue PIZARRA) |
| Comentarios, likes, ranking | S4 |
| Tiraje / impresión / kiosco físico | Publicar ≠ imprimir |
| Hero 2-col + 3 cards de “cómo leer” | Anti-patrón AP-12; contradice H-GALLERY |
4. Objetivos
Negocio / caso de estudio
- Demostrar vertical slice L2 editorial gallery-first con Issue + Piece + Event + JWT EDITOR.
- Portfolio coherente: Paper (12 UX + 11 UI) + docs + app runnable.
- Terna R-CL · S-TOP · H-GALLERY · F-STORY frente a CESTA (tabla/rail), RONDA (feed/dock) y DERIVA (mapa).
Usuario
| Rol | Objetivo medible en demo |
|---|---|
| LECTORA | Ver el número y abrir una crónica en < 30 s; llegar a la agenda en 1 tap más |
| EDITOR | Publicar una pieza DRAFT → PUBLISHED en < 2 min desde la mesa |
| AMPA | Leer un evento de la semana sin encontrar un CTA de reserva |
No objetivos v1 (explícitos)
- Paywall, newsletter transaccional, cuenta de lectora
- Comentarios, likes, bookmarks
- Inscripción / cupo / WAITLIST
- Multi-número archivo con búsqueda
- CMS de tipos, taxonomías profundas, workflow de 5 estados
- Impresión / PDF de kiosco / punto de venta
- Multi-editor con roles (redactor / jefe / fotógrafo)
- App nativa ni notificaciones push
5. Roles y permisos (resumen)
| Acción | Público (LECTORA / AMPA) | EDITOR |
|---|---|---|
| Ver lookbook del número PUBLISHED | Sí | Sí |
| Leer pieza PUBLISHED | Sí | Sí |
| Ver pieza DRAFT | No (404) | Sí (mesa) |
| Ver agenda de la semana | Sí | Sí |
| Inscribirse a un evento | No (no existe) | No |
| Login JWT | No (no cuenta lectora) | Sí |
| Listar piezas de mesa | No (401) | Sí |
| Crear pieza | No | Sí |
| Publicar / despublicar | No | Sí |
| Crear Issue | No (seed v1) | No en v1 |
[DECISIÓN] Un solo rol autenticado: EDITOR. La lectora es anónima. AMPA no es un rol de sistema: es persona / stakeholder que usa la cara pública.
6. Métricas (modelo, no instrumentadas en v1 salvo base)
| Tipo | Métrica | Definición |
|---|---|---|
| North Star | % de sesiones que abren ≥1 pieza y consultan /agenda en la misma semana del número | Completud de lectura del número, no pageviews |
| Activación lectora | 1.er tap en card de pieza | H1 |
| Activación editora | 1.er PATCH a PUBLISHED | Cierre de número |
| Expectativa | Reclamaciones “ya estoy apuntada” / “¿cuándo lo imprimís?” | H2 + microcopy |
| Integridad | GET público no devuelve DRAFT | Borrador ≠ publicado |
| Salud | Error rate API issues/pieces/events | 4xx/5xx |
7. Alcance funcional v1 (L2)
| Módulo | Incluido |
|---|---|
| Home lookbook | Número en curso + ≥3 portadas/piezas con lead (recorte 16:10 visible) + masthead |
| Ficha de crónica | Kicker, título, cuerpo, crédito, cover |
| Agenda | 3 eventos reales de seed (título, startsAt, place, audience) |
| Auth | POST /api/auth/login → JWT EDITOR |
| Mesa | GET lista DRAFT + PUBLISHED, PATCH :id { status } |
| Alta | Form + POST /api/pieces + success |
| Estados UI | Empty, loading skeleton, error de red |
| Mobile lector | Misma home y ficha a ~390px |
| Seed | 1 EDITOR, 1 Issue, 4 piezas PUBLISHED, 1 DRAFT, 3 eventos |
8. Criterios de aceptación de producto
- Un visitante puede ver el lookbook del número sin autenticación y abrir una crónica.
- Un visitante puede leer la agenda sin cuenta y sin encontrar un CTA de reserva o inscripción.
- La UI dice explícitamente que publicar no es imprimir (success de mesa y, si cabe, pie de número).
- Sin token, GET
/api/mesa/piecesy POST/PATCH de piezas responden 401. - GET
/api/issues/currentdevuelve el número PUBLISHED con ≥3 piezas publicadas. - GET
/api/pieces/:slugde un DRAFT responde 404 (no 200 + aviso). - Un EDITOR puede iniciar sesión y ver la mesa con DRAFT y PUBLISHED.
- El EDITOR puede crear una pieza y publicarla (
DRAFT→PUBLISHED). - La home es galería: no hay hero 2-col + 3 cards como superficie principal.
- Fotos reales en portada y piezas (assets del case).
- Recorte 16:10 de las portadas es visible (no recorte 1:1 que recorta el lead).
- Rutas públicas y de mesa solo las del doc 03.
9. Stack y artefactos
| Capa | Detalle |
|---|---|
| Frontend | Angular + Tailwind · puerto 4200 |
| Backend | NestJS · puerto 3017 |
| DB | Neon PostgreSQL · project morning-truth-64427459 |
| Auth | JWT (EDITOR) |
| Diseño | Paper 01M078GFW7QQF5RRA2XGKQ8BD9 |
| Repo app | /Users/cristian/orca/margen-app/ · GitHub Criscode2022/margen-app |
[COMPROBADO] Puerto, Neon, Paper file ID, repo y path de app constan en el encargo.
10. Riesgos y mitigaciones
| Riesgo | Impacto | Mitigación v1 |
|---|---|---|
| Expectativa de reserva en agenda | Reclamaciones AMPA / “ya estoy apuntada” | Copy “se lee, no se reserva”; sin botón Inscribirse |
| Expectativa de tiraje | “¿Dónde lo compro?” | Publicar ≠ imprimir en success y pie |
| DRAFT filtrado a home | Crónica a medias pública | GET público filtra status=PUBLISHED; slug DRAFT → 404 |
| Home percibida como landing de revista genérica | Pérdida de craft | Lookbook 16:10 + masthead; no hero 2-col |
| Confundir con PIZARRA (talleres + plaza) | Expectativa de cupo | Event no tiene seats ni form |
| Confundir con CESTA (lista + compromiso) | Expectativa de “traer algo” | No hay form público de captura |
Leftover walks / requests en repo | App enseña otro producto | Contrato de esta suite gana; I01 de build |
| PII de lectora | No aplica v1 | Lectora no deja datos; EDITOR es la única cuenta |
| Número vacío un martes | Abandono | Empty “este número aún no tiene piezas publicadas” |
11. Glosario
| Término | Definición en MARGEN |
|---|---|
| Número | issues: semana cerrada o en curso (weekLabel, cover, status) |
| Pieza / crónica | pieces: texto con kicker, cuerpo, cover, sección |
| Kicker | Antetítulo de sección (Patio, Cole, Barrio, Lectura) |
| Agenda | events de la semana del número; no es inscripción |
| Mesa | Superficie EDITOR: lista + alta + publicar |
| Publicar | Pasar pieza (o número) a PUBLISHED; no es imprimir |
| Borrador | DRAFT; la familia no lo ve |
| LECTORA | Visitante anónima que lee |
| EDITOR | Usuario autenticado que opera la mesa |
| Pliego | Superficie de card #F4F1EA, no el ground |
| Masthead | Shell S-TOP: wordmark + secciones + filete cadmio |
| Lookbook | Home H-GALLERY: portadas como galería, no feed ni tabla |
12. Decisiones de diseño (cierre de brief)
| ID | Decisión | Alternativa descartada |
|---|---|---|
| D1 | Home = lookbook de portadas (H-GALLERY) | Hero 2-col + 3 cards (AP-12) · tabla (CESTA) · feed (RONDA) · mapa (DERIVA) · search (PIZARRA) |
| D2 | Shell masthead de periódico (S-TOP) | Rail de iconos · dock flotante · sidebar CMS · command search |
| D3 | Registro clásico de kiosco (R-CL) | Bookish plaster×ink (primer instinto) · chapel · candlelit |
| D4 | Flujo F-STORY (hojear → leer) | F-LEAD, wizard, kanban, booking, search |
| D5 | Mood gallery / newsstand | Bookish (vetado como primer instinto), botanical CESTA, chrome RONDA |
| D6 | Un rol autenticado EDITOR | Multi-rol lectora / AMPA / fotógrafo / admin |
| D7 | Estados solo DRAFT / PUBLISHED | SCHEDULED / REVIEW / KILLED (CMS enterprise) |
| D8 | Agenda de solo lectura | Form de plaza, cupo, WAITLIST (PIZARRA) |
| D9 | Lectora sin cuenta | Paywall, newsletter obligatoria, comentarios |
| D10 | PATCH /api/pieces/:id { status } | Subruta /publish como único contrato (puede existir alias, no Must) |
| D11 | Ground G-GALLERY #FFFFFF | G-MOSS, G-CHROME, G-WATER, G-MUNICIPAL, G-ICE |
| D12 | Success de publicación en /mesa/ok/:slug | Toast único sin pantalla (L2 pide success board) |
13. Relación con el día anterior
| Case | Terna | Por qué MARGEN no lo copia |
|---|---|---|
| CESTA (N−1) | S-RAIL / H-TABLE / F-LEAD | No ONG, no tabla de necesidades, no compromiso, no rail |
| RONDA (N−2) | S-DOCK / H-FEED / F-BOOK | No gaming, no cronología, no dock, no sillas |
| DERIVA (N−3) | S-SPLIT / H-MAP / F-MAP | No turismo, no plano, no split mapa |
| PIZARRA | S-CMD / H-SEARCH / F-SEARCH | Agenda se lee; no se pide plaza |
[COMPROBADO] El brief fija terna R-CL · S-TOP · H-GALLERY · F-STORY. S / H / F son 0 coincidencias con CESTA (S-RAIL · H-TABLE · F-LEAD). Ventana 4 días ya tenía R-IN (DERIVA) y R-HY (RONDA); hoy R-CL editorial, distinto de CESTA en shell, home y flujo. Ground G-GALLERY #FFFFFF, no G-MOSS.
02-ux-research-strategy.md
02 — Estrategia de investigación UX — MARGEN
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)
| Objetivo | Método en este caso | Salida |
|---|---|---|
| Entender actores del semanario vecinal | Modelado de stakeholders + personas | §3–4 |
| Definir job de “qué hay esta semana” y de cierre de número | JTBD + stories Must | §5 |
| Mapear fricción WhatsApp / PDF → lectura | Journey + service blueprint | §6–7 |
| Traducir a requisitos L2 gallery-first | Matriz hallazgo → requisito → feature | §8 |
2. Fuentes y límites
Fuentes admisibles (secundarias / operativas)
- Conocimiento general de semanarios de barrio, grupos de WhatsApp de AMPA y cartelería de cole / biblioteca / piscina municipal.
- Analogía operativa con lookbook editorial + mesa JWT de la serie daily.
- Restricciones ALS-2: no tabla de leads, no feed, no mapa, no search-first, no inscripción, no paywall.
Límites éticos de verdad
| Prohibido | Permitido |
|---|---|
| “El 71% de madres de Usera no encuentra el PDF del cole” sin fuente | “[SUPUESTO] la lectora llega con ‘qué hay esta semana’” |
| Citas de entrevistas ficticias como campo real | Quotes de persona etiquetadas como constructo de diseño |
| Tirada medida “4.200 lecturas el domingo” como KPI real | North star modelo “sesión que abre pieza + agenda” |
| Horario oficial de una biblioteca o CEIP concreto | Eventos de seed de producto, etiquetados como demo |
| “Nuria existe y nos dijo…” | Nuria Ceballos es persona de diseño |
3. Stakeholders
| Stakeholder | Influencia | Interés | Necesidad principal |
|---|---|---|---|
| LECTORA / familia (Elena) | Baja formal | Muy alta | Ver el número de la semana y leer sin cuenta |
| EDITOR / redacción (Nuria) | Alta | Muy alta | Cerrar piezas y publicar sin CMS enterprise |
| AMPA (Raúl) | Media–alta | Alta | Que la crónica del cole y la cita se vean; no gestionar plazas aquí |
| Niños (Hugo, Inés) | Indirecta | Alta | Que el patio, el vaso y el cuento existan en el número |
| Cole / dirección | Media adopción | Media | No convertir MARGEN en secretaría virtual |
| Biblioteca / vaso municipal | Baja v1 | Media | Aparecer en agenda sin cupo online |
| Kiosco / imprenta | Baja v1 | Baja | Fuera: publicar ≠ imprimir |
| WhatsApp del grupo | Competencia / canal paralelo | — | No clonar el chat; sí sustituir el PDF perdido |
Mapa de poder (resumen)
- Decisor de adopción: la editora (quien cierra el número) y, en difusión, el AMPA (quien pega el enlace).
- Usuario frecuente de lectura: LECTORA (lookbook + ficha + agenda).
- Usuario frecuente de publicación: EDITOR (mesa).
- Riesgo de rechazo: si la home no enseña portadas en el primer viewport; si la agenda parece un formulario de plaza; si un DRAFT se filtra.
4. Personas
P1 — Elena Vargas · LECTORA
| Campo | Detalle |
|---|---|
| Edad / contexto | 38 años; Usera; dos hijos (Hugo 11, Inés 7); el móvil es el kiosco |
| Digital | Media; lee en el sofá o en la cola del cole; WhatsApp todo el día; evita “suscríbete para seguir leyendo” |
| Goals | Saber qué hay esta semana, leer una crónica de un tirón, mirar la agenda del sábado |
| Pains | Tres PDFs del cole, el cartel de la biblioteca que nadie fotografía bien, meriendas que se entera el lunes |
| Quote de diseño | “Dime qué hay esta semana. No me hagas una cuenta.” |
| Seed demo | Sin email de producto; llega por enlace del grupo |
Escenario: Abre MARGEN, ve El patio no cierra en agosto, entra a La merienda que no cabe en el grupo, pasa a Agenda, anota Pradolongo el sábado. No se registra.
[DECISIÓN] Elena no tiene cuenta. Crear usuario de lectora sería fricción y contradice H3 / S2.
P2 — Nuria Ceballos · EDITOR
| Campo | Detalle |
|---|---|
| Edad / contexto | 44 años; redactora del semanario; cierra 4–8 piezas entre jueves y domingo |
| Digital | Media–alta; desktop para escribir; móvil para revisar la home publicada |
| Goals | Ver el estado de cada pieza, escribir una nueva, publicar sin “programar newsletter” |
| Pains | Drive + WhatsApp + un Word por crónica; miedo a publicar un borrador; nadie entiende que digital ≠ kiosco |
| Quote de diseño | “Si está en DRAFT, la familia no lo ve. Si lo publico, no lo estoy mandando a imprenta.” |
| Email demo | redaccion@margen.barrio / password123 |
Escenario: Login → mesa → ve Lo que el AMPA no va a colgar en Stories en DRAFT → termina → Publicar → success con aviso de que no es el tiraje.
[COMPROBADO] Credencial demo sale del encargo. Nombre y edad son constructo de diseño. [DECISIÓN]
P3 — Raúl Méndez · AMPA
| Campo | Detalle |
|---|---|
| Edad / contexto | 42 años; Carabanchel; tesorero informal de un AMPA de CEIP; pega enlaces al grupo |
| Digital | Media; WhatsApp y Drive; desconfía de “plataformas educativas” con matrícula |
| Goals | Que la charla de vuelta al cole se lea; que la crónica de la puerta se vea el domingo |
| Pains | Cada cita se convierte en “¿cómo me apunto?”; el PDF se pisa con otra versión |
| Quote de diseño | “Que se lea. La lista de firmas la hago yo en el patio, no aquí.” |
Escenario: Abre Agenda, ve Charla de vuelta al cole el lunes 18:00, copia el dato al grupo. No hay botón Inscribirse.
[DECISIÓN] Raúl no es rol de sistema. No hay login AMPA en L2. Evita convertir MARGEN en PIZARRA.
Anti-personas
| Quién | Por qué no es target v1 |
|---|---|
| Suscriptora de paywall / “socios del papel” | S2: no hay cuenta ni cobro |
| Community manager que quiere comentarios y likes | S4; rompe F-STORY |
| Coordinadora municipal de talleres que gestiona cupo | Eso es PIZARRA |
| Vecina de CESTA que pregunta “qué falta en la despensa” | Otro job, otro día |
| Jefe de producto de CMS enterprise (40 tipos, workflow) | S3: mesa de 1–2 |
5. JTBD y user stories
Job principal (LECTORA)
Cuando se acaba la semana y el grupo del cole echa humo,
quiero ver el número y leer qué pasa en el patio, el cole y el barrio,
para no perseguir el PDF ni enterarme el lunes de la merienda.
Job principal (EDITOR)
Cuando cierro el número del domingo,
quiero listar las piezas, escribir la que falta y publicarla,
para que la familia vea solo lo cerrado y yo no confunda borrador con kiosco.
Job principal (AMPA)
Cuando hay una cita de la semana (charla, merienda, cuento),
quiero que se lea en la agenda del número,
para difundirla sin que MARGEN se convierta en hoja de inscripción.
Jobs secundarios
| Job | Rol |
|---|---|
| Distinguir número en curso de un DRAFT | Ambos |
| Leer una crónica de un tirón en el móvil | LECTORA |
| Ver crédito y kicker (quién escribe, de qué sección) | LECTORA |
| Publicar sin mandar a imprenta | EDITOR |
| Ocultar un borrador a la home | EDITOR |
| Compartir un evento por texto (día, sitio, público) | AMPA |
Stories Must (v1)
| ID | Story | AC |
|---|---|---|
| US1 | Como lectora, quiero ver el número y sus portadas | GET /api/issues/current; ≥3 cards 16:10 con lead |
| US2 | Como lectora, quiero abrir la crónica con kicker, título, cuerpo y crédito | GET :slug; foto real; autor |
| US3 | Como lectora, quiero ver la agenda de la semana | GET /api/events; 3 eventos; sin CTA de reserva |
| US4 | Como lectora, no quiero crear cuenta | Cero forms de registro en cara pública |
| US5 | Como EDITOR, quiero entrar con email/password | JWT + redirect /mesa |
| US6 | Como EDITOR, quiero listar DRAFT y PUBLISHED | GET /api/mesa/pieces JWT |
| US7 | Como EDITOR, quiero crear una pieza | POST /api/pieces → DRAFT |
| US8 | Como EDITOR, quiero publicarla | PATCH { status: "PUBLISHED" } + success |
| US9 | Como cualquiera, quiero empty / error / loading | UI-08, UI-09 |
| US10 | Como lectora en móvil, quiero leer a ~390px | UI-10; cuerpo ≥16px; targets ≥44px |
MoSCoW (v1 L2)
| Prioridad | Ítems |
|---|---|
| Must | Lookbook, ficha, agenda 3 eventos, login, mesa, alta, success, empty/loading/error, mobile lector |
| Should | Badge DRAFT/PUBLISHED en mesa; pie “publicar ≠ imprimir”; kicker visible en card |
| Could | Copiar enlace de pieza; despublicar a DRAFT; ordenar mesa por updatedAt |
| Won’t | Paywall, comentarios, inscripción, archivo search, impresión, multi-editor |
6. Journey (LECTORA → EDITOR)
| Fase | Actor | Acción | Touchpoint | Emoción [HIPÓTESIS] |
|---|---|---|---|---|
| 1 Hojear | Elena | Abre MARGEN; ve el número y las portadas | Home lookbook + masthead | Claridad, “esto es un semanario” |
| 2 Elegir | Elena | Toca La merienda que no cabe en el grupo | Card 16:10 → ficha | Reconocimiento del patio |
| 3 Leer | Elena | Kicker Patio, cuerpo, crédito Nuria | /piezas/merienda-en-el-banco | Calma de lectura |
| 4 Consultar | Elena | Abre Agenda, anota Pradolongo sábado 11:00 | /agenda | Control (sin “apuntarme”) |
| 5 Compartir | Raúl | Copia el dato al grupo | Fuera de app | Alivio |
| 6 Cerrar | Nuria | Login, ve DRAFT, publica | /mesa → /mesa/nueva → ok | Oficio, no euforia de “impreso” |
| 7 Comprobar | Nuria | Abre home en incógnito: el DRAFT no está | / pública | Confianza |
Momentos de verdad
- Portadas en el primer viewport — si hay que “descubrir la marca”, se rompe S2. [HIPÓTESIS]
- Agenda sin botón de reserva — si aparece “Inscribirse”, H2 falla.
- DRAFT invisible en público — prueba de “borrador ≠ publicado”.
- Success de mesa dice que no es imprimir — evita la mentira del tiraje.
- Masthead no esconde Agenda — Elena no busca un hamburger de 9 ítems. [HIPÓTESIS]
7. Service blueprint (resumen)
| Capa | Elementos |
|---|---|
| Frontstage LECTORA | Masthead, lookbook, ficha, agenda |
| Frontstage EDITOR | Login, mesa, alta, success |
| Backstage | Cierre de número, foto del patio, mensaje del AMPA al grupo (fuera de app) |
| Sistemas | Nest API :3017, Neon morning-truth-64427459, JWT, Angular galería |
| Soportes | Seed 1 número + 4 piezas + 3 eventos + fotos, Paper, docs |
| Fallos | 0 piezas publicadas; 401 sin token; 404 slug DRAFT; red caída → UI error |
Fallos de servicio y respuesta de diseño
| Fallo | Evidencia de UI | Recuperación |
|---|---|---|
| Número sin piezas publicadas | UI-08 Empty lookbook | “Este número aún no tiene piezas publicadas.” |
| Agenda vacía | UI-08 Empty agenda | “Esta semana no hay citas en la agenda.” |
| Mesa vacía | UI-08 Empty mesa | “Aún no hay piezas. Escribe la primera.” |
| API caída | UI-08 Error | Reintentar |
| Latencia lookbook | UI-09 Loading | Skeleton de portada + 3 cards |
| Form inválido | Inline field | No navegar a success |
| Intento de leer DRAFT por URL | 404 | “Esa crónica no está publicada.” [H3 / integridad] |
8. Matriz hallazgo → requisito → feature
| Hallazgo | Tipo | Requisito | Feature v1 |
|---|---|---|---|
| Llega con “qué hay esta semana” | [SUPUESTO] S2 | Lookbook es la home | UI-01 / H-GALLERY |
| PDF / WhatsApp no muestran portada | [HIPÓTESIS] H1 | Cards 16:10 con lead | GET current + covers |
| Agenda se lee como matrícula | [HIPÓTESIS] H2 | Cero CTA de reserva | UI-03 solo lectura |
| Borrador se filtra | [SUPUESTO] | Público = PUBLISHED | 404 en DRAFT |
| Digital ≠ kiosco | [DECISIÓN] | Copy irrenunciable | Success + pie |
| Redacción 1–2 personas | [SUPUESTO] S3 | Mesa = lista, no CMS | UI-05 / UI-06 |
| Lector no necesita cuenta | [HIPÓTESIS] H3 | Auth solo EDITOR | Login + guards API |
| L2 compacto, no enterprise | [DECISIÓN] | Un rol EDITOR; Issue seed | Sin cuenta LECTORA, sin CMS de tipos |
9. Preguntas abiertas (no bloquean v1)
| ID | Pregunta | Cómo se resolvería después |
|---|---|---|
| Q1 | ¿Archivo de números anteriores en home o en /archivo? | L+1: índice por weekLabel; no search-first |
| Q2 | ¿Un EDITOR o varios con byline distinto? | authorUserId; v1 usa authorName libre |
| Q3 | ¿Checkbox de “he leído que no es inscripción”? | Innecesario si no hay form; legal L+1 si hubiera captura |
| Q4 | ¿Despublicar vuelve a DRAFT o hay UNLISTED? | [DECISIÓN v1] PATCH a DRAFT basta |
| Q5 | ¿El Event pertenece al Issue por FK? | Sí en modelo (issueId); GET /api/events filtra el current |
| Q6 | ¿Horarios de piscina / biblioteca son oficiales? | No. Seed demo. No se afirma como calendario municipal |
10. Plan de research futuro (si hubiera campo real)
| Método | Muestra orientativa | Pregunta |
|---|---|---|
| Test de usabilidad lookbook | 5–6 madres/padres 30–50 | ¿Encuentran “qué hay esta semana” en < 20 s? |
| Shadowing de cierre | 1 redacción 1–2 personas | ¿La mesa sustituye Drive + WhatsApp? |
| Revisión de expectativa de reserva | Clics en agenda vs “¿cómo me apunto?” | Validar H2 |
| Lectura mobile 390 | 5 sesiones | ¿El cuerpo de crónica se lee de un tirón? |
Estos métodos no se han ejecutado. No se reportan hallazgos como si lo hubieran sido.
11. Síntesis
MARGEN se diseña como semanario de barrio que se hojea y se publica, no como paywall, no como inscripción municipal y no como tabla de compromisos.
La investigación del caso es constructiva y etiquetada: personas, JTBD y journey son artefactos de diseño; las métricas de validación quedan para uso real futuro.
03-information-architecture.md
03 — Arquitectura de información — MARGEN
1. Principios de IA
| Principio | Aplicación |
|---|---|
| Gallery first | La home es el lookbook del número. No hay landing de marca por delante. |
| Hojear → leer → agenda | Profundidad 2 desde la card hasta la crónica; agenda es hermana, no modal |
| Público vs EDITOR | Lookbook, ficha y agenda abiertos; mesa solo autenticada |
| Lenguaje de dominio | Número, pieza, kicker, agenda, mesa — no “post CMS”, “lead”, “SKU”, “plaza” |
| Borrador invisible | La familia no ve DRAFT; 404 si adivina el slug |
| Agenda se lee | Evento no tiene cupo, botón ni form (≠ PIZARRA) |
| Masthead siempre a mano | S-TOP: wordmark + secciones; no rail, no dock |
2. Sitemap
/ Lookbook del número (público) H-GALLERY
/piezas/:slug Ficha de crónica (público si PUBLISHED)
/agenda Agenda familiar de la semana (público)
/login Login JWT EDITOR
/mesa Lista de piezas (auth EDITOR)
/mesa/nueva Alta de pieza (auth EDITOR)
/mesa/ok/:slug Success post-publicación (auth EDITOR)
/** → redirect /
Árbol por audiencia
| Audiencia | Nodos relevantes |
|---|---|
| LECTORA / AMPA | Lookbook → Crónica → Agenda |
| EDITOR | Login → Mesa → Nueva → Ok (y lookbook público para comprobar) |
| Ambos | Wordmark → / (EDITOR autenticada: Mesa en masthead) |
[COMPROBADO] Siete rutas del encargo L2. No se inventan /inscribir, /suscribir, /comentarios, /archivo, /mesa/:id.
3. Navegación
Masthead (S-TOP)
| Slot | Destino | Visibilidad | Notas |
|---|---|---|---|
| Wordmark MARGEN | / | Todos | Cormorant; no logotipo ilustrado |
| Número | / | Todos | Estado activo en home |
| Agenda | /agenda | Todos | Citas de la semana |
| Mesa | /mesa | EDITOR; si guest → /login?next=/mesa | Lista operativa |
| Entrar / Salir | /login si guest; acción salir si auth | Todos | Acceso EDITOR |
[DECISIÓN] El masthead es la única navegación primaria. Tres destinos públicos + mesa. Encaja L2 compacto. No hay rail (CESTA), no dock (RONDA), no command search (PIZARRA).
Desktop ≥960px: cabecera horizontal, filete cadmio 2px bajo el masthead, secciones en Public Sans 14.
Mobile ~390px: wordmark + 2–3 links; Mesa se compacta a “Mesa” o icono de escritorio. [DECISIÓN] En 390 no se introduce hamburger de 9 ítems.
Pública (además del masthead)
| Elemento | Destino | Notas |
|---|---|---|
| Portada del número | / (ancla al grid) o primera pieza | Toda la portada 16:10 es clicable hacia la pieza lead o se queda como atmósfera del número |
| Card de pieza | /piezas/:slug | Cover + kicker + título + lead |
| Enlace “Agenda de la semana” | /agenda | En home, bajo el grid |
| Crédito / byline | no navega | Texto |
| Evento | no navega a form | Bloque de lectura |
[DECISIÓN] La portada del número no abre un /numeros/:id en v1: el número es la home. Un archivo de números sería L+1.
EDITOR (autenticado)
| Elemento | Destino | Visibilidad |
|---|---|---|
| Mesa | /mesa | EDITOR |
| Nueva pieza | /mesa/nueva | EDITOR |
| Badge rol | — | EDITOR |
| Nombre | Nuria Ceballos | EDITOR |
| Salir | limpia token → /login | EDITOR |
| Publicar | en form o en fila de mesa | EDITOR · no hay /mesa/:id de ficha CMS |
[DECISIÓN] L2: el PATCH vive en la fila de la mesa y/o al final del form de alta. No se añade /mesa/:id como Must.
4. Inventario de contenido
| Pantalla | Contenidos |
|---|---|
Lookbook / | Masthead, weekLabel, título del número, portada 16:10, grid ≥3 piezas (cover, kicker, título, lead), enlace agenda, empty, loading, error |
| Ficha | Cover, kicker, título, byline, cuerpo, volver al número, enlace agenda |
| Agenda | Titular de semana, 3 eventos (título, día/hora, lugar, público), disclaimer “se lee, no se reserva” |
| Login | Titular redacción, email, password, submit, error |
| Mesa | Lista: kicker, título, status, updatedAt, acciones, CTA Nueva, empty, error |
| Alta | Campos título, kicker, sección, cover (select seed), cuerpo, submit “Guardar borrador” + “Publicar” |
| Success | Mensaje, slug, “Publicar no es imprimir”, CTA ver pieza / volver a mesa |
5. Taxonomía
PublishStatus (Issue y Piece)
| Status API | Label UI | Semántica |
|---|---|---|
DRAFT | Borrador | La familia no lo ve |
PUBLISHED | Publicado | Visible en lookbook y ficha |
Orden de mesa: updatedAt descendente (lo último tocado primero).
[DECISIÓN] No hay SCHEDULED / REVIEW / KILLED: S3, mesa de 1–2.
PieceSection
| API | Label UI (kicker) | Seed |
|---|---|---|
PATIO | Patio | merienda-en-el-banco |
COLE | Cole | puerta-del-cole, lo-que-el-ampa-no-cuelga |
BARRIO | Barrio | vaso-de-agosto |
LECTURA | Lectura | silencio-y-patio |
Issue (línea seed)
| weekLabel | Título | Cover | Status |
|---|---|---|---|
17–23 ago | El patio no cierra en agosto | hero.jpg | PUBLISHED |
Piece (líneas seed)
| slug | Sección | Título | Status |
|---|---|---|---|
merienda-en-el-banco | PATIO | La merienda que no cabe en el grupo | PUBLISHED |
puerta-del-cole | COLE | El cole no ha abierto y el grupo ya está en llamas | PUBLISHED |
vaso-de-agosto | BARRIO | La piscina de Orcasitas aguanta agosto | PUBLISHED |
silencio-y-patio | LECTURA | La biblioteca no es un PDF | PUBLISHED |
lo-que-el-ampa-no-cuelga | COLE | Lo que el AMPA no va a colgar en Stories | DRAFT |
[COMPROBADO] Cuatro piezas + agenda de 3 y un DRAFT cubren el must del brief (demo: 1 número + 4 piezas + 3 eventos). El DRAFT extra es para que la mesa no sea una lista de solo publicados. [DECISIÓN]
EventAudience (texto libre en v1; valores seed)
| Valor seed | Uso |
|---|---|
| Familias | Merienda Pradolongo |
| Madres y padres | Charla vuelta al cole |
| 4 a 8 años | Hora del cuento |
No es un enum cerrado de producto: es copy. [DECISIÓN]
6. Modelo mental vs UI
| Modelo mental | Representación |
|---|---|
| “¿Qué hay esta semana?” | Home lookbook, título del número + grid |
| “La de la merienda” | Card PATIO → ficha |
| “¿A qué hora es lo del sábado?” | /agenda |
| “¿Me apunto?” | No hay UI. Copy: se lee, no se reserva |
| “Lo estoy escribiendo” | Badge Borrador en mesa |
| “Ya salió” | Badge Publicado; aparece en / |
| “¿Sale en papel?” | Success / pie: publicar ≠ imprimir |
| “El PDF del cole” | Sustituido por la pieza COLE, no por un adjunto |
7. Query string
L2 gallery-first no usa facetas de catálogo.
Único query admitido: /login?next=/mesa (redirect post-login).
[DECISIÓN] Sin ?q= ni chips de sección en home. Eso sería H-SEARCH (PIZARRA) o un archivo L+1.
8. Rutas API alineadas a IA
| UI | API |
|---|---|
| Lookbook | GET /api/issues/current |
| Ficha | GET /api/pieces/:slug |
| Agenda | GET /api/events |
| Login | POST /api/auth/login |
| Mesa list | GET /api/mesa/pieces JWT |
| Alta | POST /api/pieces JWT |
| Publicar / despublicar | PATCH /api/pieces/:id { status } JWT |
[COMPROBADO] Contrato del encargo (Issue, Piece, Event, EDITOR). No se documentan comentarios, likes ni POST /api/events como Must.
9. Decisiones de IA descartadas
| Idea | Por qué no en L2 v1 |
|---|---|
/explorar + lookbook secundario | Rompe H-GALLERY |
| Área “mi revista” / bookmarks | Cuenta de facto; contradice H3 |
/mesa/:id ficha CMS | L2 compacto; acciones en lista + form de alta |
| Search / chips de sección | H-SEARCH de PIZARRA |
| Mapa de meriendas | H-MAP de DERIVA |
| Feed de “últimas del barrio” | H-FEED de RONDA |
| Tabla de piezas como home | H-TABLE de CESTA |
| Wizard de 4 pasos de publicación | F-ONB; el form es 1 pantalla |
/inscribir/:eventId | Rompe H2 / microcopy Agenda ≠ inscripción |
/numeros/:week archivo | L+1; el número en curso es la home |
04-user-flows.md
04 — Flujos de usuario — MARGEN
Convenciones
- Actor: Guest (LECTORA / AMPA) | EDITOR
- Éxito: resultado observable
- Errores: UI + código HTTP cuando aplica
- Flujo canónico: F-STORY (hojear número → leer crónica → agenda)
- Flujo de mesa: login → lista → alta → publicar
F1 — Descubrimiento por lookbook (LECTORA)
/ → lookbook del número (H-GALLERY)
→ GET /api/issues/current
→ portada 16:10 + ≥3 cards | empty | error | skeleton
→ click card → /piezas/:slug
→ masthead Agenda → /agenda
→ masthead Mesa / Entrar → /login (si guest)
| Paso | Acción | Sistema |
|---|---|---|
| 1 | Aterriza; la portada del número (weekLabel + cover 16:10) es el primer foco | Render home |
| 2 | Recorre kicker, título y lead de cada pieza | Issue + pieces PUBLISHED |
| 3 | Abre crónica | Router |
| 4 | Masthead permanece visible | Shell S-TOP |
Éxito: al menos una ficha PUBLISHED alcanzable, o empty accionable.
AC: no hay que hacer scroll de “marca” para llegar a la primera portada. El recorte 16:10 deja ver el lead fotográfico (no un crop 1:1 que recorta cabezas).
Errores / estados
| Caso | Comportamiento |
|---|---|
| 0 piezas publicadas | UI-08: “Este número aún no tiene piezas publicadas.” |
| Red / 5xx | UI-08 + Reintentar |
| Primera carga | UI-09 skeleton de portada + 3 cards |
F2 — Leer crónica (LECTORA)
/piezas/:slug → GET /api/pieces/:slug
→ cover, kicker, título, cuerpo, crédito
→ enlace Número → /
→ enlace Agenda → /agenda
→ 404 slug DRAFT o inexistente → mensaje + volver al número
| Paso | Acción | Sistema |
|---|---|---|
| 1 | Lee kicker y título | Piece |
| 2 | Lee cuerpo de un tirón | Piece.body |
| 3 | Ve crédito (authorName) | Piece |
| 4 | Decide otra pieza, agenda o home | Router |
Éxito: crónica leíble; crédito visible; no hay comentarios ni likes.
Error: 404 si slug inexistente o status=DRAFT.
AC ficha
| # | Criterio |
|---|---|
| 1 | Foto real del cover (no color sólido) |
| 2 | Kicker (Patio / Cole / Barrio / Lectura) sobre el título |
| 3 | Título en Cormorant; cuerpo en Public Sans ≥16px |
| 4 | Crédito “Nuria Ceballos” (o authorName) visible |
| 5 | Sin CTA de inscripción, like o “suscríbete para seguir” |
[DECISIÓN] Un DRAFT nunca hace 200 en esta ruta. No se “previsualiza” en público ni con query ?preview=. Preview = L+1 con token.
F3 — Agenda de la semana (LECTORA / AMPA)
/agenda → GET /api/events
→ 3 bloques (título, startsAt, place, audience)
→ disclaimer “Esto se lee; no es una inscripción.”
→ empty / error / skeleton
| Paso | Acción | Sistema |
|---|---|---|
| 1 | Lee la semana del número | Issue.weekLabel + events |
| 2 | Recorre día, lugar, público | Event |
| 3 | Anota fuera de app (grupo, papel) | Fuera |
Éxito: 3 eventos visibles o empty honesto. Cero botones Inscribirse / Reservar / Apuntarme.
AC agenda
| # | Criterio |
|---|---|
| 1 | Cada evento muestra título, fecha-hora, lugar, audiencia |
| 2 | Disclaimer visible sobre el fold en desktop |
| 3 | No hay <form> ni CTA primario de captura |
| 4 | Horarios seed etiquetados como del número, no como “oficial Ayuntamiento” |
[DECISIÓN] Tap en evento no abre ficha de reserva. Si en L+1 hubiera detalle, seguiría siendo lectura.
F4 — Login JWT (EDITOR)
/login → POST /api/auth/login { email, password }
→ 200 { accessToken, user } → localStorage → /mesa (o ?next sanitizado)
→ 401 → mensaje error en form
| Campo | Validación cliente | API |
|---|---|---|
| required, email | @IsEmail() | |
| password | required, min 6 | @MinLength(6) |
Éxito: token guardado; user role=EDITOR, nombre Nuria Ceballos.
Credencial demo: redaccion@margen.barrio / password123.
| Caso | Comportamiento |
|---|---|
| Credenciales inválidas | 401 + mensaje |
| Red caída | Error de red en UI |
| Token caducado en mesa | 401 en GET → re-login |
F5 — Mesa de redacción (EDITOR)
/mesa (token en cliente)
→ GET /api/mesa/pieces
→ Render filas + badges DRAFT / PUBLISHED + CTA Nueva
| Caso | Comportamiento |
|---|---|
| Lista con items | Filas: kicker, título, status, updatedAt, acción Publicar si DRAFT |
| Lista vacía | Empty: “Aún no hay piezas. Escribe la primera.” |
| Fallo red / 401 | Error + reintentar / re-login |
[DECISIÓN] Sin stats endpoint en v1. Recuentos DRAFT/PUBLISHED, si se muestran, se derivan del array.
F6 — Alta de pieza (EDITOR)
/mesa/nueva
→ validación cliente
→ POST /api/pieces {
title,
kicker,
body,
cover,
section,
issueId, // default: current issue
authorName // default: user.name
}
→ 201 Piece { slug, status: DRAFT, ... }
→ o bien POST + PATCH { status: "PUBLISHED" } si elige Publicar
→ /mesa/ok/:slug
| Campo | Validación cliente (mín.) | API |
|---|---|---|
| title | required, min 4 | @MinLength(4) |
| kicker | required, min 2 | @MinLength(2) |
| body | required, min 40 | @MinLength(40) |
| cover | required, filename seed o URL relativa | allowlist de assets |
| section | required, enum | PATIO | COLE | BARRIO | LECTURA |
| issueId | default current | debe existir |
| authorName | default user.name | @MinLength(2) |
| status | no en POST; siempre DRAFT al crear | servidor fuerza DRAFT |
Éxito: registro status=DRAFT (o PUBLISHED si eligió Publicar en el mismo viaje) + pantalla success.
No hay cuenta de lectora en v1.
Errores
| Caso | Comportamiento |
|---|---|
| Validación DTO | 400 + mensaje de campo |
| issueId inexistente | 400 / 404 |
| Red caída | Error de red en form; no navegar a /mesa/ok/… |
| Doble submit | Botón disabled mientras pending |
| body < 40 | 400 |
| Sin token | 401 |
[DECISIÓN] Crear siempre como DRAFT. El botón Publicar del form hace POST + PATCH (o un flag publishNow aceptado por el servidor que no salta el estado intermedio en disco: persiste PUBLISHED, pero el contrato documentado es POST DRAFT → PATCH PUBLISHED). [DECISIÓN de implementación] ver doc 20.
[COMPROBADO] Entidades Piece = { title, kicker, body, cover, section, issueId, status, authorName } según encargo.
F7 — Publicar desde la mesa (EDITOR)
/mesa
→ PATCH /api/pieces/:id { status: "PUBLISHED" }
→ 200 Piece actualizado
→ opcional: /mesa/ok/:slug si se quiere el board de success
Transiciones (máquina simple L2)
| Desde | Hacia | Intención |
|---|---|---|
| DRAFT | PUBLISHED | Nuria cierra la crónica; entra al lookbook |
| PUBLISHED | DRAFT | Corrección / retirada temporal |
| — | SCHEDULED / REVIEW | No en v1 |
Éxito: badge actualizado; la pieza aparece en GET /api/issues/current.
Errores: 404 id; 401 sin token; 400 status inválido.
Regla de integridad (ops): Publicar no crea un PDF, un tiraje ni un Event. Publicar ≠ imprimir. Agenda ≠ inscripción.
F8 — Success de publicación (EDITOR)
/mesa/ok/:slug → copy de acuse + título/slug
→ “Publicar no es imprimir.”
→ “La familia ya puede leerla. El kiosco no ha tirado nada.”
→ CTA ver pieza pública / volver a la mesa
| Paso | Acción | Sistema |
|---|---|---|
| 1 | Lee acuse | UI |
| 2 | Comprueba la pieza en incógnito (Could) | Fuera / otra pestaña |
| 3 | Sigue con la siguiente DRAFT | /mesa |
AC: el disclaimer es visible sin scroll en desktop.
Si se entra a /mesa/ok/algo-que-no-existe: mensaje genérico + volver a mesa; no se inventa un registro. [DECISIÓN]
F9 — Logout (EDITOR)
Click "Salir" (masthead) → limpia localStorage (token + user) → /login
Matriz de errores global
| Código | Cuándo | UI |
|---|---|---|
| 400 | DTO inválido | Mensaje campo / genérico |
| 401 | Sin/mal token en mesa | Re-login |
| 404 | slug DRAFT, id o slug no existe | Mensaje + volver al número |
| 5xx / network | API caída | Error + retry |
Flujos fuera de alcance v1
- Registro / recuperación de password de EDITOR
- Cuenta de lectora / bookmarks
- Inscripción a Event
- Notificación email “ha salido el número”
- Filtros multi-criterio en mesa
- Detalle
/mesa/:id - Archivo de números
- Comentarios / likes
- Impresión / PDF de kiosco
05-data-model.md
05 — Modelo de datos — MARGEN
1. Visión general
Dominio L2 de semanario vecinal + mesa de publicación:
| Entidad | Tabla SQL | Propósito |
|---|---|---|
| User | users | Identidad de redacción (rol EDITOR) |
| Session | sessions | Sesión JWT / token persistido (Should) |
| Issue | issues | Número semanal (portada, semana, estado) |
| Piece | pieces | Crónica ligada a un número |
| Event | events | Cita de agenda familiar ligada a un número |
Base: PostgreSQL (Neon project morning-truth-64427459) · IDs: cuid() o entero serial.
[DECISIÓN] ensureSchema o Prisma son válidos si el contrato de tablas se cumple; el brief de stack no exige Prisma.
2. Enums (aplicación; persistidos como TEXT + check)
Role
| Valor | Descripción |
|---|---|
EDITOR | Operadora de la mesa; único rol autenticado v1 |
PublishStatus
| Valor | Descripción |
|---|---|
DRAFT | Invisible en cara pública |
PUBLISHED | Visible en lookbook / ficha / (si Issue) es el current |
PieceSection
PATIO | COLE | BARRIO | LECTURA
3. Diagrama ER (texto)
users
id, email, password_hash, name, role(EDITOR)
created_at, updated_at
1 ──< sessions
sessions
id, user_id → users
token_hash, expires_at
created_at
issues
id, week_label (unique), title, cover, published_at
status (DRAFT | PUBLISHED)
created_at, updated_at
1 ──< pieces
1 ──< events
pieces
id, slug (unique)
issue_id → issues
title, kicker, body, cover
section (PATIO | COLE | BARRIO | LECTURA)
status (DRAFT | PUBLISHED)
author_name
created_at, updated_at
events
id
issue_id → issues
title, starts_at, place, audience
created_at, updated_at
No hay FK entre
piecesyusersen v1: el byline esauthor_nametexto. EDITOR opera sobre el conjunto global.
4. Tablas
users
| Campo | Tipo | Constraints |
|---|---|---|
| id | TEXT | PK |
| TEXT | UNIQUE NOT NULL | |
| password_hash | TEXT | NOT NULL (bcrypt) |
| name | TEXT | NOT NULL |
| role | TEXT | NOT NULL DEFAULT 'EDITOR' |
| created_at | TIMESTAMPTZ | DEFAULT now() |
| updated_at | TIMESTAMPTZ | DEFAULT now() |
sessions
| Campo | Tipo | Constraints |
|---|---|---|
| id | TEXT | PK |
| user_id | TEXT | NOT NULL FK → users(id) ON DELETE CASCADE |
| token_hash | TEXT | NOT NULL |
| expires_at | TIMESTAMPTZ | NOT NULL |
| created_at | TIMESTAMPTZ | DEFAULT now() |
[DECISIÓN] El JWT viaja en Authorization: Bearer. sessions permite invalidar (logout) sin esperar expiración. Si el slice usa JWT stateless, documentar el hueco; D-P1-05 se cumple igual con guard.
issues
| Campo | Tipo | Constraints |
|---|---|---|
| id | TEXT | PK |
| week_label | TEXT | UNIQUE NOT NULL (17–23 ago) |
| title | TEXT | NOT NULL |
| cover | TEXT | filename (hero.jpg) |
| published_at | TIMESTAMPTZ | NULL si DRAFT |
| status | TEXT | NOT NULL DEFAULT 'DRAFT' |
| created_at / updated_at | TIMESTAMPTZ |
[DECISIÓN] GET /api/issues/current = el Issue PUBLISHED con published_at más reciente. En demo hay uno.
pieces
| Campo | Tipo | Constraints |
|---|---|---|
| id | TEXT | PK |
| slug | TEXT | UNIQUE NOT NULL, kebab-case |
| issue_id | TEXT | FK Issue NOT NULL |
| title | TEXT | NOT NULL |
| kicker | TEXT | NOT NULL |
| body | TEXT | NOT NULL (markdown simple o HTML escapado) |
| cover | TEXT | filename (merienda.jpg …) |
| section | TEXT | NOT NULL (PATIO | COLE | BARRIO | LECTURA) |
| status | TEXT | NOT NULL DEFAULT 'DRAFT' |
| author_name | TEXT | NOT NULL |
| created_at / updated_at | TIMESTAMPTZ |
lead no se persiste: se deriva del primer párrafo de body (~160 caracteres) para las cards. [DECISIÓN]
events
| Campo | Tipo | Constraints |
|---|---|---|
| id | TEXT | PK |
| issue_id | TEXT | FK Issue NOT NULL |
| title | TEXT | NOT NULL |
| starts_at | TIMESTAMPTZ | NOT NULL |
| place | TEXT | NOT NULL |
| audience | TEXT | NOT NULL |
| created_at / updated_at | TIMESTAMPTZ |
Sin seats, enrolled, status. Agenda ≠ inscripción. [COMPROBADO]
5. Reglas de integridad y negocio
| Regla | Implementación |
|---|---|
| Lectura pública de número | GET /api/issues/current sin guard; solo PUBLISHED + pieces PUBLISHED |
| Lectura pública de pieza | GET /api/pieces/:slug 200 solo si PUBLISHED; DRAFT → 404 |
| Agenda pública | GET /api/events del current issue; sin form |
| List / create / patch piezas | JWT EDITOR → 401 sin token |
| Slug único | kebab del título + sufijo -2 si colisión [DECISIÓN] |
| Status default pieza | DRAFT al crear |
| Password | Nunca en claro; solo password_hash |
| Orden lookbook | estable: seed order o created_at asc (el número se hojea, no se “destaca” por likes) |
| Orden mesa | updated_at desc |
| Orden agenda | starts_at asc |
| Publicar Issue | no Must de UI v1 (Issue va seed PUBLISHED) |
| Publicar Piece | no crea Event ni PDF |
| Público nunca lista DRAFT | GET current filtra; no hay ?includeDrafts |
6. Contratos API (resumen)
GET /api/issues/current (público)
Response 200: Issue + pieces: PieceCard[] (solo PUBLISHED):
{
"id": "issue_w33",
"weekLabel": "17–23 ago",
"title": "El patio no cierra en agosto",
"cover": "hero.jpg",
"publishedAt": "2026-08-17T05:00:00.000Z",
"status": "PUBLISHED",
"pieces": [
{
"slug": "merienda-en-el-banco",
"title": "La merienda que no cabe en el grupo",
"kicker": "Patio",
"section": "PATIO",
"cover": "merienda.jpg",
"lead": "Familias se sientan en el banco de piedra…",
"authorName": "Nuria Ceballos",
"status": "PUBLISHED"
}
]
}
JSON camelCase hacia el web. [DECISIÓN]
Si no hay Issue PUBLISHED: 404 o 200 con { pieces: [] } + empty de home. [DECISIÓN] 200 + empty es más amable para UI-08.
GET /api/pieces/:slug (público)
Piece completa (body) o 404 (inexistente o DRAFT).
GET /api/events (público)
Response 200: Event[] del current issue, startsAt asc.
{
"id": "evt_merienda",
"title": "Merienda de patio en Pradolongo",
"startsAt": "2026-08-22T09:00:00.000Z",
"place": "Parque de Pradolongo, Usera",
"audience": "Familias"
}
POST /api/auth/login
Body: { email, password }
Response 200: { accessToken, user: { id, email, name, role } }
GET /api/mesa/pieces (JWT)
Array Piece (sin necesidad de body largo; incluir status), updatedAt desc. Sin token → 401. Incluye DRAFT.
POST /api/pieces (JWT)
Body
| Campo | Tipo | Req |
|---|---|---|
| title | string | sí |
| kicker | string | sí |
| body | string | sí |
| cover | string | sí |
| section | PieceSection | sí |
| issueId | string | no (default current) |
| authorName | string | no (default user.name) |
Response 201: Piece + slug + status: "DRAFT".
PATCH /api/pieces/:id (JWT)
Body: { status?: PublishStatus, title?, kicker?, body?, cover?, section?, authorName? }
Mínimo Must: { status }.
Response: Piece actualizada. Sin token → 401.
[COMPROBADO] PATCH sobre :id con { status }.
7. Seed de referencia (2026-08-17)
EDITOR
| Nombre | Password | |
|---|---|---|
| Nuria Ceballos | redaccion@margen.barrio | password123 |
[COMPROBADO] Credencial del encargo. Nombre de pila es constructo de diseño.
Issue
| weekLabel | Título | Cover | Status | publishedAt |
|---|---|---|---|---|
| 17–23 ago | El patio no cierra en agosto | hero.jpg | PUBLISHED | 2026-08-17 07:00 Europe/Madrid |
Pieces
| slug | Sección | Título | Cover | Status |
|---|---|---|---|---|
| merienda-en-el-banco | PATIO | La merienda que no cabe en el grupo | merienda.jpg | PUBLISHED |
| puerta-del-cole | COLE | El cole no ha abierto y el grupo ya está en llamas | colegio.jpg | PUBLISHED |
| vaso-de-agosto | BARRIO | La piscina de Orcasitas aguanta agosto | piscina.jpg | PUBLISHED |
| silencio-y-patio | LECTURA | La biblioteca no es un PDF | biblioteca.jpg | PUBLISHED |
| lo-que-el-ampa-no-cuelga | COLE | Lo que el AMPA no va a colgar en Stories | colegio.jpg | DRAFT |
Textos de body / lead son contenido de producto demo, no reportaje de un diario real. [SUPUESTO de catálogo]
| slug | Lead demo (primeros ~160) |
|---|---|
| merienda-en-el-banco | En el banco de piedra del patio de manzana hay Fanta, bolsas de pan y un casco de moto. Agosto no se ha ido. El grupo del cole, sí: nadie pegó la foto. |
| puerta-del-cole | Las mochilas ya cuelgan de la valla y el cole aún no ha abierto. El hilo del AMPA lleva tres PDFs y ninguna hora buena. |
| vaso-de-agosto | El vaso de Orcasitas sigue en azul bajo los bloques. Quedan tardes. No es un chiringuito: es la piscina que hay. |
| silencio-y-patio | La sala da al patio y el cartel dice Biblioteca Municipal. El silencio no es un PDF. Hay sillas de enea y una pila de cuentos. |
| lo-que-el-ampa-no-cuelga | Borrador: lo que Raúl no va a poner en Stories — horarios, no poses. Invisible en home. |
Events
| Título | startsAt (Europe/Madrid) | Lugar | Público |
|---|---|---|---|
| Merienda de patio en Pradolongo | 2026-08-22 11:00 | Parque de Pradolongo, Usera | Familias |
| Charla de vuelta al cole | 2026-08-18 18:00 | AMPA · local demo Carabanchel | Madres y padres |
| Hora del cuento de agosto | 2026-08-19 17:30 | Biblioteca municipal, Villaverde | 4 a 8 años |
Los tres eventos son seed de producto. No se presentan como calendario oficial de un distrito, un CEIP o una biblioteca concreta. [SUPUESTO de catálogo]
8. Evolución posible (no implementada)
| Cambio | Nivel |
|---|---|
Archivo de Issues + /numeros/:week | L+1 |
authorUserId + varios EDITORES | L+1 / L3 |
| Preview token de DRAFT | L+1 |
| CMS de Event autenticado | L+1 |
| Soft delete + audit log de status | L3 |
| Comentarios / likes | Fuera (S4) |
| Cupo / inscripción en Event | Fuera (H2) |
06-tech-stack.md
06 — Stack tecnológico — MARGEN
1. Visión
| Capa | Tecnología | Notas |
|---|---|---|
| Frontend | Angular standalone + signals | Puerto 4200 |
| Estilos | Tailwind CSS | Tokens gallery (papel/tinta/cadmio/grafito/pliego/filete) |
| Backend | NestJS TypeScript | Puerto 3017, prefijo /api |
| Persistencia | PostgreSQL (ensureSchema o Prisma) | Tablas users, sessions?, issues, pieces, events |
| DB | Neon PostgreSQL | project morning-truth-64427459 |
| Auth | JWT | Rol EDITOR |
| Diseño | Paper | 01M078GFW7QQF5RRA2XGKQ8BD9 |
| Repo | /Users/cristian/orca/margen-app/ | GitHub Criscode2022/margen-app |
Stack fijo del cron (D-P0-03). No React/Next/Firebase.
[COMPROBADO] Puerto 3017, Neon, Paper y repo constan en el encargo.
2. Estructura
margen-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:
| Script | Acción |
|---|---|
| api | Arranca Nest en :3017 |
| start / web | Arranca Angular en :4200 |
3. API pública vs JWT
| Método | Ruta | Auth |
|---|---|---|
| GET | /api/issues/current | Público |
| GET | /api/pieces/:slug | Público (404 si DRAFT) |
| GET | /api/events | Público |
| POST | /api/auth/login | Público |
| GET | /api/mesa/pieces | JWT EDITOR |
| POST | /api/pieces | JWT EDITOR |
| PATCH | /api/pieces/:id | JWT EDITOR |
CORS: origen http://localhost:4200 en local.
[COMPROBADO] Superficie API del encargo. No se añaden Must extra (/comments, POST /events, /subscribe).
4. Justificación
| Elección | Razón |
|---|---|
| Angular + Nest | Alineación con la serie daily y el handoff del estudio |
| Neon serverless | Volumen bajo (S1: 1 número + 4–8 piezas; demo 4+1); no cola Redis |
| JWT EDITOR | D-P1-05: panel interno cerrado desde el día 1 |
| Filtro PUBLISHED en servidor | Source of truth = SQL; la home no “esconde” DRAFT en cliente |
| Puerto 3017 | Evitar colisión con CESTA :3016 / RONDA :3015 / DERIVA :3014 / PIZARRA :3013 |
5. Variables
.env local (nunca git):
| Variable | Servicio | Descripción |
|---|---|---|
DATABASE_URL | API | Neon morning-truth-64427459 |
JWT_SECRET | API | Firma tokens |
PORT | API | opcional, 3017 |
JWT_EXPIRES | API | opcional (p. ej. 12h) |
.env.example solo placeholders.
Web: URL de API en ApiService (default http://localhost:3017/api).
6. Dependencias de producto (mínimas)
API
@nestjs/common/core/platform-express@nestjs/jwt(o verificación manual)bcrypt(obcryptjs)- cliente Postgres (
pg) o Prisma class-validator+class-transformer
Web
- Angular standalone
- Tailwind 3
- fuentes: Cormorant Garamond + Public Sans (Google Fonts o self-host)
7. Arranque (contrato)
cd /Users/cristian/orca/margen-app
# instalar apps independientes (npm --prefix o pnpm --filter)
# apps/api/.env → DATABASE_URL + JWT_SECRET + PORT=3017
# migrate / ensureSchema + seed
# api → http://localhost:3017
# web → http://localhost:4200
8. Lo que este stack no es
| No | Por qué |
|---|---|
| npm workspaces | AP-06 |
| Firebase Auth | Stack fijo JWT + Neon |
| Next.js / React | D-P0-03 |
| Pasarela de pago / paywall | S2 / H3 |
| Elasticsearch | 4 piezas no lo necesitan |
| CMS headless (Strapi / Sanity) | S3: mesa de 1–2, no tipos |
9. Nota sobre el repo existente
[COMPROBADO] Existe /Users/cristian/orca/margen-app y el remoto previsto Criscode2022/margen-app.
El scaffold contiene leftover de otros productos (walks, requests, fotos de despensa CESTA en public/assets/needs/). Esta suite define el contrato MARGEN; el código debe alinearse a las rutas y API de los docs 03–05, no al leftover. [DECISIÓN]
07-creative-direction.md
07 — Dirección creativa — MARGEN
1. Concepto
MARGEN es un kiosco de barrio en pantalla: semanario que se hojea, no una landing de “medio digital”. El ground es papel blanco de galería. Las portadas se cuelgan como en un expositor. El cadmio es la mancha de rotativa, no un botón de marketing.
Mood elegido: gallery / newsstand.
Primer instinto descartado: bookish plaster × ink (capilla, enyesado, vela). Ese mood ya huele a revista literaria de centro. MARGEN es patio, valla del cole y vaso municipal.
[COMPROBADO] Mood, ground y paleta salen de docs/00-day-brief.md.
2. Territorio visual
| Eje | Sí | No |
|---|---|---|
| Superficie | Blanco de kiosco, filete fino, card pliego | Musgo CESTA, cromo RONDA, agua DERIVA, hielo PRIMA, chalky PIZARRA |
| Tipo | Cormorant Garamond display + Public Sans UI | Newsreader, Outfit, Spectral, Manrope, Zilla, Mulish, Lora, Karla |
| Foto | Barrio sur realista, mediodía, ropa tendida, mochilas | Stock de familia feliz, skyline, “diversity lunch” |
| Grid | Lookbook 16:10, 1 portada + 3 cards | Hero 2-col + 3 cards de features (AP-12) |
| Acento | Cadmio #C41E3A en filete y PUBLISHED | Naranja ONG, teal seguro, wine atelier |
| Chrome | Masthead de periódico | Rail de iconos, dock, sidebar CMS |
3. Paleta
| Token | Hex | Rol |
|---|---|---|
| Papel / G-GALLERY | #FFFFFF | Ground de página. Kiosco, no “off-white sucio”. |
| Tinta | #16141A | Texto, wordmark, cuerpo. Casi negro, no púrpura. |
| Cadmio | #C41E3A | Filete de masthead, acento, badge PUBLISHED, CTA de mesa |
| Grafito | #5C5866 | Kicker, meta, nav idle, crédito |
| Pliego | #F4F1EA | Solo cards (pieza, evento). Nunca el fondo de página. |
| Filete | #E8E4DC | Rules, bordes, skeletons |
Jerarquía de color: el blanco carga; el cadmio puntúa; el pliego encuadra la foto. Si el cadmio pinta fondos enteros, deja de ser rotativa y se vuelve “brand red”. [DECISIÓN]
4. Tipografía
| Uso | Familia | Tamaños de referencia |
|---|---|---|
| Wordmark | Cormorant Garamond 600 | 28–32 desktop · 24 mobile |
| Título de número | Cormorant Garamond 600 | 40–56 |
| Título de crónica | Cormorant Garamond 600 | 36–48 desktop · 32 mobile |
| Lead / entradilla | Cormorant Garamond 400 italic o Public Sans 400 | 18–20 |
| Cuerpo de crónica | Public Sans 400 | 17–18 / 28–30 lh |
| UI, nav, form, agenda | Public Sans 400/500/600 | 14 / 16 |
| Kicker | Public Sans 600 | 12–13, tracking 0.08em, uppercase, cadmio o grafito |
Prohibido: Cormorant por debajo de 20px (se rompe). Public Sans en el wordmark (se vuelve startup).
5. Fotografía
Las cinco imágenes del case son el sistema, no un moodboard:
| Asset | Lectura visual | Uso |
|---|---|---|
hero.jpg | Semanario abierto, café, cuaderno de cuadros, luz de ventana | Portada del número · cover UX |
merienda.jpg | Patio de manzana, banco de piedra, merienda, ropa tendida | Pieza Patio |
colegio.jpg | CEIP de ladrillo, plátanos, mochilas en la valla | Pieza Cole |
piscina.jpg | Vaso municipal, hamacas, bloques de ladrillo | Pieza Barrio |
biblioteca.jpg | Sala con patio, sillas de enea, cartel municipal | Pieza Lectura |
Crop: cards y portada en 16:10. El lead fotográfico (caras en el banco, mochilas, lámina de agua, mesa de lectura) debe quedar dentro del recorte. [COMPROBADO] Must del brief.
Tratamiento: saturación real, sin LUT “editorial teal-orange”, sin grano falso de película. El cadmio no se superpone como filtro: vive en UI.
6. Layout
Masthead (S-TOP)
MARGEN Número Agenda Mesa
17–23 ago · Usera · Carabanchel · Villaverde
──────────────────────────────────────────── (filete cadmio 2px)
Wordmark a la izquierda. Secciones a la derecha. Línea de distrito en Public Sans 12 grafito. Filete cadmio. No hamburger en desktop.
Home (H-GALLERY)
- Bloque de número: weekLabel + título en Cormorant + portada 16:10 (
hero.jpg). - Grid de piezas: 3 columnas desktop / 1 mobile. Card pliego: foto 16:10 + kicker + título + lead.
- Cierre: enlace a Agenda, no un form.
La portada no comparte fila con un muro de copy (“Cómo funciona MARGEN”). Eso sería H-HERO2 / AP-12.
Ficha
Cover a ancho de columna de lectura (~680px), no full-bleed de marketing. Kicker → título → byline → cuerpo. Blancos verticales de semanario, no de landing.
Mesa
Lista densa sobre papel. No imita el lookbook. Es trastienda: filas, badges, CTA cadmio. El EDITOR no necesita “inspiración de marca” para publicar.
7. Motion
| Superficie | Spec |
|---|---|
| Hover card | Filete grafito; no elevación 8px ni scale 1.04 |
| Cambio de página | Fade 160ms o ninguno |
| Skeleton | Pulso 1.2s en filete; off si reduced-motion |
| Publicar | Badge, no confetti ni “rotativa animada” |
Prohibido: parallax en portada, scroll-jacking, page transitions de 400ms, sticker de “NEW” parpadeante.
8. Anti-clichés (serie ALS-2)
| Cliché | Por qué no |
|---|---|
| Bookish plaster × ink | Primer instinto del brief; vetado |
| Wine + blush atelier (PLIEGO) | Moda, no barrio |
| Botanical / musgo (CESTA) | Despensa |
| Chrome / neón (RONDA) | Gaming |
| Chalky municipal (PIZARRA) | Inscripción |
| Familia stock con globos | Mentira social del “barrio feliz” |
| Paywall blur + “suscríbete” | Rompe S2 |
| Iconos outline de periódico 2014 | Masthead tipográfico basta |
9. Tono de marca (visual + verbal, puente a doc 09)
Cercano de kiosco, concreto, de patio. Tuteo. Segunda persona cuando habla la UI. Tercera cuando habla la crónica.
El producto se hojea y se publica. No vende una comunidad, no pide plaza, no pide dinero.
10. Criterios de aceptación creativos
- Ground de página =
#FFFFFF. Cards =#F4F1EA. Nunca al revés. - Filete cadmio bajo el masthead, 2px, no un header rojo lleno.
- ≥3 portadas/piezas con recorte 16:10 visible en el primer viewport desktop.
- Ninguna fuente de la lista vetada (Newsreader / Outfit / Spectral / Manrope / Zilla / Mulish / Lora / Karla).
- Home reconocible como kiosco, no como hero 2-col de startup.
- Success de mesa no celebra un “número impreso”.
08-design-system.md
08 — Design system — MARGEN
1. Tokens
--color-paper: #FFFFFF; /* G-GALLERY ground */
--color-ink: #16141A;
--color-cadmium: #C41E3A;
--color-graphite: #5C5866;
--color-pliego: #F4F1EA; /* cards only */
--color-rule: #E8E4DC;
--color-draft: #5C5866; /* badge DRAFT */
--color-live: #C41E3A; /* badge PUBLISHED */
--font-display: "Cormorant Garamond", Georgia, serif;
--font-ui: "Public Sans", system-ui, sans-serif;
--radius-card: 0; /* corte de kiosco, no pill */
--radius-cta: 2px;
--rule-mast: 2px solid var(--color-cadmium);
--space: 4 / 8 / 12 / 16 / 24 / 32 / 48 / 64;
Tailwind (referencia):
| Key | Valor |
|---|---|
colors.paper | #FFFFFF |
colors.ink | #16141A |
colors.cadmium | #C41E3A |
colors.graphite | #5C5866 |
colors.pliego | #F4F1EA |
colors.rule | #E8E4DC |
fontFamily.display | Cormorant Garamond |
fontFamily.sans | Public Sans |
[COMPROBADO] Hex y tipo = brief.
2. Escala tipográfica
| Token | Familia | Size / lh | Uso |
|---|---|---|---|
display-xl | Cormorant 600 | 56 / 60 | Título de número desktop |
display-l | Cormorant 600 | 40 / 44 | Título de número mobile · título crónica desktop |
display-m | Cormorant 600 | 32 / 36 | Título crónica mobile · wordmark |
lead | Public Sans 400 | 18 / 28 | Entradilla de card |
body | Public Sans 400 | 17 / 28 | Cuerpo de crónica |
ui | Public Sans 400 | 16 / 24 | Form, agenda, lista mesa |
meta | Public Sans 500 | 13 / 18 | Fecha, lugar, crédito |
kicker | Public Sans 600 | 12 / 16 · tracking 0.08em · uppercase | Sección |
3. Componentes
Masthead
| Spec | Valor |
|---|---|
| Alto desktop | 72–88px + línea de distrito 24px |
| Alto 390 | 56px + links 44px |
| Fondo | Papel |
| Rule | 2px cadmio full-bleed |
| Links | Public Sans 14 · tinta activo · grafito idle |
| Activo | aria-current="page" + filete 2px cadmio bajo el label |
Slots: Número / · Agenda /agenda · Mesa /mesa · Entrar /login o Salir.
Card de pieza (lookbook)
┌─────────────────────────────┐
│ cover 16:10 │
│ │
├─────────────────────────────┤
│ KICKER │
│ Título en Cormorant │
│ Lead 2–3 líneas Public Sans │
└─────────────────────────────┘
- Fondo pliego, sin sombra.
- Toda la card es un
<a>. - Hover: rule 1px grafito. Sin lift.
- Imagen
object-fit: coveranclada al centro-arriba para no cortar caras.
Portada de número
- Ratio 16:10 obligatorio (must brief).
- Overlay opcional: weekLabel + título en papel sobre cadmio al 0% — preferible título fuera de la foto, debajo, para no pelear con
hero.jpg. [DECISIÓN] Título bajo la foto.
Ficha de crónica
| Bloque | Spec |
|---|---|
| Columna | max-width 680px, centrada |
| Cover | 16:10, full de columna |
| Kicker | cadmio o grafito, sobre el título |
| Título | display-l / display-m |
| Byline | “Nuria Ceballos · 17 ago” meta |
| Cuerpo | body 17/28 · max 70ch |
| Volver | link “← El número” no breadcrumb de 5 niveles |
Evento (agenda)
Fila o card pliego con cuatro campos en este orden: cuándo · título · dónde · para quién.
Disclaimer de bloque, no de cada fila: “La agenda se lee. No es una inscripción.”
Fila de mesa
| Columna | Contenido |
|---|---|
| Kicker | PATIO / COLE / … |
| Título | 1 línea, truncate |
| Status | Badge Borrador / Publicado |
| updatedAt | meta, Europe/Madrid |
| Acción | Publicar (si DRAFT) · ver |
Badge DRAFT: texto grafito, rule filete.
Badge PUBLISHED: texto cadmio o papel sobre cadmio ≥14 bold.
Botones
| Variante | Uso | Spec |
|---|---|---|
| Primary | Publicar, Entrar, Guardar y publicar | Fondo cadmio, texto papel, h-44, radius 2 |
| Secondary | Guardar borrador, Volver | Rule tinta, texto tinta, fondo papel |
| Ghost | Salir, Reintentar | Texto grafito |
| Danger | — | No hay borrado Must |
Pending: disabled + aria-busy. No spinner de marca.
Form
Labels visibles (no placeholder-only). Inputs h-44, radius 2, rule filete, foco outline 2px cadmio offset 2. Textarea cuerpo min 8 filas. Select de section y cover (allowlist de assets).
Estados
| Estado | Receta |
|---|---|
| Empty | Titular Cormorant 32 + 1 línea Public Sans + (mesa) CTA Nueva |
| Error | Banner filete cadmio + texto + Reintentar |
| Loading | Skeleton portada 16:10 + 3 cards pliego; mesa: 5 filas filete |
| 404 pieza | “Esa crónica no está publicada.” + link al número |
4. Grid y breakpoints
| Viewport | Comportamiento |
|---|---|
| ≥1200 | Portada 16:10 ancha; grid piezas 3 col; mesa tabla |
| 960–1199 | Grid 2 col; mesa tabla |
| 600–959 | Portada full; grid 1–2; mesa lista |
| ~390 (UI-10) | Portada full; 1 col; cuerpo 17px; masthead compacto; mesa apilada |
Padding horizontal: 24 desktop, 16 mobile.
Main en 390: padding-bottom 24 (no hay rail inferior).
5. Iconografía
Mínima. El semanario no es un set de iconos outline.
| Sitio | Recurso |
|---|---|
| Masthead | Texto. Sin icono de “home”. |
| Empty | Nada o una rule cadmio de 32px |
| Status | Texto de badge, no semáforo |
Si hace falta un icono (salir, error): trazo 1.5, 20px, tinta. No pack Remix/Lucide a color.
6. Elevación y borde
| Superficie | Tratamiento |
|---|---|
| Página | Papel plano |
| Card | Pliego plano + (opcional) rule 1px filete |
| Mesa | Filas separadas por rule filete |
| Modal | No hay modal Must (confirmación de despublicar = Should) |
Cero sombras shadow-lg. El kiosco no flota.
7. Mapa componente → ruta
| Componente | Rutas |
|---|---|
MastheadComponent | todas |
IssueCover | / |
PieceCard | / |
PieceArticle | /piezas/:slug |
EventRow | /agenda |
DeskRow | /mesa |
PieceForm | /mesa/nueva |
EmptyState / ErrorBanner / SkeletonGallery | /, /agenda, /mesa |
8. Criterios de aceptación DS
- Tokens Tailwind = hex del brief, sin “casi blanco”
#FAFAFAcomo ground. - Cards en pliego; página en papel.
- Primary CTA nunca en grafito sobre filete (contraste).
- Cormorant no aparece en labels de form ni en badges.
- Recorte 16:10 implementado como
aspect-[16/10], no 4:3 ni 1:1. - Targets de masthead y CTA ≥44px.
09-content-guide.md
09 — Guía de contenido — MARGEN
1. Voz
Cercana de kiosco y de patio. Tuteo en UI. Segunda persona cuando la interfaz habla. Tercera cuando habla la crónica.
Sin jerga de medios (“contenido premium”, “engagement”, “comunidad de lectores”). Sin sede electrónica. Sin “únete a la conversación”.
El producto se hojea y se publica. No vende una causa, no pide plaza, no pide dinero.
2. Palabras permitidas / prohibidas
| Usar | No usar |
|---|---|
| Número / semanario | Edición impresa, tiraje, “ya está en kiosco” (si es solo digital) |
| Pieza / crónica | Post, artículo CMS, contenido, story (de feed) |
| Kicker / Patio · Cole · Barrio · Lectura | Categoría, tag, vertical, “sección lifestyle” |
| Agenda / cita | Inscripción, reserva, plaza, cupo, WAITLIST |
| Publicar / publicado | Imprimir, mandar a rotativa, “sale el papel” |
| Borrador | Pending review, staging, “oculto al algoritmo” |
| Mesa / redacción | Inbox, CRM, backoffice, CMS |
| Lectora / familia | Usuario, lead, suscriptora |
| Editora | Community manager, content owner |
| Se lee | Apúntate, reserva tu sitio |
3. Microcopy duro (literales irrenunciables)
El brief fija tres oposiciones. Aparecen donde el usuario puede confundirse.
| Oposición | Dónde | Copy |
|---|---|---|
| Publicar ≠ imprimir | Success de mesa · pie de número | “Publicar no es imprimir. El número digital no es el tiraje de kiosco.” |
| Borrador ≠ publicado | Mesa · 404 de slug DRAFT | “Borrador: la familia no lo ve.” / “Esa crónica no está publicada.” |
| Agenda ≠ inscripción | /agenda sobre el fold | “La agenda se lee. No es una inscripción.” |
[DECISIÓN] Los tres textos son obligatorios en UI. No se diluyen a un tooltip.
4. Microcopy por superficie
| Superficie | Copy |
|---|---|
| Eslogan | La semana del barrio, en un número. |
| Línea de distrito | Usera · Carabanchel · Villaverde |
| Home, bajo título | Número del 17 al 23 de agosto. |
| Card CTA implícito | Toda la card es el enlace; no “Leer más” |
| Ficha volver | ← El número |
| Agenda titular | Agenda de la semana |
| Agenda disclaimer | La agenda se lee. No es una inscripción. Si hay lista, se hace en el patio. |
| Empty lookbook | Este número aún no tiene piezas publicadas. |
| Empty agenda | Esta semana no hay citas en la agenda. |
| Empty mesa | Aún no hay piezas. Escribe la primera. |
| Error | No hemos podido cargar el número. |
| Error retry | Reintentar |
| Login titular | Redacción |
| Login submit | Entrar |
| Login error | Email o contraseña no valen. |
| Mesa titular | Mesa |
| Mesa CTA | Nueva pieza |
| PATCH PUBLISHED | Publicar |
| PATCH DRAFT | Volver a borrador |
| Alta título | Nueva pieza |
| Alta submit secundario | Guardar borrador |
| Alta submit primario | Publicar |
| Success titular | Publicada. |
| Success cuerpo | La familia ya puede leerla. Publicar no es imprimir. El kiosco no ha tirado nada. |
| 404 DRAFT | Esa crónica no está publicada. |
| Masthead Número | Número |
| Masthead Agenda | Agenda |
| Masthead Mesa | Mesa |
| Salir | Salir |
5. Crónicas seed (tono)
Las piezas demo se escriben como crónica de barrio, no como nota de gabinete ni como columnismo de centro.
| Pieza | Tono |
|---|---|
| Merienda | Observación: banco, Fanta, casco, ropa tendida. Sin moraleja. |
| Cole | El grupo echa humo; el PDF no basta. Sin atacar a un CEIP real por nombre. |
| Piscina | El vaso que hay, los bloques, agosto que se acaba. Sin “escapada”. |
| Biblioteca | Sala, patio, silencio. El cartel municipal es atrezzo, no fuente oficial. |
| DRAFT AMPA | Oficio: lo que no va a Stories. Invisible en home. |
No inventar declaraciones de vecinas reales. Si hay una voz, es de la narradora (Nuria) o de persona de diseño etiquetada.
6. Nombres propios del seed
| Nombre | Uso |
|---|---|
| Elena Vargas | LECTORA; no deja PII en producto |
| Nuria Ceballos | EDITOR; byline; redaccion@margen.barrio |
| Raúl Méndez | AMPA; aparece en copy de DRAFT, no como cuenta |
| Hugo / Inés | Hijos de Elena; no son UI |
No inventar CIF, director de CEIP, ni horarios oficiales. [SUPUESTO de catálogo demo]
Franjas demo: sábado 11:00 merienda · lunes 18:00 charla · miércoles 17:30 cuento. No se afirman como agenda municipal.
7. Tono por estado (mesa)
| Status | Frase mesa | Frase que Nuria podría decir (fuera de app) |
|---|---|---|
| DRAFT | La familia no lo ve | “Aún no. Está en la mesa.” |
| PUBLISHED | En el número | “Ya está. No, no sale en papel.” |
La app no envía esos mensajes en v1. El copy solo prepara el habla.
8. Capitalización y números
- Wordmark: MARGEN en versales Cormorant.
- weekLabel:
17–23 ago(en-dash, mes abreviado minúscula). - Fechas de agenda:
sáb 22 ago · 11:00(punto medio, 24h). - Distritos: Usera, Carabanchel, Villaverde (con mayúscula).
- No mezclar “agosto 17-23” estilo US.
9. Idioma
es-ES. Fácil lectura. EN no en v1.
No mezclar “piece” en UI. En API sí: pieces, issueId.
10. Accesibilidad de copy
- No transmitir estado solo con color (“el rojo significa publicado”).
- Badge lleva texto: Borrador, Publicado.
- Alt de fotos: descriptivo del barrio, no “imagen1”.
- hero: “Semanario abierto en una mesa, café y cuaderno de cuadros.”
- merienda: “Familias merendando en un banco de piedra de un patio de manzana.”
- colegio: “Puerta de un colegio de ladrillo, mochilas colgadas en la valla.”
- piscina: “Piscina municipal con bloques de viviendas al fondo.”
- biblioteca: “Sala de lectura municipal con mesa de madera y patio al fondo.”
11. Piezas que no se escriben
| No | Motivo |
|---|---|
| “Suscríbete para seguir leyendo” | S2 / H3 |
| “Reserva tu plaza” / “Apúntame” | H2 · Agenda ≠ inscripción |
| “Ya está en el kiosco” como hecho de publicación digital | Publicar ≠ imprimir |
| Ranking de las más leídas | Likes, otro producto |
| Lorem / “lorem ipsum merienda” | Prohibido en el case |
| Declaraciones inventadas de un CEIP o biblioteca reales | Verdad de catálogo |
10-accessibility.md
10 — Accesibilidad · MARGEN
Objetivo: WCAG 2.2 AA.
| Área | Decisión |
|---|---|
| Contraste | Tinta #16141A sobre #FFFFFF ≥ 12:1. Cadmio #C41E3A sobre blanco ≥ 5:1 en texto ≥14px bold. |
| Foco | Anillo 2px ink. Orden: masthead → nav → galería → pie. |
| Imágenes | alt = título de la pieza. Hero decorativo alt="". |
| Texto | Cuerpo ≥ 16px / 26px. Kicker no es el único medio de sección. |
| Teclado | Cards son <a>. Mesa: filas con enlace de título. |
| Movimiento | Sin autoplay. prefers-reduced-motion corta hover scale. |
| Idioma | lang="es". |
| Formularios | Labels visibles. Error junto al campo. |
| Semántica | Un h1 por página (masthead visual + h1 de pieza). |
No conforme (fuera L2): subtítulos de vídeo (no hay vídeo). PDF accesible del número (no hay PDF).
11-privacy-security.md
11 — Privacidad y seguridad · MARGEN
Datos
| Dato | Dónde | Base |
|---|---|---|
| Email redacción | users | Contrato interno |
| Piezas | pieces (cuerpo público si PUBLISHED) | Interés informativo local |
| Eventos | events (horarios públicos) | Interés informativo |
No se pide cuenta a la lectora. No hay tracking de menores. No se recogen fotos de niños identificables en el seed.
Seguridad L2
- JWT + sesión en Neon. Bcrypt 12.
- DRAFT nunca en
GET /pieces. - Rate limit en
/auth/login. - CORS acotado al front.
- Secretos solo en
.env(no git).
No se declara cumplimiento RGPD definitivo. Sí: minimización y derecho a borrar cuenta de editora (manual L2).
12-analytics.md
12 — Analítica · MARGEN
North star: piezas leídas / semana (evento piece_open, umbral 30s).
| Evento | Cuándo |
|---|---|
home_view | Carga del lookbook |
piece_open | Apertura de crónica |
agenda_view | Vista de agenda |
editor_login | JWT ok |
piece_publish | DRAFT → PUBLISHED |
No hay funnel de suscripción. No se mide “likes”. Hipótesis H2: agenda_view sin signup_click (no existe CTA).
13-qa-test-plan.md
13 — Plan de pruebas — MARGEN
1. Smoke obligatorio (D-P1-06)
| # | Acción | Esperado |
|---|---|---|
| 1 | GET /api/issues/current | 200 · weekLabel 17–23 ago · pieces.length ≥ 3 · slugs PUBLISHED presentes · ningún DRAFT |
| 2 | GET /api/pieces/merienda-en-el-banco | 200 · kicker/title/body/authorName · cover merienda.jpg |
| 3 | POST /api/auth/login redaccion@margen.barrio / password123 | 200 · accessToken · user.role=EDITOR |
| 4 | GET /api/mesa/pieces con Bearer | 200 · incluye DRAFT lo-que-el-ampa-no-cuelga |
| 5 | ng build o serve web | Web arranca o build OK |
[COMPROBADO] Smoke = current + pieza + login JWT + mesa.
2. Casos funcionales
| ID | Caso | Esperado |
|---|---|---|
| S1 | Slugs seed publicados | current incluye merienda-en-el-banco, puerta-del-cole, vaso-de-agosto, silencio-y-patio |
| S2 | DRAFT no sale en current | lo-que-el-ampa-no-cuelga ausente del array público |
| S3 | Slug DRAFT | GET /api/pieces/lo-que-el-ampa-no-cuelga 404 |
| S4 | Slug inexistente | 404 |
| S5 | Agenda | GET /api/events length = 3 · orden startsAt asc |
| S6 | Agenda UI | /agenda muestra 3 eventos y disclaimer; cero CTA Inscribirse |
| S7 | Ficha AC | kicker, título, cuerpo, crédito visibles |
| S8 | Lookbook 16:10 | portada + cards con recorte 16:10 (no 1:1) |
| S9 | Mesa sin JWT | 401 / redirect login |
| S10 | POST pieza | 201 · status=DRAFT · slug kebab |
| S11 | PATCH PUBLISHED | 200 · la pieza aparece en current |
| S12 | PATCH DRAFT (retirar) | 200 · desaparece de current · slug público 404 |
| S13 | Login malo | 401 · mensaje en form |
| S14 | Empty lookbook (Issue sin pieces PUBLISHED) | UI-08 |
| S15 | API caída | UI-08 + Reintentar conserva la vista |
| S16 | Loading | UI-09 skeletons antes del primer paint |
| S17 | Mobile 390 | Lookbook + ficha leíbles; CTA ≥44px; no hero 2-col |
| S18 | Fotos seed | 5 covers resuelven a assets |
| S19 | Password no viaja de vuelta | login response sin hash |
| S20 | Body POST | acepta title, kicker, body, cover, section |
| S21 | Success | /mesa/ok/:slug copy “Publicar no es imprimir” |
| S22 | Público no lista mesa | GET mesa sin token 401 (no 200 []) |
3. Datos de prueba
| Uso | Valor |
|---|---|
| EDITOR | redaccion@margen.barrio / password123 |
| LECTORA | Elena Vargas · sin cuenta |
| Pieza feliz | merienda-en-el-banco |
| Pieza DRAFT | lo-que-el-ampa-no-cuelga |
| Evento feliz | Merienda de patio en Pradolongo |
| weekLabel | 17–23 ago |
4. Regresión de diversidad / craft
| Check | Falla si |
|---|---|
| Home es galería | Hay hero 2-col + 3 cards de marca (AP-12) o tabla de filas (CESTA) o feed (RONDA) |
| Shell es masthead | Hay rail, dock flotante, sidebar CMS o command search como nav primaria |
| Copy honesto | Success dice “ya está en el kiosco” o agenda dice “inscríbete” |
| Paleta | Se cuelan musgo CESTA / cromo RONDA / chalky PIZARRA / hielo PRIMA |
| Tipo | Newsreader / Outfit / Spectral / Manrope / Zilla / Mulish / Lora / Karla |
| Rutas | Aparecen /inscribir, /walks, /necesidades, /inbox, /donar |
| Ground | Página en pliego #F4F1EA en vez de papel #FFFFFF |
5. Hipótesis (no se “prueban” en la demo)
H1, H2, H3 requieren uso real. El QA verifica que existen las superficies que permitirían medirlas (tap en card, disclaimer de agenda, ausencia de signup lectora, DRAFT 404).
6. Criterios de salida QA v1
- Smoke §1 en verde.
- S2, S3, S6, S8, S11, S17, S21, S22 en verde.
- Cero blockers de copy deshonesto (imprimir / inscribir / like).
- Sin
.enven el repo.
7. Fuera de este plan
Playwright e2e completo, carga, fuzzing, auditoría de un CEIP o biblioteca reales.
14-dev-handoff.md
14 — Handoff desarrollo — MARGEN
1. Arranque
cd /Users/cristian/orca/margen-app
# apps independientes (npm --prefix o pnpm --filter)
# apps/api/.env
# DATABASE_URL= # Neon morning-truth-64427459
# JWT_SECRET=
# PORT=3017
# migrate / ensureSchema + seed
# api → http://localhost:3017
# web → http://localhost:4200
Demo: redaccion@margen.barrio / password123.
2. Paridad Paper
| Debe verse en Angular | Artboard |
|---|---|
| Tokens gallery + Cormorant/Public Sans | UI-00 |
| Lookbook 16:10 + masthead | UI-01, UI-10 |
| Ficha con kicker, cuerpo, crédito | UI-02 |
| Agenda 3 eventos + disclaimer | UI-03 |
| Login EDITOR | UI-04 |
| Mesa DRAFT / PUBLISHED | UI-05 |
| Alta de pieza | UI-06 |
| Success “no es imprimir” | UI-07 |
| Empty / error / loading | UI-08…09 |
No clonar CESTA (tabla + rail), RONDA (feed + dock), DERIVA (mapa split) ni PIZARRA (command search + inscripción).
Paper se construye en paralelo; los nombres de artboard son el inventario planificado (doc 00).
3. Contratos
| Tema | Contrato |
|---|---|
| Login | { accessToken, user } camelCase (no access_token) |
| Lookbook | GET /api/issues/current → issue + pieces PUBLISHED |
| Ficha | GET /api/pieces/:slug · 404 si DRAFT |
| Agenda | GET /api/events |
| Mesa | GET /api/mesa/pieces Bearer |
| Alta | POST /api/pieces { title, kicker, body, cover, section, issueId?, authorName? } |
| Status | PATCH /api/pieces/:id { status } |
| Status enum | DRAFT | PUBLISHED |
| Section enum | PATIO | COLE | BARRIO | LECTURA |
Campos JSON en camelCase hacia el web aunque SQL sea snake_case. [DECISIÓN]
4. Mapa de rutas web
| Path | Guard cliente | Página |
|---|---|---|
/ | no | Lookbook |
/piezas/:slug | no | Ficha |
/agenda | no | Agenda |
/login | no | Login |
/mesa | soft (redirect) | Mesa |
/mesa/nueva | soft | Alta |
/mesa/ok/:slug | soft | Success |
Soft guard: si no hay token, /login?next=. La API es la autoridad.
[COMPROBADO] Solo estas rutas. No implementar /inscribir, /inbox, /walks, /mesa/:id.
5. Assets
Copiar desde el case:
2026-08-17-margen/assets/hero.jpg
2026-08-17-margen/assets/merienda.jpg
2026-08-17-margen/assets/colegio.jpg
2026-08-17-margen/assets/piscina.jpg
2026-08-17-margen/assets/biblioteca.jpg
→ apps/web/public/assets/ (o src/assets/)
cover del seed = filename (hero.jpg, merienda.jpg, …).
Quitar leftover public/assets/needs/* (despensa CESTA) si sigue en el repo.
6. DoD implementación L2
| Check | OK |
|---|---|
| API sirve :3017 | |
| Seed 1 EDITOR + 1 Issue + 4 PUBLISHED + 1 DRAFT + 3 events | |
| Smoke GET current + GET pieza + login + GET mesa | |
| JWT protege mesa / POST / PATCH | |
| DRAFT 404 en público | |
| Web lookbook + ficha + agenda + mesa + alta + success | |
| Microcopy ×3 (imprimir / borrador / inscripción) | |
| Tokens Tailwind = paleta brief | |
| Recorte 16:10 en portadas | |
ng build o equivalente OK | |
| README del repo app con credencial | |
| Home es galería (no leftover walks / tabla / feed) |
7. No hacer
| Prohibido | Motivo |
|---|---|
| npm workspaces | AP-06 |
Commit de .env | Secreto |
| Prometer tiraje o inscripción | Microcopy duro / AP-09 |
Estado SEATED / ENROLLED / CONTACTED / RECEIVED | Vocabulario de otros días |
| Search bar o mapa como home | Rompe H-GALLERY |
| Hero 2-col + 3 cards | AP-12 |
| Tabla de necesidades como home | CESTA |
| Feed + dock | RONDA |
image_gen como captura de producto | D-P0-09 |
Body POST con needSlug / tableSlug / walkId | Leftover |
8. Contactos de diseño (artefactos)
| Artefacto | Path |
|---|---|
| Brief | ux-projects/2026-08-17-margen/docs/00-day-brief.md |
| Paper | https://app.paper.design/file/01M078GFW7QQF5RRA2XGKQ8BD9 |
| Datos | docs/05-data-model.md |
| Flujos | docs/04-user-flows.md |
| IX | docs/16-interaction-specs.md |
| Build | docs/20-implementation.md |
15-roadmap.md
15 — Roadmap — MARGEN
Este documento no es un cajón de trabajo L2 a medias.
El vertical slice L2 del brief está especificado como done-scope (docs 01, 18, 19).
Aquí solo viven ideas de L+1 / L3 y pulido posterior.
1. Hecho en L2 (esta ejecución documental)
| Entrega | Evidencia |
|---|---|
| Definición producto + terna | docs 00-brief, 01 |
| Personas Elena / Nuria / Raúl, JTBD, journey | docs 02 |
| IA gallery + masthead | docs 03 |
| Flujos F-STORY + mesa EDITOR | docs 04 |
| Modelo issues / pieces / events / users + seed | docs 05 |
| Stack Angular/Nest/Neon/JWT :3017 | docs 06, 20 |
| Dirección gallery/newsstand + DS | docs 07, 08 |
| Copy Publicar ≠ imprimir · Borrador ≠ publicado · Agenda ≠ inscripción | docs 09 |
| Paper ref 12 UX + 11 UI | docs 00-paper-reference |
| Suite 00–20 + README + executive | case folder |
2. Implementación L2 (misma complejidad, código)
Contrato en docs 14 y 20. No es deuda silenciosa del case documental: es el build del repo margen-app. El alcance L2 ya está cerrado en spec; el código alinea leftover (walks / requests) al contrato.
Eso no se reabre aquí como “próximo trimestre de producto”.
3. L+1 (misma familia, subida de pulido o archivo)
| Ítem | Notas |
|---|---|
Archivo de números /numeros/:week | Home sigue siendo el current; no search-first |
| Preview de DRAFT con token | No 200 público |
authorUserId + segundo byline | Sigue siendo 1–2 personas |
| Rate limit login | Anti stuffing |
| Playwright S1–S22 | CI |
| Copiar enlace de pieza | Could lectora |
| Despublicar con confirm | PATCH DRAFT |
| API URL por environment | Deploy |
prefers-reduced-motion verificado | a11y |
| CMS mínimo de Event | Sigue sin inscripción |
Estas son subidas de pulido o archivo, no parches del L2 documental.
[DECISIÓN] No dejar backlog del vertical slice como “mañana”. Lo de arriba es L+1.
4. L3 (requiere brief nuevo de diversidad)
- Multi-distrito / varias cabeceras
- Cuenta LECTORA y bookmarks
- Newsletter transaccional “ha salido el número”
- Workflow REVIEW / SCHEDULED / KILLED
- Comentarios o cartas al director
- Impresión / PDF de kiosco / punto de venta
- Paywall
- Search como home (rompería terna)
5. Explícitamente fuera (no backlog disfrazado)
| Ítem | Por qué |
|---|---|
| Inscripción / cupo / WAITLIST | H2 · ≠ PIZARRA |
| Tabla de necesidades / compromiso | ≠ CESTA |
| Feed de historias + dock | ≠ RONDA |
| Mapa de meriendas | ≠ DERIVA |
| Likes / ranking | S4 |
| App nativa | Fuera de stack daily |
| Hero 2-col + 3 cards | AP-12 |
6. Orden de ataque si hay continuidad de código
- Contratos 05–06 + seed 05 + smoke 13 (alinear leftover)
- Web paridad UI-00…10
- Preview token + archivo de números (L+1)
- Rate limit si hay tráfico
- Evaluar brief L3 (no parche silencioso)
16-interaction-specs.md
16 — Especificación de interacción — MARGEN
1. Convenciones
| Token | Valor |
|---|---|
| Duración corta | 120–160ms |
| Easing | ease-out |
| Reduced motion | 0ms |
| Target | ≥44×44 |
| Pending | control disabled + aria-busy |
2. Masthead (S-TOP)
| Gesto | Resultado |
|---|---|
| Tap Número | / ; aria-current se mueve |
| Tap Agenda | /agenda |
| Tap Mesa sin sesión | /login?next=/mesa |
| Tap Mesa con sesión | /mesa |
| Tap Salir | limpia token → /login |
| Scroll de la galería | Masthead puede ser sticky (Should); no se esconde |
| Teclado | Tab: wordmark → links → contenido |
Estado activo: label tinta + filete 2px cadmio bajo el ítem.
Estado default: grafito.
Desktop: links a la derecha.
390: wordmark + Número + Agenda; Mesa entra en la misma fila si cabe, si no bajo la rule.
3. Lookbook home
| Gesto | Resultado |
|---|---|
| Tap portada del número | No lightbox. Scroll al grid o abre la pieza lead (merienda-en-el-banco). [DECISIÓN] abre la lead |
| Tap card | /piezas/:slug (card completa clicable, un solo enlace) |
| Tap “Agenda de la semana” | /agenda |
| Pull-to-refresh | No en v1; Reintentar solo en error |
| Primera carga | Skeleton portada 16:10 + 3 cards; no spinner centrado de marca |
| 0 publicados | Empty UI-08 |
| Error | Banner bajo masthead + Reintentar (refetch) |
| Hover desktop | Rule 1px grafito; no elevación de card |
AC: la portada 16:10 (foto + título del número) está en el viewport inicial a 390 y a 1280 (sin hero de 60vh de marca).
Orden visual = seed / createdAt ASC. No “destacados” por likes.
390 (UI-10): portada full + pila de cards. Lead de 2 líneas. Título Cormorant 32.
4. Ficha de crónica
| Gesto | Resultado |
|---|---|
| Scroll | Lectura normal; sin scroll-jacking |
| Tap ← El número | / |
| Tap Agenda (masthead o pie) | /agenda |
| Foto | No lightbox v1 |
| Selección de texto | Permitida (compartir por WhatsApp es backstage) |
Sin doble tap like. Sin barra de progreso de lectura como Must (Could L+1, off si reduced-motion).
5. Agenda
| Gesto | Resultado |
|---|---|
| Tap evento | Nada que parezca form. Could: expande nota. [DECISIÓN v1] no hay expansión; el bloque ya está completo |
| Hover | Sin CTA que aparezca (“Apuntarme”) |
| Empty | Titular + 1 línea |
| Error | Banner + Reintentar |
Si un tester busca un botón de reserva y no lo encuentra, H2 está bien implementada.
6. Login
| Gesto | Resultado |
|---|---|
| Submit | POST login; pending en botón |
| 200 | Guarda token + user; navega next sanitizado o /mesa |
| 401 | Texto bajo el form; password no se limpia |
| Enter | Submit |
next permitido: paths que empiezan por / y no // ni http. Allowlist sugerida: /mesa, /mesa/nueva.
7. Mesa
| Gesto | Resultado |
|---|---|
| Sin token | Redirect login |
| Tap Nueva pieza | /mesa/nueva |
| Tap Publicar en fila DRAFT | PATCH PUBLISHED; badge cambia |
| Tap título | Could: no hay /mesa/:id. [DECISIÓN] el título no navega; Publicar y “ver” (link a ficha si PUBLISHED) sí |
| Empty | Titular + CTA Nueva |
| Error 401 mid-session | Redirect login |
| Error red | Banner + Reintentar |
No swipe-to-archive. No kanban.
Optimistic UI en Publicar: badge pasa a Publicado; si PATCH falla, rollback + toast error.
Publicar no anima una rotativa ni un “+1 en kiosco”.
8. Alta de pieza
| Campo | Interacción |
|---|---|
| title | text, autocomplete off |
| kicker | text |
| section | select PATIO / COLE / BARRIO / LECTURA |
| cover | select de filenames seed (preview 16:10) |
| body | textarea 8+ filas |
| authorName | default Nuria Ceballos, editable |
| Gesto | Resultado |
|---|---|
| Guardar borrador | POST DRAFT; redirect /mesa o /mesa/ok/:slug con copy de borrador |
| Publicar | POST + PATCH PUBLISHED → /mesa/ok/:slug |
| Submit inválido | Foco al primer campo error; no POST |
| Error red | Botón Reintentar; no limpia campos |
| Doble tap | Ignorado mientras pending |
Submit deshabilitado si title < 4, body < 40 o section vacía.
[DECISIÓN] Dos botones visibles. Primario = Publicar. Secundario = Guardar borrador. El primario no se llama “Imprimir”.
9. Success
| Gesto | Resultado |
|---|---|
| CTA primario | Ver la pieza pública /piezas/:slug |
| CTA secundario | Volver a la mesa /mesa |
| Copy | Visible “Publicar no es imprimir” |
Sin confetti. Sin “¡ya está en el kiosco!”.
10. Motion
| Superficie | Spec |
|---|---|
| Cards lookbook | Fade 160ms, stagger ≤30ms (off si reduced-motion) |
| Cambio de badge | Crossfade 120ms |
| Masthead | Sin hide-on-scroll v1 |
| Skeletons | Estáticos o pulso 1.2s opacity 0.6–1; off si reduced-motion |
Prohibido: scroll-jacking, page transitions de 400ms, parallax en portada, like animation.
11. Criterios de aceptación de interacción
- Una LECTORA en 390px llega de lookbook a ficha en 1 tap, y a agenda en 1 tap más (masthead).
- Un EDITOR en 390px publica un DRAFT en ≤ 3 taps desde mesa (acción en fila).
- Ningún submit permite doble POST.
- El masthead no cubre el H1 de la ficha (spacer si sticky).
- Reduced-motion elimina stagger.
- La home no es hero 2-col (AP-12).
- En 390 el recorte 16:10 de la primera card es visible sin scroll horizontal.
/agendano presenta ningún control de inscripción.
17-prototype-map.md
17 — Mapa de prototipo — MARGEN
Paper no es clicable vía MCP. El prototipo vivo es la app Angular.
Los artboards de Paper se construyen en paralelo; esta tabla es el contrato de paridad.
| Ruta | Paper | Rol |
|---|---|---|
/ | UI-01, UI-08, UI-09, UI-10 | LECTORA |
/piezas/:slug | UI-02, UI-10 | LECTORA |
/agenda | UI-03 | LECTORA / AMPA |
/login | UI-04 | EDITOR |
/mesa | UI-05 | EDITOR |
/mesa/nueva | UI-06 | EDITOR |
/mesa/ok/:slug | UI-07 | EDITOR |
Proceso: UX-00…UX-11. Tokens: UI-00.
Recorrido demo (guión)
- Abrir
/— lookbook El patio no cierra en agosto, portadahero.jpg, ≥3 cards 16:10. - Tocar La merienda que no cabe en el grupo — ficha Patio, crédito Nuria.
- Masthead Agenda — 3 eventos; leer “no es una inscripción”.
- Comprobar que no hay botón Reservar.
/login—redaccion@margen.barrio/password123./mesa— ver DRAFT Lo que el AMPA no va a colgar en Stories.- Nueva pieza o Publicar el DRAFT →
/mesa/ok/:slug— “Publicar no es imprimir”. - Volver a
/en otra sesión: el DRAFT no está; lo publicado sí.
Estados en el mismo prototipo
| Estado | Cómo provocarlo |
|---|---|
| Empty lookbook | API 200 current con pieces: [] |
| Empty agenda | events [] |
| Empty mesa | lista vacía autenticada |
| Error | API caída / 5xx |
| Loading | throttle de red |
| 404 DRAFT | /piezas/lo-que-el-ampa-no-cuelga sin publicar |
| Mobile lector | 390 × 844 |
Fuera del prototipo clicable
- Paper artboards son estáticos.
- WhatsApp del AMPA / grupo del cole (backstage).
- Impresión / kiosco físico.
- Inscripción a eventos (no existe).
- Comentarios.
- Archivo de números anteriores.
18-completeness-audit.md
18 — Auditoría de completitud — MARGEN (2026-08-17)
1. Alcance auditado
Vertical slice L2: lookbook del número semanal + ficha de crónica + agenda familiar + mesa EDITOR JWT, con docs, referencia Paper, contratos Angular + Nest + Neon.
Este encargo cubre la suite documental. Paper hi-fi y el repo margen-app pueden existir en paralelo; la auditoría marca lo que esta suite cierra. El day brief no se ha sobrescrito.
2. Checklist CRON / ALS-2
| Requisito | Estado | Evidencia |
|---|---|---|
| Diversidad sector/tipo/nivel | OK | Editorial local L2; no tabla; no feed; no mapa; no search; no inscripción |
| Terna S/H/F 0 códigos = N−1 · ≥2 vs N−2 | OK | S-TOP · H-GALLERY · F-STORY vs CESTA S-RAIL · H-TABLE · F-LEAD |
| Day brief + anti-patrones | OK | docs/00-day-brief.md (no sobrescrito) |
| Paper ≥12 UX + ≥10 UI | OK | UX-count 12 · UI-count 11 · file 01M078GFW7QQF5RRA2XGKQ8BD9 |
| Docs 00–20 | OK | suite en docs/ + executive + README |
| JWT EDITOR | OK | Role EDITOR · D-P1-05 |
| API + seed + Neon | OK | contratos morning-truth-64427459, port 3017 |
| Web tokens | OK | Cormorant + Public Sans · paleta gallery |
| Gallery-first (no AP-12) | OK | UI-01 / docs 03, 07, 08 |
| Hipótesis no fake field | OK | etiquetas en doc 02 |
| Copy publicar ≠ imprimir · agenda ≠ inscripción · borrador ≠ publicado | OK | docs 01, 09, 16 |
| Ground G-GALLERY | OK | #FFFFFF · no G-MOSS / G-CHROME / G-WATER / G-MUNICIPAL / G-ICE |
3. Cobertura funcional
| Feature brief | Spec | UI Paper | API | Docs |
|---|---|---|---|---|
| Home lookbook + ≥3 portadas 16:10 | Sí | UI-01, UI-10 | GET current | 03, 04, 16 |
| Ficha kicker / título / cuerpo / crédito | Sí | UI-02 | GET :slug | 05, 09 |
| Agenda 3 eventos | Sí | UI-03 | GET events | 04, 05 |
| Login EDITOR | Sí | UI-04 | POST login | 04, 06 |
| Mesa DRAFT → PUBLISHED | Sí | UI-05 | GET mesa + PATCH | 04, 05 |
| Alta + success | Sí | UI-06, UI-07 | POST pieces | 04, 09 |
| Empty | Sí | UI-08 | 200 [] | 16 |
| Error | Sí | UI-08 | 5xx/red | 16 |
| Loading | Sí | UI-09 | pending | 16 |
| Design system | Sí | UI-00 | — | 08 |
| Seed 1 número + 4 piezas + 3 eventos | Sí | — | seed | 05 |
| Masthead | Sí | UI-01, UI-10 | — | 03, 08 |
| Mobile lector | Sí | UI-10 | — | 10, 16 |
4. Cobertura Paper (literales)
| ID | Nombre | En 00-paper-reference |
|---|---|---|
| UX-00 | Cover | Sí |
| UX-01 | Stakeholders | Sí |
| UX-02 | Personas | Sí |
| UX-03 | JTBD | Sí |
| UX-04 | Stories | Sí |
| UX-05 | Journey | Sí |
| UX-06 | Blueprint | Sí |
| UX-07 | Site map | Sí |
| UX-08 | Flujos | Sí |
| UX-09 | Datos+permisos | Sí |
| UX-10 | Métricas | Sí |
| UX-11 | Research | Sí |
| UI-00 | Tokens | Sí |
| UI-01 | Lookbook | Sí |
| UI-02 | Crónica | Sí |
| UI-03 | Agenda | Sí |
| UI-04 | Login | Sí |
| UI-05 | Mesa | Sí |
| UI-06 | Alta pieza | Sí |
| UI-07 | Success | Sí |
| UI-08 | Empty / Error | Sí |
| UI-09 | Loading | Sí |
| UI-10 | Mobile lector | Sí |
[COMPROBADO] UX-count: 12 y UI-count: 11 literales para el gate check-paper-reference.mjs. Paper hi-fi en construcción paralela; inventario planificado escrito.
5. Rúbrica de calidad (auto SCORE orientativo · docs)
| Eje | Score 1–5 | Comentario |
|---|---|---|
| Diversidad | 5 | Gallery+masthead vs tabla CESTA, feed RONDA y mapa DERIVA; L2 no L1 |
| Craft visual (spec) | 4–5 | Gallery/newsstand + Cormorant/Public Sans + fotos de barrio documentadas |
| Densidad UX docs | 5 | Suite L2: tablas, AC, etiquetas S/H/D |
| Completitud código L2 | — | App fuera o en paralelo; leftover scaffold anotado |
| Authz | 5 | Contrato: guard en mesa/POST/PATCH; DRAFT 404 |
| Verdad investigación | 5 | Sin entrevistas ni stats de campo falsas |
6. Huecos aceptados (no regresiones de cierre documental)
| Hueco | Clasificación |
|---|---|
| Paywall / comentarios / inscripción / mapa / search-home | Fuera L2 |
| e2e automatizado | L+1 |
| Preview token de DRAFT | L+1 |
| Archivo de números | L+1 |
Leftover walks / requests en repo | Build; contrato de esta suite gana |
| Paper/app no necesariamente tocados aquí | Encargo = documentación |
7. Veredicto
COMPLETO para entrega documental del caso 2026-08-17.
La suite define producto L2 usable (tablas, AC, riesgos, contratos) sin presentar investigación de campo como hecho.
Cualquier ampliación inscripción / paywall / feed / mapa / search-first requiere nuevo brief de diversidad, no parche silencioso.
19-backlog-completo.md
19 — Backlog completo — MARGEN
Inventario de ítems. Los del alcance L2 documental del día están Done.
El resto es opcional / siguiente nivel, no deuda oculta del cierre.
1. Done — L2 case documental (2026-08-17)
| ID | Ítem | Capa |
|---|---|---|
| D01 | Definición producto MARGEN + eslogan | Docs |
| D02 | Day brief diversidad editorial L2 | Docs (preexistente, no pisado) |
| D03 | Terna R-CL · S-TOP · H-GALLERY · F-STORY | Docs |
| D04 | Personas Elena Vargas / Nuria Ceballos / Raúl Méndez | Docs + Paper ref |
| D05 | JTBD + stories Must | Docs |
| D06 | IA gallery-first + masthead | Docs |
| D07 | Flujos F1–F9 + AC | Docs |
| D08 | Modelo users / issues / pieces / events | Docs |
| D09 | Contratos API :3017 | Docs |
| D10 | Auth JWT EDITOR | Docs |
| D11 | Estados DRAFT / PUBLISHED + 404 de borrador | Docs |
| D12 | Agenda de solo lectura (3 eventos) | Docs |
| D13 | Microcopy ×3 (imprimir / borrador / inscripción) | Docs |
| D14 | North star lectura de número (pieza + agenda) | Docs |
| D15 | Tokens gallery + Cormorant / Public Sans | Docs |
| D16 | Paper UX-00…11 + UI-00…10 | Docs |
| D17 | Suite docs 00–20 + README + executive | Docs |
| D18 | Seed 1 número + 4 piezas + 1 DRAFT + 3 eventos | Docs |
| D19 | Assets listados (5 fotos) | README |
| D20 | QA smoke + S1–S22 | Docs |
| D21 | Neon morning-truth-64427459 | Docs |
| D22 | Recorte 16:10 de portadas especificado | Docs |
| D23 | Ground G-GALLERY #FFFFFF | Docs |
| D24 | Rutas reales solo las 7 del encargo | Docs |
Los must-have del brief (lookbook, ficha, agenda, login+mesa, alta+success, empty/loading/error/mobile, ≥12 UX + ≥10 UI) están dentro de D01–D24, no como leftover.
2. Backlog L2 implementación (repo margen-app)
Alineación de código al contrato ya cerrado. No es alcance documental pendiente.
| ID | Ítem | Prioridad | Notas |
|---|---|---|---|
| I01 | Alinear apps/api + apps/web al contrato MARGEN | P0 | quitar leftover walks/requests/needs |
| I02 | Schema issues / pieces / events / users | P0 | |
| I03 | Seed Nuria + número + 5 piezas + 3 events | P0 | |
| I04 | GET current / pieces/:slug / events | P0 | DRAFT → 404 |
| I05 | POST pieces + PATCH :id { status } | P0 | |
| I06 | POST login JWT | P0 | redaccion@margen.barrio |
| I07 | GET mesa/pieces | P0 | |
| I08 | Lookbook + masthead Angular | P0 | 16:10 |
| I09 | Ficha / agenda / login / mesa / alta / ok | P0 | |
| I10 | Tokens Tailwind + fotos | P0 | |
| I11 | Smoke curl + build | P0 | D-P1-06 |
| I12 | Puerto API 3017 | P0 |
3. Backlog L+1 (polish / archivo)
| ID | Ítem | Prioridad | Notas |
|---|---|---|---|
| B01 | canActivate guards Angular | P1 | UX auth |
| B02 | Archivo /numeros/:week | P2 | home sigue current |
| B03 | Preview token DRAFT | P1 | no 200 público |
| B04 | Rate limit login | P1 | anti stuffing |
| B05 | Playwright smoke | P1 | CI |
| B06 | Skeleton UI-09 en código | P2 | |
| B07 | Copiar enlace de pieza | P3 | |
| B08 | Confirm al revertir a DRAFT | P2 | |
| B09 | API URL por environment | P1 | deploy |
| B10 | Página 404 amigable | P3 | |
| B11 | Sessions invalidables | P2 | |
| B12 | CMS Event (sigue sin inscripción) | P2 |
4. Backlog L3 (requiere brief nuevo)
| ID | Ítem | Dependencia |
|---|---|---|
| C01 | Multi-cabecera / varios distritos | venues + membership |
| C02 | Cuenta LECTORA + bookmarks | authz |
| C03 | Newsletter “ha salido el número” | provider |
| C04 | Workflow REVIEW / SCHEDULED | estados |
| C05 | Comentarios / cartas | S4 se rompe · brief nuevo |
| C06 | Impresión / PDF kiosco | Publicar ≠ imprimir se renegocia |
| C07 | Paywall | S2 se rompe |
| C08 | Search-first home | rompe terna |
5. Backlog explícitamente fuera
| ID | Ítem |
|---|---|
| E01 | Inscripción / cupo / WAITLIST |
| E02 | Tabla de necesidades / compromiso de caja |
| E03 | Feed de historias + dock |
| E04 | Mapa de meriendas / paseos |
| E05 | Likes / ranking |
| E06 | App nativa |
| E07 | Hero 2-col + 3 cards de marca |
6. Explicitamente no-backlog
| Idea | Razón |
|---|---|
| Hero 2-col + 3 cards | AP-12 / rompe H-GALLERY |
| Sidebar CMS | S-SIDE |
| Dock flotante | S-DOCK de RONDA |
| Rail de iconos | S-RAIL de CESTA |
| Command search municipal | PIZARRA |
| Split mapa | DERIVA |
| Wizard de publicación 4 pasos | F-ONB |
| Form “apúntame a la merienda” | H2 |
7. Orden de ataque recomendado (continuidad de código)
- I01–I12 (vertical slice runnable, sin leftover)
- B01 + B05 + B09
- B03 + B08
- B02 si hay segundo número
- Evaluar brief L3 — no parche silencioso
8. Trazabilidad
| Origen | Ítems |
|---|---|
| Day brief must-have | D01–D24, I01–I12 |
| Doc 15 L+1 | B01–B12 |
| Doc 15 L3 / fuera | C01–C08, E01–E07 |
20-implementation.md
20 — Implementación — MARGEN
1. Resumen ejecutivo técnico
| Campo | Valor |
|---|---|
| App path | /Users/cristian/orca/margen-app |
| API | NestJS · puerto 3017 · prefijo /api |
| Web | Angular standalone · puerto 4200 |
| Package | apps independientes (npm --prefix o pnpm --filter) |
| DB | Neon PostgreSQL · project morning-truth-64427459 |
| Tablas | users, issues, pieces, events (+ sessions Should) |
| Auth | JWT Bearer · role EDITOR |
| Dominio | User, Issue, Piece, Event |
| Fecha | 2026-08-17 |
| GitHub | https://github.com/Criscode2022/margen-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.
[COMPROBADO] Path, puerto, Neon, Paper y GitHub salen del encargo.
[COMPROBADO] El repo existe; contiene leftover de otros productos (walks, requests, assets de despensa). El contrato de esta suite es MARGEN.
2. Cómo arrancar
cd /Users/cristian/orca/margen-app
# instalar dependencias por app (no npm workspaces)
# apps/api/.env
# DATABASE_URL= # Neon morning-truth-64427459
# JWT_SECRET=
# PORT=3017
# schema + seed
# api → http://localhost:3017
# web → http://localhost:4200
Credenciales
| Rol | Password | |
|---|---|---|
| EDITOR | redaccion@margen.barrio | password123 |
3. Schema
Al boot o vía migrate:
CREATE TABLE/ migrate deusers,issues,pieces,events(doc 05).- Índices:
issues(status, published_at DESC),pieces(slug),pieces(issue_id, status),pieces(updated_at DESC),events(issue_id, starts_at),users(email). - Seed idempotente:
- upsert EDITOR
redaccion@margen.barrio(bcrypt depassword123) - upsert Issue por
week_label - upsert
piecesporslug - upsert
eventspor (issue_id,title)
- upsert EDITOR
No borrar datos de usuario en cada boot si ya existen filas distintas al seed.
Tablas (recordatorio)
| Tabla | Contenido |
|---|---|
users | EDITOR Nuria |
issues | 1 número seed PUBLISHED |
pieces | 4 PUBLISHED + 1 DRAFT |
events | 3 citas de agenda |
sessions | Should: sid + token_hash + 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 === EDITORprotege mesa / escritura.
Issues / pieces / events
| Método | Ruta | Auth | Notas |
|---|---|---|---|
| GET | /api/issues/current | No | Issue PUBLISHED más reciente + pieces PUBLISHED |
| GET | /api/pieces/:slug | No | 404 si no existe o DRAFT |
| GET | /api/events | No | events del current, startsAt asc |
| GET | /api/mesa/pieces | JWT | todas; updatedAt desc |
| POST | /api/pieces | JWT | crea DRAFT; slug kebab |
| PATCH | /api/pieces/:id | JWT | { status } y/o campos |
Slug
kebab(title) + opcional "-2" si unique falla
Reintentar sufijo (máx. 5). No UUID en URL de lectura.
[DECISIÓN] Timezone Europe/Madrid para weekLabel y startsAt de UI. JSON transporta ISO UTC.
Filtro público
current.pieces = pieces WHERE issue_id = current.id AND status = 'PUBLISHED'
GET /pieces/:slug = 404 unless status = 'PUBLISHED'
Publicar no toca events ni genera PDF.
5. Frontend a implementar
| Página | Ruta | Responsabilidad |
|---|---|---|
| HomePage | / | GET current, lookbook 16:10, empty/loading/error, masthead |
| PiecePage | /piezas/:slug | GET slug, ficha, 404 DRAFT |
| AgendaPage | /agenda | GET events + disclaimer |
| LoginPage | /login | form → login → mesa |
| DeskPage | /mesa | list + PATCH en fila + empty/error + logout |
| NewPiecePage | /mesa/nueva | form create + Guardar / Publicar |
| OkPage | /mesa/ok/:slug | confirmación + “no es imprimir” |
Shell: MastheadComponent persistente.
ApiService centraliza base URL http://localhost:3017/api, token storage (margen_token, margen_user), métodos tipados (Issue, Piece, Event, User).
6. Decisiones de implementación
| Decisión | Razón |
|---|---|
| Puerto API 3017 | Evitar colisión con CESTA 3016 |
Tablas issues / pieces / events | Dominio editorial, no leftover walks / requests |
| Soft auth en páginas de mesa | Simple; API es autoridad |
| Sin FK User–Piece | Byline texto (authorName) |
| Slug kebab | URL de lectura humana |
| Templates standalone | Velocidad daily |
| Crear siempre DRAFT | Borrador ≠ publicado |
| Home no usa hero 2-col | AP-12 / H-GALLERY |
| PUBLISHED no ENROLLED / RECEIVED | Dominio de crónica, no municipal ni despensa |
| JSON camelCase | Convenio web serie daily |
PATCH :id con { status } | Contrato del encargo |
Success /mesa/ok/:slug | Board UI-07 |
| Cover allowlist | Evitar URL arbitraria |
7. Variables de entorno
| Variable | Servicio | Descripción |
|---|---|---|
DATABASE_URL | API | Neon morning-truth-64427459 |
JWT_SECRET | API | Firma tokens |
PORT | API | opcional, 3017 |
JWT_EXPIRES | API | opcional, default 12h |
Web: URL de API en ApiService (default localhost:3017).
8. Smoke de implementación (mínimo)
- Schema OK (tablas existen).
GET /api/issues/currentincluye 4 slugs PUBLISHED y cero DRAFT.GET /api/pieces/lo-que-el-ampa-no-cuelga404.GET /api/eventslength 3.POST /api/auth/login200 +accessToken.GET /api/mesa/piecesBearer incluye el DRAFT.GET /api/mesa/piecessin token 401.PATCH /api/pieces/:id{ status: "PUBLISHED" }200 y la pieza aparece en current.- Web: lookbook dominante, masthead, disclaimer agenda, fotos 16:10.
9. Estructura de ficheros clave (objetivo)
margen-app/
├── package.json
├── apps/api/
│ ├── src/
│ │ ├── main.ts
│ │ ├── app.module.ts
│ │ ├── auth/
│ │ ├── issues/
│ │ ├── pieces/
│ │ └── events/
│ └── .env.example
├── apps/web/
│ ├── tailwind.config.js
│ └── src/app/
│ ├── app.routes.ts
│ ├── core/api.service.ts
│ ├── shell/masthead.component.ts
│ └── pages/
│ ├── home/ # lookbook
│ ├── piece/
│ ├── agenda/
│ ├── login/
│ ├── desk/
│ ├── new-piece/
│ └── ok/
└── README.md
Nombres de fichero orientativos; el contrato es de rutas y tablas, no de filenames exactos.
10. Seed — checklist de implementación
| # | Check |
|---|---|
| 1 | User Nuria Ceballos / redaccion@margen.barrio / password bcrypt de password123 |
| 2 | Issue 17–23 ago · El patio no cierra en agosto · hero.jpg · PUBLISHED |
| 3 | 4 pieces PUBLISHED con slugs de doc 05 y covers alineados a assets |
| 4 | 1 piece DRAFT lo-que-el-ampa-no-cuelga |
| 5 | 3 events (Pradolongo, AMPA, cuento) con starts_at Europe/Madrid |
| 6 | Secciones PATIO / COLE / BARRIO / LECTURA |
| 7 | Idempotencia: upsert por email / week_label / slug / (issue, title evento) |
11. Alineación case ↔ app
| Doc | Evidencia esperada en código |
|---|---|
| 05 data model | columnas y enums |
| 03–04 IA/flows | routes + controllers |
| 08 DS | tailwind colors + fontFamily Cormorant/Public Sans |
| 09 content | strings en templates (microcopy ×3) |
| 00 paper | URLs en README case |
| 11 security | Guard en mesa/POST/PATCH; DRAFT 404 |
12. Notas de cierre técnico
- El case documental L2 está especificado (docs + Paper ref + contratos).
- Must-have de producto del brief están escritos con AC.
- Mejoras (preview token, archivo, e2e, rate limit) viven en backlog L+1, no como deuda silenciosa.
- Si el repo aún muestra
walks/requests/ fotos de aceite, no está alineado: I01 es el primer ítem de build.