The 2026-07-06 prod outage was a missed manual migration: the 0002
job_events migration never ran against prod, so the worker crash-looped
on every job. Make the deploy pipeline own it:
- railway.json sets `npm run db:migrate` as the pre-deploy command.
Railway runs it with the service's env before starting the new
deployment; a failed migration fails the deploy and the old version
keeps serving.
- runMigrations() now serializes on a Postgres advisory lock so api and
worker pre-deploys firing off the same push can't race Drizzle's
journal writes.
- DEPLOY.md: document the automatic path; keep the manual command for
first-time setup.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SERVICE.md documents the /events polling endpoint, the event persistence
model, the in-memory backend's enqueue-202 contract, and the app-preview
route; DEPLOY.md notes the job_events migration and that db:migrate is
journal-tracked/idempotent (with the manual-psql alternative).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>