Bun.cron — first-class scheduled jobs convention
Accept. app/jobs/ is the natural parallel to app/agents/ and app/tools/ — closes the gap where spec 10 mentions cron callers without defining them.
Review verdict (2026-05-24)
Accept. app/jobs/ is the natural parallel to app/agents/ and app/tools/ — closes the gap where spec 10 mentions cron callers without defining them.
Scope adjustments:
- Singleton across replicas: deferred to Phase 3. Phase 2 ships without it — multi-instance deploys fire each job N times. Documented loudly. The
singleton: true + Redismechanism becomes a follow-up RFC once@patties/cache(usingBun.RedisClient) lands. - Edge-target cron: moved out of spec 12 and into deploy plugins.
@patties/deploy-cloudflaretranslatesapp/jobs/*intowrangler.toml [triggers];@patties/deploy-vercelemitsvercel.json crons; etc. The framework core stays neutral — it just exposes the job inventory to plugins via a build hook. - Tests: cron handler unit tests invoke the exported default function directly with a mocked
AiContext(no need to fake the clock; that's the scheduler's job, not the handler's).
Summary
Bun ships Bun.cron(expr, handler) returning a Bun.CronController with .stop() and .list(). Patties should adopt a app/jobs/**/*.ts convention where each file exports a default handler and a schedule string; the server wires them through Bun.cron on boot.
Motivation
10-agents-and-tools mentions "cron jobs" as a call site for createAiContext but no spec actually defines how cron is declared, wired, or tested. Users currently have no answer for "where does a recurring task live in a Patties app?" Bun's native cron removes the need for node-cron, croner, or external schedulers, and integrates with the same process lifecycle as Bun.serve.
Proposal
- 13-conventions: introduce
app/jobs/convention with file shape: ``ts export const schedule = "*/5 * * * *"; export default async (ctx: JobContext) => { /* ... */ };`` - 01-server: on boot, scan
app/jobs/viaBun.Glob, register each viaBun.cron(schedule, handler), retain theCronControllerfor graceful shutdown. - 10-agents-and-tools: cron handlers receive an
AiContextcreated viacreateAiContext— same shape as request handlers. - 09-plugins: deploy plugins consume the
app/jobs/*inventory via a build hook and emit vendor-native cron triggers (wrangler.toml [triggers],vercel.json crons, Deno Deploy queued tasks, etc.). The framework core stays vendor-neutral.
Trade-offs
- Multi-instance deploys (reusePort, edge replicas) will fire each job N times unless we add a lock. Document this and provide a
singleton: trueoption that requires a Redis client. - Time-zone semantics:
Bun.cronis process-local TZ; we should require explicittz:in the export.
Open questions
- Should
schedulebe a single string orstring | string[]? - How do tests fake the clock? Bun's mocks (14-testing) don't cover
Bun.crondirectly.