
Fredy Acuna / October 30, 2026 / 10 min read
Hoy la mayor parte de mi código se escribe con un agente de IA en el ciclo. Lo difícil nunca fue lograr que el agente escribiera código. Lo difícil fue que escribiera el código correcto, que se detuviera cuando una decisión real me correspondía a mí y que dejara un rastro que yo pudiera retomar a la mañana siguiente.
El flujo que uso para eso es Organic Driven Development (ODD). No lo inventé yo. Viene incluido en Gentle AI, el harness para agentes de Gentleman Programming que configura Claude Code (y otros agentes) con un orquestador, memoria persistente mediante Engram, skills y una capa de revisión. ODD es el flujo por defecto que ese orquestador ejecuta en cada solicitud.
Este post explica cómo funciona ODD desde el punto de vista de alguien que lo usa a diario, con artefactos reales de mis propios repositorios: HitZero (un SaaS para clubes deportivos), bici.taxi (una app para pedir viajes) y NOVALYX (un proyecto de video musical con IA).
ODD ejecuta el mismo protocolo, en orden, en cada solicitud. Nadie tiene que pedir un "modo de planificación".
El primer paso es el que más subestimé. Antes de ODD, preguntarle a un agente "¿qué opinas de esta función?" podía terminar con la función reescrita. Ahora una pregunta recibe una respuesta, y un cambio necesita una solicitud explícita.
Esta es mi regla favorita. Una tarea es pequeña cuando se cumplen tres cosas:
git diff.Una tarea es grande solo cuando falla esa tercera prueba: abarca varias sesiones, espera algo externo o los requisitos son demasiado largos para sobrevivir a una compactación del contexto.
Lo que la regla explícitamente no usa es la cantidad de archivos, de líneas o de pruebas involucradas. Un renombre en 30 archivos es pequeño. Un cambio de tres líneas en un cron de facturación del que dependen todos los clubes gratuitos puede ser grande. El tamaño tiene que ver con si el trabajo sobrevive a una interrupción, no con cómo se ve en el árbol de archivos.
El trabajo pequeño se queda en línea: leer, editar, ver fallar la prueba, corregir, correr la prueba enfocada y la suite una vez. Sin documentos ni ceremonia.
El trabajo grande recibe un archivo markdown en odd/tasks/<feature-name>.md, más una copia en Engram para que sobreviva a una sesión perdida. Tiene un orden fijo:
## Specs: requisitos numerados S1..Sn que citan las palabras exactas del usuario, los mensajes de error y los ejemplos. Nunca resumidos.## Tasks: una línea por tarea con sus specs vinculadas, la ruta (en línea o delegada), el estado y la evidencia.## Log: L1 es la solicitud original textual; después se agregan correcciones, decisiones y resultados de verificación.Este es un extracto recortado de uno real en HitZero, para importar atletas desde una hoja de cálculo:
# Athlete spreadsheet import (Excel/CSV + AI header mapping)
Objective: let an admin bulk-create athletes from a spreadsheet, with Cerebras suggesting the column mapping.
Branch: feat/athlete-spreadsheet-import. Delivery: ask-on-risk. Test runner: vitest (unit)
## Specs
- S4. Normalization is deterministic code, not AI: phone to +57 10 digits, dates to `YYYY-MM-DD` (not future), ...
- S8. Plan limit: ... reject the whole batch with `PLAN_LIMIT_REACHED` (409) if `active + batch > limit`, before inserting.
## Tasks
- [x] T1 (S2, S4-S6, S9): shared row validator and normalizer. Route: inline. RED: module missing; GREEN: 9/9, types suite 253/253, tsc + biome clean.
- [x] T2 (S7-S9, S13): `POST /organizations/{orgId}/athletes/import`. Route: inline. ... Risk: item 4 (advisory lock + batch limit).
## Log
- L4 (agent finding): ... OPEN DECISION: is phase 1 athletes-only with guardian contact stored as emergency contact (S10), or must guardian invites ship in the same release?
- L6 (user answer to L4): "Emergency contact only (Recommended)". Phase 1 is athletes only; T5 stays out of phase 1.
- L8 (agent decision): S3 narrowed to headers only. No sample rows (minors' names and birth dates) are sent to Cerebras.
- L11 (independent verifier): S1-S3 and S6-S13 PASS, S4 FAIL, S5 PARTIAL. Blockers: B1 a number in the birth-date column threw `RangeError` ...
- L12 (parent): one correction batch. RED types 3/13, dialog 1/7; GREEN ...
Hay algunas cosas que vale la pena señalar:
El documento también es el traspaso. Cuando el trabajo se delega, el subagente no recibe una paráfrasis. Recibe una referencia como Spec: odd/tasks/<feature>.md, T2, S3-S4, lee el documento hasta ## Log y reporta qué S# cubrió. Eso elimina el "teléfono roto" en el que los requisitos se degradan cada vez que alguien los resume.
Incluso este post empezó como uno: la tarea que lo produjo vive en odd/tasks/blog-posts-from-recent-work.md dentro del repositorio de este portafolio, con cada post como una spec numerada.
ODD aplica una sola política de prueba primero: cuando existe una prueba ejecutable y determinista y un resultado esperado claro, se observa RED antes de implementar, luego GREEN y después se refactoriza con las pruebas en verde.
También dice cuándo no hacerlo. La documentación pasiva, la falta de un runner o la ausencia de un RED significativo reciben una excepción explícita en el log en lugar de una prueba falsa. En bici.taxi no había ningún runner cuando empezó la automatización del despacho, así que la primera tarea agregó vitest, y el documento lo dice:
## TDD
- Mode: on (source: global user config "Strict TDD Mode: enabled").
- Runner: none existed; adding `vitest` (`pnpm test` → `vitest run`).
## Progress / evidence
- T1 c3a58bb: RED observed (3 files failed, modules missing) -> GREEN 23 tests.
- T3 b1b5bd2: ... buildTripView privacy tests written after the code (characterization)
- Unverified: real WAHA sends, real DB accept race, browser run of /viaje.
Esa última línea me gusta mucho. El agente no puede afirmar lo que no vio.
Gentle AI ejecuta un orquestador padre que puede lanzar subagentes. La regla no es "usa agentes para las cosas grandes". Delegar necesita una razón con nombre:
path:line.El tamaño por sí solo, la cantidad de archivos o "esto se siente complejo" nunca disparan una delegación. Sin una razón, el padre escribe en línea, siguiendo su documento.
El porqué es el contexto. La sesión padre es la que habla conmigo y sostiene el plan. Si lee cada archivo y vuelca cada log de pruebas en su propio contexto, se vuelve lenta y olvidadiza justo cuando el trabajo se alarga. Los subagentes comprimen: un explorador lee 40 archivos y devuelve 2k tokens; un verificador corre la suite y devuelve un veredicto por spec. El padre se mantiene lo bastante liviano para seguir orquestando.
Cada cambio de código termina con una línea:
Risk: none
Risk: item 4 (advisory lock + batch limit)
Los ítems son la lista de alto riesgo: (1) datos almacenados y efectos irreversibles, (2) seguridad, (3) contratos que otros consumen, (4) concurrencia, (5) entrega y entorno, (6) ninguna prueba detectaría una regresión.
Hay una verificación antes de escribir Risk: none que me ha salvado más de una vez: ¿el diff cambia un helper, una opción o un parser compartido que otro comportamiento también usa? Si es así, eso es el ítem 3, aunque la solicitud solo mencionara una pantalla.
La línea de Risk es la que decide si un cambio recibe un verificador independiente, así que no es decoración. También me dice, de un vistazo, qué commits merecen una lectura cuidadosa.
Lo que me gusta:
Lo que cuesta:
Para mí vale la pena. Los documentos de feature de HitZero, bici.taxi y NOVALYX son hoy el historial más útil que tengo de por qué el código se ve como se ve.