ODD-H

El agente decide qué hacer. El harness decide si puede decir que terminó.

Un plugin de Claude Code que lleva Organic Driven Development a hooks y a un CLI: la evidencia de cada criterio la genera odd check sobre el árbol actual, no el modelo en prosa.

Probalo: la evidencia vence simulación, no corre nada
# export-csv
Estado: locked
## Criterios
árbol de trabajo:

      
Corré los checks, editá un archivo y mirá qué pasa con la evidencia. El botón punteado es una acción humana.

Qué es

ODD-H conserva el núcleo de Organic Driven Development: explorar antes de tocar, crear un documento solo cuando hace falta y citar la spec textual. Lo que cambia es dónde vive el cumplimiento. En ODD, las reglas están en el prompt y compiten por la atención del modelo con todo lo demás. En ODD-H, las que importan para la corrección pasan al harness; en el prompt quedan solo las de criterio.

En la práctica, eso significa tres cosas:

  • "Tildé el AC-3" deja de ser una afirmación. Es una línea en .odd/ledger.jsonl con el exit code, la duración y el hash del árbol en el que se tomó.
  • El trabajo chico no paga nada. Sin feature activa, ningún gate actúa. El plugin es no-op en repos sin .odd/.
  • El trabajo grande siempre se puede retomar. Un documento por feature, commiteado en git, con un Próximo paso que se inyecta al abrir la sesión.

El diseño completo está en docs/ODD.md. Esta página describe lo que el plugin hace hoy. Los primeros pilotos contra SDD están en SDD contra ODD-H.

Cómo funciona

Tres capas, y ninguna confía en lo que otra declara: los hooks leen el ledger y el estado, no lo que el modelo dice que hizo.

Protocolo

Lo inyecta SessionStart

Diez reglas de criterio: clasificar el pedido, explorar en proporción, citar la spec, escribir ACs tipados.

CLI odd

Lo corre el agente por Bash

Ejecuta los checks, registra la evidencia, congela y enmienda criterios, cierra la feature.

Hooks

Los corre Claude Code

Deniegan escrituras indebidas y bloquean el fin de turno cuando la feature quedó en un estado que no se puede retomar.

Cuándo se crea un documento

La pregunta es una sola: ¿un agente nuevo, solo con el pedido y el git diff, podría terminarlo? Si no, va documento. Cualquiera de estos disparadores lo fuerza:

  • Va a requerir más de una sesión.
  • Depende de algo externo: una respuesta, un deploy, una API ajena, un review humano.
  • La spec supera unas 40 líneas o trae strings exactos que tienen que sobrevivir a la compactación.
  • Toca un path de riesgo (fase 4, todavía no lo detecta el harness).
  • Lo pedís vos.

Tamaño, cantidad de archivos o cantidad de pasos no disparan nada. Sin disparador, el agente trabaja inline: sin documento y sin gates.

Ciclo de vida de una feature

trackingSe escriben Spec, Criterios y Tareas.
lockedCriterios congelados. Se implementa y se juntan pruebas.
closedACs tildados por el CLI y tabla de Evidencia escrita.

El estado vive en la línea Estado: del documento. odd close solo cierra si todos los criterios tienen evidencia fresca; si falla, el agente vuelve a implementar.

Evidencia

El modelo no escribe evidencia, la pide. odd check AC-1 lee el comando de la línea del AC, lo ejecuta en la raíz del repo y agrega una entrada al ledger:

{"ts": "2026-10-28T14:02:11.204+00:00", "feature": "export-csv", "ac": "AC-1",
 "kind": "test", "cmd": "pytest tests/test_export.py::test_csv_respects_filters",
 "exit": 0, "secs": 1.84, "tree": "9c1e0a7f3b…"}

Frescura

La evidencia vale solo para el árbol en el que se tomó. El hash del árbol cubre todo el working tree, incluidos los archivos sin trackear, menos odd/tasks/ y .odd/: tildar una tarea en el documento no vence nada, pero tocar una línea de código sí. Se calcula con un índice temporal, así que no toca tu staging.

Cada AC está en uno de cuatro estados, según su última entrada en el ledger:

fresca
Pasó sobre el árbol actual.
vencida
Se tomó sobre otro árbol. Hay que volver a correrla.
en rojo
La última corrida falló sobre el árbol actual.
sin evidencia
Nunca se corrió, o el comando del AC cambió desde entonces.

Esto mata el patrón "corrí los tests, después toqué una cosa más y cerré". Cambiar la configuración también vence la evidencia, porque entra en el hash.

Tipos de criterio

TipoQué pruebaCómo se registra
testUn test puntual: pytest path::test, swift test --filterodd check, exit 0 sobre el árbol actual
cmdUn comando cualquiera: build, lint, un curl localodd check, exit 0 sobre el árbol actual
manualAlgo que solo una persona puede ver: una animación, el feel de una interacciónodd confirm, desde una terminal
judgeLo que no se reduce a un comando: fidelidad a la spec, legibilidadVeredicto de odd-verifier. Llega en la fase 4; hoy odd lock lo rechaza

La regla: si un AC puede ser test o cmd, no puede ser otra cosa.

Por qué el agente no puede confirmar un manual

odd confirm pregunta por /dev/tty, no por stdin. Un agente que lo corre por Bash no tiene terminal y falla; además, el guard de escritura le deniega la llamada y le dice que te pida a vos que lo corras. Dentro de Claude Code, ni con ! hay TTY: se corre en una terminal aparte.

TDD

Con "tdd": true en la configuración, odd close exige que cada AC test tenga una corrida en rojo anterior a la primera en verde. Es la prueba de que el test se vio fallar.

Gates

Cuatro hooks. Todos son no-op en repos sin .odd/, y los de feature no hacen nada si no hay una abierta.

EventoQué hace
SessionStartInyecta el protocolo y el estado: feature activa, su estado, el modo y el Próximo paso textual.
UserPromptSubmitCada pedido nuevo rearma el tope de bloqueos del Stop.
PreToolUseSobre Edit, Write, NotebookEdit y Bash: protege .odd/, aplica el modo read y reserva odd mode y odd confirm para vos.
StopNo deja terminar el turno en cuatro casos concretos.

Qué bloquea el Stop

No puede bloquear todo fin de turno: una feature multi-sesión termina turnos sin estar cerrada, y a veces el agente para a preguntarte algo. Bloquea solo cuando:

  • ## Criterios cambió sin odd amend. El contrato no se ablanda en silencio.
  • Todas las tareas están tildadas y la feature sigue abierta. Es la señal de que el agente cree que terminó: tiene que pasar por odd close.
  • odd close falló en este turno. No se puede intentar cerrar, fallar y despedirse igual.
  • El código cambió y el documento no. Si el árbol cambió desde el último Stop pero ## Tareas y ## Próximo paso siguen iguales, la feature no se podría retomar.

Bloquea como máximo tres veces por pedido. A la cuarta deja pasar, pero anota lo pendiente en una sección ## Sin resolver del documento. Un gate que puede trabar la sesión para siempre se termina desactivando; uno que escapa a la vista, no.

Qué protege el guard de escritura

  • .odd/ lo escribe solo el CLI. Edit y Write sobre esa carpeta se deniegan, y también cualquier comando de Bash que la mencione, salvo una invocación simple de odd o un git add, status, diff o log con flags seguros.
  • Modo read. Con /odd-read activo, Edit, Write y NotebookEdit se deniegan hasta que corras /odd-build. Sirve para reviews, auditorías o exploración.
  • Acciones humanas. El agente no puede cambiar el modo ni correr odd confirm.

Si el guard mismo falla, deniega: no deja pasar escrituras sin revisar. El Stop hace lo contrario y deja pasar, para no trabar la sesión.

Es un freno, no un sandbox. El modelo de amenaza es la pereza del modelo, no un adversario. Un intérprete con el path armado en runtime lo esquiva.

Instalación

Requisitos: Claude Code, git y Python 3.9 o posterior. El plugin usa solo la stdlib.

1. Agregar el marketplace e instalar

El repo deadcode-labs/odd-h es a la vez el plugin y su marketplace. Desde el proyecto donde lo quieras usar:

claude plugin marketplace add deadcode-labs/odd-h
claude plugin install odd-h@odd-h --scope project

Para probarlo sin instalar, en una sola sesión:

claude --plugin-dir <ruta a odd-h>

2. Activarlo en el repo

Instalado, el plugin no hace nada hasta que el repo tenga .odd/. Lo crea odd init, que podés correr directo desde el plugin:

<ruta a odd-h>/bin/odd init

Crea .odd/config.json y odd/tasks/, y agrega al .gitignore el estado efímero: state.json, ledger.jsonl y el índice del hash. Commiteá .odd/config.json y los documentos de odd/tasks/.

Dentro de una sesión de Claude Code, odd ya está en el PATH de Bash. Afuera, para odd confirm, usá la ruta a bin/odd del plugin o un alias.

Desactivarlo en un proyecto

claude plugin disable odd-h@odd-h --scope local

Primera feature

Un export CSV en una API FastAPI. El pedido trae unas 50 líneas con los nombres exactos de las columnas: eso dispara documento por spec larga. Vos pedís; el agente sigue el protocolo.

  1. Crear el documento

    El agente explora, corre odd new export-csv y completa odd/tasks/export-csv.md: la spec citada textual, el alcance y los criterios tipados.

    ## Criterios
    - [ ] AC-1 · test · `pytest tests/test_export.py::test_csv_respects_filters` · El CSV respeta los filtros activos
    - [ ] AC-2 · cmd · `ruff check app/export` · Sin errores de lint en el módulo
    - [ ] AC-3 · manual · La descarga arranca sin bloquear la UI
  2. Congelar los criterios

    odd lock guarda el hash de ## Criterios y pasa la feature a locked. Desde acá, cambiar un AC exige editar su línea y correr odd amend AC-n "motivo" en el mismo turno, que deja la entrada en Decisiones.

  3. Implementar con evidencia

    Con TDD, escribe el test y corre odd check AC-1: exit 1, y el rojo queda en el ledger. Implementa, lo vuelve a correr y da exit 0. Lo mismo con odd check AC-2.

  4. Mantener el documento al día

    Si termina un turno habiendo tocado código sin actualizar Tareas ni Próximo paso, el Stop lo bloquea: "El código cambió y el documento no." Actualiza y sigue.

  5. Confirmar lo manual

    odd close falla porque AC-3 es manual y nadie lo confirmó. El agente te pide probar la descarga. Vos lo probás y, en tu terminal:

    odd confirm AC-3
    AC-3 · La descarga arranca sin bloquear la UI
    ¿Lo verificaste vos? [s/N] s
  6. Cerrar

    odd close tilda los ACs con evidencia fresca y escribe la tabla de Evidencia en el documento. En ningún momento el agente escribió "verificado" en prosa: cada tilde salió del ledger.

En cualquier momento, odd status muestra el modo, el estado de cada AC y cuánto falta para cerrar.

Documento de feature

Uno por feature, en odd/tasks/<feature>.md, commiteado. Es la única fuente de verdad: no hay espejo en memoria externa. ## Criterios tiene formato estricto; el resto es prosa libre.

# export-csv

Estado: tracking
Disparador: spec-larga

## Objetivo
Una oración: qué cambia para el usuario.

## Spec
> Cita textual del pedido. Sin resumir, sin reordenar.

## Alcance
- Incluye: ...
- No incluye: ...

## Criterios
- [ ] AC-1 · test · `pytest tests/test_x.py::test_y` · Qué prueba

## Tareas
- [ ] T1 · Endpoint de export (AC-1)

## Decisiones

## Evidencia
(la completa `odd close`)

## Próximo paso
Una línea. Lo primero que hace quien retome.

Formato de un criterio

- [ ] AC-<n> · <tipo> · `<comando>` · <descripción>
  • test y cmd llevan comando entre backticks; manual no.
  • Cualquier línea de ## Criterios que no sea un AC es un error. Los comentarios HTML se ignoran.
  • El tilde no forma parte del contrato: lo pone odd close, no el modelo.

Decisiones de diseño

  • Criterios separados de Tareas. Los ACs son el contrato y se congelan; las tareas son el plan y se reordenan sin amend.
  • Próximo paso obligatorio. Se inyecta al abrir la sesión, así el agente retoma sabiendo dónde estaba.
  • Sin diario de progreso. El progreso es el estado de las tareas más el ledger. Un diario se desactualiza y nadie lo lee.

Referencia del CLI

La única vía para producir evidencia. Todo comando busca la raíz git desde el directorio actual y falla si no hay .odd/, salvo init.

ComandoQué hace
odd initCrea .odd/config.json y odd/tasks/, y completa el .gitignore.
odd new <feature>Crea el documento desde el template y lo activa. Nombre en minúsculas, dígitos y guiones. Una feature activa a la vez.
odd lockValida y congela ## Criterios, y pasa a locked.
odd check AC-nCorre el comando de un AC test o cmd y registra el resultado. Devuelve su exit code; un timeout se registra como 124.
odd amend AC-n "motivo"Acepta el cambio de un solo AC ya editado (nuevo, modificado o eliminado), lo anota en Decisiones y recongela.
odd confirm AC-nRegistra un AC manual. Solo una persona, desde una terminal.
odd closeSi toda la evidencia está fresca, tilda los ACs, escribe la tabla de Evidencia y cierra. Si no, falla y lista qué falta.
odd statusModo, estado de cada AC, tareas hechas y si la feature está lista para cerrar.
odd mode read|buildCambia el modo. Lo usan /odd-read y /odd-build; el agente no puede correrlo.

Comandos de Claude Code

  • /odd-read: activa el modo read. El agente puede leer, responder y proponer, pero no editar.
  • /odd-build: vuelve al modo build.

Configuración

.odd/config.json, versionado con el repo:

{
  "tdd": false,
  "hash_exclude": [],
  "check_timeout": 600
}
ClaveQué controla
tddSi es true, cada AC test tiene que verse en rojo antes de su primer verde para poder cerrar.
hash_excludePaths relativos concretos que no entran en el hash del árbol, como "assets". Útil en repos con directorios pesados. No acepta comodines, . ni ...
check_timeoutSegundos que puede tardar un odd check antes de cortarse.

Los archivos que generen tus checks (snapshots, caches) conviene que estén en .gitignore: el hash respeta el .gitignore y se toma antes de correr el comando.

Qué está hecho

El diseño se adopta en cinco fases, cada una usable por sí sola. El plugin va por la tercera.

FaseIncluyeEstado
1. ProtocoloProtocolo inyectado al iniciar y template del documentohecha
2. CLI y ledgerinit, new, lock, check, amend, confirm, close, statushecha
3. Gates básicosGuard de escritura, modo read, Stop gatehecha
4. Riesgo y subagentesrisk.yml, odd-verifier y ACs judge, odd-explorer, guard de delegaciónpendiente
5. Calibraciónmetrics.jsonl, odd doctor, defaults por stackpendiente

Hasta la fase 4, el protocolo no menciona subagentes y los paths de riesgo no se detectan solos: ese disparador queda a criterio del agente.

Límites

  • Los ACs se congelan, los tests no. Después de odd lock, el agente puede debilitar el test al que apunta un AC y el gate pasa en verde. El rojo obligatorio de TDD lo mitiga; la revisión de esos cambios por el verifier llega en la fase 4.
  • Clasificar sigue siendo juicio. Un trabajo que necesitaba documento se puede hacer inline.
  • El hash del árbol cuesta. Se calcula en cada Stop; en repos grandes, excluí directorios pesados con hash_exclude.
  • Los tests lentos hacen lento a odd check. Escribí ACs por caso, no por suite, y dejá UI y animaciones como manual.
  • El escape del Stop es visible, no imposible. A los tres bloqueos se puede terminar el turno, pero queda escrito en ## Sin resolver.

Un límite de fondo: ODD-H no mejora las decisiones de diseño. Garantiza que lo que se declaró se cumplió y que el trabajo se puede retomar.