fix(orquestador): invocaciones exactas que funcionan en el system prompt

This commit is contained in:
agent
2026-06-20 23:27:25 +02:00
parent 2f184d9dd9
commit b410328cec
+89 -16
View File
@@ -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 mientras estén en contexto. Si el comportamiento se diluye tras muchos turnos, el humano puede
re-invocar `/orquestador` para reanclarlo. 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="<tu-sessionId>" # 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` ## 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 ### 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 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 `--dangerously-skip-permissions` porque los secundarios trabajan autónomos y desatendidos y los
prompts de permiso en cada Bash los atascarían: prompts de permiso en cada Bash los atascarían:
@@ -162,8 +188,22 @@ mapeo PID→sessionId→cwd son los archivos `~/.claude/sessions/<PID>.json` (me
`sessions/<PID>.json` (con validación anti-PID-reciclado), marca tu propia sesión como `SELF`, `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>`). y reporta cwd + sessionId de cada secundario (para retomar con `claude --resume <sessionId>`).
**Tiempo — usa el de ACTIVIDAD, no el del proceso.** Para "cuánto lleva cada agente" usa **Flota tipada (goal/phase/window/age) — usa el binario `fleetview`, NO `fn run`.** La flota con
`fleetview list` (columna `AGE`, o `age`/`idle_seconds` en `--json`): es el tiempo desde su última `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/<session_id>.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 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. `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. 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 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. 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 **Todo agente que lances para que EJECUTE trabajo va como terminal visible, NUNCA como sub-agente
**retomar** — implementar una feature de horas, depurar en vivo, una sesión que evoluciona. headless del Agent tool.** El humano quiere ver y poder saltar a cada agente en la flota; un
- **Agent tool directo**: fan-out **acotado y no interactivo** — buscar en el codebase, crear sub-agente del Agent tool corre invisible, no aparece en `fleetview`, no es conmutable con
una función con `fn-constructor`, auditar N apps con `fn-recopilador`. Más barato, sin `/fleet focus` y no se puede retomar. Jerarquía obligatoria al lanzar un agente:
terminal, sin supervisión humana. Para esto NO lances kitty: usa `Agent(...)` y ya.
Regla práctica: si el humano va a querer hablar con ello o mirarlo trabajar → kitty. Si es una 1. **Estás en un perfil fleet** (`$FLEET_SOCKET` seteada, lo normal) → `spawn_fleet_agent` (window
sub-tarea que devuelve un resultado y se acabó → Agent tool. 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) ## 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). 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/<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 ### Políticas por clasificación
@@ -273,7 +328,13 @@ 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 "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` 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("<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 ### 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. vistazo y no baile entre la flota que rota por estado.
- **El orquestador no hace el trabajo pesado.** Descompone, lanza, sigue, integra. Si te - **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? 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 - **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. 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 - **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 | | 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 | | 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 <paths-específicos>` | | `git add -A` en scope compartido | Arrastra cambios de otra sub-tarea al commit | `git add <paths-específicos>` |
| 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 | | 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) | | 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` | | 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 | | `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 | | `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 | | `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/<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 | | `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 | | `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 <id> [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 ## Ejemplo end-to-end
Tarea grande: *"añade un endpoint `/api/health` al backend de la app `kanban` y, en paralelo, Tarea grande: *"añade un endpoint `/api/health` al backend de la app `kanban` y, en paralelo,