⚠️ AMENDED 2026-08-25T23:0x — three of this card's five acceptance criteria were ALREADY BUILT when it was filed
Amended on the record by
motir run MOTIR-3495, before any code was written, underrun.md's adoneblocker that merged AFTER the card OUTRANKS the card rule. Nothing was silently dropped — every original criterion is disposed of in the table below with the evidence that disposed of it.MOTIR-3459 — "Event-level IDEMPOTENCY on the engine" — was already
implementedwhen this card was created. It sits under the SAME epic (MOTIR-3413), it was created at19:09:05Z, and its commitf96b61adlanded at21:51:56Z— 47 minutes BEFORE this card was filed at22:39:06Z. The filing pre-flight verified the defect againstorigin/main(where it is still true, becausef96b61adis unmerged) and never searched the PLAN, where the fix already had a card.This card's own body asked for exactly that check and it was not run: "Re-grep before designing the fix — a wave card may have added a constraint."
Measured on parent/MOTIR-3415-event-jobs-cutover @ 6aa9d502 (local, unpushed — no PR yet).
| # | Original criterion | Verdict | Evidence |
|---|---|---|---|
| 1 | The engine enforces the option, or it is impossible to declare on an engine-routed job | DELIVERED — both arms | lib/jobs/engine/idempotency.ts (new, 70 lines): resolveIdempotencyKey at enqueue, plus parseIdempotencyTemplate which THROWS at registration on any template but event.data.<field>. Dedup is a PARTIAL UNIQUE index job_queue_job_idempotency_key ON job_queue (job_id, idempotency_key) WHERE idempotency_key IS NOT NULL, P2002 treated as "already enqueued" |
| 2 | A test proves the runtime drop, not the wiring | DELIVERED | tests/jobs/engine-idempotency.test.ts, 205 lines — including a concurrent duplicate against real Postgres, and a job declaring no template asserted unaffected |
| 3 | The dedup window is stated explicitly in code | DELIVERED | dispatcher.ts header property (4): "⚠️ ENGINE DEDUP IS UNBOUNDED WHERE INNGEST'S IS WINDOWED — a deliberate, argued divergence from MOTIR-3413's 'no job's observable behaviour changes'", with the reason (a reset token should produce ONE email, not one per window) |
| 4 | docs/jobs.md → "Canonical job: email.send" says what each lane guarantees | NOT DELIVERED — this card's residue | MOTIR-3460 added a separate § "Event-level idempotency on the Postgres engine (MOTIR-3459)" at line 693. The canonical-job section a reader of email.send actually opens is untouched: line ~249 still reads "Inngest dedups same-key events inside its window … event-level dedup enforced by the Inngest runtime", and the option table at line 91 still reads "(Inngest event-level dedup)" |
| 5 | The JobEvent index comment is corrected | NOT DELIVERED — this card's residue | prisma/schema.prisma JobEvent.@@index([idempotencyKey]) still carries "Not UNIQUE: … it is job_queue's (event, job) pair that must not double-enqueue." After MOTIR-3459 that reason is wrong: (event_id, job_id) stops a dispatcher retry; the pair that does event-level dedup is (job_id, idempotency_key). MOTIR-3459 wrote new comments on JobQueueRun and did not sweep this one |
Criteria 4 and 5 are both referrers MOTIR-3459's own change stranded — the sweep-the-referrers
corollary (plan-rules/op-replan.md), missed on the way past. That is what this card is now for.
Per the correction comment on this card (2026-08-25T23:05Z, from
the MOTIR-3467 run): lib/email.ts:265 sends Resend's own Idempotency-Key header, derived from the
same value the event carries, so email.send has had provider-level dedup all along — and
resendIdempotencyKey returns undefined for the console / file providers, so dev and E2E differ
from production in exactly this dimension.
So there are three layers, composed and unstated: Inngest's window (Inngest lane), the engine's
unbounded partial-unique (MOTIR-3459), and Resend's per-request header (email.send only, prod only).
Making that legible is the same edit as criteria 4 and 5, so it lands here rather than as a fourth card.
docs/jobs.md's "Canonical job: email.send" § Idempotency bullet names all three layers and
which lane each applies to, replacing the current Inngest-only sentence. A reader who opens only
that section can no longer infer that idempotency means Inngest.defineJob option table entry for idempotency (line ~91) no longer reads
"(Inngest event-level dedup)" — it names both runtimes.resendIdempotencyKey returns undefined for console / file, so the
provider layer is absent locally and in E2E.prisma/schema.prisma's JobEvent.@@index([idempotencyKey]) comment is corrected — the
(event_id, job_id) justification is replaced by the real one, pointing at
job_queue_job_idempotency_key.lib/ diff, it has re-absorbed
MOTIR-3459's scope and should stop.The re-scope above is conditional on those two cards landing — they are implemented on a local,
unpushed branch with no pull request. The blocked_by edges record that. If either is abandoned, the
original five criteria come back and this card must be re-planned up, not quietly shipped as prose.
implemented, commit f96b61adimplemented, commit eb66e701docs/jobs.md lines ~91, ~249 (the two stale sites) and ~693 (the section MOTIR-3460 added)prisma/schema.prisma — JobEvent.@@index([idempotencyKey])lib/email.ts:219,265 — resendIdempotencyKey and the provider header