Production runs its whole job substrate on Inngest's Hobby plan — 5 concurrent executing steps, read off the billing dashboard on 2026-08-23 (MOTIR-3406). Every work-item/transitioned event has four consumers, so one status change occupies 4 of those 5 slots and any cascade over two or more items oversubscribes the entire account. That is the measured cause of the 18-20 s fast-lane lag in docs/decisions/job-lane-occupancy.md §6, and it reproduces at a single user closing one story.
The next Inngest tier is $99/month. This epic takes the third road: run the queue ourselves, on the Postgres and the long-lived Fly machines we already pay for.
lib/jobs/client.ts, lib/jobs/defineJob.ts and app/api/inngest/route.ts — and an ESLint no-restricted-imports rule keeps it that way. All 24 job definitions are written against our own defineJob / sendEvent.Dockerfile ends CMD ["node", "server.js"]: motir-core is a long-lived process on Fly. maxDuration = 300 in the serve route was Vercel's ceiling, and the stepped-supervisor architecture exists only to survive it. So this migration deletes machinery rather than porting it.| clause | owner |
|---|---|
| a Postgres-backed queue replaces Inngest | the engine story, then the three cutover stories, then the retirement story |
| the serverless-shaped stepping goes with it | the supervisor story, which collapses the stepped loops into ordinary while loops |
IN: motir-core only, and only the job substrate beneath defineJob / sendEvent.
OUT, explicitly:
motir-ai is untouched — it has no Inngest dependency at all (grep -rl inngest motir-ai/src returns nothing; its package.json names it zero times). This epic is single-repo.motir-core imports inngest or inngest-cli, and neither package is in package.json.job_run ledger, the DLQ and /settings/workspace/jobs working exactly as before.lib/jobs/latencyBudget.ts is re-measured after cutover and the new figure recorded beside the Inngest baseline it replaces.docs/jobs.md and docs/decisions/job-lane-occupancy.md describe the substrate that actually runs, with every Inngest-specific claim removed or re-stated.docs/decisions/job-lane-occupancy.md — the measurement that motivates this, and the 5-step ceilinglib/jobs/defineJob.ts · lib/jobs/client.ts · app/api/inngest/route.ts — the three-file seamlib/jobs/registry.ts · lib/jobs/definitions/ — the 24 jobs to movelib/jobs/latencyBudget.ts — the budget this epic is expected to move