From b410328cecd657aede6109451c308ea29164ee8b Mon Sep 17 00:00:00 2001 From: agent Date: Sat, 20 Jun 2026 23:27:25 +0200 Subject: [PATCH] fix(orquestador): invocaciones exactas que funcionan en el system prompt --- .claude/commands/orquestador.md | 105 +++++++++++++++++++++++++++----- 1 file changed, 89 insertions(+), 16 deletions(-) diff --git a/.claude/commands/orquestador.md b/.claude/commands/orquestador.md index f89dc7dd..80d46cb6 100644 --- a/.claude/commands/orquestador.md +++ b/.claude/commands/orquestador.md @@ -18,10 +18,28 @@ orquestador` o `fin orquestador`. No hay hook: el modo se sostiene por estas ins mientras estén en contexto. Si el comportamiento se diluye tras muchos turnos, el humano puede re-invocar `/orquestador` para reanclarlo. -Al entrar, responde con una sola línea de confirmación y queda a la espera de la tarea grande: +**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. Dame la tarea grande; la descompongo y lanzo secundarios. 'fin orquestador' para terminar. +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` @@ -56,6 +74,14 @@ en un secundario, o las serializas (una después de otra), o las das scopes de a ### 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: @@ -162,8 +188,22 @@ mapeo PID→sessionId→cwd son los archivos `~/.claude/sessions/.json` (me `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 `). -**Tiempo — usa el de ACTIVIDAD, no el del proceso.** Para "cuánto lleva cada agente" usa -`fleetview list` (columna `AGE`, o `age`/`idle_seconds` en `--json`): es el tiempo desde su última +**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. @@ -200,16 +240,25 @@ Cuando un secundario termina (rama pusheada + report verde): 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. kitty vs Agent tool — cuándo cada uno +### 8. Cómo lanzar un agente: SIEMPRE terminal del fleet (regla dura) -- **kitty (este modo)**: trabajo **largo e interactivo** que el humano quiere **ver** y poder - **retomar** — implementar una feature de horas, depurar en vivo, una sesión que evoluciona. -- **Agent tool directo**: fan-out **acotado y no interactivo** — buscar en el codebase, crear - una función con `fn-constructor`, auditar N apps con `fn-recopilador`. Más barato, sin - terminal, sin supervisión humana. Para esto NO lances kitty: usa `Agent(...)` y ya. +**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: -Regla práctica: si el humano va a querer hablar con ello o mirarlo trabajar → kitty. Si es una -sub-tarea que devuelve un resultado y se acabó → Agent tool. +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) @@ -234,7 +283,13 @@ El contrato sigue `dod_quality.md` (golden + edge + error con evidencia ejecutab 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` (cruza el `session_id` del evento con `list_claude_fleet`). 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). +**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 @@ -273,7 +328,13 @@ 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` lo da `list_claude_fleet`/`fleetview list --json`. **Solo a idle/ESTANCADO. JAMÁS a un agente en `waiting`/`preguntando`** — esos te reclaman a TI, no un empujón del bot. +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 @@ -304,6 +365,11 @@ El orquestador no hace polling caro: drena la cola **cuando actúa** (cuando la 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 @@ -321,7 +387,7 @@ El orquestador no hace polling caro: drena la cola **cuando actúa** (cuando la | 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 kitty para un fan-out trivial | Caro y sin supervisión que aporte | Agent tool directo (`fn-constructor`, `Explore`, …) | +| 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` | @@ -336,10 +402,17 @@ El orquestador no hace polling caro: drena la cola **cuando actúa** (cuando la | `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 | | `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/`dod_contract`/`dod_status`/`role` + window tmux (alimenta `/fleet` y 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 | | `mark_claude_role_py_infra` | Marcar `role` (orchestrator/executor) en el goal.json de un Claude resolviendo PID→sessionId | +**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`, `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,