CreaRack-SL

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 un CronsReport completo.
  • Tipos públicos: CronsEnv, CronGroup, CronSource, CronStatus, CronsReport.
  • fetchLatestRun(env, workflowId) — consulta GitHub Actions API con timeout 5 s y fallback empty ante cualquier error o ausencia de GH_PAT.

IDs de workflows GHA verificados en s82:

WorkflowID
bib-reindex-ts.yml265753720
bib-reindex-claude-method.yml279618390
wiki-drift-check.yml276707608
wiki-translate.yml276287061
wiki-weekly-report.yml265712084
sync-cascade-detect.yml281534648

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 si row.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ónColor
status === 'failure'var(--danger) rojo
lastRun === nullvar(--fg-dim) gris
Antigüedad > umbral de grupovar(--warning) naranja
Antigüedad ≤ umbral de grupovar(--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]]