# 0025 — Catálogo de automatizaciones cron + scaffolder ## Objetivo Crear un directorio `crons/` como catálogo central de automatizaciones nombradas, y un conjunto de scripts en `dev-scripts/cron/` para crear nuevas automatizaciones, listarlas y aplicarlas a agentes sin editar YAML a mano. Evolución directa de la infraestructura creada en el issue 0005. ## Contexto - `shell/cron/` ya implementa el scheduler con `send_message` y `llm_prompt` (issue 0005) - Las automatizaciones se definen en cada `agents//config.yaml` bajo `schedules:`, lo que las dispersa y dificulta reutilizarlas entre agentes - No hay forma de crear una nueva automatización sin editar YAML a mano y conocer la estructura - No existe un catálogo centralizado ni scripts de gestión - Depende de: issue 0005 (completado) ## Arquitectura ``` crons/ NEW — catálogo de automatizaciones nombradas good-morning/ schedule.yaml NEW — spec (description, cron, action, output_room por defecto) prompts/ message.md NEW — plantilla de mensaje daily-summary/ schedule.yaml NEW prompts/ prompt.md NEW dev-scripts/cron/ NEW — herramientas de gestión new.sh NEW — scaffolder interactivo list.sh NEW — listar automatizaciones con descripción apply.sh NEW — añadir automatización a config de agente shell/cron/scheduler.go MODIFY — añadir Fire(name) para disparo manual en tests shell/cron/actions.go MODIFY — pequeñas mejoras si surgen al escribir ejemplos ``` ### Patrón pure core / impure shell - `pkg/` — sin cambios (no hay lógica pura nueva) - `shell/cron/` — modificación mínima: añadir `Fire(ctx, sc)` para testing manual - `crons/` — datos puros (YAML + Markdown), sin código Go - `dev-scripts/cron/` — shell scripts impuros (leen/escriben filesystem, parchean YAML) ### Convención de `crons//schedule.yaml` ```yaml # Metadata name: good-morning description: "Saludo de buenos días en una sala" # Schedule por defecto (el agente puede sobreescribir) default_cron: "0 9 * * *" # Acción action: kind: send_message # send_message | llm_prompt template: prompts/message.md # relativo a la carpeta de la automatización # Sala por defecto (opcional; el agente puede sobreescribir con output_room) default_output_room: "" ``` Este archivo es solo **documentación + template**. El agente lo referencia en su `config.yaml` usando la sección `schedules:` habitual; `apply.sh` automatiza ese paso. ## Tareas ### Fase 1: Estructura `crons/` y automatizaciones de ejemplo - [ ] **1.1** Crear `crons/` con un `README.md` que explique la convención - [ ] **1.2** Crear `crons/good-morning/schedule.yaml` + `prompts/message.md` (ejemplo `send_message`) - [ ] **1.3** Crear `crons/daily-summary/schedule.yaml` + `prompts/prompt.md` (ejemplo `llm_prompt`) ### Fase 2: Scripts de gestión en `dev-scripts/cron/` - [ ] **2.1** `dev-scripts/cron/new.sh` — scaffolder interactivo: - Pregunta: nombre, descripción, tipo (`send_message` o `llm_prompt`), cron expression - Crea `crons//schedule.yaml` y el archivo de prompt/mensaje vacío - Imprime el bloque YAML listo para copiar en `config.yaml` - [ ] **2.2** `dev-scripts/cron/list.sh` — lista todas las carpetas bajo `crons/` con nombre y descripción extraída del `schedule.yaml` - [ ] **2.3** `dev-scripts/cron/apply.sh ` — añade la entrada `schedules:` a `agents//config.yaml` con los valores por defecto del `schedule.yaml`. Usa `yq` si está disponible; en caso contrario imprime el bloque YAML para copiar a mano ### Fase 3: Mejora menor en `shell/cron/` - [ ] **3.1** Exportar `Fire(ctx context.Context, sc config.ScheduleCfg)` en `scheduler.go` para poder disparar un schedule en tests o desde CLI sin esperar al cron - [ ] **3.2** Actualizar `scheduler_test.go` para usar `Fire` en lugar de `@every 100ms` donde sea posible (reduce tiempo de test) ### Fase 4: Tests - [ ] **4.1** Test de `Fire` para `send_message` inline - [ ] **4.2** Test de `Fire` para `llm_prompt` - [ ] **4.3** Verificar que `go test -tags goolm ./shell/cron/...` pasa sin regresiones ### Fase 5: Cleanup y docs - [ ] **5.1** Añadir entrada `crons/` en la tabla de estructura de `CLAUDE.md` - [ ] **5.2** Añadir `dev-scripts/cron/` en la misma tabla - [ ] **5.3** Mención en `dev-scripts/agent/README.md` o crear `dev-scripts/cron/README.md` --- ## Ejemplo de uso ```bash # Crear una nueva automatización ./dev-scripts/cron/new.sh # → Nombre de la automatización: weekly-report # → Descripción: Resumen semanal del equipo # → Tipo de acción [send_message/llm_prompt]: llm_prompt # → Cron expression [default: 0 9 * * 1]: 0 9 * * 1 # ✓ Creado: crons/weekly-report/schedule.yaml # ✓ Creado: crons/weekly-report/prompts/prompt.md # # Añade esto a agents//config.yaml: # schedules: # - name: weekly-report # cron: "0 9 * * 1" # output_room: "!ROOM:server.com" # action: # kind: llm_prompt # template: "crons/weekly-report/prompts/prompt.md" # Listar automatizaciones disponibles ./dev-scripts/cron/list.sh # → good-morning send_message "0 9 * * *" Saludo de buenos días # → daily-summary llm_prompt "0 18 * * *" Resumen diario del equipo # → weekly-report llm_prompt "0 9 * * 1" Resumen semanal del equipo # Aplicar a un agente (parchea config.yaml automáticamente) ./dev-scripts/cron/apply.sh good-morning assistant-bot # → Añadido schedule 'good-morning' a agents/assistant-bot/config.yaml # → Edita output_room en config.yaml para apuntar a la sala correcta ``` ## Decisiones de diseño - **`crons/` como catálogo de datos, no de código**: Los archivos `schedule.yaml` son solo documentación + template. No hay un registry Go nuevo; el scheduler sigue leyendo de `config.yaml` como hasta ahora. Esto evita añadir un pattern nuevo al proyecto. - **`apply.sh` opcional**: Si `yq` no está disponible, el script imprime el bloque YAML para copiar a mano. Sin dependencias obligatorias. - **`Fire()` en lugar de cron real en tests**: Los tests actuales usan `@every 100ms` y duermen 350ms. `Fire()` los hace deterministas e instantáneos. - **No registry Go para crons**: Añadir un registry compilado (como `cmd/launcher`) para crons sería over-engineering. La gestión vía shell scripts es suficiente y más flexible. ## Prerequisitos - Issue 0005 completado (scheduler en `shell/cron/` — ya está) ## Riesgos - **`yq` no disponible en el entorno**: `apply.sh` cae back a imprimir el bloque YAML, nunca falla. Sin riesgo real. - **Paths relativos en `schedule.yaml`**: El campo `template` en el YAML es relativo a la raíz del proyecto. Documentar claramente en el `README.md` del catálogo. - **Divergencia entre catálogo y config del agente**: Si alguien edita `schedule.yaml` después de aplicarlo, el agente no se actualiza. Es intencional — `apply.sh` es un helper de scaffolding, no sync continua.