Documentar cambio a Telegram, pausa nocturna y costo estimado

Actualiza docs/arquitectura/agente-monitoreo.md: canal de notificación
cambiado de ntfy a Telegram Bot API (con la evidencia de la prueba de
entrega), horario activo 06:00-23:00 con su justificación de costo, la
tensión sin resolver con la ventana de medusa-deploy (00:00-06:00, no
se solapa con el horario activo del agente), y la estimación de costo
mensual (~US$60-65, basada en las corridas reales medidas).
This commit is contained in:
devops committed 2026-08-26 00:01:08 -05:00
1 parent 5014903c51
commit e493f7d39e
1 file changed
+70 -11
@@ -172,20 +172,79 @@ no se purgaron.
## Notificaciones ## Notificaciones
Canal: [ntfy](https://ntfy.sh) — sin sistema de notificación previo en **Canal: Telegram Bot API** (reemplaza a ntfy, cambiado 2026-08-26 por
el lab, se eligió la opción sin infraestructura nueva (tópico público preferencia — no por costo, ver sección de costo abajo). Bot creado
de ntfy.sh, nombre privado difícil de adivinar) para validar la prueba manualmente vía [@BotFather](https://t.me/BotFather); el token vive en
end-to-end sin bloquear en instalar y exponer un contenedor propio. `TELEGRAM_BOT_TOKEN_FILE` (dueño `devops`, `600` — **no** root-only,
Migrar a un ntfy self-hosted (mismo patrón que gitea/nginxproxymanager/ mismo criterio que `GITEA_TOKEN_FILE`: el servicio corre como `devops`
cloudflared) es un cambio de una sola variable (`NTFY_URL`) cuando se por el login de suscripción, un archivo `root:root` le sería ilegible).
decida. `TELEGRAM_CHAT_ID` no es secreto, va directo en `lab-agent.env`.
El mensaje se manda como texto plano, sin `parse_mode`: el motivo que
devuelve Claude es texto libre y puede traer `_`, `*`, `[`, `]` — con
Markdown/MarkdownV2 esos caracteres rompen el parser de Telegram (`400
Bad Request`) si no se escapan con cuidado. Se prioriza entrega
confiable sobre formato.
**Validado en vivo 2026-08-26** con el mismo código que usa
`notify_send()` (lectura del token, `sendMessage` vía `curl
--data-urlencode`, formato `[title]\nmessage`): entrega confirmada
(`"ok":true`) y contenido recibido tal cual en Telegram. Gotcha
encontrado durante la prueba: el primer intento falló con `400 chat not
found` porque el `chat_id` pegado correspondía a otro bot — el `chat_id`
de un usuario es el mismo across bots, pero Telegram igual devuelve
`chat not found` si *ese bot puntual* nunca recibió un mensaje de esa
persona. Se resolvió confirmando el `chat_id` real vía `getUpdates` del
bot correcto.
## Horario activo y costo
**Pausa nocturna (`ACTIVE_HOURS_START`/`END`, hora local del host):**
activo `06:00`–`23:00`, pausado `23:00`–`06:00`. El script chequea esto
como lo primero que hace tras inicializar logging — si está fuera de
horario, sale **antes** de tocar `kubectl`/`docker`/`curl` o invocar
`claude -p`, así que la pausa ahorra el costo completo de la corrida,
no solo la notificación. El timer de systemd además solo dispara dentro
de esa ventana (`OnCalendar=*-*-* 06..22:00/15:00`) — la doble
verificación es deliberada: si alguien edita la unidad de systemd sin
tocar `lab-agent.env` (o corre el script a mano), el chequeo interno
sigue protegiendo.
**Tensión sin resolver con la ventana de `medusa-deploy`:** la ventana
de auto-restart para `medusa-deploy` (`COMMERCE_BACKEND_WINDOW_*`,
`00:00`–`06:00`) **no se solapa** con el horario activo del agente
(`06:00`–`23:00`). Esto significa que, en la práctica, `medusa-deploy`
nunca es candidato real a auto-restart: la única ventana en la que la
política lo permitiría es exactamente cuando el agente está pausado y
ni siquiera corre. La regla sigue viva en el código (por si el horario
activo cambia más adelante) pero hoy es efectivamente inalcanzable.
Pendiente de decisión: mover la ventana de `medusa-deploy` a que caiga
dentro del horario activo, o aceptar que ese recurso queda cubierto
solo por diagnóstico/notificación manual.
**Estimación de costo mensual:**
| | |
|---|---|
| Corridas/día (17h activas ÷ 15 min) | 68 |
| Costo por corrida (medido en la prueba e2e, Sonnet) | ~US$0.018–0.045 con cache tibia; ~US$0.12 en frío (primera corrida del día, tras 7h de pausa el cache de prompt ya expiró) |
| Estimado diario (1 corrida fría + 67 tibias) | ~US$2.10 |
| **Estimado mensual (× 30 días)** | **~US$60–65** (rango US$35–90 según variación real de cache) |
Basado en solo ~7 corridas reales medidas durante la prueba — no es un
promedio estadísticamente robusto, es una primera estimación para
decidir si el timer se activa. Revisar el costo real después de la
primera semana en producción y ajustar frecuencia/modelo si hace falta.
## Pendiente antes de activar el timer en continuo ## Pendiente antes de activar el timer en continuo
- **Confirmación explícita del propietario del lab** para habilitar el - **Confirmación explícita del propietario del lab** para habilitar el
timer de systemd (Paso 4 original) — no se activa automáticamente timer de systemd — no se activa automáticamente solo por haber
solo por haber pasado la prueba end-to-end. pasado la prueba end-to-end ni por tener el costo estimado.
- Unidad systemd corriendo como `devops` (no root), con límites de - Unidad systemd de usuario (`systemctl --user`, corre como `devops`,
CPU/memoria. no `root`), con límites de CPU/memoria (`CPUQuota`, `MemoryMax`).
Requiere `loginctl enable-linger devops` (una vez, con sudo) para que
seguir corriendo sin sesión interactiva activa.
- Opcional: `GITEA_TOKEN_FILE` para activar la señal de "runner - Opcional: `GITEA_TOKEN_FILE` para activar la señal de "runner
atascado" vía API de Gitea Actions (hoy se omite, degradación segura). atascado" vía API de Gitea Actions (hoy se omite, degradación segura).
- Decidir la tensión con la ventana de `medusa-deploy` (ver arriba).