Ir al contenido

Conceptos fundamentales

Presage esta construido sobre un pequeno conjunto de primitivas componibles. Comprender estos conceptos le ayudara a disenar interfaces adaptativas efectivas.

El UserContext es la fuente unica de verdad sobre quien es el usuario y como se comporta. Combina tres dimensiones:

interface UserContext {
traits: UserTraits // Static data you set (role, plan, company)
signals: BehavioralSignals // Auto-computed from tracked events
maturity: Maturity // Automatic segment: 'new' | 'onboarding' | 'active' | 'power' | 'dormant'
}

Los atributos (traits) son datos estaticos que usted establece explicitamente, tipicamente desde su sistema de autenticacion o perfil de usuario.

interface UserTraits {
userId?: string
role?: string
plan?: string
signupDate?: string
company?: string
companySize?: number
locale?: string
[key: string]: unknown // Extensible with any custom field
}

Establezca los atributos con client.identify() o client.updateTraits().

Las senales de comportamiento se calculan automaticamente a partir de los eventos rastreados. Usted nunca las establece directamente.

interface BehavioralSignals {
sessionCount: number
totalEvents: number
featureUsage: Record<string, number>
lastSeenAt: string
firstSeenAt: string
currentSessionDuration: number
daysSinceSignup: number
clickMap: Record<string, number>
customSignals: Record<string, number>
}

Por ejemplo, despues de rastrear client.track('feature_used', { featureId: 'export' }) tres veces, signals.featureUsage.export sera igual a 3.

La madurez del usuario es un segmento calculado derivado de las senales de comportamiento. Clasifica a los usuarios en uno de cinco niveles:

SegmentoSignificado
newPocas sesiones (por defecto: 3 o menos)
onboardingTodavia aprendiendo (por defecto: 4-10 sesiones)
activeUsuario regular mas alla de la incorporacion
powerUsa muchas funcionalidades (por defecto: 5+ funcionalidades distintas)
dormantInactivo por demasiado tiempo (por defecto: 14+ dias)

Los umbrales son configurables mediante MaturityConfig.

Un adaptation point es un lugar en su interfaz donde se pueden mostrar diferentes variantes. Se define mediante:

interface AdaptationPoint {
id: string // Unique identifier (e.g. 'onboarding')
variants: readonly string[] // Available variant IDs
defaultVariant: string // Fallback when no rule matches
strategy: Strategy // How to select (currently: { type: 'rules' })
}

En React, el componente <Adaptive> crea un adaptation point implicitamente a partir de sus props e hijos.

Una regla mapea un conjunto de condiciones a una accion. Cuando las condiciones coinciden con el UserContext actual, la accion determina lo que sucede en el adaptation point.

interface AdaptationRule {
id: string // Unique identifier
adaptationId: string // Which adaptation point this rule targets
priority: number // Higher priority rules are evaluated first
conditions: ConditionGroup // Boolean logic tree
action: AdaptationAction // What to do when conditions match
}

Una Condition verifica un campo individual en el contexto del usuario:

interface Condition {
field: string // Dot-path: 'traits.role', 'signals.sessionCount', 'maturity'
operator: ConditionOperator // One of 14 operators
value: unknown // Value to compare against
}

Los grupos combinan condiciones con logica booleana:

interface ConditionGroup {
all?: (Condition | ConditionGroup)[] // AND — all must match
any?: (Condition | ConditionGroup)[] // OR — at least one must match
not?: Condition | ConditionGroup // NOT — inverts the result
}

Los grupos pueden anidarse a cualquier profundidad.

Una accion describe lo que sucede cuando una regla coincide:

type AdaptationAction =
| { type: 'show'; variantId: string } // Show a specific variant
| { type: 'hide' } // Hide the adaptation point entirely
| { type: 'reorder'; order: string[] } // Reorder items (e.g. navigation)
| { type: 'modify'; props: Record<string, unknown> } // Modify component props

Una estrategia determina como se seleccionan las variantes. Actualmente, solo esta disponible la evaluacion basada en reglas:

type Strategy = { type: 'rules' }

Proximamente en v0.2: Estrategias de multi-armed bandit que optimizan automaticamente la seleccion de variantes basandose en metricas de conversion.

El tracker registra eventos del usuario y alimenta el pipeline de calculo de senales.

// Track events with optional properties
client.track('feature_used', { featureId: 'dashboard-export' })
client.track('click', { elementId: 'nav-settings' })
client.track('custom_signal', { signalId: 'engagement', value: 5 })

Los nombres de eventos especiales activan calculos de senales especificos:

  • feature_used con featureId — incrementa signals.featureUsage[featureId]
  • click con elementId — incrementa signals.clickMap[elementId]
  • custom_signal con signalId y value — incrementa signals.customSignals[signalId]

Tambien puede conectar adaptadores de analitica externos (Segment, PostHog, etc.) para reenviar eventos.

La persistencia mantiene los atributos y senales entre recargas de pagina. Se proporcionan dos drivers integrados:

  • createLocalStorageDriver(prefix) — Almacena datos en localStorage bajo una clave con namespace
  • createMemoryDriver() — Almacenamiento en memoria para SSR y testing

Cuando se configura un driver de persistencia, el cliente lee los datos en cache de forma sincrona durante la inicializacion, lo que evita un destello de contenido por defecto (anti-FOUC).

Cuando un componente solicita una variante, esto es lo que sucede:

  1. El componente declara un AdaptationPoint (id, variantes, defecto)
  2. El motor de reglas filtra las reglas por adaptationId
  3. Las reglas se evaluan en orden de prioridad (la mas alta primero)
  4. La primera regla cuyas condiciones coincidan devuelve su accion
  5. La accion determina que variante se renderiza
  6. Si ninguna regla coincide, se usa el defaultVariant
  7. Un evento presage:impression se rastrea automaticamente

Todo este flujo es sincrono y ocurre en un unico ciclo de renderizado.