118d5d36d3
El orquestador no se enteraba de los cambios de estado de su flota: el drenado
era manual y el peek documentado `./fn run drain_fleet_events --advance false`
devolvia un falso `{total_new:0, cursor:0}` porque `fn run` mapea los argumentos
posicionalmente y no parsea flags `--nombre valor` (events_path acababa valiendo
"--advance", una ruta inexistente).
- drain_fleet_events: nuevo helper _normalize_fn_run_flags que renormaliza el
patron `--advance <bool>` aplanado por `fn run`, de modo que el peek funciona
directo desde la CLI sin tocar el runner de Go. Bump 1.1.0 + growth log + tests
del normalizador (unit y end-to-end por HOME).
- summarize_fleet_transitions (nueva, pure, grupo claude-fleet): resume el dict
by_classification de drain en un bloque de una linea con las tres categorias
accionables (terminados / reclaman / estancados), dedup por session_id y
truncado de objetivo.
- hook_fleet_state_inject.sh (UserPromptSubmit): si la sesion es role=orchestrator
(leido de ~/.claude/goals/<session_id>.json), hace peek de la cola sin mover el
cursor y emite el bloque FLEET-STATE cada turno. Degrada limpio si el watcher
esta caido, la cola no existe o la sesion no es orquestador.
El registro del hook va en .claude/settings.local.json (gitignored, fuera de este
commit). Pendiente, lo integra otro agente: documentar el bloque FLEET-STATE en
.claude/commands/orquestador.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.6 KiB
4.6 KiB
name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, returns, returns_optional, error_type, imports, params, output, tested, tests, test_file_path, file_path
| name | kind | lang | domain | version | purity | signature | description | tags | uses_functions | uses_types | returns | returns_optional | error_type | imports | params | output | tested | tests | test_file_path | file_path | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| summarize_fleet_transitions | function | py | infra | 1.0.0 | pure | def summarize_fleet_transitions(by_classification: dict, max_items: int = 6) -> str | Resume en una sola linea las transiciones accionables de la flota de Claudes para inyectar por un hook UserPromptSubmit al orquestador. Toma el dict by_classification de drain_fleet_events (agrupado por campo `to`) y condensa SOLO las tres categorias accionables: DICE_TERMINADO (terminados), RECLAMA (reclaman) y ESTANCADO (estancados); ignora TRABAJANDO, GONE y MAL_LANZADO. Funcion pura, determinista y defensiva: deduplica por session_id, trunca el goal y limita el total con overflow `(+N mas)`. |
|
false |
|
una cadena de UNA sola linea. 'FLEET-STATE: sin cambios' si no hay nada accionable; en caso contrario 'FLEET-STATE: terminados=[<sid8>:<goal>] reclaman=[...] estancados=[...] (+N mas) (drain con ./fn run drain_fleet_events para consumir)' omitiendo las categorias vacias. | true |
|
python/functions/infra/summarize_fleet_transitions_test.py | python/functions/infra/summarize_fleet_transitions.py |
Ejemplo
from summarize_fleet_transitions import summarize_fleet_transitions
by_classification = {
"DICE_TERMINADO": [
{"session_id": "a1b2c3d4-aaaa-bbbb", "goal": "Migrar tabla salesforce a europe-west1", "to": "DICE_TERMINADO"},
],
"RECLAMA": [
{"session_id": "e5f6g7h8-cccc-dddd", "goal": "Necesito clave API de Metabase", "to": "RECLAMA", "urgent": True},
],
"ESTANCADO": [],
"TRABAJANDO": [
{"session_id": "99887766-eeee-ffff", "goal": "Indexando registry", "to": "TRABAJANDO"},
],
}
linea = summarize_fleet_transitions(by_classification)
# FLEET-STATE: terminados=[a1b2c3d4:Migrar tabla salesforce a europe-w…] reclaman=[e5f6g7h8:Necesito clave API de Metabase] (drain con ./fn run drain_fleet_events para consumir)
print(linea)
Cuando usarla
Cuando un hook (tipico UserPromptSubmit) necesita resumir en una sola linea las
transiciones pendientes de la flota para inyectarlas al orquestador-Claude. Se
encadena despues de drain_fleet_events (que produce el by_classification) para
darle al orquestador, sin coste de lectura extra, el estado accionable de un
vistazo: quien dice haber terminado, quien reclama atencion y quien esta estancado.
Gotchas
- Dedup por session_id: dentro de cada categoria, si un mismo
session_idaparece en varios eventos se conserva solo la ultima aparicion (la transicion mas reciente en la lista). Eventos sinsession_idvalido no se deduplican entre si y cada uno cuenta como entrada propia. - Truncado de goal: el
goalse recorta a 40 caracteres con elipsis…; ungoalausente o vacio se muestra como(sin objetivo)y unsession_idausente/vacio como????????. - Solo categorias accionables: TRABAJANDO, GONE y MAL_LANZADO se ignoran a
proposito. Si las tres categorias accionables estan vacias o no existen,
devuelve
FLEET-STATE: sin cambios. - Reparto de max_items: el cupo total se llena recorriendo las categorias en
orden (terminados -> reclaman -> estancados); los sobrantes se cuentan en
(+N mas). Es defensiva ante datos mal formados (valores no-lista o eventos no-dict se omiten sin lanzar excepcion).