devAlice
← Alice Way

11. Release gates — la línea donde 'listo' se vuelve un hecho

Qué es un release gate, por qué listo se pudre sin uno, la división Auto-Gate vs Manual-Gate, el patrón de puerta acumulativa, y el día en que un track pasó todos los checks pero faltaban 42 ítems en el entregable.

La Parte 10 — PRD terminó con una sola línea. Un PRD describe el destino.

Este artículo trata sobre cómo el operador sabe que llegó.

Release gates. Las condiciones concretas, atómicas, verificables por máquina que convierten "listo" de un sentimiento en un hecho. Sin ellos, el PRD es un deseo. Con ellos, es un camino con mojones, cada uno verificable por algo distinto a la confianza del operador.

0. La premisa — "listo" es la palabra más cara

La Parte 7 — Bucles de verificación presentó el argumento a nivel de código. En el momento en que el operador siente "eso está listo," lint y build y tests se disparan automáticamente antes de que pueda aterrizar el reporte. Ese movimiento traslada una palabra frágil — "listo" — de la memoria del operador a la aplicación por parte del sistema.

Un release gate es el mismo movimiento, un nivel más arriba.

A nivel de código, "listo" significa esta porción compila y sus tests pasan. A nivel de release, "listo" significa al sistema se le permite estar live. Las dos no son la misma palabra y no pueden aplicarse con el mismo mecanismo.

Un test que pasa no te dice que la privacy policy existe. Un build que pasa no te dice que las URIs de redirección de OAuth están en la lista de permitidos. Un lint limpio no te dice que el nombre real del operador fue borrado de cada capa de metadatos. Esas preguntas pertenecen a una puerta distinta.

1. Qué es realmente un release gate

Un release gate es una tabla.

Cada fila de la tabla es una condición atómica, verificable externamente. Cada fila tiene un estado:

  • ✅ pasa — verificada, esta fila se cumple actualmente
  • ⏳ en progreso — se está trabajando, todavía no se cumple
  • 🔒 esperando al operador — bloqueada por un paso que solo puede hacer un humano (un clic en un Dashboard, registrar una cuenta, un cambio de DNS)
  • ❌ no iniciada — requisito conocido, sin movimiento
  • no aplica — la fila se añadió de forma especulativa y la situación la volvió irrelevante

La tabla es parte del PRD. No vive en un documento aparte. La razón es la misma de la Parte 10 §4 — la forma del camino está incorporada en el documento de destino. Nadie necesita preguntar "¿qué cuenta como listo?" porque el documento responde.

Los gates son acumulativos. El Gate 1 asume que el Gate 0 pasó. El Gate 9 asume del 0 al 8. El orden no es preferencia, es dependencia — no puedes pasar el Gate 2 (infraestructura) antes del Gate 1 (cumplimiento) igual que no puedes lanzar un servicio antes de tener un dominio.

Un release gate no es una lista de verificación. Es el contrato entre intención y realidad.

2. Auto-Gate frente a Manual-Gate

No todas las filas de un gate son del mismo tipo. Dos tipos, y la diferencia importa.

Filas Auto-Gate son condiciones que una máquina puede verificar. Ejemplos:

  • pnpm build sale con 0
  • pnpm lint sale con 0
  • pnpm test sale con 0
  • grep -r "operator-real-name" . no devuelve nada
  • curl -I https://devalice.jaceclub.com devuelve 200
  • el script verify:assets reporta 15/15 coincidencias SHA-256

Si la fila se puede expresar como un comando de shell que sale con 0 o distinto de 0, pertenece al conjunto Auto-Gate. El sistema puede dispararla en cada commit, cada push, cada cron nocturno — sin que el operador esté despierto.

Filas Manual-Gate son condiciones que solo un humano puede decidir. Ejemplos:

  • "La privacy policy se lee correctamente en ambos idiomas"
  • "El líder aprueba la definición del hito M2"
  • "El conjunto de contenido semilla cubre cinco dolores distintos de usuario"
  • "Los scopes de OAuth parecen apropiados para el nivel de confianza"
  • "Los hallazgos de Lighthouse 'Best Practices' son trade-offs aceptables"

Estas filas esperan a una persona. Son la parte del gate que no se puede automatizar, y pretender lo contrario es la fuente de la mayoría de los accidentes de release.

La disciplina es:

DisciplinaRazón
Cada fila Auto-Gate tiene un script — y CI lo ejecutaDe lo contrario la fila existe en papel pero no en la práctica
Cada fila Manual-Gate nombra a quién aprueba — no solo "aprobado"De lo contrario la aprueba nadie y se cuenta como pasada
Auto y Manual son visualmente distintas en la tablaEl operador no debería tener que recordar cuál es cuál
Las aprobaciones manuales dejan rastro (commit, comentario, revisión de PR)La memoria se pudre; los commits no

3. El patrón acumulativo en la práctica

Una tabla de gates con forma real de un proyecto real — devalice — en su forma abreviada:

Gate 0: lanzamiento M0 (dominio temporal)
  - pnpm build pasa          [Auto]   ✅
  - Deploy preview en Vercel [Auto]   ✅
  - Rutas de categoría × 4   [Auto]   ✅
  - Guía semilla × 4         [Manual] ✅ (líder revisó 2026-05-10)
  - ESLint clean             [Auto]   ✅
  - Variables de entorno
    en Vercel configuradas   [Manual] 🔒 (operador)

Gate 1: cumplimiento
  - Privacy Policy (ko + en) [Manual] ✅
  - Licencia de contenido
    (CC BY)                  [Auto]   ✅ (archivo existe, cabecera coincide)
  - Licencia de código (MIT) [Auto]   ✅
  - Página About existe      [Auto]   ✅
  - Aviso de cookies (PIPA)  [Manual] ✅

Gate 2: infraestructura
  - Dominio personalizado
    vinculado                [Manual] 🔒
  - SSL activo               [Auto]   — (depende de Gate 2 fila 1)
  - División de entornos
    de producción            [Manual] 🔒

Gate 3: seguridad
  - Sin secretos
    hardcodeados             [Auto]   ✅ (grep)
  - .env.example completo    [Auto]   ✅
  - RLS activado             [Manual] 🔒 (espera push de DB)
  - Lista de permitidos
    para redirección OAuth   [Manual] 🔒
  - Validación de entrada    [Auto]   ✅
  - Coincidencia SHA-256
    de assets                [Auto]   ✅ (verify:assets 15/15)

...

Tres cosas hace esta tabla que la prosa no puede.

El estado se ve de un vistazo. Escanear la columna. Contar los ✅ frente a los 🔒. La imagen es inmediata. Nadie tiene que leer tres párrafos para saber "dónde estamos."

Los bloqueadores se autoidentifican. Una fila 🔒 en el Gate 2 es la próxima cosa que tiene que pasar. La tabla le dice al operador, al líder y a la siguiente sesión qué mirar primero.

Los gates se autoordenan. "¿Lanzamos?" se reduce a "¿está liberada la última 🔒 del Gate N?" La discusión sobre el momento termina antes de empezar.

4. El día en que "listo" fue una mentira — el coste de gates no aplicados en código

Esta es la historia a la que vuelvo cuando alguien pregunta por qué nos tomamos esta molestia.

En otro proyecto — llamémoslo Track D — cuatro sub-fases se entregaron en secuencia a lo largo de varias semanas. D-0, D-1, D-2, D-3. Cada una reportó "éxito" al final. lint pasaba. build pasaba. cargo test pasaba. La puntuación de Lighthouse estaba bien. El líder del equipo leía los reportes y seguía adelante.

Varias semanas después, durante una auditoría rutinaria, descubrimos que a los entregables reales les faltaban 42 ítems repartidos entre las cuatro sub-fases. No bugs — compromisos estructurales faltantes. Cuatro primitivas de UI que la spec decía que existirían no estaban aún conectadas. Ocho estados de cola que la spec decía que se descompondrían no lo habían sido. Siete archivos que la spec decía que se eliminarían seguían en el árbol. Nueve handlers de backend legacy que la spec decía que se quitarían seguían siendo invocados.

¿Cómo ocurrió esto? Cada reporte era honesto. El entregable sí compilaba. Los tests sí pasaban. El reportero — un agente delegado en este caso — no tenía señal de que algo estuviera mal.

La razón fue simple. Las promesas en la spec eran prosa. No estaban aplicadas en código. Nada disparaba una comprobación que dijera "la spec nombró cuatro primitivas de UI — ¿existen cuatro primitivas de UI en el árbol?" Nada disparaba una comprobación que dijera "la spec decía que el archivo X está eliminado — ¿desapareció el archivo X?" El arnés de verificación comprobaba compilación, no compromisos.

La solución fue una sub-fase que nadie quería hacer: D-Catchup. Volver atrás. Verificar cada promesa contra el árbol. Conectar lo que faltaba. Eliminar lo que debería haber desaparecido. Luego — y este es el cambio duradero — añadir un script verify:d-N a cada futura delegación, y ponerlo en la merge gate, para que las próximas cuatro sub-fases no pudieran mentirnos aunque quisieran.

Esta es la lección:

Una promesa no es un release gate. Una promesa aplicada en código sí lo es.

Después de D-Catchup, cada delegación en ese proyecto recibe tres cosas en el mismo PR:

  1. El informe de delegación — qué, por qué, cómo
  2. Un script verify:phase-N.mjs que recorre cada promesa y sale con distinto de 0 ante un fallo
  3. Una regla de CI que bloquea el merge salvo que pnpm run verify:phase-N salga con 0

Eso es un release gate a escala de delegación. Misma forma que los gates M0–M10 en el PRD, solo que aplicado a una unidad más pequeña.

5. La forma de un script de verify

Un script de verify es pequeño y tonto a propósito. No es un framework de tests. Es un walker literal.

Para cada fila de la spec, un bloque:

// Fila D-1.3 — La cola debe descomponerse en 8 estados
const queueStates = readQueueStates();
if (queueStates.length !== 8) {
  fail("Queue has " + queueStates.length + " states, expected 8");
}

Tres propiedades importan.

1:1 con la spec. La fila 3 de la spec recibe la comprobación 3 del script. Si se añade una fila a la spec, el script no pasa hasta que se añada una comprobación. Los dos documentos se mueven al unísono.

La salida es el recibo. El reportero no dice "verificado." Pega el stdout en crudo. verify:d-1 passed 18 / 18. Si esa línea no está en el reporte, el reporte no se acepta.

El fallo es ruidoso y específico. "Queue has 5 states, expected 8" gana a "verify failed." El operador debería saber qué falta sin abrir el script.

El script está hecho a mano. No generado. No impulsado por framework. Es la lectura que el operador hace de la spec, expresada en código, sentada en la merge gate.

6. Filas Manual-Gate — cómo mantenerlas honestas

Las filas Auto-Gate son fáciles de mantener honestas. CI ejecuta la comprobación y o pasa o falla. Las filas Manual-Gate son donde se nota la disciplina.

Los patrones que uso:

Cada fila Manual nombra al aprobador. No "aprobado por el equipo" — nombrar el rol y la fecha. Manual ✅ líder, 2026-05-10. Si el aprobador cambia, la fila vuelve a ⏳ hasta que el nuevo aprobador la vuelva a aprobar.

Las filas Manual dejan rastro de commit. El commit que cambia una fila de ⏳ a ✅ incluye el nombre del aprobador en el mensaje y una nota de una línea sobre lo que revisó. [devAlice] chore(gate-1): privacy policy ko/en — líder aprobó. Seis meses después, cuando alguien pregunta "espera, ¿quién decidió que esto estaba bien?" el git log responde.

Las filas Manual tienen una cadencia explícita de re-revisión. Una privacy policy aprobada en M0 no está aprobada para siempre. El PRD nombra un disparador de re-revisión — re-revisión en lanzamiento M2 o re-revisión cada dos trimestres. Cuando se dispara, la fila vuelve a ⏳ hasta que se vuelva a confirmar.

Ninguna fila Manual usa "ya lo haré" como estado. O hay un responsable y una fecha límite o es ❌.

7. Las trampas

Los release gates fallan de formas predecibles.

7.1 Gates que crecen pero nunca cierran

Una tabla del Gate 3 acumula filas durante dos trimestres y nunca tiene un momento en que cada fila sea ✅. Eso no es un gate, es una lista de deseos. Dividirlo. Las filas no bloqueantes se vuelven "Gate 3 parte B" o bajan a "seguimiento post-lanzamiento." El propósito de un gate es cerrar.

7.2 Gates que pasan sin comprobación

Una fila dice Auto ✅ pero no hay ningún script en ningún sitio que la ejercite. Dos semanas después no pasa en absoluto y nadie lo sabía. Cada fila Auto necesita una comprobación ejecutable, nombrada en la fila. Si la comprobación no existe, la fila es ❌, no ✅.

7.3 Promesas que nunca se vuelven gates

Este es el patrón D-Catchup. La spec dice que X será cierto. Nada comprueba que X es cierto. El reportero es honesto sobre lo que verificó, que es lo que ellos conocían — el build, los tests, el lint. La cosa que nadie verificó es la cosa que se rompe. Cualquier cosa en una spec que merezca prometerse merece una puerta.

7.4 Aprobaciones manuales que "ocurrieron verbalmente"

Una fila dice Manual ✅ porque un líder dijo "se ve bien" en una sesión hace tres semanas. Nada en git lo registra. La próxima vez que alguien pregunte por qué pasa, el rastro está perdido. Cada aprobación manual deja un commit o una revisión de PR. Si no está en el repositorio, no ocurrió.

7.5 El gate "lo probaremos en producción"

Una fila dice pendiente — se verificará tras el lanzamiento. Eso no es un release gate, es un post-mortem en espera. O es verificable antes del lanzamiento (moverlo a ⏳ y verificarlo) o no es un criterio de lanzamiento (moverlo a una sección aparte de "seguimiento post-lanzamiento").

8. Un principio

La forma entera de los release gates se reduce a una línea.

"Cualquier cosa que la mente del operador trataría como 'listo,' que la compruebe primero el sistema. Cualquier cosa que el sistema no pueda comprobar, nombra al humano que lo hizo."

Los release gates son la capa donde el PRD deja de ser un deseo y se vuelve un contrato. Las filas Auto-Gate son el contrato aplicado por una máquina. Las filas Manual-Gate son el contrato aplicado por un humano nombrado que deja rastro. La combinación es lo que el operador quiere decir cuando dice que una porción está live.

Y una vez que los gates son reales — una vez que cada fila es o un script que sale con 0 o una aprobación humana registrada en git — la mayor parte del trabajo diario deja de necesitar al operador en el teclado. De eso trata la Parte 12 — Ralph loop. Un PRD dice qué construir. Una puerta dice cuándo está construido. Un bucle recorre el camino de uno al otro, por sí solo, 24/7, mientras el operador duerme.