fix(tmux-host): lanzar claude al recrear la sesion + backoff de arranque

check_tmux_session() recreaba la sesion tmux "lab" tras un reinicio del
NAS pero nunca lanzaba `claude` adentro -- quedaba un shell vacio hasta
que alguien entraba a mano. Confirmado en vivo tras un corte de luz real
(2026-09-19): la sesion SI se recreaba sola, pero Claude Code no.

Se agrega wait_for_workdir() con backoff (hasta 120s por defecto) antes
de tocar el log o crear la sesion, para el caso de un reinicio completo
del host donde /media/HDD-Storage puede tardar en montarse mas de lo
que tarda Docker en arrancar este contenedor -- antes eso fallaba en
silencio y ese arranque no dejaba rastro en el log.

Probado en un contenedor descartable aparte (tmux-host-test): stop+start
desde cero recreo la sesion con claude corriendo, sin intervencion.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
devopsandClaude Sonnet 5 committed 2026-09-19 20:15:37 +00:00
1 parent 9d04c73b20
commit 955eec6e75
1 file changed
+59 -3
+59 -3
View File
@@ -11,7 +11,9 @@
# b) Existencia de la sesion tmux "lab". Si no existe (crash, o el
# contenedor se acaba de recrear), SI se recrea automaticamente --
# es solo recuperar el propio entorno de trabajo, no tocar
# infraestructura externa.
# infraestructura externa. Al recrearla tambien se lanza `claude`
# adentro (ver launch_claude_in_session) -- una sesion recreada sin
# esto es un shell pelado, no "el entorno de trabajo".
#
# Todo evento (chequeo ok, falla, accion tomada) queda en LOG_FILE, que
# vive bajo /DATA (persistente, sobrevive a recrear el contenedor) y no
@@ -31,13 +33,42 @@ LOG_FILE="${LOG_DIR}/health.log"
LAB_AGENT_ENV="${WORKDIR}/scripts/lab-agent/lab-agent.env"
NETWORK_CHECK_URL="https://gitea.cruzcloud.net/"
INTERVAL_SEC="${HEALTHCHECK_INTERVAL_SEC:-1800}"
# Cuanto esperar, SOLO al arrancar, a que WORKDIR este disponible antes de
# tocarlo. Tras un `docker restart` normal el host y sus mounts ya estaban
# arriba; tras un reinicio COMPLETO del NAS (corte de luz), Docker puede
# arrancar este contenedor antes de que /media/HDD-Storage termine de
# montarse en el host -- y WORKDIR llega a el via el symlink absoluto
# /DATA/AppData -> /media/HDD-Storage/AppData (ver el gotcha del symlink en
# docs-portal/docs/arquitectura/sesion-tmux-persistente.md). Sin esta
# espera, `mkdir -p "$LOG_DIR"` de mas abajo fallaba en silencio (set -u sin
# -e) y ese arranque quedaba SIN NINGUN rastro en el log -- asi se paso por
# alto originalmente el gotcha de "no lanzo claude" reportado 2026-09-19.
STARTUP_MOUNT_WAIT_MAX_SEC="${STARTUP_MOUNT_WAIT_MAX_SEC:-120}"
STARTUP_MOUNT_WAIT_STEP_SEC=5
mkdir -p "$LOG_DIR"
wait_for_workdir() {
local waited=0
while [[ ! -d "$WORKDIR" ]] && [[ "$waited" -lt "$STARTUP_MOUNT_WAIT_MAX_SEC" ]]; do
sleep "$STARTUP_MOUNT_WAIT_STEP_SEC"
waited=$((waited + STARTUP_MOUNT_WAIT_STEP_SEC))
done
echo "$waited"
}
STARTUP_WAITED_SEC="$(wait_for_workdir)"
mkdir -p "$LOG_DIR" 2>/dev/null || true
log() {
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] $*" >> "$LOG_FILE"
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] $*" >> "$LOG_FILE" 2>/dev/null || true
}
if [[ ! -d "$WORKDIR" ]]; then
log "ARRANQUE: ERROR -- ${WORKDIR} sigue sin existir tras esperar ${STARTUP_MOUNT_WAIT_MAX_SEC}s. El mount de /DATA o /media/HDD-Storage del host no esta listo; revisar fuera del contenedor."
elif [[ "$STARTUP_WAITED_SEC" -gt 0 ]]; then
log "ARRANQUE: ${WORKDIR} tardo ${STARTUP_WAITED_SEC}s en estar disponible (probable reinicio completo del host reordenando el montaje de /DATA)."
fi
notify_telegram() {
local title="$1" message="$2"
if [[ ! -f "$LAB_AGENT_ENV" ]]; then
@@ -73,14 +104,39 @@ check_network() {
fi
}
launch_claude_in_session() {
# El pane recien creado tarda un instante en tener su shell listo para
# recibir input -- un send-keys inmediato a veces se pierde. 1s alcanza
# sobrado para un shell interactivo local (no depende de red).
sleep 1
if tmux send-keys -t "$SESSION" "claude" Enter 2>>"$LOG_FILE"; then
log "TMUX: 'claude' lanzado dentro de la sesion recien creada"
else
log "TMUX: ERROR al lanzar 'claude' dentro de la sesion recien creada"
fi
}
check_tmux_session() {
if tmux has-session -t "$SESSION" 2>/dev/null; then
log "TMUX: sesion '${SESSION}' ok"
return
fi
if [[ ! -d "$WORKDIR" ]]; then
# No se crea sobre un WORKDIR ausente (quedaria posicionada en /). Se
# reintenta en el proximo ciclo -- ver STARTUP_MOUNT_WAIT_MAX_SEC arriba
# para el caso especifico de arranque en frio del host.
log "TMUX: sesion '${SESSION}' no existe y ${WORKDIR} tampoco -- no se crea esta vuelta, se reintenta en el proximo ciclo (${INTERVAL_SEC}s)."
return
fi
log "TMUX: sesion '${SESSION}' no existe -- recreando en ${WORKDIR}"
if tmux new-session -d -s "$SESSION" -c "$WORKDIR" 2>>"$LOG_FILE"; then
log "TMUX: sesion '${SESSION}' recreada exitosamente"
# Una sesion recreada sin esto es un shell vacio hasta que alguien
# entre a mano y escriba "claude" -- exactamente el gotcha reportado
# tras el reinicio completo del NAS del 2026-09-18/19: la sesion SI se
# recreaba sola (confirmado en el log de ese arranque), pero Claude
# Code nunca arrancaba adentro.
launch_claude_in_session
else
log "TMUX: ERROR al recrear la sesion '${SESSION}'"
fi