Árboles de Evaluación de Requisitos (RET)
A primera vista
Qué: Álgebra booleana para puertas, con manejo de desconocidos (lógica de tres estados) Por qué: Hacer la lógica de las puertas explícita, auditable y determinista - sin reglas ocultas Quién: Desarrolladores y operadores que redactan requisitos complejos de puertas Requisitos previos: Comprensión básica de condiciones (ver condition_authoring.md)
Backend AOT/Bajado (RET)
RET ahora proporciona un backend aditivo reducido/AOT en ret-logic:
- Compilar una vez:
Requirement<P> -> CompiledRequirement<K> - Evaluate fast paths at runtime:
CompiledRequirement::evalCompiledRequirement::eval_blockCompiledRequirement::eval_tristate(+ variante de traza)
- Export deterministic predicate-key dependencies:
CompiledRequirement::predicate_keys()
- Compute explanation-oriented residual/progress views:
Requirement::residualCompiledRequirement::residual
- Preserve compatibility:
- Tree-walk
Requirement::eval*se mantiene soportado y sin cambios.
- Tree-walk
Esto es independiente del dominio: los dominios proporcionan un mapeo de claves determinista (PredicateRegistry) y ejecución de claves en tiempo de ejecución (PredicateRuntime). La explicación residual/progreso requiere además implementaciones de progreso a nivel de condición o predicado a través de ConditionProgressEval y PredicateProgressRuntime.
Ingesta en Tiempo de Compilación vs en Tiempo de Ejecución
La ingesta de fuentes (RON, JSON, DSL, cargas útiles de MCP, etc.) no cambia la semántica de RET. La diferencia es el ciclo de vida:
- Compile-time/load-time ingestion: parse + validate + compile once, store artefacto compilado.
- Runtime ingestion: parse + validate + compile when the gate arrives, then ejecutar artefacto compilado.
Ambos caminos convergen en el mismo comportamiento de álgebra y evaluador compilado cuando los requisitos de entrada son equivalentes.
¿Por qué RET?
Problema: ¿Cómo se combinan múltiples verificaciones de evidencia en una única decisión de puerta?
Escenario de ejemplo: “Quiero desplegar en producción si:
- El entorno es ‘producción’ Y
- Pruebas aprobadas Y
- La cobertura es superior al 85% Y
- Al menos 2 de 3 revisores aprobaron
Sin RET: Tendrías que escribir código personalizado para cada puerta, que es:
- No auditable (la lógica está oculta en el código)
- No determinista (el código puede cambiar entre ejecuciones)
- Difícil de verificar sin conexión (no se puede reproducir sin volver a ejecutar el código)
Con RET: Expresas la lógica como una estructura de árbol:
{
"requirement": {
"And": [
{ "Condition": "env_is_prod" },
{ "Condition": "tests_ok" },
{ "Condition": "coverage_ok" },
{
"RequireGroup": {
"min": 2,
"reqs": [
{ "Condition": "alice_approved" },
{ "Condition": "bob_approved" },
{ "Condition": "carol_approved" }
]
}
}
]
}
}
Beneficios:
- Explícito: La lógica es visible en la especificación del escenario
- Auditable: Cada evaluación está registrada en el runpack
- Determinista: Mismas evidencias -> mismo resultado
- Repetible: Puede verificar sin conexión sin volver a consultar a los proveedores
[Seguridad]: La lógica de puerta explícita previene puertas traseras ocultas. Toda la lógica se declara en la especificación del escenario y se rastrea en el paquete de ejecución para auditoría.
Modelo Mental: Árbol de Evaluación RET
Aquí está cómo se evalúa un árbol de requisitos:
RET EVALUATION TREE (simplified)
Gate Requirement (tree structure)
And
|-- Pred(A) -> true
|-- Pred(B) -> unknown
|-- Not(C) -> false
`-- RequireGroup (min: 2)
|-- Pred(D) -> true
|-- Pred(E) -> true
`-- Pred(F) -> false
Strong Kleene Logic: And(true, unknown, true, true) -> unknown
(gate holds)
Orden de evaluación:
- Las condiciones de los nodos se evalúan en un estado tri-estado (verdadero/falso/desconocido)
- Los nodos operadores combinan los resultados de los hijos a través de lógica de tres estados
- El resultado del nodo raíz determina el resultado de la puerta
Resultados de Tri-State
RET utiliza lógica de tres estados (no solo verdadero/falso):
true: Pases de acceso (todos los requisitos satisfechos)false: La puerta falla (requisitos contradichos)unknown: Las puertas se mantienen (requisitos inconclusos)
¿Por qué triestado? Las puertas fallan cerradas: una puerta solo se abre cuando el requisito se evalúa como true. Los resultados unknown impiden que las puertas se abran hasta que la evidencia esté completa.
Ejemplo:
Gate: And(tests_ok, coverage_ok)
Conditions:
- tests_ok: true (tests passed)
- coverage_ok: unknown (coverage report missing)
Outcome: unknown (gate holds until coverage is available)
Operadores Básicos
Y
Semántica: Todos los hijos deben ser true
Truth table (2 operands):
| Left | Right | Result |
|---|---|---|
| true | true | true |
| true | false | false |
| true | unknown | unknown |
| false | (any) | false |
| unknown | true | unknown |
| desconocido | desconocido | desconocido |
Ejemplo:
{
"requirement": {
"And": [
{ "Condition": "tests_ok" },
{ "Condition": "coverage_ok" }
]
}
}
Caso de uso: Tanto las pruebas como la cobertura deben pasar
Comportamiento:
- Todos
true->true(pases de puerta) - Cualquier
false->false(la puerta falla) - De lo contrario -> unknown (la puerta se mantiene)
O
Semántica: Cualquier hijo puede ser true
Truth table (2 operands):
| Left | Right | Result |
|---|---|---|
| true | (any) | true |
| false | false | false |
| false | unknown | unknown |
| unknown | false | unknown |
| desconocido | desconocido | desconocido |
Ejemplo:
{
"requirement": {
"Or": [
{ "Condition": "manual_override" },
{ "Condition": "tests_ok" }
]
}
}
Caso de uso: Debe pasar ya sea la anulación manual O las pruebas automatizadas
Comportamiento:
- Cualquier
true->true(pases de puerta) - Todos
false->false(la puerta falla) - De lo contrario -> unknown (la puerta se mantiene)
No
Semántica: Invertir resultado del hijo
Truth table:
| Input | Result |
|---|---|
| true | false |
| false | true |
| Tabla de verdad: | Entrada |
Ejemplo:
{
"requirement": {
"And": [
{ "Condition": "tests_ok" },
{ "Not": { "Condition": "blocklist_hit" } }
]
}
}
Caso de uso: Las pruebas deben pasar Y la lista de bloqueo NO debe ser activada
Comportamiento:
verdadero->falsofalso->verdaderounknown->unknown(cerrado por defecto: no se puede confirmar la ausencia)
RequireGroup (Quorum)
Semántica: Al menos N de M hijos deben ser true
Parámetros:
min: Número mínimo de resultadostruerequeridosreqs: Array de requisitos secundarios
Ejemplo:
{
"requirement": {
"RequireGroup": {
"min": 2,
"reqs": [
{ "Condition": "alice_approved" },
{ "Condition": "bob_approved" },
{ "Condition": "carol_approved" }
]
}
}
}
Caso de uso: Al menos 2 de 3 revisores deben aprobar
Comportamiento:
- Contar resultados
true - Si count >=
min->true(quórum alcanzado) - Si count + unknowns <
min->false(quórum imposible) - De lo contrario -> unknown (quorum pendiente)
Truth table examples:
| Outcomes | min | Result | Reason |
|---|---|---|---|
| [true, true, false] | 2 | true | 2 true >= min (quorum reached) |
| [true, unknown, unknown] | 2 | unknown | 1 true, can’t reach min yet |
| [true, false, false] | 2 | false | 1 true, max possible is 1 < min |
| [true, true, unknown] | 2 | true | 2 true >= min (already met) |
| [falso, falso, falso] | 2 | falso | 0 verdadero, imposible |
[Desarrollador]: Consulta el crate ret-logic para la implementación. RequireGroup cuenta verdadero/falso de manera independiente (desconocido no es ninguno de los dos).
Condición (Hoja)
Semántica: Referenciar una condición por clave
Ejemplo:
{
"requirement": { "Condition": "tests_ok" }
}
Caso de uso: Puerta simple con una única condición
Comportamiento:
- Evalúa el resultado de triestado de la condición
- La condición debe existir en
ScenarioSpec.conditions
Reglas de Propagación Tri-Estatal
Cómo unknown resultados se propagan a través de operadores:
Y Propagación
| Operandos | Resultado | Razón |
|---|---|---|
And(true, true, true) | true | Todos los requisitos satisfechos |
And(true, false, true) | false | Uno falla -> And falla |
And(true, unknown, true) | unknown | No se puede confirmar que todos sean verdaderos aún |
And(false, unknown) | false | Uno falla (circuito corto) |
And(unknown, unknown) | unknown | Evidencia pendiente |
Regla: false domina; todos los true producen true; de lo contrario unknown
O Propagación
| Operandos | Resultado | Razón |
|---|---|---|
Or(false, false, false) | false | Todos los requisitos fallaron |
Or(true, false, false) | true | Uno tiene éxito -> Or tiene éxito |
Or(false, unknown, false) | unknown | No se puede confirmar que todos sean falsos aún |
Or(true, unknown) | true | Uno tiene éxito (circuito corto) |
Or(unknown, unknown) | unknown | Evidencia pendiente |
Regla: true domina; todos false producen false; de lo contrario unknown
Propagación RequireGroup
| Resultados | min | conteo verdadero | conteo desconocido | Resultado |
|---|---|---|---|---|
| [T, T, F] | 2 | 2 | 0 | verdadero (mínimo alcanzado) |
| [T, U, U] | 2 | 1 | 2 | desconocido (máx 3, se necesitan 2) |
| [T, F, F] | 2 | 1 | 0 | falso (máx 1 < min) |
| [U, U, U] | 2 | 0 | 3 | desconocido (máx 3, se necesitan 2) |
| [F, F, F] | 2 | 0 | 0 | falso (imposible) |
Regla:
- Si
true_count >= min->true(quórum alcanzado) - Si
true_count + unknown_count < min->false(quórum imposible) - De lo contrario -> unknown (quorum pendiente)
[LLM Agent]: Cuando RequireGroup devuelve
unknown, necesitas más evidencia. Verifica qué condiciones son desconocidas y trabaja para satisfacerlas.
Casos de Uso Prácticos
Puerta Simple: Ambas Condiciones
Escenario: Desplegar si las pruebas pasaron Y la cobertura está por encima del 85%
{
"gate_id": "quality_gate",
"requirement": {
"And": [
{ "Condition": "tests_ok" },
{ "Condition": "coverage_ok" }
]
}
}
Quorum Gate: 2 de 3 Revisores
Escenario: Fusionar PR si al menos 2 de 3 revisores aprobaron
{
"gate_id": "review_gate",
"requirement": {
"RequireGroup": {
"min": 2,
"reqs": [
{ "Condition": "alice_approved" },
{ "Condition": "bob_approved" },
{ "Condition": "carol_approved" }
]
}
}
}
Exclusion Gate: NO en la lista negra
Escenario: Desplegar si NO está en la lista negra
{
"gate_id": "blocklist_gate",
"requirement": {
"Not": { "Condition": "blocklist_hit" }
}
}
Puerta Compleja: (A Y B) O C
Escenario: Desplegar si (pruebas aprobadas Y cobertura OK) O anulación manual
{
"gate_id": "deploy_gate",
"requirement": {
"Or": [
{
"And": [
{ "Condition": "tests_ok" },
{ "Condition": "coverage_ok" }
]
},
{ "Condition": "manual_override" }
]
}
}
Ramificación en Resultados de Puerta
Puedes enrutar a diferentes etapas basadas en los resultados de las puertas utilizando advance_to.branch:
{
"advance_to": {
"kind": "branch",
"branches": [
{ "gate_id": "env_gate", "outcome": "true", "next_stage_id": "ship" },
{ "gate_id": "env_gate", "outcome": "unknown", "next_stage_id": "hold" },
{ "gate_id": "env_gate", "outcome": "false", "next_stage_id": "deny" }
],
"default": null
}
}
Cómo funciona:
- Evaluar la puerta
env_gate - Verificar el resultado contra las ramas (de arriba hacia abajo)
- La primera rama coincidente gana
- Si no hay coincidencia y
defaultes nulo -> error
Casos de uso:
- Verdadero: Avanzar a la implementación en producción
- Desconocido: Mantener para revisión manual
- Falso: Rechazar y notificar
[Seguridad]: Utilice ramificaciones para implementar alternativas de seguridad. Por ejemplo, dirija
unknowna revisión manual en lugar de avanzar automáticamente.
Logic Mode: Fuerte Kleene
Decision Gate utiliza lógica Kleene fuerte (triestado):
Propiedades clave:
And(true, unknown)->unknown(no se puede confirmar que todo sea verdadero)Or(false, unknown)->unknown(no se puede confirmar que todo sea falso)Not(unknown)->unknown(no se puede invertir la incertidumbre)
Alternativa (no utilizada): lógica de Bochvar (cualquier desconocido -> desconocido)
¿Por qué Kleene?
- Más intuitivo para evidencia parcial
- Cortocircuitos cuando es posible (
And(false, unknown)->false) - Los balances fallan cerrados con usabilidad
[Desarrollador]: Consulta crates/ret-logic/src/lib.rs para el algoritmo de evaluación.
Casos de Uso
Primario: Puertas complejas que requieren combinaciones booleanas (Y, O, quórum) Secundario: Puertas simples con condiciones individuales (solo nodo de condición) Antipatrones: No anidar RETs demasiado profundamente - preferir condiciones enfocadas y árboles planos
Solución de problemas
Problema: Puerta Atascada en unknown
Síntomas: La puerta nunca pasa, siempre regresa unknown
Causa: Una o más condiciones están evaluando a unknown
Solución:
- Verifique el seguimiento de la puerta para ver qué condiciones son
unknown - Solucionar los problemas subyacentes de las condiciones (ver condition_authoring.md)
- Common causes:
- Error del proveedor (por ejemplo, archivo faltante para el proveedor json)
- JSONPath no encontrado (desajuste de salida de la herramienta)
- Desajuste de tipo para un comparador sensible al tipo
Problema: RequireGroup Nunca Pasa
Síntomas: RequireGroup siempre devuelve false o unknown
Causa: min es demasiado alto, o demasiadas condiciones están fallando
Solución:
- Verificar el valor de
minfrente al número de condiciones - Verificar los resultados de condición en el seguimiento de la puerta
- Asegúrese de que al menos
mincondiciones puedan sertruesimultáneamente
Ejemplo:
// BAD: min is 3, but only 2 conditions
{
"RequireGroup": {
"min": 3,
"reqs": [
{ "Condition": "a" },
{ "Condition": "b" }
]
}
}
// GOOD: min <= number of conditions
{
"RequireGroup": {
"min": 2,
"reqs": [
{ "Condition": "a" },
{ "Condition": "b" },
{ "Condition": "c" }
]
}
}
Problema: La rama no coincide
Síntomas: Error de evaluación de la puerta: “No hay una rama coincidente”
Causa: El resultado de la puerta no coincide con ninguna rama, y default es nulo
Solución:
- Agregar ramas para todos los posibles resultados (verdadero/falso/desconocido)
- O establece
defaulta una etapa de respaldo - Ejemplo:
{
"branches": [
{ "gate_id": "gate1", "outcome": "true", "next_stage_id": "ship" },
{ "gate_id": "gate1", "outcome": "false", "next_stage_id": "deny" }
],
"default": "hold" // Fallback for unknown
}
Consejos de Autoría
1. Mantener las claves de condición estables y descriptivas
- Utiliza
tests_oknopred1 - Las claves se referencian en los runpacks para auditoría
2. Utilizar RequireGroup para verificaciones de estilo quórum
- Ejemplo: “2 de 3 revisores”, “3 de 5 verificaciones de centro de datos”
- Alternativa: Múltiples condiciones “Y” (pero menos flexibles)
3. Preferir árboles más pequeños con condiciones específicas
- Más fácil de auditar y entender
- Más fácil de depurar cuando fallan las puertas
4. Validar la estructura RET durante la definición del escenario
- La puerta de decisión valida los RET en el momento de
scenario_define - Falla rápidamente si la estructura es inválida (por ejemplo, al hacer referencia a condiciones no existentes)
5. Utiliza ramificaciones para soluciones de respaldo a prueba de fallos
- Rote
unknownpara revisión manual - Ruta
falsepara alertar/negar - Route
truepara avanzar
Rutas de Aprendizaje de Referencia Cruzada
Ruta para Nuevos Usuarios: getting_started.md -> condition_authoring.md -> ESTE MANUAL -> integration_patterns.md
Ruta de Lógica Avanzada: ESTE MANUAL -> evidence_flow_and_execution_model.md -> Comprender cómo encajan los RET en la tubería de evaluación
Ruta de Seguridad: ESTE MANUAL -> security_guide.md -> Aprenda cómo la lógica explícita previene puertas traseras
Glosario
Y: Operador que requiere que todos los hijos sean true.
Puerta: Punto de decisión en un escenario, evaluado a través de RET contra evidencia.
O: Operador que requiere que cualquier hijo sea true.
Nota: Operador que invierte el resultado del hijo (true <-> false).
Condición: Definición de verificación de evidencia: consulta + comparador + valor esperado.
RequireGroup: Operador de quórum que requiere al menos N de M hijos para ser true.
RET: Árbol de Evaluación de Requisitos, álgebra booleana (Y/O/NO/RequerirGrupo) para compuertas.
TriState: Resultado de la evaluación: verdadero (aprobado), falso (fallido) o desconocido (en espera).
Lógica Kleene Fuerte: Modo de lógica de tres estados donde And(true, unknown) -> unknown.