Files
fn_registry/bash/functions/infra/fleet_send_text.md
T
egutierrez 3be8b28a8f feat(orchestration): fleet_send_text — nudge fiable por pane_id estable
El nudge del orquestador apuntaba al window_id (@N) de tmux, que migra cuando
el focus-swap de FleetView recrea windows (break-pane/join-pane): el texto
acababa en el window equivocado o en otro agente (a veces no llega). Ademas,
texto y Enter en la misma invocacion hacian que el TUI no interpretara el submit.

Nueva funcion fleet_send_text_bash_infra (grupo orchestration) que:
- resuelve el pane_id (%N) estable fresco justo antes de enviar (sessionId/PID
  a pane via tmux list-panes -a + walk de ancestros /proc), no el @N volatil;
- manda texto literal y Enter en invocaciones separadas;
- verifica con capture-pane que el texto llego antes del submit, con reintento;
- guards anti-self y error claro si el target no resuelve a un pane vivo.

Test (19/19) sobre socket tmux propio: confirma que tras break-pane el pane_id
no migra y el reenvio sigue llegando. orchestration.md (seccion Nudge + catalogo)
actualizado para usar la funcion en lugar del send-keys -t <@N> manual.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 21:08:47 +02:00

7.2 KiB

name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, error_type, file_path, tested, tests, test_file_path, params, output
name kind lang domain version purity signature description tags uses_functions uses_types error_type file_path tested tests test_file_path params output
fleet_send_text function bash infra 1.0.0 impure fleet_send_text <sessionId|PID> "<texto>" [--socket <s>] [--no-enter] [--retries N] [--dry-run] Empuja texto a UN agente de la flota tmux de forma fiable, resolviendo su pane_id (%N) ESTABLE FRESCO justo antes de enviar. Es el reemplazo del nudge antiguo del orquestador, que apuntaba al window_id (@N) leido del JSON de la flota: ese @N MIGRA cuando el focus-swap de FleetView (break-pane + join-pane) recrea windows, asi que enviar al @N viejo (cacheado por el bloque FLEET-STATE o leido un instante antes) mandaba el texto al window equivocado o a otro agente. fleet_send_text resuelve sessionId -> PID (sessions/<PID>.json) -> el pane cuyo proceso (o un ancestro suyo en /proc) es pane_pid, leyendo tmux list-panes -a en el momento del envio, y usa el pane_id (%N) que NO migra. Ademas manda el texto literal (send-keys -l) y el Enter en invocaciones SEPARADAS, verificando con capture-pane que el texto aparecio en el input antes de pulsar Enter; reintenta si no aparece. Guards: NO envia a tu propio pane; error claro si el target no resuelve a un pane vivo. Por defecto EJECUTA; --dry-run imprime el plan sin enviar.
fleet
claude-fleet
orchestration
tmux
nudge
send-keys
infra
error_go_core bash/functions/infra/fleet_send_text.sh true
golden: envio por PID resuelve el pane_id estable, inyecta el texto y se verifica via capture-pane
edge: tras break-pane (focus-swap) el pane_id NO migra y el reenvio sigue llegando
edge: resolucion por prefijo de sessionId (sessions/<pid>.json) entrega el texto
edge: --dry-run no inyecta nada y reporta status=dry-run
error: sessionId no resuelto rc=2; falta texto rc=2; PID sin pane vivo rc=4
guard: enviar a la sesion actual (self) rc=3
bash/functions/infra/fleet_send_text_test.sh
name desc
target Primer arg posicional: sessionId del agente (exacto o prefijo) o su PID (todo digitos). Por sessionId se busca en sessions/*.json el que case y su archivo (<pid>.json) da el PID; por PID se usa directo.
name desc
texto Segundo arg posicional: el texto a inyectar en el input del agente (entre comillas).
name desc
--socket Socket tmux del perfil FleetView donde vive el pane. Default: $FLEET_SOCKET, o 'fleet' si no esta seteada.
name desc
--no-enter Deja el texto en el input sin pulsar Enter (no hace submit). Por defecto envia el Enter en una invocacion separada tras el texto.
name desc
--retries Numero de reintentos si el texto no aparece en el pane tras el send (default 2). Cada reintento limpia el input con C-u antes de reenviar.
name desc
--dry-run Imprime el plan (PID, sessionId, pane, socket) y NO envia nada. Sin esto, ejecuta.
Imprime una linea de plan (target, PID, sessionId, socket, pane resuelto, modo de envio) y una linea final parseable 'pane=%N intento=N status=ok|dry-run'. Exit 0 ok/dry-run; 2 uso incorrecto o target no resuelto a PID; 3 guard (target es la sesion actual); 4 no se encontro pane vivo para el target; 5 enviado pero no verificado tras los reintentos.

fleet_send_text

Empuja texto al input de un agente de la flota tmux de forma fiable. Resuelve el pane_id (%N) estable del agente fresco justo antes de enviar (nunca cachea el window_id @N, que migra con el focus-swap), manda el texto literal y el Enter en invocaciones separadas, y verifica con capture-pane que el texto llegó antes de hacer submit. Es el reemplazo del patrón de nudge antiguo (tmux send-keys -t <window_id @N>), que fallaba "a veces" porque enviaba al window equivocado tras un focus-swap.

Ejemplo

# Nudge a un ejecutor estancado por sessionId (el orquestador lo llama tras detectar ESTANCADO):
./fn run fleet_send_text 32945650-a4e1-472b-90c9-5b38ef60a463 \
  "Sigues idle con tu DoD-contrato sin cerrar. Falta: el error path con evidencia. Cierralo o reporta el bloqueo." \
  --socket "$FLEET_SOCKET"

# Por prefijo de sessionId, en el socket por defecto ($FLEET_SOCKET o "fleet"):
./fn run fleet_send_text 32945650 "Recuerda pushear la rama antes de cerrar."

# Dejar texto en el input sin hacer submit (--no-enter), o solo ver el plan (--dry-run):
./fn run fleet_send_text 48213 "borrador..." --no-enter
./fn run fleet_send_text 48213 "texto" --dry-run

Cuando usarla

Úsala desde el modo orquestador siempre que necesites inyectar texto en el input de un agente de la flota: el nudge a un ESTANCADO, el aviso de un gap concreto a un ejecutor cuyo cierre falló la verificación, o cualquier mensaje dirigido. Sustituye al tmux send-keys -t <window_id> manual. Resuelve el target por sessionId (exacto o prefijo) o por PID. Solo a idle/ESTANCADO; jamás a un agente en waiting/preguntando (esos te reclaman a ti, no un empujón del bot). Para cerrar un ejecutor verificado met no es esto: usa kill_fleet_agent.

Gotchas

  • El bug que arregla — el window_id (@N) MIGRA: el focus-swap de FleetView (tmux_swap_window_into_console.go) trae el claude objetivo a la console con break-pane + join-pane, lo que recrea windows y cambia el @N del agente (@32@34). El bloque FLEET-STATE y el JSON de la flota pueden traer un @N ya viejo. Enviar a ese @N manda el texto al window equivocado o a otro agente. Esta función NUNCA usa @N: resuelve el pane_id (%N), que se preserva durante toda la vida del pane aunque el pane se mueva de window. Verificado en test: tras break-pane el window_id pasa de @0 a @1 pero el pane_id sigue %0 y el envío sigue llegando.
  • Resolución fresca: el mapa pane_pid → pane_id se lee con tmux -L <socket> list-panes -a en el momento del envío, no se cachea. La resolución sube por los ancestros de /proc desde el PID del agente hasta casar un pane_pid: cubre tanto exec claude (pane_pid == claude pid, match directo, como hace spawn_fleet_agent) como un claude lanzado bajo un shell (pane_pid == shell ancestro).
  • Texto y Enter separados: el texto va con send-keys -l (literal, sin interpretar nombres de tecla), luego sleep 0.3, y el Enter en una invocación aparte. Mandar texto+Enter juntos hace que el TUI de Claude Code a veces no interprete el Enter como submit. La verificación con capture-pane se hace antes del Enter (tras el submit el TUI vacía el input y no se podría comprobar). Si el texto no aparece, limpia el input con C-u y reintenta (--retries, default 2).
  • Impura: inyecta teclas en un pane ajeno. Por defecto EJECUTA; usa --dry-run para inspeccionar el plan antes.
  • Guard anti-self: resuelve el PID de claude de la sesión actual subiendo por los ancestros de /proc; si el target coincide, rehúsa con exit 3 ("No me autoenvio").
  • Verificación por fragmento ancla: comprueba que aparezcan los primeros 24 caracteres del texto (no el texto completo) para no dar falso negativo cuando el input del TUI wrapea un mensaje largo en varias líneas.
  • Socket: si no pasas --socket, usa $FLEET_SOCKET o "fleet". Si el agente no está en ese socket, no se encontrará el pane (exit 4).