models:
- name: MailingAbTest
collection: mailing_ab_tests
description: "The campaign-grain A/B control — one live test per campaign (UNIQUE among live rows), carrying the persisted deterministic sampling seed, the winner-selection metric, and the promotion state. Fragment fields live on each sibling Mailing; the winner is promoted as a NEW Mailing at 100% citing the same campaign. Winner auto-promotion rides the single send cron as an idempotent step (upstream's dormant second cron does not port)."
fields:
id:
type: uuid
attributes: ["@id", "@default(uuid)"]
description: "Unique record id"
campaign_id:
type: uuid
attributes: ["@required", "@foreign_key(engagement.EngagementCampaign.id)", "@exclude_from_foreign_key_check", "@indexed"]
description: "The campaign under test # logical FK engagement.EngagementCampaign.id — ONE live test per campaign (partial UNIQUE; the v19 collapse holds — engagement stays the attribution master and ships no A/B fields)."
winner_selection:
type: AbWinnerSelection
attributes: ["@required", "@default(opened_ratio)"]
description: "The metric the promotion step ranks done siblings by (manual defers to the operator verb). Trace ratios sort STORED numbers (MVX-1); the bridge's sale_invoiced_amount axis attributes by UTM SOURCE through the declared billing-side seam port (MVX-4 — never a cross-schema raw read)."
winner_selection_sms:
type: AbWinnerSelection?
description: "The PARALLEL sms-channel ranking axis (MVX-2 — upstream's ab_testing_sms_winner_selection): the same enum on its own nullable column, no twin module. Declares how sms-type variants rank in the mixed-channel compare action; unset means the sms variants rank by the mail axis. Sms variants rank on whatever traces exist (the sms send walk parks loudly today — zero traces rank last as a None ratio, never a fake 0%)."
promote_at:
type: datetime?
description: "Earliest auto-promotion time — the sweep's promotion step picks tests past this stamp with at least one done sibling."
completed:
type: boolean
attributes: ["@default(false)"]
description: "Terminal FLAG (not a machine): stamped together with winner_mailing_id by the promotion step/verb — promotion is idempotent (re-runs no-op once completed)."
winner_mailing_id:
type: uuid?
attributes: ["@foreign_key(Mailing.winner_of)", "@exclude_from_foreign_key_check"]
description: "The promoted winner # logical FK Mailing.id — the copy at ab_testing_pc=100 citing the same campaign. Setting it marks the test completed."
sampling_seed:
type: string
attributes: ["@required", "@max(64)"]
description: "Minted ONCE at creation (random 128-bit hex), never regenerated — the deterministic-fragment key: member iff HMAC-SHA256(seed, recipient_email) mod 100 < the sibling's ab_testing_pc. Identical fragments across runs and workers; disjoint across siblings."
metadata:
type: Metadata
attributes: ["@audit_metadata"]
description: "Audit metadata"
indexes:
- type: unique
fields: [campaign_id]
where: "deleted_at IS NULL"
description: "One live A/B test per campaign — the mailing-side fence for the re-homed invariant (fires on raw SQL)"
enums:
- name: AbWinnerSelection
description: "The winner-selection metric — the promotion step ranks done siblings by this key (manual defers to the operator). A CLOSED enum declared once (MVX-1: no per-bridge selection_add); every ranking axis is either stored trace numbers or a declared seam"
variants:
- name: manual
description: "An operator promotes explicitly (the promotion verb)"
- name: opened_ratio
description: "Highest opened ratio wins"
default: true
- name: clicks_ratio
description: "Highest clicks ratio wins (computed from trace links_click_datetime)"
- name: replied_ratio
description: "Highest replied ratio wins"
- name: sale_invoiced_amount
description: "Highest invoiced total attributed by UTM SOURCE wins (the mass_mailing_sale bridge axis, MVX-4): each variant ranks by the invoiced amount its cited engagement source carries, read through the DECLARED billing-side seam port — deny-by-default, zero billing Cargo edge, never a cross-schema raw read. A variant citing no source ranks last; an uncomposed seam skips promotion loudly rather than promoting on a fake zero."