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 toCairnloop.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 byCairnloop.Automation.Workers.DraftWorker(lines 101-114)."email"— preserves the sealed logger stub from before Phase 28. TheCairnloop.Ingress.EmailWebhookPlugis a silent secondary caller that enqueuesProcessMessage.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).