← Back
Applied AI 20 min read

This article isn’t translated yet — showing the Spanish original.

Spec driven development: qué hace verificable a una spec

Escribir la especificación como artefacto principal y dejar que el agente implemente contra ella funciona si la spec dice cómo se comprueba que está cumplida. El mecanismo explica por qué, y también dónde se rompe.

Data verified on 10 August 2026. Provider pricing, limits and flag names change.

Un agente que implementa contra una spec hace algo bastante literal. Toma el texto, lo convierte en tokens, lo mete en el context window y genera acciones condicionadas por eso. La spec es evidencia que sesga la próxima predicción, y su fuerza es exactamente esa.

Eso cambia qué es una buena spec. Con una persona, la especificación es un punto de partida y el resto lo completa el sentido común compartido: las convenciones del repo, lo que se charló en el stand-up, la intuición de que ese endpoint obviamente necesita idempotencia. El agente no tiene ese fondo común. Cuando la spec deja un hueco, el hueco se llena con el promedio de GitHub.

Hay una segunda diferencia, más importante. Una persona sabe cuándo terminó. El loop de un agent termina cuando el modelo decide que terminó, o cuando algo externo se lo dice. De ahí sale el criterio que ordena todo lo demás: una spec sirve como artefacto principal cuando define cómo se comprueba que está cumplida. Sin ese oráculo es una lista de deseos bien redactada.

Qué le pasa a una spec adentro del loop

Dos cosas del mecanismo determinan cómo hay que escribirla.

La spec compite por atención, y los huecos que deja no quedan vacíos. Entra al contexto en el paso 2 y después convive con árboles de archivos, salidas de comandos, stack traces y diffs. En un loop de cuarenta pasos, el peso relativo de esas 800 líneas de prosa baja. Mientras tanto, el modelo no tiene una primitiva limpia para "esto no está definido, freno y pregunto": cuando la spec dice "guardá el resultado" y no dice dónde, elige, y elige lo más probable dado su training. El benchmark Orchid mide esto sobre 1.304 tareas con ambigüedad léxica, sintáctica, semántica y vaguedad, y encuentra dos cosas incómodas: la ambigüedad degrada a todos los modelos y el efecto es más pronunciado en los más avanzados, y los modelos producen implementaciones funcionalmente divergentes ante la misma spec ambigua sin ser capaces de detectar ni resolver esa ambigüedad por sí solos (arXiv 2604.21505). Muchas veces aciertan. El problema es que no distinguís dónde acertó de dónde adivinó, porque el output se ve igual.

El criterio de terminación puede ser interno o externo. Si es interno —"cuando esté completo"— el resultado depende del estado del contexto en ese momento. Si es externo —un comando que devuelve exit code 0— depende del código. La diferencia entre una spec que funciona y una que no suele reducirse a eso. Consecuencia práctica: los criterios de aceptación conviene que sean comandos, porque un comando se vuelve a ejecutar y su salida vuelve a entrar al contexto.

En una spec para un agent, la línea que más pesa es la que decide cuándo se terminó.

Cuatro columnas muestran el contexto de un agente en los pasos 2, 8, 20 y 40: la banda amarilla de la spec mide siempre lo mismo, pero encima se apila una banda violeta de observaciones que crece —árbol de archivos, salidas de comandos, stack traces, diffs— hasta que el peso relativo de la spec cae de 67 % a 19 %. Abajo, dos filas contrastan qué entra al contexto y cuándo: la prosa es un único bloque amarillo en el paso 2, mientras que el comando del criterio de aceptación aparece como tres bloques rojos en los pasos 8, 20 y 40, cada uno con una flecha que sube hasta su columna.
La spec no se borra: se hunde. Lo único que vuelve a la superficie es lo que se puede volver a ejecutar.

Los tres grados de un criterio de aceptación

Un criterio es verificable si algo que no sea el modelo puede evaluarlo. Eso admite grados.

GradoQuién evalúaEjemploCostoCuándo
EjecutableUn comando, un exit codepytest tests/idempotencia.py::test_replay_devuelve_el_mismo_idBajo, repetibleDefault
InspeccionableUn chequeo determinista que no es un testEl diff de openapi.yaml es vacío; tsc --noEmit pasa; no hay ocurrencias de SELECT * en el móduloBajoInvariantes estructurales
De juicioUna persona o un LLM-as-judge"El mensaje de error explica qué hacer"Alto y ruidosoÚltima instancia, nunca como criterio único

La operación de trabajo es la conversión: cuando un criterio cae en el tercer grado, preguntate qué tendría que ser cierto para bajarlo uno.

Frase que no se puede fallarCriterio que sí
"El endpoint tiene que ser rápido"k6 run load/cobros.js con p95 por debajo de 200 ms sobre 500 requests contra el dataset de fixtures
"Hay que manejar bien los errores"Todo path de error devuelve un body con code del enum ErrorCode, y pytest tests/errores -q cubre los cinco códigos
"La UI tiene que ser accesible"axe-core no reporta violaciones de nivel A o AA en las tres vistas nuevas
"No romper compatibilidad"npm run openapi:diff no reporta breaking changes contra el tag del release anterior

No todo baja, pero baja más de lo que uno espera. Los criterios de juicio que quedan cargan el problema que ya conocés del artículo de evals: un judge tiene sesgos propios y una varianza que hay que medir antes de confiarle una decisión. Sirven como señal, no como gate.

Una escalera de tres escalones que desciende de izquierda a derecha. Arriba a la izquierda el grado 3, de juicio: lo evalúa una persona o un LLM-as-judge, cuesta caro y ruidoso, y lleva una franja roja que dice señal, no gate. En el medio el grado 2, inspeccionable: un chequeo determinista que no es un test, como tsc --noEmit o un diff vacío de openapi.yaml. Abajo a la derecha el grado 1, ejecutable: un comando y un exit code, barato y repetible, marcado como el default. Dos flechas rojas descendentes marcan la conversión, y un panel muestra dos bajadas concretas: 'tiene que ser rápido' se vuelve p95 menor a 200 ms sobre 500 requests, y 'tiene que ser accesible' se vuelve cero violaciones A o AA de axe-core en las tres vistas nuevas.
El grado no describe qué tan importante es el criterio: describe quién queda habilitado a cerrar el loop con él.

El alcance se escribe en negativo

La sección que más rinde de una spec es la de lo que queda afuera. Un agente con acceso a bash tiene alcance máximo sobre el repo, y al harness le llega un string opaco. Si la spec no delimita, el blast radius del cambio es todo lo que el proceso pueda escribir.

La documentación de Anthropic sobre harness design para aplicaciones de larga duración describe specs auto-contenidas con tres propiedades: nombran los archivos y las interfaces que tocan, declaran explícitamente el fuera de alcance, y cierran con un paso de verificación end-to-end. Eso se escribe así:

## Fuera de alcance
- No se modifica el schema de `payments`. Si hace falta una columna nueva,
  frená y reportá en vez de escribir la migración.
- No se tocan tests fuera de `tests/idempotencia/`. Si un test existente
  falla, es un hallazgo para reportar, no un archivo para editar.
- No se agregan dependencias nuevas a `pyproject.toml`.
- No se implementa idempotencia en el resto de los endpoints.

## Verificación end-to-end
Levantar el stack con `make dev`, mandar el mismo cobro dos veces con la
misma `Idempotency-Key` contra el servicio corriendo, y confirmar que
`SELECT count(*) FROM movimientos WHERE external_id = $1` devuelve 1.

La segunda prohibición cierra el agujero clásico: la forma más barata de hacer pasar la suite es editar la suite. Las restricciones que importan de verdad después se promueven del texto al harness, que es donde efectivamente se hacen cumplir.

Cómo se ve una spec ejecutable

Un ejemplo del tamaño que conviene: un cambio real, no un épico.

# Idempotencia en POST /v1/cobros

## Objetivo
Un cliente que reintenta el mismo cobro por timeout de red no debe generar
un segundo movimiento.

## Comportamiento
- El request acepta un header `Idempotency-Key` (UUIDv4, obligatorio).
- Si la clave existe y el body hasheado coincide, se devuelve la respuesta
  original con status 200 y header `Idempotent-Replay: true`.
- Si la clave existe y el body difiere, se devuelve 422 con código
  `idempotency_key_reuse`.
- Las claves expiran a las 24 horas.

## Criterios de aceptación
1. `pytest tests/idempotencia -q` pasa. Cubre: replay exacto, replay con
   body distinto, clave ausente, clave expirada, dos requests concurrentes
   con la misma clave.
2. El test de concurrencia usa dos conexiones reales contra Postgres y
   verifica que `movimientos` tiene exactamente una fila.
3. `openapi.yaml` documenta el header y los códigos de error nuevos, y
   `npm run openapi:diff` no reporta breaking changes.
4. `make lint typecheck` pasa sin warnings nuevos.
5. `make spec-check` confirma que cada comando nombrado en esta spec existe
   y corre. Si un criterio referencia un comando que dejó de existir, el CI
   falla y la spec se actualiza en el mismo PR.

Lo ejecutable acá viene de tres cosas. Cada criterio nombra un comando o un artefacto inspeccionable. El criterio 2 fija el detalle que un agente resolvería mal por default: "dos requests concurrentes" a secas se implementa con dos llamadas secuenciales en el mismo proceso, que pasa el test y no prueba nada. Y el criterio 5 hace que la spec envejezca ruidosamente en vez de en silencio.

Lo que las herramientas automatizaron

Spec Kit, de GitHub, parte el flujo en comandos con prefijo propio: /speckit.constitution para los principios del proyecto, /speckit.specify para el qué y el porqué, /speckit.clarify para las zonas subespecificadas, /speckit.plan, /speckit.tasks, /speckit.implement, /speckit.analyze y /speckit.checklist. Su plantilla de spec (templates/spec-template.md) obliga a escenarios Given/When/Then, requisitos numerados, criterios de éxito medibles y agnósticos de tecnología, y marca las ambigüedades con [NEEDS CLARIFICATION: ...] en vez de dejarlas pasar.

La pieza más interesante es /speckit.analyze (templates/commands/analyze.md): un pase de lectura sobre los propios artefactos con seis categorías de detección —duplicación, ambigüedad, subespecificación, choques con la constitution, gaps de cobertura e inconsistencia— y una rúbrica donde "criterio de aceptación no testeable" es HIGH. Un requisito con cobertura cero escala a CRITICAL solo cuando bloquea funcionalidad baseline; un gap de cobertura genérico no lo es. Es una eval de la spec, no del código. Y templates/commands/checklist.md lo dice explícito: los checklists son "unit tests for requirements writing". Un ítem válido pregunta si el requisito está definido, no si el sistema anda.

Kiro, de AWS, usa tres archivos por feature: requirements.md con user stories y criterios de aceptación, design.md con arquitectura, tasks.md con las tareas discretas. Los criterios se escriben con el patrón WHEN [condición] THEN the system SHALL [comportamiento], y para bugfixes agrega SHALL CONTINUE TO para fijar lo que no debe cambiar. La forma viene de la ingeniería de requisitos tradicional y sirve para lo mismo acá: eliminar la ambigüedad sintáctica antes de que la resuelva un modelo.

Un escalón más arriba está la práctica que documenta Anthropic en harness design for long-running application development: un agente planner expande un prompt de una a cuatro frases en una spec de producto, y después cada iteración se negocia como un sprint contract donde el generador declara qué construye y cómo se va a comprobar el éxito antes de escribir código. Un evaluador después ejercita la app corriendo vía Playwright MCP en vez de dar por buena la spec leyendo el diff. El criterio de verificación se escribe antes que la implementación, no después.

Qué tan madura es la práctica

Emergente, no consensuada. Birgitta Böckeler, en martinfowler.com (octubre de 2025), ordena el campo en tres niveles: spec-first, donde la spec guía la generación y después el código manda; spec-anchored, donde la spec se mantiene viva junto al código; y spec-as-source, donde el código pasa a ser un artefacto derivado. Su observación sobre el tooling actual es la que conviene tener presente: tanto Kiro como Spec Kit funcionan en la práctica como spec-first, aunque Spec Kit aspire a más.

Böckeler marca además dos riesgos concretos. El primero es heredar la rigidez del Model-Driven Development, con la complicación agregada de que ahora el motor de transformación no es determinista. El segundo es la inflación de ceremonia: documenta un bugfix chico que el flujo convirtió en cuatro user stories con dieciséis criterios de aceptación. La evaluación comparada de seis frameworks de arXiv 2606.04967 llega a un diagnóstico compatible: ninguno cubre bien las seis dimensiones que mide, hay un trade-off estructural entre profundidad de proceso y portabilidad, y los riesgos recurrentes son drift entre spec y código, exceso de confianza en artefactos generados y ausencia de benchmarks del proceso completo. Son buenas plantillas y buenos chequeos de forma. No son evidencia de que el enfoque escale.

Dónde vive la verificación de verdad

Un criterio de aceptación escrito en prosa es una intención. La aplicación ocurre en el harness, y hay una escalera.

Dónde se escribeQué haceSu límite
Prosa en la specSesga la predicciónEl modelo puede no seguirla y el output se ve igual
Herramienta dedicadaIntercepta con argumentos tipadosSolo cubre lo que dejó de pasar por bash libre
/goal en Claude CodeEvalúa el objetivo antes de dejar cerrarEl evaluador no corre herramientas: juzga lo que el agente ya mostró
Hook PreToolUse con exit code 2Bloquea la tool call y le devuelve el motivo al modeloCubre la acción, no la omisión
Hook Stop con {"decision": "block"}Impide que el turno termineClaude Code lo anula y termina el turno tras 8 bloqueos consecutivos

La distinción de fondo es bash contra herramientas dedicadas. Bash da alcance máximo, pero el harness solo ve un string. Promover una acción a herramienta dedicada le da un punto de intercepción: puede pedir confirmación, chequear que el archivo no cambió desde la última lectura, renderizar UI propia o paralelizar lo seguro. Lo difícil de revertir se promueve.

/goal es el escalón intermedio y tiene una consecuencia sobre cómo se redacta la condición. El evaluador no ejecuta nada: juzga la conversación. Entonces la condición tiene que ser algo que la salida del propio agente demuestre. "La salida de pytest tests/idempotencia -q aparece en la conversación y termina en 0" es evaluable; "los tests pasan" es una afirmación que el agente puede escribir sin haberla corrido.

Los hooks son el escalón duro. Un PreToolUse que sale con exit code 2 impide que la tool call se ejecute, y un Stop con {"decision": "block"} impide que el turno termine (docs). Eso convierte "corré los tests antes de dar por terminado" en una condición de salida del loop en vez de una línea sepultada treinta turnos atrás. Con un techo: tras 8 bloqueos consecutivos, Claude Code anula el Stop hook y termina el turno igual. El gate frena al agente que se distrajo, no al que no puede cumplir el criterio.

La traducción es directa. Lo que la spec prohíbe y es irreversible va a PreToolUse. Lo que la spec exige como condición de completitud va a Stop o a /goal. Lo demás queda en prosa.

Cuatro líneas de una spec, cada una marcada con un cuadrado de color, se reparten por tres flechas en tres columnas. La primera, amarilla, es prosa en el contexto: heurísticas, convenciones y decisiones de juicio, que sesgan la predicción pero no la determinan; si el agente no la sigue, no pasa nada y el output se ve igual. La segunda, violeta, es una herramienta dedicada: acciones difíciles de revertir que necesitan gate, dry-run o auditoría, y que dejan de pasar por bash libre. La tercera, roja, es un hook del harness: PreToolUse con exit code 2 bloquea la llamada y le devuelve el motivo al modelo, y Stop con decision block impide terminar el loop hasta que el comando pase. Un medidor de tres cuadrados por columna muestra la fuerza creciendo de izquierda a derecha, de sugerencia a determinista.
Escribir una restricción como prosa no es gratis ni inútil, pero es elegir el único lugar donde el modelo puede no hacerle caso sin que se note.

Cuánta libertad dejarle

Sobre-especificar rompe tanto como sub-especificar, y de manera menos visible. La especificidad tiene que coincidir con la fragilidad de lo que describís. Donde hay una única secuencia segura —el orden de una migración, los flags de un deploy— van comandos exactos. Donde el campo es abierto —cómo estructurar un módulo, qué nombre poner— van heurísticas en prosa, porque un guion exacto para una decisión de juicio produce peor resultado que el criterio del modelo. La misma doc de harness design de Anthropic lo recomienda al revés de lo que dicta el instinto: mantener la spec alta y no sobre-especificar la implementación.

Hay un caso que sorprende: los prompts y skills escritos para modelos anteriores suelen ser demasiado prescriptivos para los actuales y bajan la calidad del output. Enumerar quince pasos donde alcanzaba con enunciar el objetivo y las restricciones es más frágil, no más seguro. Lo mismo con pedir explícitamente "verificá tu trabajo": en los modelos actuales esa instrucción produce sobre-verificación, porque el comportamiento ya viene de fábrica. La spec dice qué tiene que ser cierto al final, no cómo llegar.

Lo que sí conviene pedir es la lectura. Specine, aceptado en ICSE 2026, ataca el problema de specification perception —el modelo no percibe la spec que vos creés que escribiste— extrayendo la spec tal como el LLM la interpretó y reconciliándola con la original, y reporta +29,60 % de Pass@1 sobre el mejor baseline (arXiv 2509.01313). La versión barata de eso es pedirle al agente que devuelva su lectura de la spec antes de tocar código, y leerla.

Qué tipo de trabajo no se especifica bien

Los cuatro criterios que Anthropic usa para decidir si construir un agent —complejidad, valor, viabilidad y costo del error— sirven igual de bien acá. El que más filtra es el último: si un error no se detecta ni se revierte, no hay spec que te cubra, porque la spec no ejecuta nada.

Diagnóstico y exploración. No podés escribir el criterio de aceptación de "averiguá por qué el consumer se traba los martes", porque el criterio es el hallazgo. Lo que sí se especifica es el entregable —qué evidencia hay que traer, qué comandos hay que correr— y ahí el artefacto principal deja de ser una spec y pasa a ser una skill.

Cambios donde el criterio es el gusto. Copy, jerarquía visual, tono de un mensaje de error. Se acota con restricciones duras —largo máximo, tokens del design system, contraste— pero la decisión final es de juicio y conviene que quede afuera del gate.

Migraciones e infraestructura irreversible. El problema no es la spec sino el costo del error, y la respuesta es bajar de tier. Un workflow con la lógica en tu código, donde el paso destructivo es una llamada tuya y no una decisión del modelo, es más simple y más seguro que un agent con una spec muy detallada.

Refactors grandes. Este parece inespecificable y no lo es. El criterio de aceptación de un refactor es "el comportamiento observable no cambió", y eso se escribe: tests de caracterización escritos antes, cobertura medida antes y después, diff de la API pública vacío. Si esos tests no existen, escribirlos es el trabajo, y ese trabajo sí se especifica.

Cómo se rompe en la práctica

El agente escribe el test y el código. El criterio pasa por construcción: el test verifica lo que el código hace, no lo que la spec pide. Es la falla más silenciosa porque el CI queda verde. La mitigación estructural es que el criterio nombre tests que ya existen, o que los escriba otro actor: vos, o un agent con tarea separada y sin acceso al diff de implementación.

Spec drift. El código evoluciona, la spec no, y a los dos meses es un documento arqueológico. La defensa que se sostiene es tratarla como código: vive en el repo, entra en el mismo PR que el cambio, y el chequeo de comandos del criterio 5 la rompe en CI cuando dejó de describir el sistema.

Inflación. Cuatrocientas líneas de spec para un cambio de veinte diluyen los criterios que importan en prosa que compite por atención. El bugfix convertido en cuatro user stories con dieciséis criterios que documenta Böckeler es el caso testigo. Si la spec es más larga que el diff esperado, hay algo mal calibrado.

Checklist theater. Ítems que suenan a verificación y no verifican: "confirmar que el manejo de errores es correcto". Un ítem que no nombra un comando, un archivo o un valor concreto es decoración.

Herramientas

HerramientaPara quéCosto
GitHub Spec KitEl toolkit de referencia y el que fija el vocabulario. Ciclo constitution → specify → plan → tasks → implement, con plantillas, checklists y análisis cruzado entre artefactos. Agnóstico del agente. Doc oficialGratis
AWS Kiro (Specs)La implementación más opinionada: tres archivos con roles separados y criterios en WHEN / THEN / SHALL. La mejor para ilustrar spec ejecutableTier gratuito de 50 créditos; los planes útiles arrancan en USD 20 al mes
OpenSpecLa variante liviana: menos ceremonia, orientada a cambios incrementales sobre código existente en vez de features de ceroGratis
BMAD-METHODEl otro polo: reparte la especificación entre roles de agente que se pasan artefactos. Útil para discutir dónde la ceremonia se vuelve costo puroGratis
Gherkin / CucumberEl antecedente que hace ejecutable a una spec desde antes de los LLMs. "Criterio de aceptación verificable" tiene una definición operativa de veinte añosGratis
Playwright MCPLa pieza que cierra el loop: el agente evaluador ejercita la app corriendo en vez de leer el diffGratis

El nivel de rigor conviene que escale con el tamaño del cambio. Spec Kit para una feature nueva y OpenSpec para un ajuste incremental resuelven problemas distintos, y usar el primero para el segundo es exactamente la inflación que describe Böckeler.

El criterio operativo

La práctica es nueva y el tooling más nuevo todavía. Antes de convertirla en proceso, medila sobre tu propio trabajo: tomá el mismo tipo de ticket hecho de las dos maneras, y registrá cuántos pasaron review sin cambios de comportamiento en el primer intento, cuánto tiempo llevó escribir la spec y cuánto se ahorró en iteraciones. Un artículo que pide criterios verificables no puede pedirte que adoptes el enfoque sin uno.

Mientras tanto, el criterio chico se sostiene solo. Si podés escribir el comando que dice que el trabajo está hecho, escribí la spec. Si no podés, ese es el primer problema, y probablemente no lo resuelva un agent.

Keep going

Reading

Videos

Next · Applied AI · 18 min Evals: if it can't block a deploy, it isn't an eval Read next →