Repository navigation
Add @corbits/cron: cron ticks delivered as mail (CL-8183) - #800
Merged
Merged
Conversation
Cron expression tests moved from the retired @corbits/workflow-schedule, plus a DB-gated ticker test proving a due row fires once, a not-yet-due row doesn't, and SKIP LOCKED means two concurrent tickers never double-fire the same row.
The vendored Interchange workflow-trigger grammar (vendor/intx/workflow/src/definition/triggers.ts) has no `schedule` trigger and no scheduler, so a saved cron expression can never launch a workflow natively. @corbits/cron is a small experimental bridge instead: a tenant saves a cron expression plus a mail (to/subject/body) on its own `cron.schedule` table, and `createCronTicker` polls for due schedules and delivers each as plain mail through the host's transport — a schedule-triggered workflow becomes a mail-triggered one addressed at itself. - Moves the 5-field cron parser out of the retired @corbits/workflow-schedule (deleted entirely; its only other consumer, @corbits/workflows' scheduleCronFromProjection, is deleted alongside it since nothing outside that package read the re-export). - One drizzle table, its own `cron` Postgres schema, FK'd to Interchange's native `tenant` table. - createCronTicker claims due rows with SELECT ... FOR UPDATE SKIP LOCKED so concurrent tickers never double-fire, and only the latest missed tick fires. - mountCron adds CRUD at /api/tenants/:tenantId/cron.
TheGreatAxios
force-pushed
the
cl-8183-cron
branch
from
September 17, 2026 21:59
87e1388 to
26f773a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The vendored Interchange workflow-trigger grammar
(
vendor/intx/workflow/src/definition/triggers.ts) has noscheduletrigger and no scheduler.
@corbits/cronis a small experimental bridgeinstead: a tenant saves a cron expression plus a mail
(
to/subject/body) on its owncron.scheduletable, andcreateCronTickerpolls for due schedules and delivers each as plainmail through the host's transport — a schedule-triggered workflow becomes
a mail-triggered one addressed at itself, so workflows keep the plain
mailtrigger they already have.src/cron.ts— the 5-field cron parser, moved unchanged from the nowretired
@corbits/workflow-schedule(which is deleted entirely).src/schema.ts— thecron.scheduletable (owncronPostgres schema,FK'd to Interchange's native
tenanttable) plus its migration,registered in
scripts/db-setup.tsalongside every other installedpackage.
src/ticker.ts—createCronTicker({ db, deliver, intervalMs }):claims due rows with
SELECT ... FOR UPDATE SKIP LOCKEDin onetransaction so two concurrent tickers never double-fire a row, and only
the latest missed tick fires (a schedule down for hours fires once on
recovery, not once per missed minute).
src/mount.ts—mountCron(app, { db, requireTenantMember }): CRUD at/api/tenants/:tenantId/cron.@corbits/workflow-schedule's only other consumer,@corbits/workflows'scheduleCronFromProjection(nativeScheduleTriggerparsing off a frozen projection), is deleted alongsideit — nothing outside that one file read it, and native schedule triggers
don't fire against the vendored grammar anyway.
packages/cronneeds anexplicit ruling added to
scripts/checks/no-product-tenancy.tsfor itsone table, same as chat/webhook-triggers/notify/etc.
Suggested hub mount (not wired up — apps/hub/src/index.ts intentionally untouched)
Test plan
bun run typecheckbun run lintbun run fmtbun run check:structuralbun test packages/cron(unit + DB-gated, against local Postgres)bun run check)