Imaginá una firma de inversiones con un agente que toma pedidos de retiro. Un día, alguien cambia la cuenta de destino de un cliente y pide un retiro a los pocos minutos, y resulta ser un fraude. Compliance fija una regla nueva: después de un cambio de destino, no pasa ningún retiro hasta que back office lo verifique con el cliente. Durante cuánto tiempo aplica depende de la política de cada cuenta.
No alcanza con actualizar un valor. La firma nunca guardó cuándo cambia un destino ni cuándo se verifica, y no tiene políticas para consultar. Si las reglas del agente están en su código, hay que escribir esa lógica en cada lugar desde donde pueda pedir un retiro y sacar una versión nueva. Si están en el prompt, le pediste al modelo que la cumpla, y nada lo obliga.
Antes de que la regla rija, alguien debería poder contestar tres preguntas:
- ¿Qué pedidos pasados habría rechazado?
- ¿Quién la aprobó?
- Después, ¿qué regla rechazó cada pedido, y en qué versión?
En el resto de la nota hago ese cambio en Zarel, con un contrato de demo en un stack local, el 3 de noviembre de 2026. Los comandos y las salidas son de esa corrida, recortados. Es el mismo agente de demo que en nuestro sitio no da consejos de inversión, ampliado para registrar pedidos de retiro. No mueve plata.
Paso 1: agregar los datos
En Zarel, las reglas de un agente y los datos que leen se declaran en un contrato, fuera de su código. La primera publicación agrega una política y las dos fechas de cada cuenta:
- name: withdrawal_policies
fields:
- name: name
type: string
required: true
- name: destination_hold_days
type: number
required: true
- name: accounts
fields:
# los campos que ya tenía, y:
- name: policy
type: reference
references: withdrawal_policies
- name: destination_changed_at
type: datetime
- name: destination_verified_at
type: datetime
- name: destination_hold_days
type: number
compute:
expression: policy.destination_hold_days
strategy: read
No hay migración ni despliegue, y ningún pedido se trata distinto. Back office carga las políticas y las cuentas mientras siguen llegando pedidos. En esta corrida se aceptaron cinco, de 30.000 cada uno.
Los datos van primero por un motivo: el paso siguiente juzga pedidos pasados, y de un pedido anterior a estos campos no se puede saber qué tenían.
Paso 2: probar la regla contra lo que ya pasó
La regla es un chequeo sobre cada pedido de retiro:
- name: recent_destination_change
on: [ create ]
effect: deny
expression: >-
account.destination_changed_at > now() - duration(account.destination_hold_days, 0, 0, 0)
and (account.destination_verified_at == null
or account.destination_verified_at < account.destination_changed_at)
message: recent_destination_change
Una prueba en seco juzga con ella los cinco pedidos pasados:
$ zarel contract spec dry-run rule.yaml --from ... --to ... --watch
- Sessions evaluated: 5
- Re-evaluated with the same outcome: 3 (60.0%)
- Outcomes changed: 2 (40.0%)
| from | to | count | cause |
| executed | refused | 2 | Diverged stages: checks; check:withdrawal_requests.recent_destination_change |
Cada pedido, con la regla:
- Rechazado: destino cambiado hace 2 días, espera de 7, sin verificar.
- Rechazado: destino cambiado hace 2 días, espera de 7, verificado antes de ese cambio.
- Aceptado: destino cambiado hace 60 días, espera de 7.
- Aceptado: destino cambiado hace 2 días, espera de 7, verificado después de ese cambio.
- Aceptado: destino cambiado hace 2 días, espera de 1.
Si la prueba abarca un período más largo, también alcanza pedidos de antes de que existieran los campos nuevos. A esos no los informa como iguales: dice que no pudo juzgarlos (field_not_declared_then). Tampoco vuelve a correr el modelo, así que no puede decir qué habría propuesto el agente con la regla nueva.
Paso 3: la aprueba otra persona
En esta corrida, el contrato dice quién aprueba un cambio en sus entidades. Por eso, publicar crea una propuesta, y todavía no rige nada:
$ zarel contract spec apply rule.yaml
"publication": {
"status": "proposed",
"requirements": { "entities": "four_eyes" }
}
Mientras esperaba, un pedido nuevo de la primera de esas cuentas se siguió aceptando, y a un asesor que intentó aprobar la propuesta se lo rechazó.
La propuesta también le avisó a quien aprueba algo que necesita saber: una cuenta todavía no tiene política, así que la regla no se le puede aplicar hasta que alguien se la asigne. Un chequeo sobre un valor vacío no se dispara, y conviene enterarse antes de que rija.
El usuario de back office la aprobó por la API. La publicación registró quién la propuso, quién la aprobó y con qué rol. Este paso es opcional: un contrato que no nombra aprobadores rige cuando se publica.
Paso 4: rige desde la acción siguiente
No hubo ningún despliegue. El siguiente pedido de esa primera cuenta:
HTTP 422 validation_violation
a validation rule refused the write (entity withdrawal_requests; rule recent_destination_change)
Las tres cuentas aceptadas se volvieron a aceptar. Los rechazos de antes y de después de un cambio citan versiones distintas del contrato por su hash, en la cadena de decisiones.
Otras dos cosas cambiaron sin una versión nueva del contrato, porque son datos. Back office acortó la política a 1 día y el mismo pedido se aceptó. Con la espera otra vez en 7 días, un asesor intentó registrar la verificación y se lo rechazó. Back office la registró, y el pedido pasó.
Lo que esto no hace
- Cubre lo que pasa por Zarel. Lo que el agente pueda hacer por otro camino no lo juzga nada de esto.
- Alguien sigue editando un contrato y publicándolo. Acá fue un archivo YAML y una terminal, y la aprobación fue por la API. Alguien de riesgo no trabajaría así.
- Publicar un documento entero pide permiso de edición sobre todas sus secciones, aunque cambie una sola. Quien sólo deba tocar esta regla usaría un borrador, y esta corrida no lo recorrió.
- La regla rechaza el pedido. No lo retiene para que decida otra persona: un chequeo que pide confirmación se la pide a quien está actuando. Retenerlo necesitaría un estado en el pedido, y esta corrida no lo recorrió.
- Los pedidos fueron directo a la API, sin un modelo de por medio, en una sola máquina y sin carga.
- Zarel está en pre-lanzamiento, sin clientes en producción y sin certificaciones. Si cambiar una regla es lo que más les duele a los equipos con agentes es algo que todavía les estamos preguntando.