Reglas como tests: escribís la regla una vez y corre en cada commit

El patrón en un dibujo
Un test chequea un pedazo de código. Escribís el código, después escribís su test, uno por uno.
Un código también tiene reglas: «las entidades de un test reusan los mocks del slice», «cada caso de fallo afirma su código de error». Esas reglas vivían en un documento y en la cabeza de quien revisa. Ahora cada regla pasa a ser un test propio. Lo escribís una vez, y corre sobre cada archivo de su alcance, incluidos los que un agente escriba mañana.
Antes
Un código, un test
código A
su test A
código B
su test B
código C
su test C
Cada archivo necesita su propio test, escrito a mano.
Ahora
Una regla, un test, todos los archivos
La regla
«Las entidades reusan los mocks del slice»
Su test
se escribe una vez
- user.test.ts
- order.test.ts
- route.test.ts
- el archivo que un agente escriba mañana
En cada commit, cada push y cada corrida de CI, en menos de 500 ms.
Esa es la idea entera. El resto del post cuenta cómo funciona, cómo se ve el test y por qué cambia lo que cuesta revisar código escrito por IA.
Por qué lo necesitaba
Mis agentes de IA commitean pasando por un gate de guardianes: subagentes revisores, uno por área del código, que leen el diff en stage y le ponen nota.
Los guardianes revisan bien, y se pagan en cada pasada. Un fix de una línea reabre la ronda, porque cualquier cambio en el área de un guardián anula su nota. Planes que tendrían que llevar minutos se llevaban una tarde.
Casi todo lo que chequea un guardián es un juicio puntual sobre un solo archivo. Esa pregunta pide un modelo que lea el archivo, y solo ese archivo.
Jev, en dos líneas
Jev es el modelo de evaluación de TypeSafe. Le das un contexto y un conjunto de preguntas de sí o no, y te devuelve la probabilidad del sí para cada una, al ritmo de un linter, cobrando solo la entrada.
Cómo funciona
Una corrida, por archivo
menos de 500 ms- 1
La regla
Un archivo declarativo en el repo: qué mira y qué pregunta.
- 2
Las piezas
El test, el módulo que prueba, los mocks que podría usar. Se arman una vez por archivo.
- 3
El alcance, en código
Si el archivo no tiene lo que la pregunta juzga, se saltea con su razón.
- 4
Una llamada a Jev
Todas las preguntas de la regla juntas. Una probabilidad por pregunta.
- 5
El veredicto
Cada probabilidad contra su barrera. Un rojo nombra el archivo, la regla y la pregunta.
- 6
El registro
Guardado por hash del contenido. El gate del commit lo lee sin red.
Una regla es un archivo declarativo que vive en el repo. Dice qué mirar y qué preguntar:
export default {
title: 'The test reuses the slice’s mocks and leaves them untouched',
on: 'src/**/*.test.ts',
// Las piezas de contexto que Jev recibe por cada archivo.
state: ['file', 'subject', 'mocks'],
questions: {
reuses_slice_mocks: {
// Por debajo de esta probabilidad el test falla. Sale de un rojo y un verde reales.
barrier: 0.6,
// El código decide a quién se le pregunta: sin mocks, no hay pregunta.
applies: { mocks: true },
instructions:
'Does every entity-shaped object in `file` ' +
'name an imported mock or spread one?',
},
},
}El motor arma las piezas de cada archivo (el test, el módulo que prueba, los mocks que podría usar), saltea los archivos que quedan fuera del alcance de la pregunta y le manda a Jev una llamada por regla con todas sus preguntas.
Cómo se ve el test
The test reuses the slice’s mocks
reuses_slice_mocks · bajo la barrera
Does every object in `file` shaped like a domain entity either name an imported mock or spread one?
Es una suite de Vitest común: un describe por archivo, un test por regla, una fila por pregunta. Un rojo nombra el archivo, la regla y la pregunta exacta que falló, así quien lo lea (una persona o un agente) sabe qué arreglar.
Se fabrica una vez, corre en todos lados
Se fabrica una vez
El test de la regla
Una suite de Vitest: un comando, un código de salida, filas en stdout
Corre en todos lados
Hook de pre-commit
Frena el commit ante un rojo
Push
Revisa la rama entera
CI/CD
El mismo comando, el mismo código de salida
El agente, mientras escribe
Chequea su propio test antes de pedir el commit
Una barrida del árbol
Audita el código viejo contra una regla nueva
El test de la regla se fabrica una vez y corre en el hook de pre-commit, en el push, en CI/CD, en una barrida del árbol entero y adentro del propio loop del agente mientras escribe código. Cada corrida lleva menos de 500 ms.
Los resultados se guardan por hash del contenido. El gate del commit solo lee ese registro, así que funciona sin red y al instante, y el mismo archivo con las mismas preguntas se pregunta una sola vez.
Lo que ahorra: tiempo, tokens, rondas
Antes
Revisión con un agente guardián
Un subagente que razona por área, en cada commit
- git commit
- El gate rechaza: falta revisión
- Lanzar al guardiánagente
- Cargar brief, leer el diff, recorrer el repominutos · tokens
- Nota y plan de fixestokens
- Arreglar una línea
Cualquier cambio en su área anula la nota: la ronda empieza de nuevo.
Ahora
El test de la regla con Jev
Una pregunta de sí o no por regla, por archivo
- Editar el test
- Correr la reglamenos de 500 ms
- El rojo nombra archivo y preguntasolo tokens de entrada
- Arreglar una línea
- Verde, registrado por hash
El agente cierra este loop solo, antes de pedir el commit.
La diferencia está en dónde se cierra el loop. Con un guardián, cada fix vuelve a pasar por un agente que razona. Con el test de la regla, el mismo agente que escribió el código corre la regla, lee el rojo, arregla la línea y la corre de nuevo, todo antes de pedir el commit.
Tiempo
Con un guardián en cada commit
Una ronda de minutos por pasada
Con el test de la regla
Menos de 500 ms por corrida
Tokens
Con un guardián en cada commit
Un agente que razona y lee su brief, el diff y el repo
Con el test de la regla
Una llamada por archivo que cobra solo la entrada, y cero para el contenido que ya tiene respuesta
Rondas
Con un guardián en cada commit
Cada fix en su área reabre la revisión
Con el test de la regla
El agente arregla y vuelve a chequear solo, antes del commit
Loops del harness
Con un guardián en cada commit
Un peaje fijo en cada commit
Con el test de la regla
Al guardián lo llama una fila en rojo, y esa fila es su brief
Por qué funciona la arquitectura
En el repo
revisado en PR, como cualquier código
rules/<scope>/*.ts
qué mirar, qué preguntar
En el motor
fuera del repo, evoluciona solo
Runner de Vitest
un test por regla, por archivo
Jev, por el AI Gateway
una llamada por regla
Registro por hash
el gate lo lee sin red
- 01
La regla es dato
El alcance vive en
appliesyunless. Una falsa alarma sobre una clase de archivos es un diff de una línea. - 02
Se revisa como código
Las reglas viven en el repo: cambiar una regla es una PR con su propia review.
- 03
Un motor, todos los scopes
Tests, mocks, documentación: un scope nuevo es una carpeta de reglas, y el motor queda igual.
- 04
Preguntar y frenar van separados
El gate lee un registro por hash del contenido: sin red, al instante, reproducible.
- 05
Vitest de regalo
Códigos de salida, filtros, bail y reporters vienen incluidos, y CI también.
- 06
Se calibra antes de frenar
observeimprime la respuesta al lado de su barrera y deja pasar.
La decisión de diseño que sostiene todo es la separación. Las reglas viven en el repo y se revisan como código; el motor que las corre vive afuera y evoluciona por su cuenta. Sumar una pregunta es una entrada, sumar un scope es una carpeta de reglas, y una falsa alarma se arregla con una línea de alcance.
Dónde está la línea
Lo detecta Jev
Un juicio sobre un archivo
- ¿Un cambio en el código podría hacer fallar estas aserciones?
- ¿Cada caso de fallo afirma su código de error?
- ¿El test afirma las llamadas, además del valor?
- ¿Cada entidad es un mock importado, o un spread de uno?
Lo hace mejor el código
Comparar, contar, cruzar referencias
expect.assertions(N)contra la cuenta real- ¿El
beforeEachrestaura cada mock que un caso pisa? - ¿Cada caso pasaría en cualquier orden?
- ¿El test se conecta a una base real?
Jev es fuerte en un juicio sobre un archivo. Todo lo que compara dos listas, cuenta o depende del orden es trabajo del código común. La regla práctica que salió de calibrar: el código enumera, Jev juzga de a una cosa.
Cómo empezar
- Listá lo que chequea tu revisor. Cada guardián, cada checklist de review.
- Repartí cada ítem en código (un grep, un chequeo de AST), un juicio sobre un archivo o razonamiento de verdad.
- Escribí el cajón del medio como reglas. Una pregunta de sí o no cada una, con el mecanismo visible en el texto.
- Calibrá cada pregunta con un rojo y un verde reales antes de dejar que frene algo.
- Enchufá la suite al hook y a CI, y guardá al agente revisor para el último cajón.
El agente revisor sigue estando, y se lo llama para las preguntas que necesitan razonar, con una fila en rojo como brief. Todas las demás reglas pasan a ser un test que escribís una vez.