# Scheduler — Cron expression parsing, matching, scheduling

Tag: `scheduler`. Grupo de funciones para trabajar con expresiones cron: parsear, validar coincidencias, calcular proximo run, traducir a lenguaje humano. Lo consume `apps/dag_engine` + `cpp/apps/dag_engine_ui` (tab Schedule). Crece a medida que se anaden nuevos formatos (Quartz, k8s CronJob, etc.).

Filtro MCP: `mcp__registry__fn_search query="" tag="scheduler"`.

## Funciones del grupo

| ID | Firma corta | Que hace |
|---|---|---|
| [parse_cron_expr_go_core](../../functions/core/parse_cron_expr.md) | `ParseCronExpr(expr string) (CronSchedule, error)` | Parsea expresion cron de 5 campos a struct expandido. Soporta `*`, rangos, listas, pasos y aliases `@hourly/@daily/@weekly/@monthly/@yearly`. |
| [cron_match_go_core](../../functions/core/cron_match.md) | `CronMatch(s CronSchedule, t time.Time) bool` | True si un instante coincide con un schedule. Compara los 5 campos. |
| [next_cron_time_go_core](../../functions/core/next_cron_time.md) | `NextCronTime(s CronSchedule, after time.Time) time.Time` | Proxima ejecucion despues de un instante. Salta minuto a minuto. Zero time si no hay match en 366 dias. |
| [cron_explain_go_core](../../functions/core/cron_explain.md) | `CronExplain(expr string) string` | Traduce expresion cron a frase humana corta (`"every 15 minutes"`, `"daily at 09:00"`, `"weekdays at HH:MM"`). |

## Ejemplo canonico end-to-end

Trabajar con un schedule completo: parsear, calcular proximo run, explicarlo al usuario.

```go
package main

import (
    "fmt"
    "time"
    "fn-registry/functions/core"
)

func main() {
    expr := "*/15 * * * *"

    // 1. Parsear
    sched, err := core.ParseCronExpr(expr)
    if err != nil {
        panic(err)
    }

    // 2. Proximo run
    next := core.NextCronTime(sched, time.Now())
    fmt.Printf("next run: %s\n", next.Format(time.RFC3339))

    // 3. Explicacion humana
    fmt.Printf("schedule: %s (%s)\n", expr, core.CronExplain(expr))
    // -> schedule: */15 * * * * (every 15 minutes)

    // 4. Verificacion puntual
    if core.CronMatch(sched, time.Now()) {
        fmt.Println("would trigger now")
    }
}
```

## Fronteras del grupo

NO cubre:
- Persistencia de schedules (vive en BD de quien lo use: `apps/dag_engine/store/`).
- Spawn/wait de procesos (eso es `process_*_go_infra`).
- Sintaxis Quartz extendida con `?` o anos. Solo soporta los 5 campos estandar + aliases `@hourly/@daily/@weekly/@monthly/@yearly`.
- Timezones — todo se evalua en `time.Local` del proceso. Si necesitas TZ explicita, parsea fuera y traduce.
- Sintaxis especial de Kubernetes CronJob (segundos opcionales en el primer campo).
- DAG orchestration / DAG runs (consume estas funciones via `apps/dag_engine`).

## Prerequisitos

- Go stdlib. Sin dependencias externas en ninguna funcion del grupo.
- Tipo compartido `cron_schedule_go_core` (struct con `Minute/Hour/Day/Month/Weekday` como `[]int` expandidos).

## Notas

- `cron_match` esta tageada `pendiente-usar` — sin consumidores actuales aparte del propio paquete. Si se incorpora a `next_cron_time` como optimizacion -> quitar tag.
- `cron_explain` mantiene contrato simple: si el patron no encaja en los reconocidos, devuelve el expr crudo sin error. Apto para UI fallback.
- Para anadir nueva funcion al grupo: anadir tag `scheduler` al `tags:` del frontmatter, anadir fila a la tabla arriba, actualizar `N` en `docs/capabilities/INDEX.md`.

## Consumidores actuales

- `apps/dag_engine` — `parse_cron_expr_go_core`, `next_cron_time_go_core` (+ `cron_ticker_go_infra` adyacente).
- `cpp/apps/dag_engine_ui` — tab Schedule consume `/api/dags` expuesto por dag_engine; via backend usa estas funciones.
- Candidato futuro: `cron_explain_go_core` en `dag_engine` `/api/dags` (anadir campo `schedule_human` al JSON response) para que la UI no tenga que duplicar logica.
