Funnel Platform v3 · guía del ecosistema

Todo lo que construimos el 15-Abr-2026 en una página. Para volver cuando quieras entender cómo funciona este nuevo ecosistema y cómo operarlo.

✅ Runtime rubric · gate superado
98 / 100
10 dimensiones observables · 8/10 perfectas · 2/10 a 9/10 (gaps menores en taxonomía de eventos y device split de dwell).
Entornolocalhost:3847 + Supabase sa-east-1
Clientes activos28 · isolated por client_slug
Eventos en DB~2.400 seed + tráfico real
Cronrotation cada 15 min

Qué es esto en 30 segundos

Funnel Platform v3 es la infraestructura de A/B testing bayesiano multi-tenant para los funnels de todos los clientes LHT. Cada visitante que llega a una landing es asignado a una variante (Thompson bandit), cada click/scroll/conversión se registra en Supabase, y un cron cada 15 min retira perdedores y promueve ganadores automáticamente.

Reemplaza al flujo manual donde vos mirabas ads a mano y decidías qué matar. Ahora el sistema lo hace con matemática y vos ves el resultado en un panel.

Arquitectura en 5 capas

1. VISITANTE ↓ (abre landing = vsl.html del cliente X) 2. RUNTIME TRACKER (JS inyectado en vsl.html) ├─ pide variante → GET /api/v3/variante?client_slug=X&source=meta ├─ emite impression → POST /api/v3/track ├─ tracking pasivo → scroll_depth · dwell · video_50pct └─ tracking activo → click en CTA → conversion / whatsapp 3. ENDPOINTS (server.js → tools/funnel-platform-v3/*.mjs) ├─ /api/v3/variante Thompson Sampling · picks v1/v2/v3 ├─ /api/v3/track inserta evento + dedup por event_id ├─ /api/v3/stats agrega KPIs + posterior per-source ├─ /api/v3/config CRUD de experimentos · rotationLog ├─ /api/v3/pivot matriz device × país × hora ├─ /api/v3/cron-rotation promueve/retira variantes └─ /api/v3/tracking-check audita Pixel/CAPI/GA4/Sheets 4. SUPABASE (cwibixnngldlwzxpfchf · Postgres) ├─ events (client_slug, source, variant_id, event_name, event_id, …) ├─ config (client_slug, source, data = {activeVariants, rotationLog, …}) └─ sessions (session_id, client_slug, source, device, country, tz) ⚙ Row Level Security FORCE · 0 leaks cross-tenant garantizado 5. PANEL POR CLIENTE (public/clients//panel.html) KPI strip + variants table + pivot · auto-refresh 30s

Qué tiene cada cliente ahora

Cómo funciona el Thompson Sampling

En vez de repartir 50/50 el tráfico y decidir al final, el bandit usa una distribución Beta(α=conversiones+1, β=visitantes−conversiones+1) por variante. Cada request muestrea de esas distribuciones y pickea al que salga mejor. Ventaja: mientras explora también explota, no perdés plata tráfico en perdedores obvios.

Regla interna: si una variante supera P(best) ≥ 0.60 en el Monte Carlo con 200 sims, se explota 100%. Si nadie supera ese umbral, se bucketiza por hash determinístico de la sesión (mismo visitante = misma variante en refresh).

Las 10 dimensiones del rubric

#DimensiónQué verificaScore
D51A/B runtime infravariante endpoint live · ≥3 variantes · p_best válido · posterior fresco10/10
D52Source isolationexperimentos independientes por meta/youtube/organic/email/whatsapp10/10
D53Event taxonomy≥10 tipos de eventos (impression, conversion, scroll_depth, dwell, …)9/10
D54Dwell analyticspercentiles p50/p75/p95 + device split por bucket9/10
D55Auto-rotationcron corre + log tiene promote & retire observados10/10
D56Panel observabilitypanel fetchea stats/variante/pivot + KPI strip + tabla + refresh10/10
D57Cross-pixel dedup100 eventos POST → 100 recuperados por event_id (sin doble conteo)10/10
D58Exit-popup factorial5 buckets de timing compitiendo en la landing10/10
D59Tracking auditendpoint /tracking-check reporta ok en Pixel/CAPI/GA4/Sheets10/10
D60Multi-tenant RLScliente A no ve data de cliente B · naked query rechazada 4xx10/10

Cómo operarlo (rutina diaria, 5 min)

  1. Abrir el panel del clienteIr a localhost:3847/clients/<slug>/panel.html. Te da impresiones, conversiones, calificadas, WhatsApp, variante ganadora del momento, tabla de variantes con P(best), y pivot device/hora.
  2. Mirar P(best)Si alguna variante superó 85% sustained durante 48h, el cron la promueve automáticamente. Si ninguna despega, agregá variante nueva.
  3. Agregar una variante nuevaEditás public/clients/<slug>/variants.json, agregás {id:"v4",label:"…",hook:"…"}, corrés bootstrap otra vez y el cron la integra.
  4. Forzar rotaciónSi querés ver si el cron tomaría alguna acción: curl /api/v3/cron-rotation?client_slug=<slug>&source=meta. Devuelve qué promovió, retiró o agregó.
  5. Auditar trackingSi sospechás que algún evento no se está registrando (Pixel vs CAPI vs DB): curl /api/v3/tracking-check?client=<slug>&event_id=<uuid>.

Archivos clave · dónde mirar cuando algo falla

ArchivoResponsabilidad
tools/funnel-platform-v3/variante.mjsThompson Sampling + pick de variante. Si querés ajustar el umbral 60% de exploit/explore, acá.
tools/funnel-platform-v3/track.mjsIngestión de eventos. Si un event_name es rechazado con 400, es acá (ALLOWED_EVENTS).
tools/funnel-platform-v3/stats.mjsAgregaciones para el panel. Thompson Monte Carlo 5000 sims para el posterior vitalicio.
tools/funnel-platform-v3/config.mjsCRUD de experimentos. action="retire" / "activate" / "requeue" para mutar active/queue/retired.
tools/funnel-platform-v3/cron-rotation.mjsAlgoritmo que promueve campeones (P≥0.85 sustained 48h) y retira perdedores (P≤0.05 con ≥50 imp).
tools/funnel-platform-v3/exit-popup-factorial.mjs5 buckets de exit-popup. Cambiá el array EXIT_POPUP_BUCKETS para tocar timings.
tools/funnel-platform-v3/runtime-scorer.mjsScorer D51-D60. Correlo cuando metés cambios grandes para ver qué rompiste.
tools/funnel-platform-v3/sql/*.sql4 migrations. Schema, indices (siempre lead con client_slug), RLS policies, event_name CHECK.
public/clients/_template/panel.htmlTemplate del panel. Editar acá + correr panel-deploy.mjs propaga a los 28 clientes.

Comandos útiles

# Ver score runtime actual
$ node -e "import('./tools/funnel-platform-v3/runtime-scorer.mjs').then(m=>m.scoreRuntime({baseUrl:'http://localhost:3847',client:'lht-pro'}).then(r=>console.log(r.total+'/100')))"

# Test endpoint variante para un cliente
$ curl "http://localhost:3847/api/v3/variante?client_slug=lht-pro&source=meta"

# Ver eventos recientes en Supabase
$ curl "http://localhost:3847/api/v3/stats?client_slug=lht-pro&source=meta"

# Forzar rotación manual
$ curl "http://localhost:3847/api/v3/cron-rotation?client_slug=lht-pro&source=meta"

# Re-deployar todos los paneles (después de editar el template)
$ node tools/funnel-platform-v3/panel-deploy.mjs

# Re-bootstrap v3 en todos los clientes (idempotente)
$ node tools/funnel-platform-v3/__bootstrap-client-funnels.mjs

Qué sigue · roadmap corto

Seguridad y aislamiento

Invariante crítica: cada tabla tiene client_slug NOT NULL como primera columna después del id, cada índice lead con client_slug, y las RLS policies son FORCE ROW LEVEL SECURITY. Esto significa que si el código omite el client_slug en una query, Postgres rechaza la fila en lugar de devolver datos de otro cliente. La D60 valida esto automáticamente en cada run del scorer.

Credenciales y rotación

Las keys están en .env (no commiteadas). Para rotar la password de la DB:

  1. Supabase dashboardSettingsDatabaseReset database password.
  2. Copiar la nuevay pegarla en .env reemplazando la línea SUPABASE_DB_URL=….
  3. Restart del servercon node server.js. Listo.