Paso 6: Test-first por defecto
Paso 6: Test-first por defecto
Objetivos
- Determinar cuándo aplica la política test-first y qué hacer cuando no.
- Aplicar el ciclo con la cantidad exacta de pruebas que indica la regla.
- Realizar una comprobación diferencial.
- Mantener el estado temporal fuera del cambio.
Una política, sin interruptor
Desde v4.0.0 no existe una opción de TDD estricto: la guía test-first se instala por defecto. gentle-ai sync rechaza el antiguo flag --strict-tdd.
Nota sobre la documentación.
docs/agents.mdtodavía describe Strict TDD como un modo que se configura de forma independiente. Tanto las notas dev4.0.0comodocs/usage.mdy el código desyncindican que se retiró. Este curso sigue a estos últimos.
Cuándo aplica
Test-first aplica cuando existen dos cosas:
- Una prueba relevante, ejecutable y determinista.
- Un resultado esperado claro.
Que haya pruebas o un framework en el proyecto no basta para establecer que aplica. Cuando no aplica, por ejemplo en documentación pasiva, sin ejecutor disponible o sin un RED significativo, se explica la excepción y se hacen comprobaciones funcionales o estructurales proporcionales. La excepción nunca exime de comprobar.
flowchart TD
A["Cambio de comportamiento"] --> B{"¿Hay prueba relevante, ejecutable<br/>y determinista, y resultado claro?"}
B -->|"Sí"| C["RED observado"]
C --> D["GREEN mínimo"]
D --> E["PRESERVE"]
E --> F["REFACTOR con pruebas en verde"]
F --> G["Comprobación diferencial"]
B -->|"No"| H["Explicar la excepción"]
H --> I["Comprobaciones funcionales proporcionales"]
El ciclo
| Fase | Qué se hace | Evidencia |
|---|---|---|
| RED | Una prueba por cada regla pedida, que debe fallar por el motivo previsto | La falla observada |
| GREEN | El cambio mínimo que la hace pasar | La prueba enfocada pasando |
| PRESERVE | Una prueba por cada comando u opción existente que el cambio toca, que confirma su comportamiento previo | Pasa desde el principio, por eso no requiere RED |
| REFACTOR | Mejorar la claridad mientras las pruebas siguen en verde | Las pruebas enfocadas siguen pasando |
Cuántas pruebas
La regla fija un número, y no es “las máximas posibles”:
Write one RED test per requested rule. For every existing command or option the change touches, add one test proving its previous behavior still holds; add no other cases.
internal/components/agentguidance/routing.go
Es decir:
- Una prueba RED por regla pedida.
- Una prueba de preservación por cada comando u opción existente afectada.
- Ninguna otra. El README describe esto como no rellenar con casos adicionales.
Un comportamiento existente cuenta como afectado cuando comparte el código que cambiaste: opciones, parsers, helpers o validación.
Qué tiene que afirmar cada prueba
Una sola prueba por regla no significa una prueba débil. Cada una debe:
- Afirmar todos los efectos observables de la regla: salida, código de salida y datos persistidos. Las versiones de Pi y OpenCode detallan stdout y stderr exactos, y que la entrada rechazada deje intactos datos y contadores.
- Cubrir los casos que la propia regla nombra: sus ejemplos, límites y errores.
- Pasar por la interfaz pública, sin mirar el almacenamiento interno.
Ejemplo
Pedido: “report --export csv debe rechazar --limit 0 con el mensaje ‘limit must be positive’.”
# Prueba RED, una por la regla
$ report --export csv --limit 0
# Se espera: código de salida 1, stderr exacto, ningún archivo creado
# Prueba PRESERVE, una por cada opción compartida que se tocó
$ report --limit 10
# Se espera: la misma salida que antes del cambio
La comprobación diferencial
Es la regla menos conocida y la que detecta regresiones sutiles. Antes de terminar:
- Para cada comando existente que comparte el código cambiado,
- ejecútalo con la entrada nueva o cambiada, por ejemplo con la opción nueva pasada a un subcomando hermano,
- contra el código anterior y contra tu cambio,
- y compara stdout, stderr y código de salida.
Cualquier diferencia que nadie pidió es una regresión: se arregla y se fija con una prueba.
sequenceDiagram
participant A as Copia de la base
participant B as Tu cambio
participant C as Comparación
A->>C: stdout, stderr y código de salida
B->>C: stdout, stderr y código de salida
C->>C: ¿Hay alguna diferencia que nadie pidió?
C-->>B: Sí, es una regresión: arreglar y fijar con prueba
Dónde vive la copia base
El texto canónico pide una copia del commit base en un directorio creado con mktemp -d. Esa copia no se incluye en las pruebas confirmadas: las pruebas que se commitean nunca leen el historial de Git ni un commit fijo.
Nota sobre la documentación.
docs/usage.mdmencionagit stasho una copia deHEAD. El texto instalado en los agentes especifica la copia enmktemp -d, y es la forma que no toca tu árbol de trabajo.
Estado compartido y documentación
Dos reglas acompañan al ciclo:
Estado compartido. Un registro, entrada de lista, archivo o configuración que otros comandos también crean o leen se modifica con la función existente, conservando sus invariantes de orden, unicidad, validación y normalización. Nunca se reimplementa ese paso con otra forma.
Documentación. Si agregas o cambias un comando, una opción o un mensaje, actualizas el texto de ayuda y la documentación que lo describen.
Estado temporal
El cambio no debe arrastrar cachés ni rutas temporales:
- El estado de pruebas, como directorios temporales,
HOME,TMPDIRy cachés de herramientas, queda fuera del árbol de trabajo en un directorio creado conmktemp -d. - Si tiene que vivir dentro del árbol, se usa una ruta excluida por Git y se elimina antes de terminar.
- Los subagentes no borran esos directorios: informan sus rutas exactas, y el parent los elimina después.
Reenviar la política
La política no llega sola a un subagente. El ejecutor se registra en el documento de feature, y tanto el ejecutor como la disciplina de pruebas y la evidencia o excepción se reenvían a cada worker.
Hay dos prohibiciones explícitas: nunca inventar evidencia RED/GREEN y nunca inventar un ejecutor. Si falta un comando exacto, el worker informa la limitación.
La regla que atrapó nueve premisas erróneas
La crónica de RDD cuenta una regla de disciplina relacionada: nunca se edita una prueba existente que falla, se detiene el trabajo y se informa. Según el mantenedor, esa regla detectó nueve premisas equivocadas: nueve veces un agente iba a arreglar algo, una prueba antigua se puso en rojo, y resultó que la prueba tenía razón y el diagnóstico no.
Es una anécdota de un proyecto, no una métrica general, pero ilustra por qué una prueba roja se trata como información.
Actividad
Elige una función de tu proyecto que comparta un helper con otras. Escribe:
- La regla nueva y su única prueba RED, con todos sus efectos observables.
- La lista de comandos u opciones que comparten el código, con una prueba PRESERVE para cada uno.
- Los comandos exactos de tu comprobación diferencial, usando una copia en
mktemp -d.
Autoevaluación
-
Un proyecto tiene pruebas, pero el cambio es documentación pasiva. ¿Qué corresponde?
- A. Explicar la excepción y hacer comprobaciones estructurales
- B. Escribir una prueba RED igualmente
- C. Omitir toda comprobación
- D. Activar el modo estricto
-
¿Cuántas pruebas pide la regla para dos reglas nuevas que afectan a tres opciones existentes?
- A. Tantas como se pueda
- B. Dos
- C. Tres
- D. Dos RED y tres de preservación, y ninguna más
-
¿Qué compara la comprobación diferencial?
- A. Solo los tiempos de ejecución
- B. El tamaño de los binarios
- C. Stdout, stderr y código de salida entre código anterior y cambio
- D. La cobertura de pruebas
-
Durante una tarea, una prueba existente se pone en rojo. ¿Qué indica la experiencia que relata el mantenedor?
- A. Actualizarla para reflejar el nuevo comportamiento
- B. No editarla: detener el trabajo e informar
- C. Eliminarla si es antigua
- D. Marcarla como omitida hasta el cierre
Respuestas
- A. La presencia de pruebas no establece que la política sea aplicable.
- D. Es el conteo exacto de la regla.
- C. Cualquier diferencia no pedida es una regresión.
- B. La fuente describe esa regla al relatar la segunda corrida.
Lista de control
- Compruebo si test-first aplica antes de iniciar el ciclo.
- Escribo una prueba por regla, con todos sus efectos observables.
- Identifico lo que comparte el código cambiado y agrego pruebas PRESERVE.
- Hago la comprobación diferencial en una copia temporal.
- Nunca informo una ejecución que no observé.
Fuentes
internal/components/agentguidance/routing.go, internal/assets/opencode/agents/gentle-ai-worker.md, docs/usage.md, README.md, internal/cli/sync.go, docs/architecture/the-organic-rdd-story.md, notas de v4.0.0.