Durable audit envelope for a bulk outbound action (D-13, OBS-02-shaped). Snapshots
template_id, rendered_body, and recipient cohort at confirmation time. Per-recipient
delivery flows through Outbound.trigger/2 (sealed) carrying the envelope id. Refused
attempts persist with status: :refused_cap_exceeded.
Columns
:id— UUID generated by the caller atbulk_trigger/2confirmation time (autogenerate: false— callers must supply an explicitEcto.UUID.generate/0value so tests and downstream correlation keys can assert on it).:template_id— string id of the configured template that was used at confirmation.:rendered_body— the fully rendered message body at confirmation time. Never re-rendered at worker run time (CLAUDE.md: snapshot trust facts at decision time).:recipient_conversation_ids— list of:integerconversation ids the operator targeted. Persisted for audit (OBS-02); per-recipient delivery is tracked on the individualsystem_outboundMessagerows created via the sealedOutbound.trigger/2lane.:count— denormalized cohort size (must be> 0).:effective_cap— the value ofmax_batch_size/0at decision time. Snapshotted on BOTH the:submittedand:refused_cap_exceededpaths so OBS-02 readers can comparecountagainst the policy that was actually in effect when the operator confirmed (WR-05). Must be> 0.:requested_by— actor string (operator, system, etc.); nullable so system-initiated bulk actions are representable.:requested_at— confirmation timestamp.:status—:submitted(default) or:refused_cap_exceeded. A refused row records that an oversized cohort was attempted — mirrorsGovernance.propose_blocked's "blocked attempts persist" posture so OBS-02 reads see both lanes from one table.:refused_reason— operator-facing reason string (only set when refusing).
Status enum
:submitted — fan-out enqueued (per-recipient delivery is tracked on Message rows)
:refused_cap_exceeded — cohort exceeded max_batch_size; no fan-out occurredValidation
:id,:template_id,:rendered_body,:recipient_conversation_ids,:count,:effective_cap, and:requested_atare required.:countmust be> 0(a zero-cohort envelope has no audit value).:effective_capmust be> 0.
See priv/repo/migrations/20260527063000_add_outbound_bulk_envelopes.exs for the
underlying table definition (D-15).
Summary
Functions
Builds a changeset for the bulk envelope.
Functions
Builds a changeset for the bulk envelope.
Casts all writable fields and validates required ones (:id, :template_id,
:rendered_body, :recipient_conversation_ids, :count, :effective_cap,
:requested_at). Enforces count > 0 and effective_cap > 0 (zero values
have no audit value).
:requested_by is intentionally optional — system-initiated bulk actions are
representable with nil here. :status defaults to :submitted; setting it to
:refused_cap_exceeded with a :refused_reason is also valid.