Files
fn_registry/python/functions/infra/mark_claude_role.md
T
egutierrez e4a36f1133 chore(tags): anadir tag 'orchestration' a las 6 funciones del grupo que faltaban
capability_groups.md exige que toda funcion de un grupo lleve su tag plano para
ser descubrible via fn_search tag='orchestration'. 6 de las funciones del grupo
(reboot_all_claudes, classify_fleet_termination, list_claude_fleet,
drain_fleet_events, mark_claude_role, set_dod_contract) no lo llevaban. Se anade
sin borrar los tags existentes.

notify_desktop_go_infra ya llevaba el tag pero no figuraba en la tabla del grupo:
se decide que SI pertenece (la usa el orquestador/watcher para avisar de un
RECLAMA u otro evento urgente) y se anade a la tabla en orchestration.md (commit
anterior), en lugar de quitarle el tag. Resultado: 13 funciones con tag
orchestration, identicas a las 13 filas de la tabla del grupo (sin drift).

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

4.9 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
mark_claude_role function py infra 1.0.0 impure def mark_claude_role(pid: int, role: str, wait_s: float = 10.0, sessions_dir: str | None = None, goals_dir: str | None = None) -> dict Marca el role (orchestrator | executor) de una sesion de Claude Code resolviendo PID -> sessionId. Sondea ~/.claude/sessions/<pid>.json (escrito por Claude Code unos segundos despues de arrancar) hasta wait_s segundos con deadline time.monotonic, extrae el sessionId y escribe SOLO la clave `role` en ~/.claude/goals/<sessionId>.json preservando el resto del goal (goal, phase, dod, dod_contract...). Escritura atomica tmp + os.replace. Si el sessions JSON no aparece a tiempo devuelve ok=False timeout sin lanzar. Pensado para el launcher del meta-orquestador de flota (fleetview) que necesita clasificar el orquestador frente a los executors.
fleet
claude-fleet
role
session
goal
orchestrator
launcher
pid
orchestration
false error_go_core
os
json
time
name desc
pid PID del proceso Claude Code recien arrancado. Se usa para localizar ~/.claude/sessions/<pid>.json
name desc
role role a marcar: 'orchestrator' o 'executor'. Cualquier otro valor lanza ValueError sin tocar disco
name desc
wait_s segundos maximos a esperar (sondeo cada ~0.25s) a que aparezca sessions/<pid>.json con un sessionId no vacio. Default 10.0
name desc
sessions_dir directorio de los sessions JSON. Default ~/.claude/sessions
name desc
goals_dir directorio de los goal JSON. Default ~/.claude/goals (se crea si falta)
dict. En exito: ok=True, pid (int), session_id (str), role (str), path (ruta del goal escrito). En timeout del poll: ok=False, error (str con 'timeout esperando sessions/<pid>.json'), pid (int). NO lanza en timeout; el launcher decide true
sessions presente resuelve y escribe role preservando otros campos
role invalido lanza ValueError sin escribir
sessions ausente devuelve timeout sin crash
goal inexistente se crea con solo role
python/functions/infra/mark_claude_role_test.py python/functions/infra/mark_claude_role.py

Ejemplo

import os
from infra.mark_claude_role import mark_claude_role

# El launcher de flota acaba de arrancar un Claude Code orquestador (subprocess).
proc = launch_claude_orchestrator(...)  # devuelve un objeto con .pid

# Marca su role: espera a que Claude Code escriba ~/.claude/sessions/<pid>.json
# (puede tardar unos segundos) y luego pone role=orchestrator en su goal.json.
res = mark_claude_role(proc.pid, "orchestrator", wait_s=15.0)

if res["ok"]:
    print(f"marcado: {res['session_id']} -> {res['role']} en {res['path']}")
else:
    # Timeout: el sessions/<pid>.json no aparecio a tiempo. El launcher decide
    # (reintentar mas tarde, loguear, dejar sin role hasta el proximo barrido).
    print("no se pudo marcar:", res["error"])

Cuando usarla

Usala en el launcher del meta-orquestador de flota justo despues de arrancar un proceso Claude Code, cuando ya conoces su PID pero todavia NO su sessionId. Resuelve el mapeo PID -> sessionId esperando el archivo que Claude Code escribe unos segundos despues, y deja marcado el role en el goal.json para que la TUI fleetview clasifique al orquestador frente a los executors. Tambien sirve para re-marcar el role de una sesion ya existente sin pisar el resto de su goal.

Gotchas

  • Impura: lee ~/.claude/sessions/<pid>.json y escribe ~/.claude/goals/<sessionId>.json (crea goals_dir si falta).
  • Timing del sessions JSON: Claude Code NO escribe sessions/<pid>.json al instante de arrancar — tarda unos segundos. Por eso la funcion sondea hasta wait_s (deadline con time.monotonic, no un unico sleep largo) y reacciona en cuanto el archivo aparece con un sessionId no vacio. Ajusta wait_s segun cuanto tarde tu maquina en arrancar la sesion.
  • Timeout NO lanza: si el archivo no aparece a tiempo devuelve {"ok": False, "error": "timeout esperando sessions/<pid>.json", "pid": pid}. Es decision deliberada: el launcher decide si reintentar o continuar. Solo role invalido lanza (ValueError), y lo hace antes de tocar disco.
  • NO pisa otros campos del goal: abre el goal existente (dict vacio si no existe o es JSON invalido / no-dict), setea UNICAMENTE la clave role y conserva todo lo demas (goal, phase, dod, dod_contract, etc.). Si el goal.json no existia, lo crea con solo {"role": ...}.
  • Escritura atomica: escribe a un archivo temporal y hace os.replace, asi un lector concurrente (la TUI fleetview) nunca ve un goal.json a medio escribir.
  • sessionId vacio o sessions JSON ilegible: se trata como "aun no listo" y se sigue sondeando hasta el deadline (no es un error inmediato).