Saltar al contenido

Anunciarse en NOalergeNO

Arquitectura de publicidad

El cortafuegos, y por qué es real

La separación entre motor de seguridad y motor comercial no es una política que alguien pueda saltarse con una prisa un viernes. Está sostenida por tres mecanismos independientes:

1. Grafo de dependencias. apps/pwa/js/anuncios.js no importa nada de packages/motor. No es que no lo use: es que no puede.


grep -r "packages/motor\|@noalergeno/motor" apps/pwa/js/anuncios.js
# sin resultados

2. Un contexto que se niega a contaminarse. La única entrada del motor comercial es construirContexto(), que lanza una excepción si detecta cualquier campo de salud:


const VETADOS = ['perfil','evita','alergenos','condiciones','salud',
                 'diagnostico','health_profile','porNo','matriz','resultado'];

Si alguien intenta pasar el perfil, no sale un anuncio peor: no sale anuncio y se registra un error. Fallar en cerrado, no en abierto.

3. Bases de datos distintas. 002_publicidad.sql está escrito para ejecutarse en otra base. No hay ninguna clave foránea del esquema comercial al esquema de personas. Aunque alguien escribiera la consulta, no hay JOIN posible.

Lo que entra al motor comercial


{
  categoria: 'galletas',
  subcategoria: null,
  mercado: 'ES',
  idioma: 'es',
  productoActual: '8410000810004',
  tipoContexto: 'resultado'
}

Eso es todo. Contexto de compra. No hay ni un campo donde quepa una condición de salud, y el esquema de ad_contexts en base de datos tampoco lo tiene.

Lo que se vende

No se vende «audiencia celíaca». Se vende contexto de compra de alta intención:

«Un consumidor está buscando una alternativa en la categoría galletas, en España.»

La diferencia no es de marketing. Es que la primera frase requiere saber que la persona es celíaca, y la segunda no. Una impresión después de un producto incompatible vale más que un banner genérico precisamente porque hay intención, no porque haya un diagnóstico.

Zonas sin publicidad

Nunca, bajo ninguna campaña, ninguna excepción:

  • El escáner y la cámara.
  • La pantalla de proceso.
  • El resultado, su explicación y sus evidencias.
  • Información insuficiente e información contradictoria.
  • Alertas alimentarias y retiradas.

Está implementado en el motor: compararConPerfil() devuelve zonaSegura: true para contiene, puede, contradictoria, insuficiente y muy_bajo, y pintarResultado() no llama al módulo comercial si zonaSegura es cierto.

Verificado en navegador — ver TESTING.md:

ResultadoAnuncios en la pantalla
CONTIENE0
PUEDE CONTENER0
INFORMACIÓN CONTRADICTORIA0
INFORMACIÓN INSUFICIENTE0
DECLARADO SINtras el bloque de fuentes
NO HEMOS DETECTADOtras el bloque de fuentes

Orden de la pantalla


RESULTADO
   ↓
POR QUÉ
   ↓
EVIDENCIA
   ↓
FUENTE
   ────────── frontera visible ──────────
ALTERNATIVAS (orgánicas, sin pagar)
   ↓
PATROCINADO
   ↓
DÓNDE COMPRAR

Nada de la derecha adelanta a nada de la izquierda. Además, el módulo comercial se pinta en un requestAnimationFrame posterior y dentro de un try: no puede retrasar el resultado ni romperlo si falla.

Formatos

Por prioridad de inventario:

1. Alternativa patrocinada — el formato estrella. Aparece cuando la marca puede ofrecer algo relevante, después de un resultado incompatible. 2. Búsqueda patrocinada. 3. Patrocinio de categoría. 4. Colocación de retailer. 5. Cupón. 6. Banner — deliberadamente el último.

Elegibilidad

Una marca no clasifica sus propios alérgenos. Puede aportar datos; la clasificación la determina NOalergeNO.


La marca pide:  "quiero anunciar esto como sin gluten"
       ↓
¿Hay evidencia adecuada?
       ├── sí → eligible, con la evidencia registrada y una fecha de caducidad
       └── no → not_eligible

En ad_eligibility: status, evidence, evidence_source, verified_at, expires_at. Si la evidencia caduca, la campaña se suspende sola. Si aparecen fuentes contradictorias sobre el producto, las campañas afectadas se pausan. Seguridad por encima de ingreso, y automatizado para que no dependa de que alguien se acuerde.

Identificación y transparencia

  • Toda publicidad va etiquetada «Patrocinado», con su propia superficie visual (cálida y neutra) y nunca con los colores de la escala semafórica.
  • El lector de pantalla la anuncia como publicidad antes de leer su contenido (aria-label="Publicidad de <marca>").
  • «¿Por qué veo este anuncio?» está implementado y responde con el contexto real: «Este anuncio aparece porque estás consultando la categoría galletas en ES. No usamos tu perfil de salud para elegirlo. Paga: <anunciante>.»
  • ad_transparency_log guarda la creatividad tal como se mostró, el anunciante, quién paga, el contexto y el motivo. Para poder auditarlo años después.

Prohibido

Intersticiales antes del resultado. Vídeos obligatorios. Cuentas atrás. Popups. Anuncios encima del escáner. Autoplay con sonido. Anuncios que parezcan alertas. Anuncios que usen los colores del sistema de seguridad. Dark patterns.

Si no hay nada relevante, no se rellena

Implementado: si no hay candidato para el contexto, no sale anuncio. Sale una frase honesta:

Todavía no tenemos alternativas que ofrecerte para esta categoría. Preferimos no enseñarte nada antes que enseñarte cualquier cosa.

Confianza del usuario por encima de tasa de relleno. Es una decisión económica consciente, no un descuido.

Menores

Los perfiles infantiles los gestiona un adulto. No hay publicidad personalizada para menores. La publicidad contextual se mantiene, porque no usa nada de la persona.