0012 — Vercel Cron allowed for web-app-native schedules

Context

ADR-0003 established that every scheduled job runs on AWS EventBridge + Lambda under agents/. The stated reason: scheduled work at that time was entirely agent-side (scraping, SMS reminders, inbox polling). Per ADR-0001, agent work doesn't run on Vercel.

The metric snapshot cron is a new case that doesn't fit that mold. It reads the dashboard's own endpoints, reuses ~700 lines of in-app computation, and writes one row per property to DynamoDB once per day. Putting it on a Lambda would require either (a) duplicating the computation into the Lambda bundle, or (b) self-calling back into the web app over HTTP — adding a deploy pipeline and SSM secrets for a job that is fundamentally web-app telemetry.

Decision

Two scheduling mechanisms, one rule per category.

Category Mechanism Location Example
Agent work (scraping, SMS, inbox, browser-agent triggers) EventBridge + Lambda agents/<name>/ listings-sync, tour-reminder
Web-app-native telemetry, report generation, in-app cron Vercel Cron src/app/api/cron/<name>/ metric snapshot

The rule: if the scheduled job only reads and writes data the web app already has in-process — no browser automation, no external scraping, no SQS work — it can live on Vercel Cron. Everything else stays on EventBridge per ADR-0003.

What's on each today

Lambda + EventBridge (per ADR-0003):

Job Schedule Lambda
Subscription renewal rate(30 min) agents/outlook-subscription-renewer
Tour reminder rate(1 hour) agents/tour-reminder
Listings sync rate(15 min) agents/listings-sync
Application link one-shot on tour confirm agents/application-link
Work order pipeline rate(8 hours) agents/wo-pipeline

Vercel Cron (new):

Job Schedule Route
metric snapshot daily at 04:00 UTC src/app/api/cron/snapshot-metrics/route.ts

Consequences

Easier

Harder

Follow-up

Alternatives considered

Move metric snapshot to Lambda per ADR-0003. Rejected. Requires either duplicating 700+ lines of dashboard computation into the Lambda bundle, or having the Lambda HTTP-call back into the web app on every run. Either way the Lambda is doing nothing the Next.js app can't do natively.

Supersede ADR-0003 entirely. Rejected. The agent/web decoupling (ADR-0001) still holds. Agent-side scheduled work still belongs on Lambda. ADR-0003's core principle is correct for that category; this ADR just adds a narrow carve-out it didn't anticipate.

Cron-triggered SQS message → Lambda consumer. Rejected. Adds queue indirection for a job that is fundamentally "once a day, write one row per property." No backpressure or retry semantics needed.