fix(api): preserve terminal scheduled automation dispatch

AB
2mo ago
+1090-33

Summary

Fixes #6128.

A bounded scheduled occurrence could be accepted by QStash and then dropped because evaluation disabled the automation before delivery. The dispatch route returned 200, so no retry, failure callback, or run record followed.

Design

  • Evaluate marks the queued payload as terminal when the recurrence has no next occurrence.
  • After enqueueing, evaluation moves that occurrence to an occurrence-specific future reservation in nextRunAt, using a CAS on enabled and the original nextRunAt. This keeps the terminal row out of the due scan while QStash delivers it, without a schema migration.
  • Dispatch accepts a terminal message while the reservation matches, and disables the automation only after dispatchAutomation records an outcome. A user pause is still rejected by the enabled guard; a changed reservation is stale.
  • The reservation is derived from the original occurrence, so a delayed message cannot claim a later terminal occurrence for the same automation.
  • Terminal finalization failures return non-2xx for QStash retry. If delivery retries are exhausted, run-failed records dispatch_failed and closes the matching recurrence. Existing dispatched or skipped_offline outcomes are preserved.
  • No database migration.

Checks

  • bun test apps/api/src/app/api/automations/evaluate/route.test.ts apps/api/src/app/api/automations/dispatch/[id]/route.test.ts apps/api/src/app/api/automations/run-failed/route.test.ts — 11 passed.
  • bunx biome check on changed files — passed.
  • bun run lint — passed.
  • API typecheck is blocked by the workspace missing next and next/server dependencies.

Summary by CodeRabbit

  • Bug Fixes

    • Improved automation scheduling to prevent stale or duplicate terminal dispatches.
    • Ensured terminal automations are finalized or disabled safely after successful or failed delivery.
    • Preserved correct recurrence timing for one-time and recurring automations.
    • Added safeguards against concurrent state changes and outdated scheduling information.
  • Tests

    • Expanded coverage for terminal, paused, disabled, recurring, and failed automation scenarios.