ir.schedule, and dispatches each one via the existing pipeline
run route. Schedules saved by the Schedule tab
finally fire.
Endpoint
x-cron-secret: $CRON_SECRET header.)
Auth: Fails closed when CRON_SECRET env is unset (matches the
contract of /api/v1/cron/approvals-expire and other cron routes).
Response:
Per-kind decision logic
Implemented as the pureselectDuePipelines(inputs, now) helper at
lib/flow/schedule/due-pipelines.ts.
| Kind | ”Due” condition |
|---|---|
| cron | cron-parser yields any occurrence in (lastRunAt, now] (most recent wins) |
| interval | now - lastRunAt >= minutes * 60_000 |
| rrule | RRule.between(lastRunAt, now) returns ≥ 1 occurrence |
lastRunAt = null (pipeline never ran): treated as 24h lookback to
avoid stampeding old, never-run pipelines that were created days/weeks
ago.
Bad schedules (malformed cron, malformed rrule) silently skip the
pipeline — parsePipelineIR would have rejected the IR save earlier
anyway.
How to fire it (every-minute external trigger)
The scheduler tick is not self-firing — it’s a stateless endpoint expecting an external orchestrator. Options:| Setup | Approach |
|---|---|
| Self-hosted (k8s) | A CronJob resource hitting /api/v1/cron/flow-scheduler-tick every minute |
| Self-hosted (Linux box) | * * * * * curl -fsSL -H "x-cron-secret: $CRON_SECRET" https://your-app/api/v1/cron/flow-scheduler-tick |
| AWS | EventBridge Schedule → API destination — playground does NOT enable this by default (project rule: no new AWS resources without authorization) |
| Vercel | A Vercel Cron Job (vercel.json crons array) hitting the endpoint |
Idempotency
- Calling the endpoint multiple times in one minute does not double-fire
a pipeline — once a run is dispatched,
lastRunAtadvances to the fire time, and the next tick’s(lastRunAt, now]window excludes the same occurrence. - Per-tick fanout is O(n) over all IR-native pipelines + 1 IR fetch each. Phase 6.6 hardening adds an index + a “schedule_only” projection.
Honest residuals (carried into Phase 6.6)
- Per-tenant scheduler dashboard showing miss / lag / failed dispatches
trigger.file_arrival.deadline.{windowMinutes, onMiss}enforcement — currently only pure schedule kinds fire; deadline windows on file_arrival triggers are not yet checked- Lock-free guarantee under concurrent ticks — current impl assumes the external orchestrator doesn’t double-fire the endpoint; an advisory PG lock around the sweep would close the gap