Notas

Un modelo frontera coordina, los baratos ejecutan

Notas de campo sobre una pipeline de agentes construida sobre Orca: dónde termina el control plane, qué políticas aporta la capa propia y por qué un Delivery sin ack parecía una notificación perdida.

Durante cuatro días, una combinación de un coordinador frontera y ejecutores baratos cerró seis épicas en dos proyectos reales: más de cuarenta tareas y una suite que creció de unos 1.100 tests a casi 2.000. El coste estimado que enseñaba el agente por tarea iba de $0,01 a $0,13.

La arquitectura separa dos responsabilidades: Orca es el control plane; la pipeline es un adaptador de políticas. Orca conserva la ejecución y su estado duradero. La capa propia decide qué puede tocar cada agente, qué pruebas debe superar y cuándo es seguro aceptar, reintentar o cerrar su trabajo.

Un incidente muestra por qué importa esa frontera. Dos subagentes terminaron casi a la vez. El coordinador atendió al primero y el segundo pareció desaparecer. No se había perdido ningún mensaje. Había un Delivery FIFO antiguo sin ack, y Orca estaba reproduciendo correctamente ese lote mientras los avisos posteriores esperaban detrás. La solución no necesita otro buzón: necesita respetar el contrato del que ya existe.

1. La frontera que hace útil a la pipeline

Arquitectura en dos capas. Orca conserva Runs, Tasks, Dispatches, Deliveries, terminales y worktrees. Un adaptador de políticas aplica perfiles de proyecto, permisos, compatibilidad, verificadores y cleanup. El coordinador y los workers operan a través de ambas capas sin duplicar estado.
Orca conserva el estado; la pipeline decide qué está permitido y qué evidencia hace falta para aceptar un resultado.

Orca ya sabe crear Runs, representar un DAG, despachar una Task, mantener threads duraderos, supervisar terminales y crear worktrees. Volver a implementar cualquiera de esas piezas exige sincronizar dos verdades. En la práctica acabas preguntándote si manda el Dispatch de Orca, el fichero de una guardia o el PID de un watcher.

La capa propia solo merece existir donde añade una decisión comprobable:

Orca, control planePipeline, policy adapter
Run, DAG, Task y Dispatchperfil de proyecto y límite de concurrencia
Delivery FIFO y threadsresolución completa antes del ack
terminal y transcriptcomando seguro del agente y confirmación de arranque
worktree y ciclo de vidasetup, raíces editables y cleanup seguro
outcome del workergates, QA y política de retry

Esta frontera elimina mucho código. No hay daemon, broker, SQLite, spool NDJSON ni cola local. Tampoco hay un wrapper para cada comando de Orca: si el wrapper no puede imponer o verificar una política, sobra.

2. Las decisiones que sostienen el sistema

Cuatro decisiones hacen que esta capa propia merezca su sitio.

Worktrees reales. Cada worker tiene checkout, rama y terminal propios. No comparte índice, cachés de build ni servidor de desarrollo. Y el trabajo sobrevive a la sesión: si falla el proveedor, se reemplaza el proceso y se conserva el disco.

Specs completas. La Task de Orca contiene el contrato entero: objetivo, alcance, criterios, rutas, casos y gates. No se mantiene una copia en .pipeline/specs/ ni se despacha una frase que apunte a un fichero externo: eso crearía artefactos huérfanos y specs ausentes tras una recuperación. La Task es la única copia, lo que además encaja con una propiedad importante de Orca: el spec de una Task es inmutable.

Default-deny. OpenCode arranca con una base cerrada, una capa del stack y una overlay del perfil. Un worker de web no puede tocar Electron; uno de seguridad puede editar dos directorios; uno de QA solo escribe evidencia. Secretos, claves, .git/** y .pipeline/** no se pueden reabrir desde una capa posterior.

Verificación independiente. El coordinador vuelve a ejecutar los gates y revisa el diff. No basta con que el worker diga «todo verde». En Lawyer, una tarea que crea rutas usa un perfil que añade build y el verificador de bundle. En BIA, tocar estilos globales añade CSS computado y Playwright: typecheck, lint y unit tests pasaron dos veces con la aplicación visualmente rota.

La elección del gate sale de lo que el cambio puede romper, no de una lista ceremonial.

3. El mensaje no se perdió: estaba detrás de un Delivery

El mailbox público de Orca tiene un contrato preciso. check devuelve el Delivery FIFO más antiguo. Ese Delivery puede contener muchos mensajes y se reproduce idéntico hasta que el coordinador confirma el lote entero con su deliveryId.

Ciclo del mailbox. Check obtiene el Delivery más antiguo; el coordinador procesa todos sus mensajes; cada worker terminado se libera, retiene o reutiliza y las preguntas o escalaciones quedan duraderas; solo entonces se hace ack y se comprueba el siguiente Delivery. Si el coordinador cae antes del ack, el mismo Delivery se reproduce y las acciones son idempotentes.
El ack es el commit del coordinador. Va después de resolver todo el lote, nunca después del primer mensaje.

El bucle correcto es pequeño:

  1. Obtener el Delivery más antiguo.
  2. Procesar todos sus mensajes.
  3. Para cada worker_done, validar Task, Dispatch y outcome; después reutilizar, retener o liberar el terminal.
  4. Dejar una pregunta como needs_input en su thread; bloquear la Task ante una escalation.
  5. Hacer ack solo cuando cada decisión ya sea duradera en Orca.
  6. Volver a comprobar sin espera hasta vaciar lo ya disponible.

Si el coordinador se cierra entre los pasos 3 y 5, Orca entrega el mismo lote al volver. Por eso las acciones deben ser idempotentes: worker-release lo es, marcar una Task como bloqueada lo es y una reutilización comprueba antes si la nueva Task ya tiene Dispatch.

La semántica importante es esta: worker_done no significa que el proceso haya muerto. Completa Task y Dispatch, y deja el worker idle. Mandarle un mensaje al Dispatch completado no crea un segundo intento. Hay que reutilizar explícitamente el terminal con otra Task, retenerlo para depurar o liberarlo.

4. Notificación básica no es mailbox fiable

La versión 1.4.182 de Orca incluye el aviso básico al coordinador de worker_done (#12988). Lo que todavía no incluye es el retry que cubre carreras, reinicios y waiters obsoletos (#14332), fusionado después de la release 1.4.182.

La consecuencia no es construir otro buzón. Es tratar esa versión como degradada: el perfil puede declarar tres workers, pero el límite efectivo es uno. El paralelismo solo se fuerza de forma explícita por Run, y queda visible en el receipt.

Tampoco basta con decir «cualquier versión posterior estará arreglada». La pipeline lleva una tabla exacta de compatibilidad. La primera release que incluya #14332 se añade después de verificarla y superar un smoke real: dos workers terminan casi a la vez, el coordinador no mira mientras trabajan, se comprueban Delivery, replay, ack y cleanup.

Un terminal coordinador mantiene un Run supervisado. Si quieres dos Runs simultáneos, necesitas dos coordinadores. Eso evita waiters que compiten por el mismo buzón.

5. La carrera de lanzamiento sigue siendo un workaround

El atajo de crear worktree, arrancar agente e inyectar la tarea en una sola llamada puede despachar antes de que un agente nuevo esté listo. La carrera sigue documentada en la issue #13488.

Lanzamiento dividido. Crear worktree, ejecutar setup, escribir permisos antes de arrancar, lanzar agente, esperar tui-idle, despachar al terminal existente y confirmar un transcript real con worker-read.
El workaround termina en una señal pública y estructurada: `worker-read`. No intenta reconocer palabras partidas en la TUI.

Mientras no exista una release verificada, la secuencia es deliberadamente aburrida:

  1. Crear el worktree sin agente.
  2. Ejecutar setup.
  3. Escribir la política de OpenCode.
  4. Lanzar el agente.
  5. Esperar tui-idle y despachar sobre ese terminal existente.
  6. Confirmar un transcript real con worker-read.

Leer la pantalla con grep, medir timestamps de silencio o enviar sondas al buzón confundiría estados legítimos con fallos. La pipeline solo reintenta por algo observable: dispatch no iniciado, outcome fallido, error de proveedor o gate fallido. El silencio, por sí solo, no es un error.

6. OpenCode y Codex no ofrecen la misma garantía

OpenCode puede prevenir escrituras por ruta. Su política se compone como base + stack + overlay y se valida antes de arrancar. Una sustitución puede estrechar el mapa de edición, pero no borrar prohibiciones no negociables.

Codex tiene otra granularidad. Arranca con workspace-write, red desactivada y approvals desactivados, siguiendo su configuración oficial. Eso limita el proceso al workspace, pero no equivale a permitir solo dos carpetas dentro del repo. La garantía adicional es detectiva: antes de aceptar el resultado, la pipeline compara todo el diff —commits, staged, unstaged y untracked— con las raíces del perfil.

No vender ambas cosas como equivalentes importa. Una barrera preventiva y un gate detectivo pueden producir el mismo rechazo final, pero tienen distinta exposición.

7. Recuperación sin autocuración imaginaria

«Autocurarse por silencio» parece seductor: si un worker lleva 210 segundos sin salida, leer la pantalla, clasificar y relanzar. En la práctica, un turno largo, un TUI que parte palabras y un coordinador ocupado producen falsos positivos.

La política de recuperación empieza por estados que Orca sí conoce:

  • un Dispatch no arrancó;
  • el outcome es failed o el proveedor devolvió error;
  • un gate independiente falló;
  • una Task quedó bloqueada por una escalation.

Tras fallos repetidos, el orden es: partir la Task, relanzar limpio en el mismo worktree y solo entonces escalar el modelo. Una sesión rota no obliga a tirar el código. Y un modelo más caro no arregla una tarea mal partida.

El cierre tampoco improvisa. Primero worker-release, que archiva el output y libera el terminal exacto; luego worktree rm. Si hay cambios sin guardar, se niega. No silencia errores ni baja a Git por detrás de Orca.

8. QA: estructura, píxeles y evidencia

Un ejecutor sin visión puede recorrer el árbol de accesibilidad, comprobar texto y estructura y producir capturas. Lo que no puede hacer es dictaminar peso visual, contraste o fidelidad. Ese veredicto pertenece a quien tenga ojos: el coordinador o el humano.

La separación funciona si el reporte enumera también lo que no pudo juzgar. En una tanda, un worker comprobó correctamente que una cuadrícula tenía cinco celdas y escaló que no podía saber si las pistas vacías parecían pintadas. La revisión visual encontró justo ese defecto.

Y la evidencia también se verifica. Una captura existe, pesa cientos de kilobytes y aun así puede estar corrupta. Después de ver PNG cuya firma empezaba por efbfbd50 en vez de 89504e47, la firma pasó a ser un gate.

9. Qué costó de verdad

Datos observados entre el 8 y el 11 de agosto de 2026, no una tabla de precios del proveedor:

MétricaObservado
Épicas cerradas6
Tasks procesadas~40
Tests~1.100 → ~1.950, verdes
Coste estimado mostrado por el ejecutor$0,01–$0,13 por Task
Suscripción que pagaba entonces$10/mes; las llamadas aparecían como $0,00
Presupuesto de tiempo por Task45 minutos

El coste de tokens nunca fue la parte cara. Lo caro fue una spec ambigua, un gate que no cubría la superficie, un Delivery mal confirmado o veinte minutos mirando el indicador equivocado.

La pipeline se mantiene pequeña porque solo conserva lo que Orca no puede conocer: qué puede tocar una Task y qué evidencia la hace aceptable. Runs, mensajes, Tasks y Dispatches siguen perteneciendo a Orca. La meta no es que la pipeline «se arregle sola», sino que cada fallo tenga un dueño, un estado duradero y una recuperación que no invente otra fuente de verdad.