Skip to main content
Traducción. Autoritativo: fs-loy-0004-rules-engine.md.

Contexto

Esto es lo que convierte al módulo en un producto y no en una base de datos. El contrato al que convergió toda la industria es evento → condiciones → efectos: llega un evento, se evalúa contra las reglas activas, y las que calzan emiten efectos. Talon.One construyó una empresa sobre esa abstracción y Open Loyalty implementa la misma forma, lo que es buena evidencia de que es la correcta. La tensión de diseño es expresividad contra predictibilidad. Un lenguaje de reglas lo bastante potente para ser Turing-completo es un lenguaje con el que un tenant se puede colgar, y sobre el que los agentes no pueden razonar. La resolución es la misma que se usó para segmentos (ADR-012): un árbol de condiciones declarativo y versionado, deliberadamente restringido, más un catálogo paramétrico de efectos para que agregar un tipo de efecto sea un cambio de código con su spec, nunca un insert.

Alcance (normativo)

  • loyalty.rule_campaigns: contenedor con ventana de vigencia y presupuestos total y por contacto.
  • loyalty.rules: AST de condiciones versionado (JSON) más una lista ordenada de efectos.
  • loyalty.rule_effect_types: paramétrico, semillas is_system award_points, issue_coupon, send_webhook, trigger_campaign.
  • El matcher: dado un evento registrado y un snapshot del contacto, producir las reglas que calzan.
  • El ejecutor del efecto award_points, llamando al servicio del libro de FS-LOY-0002.
  • Caché de reglas compiladas por tenant, invalidada al publicar.
  • loyalty.rule_executions para deduplicación y para responder “por qué este member recibió estos puntos”.
  • Dry-run: evaluar un evento contra las reglas activas y devolver los efectos sin aplicarlos.

Fuera de alcance (normativo)

  • Efectos distintos de award_points. issue_coupon llega con FS-LOY-0007, apply_discount es F2 (DEC-H4) y no cuesta nada ahora porque el catálogo ya está abierto.
  • La UI de consola para construir reglas — F1b.
  • La pertenencia a segmentos como condición: la condición puede referenciar un segmento, pero los segmentos son propiedad de core y se evalúan allá.
  • La ingesta del evento en sí, que es TS-003.

Comportamiento (normativo)

  1. Las reglas son versionadas e inmutables una vez publicadas. Editar una regla publicada crea una versión nueva; la anterior permanece para auditar qué aplicó realmente una ejecución.
  2. Publicar invalida la caché de reglas compiladas del tenant. Una regla rige para los eventos que llegan después de la publicación, nunca retroactivamente.
  3. Las condiciones son un árbol declarativo restringido: atributos tipados de perfil, propiedades del evento, agregados de eventos sobre una ventana, pertenencia a segmento y a nivel, y límites de frecuencia, combinados con AND/OR/NOT. PROHIBIDO: expresiones arbitrarias, código provisto por el usuario, bucles no acotados.
  4. Los efectos se deduplican por (evento, regla). Reprocesar el mismo evento nunca debe acreditar puntos dos veces. Esto lo garantiza un constraint único sobre rule_executions, no una convención.
  5. El presupuesto de una campaña se verifica y decrementa dentro de la transacción del efecto. Pasarse del presupuesto es imposible, no improbable.
  6. Los efectos se ejecutan en el orden declarado. Un efecto que falla aborta los efectos restantes de esa regla y registra la falla; los efectos de otras reglas no se ven afectados.
  7. Agregar una fila a rule_effect_types no agrega comportamiento. La API rechaza un código de efecto no soportado con UNSUPPORTED_CODE al publicar la regla, no al ejecutarla.
  8. La evaluación ocurre en el procesador, fuera del camino caliente. El endpoint de ingesta ya respondió 202.

Datos (normativo)

API (normativo)

simulate es el dry-run. Es un endpoint de Management a propósito: es una herramienta de diseño, no un camino de runtime, y jamás debe poder escribir.

Eventos (normativo)

Emite loyalty.points.earned a través del servicio del libro cuando se ejecuta award_points. Consume core.event.tracked del pipeline de ingesta.

Criterios de aceptación (normativo)

  1. Una regla publicada “1 punto por cada 1000 unidades monetarias en invoice_paid” aplicada a un evento de 45 990 CLP acredita exactamente 45 puntos.
  2. Reprocesar el mismo evento 100 veces produce exactamente una fila en el libro y una en rule_executions — el test de tormenta de replays.
  3. Editar una regla publicada crea la versión 2; una ejecución registrada bajo la versión 1 sigue reportando las condiciones que efectivamente se aplicaron.
  4. Una campaña con budget_total = 1000 no puede excederlo con 50 eventos calificantes concurrentes.
  5. Publicar invalida la caché: un evento que llega inmediatamente después calza con la regla nueva; uno que llegó antes, no.
  6. simulate devuelve los efectos y no escribe nada — verificado por el conteo de filas del libro antes y después.
  7. Negativo: una regla que referencia un código de efecto sin implementación se rechaza al publicar con UNSUPPORTED_CODE.

Ejecución

Arquetipo pipeline asíncrono — esta es la feature que TS-004 existe para demostrar. El matcher y el ejecutor viven en backend/workers; los endpoints de gestión en backend/api.

Preguntas abiertas

Ninguna pendiente. Toda pregunta que llevaba este spec quedó respondida en el registro consolidado (../../../../design/open-questions-v1.md, v1.1) y se incorporó a las secciones normativas de arriba.

Changelog

Registro de entrega

Aún no implementado.