# Jobs on the cron tick and leased queue, not a Postgres worker

The repository's architectural decision, rationale and consequences.

# Jobs on the cron tick and leased queue, not a Postgres worker

**Status:** accepted · **Date:** 2026-11-13 · **Deviates from:** product ADR-03 (graphile-worker/pg-boss) · **Related:** [ADR-0005](0005-effect-boundary.md), [ADR-0006](0006-effect-persisted-queue.md)

**Enforcement:** `[lint]` only `platform/jobs/run.ts` imports `*.jobs` (D4) · `[test]` each job's Effect tests with `TestClock`.

## Decision [#decision]

A Worker has no long-running process for graphile-worker or pg-boss. BJJ Travel uses the template's one-minute cron tick (`platform/jobs/run.ts`) with Drizzle leases (ADR-0006) for:

| Event (ARD)      | Job                                                                                                         |
| ---------------- | ----------------------------------------------------------------------------------------------------------- |
| route.requested  | AI route: lease, call the model adapter with a 20 s budget, validate with rules, persist `route_suggestion` |
| proposal.sent    | translation + 3-day reminder                                                                                |
| window.published | match windows against academy alerts (and drop invisible instructors)                                       |
| checkin.closed   | split and schedule the D+2 payout                                                                           |
| window.ended     | remove from the map; purge itineraries 30 days later                                                        |
| record.objection | remove a public academy record within 24 h and blocklist it                                                 |

Retries are per job with leases; only idempotent reads retry automatically (`idempotentRead`). Cloudflare Queues or Workflows can replace a job later without changing its domain code, because jobs return `Effect` and the transport is the tick.

