orquestador/specs/DECISION_EVALUATOR.md

5.5 KiB
Raw Permalink Blame History

Evaluador de decisiones (T05)

PureDecisionEvaluator implementa DecisionEvaluator sin Spring, I/O ni persistencia. Es reutilizable entre workers; no guarda contexto ni resultados entre llamadas. Devuelve Selected(output, matchedRuleIds) o Failure(code). ConditionEvaluator permite evaluar guardias: devuelve boolean o lanza DomainException con EVALUATION_ERROR. No convierte errores en condiciones falsas.

Semántica ejecutable

  • EQ/NEQ comparan tipos JSON compatibles; números mediante BigDecimal.compareTo, sin depender de la escala. No hay conversión de strings a números o fechas.
  • Null es un valor presente. Solo EQ/NEQ lo comparan: null EQ null es true, null EQ un valor no nulo es false. Una ruta ausente produce EVALUATION_ERROR en cualquier comparación, incluso si el otro operando es null.
  • EQ/NEQ admiten objetos y arrays estructurales, conservando el orden del array e ignorando el orden de claves del objeto. Valores anidados con tipos incompatibles producen error; formas/tamaños distintos producen desigualdad.
  • GT/GTE/LT/LTE requieren dos números. No existe orden implícito de strings, fechas, booleanos, objetos o null.
  • IN requiere array homogéneo y un operando izquierdo compatible con sus elementos. Revisa toda la lista antes de buscar: una coincidencia temprana no oculta tipos mezclados. Null no participa en IN. Una lista vacía retorna false para un valor no nulo. La igualdad numérica tampoco depende de escala dentro de IN.
  • EXISTS retorna false solo para un path sin valor; un null explícito sí existe. AND/OR cortocircuitan en orden; NOT invierte el resultado y propaga errores.
  • UNIQUE examina reglas hasta detectar una segunda coincidencia, que retorna AMBIGUOUS_DECISION incluso si ambas salidas son iguales. Si ninguna coincide, usa defaultOutput. Un error previo a decidir el resultado no usa el default.
  • FIRST se habilita únicamente con hitPolicy=FIRST y selecciona la primera regla coincidente, sin evaluar los datos de reglas posteriores. La comprobación estructural y de límites sí cubre la definición completa previamente.
  • Selected conserva el ID de la regla elegida; lista vacía identifica el default. La persistencia de esa evidencia en auditoría pertenece a las tareas posteriores.

JSON Pointer y contexto

El resolver solo admite las raíces tipadas /request/data, /event/data, /results y /actor. Desescapa ~1 y ~0 una sola vez y admite claves vacías y caracteres Unicode. En arrays acepta índices decimales canónicos sin ceros iniciales; índices fuera de rango, '-' y acceso a un hijo de un escalar no resuelven valor. No ejecuta expresiones.

Actor expone subject, businessScope y roles provenientes del snapshot confiable; roles se ordenan para que su representación sea determinista. Los campos del evento no sustituyen esa identidad. Resolver raíz /request/data devuelve únicamente data, no identidad, estado, revisión ni ACL de la solicitud.

Límites

EvaluationLimits permite reducir límites hasta los techos operativos: profundidad 20 y 1000 nodos. Se configura al construir el evaluador; el wiring del motor vendrá con los servicios que lo consumen. No es configuración editable del flujo.

La raíz de cada condición tiene profundidad 1. Cada condición y cada expresión path/literal cuenta como nodo; una comparación tiene tres nodos y profundidad 2. Los literales son hojas del AST. Se suman todas las reglas y ocurrencias, aunque compartan el mismo objeto Java. Los IDs duplicados son error. El preflight usa una pila explícita y rechaza también ramas que un cortocircuito habría omitido.

Para acotar comparaciones de datos, IN admite hasta maxNodes elementos y la igualdad estructural hasta maxNodes pares visitados por comparación. Exceder el presupuesto devuelve EVALUATION_ERROR. Esto no sustituye límites de bytes al recibir/deserializar: definición 1 MiB y datos 256 KiB siguen pendientes de T06/T09.

Solapamientos estáticos

IntervalOverlapAnalyzer reconoce EQ/GT/GTE/LT/LTE numéricos de path frente a literal, operandos invertidos y conjunciones AND del mismo path. Intersecta extremos abiertos y cerrados con BigDecimal y descarta intervalos vacíos. Devuelve parejas de IDs y path con solapamiento demostrado, además de IDs de reglas fuera del subconjunto.

No analiza OR/NOT/EXISTS/IN/NEQ ni compara intervalos sobre paths diferentes. La ausencia de solapamientos encontrados no certifica disjunción general. T06 consumirá estos diagnósticos al publicar; FIRST puede tener precedencia intencional. UNIQUE mantiene obligatoriamente detección de ambigüedad en runtime.

Evidencia y alcance

DecisionEvaluatorTest usa el flujo scoring.json contractual para 71/70/61/60 y fracciones de precisión alta. Las fixtures missing-score/string-score se rechazan por el schema BureauResult y, si llegan al evaluador, producen EVALUATION_ERROR, nunca una salida de scoring bajo. No es todavía una prueba de adapter de buró.

Las suites incluyen los once operadores, null/ausencia, IN heterogéneo, condiciones anidadas, cortocircuito, FIRST/UNIQUE, límites exactos, 50000 niveles de entrada, uso concurrente, JSON Pointer y extremos de intervalos. La prueba de arquitectura incluye el evaluador de application y lo compila con únicamente java.base.

No hay nuevos endpoints, llamadas externas, cambios SQL ni escritura de solicitudes. El mapeo JSON a DSL usado por la prueba scoring es exclusivamente un fixture de pruebas; el parser/validador completo de publicación corresponde a T06. Los criterios A04–A06 se verifican al nivel de decisiones/contrato, no como flujo REST completo.