# `Cairnloop.Workers.ProcessMessage`
[🔗](https://github.com/szTheory/cairnloop/blob/main/lib/cairnloop/workers/process_message.ex#L1)

Oban worker that dispatches inbound messages to the correct handler by channel.

## Two-channel dispatch (Phase 28 D-07)

The `perform/1` function uses two clauses, matched on the `"channel"` arg:

- **`"widget"`** — delegates to `Cairnloop.Chat.ingest_widget_message/2`, which
  creates a `:user`-role Message row and broadcasts `{:message_created, message.id}`
  on `"conversation:#{id}"` plus `{:conversations_changed}` on `"conversations"`.
  Follows the canonical "Worker → context facade → broadcast" pattern established by
  `Cairnloop.Automation.Workers.DraftWorker` (lines 101-114).

- **`"email"`** — preserves the sealed logger stub from before Phase 28. The
  `Cairnloop.Ingress.EmailWebhookPlug` is a silent secondary caller that enqueues
  `ProcessMessage.new(%{channel: "email", content: content})`. The email branch must
  keep that arg shape working (Pitfall 2 / OQ-2). Full email-to-Conversation wiring
  is deferred to a future host-integration phase.

## Idempotency (D-07)

The `unique:` option deduplicates jobs within a 30-second window for the same
`(conversation_id, content)` pair. This guards against duplicate message inserts
caused by channel-reconnect retry storms (Pitfall 5 partial mitigation).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
