334a71eed1
- mark_claude_role (python/functions/infra): resuelve PID->sessionId esperando sessions/<PID>.json y escribe role en el goal.json sin pisar el resto. 4 tests. - launch_fleetclaude: el pane derecho arranca el ORQUESTADOR con el skill /orquestador embebido como primer prompt; tras arrancar, mark_claude_role le pone role=orchestrator (en background, no-fatal) para que la TUI lo pinee arriba; ademas siembra 1 ejecutor idle inicial en su propia window. - skill /orquestador: regla 'no te vigiles a ti mismo' (ignora en la cola su propia sesion y cualquier role=orchestrator). Validado en vivo (perfil aislado): claude /orquestador entra en modo, role marcado, idle sembrado, pin correcto, fleet2 intacto. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.9 KiB
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. |
|
false | error_go_core |
|
|
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 |
|
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>.jsony escribe~/.claude/goals/<sessionId>.json(creagoals_dirsi falta). - Timing del sessions JSON: Claude Code NO escribe
sessions/<pid>.jsonal instante de arrancar — tarda unos segundos. Por eso la funcion sondea hastawait_s(deadline contime.monotonic, no un unicosleeplargo) y reacciona en cuanto el archivo aparece con unsessionIdno vacio. Ajustawait_ssegun 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. Soloroleinvalido 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
roley 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).