The three tables the engine runs on, as a Prisma migration, following motir-core's schema conventions.
job_event — the emitted event log. One row per sendEvent; a dispatcher fans it out to one job_queue row per subscribing job. Carries the event name, its payload, received_at, and the workspace_id when the payload has one.job_queue — one row per RUN: the job id, the triggering event, run_at (when it becomes claimable), attempts, state, the claiming worker and its lease. This is the table the worker's claim loop reads.job_step — memoized step results, keyed (run_id, step_id) with a unique index. This is what makes step.run idempotent across retries and restarts, and it is the whole reason the 84 existing call sites do not have to change.@relation on both sides with matching onDelete / onUpdate, never created in raw SQL alone — a scalar column with a raw-SQL FK puts the schema graph and the migration-built database in permanent drift and makes the next migrate dev propose dropping it.job_run / job_run_dlq already use (MOTIR-63). workspace_id is NULL for system jobs, and the policy must handle that case rather than assuming it is always present.@@index on the same model — the differ pairs indexes by column list and reports a permanent spurious RENAME. Give any partial index a column list of its own, chosen from what its query actually filters on.prisma migrate diff --from-schema … --to-config-datasource --exit-code reports no drift after a from-empty replay.(run_id, step_id) is unique on job_step — asserted by a test that tries to insert a duplicate, not by reading the schema.prisma/schema.prisma — where the models golib/jobs/dlq.ts and the job_run / job_run_dlq models — the existing ledger tables and their RLS, the pattern to mirrormotir-core/CLAUDE.md § Migrations — the @relation and partial-index rules above