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, semillasis_systemaward_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_executionspara 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_couponllega con FS-LOY-0007,apply_discountes 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
corey se evalúan allá. - La ingesta del evento en sí, que es TS-003.
Comportamiento (normativo)
- 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.
- 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.
- 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.
- 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. - El presupuesto de una campaña se verifica y decrementa dentro de la transacción del efecto. Pasarse del presupuesto es imposible, no improbable.
- 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.
- Agregar una fila a
rule_effect_typesno agrega comportamiento. La API rechaza un código de efecto no soportado conUNSUPPORTED_CODEal publicar la regla, no al ejecutarla. - 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)
Emiteloyalty.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)
- 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. - Reprocesar el mismo evento 100 veces produce exactamente una fila en el libro y una en
rule_executions— el test de tormenta de replays. - 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.
- Una campaña con
budget_total = 1000no puede excederlo con 50 eventos calificantes concurrentes. - Publicar invalida la caché: un evento que llega inmediatamente después calza con la regla nueva; uno que llegó antes, no.
simulatedevuelve los efectos y no escribe nada — verificado por el conteo de filas del libro antes y después.- 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 enbackend/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.