Files
fn_registry/dev/flows/0012-fleet-orchestrator-dod.md
T
agent b569561115 docs(flows): 0012 meta-orquestador de flota con DoD-contrato
Diseño del sistema para manejar 20-30 agentes Claude hablando solo con
uno: 4 roles (orquestador/splitter/ejecutores/verificador), DoD-contrato
fijo en goal.json como criterio estable de terminación, máquina de
terminación clasificable sin LLM, y 3 fases (watcher edge-triggered,
orquestador reactivo + verificador independiente, spawn en flota +
splitter). Métrica de salud = throughput de DoD cumplidos.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 19:38:35 +02:00

11 KiB

name, id, status, created, updated, priority, risk, related_issues, apps, trigger, schedule, expected_runtime_s, tags, dod_evidence_schema
name id status created updated priority risk related_issues apps trigger schedule expected_runtime_s tags dod_evidence_schema
fleet-orchestrator-dod 0012 pending 2026-06-20 2026-06-20 high medium
fleetview
fleet_watcher
dag_engine
manual 0
orchestration
fleet
dod
multi-agent
watcher
id kind expected required
watcher_events cmd sqlite3 apps/fleet_watcher/operations.db 'SELECT count(*) FROM fleet_events WHERE created_at > date(now,-1 day)' > 0; cada fila es una TRANSICION de estado, no un nivel repetido true
id kind expected required
dod_contract_on_spawn cmd todo agente lanzado por el orquestador tiene dod_contract no vacio en su goal.json; spawn sin dod_contract se rechaza true
id kind expected required
verifier_verdict log al cerrar un agente, existe un veredicto del verificador (met|failed) con evidencia citada; el verificador NO es el mismo agente que ejecuto la tarea true
id kind expected required
human_load_reduction url con N>=10 agentes vivos, el orquestador presenta UN resumen agrupado por prioridad (no N mensajes sueltos); el humano responde solo lo que requiere decision true
id kind expected required
push_on_reclama screenshot un agente que pasa a waiting/preguntando/bloqueado dispara PushNotification al movil en < 1 min true
id kind expected required
stall_nudge log un agente idle con dod_contract incompleto y sin actividad N min recibe un nudge automatico (send-keys) registrado en fleet_events; jamas se nudgea a un agente en waiting false

Goal

Un meta-orquestador que permita a una persona manejar una flota de 20-30 agentes Claude hablando solo con uno. El orquestador no entra nunca en los detalles de cada agente: vigila estados, persigue que cada agente termine lo que empieza (cumpla un DoD-contrato fijo), y solo escala a la persona lo que requiere su decisión. La métrica de salud es el throughput de DoD cumplidos, no el número de agentes vivos.

Problema que resuelve

Hoy lanzar muchos Claudes produce saturación: N ventanas dispersas, N agentes que quedan idle sin cerrar nada, y la persona como cuello de botella revisando todo. El criterio de "terminado" tampoco existe de forma estable: el campo dod del goal.json lo reescribe el hook GOAL-TRACKER con cada prompt (es un resumen móvil), así que no hay un blanco fijo contra el que evaluar la terminación. Resultado: 30 agentes vivos que no resuelven nada.

Arquitectura: 4 roles

El orquestador delega, nunca ejecuta (regla orquestador-delega-no-ejecuta). Reparto:

orquestador  (la persona habla SOLO con el; solo vigila, agrupa y escala)
  ├── splitter      agente EFIMERO. Tarea grande -> la parte en sub-tareas
  │                 atomicas (paralelas o secuenciales), cada una con su
  │                 dod_contract pequeno y verificable. Tope de fan-out.
  ├── ejecutores    Claudes INTERACTIVOS en la flota tmux (los 20-30 que la
  │                 persona ve). Cada uno con UNA tarea y UN dod_contract.
  └── verificador   agente EFIMERO e INDEPENDIENTE del ejecutor. Al cierre:
                    compara lo hecho contra el dod_contract -> met | failed
                    con evidencia citada. Cero auto-aprobacion.

Distinción dura: splitter y verificador son agentes efímeros (subagentes vía Agent tool / SDK: corren, devuelven un resultado estructurado y mueren). NO ocupan slot en la flota visible. La flota que la persona maneja = solo ejecutores con tarea. La maquinaria de verificación y descomposición es invisible para ella.

El reparto de coste: el watcher vigila (barato, sin LLM, siempre activo); el orquestador y los agentes efímeros piensan (caro, solo cuando hay algo que decidir).

Modelo de datos: DoD-contrato fijo

En el goal.json de cada agente conviven dos campos distintos:

  • dod (ya existe) — resumen móvil que el hook GOAL-TRACKER reescribe con cada prompt. Se queda como está.
  • dod_contract (NUEVO, FIJO) — criterio de aceptación con evidencia ejecutable, escrito UNA vez al lanzar el agente y nunca reescrito por hooks. Es el blanco estable contra el que se evalúa "terminado".
  • dod_status (NUEVO) — pending | met | failed, lo actualiza el verificador.

El hook GOAL-TRACKER debe respetar dod_contract/dod_status (solo reescribe dod). Spawn sin dod_contract se rechaza: ningún agente arranca sin saber cuándo habrá terminado.

Máquina de terminación (lo que el watcher clasifica, mecánico, sin LLM)

Estado del agente Clasificación Acción
waiting / phase preguntando/bloqueado RECLAMA escalar a la persona (push inmediato)
idle + phase hecho DICE-TERMINADO orquestador lanza verificador contra dod_contract
idle + phase≠hecho + sin actividad N min ESTANCADO nudge automático: "cierra tu DoD"
busy + phase haciendo/testeando TRABAJANDO no molestar
sin dod_contract MAL LANZADO bloquear / re-lanzar con DoD

"Dar por terminado al hablar con ellos": cuando la persona se enruta a un ejecutor, lo primero es cerrar su dod_contract — si el verificador dice met, se cierra/reasigna; si quedó a medias, se empuja a terminar antes de abrir nada nuevo.

Fases de construcción

Fase 1 — fleet_watcher (cerebro barato, sin LLM)

Service liviano (Go o bash) que corre como step de dag_engine o systemd-user. Cada N segundos:

  1. Snapshot del fleet (fleetview list --json, extendido con dod_contract/dod_status/phase).
  2. Diff contra el snapshot anterior -> transiciones (edge-triggered, no nivel).
  3. Clasifica cada transición según la máquina de terminación.
  4. Escribe un evento por transición en apps/fleet_watcher/operations.db (tabla fleet_events).
  5. Push inmediato (PushNotification) para clasificación RECLAMA.

DoD Fase 1:

  • Golden: un agente pasa busy->idle -> aparece 1 evento DICE-TERMINADO o ESTANCADO en fleet_events.
  • Edge 1: el mismo agente sigue idle 10 ticks -> NO se duplica el evento (edge, no nivel).
  • Edge 2: un agente pasa a waiting -> evento RECLAMA + push en < 1 min.
  • Error 1: goal.json corrupto/ausente -> el agente se clasifica MAL LANZADO sin crash del watcher.
  • Vida: 7 días corriendo, 0 crashes (journalctl/log), cola sin huecos.

Fase 2 — orquestador-Claude reactivo + verificador + splitter

Extiende el skill /orquestador. NO hace polling. Despierta por: la persona | heartbeat largo (ScheduleWakeup 20-30 min) | push del watcher. Al despertar:

  1. Vacía fleet_events, agrupa por prioridad (RECLAMA > DICE-TERMINADO > ESTANCADO) y por ámbito.
  2. Para DICE-TERMINADO: lanza un verificador (Agent efímero) que compara el output del ejecutor con su dod_contract -> met/failed+evidencia. met -> autocierra y reporta; failed -> nudge al ejecutor con el gap o escala.
  3. Para ESTANCADO: nudge (send-keys) bajo política (solo idle con DoD pendiente; jamás waiting).
  4. Para RECLAMA: presenta a la persona UN resumen corto con la decisión concreta que se necesita. Usa /fleet focus para saltarla al agente elegido.

DoD Fase 2:

  • Golden: un agente DICE-TERMINADO con DoD realmente cumplido -> verificador met -> autocierre + reporte, sin intervención humana.
  • Edge 1: agente DICE-TERMINADO con DoD a medias -> verificador failed -> nudge con el gap, no se cierra.
  • Edge 2: 10 agentes con eventos a la vez -> un solo resumen agrupado, no 10 mensajes.
  • Error 1: verificador no puede leer el output -> reporta "no evaluable", escala, no autocierra en falso.
  • Vida: 7 días gestionando flota real; la persona responde solo decisiones, no enrutamiento.

Fase 3 — spawn dentro de la flota + splitter

Extiende /orquestador para lanzar ejecutores con TmuxNewClaudeWindow (socket fleet) en vez de kitties sueltas, escribiendo dod_contract en el goal.json del nuevo agente y un prompt con el DoD claro. Antes de spawnar, si la tarea se estima grande, pasa por el splitter (Agent efímero) que devuelve un plan de sub-tareas con dependencias; el orquestador spawna un ejecutor por sub-tarea (paralelas a la vez, secuenciales encadenadas).

DoD Fase 3:

  • Golden: una tarea atómica -> 1 ejecutor en la flota con dod_contract escrito; /fleet lo lista.
  • Edge 1: una tarea grande -> splitter devuelve >=2 sub-tareas, cada una con su dod_contract; se spawnan respetando deps.
  • Edge 2: tope de fan-out -> el splitter nunca genera más de K sub-agentes de golpe (sin explosión).
  • Error 1: spawn sin dod_contract -> rechazado con mensaje claro.
  • Vida: 7 días lanzando trabajo real por esta vía.

Pre-requisitos

  • Sesión tmux fleet activa (perfil launch_fleetclaude); /fleet operativo (flow previo).
  • PushNotification configurado (Remote Control activo en el móvil).
  • dag_engine activo para schedule del watcher (regla dag-engine-over-cron).

Acceptance

  • dod_contract fijo escrito al spawn y respetado por el hook GOAL-TRACKER.
  • watcher edge-triggered con eventos en fleet_events + push en RECLAMA.
  • verificador independiente del ejecutor, con veredicto+evidencia.
  • splitter con tope de fan-out para tareas grandes.
  • orquestador presenta resumen agrupado, no N mensajes; usa /fleet focus.
  • la persona maneja >=10 agentes respondiendo solo decisiones.

Definition of Done

  • Mecánica: watcher + funciones del registry compilan, fn doctor verde, sin drift uses_functions.
  • Cobertura: cada fase con su golden + >=2 edge + >=1 error path, evidencia ejecutable (ver DoD por fase).
  • Vida útil: >=7 días de uso real gestionando flota, 0 crashes del watcher, 0 "done" falsos detectados (verificador funciona).
  • Carga humana: medible reducción — la persona responde decisiones, no enrutamiento ni vigilancia.
  • Secrets: cero credenciales fuera de pass/vaults; el watcher no loguea contenido de sesiones, solo estados/transiciones.

Notas (onboarding)

Para usarlo: lanzas trabajo por el orquestador (no abres Claudes a mano). Cada tarea recibe un dod_contract. El watcher vigila en background y empuja al móvil cuando un agente te reclama. Cuando vuelves, el orquestador te da un resumen agrupado y te lleva (/fleet focus) solo a lo que necesita tu decisión; lo demás (verificar cierres, empujar estancados, dividir tareas grandes) lo hace solo con agentes efímeros. La flota que ves = ejecutores con tarea; la maquinaria de verificación/división es invisible.

Relación con otras reglas: dod_quality (las 3 capas + verificador independiente), orquestador-delega-no-ejecuta (el orquestador no ejecuta), dag-engine-over-cron (schedule del watcher), autonomous_loop (fn-orquestador autónomo es el primo no-interactivo de este flujo), y el flow previo de /fleet/fleetview (la base de datos de estado).