00 — Aviso al lector
Este documento es lectura honesta del estado de La Vigencia a fecha de mayo de 2026. Cubre las decisiones de design system y producto tomadas durante los dos meses que separan el primer commit (19 marzo, bajo el nombre PolyNews) del estado actual tras el rebrand y la maduración del newsroom completo.
No es caso de éxito. Es el inventario de las decisiones que sostiene un producto editorial diario sobre regulación española producido por una orquesta de 15 agentes editoriales bajo supervisión humana del conjunto, no de la pieza.
Sin AI-slop. Sin “elevar”, sin “transformar”, sin “potenciar”. El producto declara explícitamente en su propia documentación pública que no finge editorial humana. Este caso conserva esa misma honestidad.
01 — La decisión que cambia todo
El BOE publica entre 80 y 250 disposiciones cada mañana. Ningún Compliance Officer, abogado de empresa o autónomo puede leerlas. Las herramientas existentes son tres: agregadores de RSS sin criterio editorial, suscripciones premium de despacho de cinco cifras anuales, o blogs de bufete que escriben tres veces al mes.
La Vigencia entrega cada mañana, antes de las 09:00 CEST, una edición editorial estructurada del BOE más EUR-Lex más 17 reguladores sectoriales. El registro es el de un periódico generalista de calidad (El País, Cinco Días), no el de un boletín de bufete.
La decisión fundacional del proyecto es doble. Una, construir un periódico, no un agregador. Dos, declarar abiertamente que la producción es agéntica. La segunda es la decisión más interesante para un diseñador senior y la que articula todo lo demás.
La inmensa mayoría de productos editoriales con IA esconden o disfrazan su uso de modelos generativos. La Vigencia hace lo contrario: publica en /como-se-hace el pipeline completo con sus nueve etapas, sus 15 agentes nombrados, sus horarios y la única intervención humana real: revisión del conjunto, no de la pieza. La transparencia metodológica se vuelve activo de marca, no problema de comunicación.
Esa decisión obliga a reescribir las preguntas básicas de cualquier design system editorial:
- ¿Quién firma una pieza producida por un agente? El periódico, no el agente. Byline: “Por La Vigencia · Redacción”.
- ¿Cuál es la unidad editorial primaria? La edición diaria, no la pieza individual. El humano supervisa la edición.
- ¿Qué es voz de marca cuando 15 agentes escriben? Un system prompt extenso con vocabulario aprobado y prohibido, codificado en código y aplicado en dos capas.
- ¿Cuál es la promesa al lector? Que la pieza sea citable, trazable y honesta sobre su origen. No que la haya escrito un humano.
La Vigencia responde a estas cuatro preguntas con coherencia. El resto del documento explica cómo.
02 — Anatomía de una ficha: ocho bloques fijos
Cada disposición publicada vive en su propia URL /d/[id] con estructura editorial fija de ocho bloques. La regularidad no es decorativa: es la condición que permite que 15 agentes coordinados produzcan 5347 fichas con coherencia visual y estructural.
Los ocho bloques
1. Hero con imagen real. El sistema elige imagen siguiendo una jerarquía determinista de seis niveles (ver §06). Foto Wikimedia del protagonista si la pieza cita persona pública, foto Unsplash temática si menciona órgano (Tribunal Constitucional, TJUE, CNMV, AEPD), foto Unsplash sectorial, viñeta LoRA por sector, badge tipográfico como último recurso.
2. Eyebrow + headline editorial. Kicker en versalitas (dos a cuatro palabras) + titular periodístico de máximo 60 caracteres producido por el writer agent. No es el título BOE en bruto, que mide 250-560 caracteres y es ilegible. Es titular escrito, no copiado.
3. Byline + dateline. “Por La Vigencia · Redacción · 12 de mayo de 2026 · 2 min lectura”. Fecha en zona horaria Madrid. Reading time honesto: solo se muestra si el cuerpo supera 80 palabras. Si la pieza es trámite administrativo corto, no se inventa una métrica.
4. Deck + crónica con drop-cap. Sumario de dos frases en cursiva con borde de acento, seguido de crónica editorial de 200-400 palabras estructurada en tres a cuatro párrafos: lede, contexto, implicaciones. Drop-cap en la primera letra del primer párrafo, recurso tipográfico clásico de periódico.
5. En cifras (sidebar). Impacto 1-5, plazo, vigor, sectores. Bloque “Antes/Ahora” si la pieza modifica normativa anterior. Bloque “Coste estimado” si hay análisis de impacto económico publicado.
6. Texto íntegro del BOE colapsado. Extracto de 2000 caracteres del texto legal raw dentro de <details> plegable. Esto es la pieza honesta: el lector puede verificar la fuente sin salir del periódico.
7. Refs cruzadas + histórico de cambios + relacionadas + similares semánticas. Las relacionadas se obtienen con RAG vectorial sobre el corpus (ver §05). Las refs cruzadas y el histórico de cambios se calculan desde la estructura SQL de la disposición.
8. Endmark ◊. Cierre editorial clásico de periódico cultural. Es decisión consciente de marca: el lector reconoce el patrón sin necesidad de explicación.
Lo importante: la regularidad permite todo lo demás
Cuando las 5347 disposiciones tienen los mismos ocho bloques, tres consecuencias operativas:
Componentes reutilizables. El sistema tiene un solo template /d/[id] que renderiza cualquier disposición. Sin variantes ad hoc.
Indexación predecible. El RAG sabe que la crónica está siempre en llm_body, que el deck está en llm_summary, que el impacto está en llm_impact. La búsqueda semántica opera sobre campos estructurados, no sobre prosa libre.
Comparabilidad. Dos disposiciones se pueden cruzar bloque a bloque porque están escritas en el mismo formato. Las refs cruzadas son función del sistema, no de cada pieza.
La irregularidad editorial es romántica. La regularidad es operativa.
03 — El newsroom: 15 agentes coordinados
Esta es la pieza que más interesa a un lector senior y la que define el modelo de producción.
La Vigencia opera un newsroom de 15 agentes editoriales nombrados, cada uno con rol específico, viviendo en src/agents/. No es un script con un único prompt. Es una orquesta con roles diferenciados.
Los 15 roles
- Fetcher. Ingesta desde tres fuentes verificables: BOE Sumario XML, EUR-Lex Cellar SPARQL, TJUE. Retry x3 con backoff exponencial.
- Writer. Produce headline, kicker, deck y crónica de 200-400 palabras siguiendo un system prompt de 150 líneas con vocabulario aprobado y prohibido.
- Editor-in-chief. Selecciona la edición del día: cuál es el lead, cuáles los cuatro secondaries, qué orden sectorial sigue la portada.
- Fact-checker. Revisa cifras, fechas y citas concretas contra la fuente BOE.
- Copy-desk. Aplica reglas de estilo y consistencia.
- Photo-editor. Selecciona imagen siguiendo la jerarquía determinista de seis niveles.
- News-anchor. Produce el guion narrado del audio briefing diario.
- Columnist. Produce las piezas de opinión.
- Archivist. Mantiene la hemeroteca con timestamps de cambios.
- Validator. Verifica que la pieza cumple los gates antes de persistir.
- Investigative-reporter. Detecta patrones a través de múltiples disposiciones.
- Orchestrator. Coordina la pipeline diaria y dispara alertas en caso de fallo.
- News-anchor-runner, news-anchor-templates, newsroom-daily. Subagentes operativos.
Lo que esto significa operativamente
Cada agente tiene su propio system prompt, su propia salida estructurada (los JSON shapes varían según rol) y sus propios gates de aceptación. No es un único LLM produciendo de todo. Es un sistema de roles donde el writer no decide qué se publica, el editor-in-chief no escribe, el fact-checker no titula.
Para un Design System Architect senior la lectura es directa: el newsroom es un design system aplicado a la producción editorial agéntica. Tokens (vocabulario), componentes (agentes con rol específico), reglas (gates de aceptación), variantes (subagentes operativos). La misma disciplina que aplica un equipo de DS bien constituido a una librería de componentes, aplicada a la generación de contenido.
El humano interviene sobre el conjunto, no sobre la pieza
La intervención humana es una y vinculante: revisar la portada cada mañana y una muestra aleatoria de diez piezas. Si detecta deriva (palabras prohibidas, registro burocrático, alucinación), ajusta el system prompt del agente correspondiente, no reescribe la pieza.
Esto importa porque define qué hace el operador único en este modelo: no produce, gobierna el sistema que produce. Es la misma regla operativa que en Lo Peix (el operador construye el pipeline, no las piezas), aplicada con disciplina mayor: aquí el humano ni siquiera escribe el guion, escribe el guion que escribe el guion.
04 — Voz editorial codificada como sistema
Aquí está la decisión que separa al proyecto de un wrapper de ChatGPT a pelo. La voz editorial no vive en el cerebro del operador. Vive en el sistema, en tres capas.
Capa 1 · System prompt del writer
El prompt del writer en src/agents/writer.ts ocupa aproximadamente 150 líneas y codifica reglas explícitamente prohibitivas. Algunas literales:
- “PROHIBIDO empezar con ‘El Boletín Oficial del Estado…’, ‘Mediante…’, ‘Por la presente…’, ‘Se hace saber…’, ‘La resolución que nos ocupa…’”
- “PROHIBIDO la sintaxis ‘por la que se’, ‘mediante la cual’, ‘se publica que’. Reescribe en activa: ‘publica’, ‘obliga a’, ‘multa a’, ‘modifica’.”
- “TONO: periódico generalista de calidad (El País / Cinco Días). No es un blog de fan ni un tuit. No mete adjetivos opinativos (‘escandaloso’, ‘polémico’).”
Y lista de muletillas con su versión llana: “abre convocatoria para cubrir” → “convoca”, “en el marco de” → “en”, “tendrá lugar” → “será”, “por su parte” → eliminar.
Capa 2 · Sanitización defensiva en persistencia
Cuando el writer devuelve la crónica, antes de guardar en Neon, dos funciones la limpian: cleanJournalese() y fixMissingDe(). Esto es disciplina de DS aplicada al texto: el corpus que entra a la base de datos ya está sanitizado.
Capa 3 · Sanitización defensiva en renderizado
Cuando una página /d/[id] se renderiza, las mismas dos funciones se aplican otra vez sobre el texto leído de DB. Es red de seguridad para piezas legacy que entraron al corpus antes de las reglas actuales.
Por qué importa la doble capa
Si las reglas viven solo en el prompt del LLM, mejorarlas significa re-procesar 5347 items. Si viven solo en render-time, la DB queda sucia y cualquier consumidor del corpus (/data/news.jsonl, MCP futuro, agentes externos) recibe texto sin curar.
La doble capa permite tres cosas operativas:
- La DB queda limpia para consumidores estructurados.
- El render time corrige corpus legacy sin reprocesar.
- Añadir una regla nueva propaga inmediatamente al corpus existente sin coste de re-enrich.
Esto es la disciplina de tokens y componentes aplicada al texto. Separa generación de presentación, y aplica reglas en ambos extremos del pipe.
Lo que un DS senior se lleva
Si tu producto tiene contenido generado por LLM en producción y tu única defensa de voz es el prompt, tu voz va a derivar con cada cambio de modelo. La defensa en dos capas vuelve la voz independiente del modelo subyacente.
05 — RAG semántico sobre el corpus regulatorio
/d/[id] muestra al final de cada ficha una sección de normas semánticamente similares con score de similitud 0-100. Detrás hay un RAG funcionando sobre el corpus completo de 5347 disposiciones.
Stack del RAG
- Almacenamiento vectorial. pgvector sobre Neon Postgres. Mismo plano que el corpus principal, sin servicio aparte.
- Modelo de embeddings. e5 multilingüe, 1024 dimensiones. Soporte español nativo, coste razonable.
- Estrategia de chunking. Por documento completo. Cada disposición es un chunk. Para piezas con
body_excerpt_longextenso (hasta 5000 caracteres), el embedding se construye sobre headline + summary + primer párrafo del body. - Búsqueda híbrida. Vectorial + filtros léxicos por sector + filtros temporales. Sin componente geoespacial (no aplica a regulación).
- Endpoint. Lookup por id en
semanticSimilarTo(id, k)desdesrc/lib/rag-search.ts. - Modelo LLM generador. DeepSeek-V3 vía Together AI ($1.25 por millón de tokens). Salto cualitativo notable sobre Llama 70B en titulares 5W.
- Doble proveedor para resiliencia. Fallback automático a Workers AI Llama 3.1 8B / Llama 3.3 70B vía Cloudflare AI Gateway. El writer agent intenta Together primero; si falla, cae a Workers AI con el mismo prompt.
Por qué un solo proveedor de DB para corpus + vectores
Decisión arquitectónica explícita: el corpus y los vectores viven en la misma Neon. Esto evita el patrón habitual de “Postgres para datos, Pinecone o Weaviate para vectores”. Una sola query SQL combina filtros relacionales y similitud vectorial. Un solo punto de mantenimiento.
Para un DS senior es lectura útil: cuando una decisión arquitectónica reduce la complejidad operativa sin sacrificar funcionalidad, suele ser la correcta. La complejidad del sistema crece con cada proveedor externo añadido.
Lo que falta y se reconoce
El MCP server público está planificado, no construido. Cuando se construya, cada disposición será consultable por agentes externos vía protocolo MCP sin tener que parsear HTML. La superficie agéntica completa de La Vigencia llegará entonces; hoy es JSON-LD denso + /data/news.jsonl público + RAG interno.
06 — La jerarquía de imágenes infalible
Esta es la pieza más interesante del proyecto desde el punto de vista del DS aplicado a sistemas con IA, y merece análisis detallado.
El problema
Un periódico necesita imagen real. Stock fotografía corporativa convierte cualquier pieza en boletín de bufete. Cartoon LoRA en cada pieza convierte el periódico en blog ilustrado. La regulación habla de personas concretas (consejeros, ministros, magistrados) y de órganos concretos (TC, TJUE, CNMV, AEPD): cada pieza pide su imagen específica.
Un equipo con photo editor profesional resuelve esto a coste alto. Una pipeline agéntica tiene que resolverlo a coste cero por pieza.
La solución: seis niveles de fallback en cascada
El photo-editor agent aplica jerarquía determinista por hash del id de la pieza. Mismo id, misma foto siempre. Auditable.
Nivel 1 · Retrato Wikimedia de persona pública mencionada. El agent normaliza el texto con normalize('NFD') para hacer la búsqueda acento-insensitive, aplica longest-match-wins (si una pieza menciona “Felipe VI” y “VI”, gana “Felipe VI”), y usa word-boundary regex para evitar falsos positivos. 46 personas públicas curadas en data/people-photos.json.
Nivel 2 · Foto curada hardcoded para temas recurrentes. Marina, ferrocarril, aviación. Listado manual donde la atribución temática es obvia y estable.
Nivel 3 · Foto Unsplash temática por órgano detectado. Si la pieza menciona TC, TJUE, CNMV, AEPD, sanciones: la pipeline tiene foto Unsplash asignada a cada uno de esos órganos. 23 sectores/temas en data/stock-photos.json.
Nivel 4 · Foto Unsplash sectorial. Si la pieza pertenece a uno de los 18 sectores definidos pero no menciona órgano específico, se elige foto sectorial.
Nivel 5 · Viñeta LoRA por sector. Estilo único entrenado con joanarbo/lv-style@749bae37 en Replicate. Una viñeta por sector como fallback ilustrativo. Idempotencia en generate-cartoon.ts (skip si archivo existe) + modelo gratis por defecto (FLUX-schnell-Free).
Nivel 6 · Badge tipográfico. Último recurso. Una placa tipográfica con el slug del sector. Imagen pobre pero coherente con la voz tipográfica del periódico.
Por qué esto es DS y no contenido
Tres razones que importan para un practitioner senior:
Determinismo. Mismo id, misma foto siempre. Esto es disciplina de DS aplicada a la composición visual: la pieza tiene la misma identidad cada vez que se renderiza. No hay deriva por aleatoriedad del agente.
Auditabilidad. La regla que eligió cada foto es trazable. Si una foto está mal, se ajusta el catálogo o la regla, no se reentrena el agent.
Sin coste runtime. Los catálogos son JSON commiteado al repo. La selección es lookup local. Cero llamadas externas en cada render.
Para un DS senior, esto es ejemplo de cómo se construye un sistema de imagen para producto editorial con IA sin caer en “que el agente decida la foto en cada pieza”. La decisión la toma el sistema con criterio codificado, no el LLM por interpretación.
07 — Tres horarios, una edición: el cron self-healing
El producto promete una edición publicada antes de las 09:00 CEST cada mañana laborable. La implementación de esa promesa pasa por una decisión operativa pequeña pero significativa.
El problema
El BOE publica su sumario XML cada noche. Si el fetcher hit un blip del BOE de dos minutos en la única ventana en la que corre el cron, la edición del día no publica. Pasó el 11 de mayo de 2026: la home se quedó con la edición del viernes durante tres días.
La solución
Triple schedule del workflow regpulse-newsroom.yml:
| Ejecución | Schedule |
|---|---|
| Primera ejecución | 30 5 * * * (05:30 UTC) |
| Segunda ejecución | 30 6 * * * (06:30 UTC) |
| Tercera ejecución | 30 7 * * * (07:30 UTC) |
El pipeline es idempotente. Si la primera tuvo éxito, las siguientes hacen upsert sobre la base de datos sin cambios y el commit step termina con “No changes — saltando build+deploy”. Esto ahorra aproximadamente seis minutos de CI por ronda extra.
Si la primera falló, la segunda retoma desde el último estado consistente. Si la segunda también falló, la tercera lo intenta. Cero acción humana cuando el BOE blippea.
El reordenamiento del workflow
Pieza adicional aprendida del incidente del 12 de mayo. El paso refresh:data (BDNS + PLACSP + EU infringements + AEPD scraping) estaba al principio del workflow. Ese scrape hit el timeout de 30 minutos, mató el job entero, y la edición editorial nunca llegó a Cloudflare.
La solución no fue acelerar BDNS. Fue mover refresh:data al final del workflow con continue-on-error: true. La pipeline ahora publica la edición editorial primero; los datos auxiliares hacen catch-up sin bloquear el deploy.
Lo que un DS senior se lleva
Dos lecciones operativas:
Idempotencia como diseño de día uno. Cualquier pipeline que dependa de fuente externa debe poder reintentarse sin daño. Esto cambia el diseño de los handlers desde el principio.
Separación entre lo crítico y lo decorativo. En cualquier producto editorial hay un núcleo (la edición del día) y hay accesorios (datos auxiliares, métricas, secundarios). Los accesorios no pueden bloquear el núcleo. Si lo hacen, se reordena el pipeline.
08 — Producto multi-tier y demo-gating como mecanismo de filtro
El producto vende a siete tiers desde 0 € hasta enterprise demo-gated. La decisión interesante para un DS senior no es la lista de precios sino el routing por audiencia.
Los siete tiers
| Canal | Precio | Persona dominante | Estado |
|---|---|---|---|
| Gratuito | 0 € | Cualquiera | activo |
| Autónomo | 15 €/mes | 3,4M autónomos españoles | activo |
| Starter | 29 €/mes | Profesional individual con un sector | activo |
| Compliance | 99 €/mes (BETA -50%, normal 199 €) | CCO / Head of Legal en startup IA, FinTech, HealthTech | activo |
| Annotate (despachos) | Demo · pricing por despacho | Despacho jurídico boutique 5-15 abogados | demo-gated |
| Enterprise | Demo · pricing por uso | Compañía con equipo legal + necesidad API | demo-gated |
| Enterprise Pro | Demo · pricing a medida | Multinacional / IBEX35 / despacho grande con SLA | demo-gated |
Routing por landing dedicada
A diferencia de lorsclub que separa audiencias por subdominio, La Vigencia las separa por landing dedicada: /autonomos (15 €), /lawyers (Annotate), /ia-compliance (Compliance 99 €), /precios (todos los tiers).
Cada landing es producto en sí mismo: dateline editorial, product-tag negro, eyebrow específico. Un patrón visualmente memorable (dateline + double rule + ornamento ❦ + product-tag) replicado en las nueve landings del catálogo. Para un DS senior la lección operativa es que la coherencia visual entre las landings de producto vale más que la creatividad por landing.
Demo-gating como mecanismo de filtro, no de venta
Tres tiers superiores (Annotate, Enterprise, Enterprise Pro) no tienen precio público. Esto no es opacidad: es información asimétrica gestionada con criterio. El primer precio que ponga el producto en esos tiers ancla la negociación. Sin precio público, la venta arranca con 20 minutos de conversación en Cal.com, propuesta calibrada al caso real.
Es la misma lógica del white-label en lorsclub: el sistema permite vender a audiencias muy distintas sin que ninguna vea la oferta de las otras. Aquí se aplica al pricing de enterprise.
Lo que no se construyó en pricing
- Tier Lawyers per-firm con precio público. Lock-in temprano sin saber qué pagaría un despacho real. Demo-gating filtra mejor.
- Precio público Enterprise. Mismo razonamiento + protección contra anclaje de competidores.
- White-label / multi-tenant. Fragmentaría la voz editorial.
- Display ads. Conflicto con voz editorial declarado en
/como-se-hace. - Paywall en el corpus base. Literal del producto: “la promesa es la edición, no el acceso”.
09 — Lo que NO se construyó
Para un Design System Architect senior esta sección suele ser la más útil del documento.
| No construido | Por qué | Qué ocupó su lugar |
|---|---|---|
| Multi-idioma (EN, CA, EUS) | La regulación es española, el lector es español, el LLM trabaja en español. ALL_LOCALES = ['es'] literal en seo.ts. | Foco editorial en es-ES sin compromisos. |
| White-label / multi-tenant | Fragmentaría la voz editorial; el sistema editorial es la marca. | Single tenant fuerte + Annotate como workspace despacho. |
| Editorial humana fingida | ”No fingimos editorial humana — lo decimos aquí” (literal en /como-se-hace). | Transparencia metodológica publicada: nueve etapas declaradas con horarios y agentes. |
| Publicidad / sponsored content | ”Sin afiliados. Sin tracking de terceros” (literal /como-se-hace). | Suscripción directa multi-tier. |
| Paywall en el corpus base | ”La promesa es la edición, no el acceso”. | Corpus público + diferenciación por curación, RAG semántico y crónica. |
| Editorial neutra sin pauta de voz | Sería ChatGPT a pelo. | System prompt extenso con vocabulario aprobado/prohibido + cleanJournalese() + fixMissingDe() en dos capas. |
| ChatGPT plugin / OpenAI store | Roadmap MCP nativo (más estándar). | MCP planificado. |
| Comments / community / engagement metrics | Ruido sobre el producto editorial. El lector compara contra El País, no Reddit. | Share gate con email opcional + Telegram channel para distribución. |
| Personalización heavy (feed algorítmico por usuario) | Cada lector ve la edición. La portada es responsabilidad del editor-in-chief agent, no del feed. | Edición única con sectores filtrables como capa secundaria. |
| Cron único a 05:30 sin retries | Un blip BOE de 2 min mata la edición del día (incidente real 11-mayo). | Triple-schedule self-healing 05:30/06:30/07:30. |
El patrón
Cada “no” preserva una decisión más profunda: La Vigencia es un periódico, no un agregador, no un SaaS, no una API. Cada feature rechazada que parecía obvia para “un producto digital de IA en 2026” es exactamente lo que diluiría el posicionamiento.
Para un DS senior cada “no” se traduce en componentes que no se construyen. Sin comments, no hay componente de comentarios. Sin personalización heavy, no hay componente de feed configurable. Sin multi-idioma, no hay sistema i18n. Cada “no” simplifica el sistema. Cada “sí” añade superficie a mantener.
10 — Frases citables del producto
| Frase | Dónde aparece | Función estratégica |
|---|---|---|
| ”La regulación que rige, antes de que afecte a tu negocio” | Home, meta description global | Posiciona el producto como forward-looking |
| ”Lo que viene, no lo que ya pasó” | Vigía sectorial landing | Diferenciación clara: monitorización vs análisis post-hoc |
| ”Tres horas. Nueve manos. Un solo gate humano sobre el conjunto” | /como-se-hace deck | Define el modelo de producción honestamente |
| ”No fingimos editorial humana — lo decimos aquí” | /como-se-hace FAQ | Anti-AI-washing explícito como ventaja competitiva |
| ”Tu BOE en 90 segundos” | /brief landing | Audio briefing diario |
| ”El mismo Real Decreto. Tres lecturas. Una conversación” | /lawyers Annotate | Anotaciones colaborativas como pitch sintetizable |
| ”Cuánto te cuesta cumplir” | /impacto landing | Reframea regulación como problema de presupuesto |
| ”No te decimos sólo qué cambió — te decimos cómo cambió” | /cambios landing | Diferenciación frente a agregadores |
| ”Honestamente: nada al 100%“ | /predicciones deck | Humildad como credibilidad |
| ”Score 0-100 verificable, no opacidad” | /vigia landing | Métrica auditable como rasgo de marca |
| ”No te suscribimos a nada” | Share gate en /d/[id] | Anti-dark-pattern explícito |
| ”La promesa es la edición, no el acceso” | /como-se-hace | Define qué se vende y qué no |
Tres registros conviven: editorial clásico (gravedad periodística), tecnológico declarado (transparencia sobre el método agéntico), comercial honesto (anti-dark-pattern). La densidad por línea es alta. El cliché está prohibido.
11 — El design system: qué hay y qué falta
La Vigencia tiene design system parcialmente formalizado. Lectura honesta de qué existe y qué está pendiente.
Lo que hay
Tokens como CSS variables globales.
- Color cinabrio
#CC3F2Acomo accent semántico (siempre marca acción o jerarquía editorial). - Ink
#1A1A1Acomo tinta principal. - Paper-warm
#FAF8F4como fondo. - Familia tipográfica triple: Newsreader (serif display y body), EB Garamond (fallback), JetBrains Mono (versalitas y dateline).
- Sin sans-serif primario. Decisión editorial consciente: el periódico es serif.
Patrón de cohesión cross-landing. Tres marcadores idénticos en las nueve landings: dateline con weekday-day-month-year-roman-Madrid, double rule + ornamento ❦, eyebrow con product-tag negro inline (ej. “La Vigencia · Brief · Producto · Audio”). CSS prefix por página (inf-, rbf-, rlw-) evita conflictos.
Iconografía propia. Componente Icon.astro con SVGs line-drawing (hourglass, bolt, alert, flag-eu, cash). Prohibición editorial explícita de emojis en producción.
Imagen LoRA con estilo único. Modelo joanarbo/lv-style@749bae37 entrenado en Replicate. Una viñeta por sector usada como fallback ilustrativo.
Sistema de score visual. Score 0-100 con tres umbrales semánticos (verde <30, ámbar <60, rojo ≥60) en el producto Vigía. El color es semántico cuando codifica gravedad.
Lo que falta
- No hay
tokens.jsonformal. - No hay Storybook.
- El patrón de las nueve landings se replica a mano, sin componente Astro compartido.
- No hay sistema documentado de spacing más allá de
clamp()responsive. - No hay variantes de DS para densidad de información (la pieza es siempre la misma para todos los dispositivos).
Lectura para un DS senior
El sistema funciona porque la marca es tipográfica antes que componencial. Una vez fijadas Newsreader + cinabrio + ornamento ❦, la coherencia visual viaja sin necesidad de tokens formales. Es decisión consciente: el coste de formalizar Storybook para un producto de operador único excede el valor que aporta.
Esta es lección operativa contracorriente: no todo design system tiene que ser DTCG-compliant. Para un producto editorial de un operador donde el branding es tipográfico, las variables CSS bien nombradas y el patrón replicado son suficientes. Cuando llegue equipo, formalizar.
12 — Cómo lee un agente externo La Vigencia
A diferencia de productos que ocultan su capa agéntica, La Vigencia está construida para ser consumida por agentes externos con la misma calidad que por humanos.
Tres superficies para agentes
JSON-LD embebido en cada /d/[id]. Once tipos de schema: NewsArticle, NewsMediaOrganization, WebSite, WebPage, BreadcrumbList, ListItem, ImageObject, CreativeWork, SpeakableSpecification, ContactPoint, Thing.
Cada NewsArticle lleva tres campos clave para citación:
articleBodycon el cuerpo completo de la crónica.isBasedOnapuntando a la fuente BOE primaria.citationconidentifier(elnumero_oficial).speakablecon cssSelector sobre el deck y el sumario, para asistentes de voz.
Endpoint /data/news.jsonl con corpus completo. 5347 disposiciones servidas como asset estático JSONL desde edge. Un crawler o agente externo descarga el corpus completo en una sola request sin parsear HTML.
ai.txt declarando políticas de uso. Presente en dist/ai.txt. agents.txt y llms.txt planificados.
Lo que falta y se reconoce
Sin detección de consumidor en runtime. A diferencia de lorsclub que sirve renderings distintos según el User-Agent, La Vigencia entrega el mismo HTML a humano y agente. El JSON-LD denso hace que el HTML sea consumible por ambos. Es decisión consciente: el coste de mantener dos renderings supera la ganancia.
MCP server público planificado. Cuando se construya, la capa agéntica completa estará disponible. Hoy es JSON-LD + JSONL + RAG interno + lookup por id.
Lo que un DS senior se lleva
Hay dos modelos de “diseñar para agentes externos”. Lo Peix tiene agente embebido (concierge IA dentro de la app móvil). Lorsclub tiene render diferenciado por consumidor (HTML distinto según User-Agent). La Vigencia tiene render unificado con JSON-LD denso (mismo HTML para los dos, semántica embebida).
Los tres son legítimos. La elección depende de tres factores: si quieres mostrar UI distinta a cada consumidor (lorsclub), si el agente vive dentro de tu producto (Lo Peix), o si te basta con que el mismo HTML sea legible para ambos (La Vigencia). Es decisión arquitectónica con consecuencias de mantenimiento concretas. No hay respuesta universal.
13 — Lo que un DS Architect senior se lleva de aquí
Cinco decisiones aplicables a tu propio sistema, ordenadas por dificultad creciente.
1. Declarar el modelo de producción (fácil)
Si tu producto se apoya en LLM, declararlo explícitamente vale más que ocultarlo. La Vigencia publica /como-se-hace con nueve etapas, 15 agentes nombrados, horarios y gates. La transparencia metodológica se vuelve argumento de venta, no problema. Replicable en cualquier producto editorial con IA: una página /como-se-hace ahorra mil discusiones en LinkedIn sobre AI-washing.
2. Sanitización defensiva en dos capas (fácil-medio)
Si tu producto persiste texto generado por LLM, las reglas de limpieza viven en dos sitios: en persistencia (DB queda limpia) y en renderizado (corpus legacy se sanitiza sin reprocesar). Replicable en cualquier producto con corpus textual donde la voz importa. La doble capa cuesta poco y rentaba mucho.
3. Cron self-healing con triple schedule (medio)
Si tu producto depende de fuentes externas con publicación programada, triple schedule consecutivo + idempotencia + early-exit ahorra acción humana. La Vigencia publica 05:30, 06:30, 07:30 UTC. Si la primera tuvo éxito, las dos siguientes son no-ops. Si la primera falló, la segunda retoma. Cero alertas a las 3 AM. Replicable en cualquier pipeline editorial con dependencia externa.
4. Jerarquía determinista de imágenes por hash (medio-difícil)
Si tu producto compone visualmente piezas de contenido con activos heterogéneos (fotos, ilustraciones, badges), una jerarquía de seis niveles con catálogos curados commiteados al repo y selección por hash determinista bate a cualquier “agente decide la imagen”. Cero coste runtime, auditabilidad completa, misma identidad visual cada vez que se renderiza la pieza. Replicable en cualquier producto editorial con composición visual.
5. Voz editorial codificada como sistema de roles (difícil)
Si tu producto se apoya en LLM para generar contenido, la voz vive en el sistema de roles, no en el prompt monolítico. Quince agentes con responsabilidades diferenciadas, con vocabulario aprobado y prohibido por rol, con gates de aceptación por rol. Esto es disciplina de DS aplicada a la producción editorial. La voz no es función del modelo subyacente, es función del sistema que lo orquesta.
14 — Anexo técnico
Para quien quiera verificar las afirmaciones del documento contra el código.
Stack
| Capa | Tecnología |
|---|---|
| Frontend | Astro 5 + TypeScript |
| Hosting | Cloudflare Pages (proyecto polynews) |
| Backend / Functions | Cloudflare Worker via @astrojs/cloudflare adapter |
| DB principal | Neon Postgres |
| Corpus público | JSONL asset estático servido desde edge (/data/news.jsonl, 12,6 MB) |
| Vector search | pgvector sobre Neon |
| Embeddings | e5 multilingüe · 1024 dimensiones |
| LLM principal | DeepSeek-V3 vía Together AI |
| LLM fallback | Workers AI Llama-3.1-8B / Llama-3.3-70B |
| Pagos | Stripe (payment links + webhooks) |
| Resend + ButtonDown + Beehiiv | |
| Storage | Cloudflare R2 + KV |
| Anti-abuse | Cloudflare Turnstile |
| Analytics | Umami + Cloudflare Insights + GA4 |
| Build pipeline | pnpm + Astro build → wrangler pages deploy |
| CI / CD | GitHub Actions (3 workflows · 5 schedules) |
| Tests | Vitest (27 unitarios) + Playwright (7 specs) + smoke-prod custom (12 checks) |
Números del repositorio
- Funciones SSR activas: 52
- Migraciones SQL: 7 (drizzle)
- Tests unitarios: 27 (vitest, 6 archivos)
- Tests e2e: 7 archivos Playwright
- Smoke prod checks: 12
- Schemas JSON-LD distintos: 11
- URLs en sitemap-news: 90
- HTML prerenderizados: 70 archivos
.astroensrc/pages/(más rutas dinámicas) - Cron jobs operativos: 5 schedules en 3 workflows
- Idiomas con landing nativa: 1 (es)
- Scripts CLI: 82 (
scripts/*.tsy*.mjs) - Agentes editoriales: 15
- Sectores definidos: 18
- Disposiciones en corpus público: 5347
Pipeline editorial diario
05:30 UTC · regpulse-newsroom.yml (intento 1)
↓ si falla
06:30 UTC · regpulse-newsroom.yml (intento 2)
↓ si falla
07:30 UTC · regpulse-newsroom.yml (intento 3)
Cada intento ejecuta:
→ ingest BOE Sumario XML (retry x3, backoff exponencial)
→ ingest EUR-Lex Cellar SPARQL
→ ingest TJUE
→ enrich con writer agent (DeepSeek-V3, fallback Workers AI)
→ fact-check
→ photo-editor (jerarquía 6 niveles)
→ editor-in-chief (selecciona lead + secondaries)
→ news-anchor (guion audio briefing)
→ persistir con cleanJournalese() + fixMissingDe()
→ commit + push
→ astro build
→ wrangler pages deploy
→ smoke-prod (12 checks)
→ refresh-data (BDNS + PLACSP + UE + AEPD, continue-on-error: true)
Si stored=0 && errors>0 → process.exit(1) + alerta Telegram
Schemas JSON-LD (11 tipos)
NewsArticle · NewsMediaOrganization · WebSite · WebPage · BreadcrumbList · ListItem · ImageObject · CreativeWork · SpeakableSpecification · ContactPoint · Thing
Sanitización defensiva (doble capa)
LLM output (writer agent)
→ cleanJournalese()
→ fixMissingDe()
→ persist a Neon
Render time (/d/[id])
→ fetch from Neon
→ cleanJournalese() (red de seguridad para corpus legacy)
→ fixMissingDe()
→ render HTML + JSON-LD
Verificación reproducible
# Sitemap público
curl -s https://lavigencia.com/sitemap-index.xml | grep -c '<loc>'
curl -s https://lavigencia.com/sitemap-news.xml | grep -c '<loc>'
# → 90
# Schemas JSON-LD distintos
grep -rhoE '"@type":\s*"[A-Z][a-zA-Z]+"' src/ | sort -u | wc -l
# → 11
# Migraciones SQL
ls drizzle/*.sql | wc -l
# Idiomas
grep "ALL_LOCALES" src/lib/seo.ts
# → ALL_LOCALES: SeoLang[] = ['es']
# Crones operativos
grep -E "cron:" .github/workflows/*.yml | wc -l
# → 5
# Tests
pnpm test 2>&1 | grep -E "Tests "
# → Tests 27 passed (27)
# Smoke prod
BASE_URL=https://lavigencia.com pnpm smoke:prod
# → 12 check(s) OK
# Agentes editoriales
ls src/agents/*.ts | wc -l
# → 15
# Corpus público
curl -s https://lavigencia.com/data/news.jsonl | wc -l
# → ~5347
# Catálogos de imagen curados
cat data/people-photos.json | jq 'keys | length'
# → 46
cat data/stock-photos.json | jq 'keys | length'
# → 23
URLs públicas verificables:
- Edición diaria:
https://lavigencia.com/ - Ficha individual:
https://lavigencia.com/d/boe-BOE-A-2026-10225 - Pipeline declarado:
https://lavigencia.com/como-se-hace - Vigía sectorial:
https://lavigencia.com/vigia - Audio briefing:
https://lavigencia.com/brief - Pricing:
https://lavigencia.com/precios - Corpus público:
https://lavigencia.com/data/news.jsonl
Glosario
| Término | Definición canónica |
|---|---|
| Edición | Conjunto de disposiciones del día seleccionadas y ordenadas por el editor-in-chief agent. Una edición = un lead + cuatro secondaries + sector pages + audio briefing |
| Disposición | Unidad atómica del corpus. Cada BOE-A-YYYY-NNNN es una disposición |
| Crónica | El campo llm_body de 200-400 palabras producido por el writer agent. Tres párrafos: lede / contexto / implicaciones |
| Vigía | Producto forward-looking que monitoriza cinco vetas por sector con score 0-100 |
| Annotate | Producto para despachos. Anotaciones colaborativas con colores por categoría + workspace por despacho. Demo-gated |
| Brief | Producto audio. “Tu BOE en 90 segundos” — guion narrado por agente news-anchor + voz sintética |
| Reports | Producto mensual. Un informe por sector con métricas, top piezas y coste agregado |
| Antes/Ahora | Producto de diff narrado. Para cada modificación, muestra qué decía antes y qué hay que hacer |
| Score Vigía | Indicador 0-100 sectorial: densidad 25 + plazos 25 + inestabilidad 20 + UE 20 + concesiones 10. Componentes trazables |
| Veta | Cada una de las cinco categorías que monitoriza Vigía. Filón temático |
| Concesión directa | Reparto sin concurrencia detectado por NER sobre el texto íntegro BOE |
| Inestabilidad | Norma “parcheada” recientemente: Real Decreto modificado en menos de 60 días |
| Newsroom | El conjunto de 15 agentes editoriales en src/agents/ |
Caso lavigencia.com · Lectura desde dentro del oficio · Versión 1.0 · Septiembre 2026 Autoría: Joan Arbó · joanarbo.com