Ayer domingo salió The Dispatch #19, el newsletter de la corporación. La edición la produjeron cuatro agentes en cadena: The Oracle buscó un dato externo verificado, Wags definió el mensaje estratégico, Saul lo auditó contra el brand book y el template lo maquetó. Salió a la lista vía Resend.
Esa es la parte que se ve. Lo interesante pasó antes de apretar el botón.
Un agente le corrigió la plana a otro
Wags había escrito una frase que sonaba muy bien: “una máquina de producción audiovisual que ahora corre sola”. Saul la mató. Y no por gusto: detectó que contradecía directamente otro bullet del mismo correo, donde yo digo que “nada se genera sin que yo apruebe el costo”. No pueden ser ambas verdad.
Quedó como “un pipeline que antes no existía”. Menos épico, cierto. Yo no intervine en ningún momento.
El footer prometía algo que no existía
Antes de enviar reviso siempre el HTML renderizado. Y ahí estaba, en el pie del correo: “puedes darte de baja cuando quieras, sin preguntas”.
No había link de baja. Ninguno. El newsletter llevaba así desde que se creó el template.
La causa raíz es la parte que me dejó pensando. El SKILL.md de mi propio comando /dispatch afirmaba, con todas sus letras, que “el unsubscribe link en Resend es automático — no requiere implementación manual”. Es falso. Resend inyecta el link solo si el HTML trae el placeholder {{{RESEND_UNSUBSCRIBE_URL}}}, que es opt-in. Si no está, el API acepta el broadcast igual y lo envía sin un solo error.
Por eso nadie lo notó en meses: el envío nunca falló.
Lo arreglé antes de mandar la #19.
109 días con la puerta abierta
El segundo hallazgo era más grave. El endpoint /api/send-dispatch no tenía autenticación. Ninguna. Cualquiera que descubriera la ruta podía hacer un POST y disparar un correo a toda mi lista de suscriptores, con el contenido que se le ocurriera.
109 días en producción así.
Lo cerré con un secreto compartido por header Authorization: Bearer, siguiendo el patrón que otro endpoint del mismo repo (alfred-audit.ts) ya usaba. Falla cerrado: si el secreto no está configurado, no manda nada.
Pero la lección no es “puse auth”. La lección es por qué estuvo 109 días abierto: porque funcionaba perfecto. Cada semana el newsletter salía. Cero errores, cero alertas, cero motivos para ir a mirar.
La prueba que pasó tres veces sin probar nada
Acá viene la parte que de verdad me incomoda.
Probé el fix contra el preview deployment de Vercel. Tres casos: sin header, con header incorrecto, con header válido. Los tres devolvieron 401.
Perfecto, pensé. La auth funciona.
Era mentira. Miré el body de la respuesta: {"error":{"code":"401","message":"Protected deployment"}}. Ese 401 lo emitía Vercel, no mi código. Los previews del proyecto tienen SSO activo, así que Vercel corta la request antes de que el handler alcance a ejecutarse. Mi código nunca corrió. Ni una vez.
Lo insidioso es esto: un endpoint completamente desprotegido habría dado exactamente el mismo resultado en esa prueba. Tres 401 idénticos. Si me quedo mirando solo el código de estado, cierro la tarea convencido de haber verificado algo que no verifiqué jamás.
Rehice la verificación bien: cinco casos contra el código real, primero en local y después contra el dominio de producción ya mergeado. Dos de esos cinco eran justamente los que faltaban:
- Un caso positivo. Secreto correcto → la request pasa la auth y llega a validar el payload → 400. Sin ese caso, “todo bloqueado” es indistinguible de “bien protegido”.
- El falla-cerrado. Sin la variable configurada → 401 más log.
Una prueba que solo sabe decir “no” no es una prueba.
Lo que quedó construido
Cerré también la fricción del proceso. El render del email lo escribía a mano cada semana, en un script que moría con la sesión — justo el lugar donde se cuelan los errores. Ahora existe scripts/render-dispatch.mjs: renderiza y corre chequeos que bloquean con código 1. Campos faltantes. Emojis. Primera oración demasiado larga para el subject. Y el que importa: HTML sin link de baja.
Cada chequeo corresponde a un error que ya cometí. Ninguno es hipotético.
Dos detalles menores del día, por si te sirven: el webhook de Vercel no gatilló deploy con el merge y tuve que forzarlo con vercel --prod — no era el límite diario de deploys, que estaba lejos de agotarse. Y Astro en local escucha solo por IPv6, así que 127.0.0.1:4321 no conecta; hay que entrar por [::1]:4321.
Lo que las tres fallas tenían en común
No fueron dos bugs y un test flojo. Fueron tres versiones de la misma cosa.
El SKILL.md afirmaba con confianza algo falso. El endpoint parecía seguro porque nunca había fallado. El test devolvió 401 tres veces y no probaba nada.
Ninguna de las tres producía una señal de error.
Un sistema que falla ruidosamente se arregla el mismo día. Un sistema que miente en silencio se queda así 109 días.
Y con agentes esto se amplifica, porque los agentes leen la documentación interna como verdad y la ejecutan al pie de la letra. Si mi SKILL.md dice que el unsubscribe es automático, ninguno de mis agentes va a dudarlo. No tienen desde dónde.
El sábado escribí que los agentes no fallan por creatividad, fallan por literalidad. Ayer aprendí la lección hermana: también heredan tus certezas equivocadas. Y esas no hacen ruido.