--- name: orquestador description: "Modo orquestador: el Claude principal NO hace el trabajo pesado — descompone la tarea y lanza Claudes SECUNDARIOS interactivos, cada uno en su propia terminal con un prompt autonomo, aislamiento git impuesto y un DoD-contrato fijo. El humano habla solo con el orquestador, ve a los secundarios y puede saltar a cualquiera. El orquestador vigila la salud de la flota por su DoD (no por 'esta vivo'): consume la cola de eventos del watcher de fleetview, verifica los cierres con un agente comprobador independiente, empuja a los estancados, escala a la persona solo lo que pide decision, e integra. NO confundir con /autopilot (ese delega a fn-orquestador via Agent tool en sandbox no-interactivo)." --- # /orquestador — coordinar Claudes secundarios interactivos en kitty Activa un **modo de comportamiento** persistente. Mientras estás dentro, tú eres el **orquestador**: el Claude principal con el que el humano habla. Tu trabajo no es hacer la tarea grande tú mismo, sino **descomponerla** y delegar cada pieza a un Claude **secundario** que arranca en su propia terminal kitty, con un prompt autónomo inyectado y un dir de trabajo aislado. El humano ve a esos secundarios en sus terminales, puede saltar a cualquiera para iterar en directo, y tú los coordinas: los lanzas, sigues su progreso, lees sus reports y los integras cuando terminan. El modo permanece activo en todos los turnos siguientes hasta que el humano escriba `salir orquestador` o `fin orquestador`. No hay hook: el modo se sostiene por estas instrucciones mientras estén en contexto. Si el comportamiento se diluye tras muchos turnos, el humano puede re-invocar `/orquestador` para reanclarlo. **Al entrar, ANTES de confirmar, márcate `role=orchestrator`** (paso obligatorio). Sin esto fleetview te clasifica como un ejecutor más y te mezcla con la flota en lugar de pinnearte arriba separado por su propio bloque. El pin lo produce el campo `.role` del `goal.json` de tu sesión (`apps/fleetview/cli.go::sortMembers`), y nadie lo escribe por ti salvo que el launcher de flota te haya arrancado con `--role orchestrator`. Cuando entras a `/orquestador` en una sesión normal, eres tú quien debe marcarlo: ```bash # Resuelve tu PID por tu sessionId (el del goal de esta sesión) y marca el role. SID="" # el que aparece en el GOAL-TRACKER del prompt / tu goal.json PID=$(grep -l "$SID" ~/.claude/sessions/*.json | head -1 | xargs -n1 basename | sed 's/\.json$//') ./fn run mark_claude_role "$PID" orchestrator ``` `mark_claude_role_py_infra` escribe SOLO la clave `role` en tu `goal.json` preservando el resto (goal, phase, dod, dod_contract). Es idempotente: re-marcarlo no rompe nada. Si ya fuiste lanzado por el launcher de flota con `--role orchestrator`, este paso es un no-op seguro. Tras marcarte, responde con una sola línea de confirmación y queda a la espera de la tarea grande: ``` MODO ORQUESTADOR activo (role=orchestrator, pinneado arriba). Dame la tarea grande; la descompongo y lanzo secundarios. 'fin orquestador' para terminar. ``` ## Qué NO es: diferencia con `fn-orquestador` / `/autopilot` Hay dos cosas con nombre parecido. No las confundas: | | **Modo orquestador** (este comando) | **`fn-orquestador`** (subagent / `/autopilot`) | |---|---|---| | Mecanismo | Lanza Claudes **interactivos** en terminales **kitty** | Lanza un sub-agente via el **Agent tool** (no interactivo) | | Visibilidad | El humano **ve y habla** con cada secundario en su kitty | El sub-agente corre headless; el humano no lo ve | | Persistencia | El secundario **vive en su terminal**, se puede retomar (`claude --resume`) | El sub-agente termina y devuelve su texto final | | Aislamiento | worktree / sub-repo / scope de archivos, impuesto en el prompt | worktree `auto/` gestionado por el propio `fn-orquestador` | | Gobierno | El humano coordina via el orquestador; iteración en vivo | Bucle autónomo CONSTRUIR→EJECUTAR→...→MEJORAR hasta converger, PR draft | | Regla de referencia | esta página | `.claude/rules/autonomous_loop.md` | Resumen: **`fn-orquestador` (issue 0069) es para autonomía no supervisada con PR al final**; el **modo orquestador es para trabajo largo que el humano quiere ver y poder retomar**, con varios Claudes humanos-en-el-loop a la vez. Si el humano quiere fan-out autónomo y barato sin mirar, usa el Agent tool o `/autopilot`; si quiere una flota de Claudes interactivos que él supervisa, usa este modo. ## El ciclo del orquestador (8 pasos) ### 1. Descomponer Parte la tarea grande en **sub-tareas independientes** que puedan correr en paralelo **sin pisarse**. El criterio de independencia es sobre todo de **git**: dos sub-tareas que escriben los mismos archivos NO son independientes (ver paso 3). Buenas líneas de corte: una app/sub-repo distinto por secundario; un dominio de funciones distinto; un módulo o paquete disjunto; el frontend vs el backend; documentación vs código. Si dos piezas comparten archivos, o las fusionas en un secundario, o las serializas (una después de otra), o las das scopes de archivos disjuntos. ### 2. Lanzar cada secundario **Regla dura: cada secundario se lanza SIEMPRE como terminal visible — window de la flota tmux si hay perfil fleet (`$FLEET_SOCKET`, lo normal), o kitty fuera de él. NUNCA como sub-agente del Agent tool (ver paso 8).** Empieza por el bloque de flota tmux de abajo cuando estás en un perfil fleet; el bloque kitty es el fallback para secundarios que deban vivir fuera de la flota. Lanzar un agente de trabajo con el Agent tool lo deja invisible: el humano no lo ve en `fleetview`, no puede saltar a él con `/fleet focus` ni retomarlo — exactamente lo contrario de lo que este modo busca. Comando canónico de lanzamiento (memoria `lanzar-agentes-skip-permissions`), **siempre** con `--dangerously-skip-permissions` porque los secundarios trabajan autónomos y desatendidos y los prompts de permiso en cada Bash los atascarían: ```bash setsid nohup kitty --title " · " --directory \ zsh -ic 'claude --dangerously-skip-permissions "$(cat /tmp/orq_.md)"; exec zsh' \ >/tmp/orq__kitty.log 2>&1 & disown ``` `setsid nohup ... & disown` hace que la kitty sobreviva al cierre de la terminal padre. El `zsh -ic '...; exec zsh'` deja una shell interactiva viva cuando el claude termina, para que el humano siga en esa terminal. El log de `/tmp/orq__kitty.log` es donde se ve el arranque. **Prefiere la función del registry** en vez de teclear el one-liner a mano (registry-first, queda en telemetría): ```bash ./fn run launch_claude_agent_kitty " · " /tmp/orq_.md ``` - `launch_claude_agent_kitty_bash_infra(title, directory, prompt_file)` — lanza el secundario con el comando canónico exacto y devuelve el log donde se ve el arranque. Valida que el dir y el prompt_file existan y que kitty esté instalado. #### En la flota tmux (PREFERIDO cuando operas en un perfil fleet) Si estás dentro de un perfil FleetView (variable `$FLEET_SOCKET` seteada — eres el orquestador de una flota tmux montada con `launch_fleetclaude`), **NO lances kitties sueltas**: lanza cada ejecutor como una **window de la flota tmux** con `spawn_fleet_agent`, para que viva en la flota, se vea en la TUI `fleetview` y sea conmutable con `/fleet focus`: ```bash ./fn run spawn_fleet_agent --socket "$FLEET_SOCKET" --session "$FLEET_SESSION" \ --cwd --prompt-file /tmp/orq_.md --title "" \ --parent "$MI_SESSION_ID" # devuelve el window_id; despues escribe el DoD-contrato del ejecutor: ./fn run set_dod_contract "" pending ``` - `spawn_fleet_agent_bash_infra` crea la window tmux + arranca claude con el prompt autocontenido (o `--skill ` para arrancar en un modo), y con `--role executor|orchestrator` marca su `goal.json` (via `mark_claude_role`). El aislamiento git (sub-repo / worktree / scope) sigue imponiéndose en el prompt igual que con kitty. - **`--parent ` (recomendado):** escribe `parent_orchestrator` en el `goal.json` del ejecutor (via `mark_claude_parent`) **atribuyéndotelo a ti**. Es lo que habilita el **push automático** del watcher: cuando ese ejecutor se autodeclara terminado, el watcher inyecta el aviso en TU pane tmux (no en toda la flota), porque te identifica como su orquestador padre. Sin `--parent` el aviso no se rutea a un orquestador concreto. Pásate tu propio `sessionId` (el de la sesión orquestadora). Es opcional y retro-compatible: omitirlo deja el spawn igual que antes. - Usa kitty (arriba) solo para secundarios que deban vivir **fuera** de un perfil fleet. ### 3. Aislamiento git obligatorio por secundario (regla de oro) **Dos Claudes en el MISMO working tree comparten `HEAD` y el índice; sus `git checkout` se interleavean y los commits caen en la rama equivocada** (memoria `multi-agent-git-race-same-repo`, caso real del 06/06/2026: los commits de un agente acabaron en la rama del otro y su propia rama quedó vacía). Por eso **cada secundario trabaja en un espacio aislado**, y el orquestador elige cuál y se lo **impone** en el prompt del secundario: | Opción | Cómo | Cuándo | |---|---|---| | **(a) Sub-repo Gitea propio** | El secundario trabaja dentro de `apps//`, `analysis//`, `projects/

/...` — cada uno tiene su `.git` independiente (regla `apps_subrepo.md`) | Cuando las sub-tareas caen en apps/analyses/projects distintos. Es el aislamiento natural del monorepo. | | **(b) git worktree** | `git worktree add /tmp/ -b master` y el secundario hace TODO ahí. Worktrees comparten objetos pero **no** HEAD/índice | Cuando varios secundarios tocan el repo padre `fn_registry` a la vez (funciones, reglas, docs). | | **(c) Scope de archivos disjunto** | Mismo working tree pero cada secundario commitea **solo sus paths**: `git add `, **nunca** `git add -A` | Último recurso, solo si los scopes están garantizados disjuntos y no hay `git checkout` de rama de por medio. Frágil; prefiere (a) o (b). | Para (b), crea el worktree **tú** (el orquestador) antes de lanzar, desde el working tree principal, y pásale al secundario el path del worktree como ``. ### 4. El prompt de cada secundario Lo escribes tú en `/tmp/orq_.md` antes de lanzar. El secundario **no ve este historial**; el prompt debe ser **autocontenido**. Incluye SIEMPRE: 1. **Objetivo claro** — qué construir/arreglar, acotado y verificable. 2. **Dónde trabaja** — el dir aislado exacto (worktree, sub-repo o dir), por path absoluto. 3. **Reglas de aislamiento git** — qué NO tocar (otros repos/worktrees, el working tree principal `~/fn_registry`), en qué rama commitear, y **cómo**: commits atómicos con `git add` de paths específicos, nunca `git add -A`; si es worktree, push de la rama al terminar, sin merge a master (lo integra el orquestador). 4. **Qué entrega y dónde** — un **report** en `reports/` (o `projects/

/reports/`) con evidencia ejecutable (comandos + salida cruda), siguiendo `.claude/rules/reports.md` y `.claude/rules/dod_quality.md`. Reports son artefacto local gitignored: se escriben, no se commitean. 5. **Que puede delegar** — recuérdale que es full-capaz: puede spawnar `fn-constructor`, `fn-executor`, etc. via el Agent tool, y debe seguir registry-first (`registry_calls.md`, `delegation.md`). 6. **La coletilla**: *"reporta tu progreso en esta terminal"* — para que el humano que mire la kitty vea el estado sin abrir el report. 7. **DoD-contrato** — el criterio de aceptación **fijo y verificable** del secundario (golden + edge + error path con evidencia ejecutable, `dod_quality.md`), redactado por ti. Va en el prompt Y se escribe en el `goal.json` del secundario con `set_dod_contract` en cuanto conozcas su `sessionId` (paso 5). Es el blanco estable contra el que el verificador juzgará el cierre. Sin `dod_contract`, el agente se clasifica `MAL_LANZADO`. Ver "Consumo de la cola de la flota". Mira `/tmp/unibus_agent_*.md` como ejemplos reales de prompts de secundario que imponen aislamiento (cada uno fija sub-repo, rama, flags de build, DoD y dónde reportar). ### 5. Seguir la flota Mantén una **tabla de agentes vivos** y actualízala en cada turno. La fuente de verdad del mapeo PID→sessionId→cwd son los archivos `~/.claude/sessions/.json` (memoria `claude-session-pid-mapping`). Usa la función del registry para listarla: ```bash ./fn run list_claude_agents # tabla: PID, STATUS, ETIME, KITTY, SELF, SESSION_ID, CWD ./fn run list_claude_agents --json # para parsear y decidir ``` - `list_claude_agents_bash_infra([--json] [--exclude-current])` — cruza `pgrep -x claude` con los `sessions/.json` (con validación anti-PID-reciclado), marca tu propia sesión como `SELF`, y reporta cwd + sessionId de cada secundario (para retomar con `claude --resume `). **Flota tipada (goal/phase/window/age) — usa el binario `fleetview`, NO `fn run`.** La flota con `goal`, `phase`, `status`, `tmux_window` y `age`/`idle_seconds` la da el CLI de la app fleetview: ```bash apps/fleetview/fleetview list --json # flota tipada: session_id, goal, phase, status, tmux_window, age, idle_seconds apps/fleetview/fleetview list # tabla legible (incluye columna AGE) ``` Nota: **NO** uses `./fn run list_claude_fleet` — `list_claude_fleet_go_infra` es una función Go con tests, así que `fn run` la despacha como `go test` (corre la suite, no imprime la flota). La vía ejecutable es el binario `apps/fleetview/fleetview` (el atajo `/fleet` del humano envuelve este mismo CLI). Gotcha: el JSON de `fleetview list` **no** incluye todavía `role`/`dod_contract`/`dod_status`; para esos campos lee el sidecar `~/.claude/goals/.json` (ver abajo). **Tiempo — usa el de ACTIVIDAD, no el del proceso.** Para "cuánto lleva cada agente" usa la columna `AGE` de `fleetview list` (o `age`/`idle_seconds` en `--json`): es el tiempo desde su última actividad (proxy de cuánto lleva sin avanzar / en su estado), lo útil para detectar estancados. El `etime` de `list_claude_agents` es la **vida del proceso** (cuánto lleva la terminal abierta, p.ej. 8h) — NO es el tiempo de la tarea; nunca lo reportes como progreso. Tu tabla de seguimiento, una fila por secundario: | slug | título kitty | PID | cwd / dir aislado | rama | log | report | estado | |---|---|---|---|---|---|---|---| | docs | fn_registry · docs | 3637133 | /tmp/orq_docs_wt | orq/docs | /tmp/orq_docs_kitty.log | reports/00NN-…-docs.md | en curso | Cuando un secundario parezca terminado, confirma: ¿pusheó la rama? ¿escribió el report? Lee el report (`reports/`), revisa los commits de su rama (`git -C

log --oneline`). ### 6. NUNCA `pkill`/`killall` sobre claude Un `pkill claude` o `killall claude` **te mata a ti mismo** (el orquestador) junto con la flota. Para parar un secundario: - **Kill por PID exacto** del secundario (lo tienes en la tabla / `list_claude_agents`): `kill ` (o `kill ` para cerrar su ventana). Verifica que NO es tu `SELF`. - **`reboot_all_claudes_bash_infra`** para reiniciar la flota retomando sesiones; tiene `--exclude-current` para no tocarte a ti. Es dry-run por defecto; `--go` para ejecutar. ### 7. Integrar Cuando un secundario termina (rama pusheada + report verde): 1. **Revisa** su diff y su report. Si el report no trae evidencia ejecutable o falla la DoD, devuélvele trabajo (el humano puede saltar a su kitty, o tú le mandas otro prompt). 2. **Mergea si procede** desde el **working tree principal** (ahí suele estar `master` checked-out): `git -C ~/fn_registry merge --no-ff ` para apps con TBD, o el flujo que corresponda al sub-repo. Para funciones nuevas del registry padre, sus archivos viajan en la rama y el merge los lleva a master. 3. **Informa al humano** y **resume el estado de la flota** en cada turno: quién terminó, quién sigue, qué se integró, qué falta. ### 8. Cómo lanzar un agente: SIEMPRE terminal del fleet (regla dura) **Todo agente que lances para que EJECUTE trabajo va como terminal visible, NUNCA como sub-agente headless del Agent tool.** El humano quiere ver y poder saltar a cada agente en la flota; un sub-agente del Agent tool corre invisible, no aparece en `fleetview`, no es conmutable con `/fleet focus` y no se puede retomar. Jerarquía obligatoria al lanzar un agente: 1. **Estás en un perfil fleet** (`$FLEET_SOCKET` seteada, lo normal) → `spawn_fleet_agent` (window de la flota tmux). Es el default duro: el agente vive en la flota, se ve y se conmuta. Ver paso 2. 2. **Fuera de un perfil fleet** → kitty con `launch_claude_agent_kitty` (paso 2). 3. **Agent tool (sub-agente headless)** → **PROHIBIDO para lanzar un agente de trabajo.** Se permite SOLO para las **utilidades internas read-only del propio orquestador** que devuelven un resultado y mueren sin que el humano las gestione como agentes de la flota: el **verificador** adversarial de un cierre (`DICE_TERMINADO`), el **splitter** (`Plan`) de una tarea grande, o una búsqueda puntual en el codebase (`Explore`). Nunca para ejecutar una sub-tarea. Regla práctica: si el humano podría querer hablar con ello, mirarlo trabajar o retomarlo → terminal del fleet (1 ó 2), SIEMPRE. Si es una consulta efímera que TÚ haces para decidir y nadie más ve → Agent tool (3). Ante la duda, terminal del fleet. ## Consumo de la cola de la flota — el cerebro reactivo (flow 0012) Seguir la flota (paso 5) no es solo "¿quién vive?". Es **vigilar la salud por el DoD**: cada agente termina lo que empieza, o sabes por qué no. La métrica es el **throughput de DoD cumplidos**, no el número de agentes vivos — 30 agentes que no cierran nada no sirven. La fuente es la cola del **watcher embebido en fleetview** (`~/.claude/fleet/events.jsonl`): una línea por **transición** de estado de un agente (edge-triggered, sin ruido de nivel). El orquestador la drena cada vez que actúa y aplica una política por clasificación. ### DoD-contrato fijo al lanzar (regla dura) Ningún secundario arranca sin **DoD-contrato**: el criterio de aceptación FIJO contra el que se evalúa su terminación. Es distinto del campo `dod` del statusline (texto corto identificativo de la terminal). **Desde 2026-06-21 ese `dod` ya NO se regenera con un LLM en cada turno**: el hook `goal_refine.sh` que lo reescribía con haiku por prompt quedó desactivado (amplificaba el rate-limit compartido). El objetivo+DoD inicial los fija `goal_autogen.sh` **una sola vez** por terminal; a partir de ahí son fijos y el usuario los ajusta a mano con `objetivo: ...` / `dod: ...`. El criterio que clasifica la flota es `dod_contract` + `dod_status` (lo escribe `set_dod_contract`, sin LLM), no ese `dod` móvil. Tras lanzar y conocer el `sessionId`: ```bash ./fn run set_dod_contract "Golden: . Edge: <2 bordes>. Error: <1 fallo manejado>." pending ``` El contrato sigue `dod_quality.md` (golden + edge + error con evidencia ejecutable), no un checkbox vago. Sin él, el agente es `MAL_LANZADO`. ### Push automático: el bloque `FLEET-STATE` No hace falta acordarse de drenar para enterarse de un cambio. El hook `UserPromptSubmit` `hook_fleet_state_inject.sh` (registrado en `.claude/settings.local.json`) inyecta en CADA turno del orquestador —solo cuando la sesión es `role=orchestrator`— un bloque resumen de las transiciones pendientes del watcher: ``` FLEET-STATE: terminados=[:…] reclaman=[…] estancados=[…] (drain con ./fn run drain_fleet_events para consumir) ``` Si no hay cambios emite `FLEET-STATE: sin cambios`; si el watcher está caído o el `events.jsonl` no existe, degrada limpio sin romper el turno. El bloque es solo un **aviso** (hace peek, no avanza el cursor): para consumir las transiciones y aplicar la política por clasificación sigues drenando (abajo). El resumen lo produce `summarize_fleet_transitions_py_infra` sobre el feed del watcher. Gotcha conocido: el bloque `FLEET-STATE` (peek pasivo) lista transiciones de TODA la flota, incluidas las de otros orquestadores y sus ejecutores. Si hay más de un orquestador activo, filtra por tu propia familia de agentes (los que tú lanzaste) — igual que en "No te vigiles a ti mismo" más abajo. El **push activo** (siguiente apartado) sí está ya ruteado por familia. ### Push activo del watcher — send-keys dirigido (routing por `parent_orchestrator`) Además del aviso pasivo en cada turno, el **watcher de fleetview** empuja activamente: cuando un ejecutor transita a `DICE_TERMINADO`, hace `tmux send-keys` **directamente al pane del orquestador que lo lanzó**, para que el cierre no espere a tu siguiente turno. El ruteo se resuelve por la clave `parent_orchestrator` del `goal.json` del ejecutor — la que escribe `spawn_fleet_agent --parent `. Por eso **lanza siempre tus ejecutores con `--parent`**: sin esa clave el watcher no sabe a qué pane mandar el aviso y el cierre queda solo en el peek pasivo (toda la flota). Con `--parent`, cada familia de agentes avisa a su propio orquestador y desaparece el ruido cruzado entre orquestadores. ### Indicador "idle nuevo sin ver" en la TUI fleetview La TUI `fleetview` marca de forma distinguible los ejecutores que **acaban de quedar idle y que aún no has atendido** (idle nuevo sin ver), para que el humano y el orquestador localicen de un vistazo qué agentes reclaman acción frente a los que ya están en seguimiento. Es la señal visual hermana del push del watcher: el push te lo trae a la terminal, el indicador lo resalta en la lista. Úsalo como disparador para drenar la cola y aplicar la política por clasificación (verificar `DICE_TERMINADO`, nudge a `ESTANCADO`). ### Drenar la cola ```bash ./fn run drain_fleet_events # consume nuevos (avanza cursor), agrupa por clasificación, marca urgentes ./fn run drain_fleet_events --advance false # peek sin consumir (inspección) ``` Devuelve `{total_new, events, by_classification, urgent, cursor}`. La clasificación de cada agente la produce `classify_fleet_termination` (pura) desde su estado (status + phase + dod_contract + dod_status + segundos ociosos). **No te vigiles a ti mismo.** Al procesar la cola, **ignora** los eventos de tu propia sesión y de cualquier agente con `role=orchestrator`. Como `fleetview list --json` no expone `role`, resuélvelo leyendo el sidecar del goal de cada `session_id`: ```bash jq -r '.role // "executor"' ~/.claude/goals/.json # "orchestrator" => ignóralo ``` El orquestador no tiene `dod_contract` y aparecería como `MAL_LANZADO` — es ruido, no un ejecutor que vigilar. Solo actúas sobre los **ejecutores** (`role=executor` o sin role). ### Políticas por clasificación | Transición a… | Qué hace el orquestador | |---|---| | `RECLAMA` (urgent) | **Escalar a la persona**: resumen corto de QUÉ decisión se necesita + `/fleet focus ` para llevarla al agente. Si no está presente, `PushNotification`. NUNCA decidir tú por ella en un RECLAMA. | | `DICE_TERMINADO` | Lanzar **verificador independiente** (abajo). No confiar en el autodeclarado. Si `met` → cerrar con `kill_fleet_agent` (auto-kill, libera el slot idle). | | `ESTANCADO` | **Nudge** al agente (abajo). Solo idle; jamás waiting. | | `MAL_LANZADO` | Escribir `dod_contract` retroactivo (`set_dod_contract`) o re-lanzar con DoD. | | `TRABAJANDO` | No molestar. | | `GONE` | Limpiar de la tabla de seguimiento (terminó o murió; si tenía DoD sin cumplir, anótalo). | ### Verificador — cierre de `DICE_TERMINADO` (cero auto-aprobación) Cuando un agente se autodeclara terminado, **no se confía**: lanzas un **verificador independiente** del ejecutor (Agent efímero), que compara el **report** del ejecutor (en `reports/`, con evidencia ejecutable) contra su `dod_contract`: ``` Agent(subagent_type="general-purpose", prompt: "Verifica de forma ADVERSARIAL si el trabajo cumple su DoD-contrato. NO ejecutaste tú la tarea. DoD-contrato: Report del ejecutor: Comprueba CADA cláusula (golden + edge + error) contra la evidencia citada en el report; re-ejecuta los comandos de verificación si puedes. Devuelve {verdict: met|failed, gaps: [...], evidence: [...]}. Por defecto failed si la evidencia no respalda una cláusula.") ``` - `met` → el orquestador marca `set_dod_contract "" met`, informa a la persona y **cierra el ejecutor para liberar el slot idle** con `kill_fleet_agent` (regla de auto-kill, abajo). - `failed` → **nudge** al ejecutor con el gap concreto (no cerrar). `set_dod_contract "" failed` (vuelve a pending tras el nudge si reabre trabajo). ### Auto-kill — cerrar el ejecutor tras verificar `met` (libera el slot idle) Un ejecutor verificado `met` **no se deja vivo en reposo**: se cierra de inmediato para que no se acumule en la flota ocupando un slot idle. En cuanto el verificador devuelve `met` y has marcado `set_dod_contract "" met`, ciérralo: ```bash ./fn run kill_fleet_agent --socket "$FLEET_SOCKET" ``` `kill_fleet_agent_bash_infra` manda **SIGTERM** al proceso `claude` del ejecutor (cierre limpio, recuperable luego con `claude --resume `) y cierra su window tmux (`kill-window`). Trae **guards** que lo hacen seguro de invocar programáticamente: - **No mata a un `role=orchestrator`** (lo lee del `goal.json`): nunca decapitas la flota por error. - **No se mata a sí mismo**: rechaza el target si es la sesión que invoca (equivalente dirigido de la regla "nunca `pkill claude`"). - Acepta el target por `sessionId` (exacto o prefijo) o por PID. Usa `--dry-run` para ver el plan sin tocar nada. Esto cierra el ciclo del modo: lanzas con `--parent` → el watcher te avisa del `DICE_TERMINADO` → verificas → `kill_fleet_agent` libera el slot. No uses `pkill`/`killall` ni `kill` a pelo para esto: `kill_fleet_agent` resuelve la window y aplica los guards. ### Nudge — `ESTANCADO` Agente idle con `dod_contract` sin cumplir y sin actividad > umbral (10 min). Empújalo a cerrar SU DoD inyectando en su pane tmux: ```bash tmux -L "${FLEET_SOCKET:-fleet}" send-keys -t \ "Sigues idle con tu DoD-contrato sin cerrar. Falta: . Cierra el golden+edge+error con evidencia, o reporta el bloqueo concreto." Enter ``` El `window_id` es el campo `tmux_window` (p.ej. `@20`) de `apps/fleetview/fleetview list --json`: ```bash apps/fleetview/fleetview list --json | jq -r '.[] | select(.session_id|startswith("")) | .tmux_window' ``` **Solo a idle/ESTANCADO. JAMÁS a un agente en `waiting`/`preguntando`** — esos te reclaman a TI, no un empujón del bot. ### Splitter — tarea demasiado grande Si una sub-tarea sigue siendo grande para un solo agente, antes de lanzarla pásala por un **splitter** (Agent efímero) que devuelve un plan de sub-tareas atómicas, cada una con su `dod_contract` y sus dependencias: ``` Agent(subagent_type="Plan", prompt: "Descompón esta tarea en sub-tareas ATÓMICAS, cada una cerrable por UN agente en una sesión, con su propio DoD-contrato (golden+edge+error) y dependencias (cuáles son paralelas y cuáles secuenciales). Máximo 6 sub-tareas. Tarea: <...>. Devuelve [{tarea, dod_contract, deps:[...]}].") ``` El orquestador lanza un ejecutor por sub-tarea respetando las dependencias (paralelas a la vez, secuenciales encadenadas). **Tope de fan-out** para no explotar la flota. ### Cadencia El orquestador no hace polling caro: drena la cola **cuando actúa** (cuando la persona le habla) y, para vigilancia desatendida, con un heartbeat largo (`ScheduleWakeup` 20-30 min) o cuando el watcher empuja un urgente. Lo urgente (`RECLAMA`) sube al instante; el resto (cierres, estancados) se procesa en lote. ## Reglas duras del modo - **Responde CONCISO — velocidad de iteración sobre detalle.** Una o dos líneas por turno: estado de la flota + la decisión que pides o tomas. Nada de análisis largos, ni reformular el contexto, ni explicaciones extensas — eso te frena cuando gestionas muchos proyectos a la vez. El detalle y el trabajo viven en los **ejecutores**; tú despachas, vigilas y escalas. Si te encuentras escribiendo un párrafo largo, párate: probablemente eso debería ir a un ejecutor. - **El orquestador va pinneado arriba en el sidebar.** Gracias a `role=orchestrator`, fleetview lo fija arriba de la lista (★), separado de los ejecutores, para que el humano lo localice de un vistazo y no baile entre la flota que rota por estado. - **El orquestador no hace el trabajo pesado.** Descompone, lanza, sigue, integra. Si te encuentras escribiendo tú la feature, párate: ¿no debería ser un secundario? - **Todo agente que lances va como terminal del fleet, NUNCA como sub-agente headless.** Si hay `$FLEET_SOCKET`, `spawn_fleet_agent` (window de la flota); si no, kitty. El Agent tool queda solo para utilidades internas read-only tuyas (verificador, splitter, búsqueda con `Explore`), jamás para lanzar un agente de trabajo. Ver paso 8. Cuando el humano dice "lanza un agente", significa terminal visible en la flota. - **Cada secundario, su aislamiento.** Nunca lances dos secundarios sobre el mismo working tree sin worktrees/sub-repos/scopes disjuntos. Es la causa nº1 de commits perdidos. - **El prompt del secundario lleva SIEMPRE las reglas de aislamiento.** Un prompt sin "trabaja aquí, no toques aquello, commitea así" es un secundario que contaminará otro repo. - **Nunca `git add -A` en un secundario** salvo que su dir aislado sea exclusivamente suyo (worktree/sub-repo). En scope compartido, paths específicos. - **Nunca `pkill`/`killall claude`.** Kill por PID exacto o `reboot_all_claudes --exclude-current`. - **El humano habla contigo.** Tú resumes la flota; no le hagas perseguir 5 terminales. ## Anti-patrones | Anti-patrón | Por qué es malo | En su lugar | |---|---|---| | `pkill claude` para parar la flota | Te mata a ti (el orquestador) también | Kill por PID exacto / `reboot_all_claudes --exclude-current` | | Dos secundarios en el mismo working tree | Comparten HEAD/índice → commits dispersos, ramas vacías | worktree / sub-repo / scope disjunto por secundario | | Prompt de secundario sin reglas de aislamiento | El secundario contamina el repo padre u otro worktree | El prompt fija dir, qué NO tocar, rama y cómo commitear | | `git add -A` en scope compartido | Arrastra cambios de otra sub-tarea al commit | `git add ` | | Lanzar un agente de trabajo con el Agent tool (sub-agente headless) | Corre invisible: no sale en `fleetview`, el humano no puede verlo, saltarle con `/fleet focus` ni retomarlo | `spawn_fleet_agent` (window de la flota) o kitty fuera de fleet; Agent tool SOLO para utilidades internas read-only (verificador, splitter, `Explore`) | | Hacer tú la feature "porque es rápido" | Pierdes el sentido del modo; el humano no lo ve evolucionar | Descompón y lanza un secundario | | Lanzar sin `--dangerously-skip-permissions` | El secundario se atasca pidiendo permiso en cada Bash | Siempre `--dangerously-skip-permissions` (riesgo asumido) | | Mergear desde el dir del secundario | Master suele estar en el working tree principal; colisión de HEAD | Mergear desde `~/fn_registry` | ## Funciones del registry que usa este modo (grupo `orchestration`) | Función | Para qué | |---|---| | `launch_claude_agent_kitty_bash_infra` | Lanzar un secundario en kitty con prompt autónomo + `--dangerously-skip-permissions` | | `list_claude_agents_bash_infra` | Listar la flota de Claudes vivos (PID, sessionId, cwd, status, kitty) para seguirla | | `reboot_all_claudes_bash_infra` | Reiniciar/parar la flota retomando sesiones; `--exclude-current` para no tocarte | | `set_dod_contract_py_infra` | Escribir el DoD-contrato fijo (`dod_contract`/`dod_status`) en el `goal.json` de un secundario al lanzarlo | | `drain_fleet_events_py_infra` | Consumir la cola de transiciones del watcher (`~/.claude/fleet/events.jsonl`), agrupada por clasificación + urgentes | | `summarize_fleet_transitions_py_infra` | Resumir las transiciones del feed en una línea (`terminados/reclaman/estancados`); alimenta el bloque `FLEET-STATE` que el hook `UserPromptSubmit` inyecta cada turno | | `classify_fleet_termination_go_infra` | Clasificar el estado de terminación de un agente (RECLAMA/MAL_LANZADO/DICE_TERMINADO/ESTANCADO/TRABAJANDO) — lo usa el watcher | | `list_claude_fleet_go_infra` | Fleet tipado con goal/phase/`role` + `tmux_window` (alimenta `/fleet` y el watcher). **Invócala por el binario `apps/fleetview/fleetview list --json`**, NUNCA por `./fn run` (la despacha como `go test`). El JSON del CLI aún no expone `role`/`dod_contract`/`dod_status`; léelos de `~/.claude/goals/.json` | | `spawn_fleet_agent_bash_infra` | Lanzar un ejecutor (o el orquestador) como window de la flota tmux — preferido sobre kitty cuando hay perfil fleet. `--parent ` atribuye el ejecutor a ti y habilita el push activo del watcher | | `mark_claude_role_py_infra` | Marcar `role` (orchestrator/executor) en el goal.json de un Claude resolviendo PID→sessionId | | `mark_claude_parent_py_infra` | Marcar `parent_orchestrator` (sessionId del orquestador que lo lanzó) en el goal.json de un ejecutor resolviendo PID→sessionId. Lo invoca `spawn_fleet_agent --parent`; habilita el routing del watcher al pane del orquestador padre | | `kill_fleet_agent_bash_infra` | Cierre dirigido de UN ejecutor: SIGTERM al claude + kill-window de su window tmux. Guards anti-orquestador y anti-self. Lo usa el orquestador para liberar el slot idle tras verificar `met` (auto-kill) | **Cómo invocarlas.** Las Bash y Python del grupo se lanzan con `./fn run [args]` (verificado: `list_claude_agents`, `drain_fleet_events`, `reboot_all_claudes`, `set_dod_contract`, `mark_claude_role`, `mark_claude_parent`, `kill_fleet_agent`, `launch_claude_agent_kitty`, `spawn_fleet_agent`). Las **Go con tests** NO: `./fn run` las despacha como `go test`. Por eso `list_claude_fleet_go_infra` se usa por el binario `apps/fleetview/fleetview list --json`, y `classify_fleet_termination_go_infra` la consume el watcher embebido en fleetview (no se invoca a mano). ## Ejemplo end-to-end Tarea grande: *"añade un endpoint `/api/health` al backend de la app `kanban` y, en paralelo, documenta el grupo de capacidad `deploy` en `docs/capabilities/deploy.md`"*. Dos piezas independientes: una toca el sub-repo `apps/kanban` (su propio `.git`), la otra toca el repo padre `fn_registry` (docs). Aislamiento natural distinto para cada una. ```bash # 1. Descomponer → 2 secundarios independientes: # A) health endpoint → sub-repo apps/kanban (aislamiento (a)) # B) doc capability → worktree del padre (aislamiento (b)) # 2. Preparar aislamiento de B (worktree del padre; A ya está aislado por su sub-repo): git -C ~/fn_registry worktree add /tmp/orq_capdoc -b orq/cap-deploy master # 3. Escribir los prompts autónomos (autocontenidos, con reglas de aislamiento): # /tmp/orq_health.md → "trabaja en apps/kanban (sub-repo propio), rama issue/health, # commits atómicos de tus paths, push al terminar, report en reports/. No toques el # repo padre. Reporta tu progreso en esta terminal." # /tmp/orq_capdoc.md → "trabaja SOLO en /tmp/orq_capdoc (worktree), rama orq/cap-deploy, # toca solo docs/capabilities/deploy.md, git add de ese path, push al terminar, report # en reports/. No toques ~/fn_registry. Reporta tu progreso en esta terminal." # 4. Lanzar ambos secundarios (cada uno su kitty, su dir aislado): ./fn run launch_claude_agent_kitty "kanban · health endpoint" \ ~/fn_registry/apps/kanban /tmp/orq_health.md ./fn run launch_claude_agent_kitty "fn_registry · doc deploy" \ /tmp/orq_capdoc /tmp/orq_capdoc.md # 5. Seguir la flota (cada turno): ./fn run list_claude_agents # → tabla con los 2 secundarios vivos (PID, cwd, sessionId, status) + tu SELF. # Lee /tmp/orq_*_kitty.log para el arranque; cuando terminen, lee sus reports/. # 7. Integrar (desde el working tree principal): git -C ~/fn_registry/apps/kanban merge --no-ff issue/health # sub-repo de la app git -C ~/fn_registry merge --no-ff orq/cap-deploy # repo padre (la doc) git -C ~/fn_registry worktree remove /tmp/orq_capdoc # limpiar worktree # Resumen al humano: A integrado (endpoint + test verde), B integrado (doc), # flota vacía. Tarea grande hecha. ``` ## Salida del modo Cuando el humano escriba `salir orquestador` o `fin orquestador`, cierra con un resumen de la flota: secundarios lanzados, cuáles terminaron e integraste, cuáles siguen vivos (con su kitty para que el humano decida), y los reports generados. Si quedan secundarios vivos, recuérdale que `list_claude_agents` los lista y que para pararlos es kill por PID exacto, nunca `pkill`. ## Relación con otras reglas - `.claude/rules/autonomous_loop.md` — `fn-orquestador` (Agent tool, sandbox no-interactivo). Es lo que este modo **no** es; tenlas claras separadas. - `.claude/rules/apps_subrepo.md` — apps/analyses/projects son sub-repos Gitea (`apps/*` gitignored): el aislamiento natural (opción (a)) y el gotcha de `git init` antes de limpiar un worktree con una app nueva dentro. - `.claude/rules/reports.md` + `.claude/rules/dod_quality.md` — qué entrega cada secundario: report con evidencia ejecutable + gaps. - `.claude/rules/delegation.md` + `.claude/rules/registry_calls.md` — los secundarios siguen registry-first y delegan a `fn-constructor` igual que tú. - Memorias: `lanzar-agentes-skip-permissions`, `multi-agent-git-race-same-repo`, `claude-session-pid-mapping`, `prefiere-kitty-terminal`.