Oban worker that processes inbound webhook feedback from a durable ingress row.
Job args contain only ingress_id per Phase 33 D-01 (durable-spine-over-queue-archaeology).
All correlation data — adapter identity, delivery_id, provider_message_id, normalized_status —
is read from the persisted Chimeway.Webhooks.Ingress row rather than carried through
Oban job args.
Safe-noop semantics:
- Hard-deleted ingress rows →
:ok(Pitfall 2: race against operator/test cleanup). - Already-ignored rows →
:ok(idempotent dedup convergence). - Already-processed rows →
:ok(idempotent re-run on Oban retry). - Stale delivery_id → write
ingress_state: :ignored, ignored_reason: :delivery_not_found, return:ok. - Stale provider_message_id → write
ingress_state: :ignored, ignored_reason: :provider_message_id_not_found, return:ok.
The normalize_perform_result/1 table mirrors WorkflowProgressionWorker.normalize_progress_result/1
so all understood-but-ignored outcomes collapse to :ok at the Oban queue boundary.
Threats covered:
- T-33-RETRY (DoS retry storm): stale lookups return
:okrather than raising. - T-33-PII (worker-side): only
ingress_state,ignored_reason,processed_atare written; no raw job args or provider payload is persisted on the ingress row. - T-33-AUTH-LEAK (worker-side):
String.to_existing_atom/1used only on the bounded~w(succeeded bounced failed)set aftercanonicalize_status/1. NoString.to_atom/1. - T-33-IDEMPOTENT:
:ignoredand:processedbranches return:okwithout re-applying side effects, preventing double-attempt rows or double-signal emission on retries.
Backwards-compat shim (A6, deploy-safety):
Two extra perform/1 heads handle the legacy %{"delivery_id" => …} and
%{"provider_message_id" => …} arg shapes for one release cycle, protecting in-flight
pre-Phase-33 Oban jobs. Drain the queue and remove these clauses in Phase 34 or v1.5.