diff --git a/workloads/docs-portal/docs/arquitectura/sesion-tmux-persistente.md b/workloads/docs-portal/docs/arquitectura/sesion-tmux-persistente.md
index dc06db2..b533bcc 100644
--- a/workloads/docs-portal/docs/arquitectura/sesion-tmux-persistente.md
+++ b/workloads/docs-portal/docs/arquitectura/sesion-tmux-persistente.md
@@ -37,6 +37,9 @@ flowchart LR
SSH["Cliente SSH
(laptop / tablet / lo que sea)"] -->|docker exec| C["Contenedor tmux-host
--network host, --restart unless-stopped"]
C --> T["tmux server
sesión 'lab'"]
T --> CC["proceso claude
(Claude Code)"]
+ C --> HC["healthcheck.sh (background)
cada 30 min"]
+ HC -.->|curl| NET["gitea.cruzcloud.net"]
+ HC -.->|recrea si falta| T
C -.->|bind mount| DATA["/DATA (host)
repos y configs del lab"]
C -.->|bind mount| SOCK["/var/run/docker.sock (host)
control de Docker/k3d"]
C -.->|bind mount| HOME["/DATA/AppData/k3d-lab-v3/claude-code-home (host)
-> /root/.claude"]
@@ -49,7 +52,17 @@ reinicia.
## Contenedor: `tmux-host`
+La imagen se construye desde `scripts/tmux-host/Dockerfile` (antes era
+un `docker run ... alpine sh -c "apk add ..."` inline — mismo contenido,
+pero ahora como imagen versionada en vez de reinstalar todo en cada
+arranque). El Dockerfile fija `WORKDIR /DATA/AppData/k3d-lab-v3/lab-cruzcloud`
+(la raíz del lab, con `CLAUDE.md`, `apps-registry/`, `platform-infra/` y
+`scripts/`) y agrega `healthcheck.sh` (ver más abajo) como proceso en
+background del contenedor.
+
```bash
+docker build -t tmux-host:local scripts/tmux-host
+
docker run -d \
--name tmux-host \
--restart unless-stopped \
@@ -59,9 +72,15 @@ docker run -d \
-v /DATA/AppData/k3d-lab-v3/claude-code-home:/root/.claude \
-v /DATA/AppData/k3d-lab-v3/claude-code-home.json:/root/.claude.json \
--network host \
- alpine sh -c "apk add --no-cache tmux docker-cli bash curl git kubectl nodejs npm && npm install -g @anthropic-ai/claude-code && git config --global --add safe.directory '*' && tail -f /dev/null"
+ tmux-host:local
```
+`WORKDIR` hace que `docker exec` (y por lo tanto `lab-session.sh`)
+arranquen ya posicionados en la raíz del lab sin tener que navegar a
+mano — `lab-session.sh` además pasa la ruta explícita vía `tmux new -c`
+por las dudas, para no depender de que `docker exec` herede el `WORKDIR`
+de la imagen.
+
| Volumen | Destino | Por qué |
|---|---|---|
| `/DATA` | `/DATA` | Acceso a los repos (`apps-registry`, `platform-infra`, `scripts`) y configs del lab, mismos paths que en el host |
@@ -195,13 +214,47 @@ Si la sesión `lab` existe, se adjunta a ella (mismo shell, mismo
ejemplo justo después de que el contenedor se recreó desde cero— la
crea. No hay que recordar cuál de los dos casos aplica.
+## Chequeo de salud automático (cada 30 min)
+
+`scripts/tmux-host/healthcheck.sh` corre en background dentro del
+propio contenedor (lanzado por `entrypoint.sh`, el `ENTRYPOINT` de la
+imagen) y cada `HEALTHCHECK_INTERVAL_SEC` (1800s por defecto) hace dos
+chequeos independientes, con una respuesta distinta según el tipo de
+falla:
+
+- **Conectividad de red real** (`curl` a `https://gitea.cruzcloud.net/`):
+ si falla, **solo se registra** en el log y se notifica por Telegram —
+ el contenedor nunca intenta "arreglar" la red del host. Un problema de
+ red puede ser de hardware/BIOS (deuda técnica ya conocida, ver
+ `incidente-medusa-crashloop-2026-08.md`), fuera del alcance real de un
+ contenedor.
+- **Existencia de la sesión tmux `lab`** (`tmux has-session`): si no
+ existe, **sí se recrea automáticamente** (`tmux new-session -d -s lab
+ -c `) — es solo recuperar el propio entorno de trabajo del
+ contenedor, no tocar infraestructura externa, así que es seguro
+ hacerlo sin intervención humana.
+
+Todo evento (chequeo ok, falla de red, sesión recreada) queda en
+`/DATA/AppData/k3d-lab-v3/tmux-host/logs/health.log` — persistente en
+`/DATA`, no solo en el stdout del contenedor (que se pierde con
+`docker logs` al recrearlo). Las notificaciones de Telegram reusan el
+mismo bot/`chat_id` ya configurado para `scripts/lab-agent/`
+(`lab-agent.env`), en vez de duplicar credenciales entre los dos
+agentes del lab.
+
+Para probar el intervalo sin esperar 30 minutos, se puede levantar el
+contenedor con `-e HEALTHCHECK_INTERVAL_SEC=60` (solo para pruebas en un
+contenedor descartable — nunca cambiar esto en el `tmux-host` real sin
+volver a 1800 después).
+
## Qué sobrevive a qué
| Evento | ¿Sobrevive la sesión tmux (`lab`)? | ¿Sobrevive el login de Claude Code? |
|---|---|---|
| Corte de SSH / cerrar la terminal / cambiar de dispositivo | Sí — es exactamente el caso que este setup resuelve | Sí |
-| `docker restart tmux-host` (reinicio del NAS) | **No** — el proceso `tmux server` muere con el contenedor; `lab-session.sh` crea una sesión nueva automáticamente | Sí, vía el volumen montado en `/root/.claude` |
-| `docker rm` + recrear el contenedor desde cero | No | Sí, vía el mismo volumen — no hace falta volver a autenticar ni reconfirmar el diálogo de confianza del folder (ver el mount de `claude-code-home.json`) |
+| `docker restart tmux-host` (reinicio del NAS) | **No al toque, pero se auto-recupera solo** — el proceso `tmux server` muere con el contenedor; `healthcheck.sh` la recrea en el próximo ciclo (máx. 30 min) sin intervención, o `lab-session.sh` la crea al instante si alguien se reconecta antes | Sí, vía el volumen montado en `/root/.claude` |
+| `docker rm` + recrear el contenedor desde cero | No, pero igual se auto-recupera sola (mismo mecanismo de arriba) | Sí, vía el mismo volumen — no hace falta volver a autenticar ni reconfirmar el diálogo de confianza del folder (ver el mount de `claude-code-home.json`) |
+| Falla de red real del host/NAS | N/A | N/A — `healthcheck.sh` la detecta y notifica, pero no la corrige (fuera de su alcance) |
## Validación en vivo