MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-3495

(motir-core) Three dedup layers, none legible where a reader looks — `docs/jobs.md`'s canonical-job § and `JobEvent`'s index comment still name Inngest as the only enforcer after MOTIR-3459

Done
Description

⚠️ 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, under run.md's a done blocker 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 implemented when this card was created. It sits under the SAME epic (MOTIR-3413), it was created at 19:09:05Z, and its commit f96b61ad landed at 21:51:56Z — 47 minutes BEFORE this card was filed at 22:39:06Z. The filing pre-flight verified the defect against origin/main (where it is still true, because f96b61ad is 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."

Original acceptance criteria, disposed of one by one

Measured on parent/MOTIR-3415-event-jobs-cutover @ 6aa9d502 (local, unpushed — no PR yet).

#Original criterionVerdictEvidence
1The engine enforces the option, or it is impossible to declare on an engine-routed jobDELIVERED — both armslib/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"
2A test proves the runtime drop, not the wiringDELIVEREDtests/jobs/engine-idempotency.test.ts, 205 lines — including a concurrent duplicate against real Postgres, and a job declaring no template asserted unaffected
3The dedup window is stated explicitly in codeDELIVEREDdispatcher.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)
4docs/jobs.md"Canonical job: email.send" says what each lane guaranteesNOT DELIVERED — this card's residueMOTIR-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)"
5The JobEvent index comment is correctedNOT DELIVERED — this card's residueprisma/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.

And there is a THIRD dedup layer nobody documents

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.

Acceptance criteria

  • 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.
  • The defineJob option table entry for idempotency (line ~91) no longer reads "(Inngest event-level dedup)" — it names both runtimes.
  • The dev-provider gap is stated: 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.
  • No code change, no migration, no new test. If this card grows a lib/ diff, it has re-absorbed MOTIR-3459's scope and should stop.

⚠️ If MOTIR-3459 / MOTIR-3460 are cancelled rather than merged

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.

Context refs

  • MOTIR-3459 — the enforcement, implemented, commit f96b61ad
  • MOTIR-3460 — the docs card, implemented, commit eb66e701
  • docs/jobs.md lines ~91, ~249 (the two stale sites) and ~693 (the section MOTIR-3460 added)
  • prisma/schema.prismaJobEvent.@@index([idempotencyKey])
  • lib/email.ts:219,265resendIdempotencyKey and the provider header