Merge orq/doc-tags: documentar tools MCP fleet_* + corregir drift role/dod + tag orchestration a 6 funciones

This commit is contained in:
2026-06-21 18:05:48 +02:00
8 changed files with 49 additions and 11 deletions
+7
View File
@@ -168,6 +168,13 @@ políticas por clasificación, verificador, auto-kill, nudge, splitter, cadencia
no el número de agentes vivos — el hook te empuja un bloque `FLEET-STATE` cada turno; tú drenas con
`./fn run drain_fleet_events` y actúas por clasificación.
**Vía preferida — tools MCP `fleet_*`:** si la sesión tiene el MCP `orchestrator` conectado (lo
normal: está en `.mcp.json`), usa sus 6 tools — `mcp__orchestrator__fleet_list` / `fleet_drain` /
`fleet_classify` / `fleet_set_dod` / `fleet_kill` / `fleet_spawn` — en lugar de los `./fn run`
equivalentes: permisos pre-aprobados y salida estructurada, y `fleet_list` expone `role`/`dod_*`
directamente. El `./fn run` (y el binario `fleetview` para el listado) es el fallback CLI. Mapa
completo op→tool en `.claude/rules/orchestration.md`.
### 6. Parar un ejecutor — NUNCA `pkill`/`killall claude` (canónica)
Un `pkill claude` o `killall claude` **te mata a ti mismo** (el orquestador) junto con la flota.
+36 -5
View File
@@ -34,8 +34,11 @@ 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/<session_id>.json` (ver abajo).
CLI). El JSON de `fleetview list` **ya incluye** `role`/`dod_contract`/`dod_status` (además de
`tmux_window`): el binario los serializa directamente (`""` cuando el `goal.json` no los declara,
ver `apps/fleetview/cli.go`). El tool MCP `fleet_list` (ver abajo) además rellena los que el binario
deje vacíos leyéndolos del sidecar `~/.claude/goals/<session_id>.json`, así que con el MCP nunca te
faltan. Ya no hace falta leer el sidecar a mano salvo que uses el binario crudo y el campo venga vacío.
**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
@@ -43,6 +46,29 @@ actividad (proxy de cuánto lleva sin avanzar / en su estado), lo útil para det
`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.
### Vía preferida: tools MCP `fleet_*` (`orchestrator_mcp`)
El MCP `orchestrator` (registrado en `.mcp.json` como `orchestrator`, binario
`apps/orchestrator_mcp/orchestrator_mcp`) expone la maquinaria de la flota como **6 tools** que
envuelven las mismas funciones del registry. **En una sesión con `orchestrator_mcp` conectado,
prefiere los tools `mcp__orchestrator__fleet_*` sobre `./fn run`**: tienen permisos pre-aprobados,
devuelven salida estructurada y se registran en la telemetría como cualquier MCP (regla
`registry_calls.md`). El `./fn run` (o el binario `fleetview` para el listado) sigue siendo el
**fallback CLI** cuando el MCP no está conectado. Mapa de cada operación de la flota a su tool:
| Operación de la flota | Tool MCP (preferido) | Fallback `./fn run` / binario |
|---|---|---|
| Listar la flota tipada (session_id, goal, phase, status, **role, dod_contract, dod_status**, tmux_window, age, idle_seconds) | `mcp__orchestrator__fleet_list` | `apps/fleetview/fleetview list --json` (NO `./fn run list_claude_fleet`) |
| Drenar la cola de transiciones del watcher (agrupada por clasificación + urgentes) | `mcp__orchestrator__fleet_drain` (`advance` true consume, false hace peek) | `./fn run drain_fleet_events` |
| Clasificar el estado de terminación de UN agente (RECLAMA/MAL_LANZADO/DICE_TERMINADO/ESTANCADO/TRABAJANDO) | `mcp__orchestrator__fleet_classify` | (Go con tests; lo consume el watcher, no se invoca a mano) |
| Escribir el DoD-contrato fijo (`dod_contract`/`dod_status`) en el `goal.json` de un agente | `mcp__orchestrator__fleet_set_dod` | `./fn run set_dod_contract` |
| Cerrar dirigido UN ejecutor (auto-kill: SIGTERM + kill-window, con guards) | `mcp__orchestrator__fleet_kill` (`dry_run` para ver el plan) | `./fn run kill_fleet_agent` |
| Lanzar un ejecutor como window de la flota tmux (con `parent` para el push) | `mcp__orchestrator__fleet_spawn` | `./fn run spawn_fleet_agent` |
Ventaja extra de `fleet_list`: expone `role`/`dod_contract`/`dod_status` directamente (y rellena los
vacíos desde el sidecar `goal.json`), así que la regla "No te vigiles a ti mismo" se resuelve sin leer
el sidecar a mano — filtra por el `role` que ya trae cada fila.
Mantén una **tabla de seguimiento**, una fila por secundario, y actualízala en cada turno:
| slug | título kitty | PID | cwd / dir aislado | rama | log | report | estado |
@@ -134,10 +160,14 @@ produce `classify_fleet_termination` (pura) desde su estado (status + phase + do
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`:
cualquier agente con `role=orchestrator`. El `role` ya viene en cada fila de `fleet_list` (y de
`fleetview list --json`), así que filtras directamente por ese campo. Solo si usas el binario crudo y
la fila trae `role` vacío, cae al sidecar del goal de cada `session_id`:
```bash
# Preferido: filtrar por el role que ya trae fleet_list / fleetview list --json.
apps/fleetview/fleetview list --json | jq -r '.[] | select((.role // "executor") != "orchestrator") | .session_id'
# Fallback solo si el binario dejó role vacío en alguna fila:
jq -r '.role // "executor"' ~/.claude/goals/<session_id>.json # "orchestrator" => ignóralo
```
@@ -271,11 +301,12 @@ en lote.
| `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/<session_id>.json` |
| `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/<session_id>.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 <tu-sessionId>` 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) |
| `notify_desktop_go_infra` | Notificación de escritorio del fleet (`notify-send --app-name=fleetview`, degradación silenciosa si no hay `notify-send`). La usa el orquestador/watcher para avisar a la persona de un `RECLAMA` u otro evento urgente cuando no está mirando la terminal |
**Cómo invocarlas.** Las Bash y Python del grupo se lanzan con `./fn run <id> [args]` (verificado:
`list_claude_agents`, `drain_fleet_events`, `reboot_all_claudes`, `set_dod_contract`,
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "reboot_all_claudes([--go|--yes] [--resume-mode resume|continue|none] [--exclude-current] [--only-idle] [-h|--help])"
description: "Cierra todas las terminales kitty con una sesion de Claude Code corriendo y las relanza retomando la misma sesion (claude --resume <sessionId>). Mapea cada PID vivo a su ~/.claude/sessions/<PID>.json para sacar sessionId, cwd y la ventana kitty. DRY-RUN por defecto; --go ejecuta de verdad de forma desacoplada."
tags: [claude, session, terminal, kitty, reboot, infra, terminal-capture]
tags: [claude, session, terminal, kitty, reboot, infra, terminal-capture, orchestration]
uses_functions: []
uses_types: []
returns: []
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: pure
signature: "func ClassifyFleetTermination(status, phase, dodContract, dodStatus string, idleSeconds, stallThresholdSeconds int) string"
description: "Clasifica MECANICAMENTE el estado de terminacion de un agente Claude de la flota para que un watcher barato sin LLM decida que hacer. Pura y determinista. Devuelve una de RECLAMA, MAL_LANZADO, DICE_TERMINADO, ESTANCADO o TRABAJANDO segun precedencia fija: RECLAMA (pide input humano) manda sobre todo, luego MAL_LANZADO (sin DoD-contrato), luego DICE_TERMINADO, ESTANCADO y TRABAJANDO."
tags: [fleet, claude-fleet, classification, watcher, termination, orchestrator, pure, infra]
tags: [fleet, claude-fleet, classification, watcher, termination, orchestrator, pure, infra, orchestration]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "func ListClaudeFleetFrom(claudeDir string) ([]ClaudeFleet, error) | func ListClaudeFleet() ([]ClaudeFleet, error)"
description: "Lista la flota de procesos Claude Code de la maquina local (Linux). Escanea ~/.claude/sessions/*.json, cruza cada PID vivo contra /proc para validar liveness (anti-PID-reciclado via procStart == campo 22 de /proc/<pid>/stat), une el goal/phase de ~/.claude/goals/<sessionId>.json, extrae KITTY_PID del environ y deriva los campos de display (Target, Rename). Devuelve todas las sesiones ordenadas por status (idle, waiting, busy, otro) y por updatedAt desc; el caller filtra por Alive. Pieza de datos de la app TUI fleetview."
tags: [claude-fleet, infra, claude, session, proc, fleet, tui]
tags: [claude-fleet, infra, claude, session, proc, fleet, tui, orchestration]
uses_functions: []
uses_types: [claude_fleet_go_infra]
returns: [claude_fleet_go_infra]
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.1.0"
purity: impure
signature: "def drain_fleet_events(events_path: str | None = None, cursor_path: str | None = None, advance: bool = True) -> dict"
description: "Drena la cola JSONL de eventos de la flota desde un cursor persistente. Lee los eventos nuevos (desde la linea del cursor hasta el final), los parsea saltando lineas en blanco o JSON invalido, los agrupa por su campo `to` (RECLAMA, MAL_LANZADO, DICE_TERMINADO, ESTANCADO, TRABAJANDO, GONE), aisla los urgentes y avanza el cursor para no reprocesar. Pensado para que el orquestador-Claude de flota consuma eventos nuevos cada vez que despierta."
tags: [fleet, claude-fleet, jsonl, cursor, queue, drain, watcher, orchestrator]
tags: [fleet, claude-fleet, jsonl, cursor, queue, drain, watcher, orchestrator, orchestration]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "def mark_claude_role(pid: int, role: str, wait_s: float = 10.0, sessions_dir: str | None = None, goals_dir: str | None = None) -> dict"
description: "Marca el role (orchestrator | executor) de una sesion de Claude Code resolviendo PID -> sessionId. Sondea ~/.claude/sessions/<pid>.json (escrito por Claude Code unos segundos despues de arrancar) hasta wait_s segundos con deadline time.monotonic, extrae el sessionId y escribe SOLO la clave `role` en ~/.claude/goals/<sessionId>.json preservando el resto del goal (goal, phase, dod, dod_contract...). Escritura atomica tmp + os.replace. Si el sessions JSON no aparece a tiempo devuelve ok=False timeout sin lanzar. Pensado para el launcher del meta-orquestador de flota (fleetview) que necesita clasificar el orquestador frente a los executors."
tags: [fleet, claude-fleet, role, session, goal, orchestrator, launcher, pid]
tags: [fleet, claude-fleet, role, session, goal, orchestrator, launcher, pid, orchestration]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "def set_dod_contract(session_id: str, contract: str, status: str = 'pending', goals_dir: str | None = None) -> dict"
description: "Escribe un DoD-contrato fijo y su estado en el sidecar goal.json de una sesion Claude (~/.claude/goals/<session_id>.json). Preserva TODOS los campos existentes (goal, phase, dod, history, prompts) y solo actualiza dod_contract y dod_status. Usado por el meta-orquestador de flota para fijar el criterio de aceptacion estable contra el que se evalua la terminacion de un agente. Escritura atomica via tmp + os.replace."
tags: [fleet, claude-fleet, dod, goals, orchestrator, sidecar, json]
tags: [fleet, claude-fleet, dod, goals, orchestrator, sidecar, json, orchestration]
uses_functions: []
uses_types: []
returns: []