fix(orquestador): invocaciones exactas que funcionan en el system prompt
This commit is contained in:
@@ -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="<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`
|
||||
@@ -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/<PID>.json` (me
|
||||
`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>`).
|
||||
|
||||
**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/<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
|
||||
`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/<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
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
@@ -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 <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 |
|
||||
| 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/<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 |
|
||||
| `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
|
||||
|
||||
Tarea grande: *"añade un endpoint `/api/health` al backend de la app `kanban` y, en paralelo,
|
||||
|
||||
Reference in New Issue
Block a user