1.6.6 Story-level E2E: real-failure → DLQ → operator replay → success path (closes the Story)
Estimate: 18m · Depends on: 1.6.5
The Story-closing E2E proves the runtime + the patterns + the dashboard hold together end-to-end against a forced failure. Same shape as Story 1.5.6 — a Playwright spec that drives the user-visible flow, plus a Vitest integration spec for the cross-job invariants.
Spec at tests/e2e/jobs-flow.spec.ts covers:
- Happy-path workspace invite: sign in as workspace owner → invite a new email → assert the invite-acceptance email is enqueued (visible in the dashboard's "Recent runs" tab as a
succeededrow); the dev-console email provider logs the email body. - Forced-failure path: a Playwright fixture flag makes
lib/email.ts's dev-console provider throw deterministically for a specific recipient email pattern; invite that email → confirm the run appears asfailedwith attempts climbing to 3 → confirm a row lands in the DLQ tab → confirm the DLQ tab badge increments. - Replay path: clear the failure flag → click "Replay" on the DLQ row → confirm a new
job_runrow appears assucceeded→ confirm the DLQ row shows a non-nullreplayed_at→ confirm the email body now logs to the dev console. - Cross-workspace isolation: as a member of workspace A, navigate to
/settings/workspace/jobswhile workspace B is active → confirm zero runs visible even though workspace A has many. This catches RLS misses the Vitest tests can't (they run with a single connection). - Role gating: as a non-owner member of a workspace, the Replay button is disabled with a tooltip explaining the gate.
- Empty-state path: a fresh workspace with no job runs shows the documented empty state for both tabs.
Vitest integration spec at tests/jobs/integration.test.ts covers the cross-cutting invariants that don't surface via the browser:
- The
system.daily-health-checkscheduled job, manually fired via the in-process harness, writes the expected synthetic-eventjob_runrow. - Idempotency: firing the same
email.sendevent twice with the same idempotency key results in exactly onejob_runrow and exactly onesendEmailinvocation. - The DLQ replay does NOT bypass the idempotency window (a replay within 24h is a no-op against the original idempotency key — this is the "replay-after-fixing-the-underlying-bug-but-the-key-is-still-deduped" trap that real ops surfaces hit; document the workaround inline with the test).
Story-level verification recipe (manual, ≤12 minutes): pull main; pnpm install && pnpm dev + npx inngest-cli dev; walk the spec's scenarios interactively in the browser, spot-checking that the Inngest dev UI shows the same runs the dashboard does (the two surfaces should agree on every visible run).
If any scenario fails: fix in this Subtask if it's a Story-level regression; log a finding if it points at a deeper service-layer issue (per mistake #27).
Acceptance criteria
tests/e2e/jobs-flow.spec.tscovers every bullet in the scenario list; each scenario is its owntest()block.- Forced-failure scenario uses a Playwright fixture flag to make
lib/email.ts's dev-console provider throw deterministically; the flag scope is per-spec, not global. - Replay scenario asserts the DLQ row's
replayed_atchanges from null to a timestamp, AND a freshsucceededjob_run appears. - Cross-workspace isolation scenario uses the existing two-workspace fixture from Story 1.2; asserts zero runs visible when active workspace doesn't match the run's workspace.
- Role-gating scenario reuses the existing owner/member fixture pair; verifies the disabled state + tooltip.
- Vitest integration spec at
tests/jobs/integration.test.tscovers: scheduled job firing; idempotency dedup across duplicate sends; DLQ replay vs idempotency-window interaction (with the documented workaround). - Story-level verification recipe reproduces locally in ≤12 minutes (includes the Inngest dev-server startup time).
- All quality gates green; existing E2E suite (auth + workspace-flows + projects-flow + work-items-isolation + shell-a11y + shell-keyboard + shell-flows + jobs-dashboard) stays green; the new spec runs alongside without flake.
- Any cross-Subtask issue surfaced during verification logged in PRODECT_FINDINGS.md.
Context refs
tests/e2e/workspace-flows.spec.ts+jobs-dashboard.spec.ts— existing patterns to mirrortests/e2e/_helpers/db-reset.ts+email-capture.ts— the reset + email-inspection helpers- The full jobs stack (
lib/jobs/*, the serve route, the dashboard page, the email job) - The Story 1.2 two-workspace fixture pattern; the Story 1.5 a11y + keyboard specs (to extend, not duplicate)