# META EVOLUTION — Documentación del Sistema

**Versión:** 7.6 + 8 mejoras operativas · auditado y reparado 2026-07-24
**Ecosistema:** HJAZZI (VPS Contabo, Ubuntu, Hermes Agent)
**Propósito:** Convertir un VPS común en una plataforma auto-monitoreada, auto-reparable y auto-mejorable.

---

## 1. ¿QUÉ ES META EVOLUTION?

Meta Evolution es un **sistema operativo secundario** compuesto por 12 capas de scripts Python que corren como cron jobs sobre Linux. No es una aplicación — es una capa de inteligencia operativa que:

- Monitorea servicios, disco, RAM, y conectividad
- Detecta y repara fallas automáticamente
- Aprende de incidentes pasados para mejorar decisiones futuras
- Genera reportes diarios y alertas
- Mantiene memoria persistente entre sesiones
- Evoluciona sin intervención humana

Cada capa es un script autónomo (no_agent) que pesa entre 150 y 550 líneas. No requieren base de datos, no requieren un LLM corriendo 24/7. Son procesos ligeros que escriben en archivos JSON.

---

## 2. ARQUITECTURA — LAS 12 CAPAS

```
Capa 0  — Guardian              [cada 15min]
Capa 1  — Health Monitor        [cada 15min]
Capa 2  — Auto Watcher          [cada 5min]
Capa 3  — Memory Core           [tiempo real]
Capa 4  — Predictor             [cada hora]
Capa 5  — Benchmark Suite       [6AM diario]
Capa 6  — Daily Digest          [8AM diario]
Capa 7  — Post-Session          [al finalizar sesión]
Capa 8  — Proyectos Autónomos   [bajo demanda]
Capa 9  — Market Intelligence   [7AM diario]
Capa 10 — Memory Compactor      [3AM diario]
Capa 11 — Deep Diagnosis (LLM)  [cada 15min, solo si hay breaker abierto]
```

### Capa 0 — Guardian (`guardian.py`)
Verifica que el ecosistema esté vivo:
- Gateway de Hermes activo
- Scripts críticos existan en disco
- Heartbeats recientes de cada script
- Espacio en disco (<85%)

Si algo está mal, registra un incidente y sale con exit code 1.

### Capa 1 — Health Monitor (`health_monitor.py`)
Toma métricas del VPS cada 15 minutos:
- Estado de cada servicio (systemctl + docker)
- Memoria RAM usada/disponible
- Disco usado/disponible
- Load average
- Health HTTP de cada servicio
- Estado de nginx y UFW
- Puertos escuchando

Escribe todo en `timeseries.jsonl` — el archivo más importante del sistema, con +4700 mediciones acumuladas.

### Capa 2 — Auto Watcher (`auto_watcher.py`)
Es el cerebro de auto-reparación:
1. Lee las últimas 3 mediciones de timeseries.jsonl
2. Detecta servicios caídos en TODAS las mediciones (caída sostenida, no transitoria)
3. Consulta Memory Core antes de actuar (¿este servicio tiene historial? ¿qué dice la experiencia?)
4. Intenta restart via systemctl
5. Si funciona → resetea contador, registra lección
6. Si falla → incrementa contador. A los 3 strikes → circuito breaker + alerta + escalar a humano
7. Si está en blocklist → no intenta nada, solo registra

### Capa 3 — Memory Core (`memory_core.py`)
API unificada de memoria persistente. Todos los demás scripts la consultan:
- `get_service_history(service)` — incidentes, patrones, última vez
- `suggest_action(service)` — ¿restart? ¿esperar? ¿escalar?
- `record_action(service, action, success, details)` — registra para aprendizaje futuro
- `get_trending_services()` — servicios con más incidentes en 24h
- `summary()` — resumen legible

Lee de tres archivos:
- `service_memory.json` — memoria por servicio (incidentes, restarts, patrones)
- `incidents.json` — incidentes activos + históricos
- `lessons.json` — lecciones aprendidas

### Capa 4 — Predictor (`predictor.py`)
Cada hora analiza las métricas de las últimas 24h:
- Drift en health checks HTTP (tiempo de respuesta)
- Tendencia de memoria RAM
- Servicios con reinicios frecuentes
- Patrones horarios de fallas

Genera alertas anticipadas: "shop tiene +800% drift en tiempo de respuesta".

### Capa 5 — Benchmark Suite (`benchmark_suite.py`)
8 tests de calidad que corren a las 6AM:
- Tiempo de respuesta de cada servicio
- Disponibilidad 24h
- Latencia de red
- Score A-F

No depende de archivos externos — consulta systemctl y docker ps en VIVO.

### Capa 6 — Daily Digest (`daily_digest.py`)
A las 8AM genera un reporte diario con:
- Score de salud del ecosistema
- Resultados de benchmarks
- Incidentes del día anterior
- Auto-healing realizado
- Predicciones del predictor
- Tendencias de mercado (de Capa 9)

Auto-actualiza STATE.md con los resultados.

### Capa 7 — Post-Session (`post_session.py`)
Al finalizar cada sesión de Hermes, reflexiona:
- ¿Qué se hizo?
- ¿Qué problemas se encontraron?
- ¿Hay algún patrón reusable que merezca convertirse en skill?
- Registra heartbeat

Funciona standalone sin necesidad de stdin pipe.

### Capa 8 — Proyectos Autónomos (`project_framework.py`)
Framework para crear proyectos con ciclo de vida de 9 pasos:
1. Leer objetivo
2. Consultar Memory Core
3. Analizar contexto
4. Generar plan
5. Ejecutar acciones
6. Medir resultados
7. Registrar experiencia
8. Actualizar conocimiento
9. Generar reporte

Proyectos activos: auto-deployer, blog-publisher, trading-agent, visual-marketer.

### Capa 9 — Market Intelligence (`market_intelligence.py`)
A las 7AM escanea:
- Hacker News (top posts)
- GitHub Trending (repositorios)
- Google Trends (términos relevantes)

Genera reporte en `market/market_report_*.json` con oportunidades de negocio identificadas.

### Capa 10 — Memory Compactor (`memory_compactor.py`)
A las 3AM hace limpieza automática:
- Purga snapshots de más de 30 días
- Comprime incidents resueltos de más de 7 días
- Rota cron outputs de más de 7 días
- Reporta heartbeats incompletos

Corre como no_agent, sin interacción humana.

### Capa 11 — Deep Diagnosis (`deep_diagnosis.py`)

**La primera capa con razonamiento LLM.** Las capas 0–10 son Python puro; esta
llama a **Claude Fable 5** vía OpenRouter cuando el sistema ya se rindió.

Se dispara solo cuando el auto_watcher agota sus 3 reintentos y abre el circuit
breaker. En ese punto la Capa 2 escalaba a un humano y ahí moría el asunto;
ahora antes de escalar se produce un diagnóstico razonado.

**Cómo funciona:**

1. Lee `autoheal_state.json` buscando servicios con `retries >= 3`
2. Si no hay ninguno, **sale en ~130 ms sin gastar un token** (el caso normal)
3. Si lo hay, recoge evidencia de solo lectura: `systemctl status`,
   `journalctl -n 60`, la serie temporal reciente, incidentes y lecciones
   previas del servicio, disco y memoria
4. Se la entrega a Fable 5 pidiéndole causa raíz, evidencia que la sustenta,
   acciones priorizadas marcadas `[SEGURA]`/`[RIESGOSA]`, y qué falta para
   estar seguro
5. Escribe el informe en `~/.hermes/evolution/diagnoses/<servicio>-<ts>.md`
6. Registra el evento y alerta por Telegram si el alerter está configurado

**FASE 1 — solo diagnostica. No ejecuta ninguna acción sobre el sistema.**
La lista blanca de acciones (`ALLOWLIST_FASE2` en el script) está declarada
pero sin uso: sirve para que el modelo proponga acciones del vocabulario
correcto. La ejecución automática se habilitará solo tras revisar la calidad de
varios diagnósticos reales.

**Controles de gasto** — necesarios porque Fable 5 cuesta $10/$50 por millón de
tokens:

| Control | Valor | Para qué |
|---|---|---|
| `DAILY_BUDGET_USD` | $1.00 | Tope duro diario; al alcanzarlo pospone |
| `COOLDOWN_HOURS` | 6 h | No re-diagnostica el mismo servicio antes |
| `MAX_OUTPUT_TOKENS` | 1500 | Acota el coste de salida, el más caro |
| Evidencia truncada | 14.000 chars | Acota el coste de entrada |

El gasto se acumula en `memory/diagnosis_spend.json` con retención de 30 días.
Un diagnóstico real medido costó **$0.047** (1.195 tokens de entrada, 700 de
salida).

> ⚠️ **Requiere saldo en OpenRouter.** Con la cuenta a cero la llamada devuelve
> `HTTP 402` y la capa registra el error sin romper nada — pero tampoco
> diagnostica. Verificar saldo en https://openrouter.ai/settings/credits

---

## 3. FLUJO DE DATOS

```
health_monitor (Capa 1)
    │ cada 15min
    ▼
timeseries.jsonl  ◄── predictor (Capa 4) lo lee cada hora
    │
    ├──► auto_watcher (Capa 2) cada 5min
    │       │
    │       ├──► consulta memory_core (Capa 3)
    │       ├──► systemctl restart (si aplica)
    │       ├──► incidents.json / lessons.json / service_memory.json
    │       └──► event_logger + alerter (Telegram)
    │
    ├──► daily_digest (Capa 6) cada 24h
    │       └──► STATE.md
    │
    └──► dashboard web (puerto 5050)
            └──► /api/system + /api/events
```

**Archivos clave del sistema:**

| Archivo | Ruta | Propósito |
|---|---|---|
| timeseries.jsonl | `~/.hermes/evolution/metrics/` | Serie temporal de métricas (15min) |
| heartbeats.json | `~/.hermes/evolution/metrics/` | Último latido de cada script |
| events.jsonl | `~/.hermes/evolution/metrics/` | Log centralizado de eventos |
| health_checks.jsonl | `~/.hermes/evolution/metrics/` | Resultados de healthchecks HTTP |
| incidents.json | `~/.hermes/evolution/memory/` | Incidentes activos + históricos |
| lessons.json | `~/.hermes/evolution/memory/` | Lecciones aprendidas |
| service_memory.json | `~/.hermes/evolution/memory/` | Memoria por servicio |
| autoheal_state.json | `~/.hermes/evolution/memory/` | Estado del circuit breaker |
| benchmarks.jsonl | `~/.hermes/evolution/metrics/` | Resultados de benchmarks |
| market_report_*.json | `~/.hermes/evolution/market/` | Reportes de market intelligence |
| STATE.md | `~/.hermes/` | Fuente de verdad del estado actual |

---

## 4. JOBS PROGRAMADOS (14)

> ⚠️ **No están en el crontab del sistema.** Hermes tiene su propio scheduler:
> los jobs se definen en `~/.hermes/cron/jobs.json` y los ejecuta un ticker
> propio, con historial en `~/.hermes/cron/executions.db`. Un `crontab -l`
> devuelve vacío — eso es lo esperado, no un fallo.

| Job | Schedule | Tipo | Script |
|---|---|---|---|
| Auto Watcher | cada 5min | no_agent | auto_watcher.py |
| trading-agent-247 | cada 5min | no_agent | trading_agent_watchdog.sh |
| Health Monitor | cada 15min | no_agent | health_monitor.py |
| Health Checker V8 | cada 15min | no_agent | health_checker.py |
| Guardian | cada 15min | no_agent | guardian.py |
| Predictor | cada hora | no_agent | predictor.py |
| Memory Sanity V8 | 2AM | no_agent | memory_sanity.py |
| Memory Compactor | 3AM | no_agent | memory_compactor.py |
| ECC Daily Backup | 4AM | LLM-driven | ecc_backup.py |
| Benchmark Suite | 6AM | no_agent | benchmark_suite.py |
| Market Intelligence | 7AM | no_agent | market_intelligence.py |
| Daily Digest | 8AM | no_agent | daily_digest.py |
| Blog Publisher | lunes 10AM | no_agent | blog_publisher.py |
| Deep Diagnosis (Capa 11) | cada 15min | no_agent + LLM | deep_diagnosis.py |

**Ya no existen como jobs programados** (estaban en la doc anterior pero no en
el scheduler): Hermes Backup, App Backup, UROPIC IG. Si se necesitan, hay que
volver a registrarlos.

Para inspeccionar el estado real:

```bash
python3 -c "
import json
d=json.load(open('/root/.hermes/cron/jobs.json'))
for j in d['jobs']:
    print(f\"{j['name'][:32]:34} {j['schedule_display']:14} {j['last_status']}\")
"
```

---

## 5. SERVICIOS MONITOREADOS (12 activos + 6 pendientes)

### Activos (verificado en vivo 2026-07-24, todos respondiendo)

| Servicio | Puerto | Tipo | Descripción |
|---|---|---|---|
| hermes-gateway | **8765** | systemd | Gateway de Hermes Agent |
| hermes-dashboard | 5050 | systemd | Dashboard V8 — metrics.hjazzi.com |
| hermes-upload | 5060 | systemd | File Upload — evolution.hjazzi.com/upload |
| acta-core | 8000 | systemd | ACTA Core — motor de ingesta documental (uvicorn) |
| shop-hjazzi | 5021 | systemd | Tienda HJAZZI (Flask) |
| uropic | 5010 | systemd | UROPIC Sorteos (Flask + gunicorn) |
| shangrila | 5020 | systemd | Mercado Shangri-La (Flask + gunicorn) |
| estiloyvida | 5022 | systemd | Estilo y Vida tienda (Flask) |
| logis | 5015 | systemd | LOGIS Multi-nicho (Flask) |
| nicolas | **5016** | systemd | Nicolas web de servicios (Flask) |
| leadcapture | 5030 | systemd | Lead capture endpoint (Flask + gunicorn) |
| n8n | 5678 | Docker | n8n automation platform |

> **Corrección importante:** el gateway escucha en **8765**, no en 8080. La doc
> anterior decía 8080 — probablemente un residuo del incidente de "puerto 8080
> duplicado" resuelto el 2026-07-23. Cualquier healthcheck que apunte a 8080
> fallará. `nicolas` sí tiene puerto (5016); antes figuraba como "-".
>
> `acta-core` y `hermes-upload` no estaban documentados y llevan tiempo
> corriendo.

### Pendientes de restaurar (unit files instalados, apps perdidas en migración)

| Servicio | Puerto | App en |
|---|---|---|
| hermes-bridge | 5005 | /root/hermes-bridge/ |
| hjazzi-bot | - | /root/hjazzi/ |
| cognitive-bot | - | /root/cognitive_signals/ |
| cognitive-webhook | 5020 | /root/cognitive_signals/ |
| app-hjazzi | 5020 | /root/app-hjazzi/ |
| hjazzi-monitor | - | /root/hjazzi/ |

---

## 6. SISTEMA DE ALERTAS

Cuando un servicio cae:

1. **Health Monitor** lo detecta en 15min o menos
2. **Auto Watcher** confirma la caída en 3 mediciones consecutivas (mínimo ~15min)
3. **Alerter** envía mensaje por Telegram si hay token configurado
4. **Event Logger** registra el evento en events.jsonl
5. **Dashboard** refleja el cambio en la próxima actualización (30s)

Cuando se recupera:

1. **Auto Watcher** detecta que volvió
2. **Alerter** envía notificación de recuperación
3. **Incidentes** se marcan como resueltos
4. **Memory Core** registra la lección

Si 3 reintentos fallan:

1. **Circuito breaker** se activa — no se intenta más
2. **Alerta crítica** por Telegram
3. **Incidente** queda abierto para revisión humana

---

## 7. DASHBOARD WEB

**URL:** `http://metrics.hjazzi.com:5050` (o `http://<vps-ip>:5050`)
**Stack:** Flask + gunicorn, sin base de datos
**Systemd:** hermes-dashboard.service

Muestra en tiempo real:
- Todos los servicios con indicador 🟢/🔴
- Barras de uso de disco y RAM
- Uptime de cada servicio en las últimas 24h (con barras de progreso)
- Últimos 20 eventos del log centralizado
- Auto-refresh cada 30 segundos

**APIs:**
- `GET /api/system` — servicios, métricas del sistema, uptime 24h
- `GET /api/events` — últimos 50 eventos

---

## 8. UBICACIÓN DE ARCHIVOS

```
~/.hermes/
├── STATE.md                          ← Fuente de verdad del ecosistema
├── scripts/                          ← Todos los scripts V7/V8
│   ├── guardian.py                   ← Capa 0
│   ├── health_monitor.py             ← Capa 1
│   ├── auto_watcher.py               ← Capa 2
│   ├── memory_core.py                ← Capa 3
│   ├── predictor.py                  ← Capa 4
│   ├── benchmark_suite.py            ← Capa 5
│   ├── daily_digest.py               ← Capa 6
│   ├── post_session.py               ← Capa 7
│   ├── project_framework.py          ← Capa 8
│   ├── market_intelligence.py        ← Capa 9
│   ├── memory_compactor.py           ← Capa 10
│   ├── deep_diagnosis.py             ← Capa 11 (LLM)
│   ├── heartbeat.py                  ← Registro de heartbeats
│   ├── event_logger.py               ← Log centralizado V8
│   ├── alerter.py                    ← Alertas Telegram V8
│   ├── health_checker.py             ← Healthchecks HTTP V8
│   ├── memory_sanity.py              ← Auto-limpieza V8
│   ├── state_verifier.py             ← Verificador STATE.md V8
│   ├── ecc_backup.py                 ← Backup ECC diario
│   ├── hermes_backup.sh              ← Backup GitHub
│   ├── app_backup.sh                 ← Backup apps V8
│   ├── blog_publisher.py             ← Blog automático
│   └── trading_agent_watchdog.sh     ← Watchdog trading
│
└── evolution/
    ├── memory/
    │   ├── service_memory.json       ← Memoria por servicio
    │   ├── incidents.json            ← Incidentes
    │   ├── lessons.json              ← Lecciones aprendidas
    │   ├── autoheal_state.json       ← Circuit breaker
    │   └── archived_services.json    ← Servicios archivados
    │
    ├── metrics/
    │   ├── timeseries.jsonl          ← Serie temporal (15min)
    │   ├── heartbeats.json           ← Últimos latidos
    │   ├── events.jsonl              ← Log centralizado
    │   ├── health_checks.jsonl       ← Healthchecks HTTP
    │   └── benchmarks.jsonl          ← Benchmarks históricos
    │
    ├── market/                       ← Reportes Market Intelligence
    ├── reflections/                  ← Reportes de predictor y daily
    └── backups/                      ← Snapshots ECC
```

---

## 9. COMANDOS ÚTILES

```bash
# Ver estado de todos los scripts V7
python3 ~/.hermes/scripts/guardian.py
python3 ~/.hermes/scripts/health_monitor.py
python3 ~/.hermes/scripts/health_checker.py

# Ver resumen de memoria
python3 -c "
import sys; sys.path.insert(0, '$HOME/.hermes/scripts')
from memory_core import summary; print(summary())
"

# Ver heartbeats
python3 -c "import json; print(json.dumps(json.load(open('$HOME/.hermes/evolution/metrics/heartbeats.json')), indent=2))"

# Ver eventos recientes
tail -20 ~/.hermes/evolution/metrics/events.jsonl | python3 -m json.tool

# Dashboard
curl http://127.0.0.1:5050/api/system | python3 -m json.tool
curl http://127.0.0.1:5050/api/events | python3 -m json.tool

# Logs de servicios
journalctl -u hermes-dashboard --no-pager -n 30
journalctl -u leadcapture --no-pager -n 20

# Ver servicios activos
for s in hermes-gateway shop-hjazzi uropic shangrila estiloyvida logis nicolas leadcapture; do
  echo "$s: $(systemctl is-active $s)"
done
docker ps --filter name=n8n-n8n-1 --format 'n8n: {{.Status}}'
```

---

## 10. HISTORIAL DE CAMBIOS

### 2026-07-24 — Auditoría y reparación

**Bug crítico: el auto-sanador llevaba tiempo muerto.**
`auto_watcher.py` fallaba en *cada* ejecución (cada 5 min) con
`UnboundLocalError: cannot access local variable 'heal_state'`. La causa: la
variable se usaba en la línea 297 (rama "no hay servicios caídos") pero se
asignaba en la 306, después del `return`. Es decir, **fallaba justamente cuando
todo estaba bien**, que es el caso normal. Durante ese tiempo el ecosistema
monitoreaba pero no se auto-reparaba: si un servicio caía, nadie lo levantaba.
Corregido moviendo la carga de estado antes de la bifurcación. Verificado: exit
code 0.

**Benchmark Suite: 5/8 → 8/8 (Grado A).** Tres problemas:

| Check | Problema | Corrección |
|---|---|---|
| `healthcheck-bridge` | Apuntaba a `hermes-bridge` (5005), servicio inexistente → HTTP 000 | Renombrado a `healthcheck-gateway`, apunta a 8765 |
| `healthcheck-uropic` | Apuntaba al 5022, que es **estiloyvida**, no uropic → 404 | Corregido a 5010/health |
| `systemd-all-active` | Listaba 7 servicios fantasma (hjazzi-monitor, hjazzi-bot, cognitive-bot, cognitive-webhook, hermes-bridge, hermes-dispatcher, app-hjazzi) | Lista real de 11 servicios |

`response-time-simple` también medía el bridge muerto; ahora mide el gateway.

**Blog Publisher:** el estado `error` era residual, del fallo del 2026-07-23 a
las 04:32, anterior a la creación de `/var/www/hjazzi/blog/`. Ya publicó dos
artículos con éxito ese mismo día a las 18:48 y 18:55. No requirió cambios.

**Health Checker: dos bugs de la misma familia.** Apuntaba al gateway en 8080
(muerto → Connection refused) y a `nicolas` en 5015, que es el puerto de
`logis` — o sea, daba ✅ midiendo el servicio equivocado, un falso positivo.
Corregidos a 8765/health y 5016/ respectivamente. Pasó de 9/10 a **10/10**.

**Deriva documental corregida:** 17 jobs documentados → 13 reales; gateway 8080
→ 8765; 9 servicios → 12; `acta-core` y `hermes-upload` no estaban
documentados; aclarado que el scheduler es propio de Hermes y no el crontab
del sistema.

**Health Monitor: punto ciego cerrado.** No monitoreaba `hermes-dashboard`,
`hermes-upload` ni `acta-core` — tres servicios activos invisibles para el
auto-sanador: si caían, nadie los levantaba. Pasó de 9/9 a **12/12**.

**Nueva Capa 11 — Deep Diagnosis.** Primera capa con razonamiento LLM
(Claude Fable 5 vía OpenRouter). Ver sección 2. Registrada como job
`6eb1175358d8`, cada 15 min, no_agent. Fase 1: solo diagnostica.

**Nuevo: registro único + detector de deriva.** Tres de los cuatro bugs de esta
auditoría compartían causa raíz: cada capa codificaba su propia lista de
servicios y puertos, y se desincronizaban de la realidad sin avisar. El sistema
reportaba verde mientras chequeaba cosas equivocadas.

- `~/.hermes/config/services.json` — registro único de los 12 servicios
- `drift_detector.py` — compara el registro contra systemd, los puertos que
  realmente escuchan, los endpoints HTTP y los puertos codificados en las
  capas. Job `a2779af85ccd`, diario 5AM. Sale con código 1 si hay deriva.

Validado inyectando deriva a propósito: detecta puerto erróneo, health roto,
servicio no declarado y puerto fantasma en los scripts.

> **Pendiente:** las capas siguen leyendo sus listas codificadas; el detector
> avisa de la divergencia pero no la corrige. Migrarlas a leer del registro es
> el siguiente paso natural.

**Plantilla reutilizable** en `hermes-ecc/template/` — `bootstrap.sh` levanta la
arquitectura en un VPS nuevo autodetectando servicios y puertos vía cgroup.

---

## 10.1 HISTORIAL ANTERIOR (2026-07-23)

### Problemas resueltos

1. **Puerto 8080 duplicado** — http.server compitiendo con gateway → matado
2. **health_monitor veía 12 servicios fantasmas** → corregido a 9 reales
3. **auto_watcher en loop con 8 servicios inexistentes** → blocklist + circuit breaker reset
4. **STATE.md decía "13/13 activos" (mentira)** → actualizado con datos reales
5. **Blog Publisher caído** — faltaba directorio /var/www/hjazzi/blog/ → creado
6. **Guardian falsos positivos por BM** — tolerancia 28h→30h
7. **incidents/lessons con 5000+ líneas de ruido** → compactado
8. **leadcapture no instalado** → restaurado desde backup ECC
9. **Unit files de 6 servicios perdidos** → recuperados del snapshot auto-deployer
10. **Memory Core contando mal** — formato service_memory.json corregido

### Mejoras aplicadas (V8)

| Mejora | Archivo(s) | Propósito |
|---|---|---|
| Dashboard web | `/opt/hermes-dashboard/app.py` | Ver el estado del VPS desde el navegador |
| Alertas Telegram | `alerter.py` + integrado en `auto_watcher.py` | Notificaciones cuando algo se rompe |
| Healthcheck HTTP | `health_checker.py` | Verificar que las apps responden, no solo systemctl |
| Log centralizado | `event_logger.py` | Un solo archivo para todos los eventos |
| Memory Sanity | `memory_sanity.py` | Archivar servicios inactivos automáticamente |
| Backup de apps | `app_backup.sh` | No perder el código de las apps otra vez |
| Heartbeats en backups | `ecc_backup.py`, `hermes_backup.sh` | Saber si los backups realmente corren |
| State Verifier | `state_verifier.py` | Detectar cuando STATE.md no coincide con la realidad |

---

## 11. PRINCIPIOS DE DISEÑO

1. **Un script, una responsabilidad.** Cada capa hace una cosa y la hace bien.
2. **Sin base de datos.** Todo es archivos JSONL. Si se corrompe un archivo, se pierde ese dato, no todo el sistema.
3. **Sin dependencias externas.** Flask para el dashboard es la única excepción. Todo lo demás es Python estándar + curl.
4. **no_agent por defecto.** Los cron jobs no cargan un LLM. Son scripts ligeros
   que ejecutan en milisegundos. *La Capa 11 es la única excepción, y respeta el
   principio en espíritu: solo llama al modelo cuando el sistema determinista ya
   se rindió (circuit breaker abierto). En operación normal sale en ~130 ms sin
   gastar un token. El LLM es el último recurso, no el primero.*
5. **Heartbeats en todo.** Si un script no registra heartbeat, el guardian lo detecta.
6. **Eventos centralizados.** Todos los scripts escriben al mismo archivo events.jsonl.
7. **Auto-limpieza.** Los datos viejos se archivan o purgan automáticamente. La memoria no crece indefinidamente.
8. **Verificación cruzada.** health_monitor verifica systemctl, health_checker verifica HTTP. Si uno falla, el otro puede confirmar.

---

*Documento generado el 2026-07-23. Para la versión más actualizada, leer `~/.hermes/STATE.md`.*
