## Maquinaria del modo orquestador: vigilancia reactiva de la flota

Esta regla recoge la **maquinaria estable** del modo `/orquestador` (`.claude/commands/orquestador.md`):
cómo se sigue la flota, cómo se consume la cola del watcher, cómo se clasifica cada agente y qué
política se aplica a cada clasificación, el verificador adversarial de cierres, el auto-kill, el
nudge, el splitter, la cadencia, y el catálogo de funciones del registry del grupo `orchestration`.

El comando `/orquestador` se queda con la doctrina y el flujo de cada turno; el detalle operativo
vive aquí para que el prompt del comando sea corto y la maquinaria no se diluya. El cerebro reactivo
de esta regla corresponde al flow 0012.

### Seguir la flota — listado y tiempo

La fuente de verdad del mapeo PID→sessionId→cwd son los archivos `~/.claude/sessions/<PID>.json`
(memoria `claude-session-pid-mapping`). Para listar la flota de Claudes vivos:

```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/<PID>.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 <sessionId>`).

**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, pane_id ("%N", el id estable), tmux_window ("@N", interno para focus/send-keys), 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). 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
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.

### 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**, **pane_id** (el id estable), 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.

**Identifica a cada agente por su `pane_id` ("%N").** Es el id ESTABLE de por vida del pane: el
`fleet_list` del MCP lo expone como el único identificador y **omite a propósito el `tmux_window`
("@N")**, que migra cuando el focus-swap mueve el pane entre windows y por eso nunca debe usarse ni
mostrarse como id (la persona no tiene referencia mental de "@4"). Las operaciones internas que sí
necesitan la window viva — `focus`, `send-keys`/nudge y `kill` — la resuelven BAJO DEMANDA contra
tmux a partir del session_id/PID (`kill_fleet_agent` y `fleetview focus` la recalculan por llamada);
para el nudge, lee `tmux_window` del binario `fleetview list --json` (que sí lo conserva como campo
interno), nunca del payload del MCP.

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 |
|---|---|---|---|---|---|---|---|
| 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 <dir> log --oneline`).

### El cerebro reactivo: vigilar la salud por el DoD

Seguir la flota 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 <sessionId> "Golden: <caso feliz+evidencia>. 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`— una línea recordatorio del rol
(`MODO ORQUESTADOR activo (role=orchestrator).`, que reancla el modo aunque su prompt se haya
diluido del contexto) seguida de un bloque resumen de las transiciones pendientes del watcher:

```
FLEET-STATE: terminados=[<sid>:<goal>…] 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 (la línea de rol se sigue emitiendo). 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.

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=<X> session=<Y>. 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
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
<tu-sessionId>`. 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`. 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
```

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 <sid>` 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: <contract>
   Report del ejecutor: <ruta del reports/NNNN-*.md>
   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.")
```

El verificador (y el splitter y las búsquedas con `Explore`) son la **única** excepción autorizada al
Agent tool dentro del modo: utilidades internas read-only del propio orquestador, que devuelven un
resultado y mueren sin que el humano las gestione como agentes de la flota. Jamás se usa el Agent tool
para ejecutar una sub-tarea (ver paso 8 del comando).

- `met` → el orquestador marca `set_dod_contract <sid> "<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 <sid>
  "<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 <sid> "<contract>" met`, ciérralo:

```bash
./fn run kill_fleet_agent <sessionId> --socket "$FLEET_SOCKET"
```

`kill_fleet_agent_bash_infra` manda **SIGTERM** al proceso `claude` del ejecutor (cierre limpio,
recuperable luego con `claude --resume <sessionId>`) 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`", paso 6 del comando).
- 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 <window_id> \
  "Sigues idle con tu DoD-contrato sin cerrar. Falta: <gap>. 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("<sid>")) | .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), **siempre dentro del tope de fan-out** (ver "Tope de fan-out" abajo).

### Tope de fan-out (regla dura)

**Máximo 6 ejecutores `role=executor` activos simultáneos por orquestador.** Si se alcanza el tope,
el orquestador NO lanza más: **encola** las sub-tareas restantes y las despacha a medida que un slot
se libera — un slot se libera cuando un ejecutor se verifica `met` y se cierra con `kill_fleet_agent`
(auto-kill). El conteo es de la **familia propia** (ejecutores con tu `parent_orchestrator`), no de
toda la flota; resuélvelo con el routing por `parent_orchestrator`, igual que el push activo.

Por qué un número duro y no "los que hagan falta": ya hubo el caso de **30 agentes que no cerraban
nada** y, al competir todos por el mismo rate-limit compartido, hubo que desactivar `goal_refine`
(el hook que reescribía el `dod` con un LLM por prompt). Más ejecutores no es más throughput: el
cuello de botella es el rate-limit compartido y los DoD que nadie cierra, no el número de procesos.

### 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.

## Funciones del registry del 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` + `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` |
| `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 <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`,
`mark_claude_role`, `mark_claude_parent`, `kill_fleet_agent`, `launch_claude_agent_kitty`,
`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).

## Relación con otras reglas

- `.claude/commands/orquestador.md` — la doctrina y el flujo de cada turno del modo; esta regla es su
  maquinaria operativa.
- `.claude/rules/autonomous_loop.md` — `fn-orquestador` (Agent tool, sandbox no-interactivo). Es lo
  que el modo orquestador **no** es.
- `.claude/rules/apps_subrepo.md` — apps/analyses/projects son sub-repos Gitea (`apps/*` gitignored):
  el aislamiento natural y el gotcha de `git init` antes de limpiar un worktree con una app nueva.
- `.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`.
- Memorias: `lanzar-agentes-skip-permissions`, `multi-agent-git-race-same-repo`,
  `claude-session-pid-mapping`, `prefiere-kitty-terminal`.
