You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Parent/reference: #50 (approved-purpose wiring and Datamailer retirement)
Related authorities: #21 (Relay ownership), #48 (Relay-owned templates), #49 (website delivery intent/status), #227 (course/event/marketing preferences), #290 (static Datamailer inventory), #297 (adoption-ledger reconciliation)
Explicitly separate from: #149 (system-evaluation, peer-task, score-visibility, and peer/score-notification decisions)
Decision outcome
Record whether the target website wants any Relay-owned course email-campaign product semantics. The answer must be one independent ADOPT, DEFER, or REJECT decision for the campaign family; silence, “port CMP,” or a partial source diff is not approval.
This is a product decision gate, not permission to copy the CMP tree. It must never become a wholesale Datamailer port. Until the decision and the Relay contract are accepted, do not add a campaign model, migration, route, outbox event, provider call, sender configuration, source pin, or new Datamailer send.
Evidence boundary
The current CMP main is 6d3cc0e0e0b9a1d28be1a8d53209555269e1d3fe (tree 45f1f8e2548b25e3ef89d44b2370efbb647ff088, parent e49c575ce3f567cc14a8f66a89d3ccaca3bfe2f4), titled Dispatch campaign Queue asynchronously; fix missing campaign from_email. The commit changes exactly these nine source paths:
The reviewed change introduces or completes these semantics in the CMP line:
A separate EmailCampaign model and QUEUE_PENDING status/migration.
A Studio campaign queue action that enqueues campaign.queue and returns before the potentially long-running provider call; an asynchronous outbox sender later calls Datamailer and marks the campaign queued.
Campaign payloads that include configured from_email through the CMP Datamailer configuration.
API serialization/UI status changes and tests for the new lifecycle.
The website authority remains origin/mainface8e4808d65afbf0374d1ced7a88079950d663 (tree c8496fb2a37529eb7c63e1eeb9e35e29a2aadada) and still pins CMP 98a235283904b4ef9ad29e196298540756cf1bcc. On that target, the campaign domain is RegistrationCampaign in courses/models/cohort.py; the adopted Studio path studio_courses/views/campaign_datamailer.py currently calls Datamailer campaign queueing synchronously and there is no target EmailCampaign, campaign.queue outbox event, queue_pending status, or email-campaign API surface. This current implementation is characterization evidence, not authorization for another Datamailer send.
Required owner decision
Choose one disposition for the whole campaign family:
ADOPT
If campaigns are wanted, answer all fields below. Adoption approves target product semantics only; it does not approve any CMP bytes, Datamailer call, or Relay activation.
Domain and lifecycle: Is a campaign a first-class target-owned object, a versioned message intent, or another bounded abstraction? Define its relationship to Course, Cohort, and any existing RegistrationCampaign; draft/preview/test/queue/cancel/replace/archive states; immutable content/version and correction/history rules; and whether an EmailCampaign analogue is actually needed.
Actor and parity: Name the capabilities allowed to create, edit, preview, test-send, queue, cancel, inspect, or resend. Define exact Course/Cohort scope, reauthentication/confirmation, CSRF, audit, private/no-store responses, and parity between Studio and /api/v1/admin/. No copied CMP staff-token or broad admin behavior is accepted by inference.
Purpose and audience: Choose the approved Decision: Confirm the MVP transactional email purpose catalog #22 purpose ID(s), trigger/action model, exact recipient query and cohort boundary, account/preference/suppression rules, excluded/removed/ambiguous recipients, and whether the campaign is course-update, newsletter/marketing, or a new purpose requiring authority. Define subject/body/context ownership without storing an unapproved local renderer or raw recipient/body data.
Relay contract and sender: Bind each campaign to a Relay template key and immutable version, typed context, sender/reply-to policy, and development-only courses sender where applicable. from_email must mean a Relay-owned sender identity, not a new website DATAMAILER_FROM_EMAIL or SES fallback. Define accepted-versus-delivered, callback/reconciliation, suppression, and provider-unavailable behavior.
Async and idempotency semantics: Decide whether queueing creates one website delivery intent plus one durable after-commit job, the exact idempotency/version key and conflict rule, pending/accepted/delivered/retryable/ambiguous/dead/suppressed states, lease/retry/worker-restart behavior, and how response loss or ambiguous Relay acknowledgement is resolved. A request transaction must never contact Relay or Datamailer.
Migration and retirement: Define how existing RegistrationCampaign/Datamailer history and any pending legacy work are characterized, retained, reconciled, suppressed, or excluded; no campaign import may send or requeue. Preserve target migration identities, redaction/retention, rollback, one-active-sender, and Wire approved messages through Relay and retire Datamailer #50 retirement gates.
DEFER
Preserve the target's current campaign data/read/history characterization but do not activate or extend new campaign sending. Name a concrete reopen condition (for example, an accepted Relay campaign API plus an owner-approved purpose/audience contract). Record all nine CMP paths and any dependent campaign bytes as unapplied source drift; no Datamailer queue/test-send/upsert or sender configuration is permitted as a temporary implementation.
REJECT
State why course email campaigns are outside the target product, preserve only the minimum read-only historical evidence required by #50, and record the entire CMP campaign family as an explicit source-sync exclusion. Do not create a local replacement model, renderer, queue, or provider adapter.
Scope after an ADOPT decision
Only after the owner response does PM split the smallest target-native implementation slices under #48/#49/#50, refresh #297/#290 source/overlay evidence, and re-groom each slice against one exact current website/CMP identity. A future implementation must use the website application-service and Relay boundary, not copy the CMP EmailCampaign/Datamailer implementation literally.
Explicit non-goals
No CMP source-pin movement, sync_course_platform.py --apply, copied files, migration transplant, migration squash, or adoption-ledger rewrite in this issue.
No website EmailCampaign model, API/Studio route, queue/outbox sender, from_email setting, Datamailer/SES/Relay provider call, credential, sender/domain activation, real/test recipient, or deployment.
No wholesale Datamailer port, local template renderer, second provider/event stack, synchronous request-time network call, blind retry after ambiguity, or dual sender.
No production/protected-data/queue access and no import, drain, freeze, retirement, or canary action.
Acceptance criteria
A named course/product owner records exactly one ADOPT, DEFER, or REJECT disposition for the campaign family.
An ADOPT response completes domain/lifecycle, actor/parity, purpose/audience, Relay/sender, async/idempotency, migration/history, privacy, and rollback fields above; a DEFER or REJECT response names its preserved behavior and reopen/exclusion rule.
This decision issue has no implementation or render change; screenshots and provider/browser execution are not applicable at this stage. Any future ADOPT child must include independent desktop/mobile scenarios, including:
authorized Studio and admin-API campaign draft/preview with exact Course/Cohort scope and masked data;
unauthorized, stale-revision, missing-Relay, suppressed-recipient, and ambiguous-acknowledgement states with no existence/body/recipient leakage;
queue request returning a safe pending/accepted status, refresh/reconciliation to terminal state, retry/dead handling, and no request-time network call;
This issue remains blocked for engineering until the owner response is recorded. PM then either closes the decision gate with an explicit source exclusion or creates/grooms only the minimum implementation children. No code, source pin, provider, deployment, commit, or merge is authorized by this issue.
Parent/reference: #50 (approved-purpose wiring and Datamailer retirement)
Related authorities: #21 (Relay ownership), #48 (Relay-owned templates), #49 (website delivery intent/status), #227 (course/event/marketing preferences), #290 (static Datamailer inventory), #297 (adoption-ledger reconciliation)
Explicitly separate from: #149 (system-evaluation, peer-task, score-visibility, and peer/score-notification decisions)
Decision outcome
Record whether the target website wants any Relay-owned course email-campaign product semantics. The answer must be one independent
ADOPT,DEFER, orREJECTdecision for the campaign family; silence, “port CMP,” or a partial source diff is not approval.This is a product decision gate, not permission to copy the CMP tree. It must never become a wholesale Datamailer port. Until the decision and the Relay contract are accepted, do not add a campaign model, migration, route, outbox event, provider call, sender configuration, source pin, or new Datamailer send.
Evidence boundary
The current CMP
mainis6d3cc0e0e0b9a1d28be1a8d53209555269e1d3fe(tree45f1f8e2548b25e3ef89d44b2370efbb647ff088, parente49c575ce3f567cc14a8f66a89d3ccaca3bfe2f4), titledDispatch campaign Queue asynchronously; fix missing campaign from_email. The commit changes exactly these nine source paths:api/views/email_campaign_serializers.pycadmin/templates/cadmin/include/email_campaign_status_badge.htmlcadmin/tests/test_campaign_datamailer_views.pycadmin/views/campaign_datamailer.pycourse_management/datamailer/payloads/email_campaigns.pycourse_management/datamailer_outbox_senders.pycourses/migrations/0044_alter_emailcampaign_status.pycourses/models/course.pycourses/tests/test_datamailer_outbox_campaign_queue.pyThe reviewed change introduces or completes these semantics in the CMP line:
EmailCampaignmodel andQUEUE_PENDINGstatus/migration.campaign.queueand returns before the potentially long-running provider call; an asynchronous outbox sender later calls Datamailer and marks the campaign queued.from_emailthrough the CMP Datamailer configuration.The website authority remains
origin/mainface8e4808d65afbf0374d1ced7a88079950d663(treec8496fb2a37529eb7c63e1eeb9e35e29a2aadada) and still pins CMP98a235283904b4ef9ad29e196298540756cf1bcc. On that target, the campaign domain isRegistrationCampaignincourses/models/cohort.py; the adopted Studio pathstudio_courses/views/campaign_datamailer.pycurrently calls Datamailer campaign queueing synchronously and there is no targetEmailCampaign,campaign.queueoutbox event,queue_pendingstatus, or email-campaign API surface. This current implementation is characterization evidence, not authorization for another Datamailer send.Required owner decision
Choose one disposition for the whole campaign family:
ADOPT
If campaigns are wanted, answer all fields below. Adoption approves target product semantics only; it does not approve any CMP bytes, Datamailer call, or Relay activation.
Course,Cohort, and any existingRegistrationCampaign; draft/preview/test/queue/cancel/replace/archive states; immutable content/version and correction/history rules; and whether anEmailCampaignanalogue is actually needed./api/v1/admin/. No copied CMP staff-token or broad admin behavior is accepted by inference.coursessender where applicable.from_emailmust mean a Relay-owned sender identity, not a new websiteDATAMAILER_FROM_EMAILor SES fallback. Define accepted-versus-delivered, callback/reconciliation, suppression, and provider-unavailable behavior.pending/accepted/delivered/retryable/ambiguous/dead/suppressed states, lease/retry/worker-restart behavior, and how response loss or ambiguous Relay acknowledgement is resolved. A request transaction must never contact Relay or Datamailer.RegistrationCampaign/Datamailer history and any pending legacy work are characterized, retained, reconciled, suppressed, or excluded; no campaign import may send or requeue. Preserve target migration identities, redaction/retention, rollback, one-active-sender, and Wire approved messages through Relay and retire Datamailer #50 retirement gates.DEFER
Preserve the target's current campaign data/read/history characterization but do not activate or extend new campaign sending. Name a concrete reopen condition (for example, an accepted Relay campaign API plus an owner-approved purpose/audience contract). Record all nine CMP paths and any dependent campaign bytes as unapplied source drift; no Datamailer queue/test-send/upsert or sender configuration is permitted as a temporary implementation.
REJECT
State why course email campaigns are outside the target product, preserve only the minimum read-only historical evidence required by #50, and record the entire CMP campaign family as an explicit source-sync exclusion. Do not create a local replacement model, renderer, queue, or provider adapter.
Scope after an ADOPT decision
Only after the owner response does PM split the smallest target-native implementation slices under #48/#49/#50, refresh #297/#290 source/overlay evidence, and re-groom each slice against one exact current website/CMP identity. A future implementation must use the website application-service and Relay boundary, not copy the CMP
EmailCampaign/Datamailer implementation literally.Explicit non-goals
sync_course_platform.py --apply, copied files, migration transplant, migration squash, or adoption-ledger rewrite in this issue.EmailCampaignmodel, API/Studio route, queue/outbox sender,from_emailsetting, Datamailer/SES/Relay provider call, credential, sender/domain activation, real/test recipient, or deployment.Acceptance criteria
ADOPT,DEFER, orREJECTdisposition for the campaign family.ADOPTresponse completes domain/lifecycle, actor/parity, purpose/audience, Relay/sender, async/idempotency, migration/history, privacy, and rollback fields above; aDEFERorREJECTresponse names its preserved behavior and reopen/exclusion rule.6d3cc0e, current target Datamailer behavior, Integrate Relay for transactional email and retire Datamailer #21/Manage Relay-owned versioned transactional templates in Studio #48/Build the durable Relay delivery intent and status lifecycle #49 technical capability, or Pin the adopted Datamailer source surface in a static inventory #290's inventory, and that Decision: Choose CMP system-evaluation, peer-assignment, score, and notification behavior #149 remains separate.ADOPTresponse are bounded implementation children created, each with explicit Manage Relay-owned versioned transactional templates in Studio #48/Build the durable Relay delivery intent and status lifecycle #49/Wire approved messages through Relay and retire Datamailer #50 dependencies and no direct Datamailer/SES path;DEFER/REJECTleaves all campaign source bytes unapplied.Browser and verification scenarios
This decision issue has no implementation or render change; screenshots and provider/browser execution are not applicable at this stage. Any future ADOPT child must include independent desktop/mobile scenarios, including:
Required lifecycle
This issue remains blocked for engineering until the owner response is recorded. PM then either closes the decision gate with an explicit source exclusion or creates/grooms only the minimum implementation children. No code, source pin, provider, deployment, commit, or merge is authorized by this issue.