Crons Health — Monitorización de los 12 crons del workspace en el Pulse
Crons Health — Monitorización de los 12 crons del workspace en el Pulse
Resumen
Implementa el sistema completo de monitorización del estado de los 12 crons del workspace CreaRackSL en la tarjeta “Crons del sistema” de la banda 4 del Pulse (PulsePanel.tsx). Introducido en el commit 009ca75 (s82, 2026-05-24), cierra el pendiente de s81 en el que 9 de 12 filas mostraban ”—” al carecer de datos reales.
Tras esta feature, 11 de 12 crons tienen lastRun real. El cron pg_dump CreaRack-Pro permanece con ”—” honesto por no tener telemetría accesible desde el workspace (Regla 13: sin fakes).
Motivación (s81 → s82)
Antes de s82, CronsHealthCard recibía solo data.bibliotecario (los 3 crons del Bibliotecario con datos reales) y construía el resto de filas con lastRun: null hardcodeado. Los 9 crons externos (Reindex, Wiki, Sync, Backups) mostraban ”—” con el comentario “Los crons con «—» esperan al endpoint /biblioteca/crons”.
s82 completa ese endpoint y conecta la tarjeta a él.
Arquitectura
PulsePanel.tsx
└── fetch('/biblioteca/crons') cada 60s (best-effort)
│
▼
functions/biblioteca/crons.ts (CF Access · browser)
functions/api/biblioteca/crons.ts (Bearer · server-to-server)
│ ambos llaman a:
▼
functions/_lib/crons.ts
└── buildCronsReport(env: CronsEnv): Promise<CronsReport>
├── D1: bib_wiki_log → Bibliotecario ingest/curate/lint
├── D1: bib_nodes (MAX .py) → Reindex AST Python
├── D1: activity_log → DR Backups workspace
└── GitHub Actions API (×6) → workflows reindex-ts, reindex-cm,
wiki-drift, wiki-translate,
wiki-weekly, sync-cascade
Componentes nuevos
functions/_lib/crons.ts — Builder (299 LOC)
Biblioteca compartida que reúne los datos de todas las fuentes. Exporta:
buildCronsReport(env)— función principal. Lanza todas las consultas en paralelo (Promise.all) y devuelve unCronsReportcompleto.- Tipos públicos:
CronsEnv,CronGroup,CronSource,CronStatus,CronsReport. fetchLatestRun(env, workflowId)— consulta GitHub Actions API con timeout 5 s y fallbackemptyante cualquier error o ausencia deGH_PAT.
IDs de workflows GHA verificados en s82:
| Workflow | ID |
|---|---|
bib-reindex-ts.yml | 265753720 |
bib-reindex-claude-method.yml | 279618390 |
wiki-drift-check.yml | 276707608 |
wiki-translate.yml | 276287061 |
wiki-weekly-report.yml | 265712084 |
sync-cascade-detect.yml | 281534648 |
functions/biblioteca/crons.ts + functions/api/biblioteca/crons.ts — Handlers (13 LOC cada uno)
Handlers CF Pages Functions que simplemente llaman a buildCronsReport(env) y devuelven Response.json con Cache-Control: private, max-age=60. Ver [[entity—biblioteca—endpoint—crons]] para detalles de auth.
src/components/biblioteca/PulsePanel.tsx — CronsHealthCard (refactor)
Antes: recibía { bibliotecario: BibliotecarioActivity } y construía las filas de los 9 crons externos con lastRun: null hardcodeado.
Después: recibe { report: CronsReport | null }. Mientras report es null muestra un spinner “Cargando estado de los crons…”. Cuando llega el report:
- Las filas se renderizan desde
report.crons(datos del servidor, no hardcoded). - El dot de color usa
cronDotColor(row): rojo sirow.status === 'failure', gris si sin datos, naranja/verde según antigüedad vs umbral del grupo. - Los crons con
detailUrl(fuente GHA) renderizan su label como<a href target="_blank">enlazando al run específico. - El tooltip del dot incluye
último run: <status>cuando hay status. - El footer ya no dice “Los crons con «—» esperan al endpoint…” sino una nota honesta sobre
pg_dump PROD.
Fetch en PulsePanel: fetch('/biblioteca/crons') en el useEffect junto al resto de fetches del Pulse. Es best-effort: si falla, setCrons(null) no se llama (queda en null) y la tarjeta muestra el estado de carga. Refresh automático cada 60 s (intervalo compartido del Pulse).
Criterios de color del dot
| Condición | Color |
|---|---|
status === 'failure' | var(--danger) rojo |
lastRun === null | var(--fg-dim) gris |
| Antigüedad > umbral de grupo | var(--warning) naranja |
| Antigüedad ≤ umbral de grupo | var(--success) verde |
Umbrales por grupo:
- Bibliotecario: 24 h
- Reindex: 12 h
- Wiki: 48 h
- Sync: 24 h
- Backups: 36 h
Decisiones de diseño
Sin tabla de cache (hot-cache)
Se eligió no crear una tabla D1 propia para cachear el estado de los crons. El endpoint consulta en cada request con timeouts cortos y Promise.all. Cache-Control: private, max-age=60 evita peticiones redundantes desde el mismo navegador. Si las fuentes tardan o fallan, la tarjeta degrada limpiamente a ”—”.
Dos endpoints gemelos
/biblioteca/crons (CF Access) y /api/biblioteca/crons (Bearer) son idénticos funcionalmente. La separación sigue el patrón del workspace de tener rutas /api/* para uso programático (agentes, scripts) y rutas sin prefijo para el navegador.
pg_dump honesto
El cron pg_dump CreaRack-Pro corre en un servidor Hetzner de PROD sin escribir telemetría en ninguna tabla D1 del workspace. Se devuelve lastRun: null con un note explicativo en lugar de inventar un timestamp o leer una fuente no canónica. Esto sigue la Regla 13 del proyecto.
Estado actual (post-s82)
- 11/12 crons con datos reales.
- 1/12 sin telemetría (
pgdump-pro) — estado conocido, no un bug. - Sin migración D1 requerida (las tablas consultadas ya existían).
Véase también
- [[entity—biblioteca—endpoint—crons]]
- [[entity—biblioteca—service—crons-builder]]