The durable async substrate every later Epic depends on: long-running LLM calls (Epic 7's planning agent passes run 30-60 s and cannot live inside an HTTP request), scheduled work (Epic 5 notification digests, Epic 7 auto-suggest cadences), webhook fan-out (Epic 6 search indexing, Epic 7's GitHub PR-merged → mark-Subtask-done flow), and transactional email retries (Story 1.1's password-reset + Story 1.2's workspace invites currently fire synchronously and silently swallow provider failures). Ships the runtime + the patterns (idempotency, retries, dead-letter, replay) + the first real production job + the operator surface so failures surface to a human instead of disappearing.
Prerequisites: Story 1.1 ships the lib/email.ts abstraction the first real job (1.6.3) wraps; the password-reset + workspace-invite send-sites become the first job's callers (replacing today's synchronous fire-and-pray sends — captured as a finding in PRODECT_FINDINGS.md). Story 1.2 ships WorkspaceMembership, which 1.6.5's job-runs dashboard filters by (so workspace admins only see their workspace's runs). Story 1.5 ships AppLayout + Sidebar; the dashboard route in 1.6.5 hangs off the existing sidebar's "Settings" group.
pnpm install && pnpm dev; in a second terminal, npx inngest-cli dev.succeeded within ~3 seconds; the dev-console email body matches the invite template.FORCE_EMAIL_FAILURE_FOR=fail@example.test in the dev shell; invite fail@example.test → watch the run climb to attempts=3 then transition to failed; confirm the DLQ tab badge increments.succeeded run appears AND the DLQ row's "Replayed" column shows the timestamp.pnpm dev; confirm the dashboard still shows historical runs (they're DB-persisted, not in-memory).