From bf67ff3180f08390b84edf1d1b9bce16dac481d7 Mon Sep 17 00:00:00 2001 From: agent Date: Sun, 21 Jun 2026 21:50:32 +0200 Subject: [PATCH] docs(orquestador): deteccion de flota por $TMUX, kitty solo fuera de tmux orquestador.md + orchestration.md: la deteccion de 'estoy en una flota' se hace por $TMUX (via detect_fleet_context), NO por $FLEET_SOCKET (fragil). kitty es fallback SOLO cuando in_tmux=false. spawn_fleet_agent auto-detecta el socket (ya no hace falta pasar --socket/--session). Documenta la linea CONTEXTO FLEET del hook y anade detect_fleet_context al catalogo del grupo orchestration. --- .claude/commands/orquestador.md | 49 ++++++++++++++++++++------------- .claude/rules/orchestration.md | 20 ++++++++++++-- 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/.claude/commands/orquestador.md b/.claude/commands/orquestador.md index 66975ac0..528517ff 100644 --- a/.claude/commands/orquestador.md +++ b/.claude/commands/orquestador.md @@ -75,36 +75,46 @@ siendo grande para un agente, pásala por el **splitter** (ver `.claude/rules/or ### 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 cuando estás en un perfil fleet; kitty es -el fallback para secundarios que deban vivir fuera de la flota. +estás dentro de tmux/una flota, o kitty SOLO cuando de verdad no hay tmux. NUNCA como sub-agente del +Agent tool (ver paso 8).** La detección de "estoy en una flota" se hace por **`$TMUX`** (señal +fiable, vía `detect_fleet_context`), **NO por `$FLEET_SOCKET`** (a veces viene vacía en un claude +resumido/relanzado pese a vivir en la flota → te haría caer a kitty por error). El hook +`hook_fleet_state_inject.sh` te inyecta cada turno una línea `CONTEXTO FLEET: … socket=` cuando +estás dentro de la flota; úsala. Empieza por el bloque de flota tmux; kitty es el fallback solo fuera +de tmux. Siempre con `--dangerously-skip-permissions` (memoria `lanzar-agentes-skip-permissions`): los secundarios trabajan autónomos y desatendidos; los prompts de permiso en cada Bash los atascarían. -#### En la flota tmux (PREFERIDO en perfil fleet) +#### En la flota tmux (PREFERIDO siempre que estés en tmux) -Si estás dentro de un perfil FleetView (`$FLEET_SOCKET` seteada), **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`: +Si estás dentro de tmux/una flota (`$TMUX` seteada — compruébalo con `detect_fleet_context`, **no** +con `$FLEET_SOCKET`), **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" \ +# spawn_fleet_agent auto-detecta el socket/session de $TMUX — NO hace falta pasar --socket/--session: +./fn run spawn_fleet_agent \ --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 `), y con `--role executor|orchestrator` marca su `goal.json`. El aislamiento - git (sub-repo / worktree / scope) sigue imponiéndose en el prompt. +- `spawn_fleet_agent_bash_infra` **auto-detecta** socket/session del contexto tmux (`$TMUX`) vía + `detect_fleet_context`; pásalos explícitos solo si quieres otra flota (los explícitos priman). + Crea la window tmux + arranca claude con el prompt autocontenido (o `--skill `), y con + `--role executor|orchestrator` marca su `goal.json`. El aislamiento git (sub-repo / worktree / + scope) sigue imponiéndose en el prompt. - **`--parent ` (recomendado):** escribe `parent_orchestrator` en el `goal.json` del ejecutor atribuyéndotelo a ti. Es lo que habilita el **push activo** del watcher (te avisa en TU pane cuando ese ejecutor termina). Sin `--parent` el aviso no se rutea. Opcional y retro-compatible. Ver `.claude/rules/orchestration.md`. -#### Fuera de la flota (kitty fallback) +#### Fuera de tmux (kitty fallback) + +Solo cuando `detect_fleet_context` reporta `in_tmux=false` (de verdad no hay tmux): ```bash ./fn run launch_claude_agent_kitty " · " /tmp/orq_.md @@ -113,7 +123,8 @@ la flota, se vea en la TUI `fleetview` y sea conmutable con `/fleet focus`: - `launch_claude_agent_kitty_bash_infra(title, directory, prompt_file)` lanza el secundario con el comando canónico (`setsid nohup kitty … zsh -ic 'claude --dangerously-skip-permissions … ; exec zsh'`) que sobrevive al cierre de la terminal padre y deja una shell viva al terminar el claude; - devuelve el log de arranque (`/tmp/orq__kitty.log`). Usa kitty solo fuera de un perfil fleet. + devuelve el log de arranque (`/tmp/orq__kitty.log`). Usa kitty solo cuando NO estás en tmux + (`$TMUX` vacía); estando en una flota, kitty fragmenta la flota — usa `spawn_fleet_agent`. ### 3. Aislamiento git obligatorio por secundario (regla de oro) @@ -204,8 +215,8 @@ Cuando un secundario termina (rama pusheada + report verde): **Todo agente de trabajo va como terminal visible del fleet, NUNCA como sub-agente headless del Agent tool.** Un sub-agente headless corre invisible: no sale en `fleetview`, no es conmutable con `/fleet focus` ni se puede retomar. Jerarquía al lanzar un agente: -1. **En perfil fleet** (`$FLEET_SOCKET`, lo normal) → `spawn_fleet_agent` (window de la flota tmux). -2. **Fuera de un perfil fleet** → kitty con `launch_claude_agent_kitty`. +1. **Dentro de tmux/flota** (`$TMUX` seteada — comprueba con `detect_fleet_context`, NO con `$FLEET_SOCKET`) → `spawn_fleet_agent` (auto-detecta el socket; window de la flota tmux). +2. **Fuera de tmux** (`in_tmux=false`) → kitty con `launch_claude_agent_kitty`. 3. **Agent tool (sub-agente headless)** → **PROHIBIDO para lanzar un agente de trabajo.** SOLO para utilidades internas read-only tuyas que devuelven un resultado y mueren: el **verificador** adversarial de un cierre, el **splitter** (`Plan`), o una búsqueda puntual (`Explore`). @@ -268,10 +279,10 @@ git -C ~/fn_registry worktree add /tmp/orq_capdoc -b orq/cap-deploy master # /tmp/orq_health.md → trabaja en apps/kanban (sub-repo propio), rama issue/health, push, report. # /tmp/orq_capdoc.md → trabaja SOLO en /tmp/orq_capdoc (worktree), rama orq/cap-deploy, push, report. -# 4. Lanzar ambos (window de la flota si hay $FLEET_SOCKET; aquí kitty fallback). Tras conocer su -# sessionId, escribe su DoD-contrato con set_dod_contract. -./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 +# 4. Lanzar ambos como windows de la flota (estás en tmux → spawn_fleet_agent auto-detecta el socket +# de $TMUX; kitty SOLO si in_tmux=false). Tras conocer su sessionId, escribe su DoD-contrato. +./fn run spawn_fleet_agent --cwd ~/fn_registry/apps/kanban --prompt-file /tmp/orq_health.md --title "kanban · health endpoint" --parent "$MI_SESSION_ID" +./fn run spawn_fleet_agent --cwd /tmp/orq_capdoc --prompt-file /tmp/orq_capdoc.md --title "fn_registry · doc deploy" --parent "$MI_SESSION_ID" # 5. Seguir cada turno: drena FLEET-STATE, verifica DICE_TERMINADO, nudge a ESTANCADO, lee reports/ (maquinaria en orchestration.md). diff --git a/.claude/rules/orchestration.md b/.claude/rules/orchestration.md index c17078fc..32c307e7 100644 --- a/.claude/rules/orchestration.md +++ b/.claude/rules/orchestration.md @@ -123,6 +123,21 @@ existe, degrada limpio sin romper el turno (la línea de rol se sigue emitiendo) clasificación sigues drenando (abajo). El resumen lo produce `summarize_fleet_transitions_py_infra` sobre el feed del watcher. +Además, el mismo hook inyecta una línea **`CONTEXTO FLEET`** cuando detecta (vía +`detect_fleet_context_bash_infra`, leyendo **`$TMUX`**, no `$FLEET_SOCKET`) que el orquestador vive +dentro de una flota tmux: + +``` +CONTEXTO FLEET: estás dentro de la fleet tmux socket= session=. Lanza ejecutores con spawn_fleet_agent (auto-detecta el socket) — NUNCA kitty/launch_claude_agent_kitty estando aquí. +``` + +Es el recordatorio que evita el bug de caer a kitty cuando `$FLEET_SOCKET` viene vacía pese a estar +en la flota: la detección de contexto se hace por `$TMUX` (señal fiable que todo proceso dentro de +tmux tiene siempre), no por `$FLEET_SOCKET` (a veces ausente en un claude resumido/relanzado). Esta +parte del hook no necesita venv ni python (solo bash + tmux) y se emite antes del bloque +`FLEET-STATE`; si el detector falta o `$TMUX` está vacía, simplemente no se emite la línea (turno +intacto). + 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 @@ -302,7 +317,8 @@ en lote. | `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` + `dod_contract`/`dod_status` + `tmux_window` (alimenta `/fleet`, el watcher y el tool `fleet_list`). **Invócala por el tool `mcp__orchestrator__fleet_list` (preferido) o el binario `apps/fleetview/fleetview list --json`**, NUNCA por `./fn run` (la despacha como `go test`). El JSON del CLI **ya expone** `role`/`dod_contract`/`dod_status` (`""` si el `goal.json` no los declara); el tool MCP además rellena los vacíos desde `~/.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 | +| `detect_fleet_context_bash_infra` | Detectar si estás en una flota tmux derivando socket/session de `$TMUX` (señal fiable), con fallback a `$FLEET_SOCKET`. Devuelve JSON `{in_fleet,in_tmux,socket,session,source}`. Lo usan `spawn_fleet_agent` (auto-detección de socket) y el hook (línea `CONTEXTO FLEET`) para no caer a kitty estando en la flota | +| `spawn_fleet_agent_bash_infra` | Lanzar un ejecutor (o el orquestador) como window de la flota tmux — preferido sobre kitty siempre que estés en tmux. **Auto-detecta socket/session de `$TMUX`** (vía `detect_fleet_context`) si no se pasan `--socket`/`--session` (los explícitos priman). `--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) | @@ -311,7 +327,7 @@ en lote. **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 +`spawn_fleet_agent`, `detect_fleet_context`). 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).