Repository navigation
Conversation
SES stores the MAIL FROM subdomain on the domain identity, so every sender on a domain shares it. Creating a sender with a different label moved it in SES for every sender on the domain, which only the admin-gated update route should do. Create now reuses the domain's label when the request omits it, and rejects a different label with a 400 before SES is called. Update writes the new label to every sender on the domain in the organization, so their stored config stays in step with SES. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Silthus
force-pushed
the
fix/email-mail-from-label-overwrite
branch
from
October 7, 2026 11:46
3ce4978 to
30eac42
Compare
This was referenced Oct 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Feature flags:
workflows-email-domain-agent-setup. Default off; this handoff does not activate them.feedback).mail_from_subdomainre-set the label in SES for every sender on the domain.Note
🔵 Merge risk: low (merge-risk rubric)
workflows-email-domain-agent-setupon, create, edit and verify hold a per-domain advisory lock across SES calls, and an edit rewrites the label on senders in other projects of the organization.test_each_sender_keeps_its_own_mail_from_label_while_the_flag_is_offpins that. The only flag-off additions are one flag evaluation and oneTeamlookup on create.Fork handoff for source #111367. Modernized head
30eac42d0dfe604c23c6f402d8e34058e248ea24. This draft prepares review and recreation in the upstream repository.Changes
workflows-email-domain-agent-setupflag is on, the flag that also gates the email domain MCP tools in #109703. Turning it on enables both PRs together. With it off, create, edit and verify behave as on master.distinct_id, and theorganizationandprojectgroups with their id group properties. The API views pass the request user, so create, edit and verify agree for the same caller.feedback.FOR UPDATE OF posthog_integration, so noTeamrow is locked.EmailIntegration, so it follows the same flag rule as verify.Note
The setup modal pre-fills
feedback. On a domain that already uses another label, the user now sees the 400 in a toast and edits the field. The modal does not look up the domain's label.Warning
Roll the flag out by organization. A teammate with the flag off still edits only their own sender, and their verify can re-set the old label for the domain.
Read first:
EmailIntegration._domain_guardandcreate_native_integration, where the flag picks the path. With the flag on, each holds a Postgres advisory lock for the length of its SES calls.Overlap with #109703 at 058782e: a trial merge conflicts in two files. Both PRs can merge in either order, because neither changes behavior until the flag is on. The PR that lands second resolves them:
posthog/api/integration.py: feat(workflows): let agents set up an email sending domain via mcp PostHog/posthog#109703 routesemail_updateandemail_verifythrough_get_email_integration(). That helper passesacting_user=cast(User, self.request.user), anddomain_connect_apply_urlpasses it toresolve_email_context.posthog/models/integration/email.py: both PRs add methods afterupdate_native_integration. Keep both.EMAIL_DOMAIN_AGENT_SETUP_FLAGand evaluate it with the same arguments. The second PR keeps one constant and one helper.How did you test this code?
Prior validation retained from the original PR (2026-10-05)
Test seams: the public methods of
EmailIntegration(create_native_integration,update_native_integration,verify), and the email edit endpoint for the request user. The SES facade andposthoganalytics.feature_enabledare mocked as external boundaries. The flag fake answers by organization group or bydistinct_id, like a real rollout.Test rationale:
test_new_sender_keeps_the_domain_mail_from_labelandtest_new_sender_cannot_move_the_domain_mail_from_labelcatch path 1, including a legacy blank stored label. The closest existing test istest_duplicate_domain_in_same_organization_allowed; no existing test sent a label on create.test_verifying_any_sender_keeps_a_changed_mail_from_labelcatches path 2. It edits one sender, then verifies every sender through instances loaded before the edit, and asserts the label sent to SES. It fails without the sibling write, and it fails without the re-read under the lock.test_editing_a_sender_without_a_label_keeps_the_current_domain_labelcatches a name-only or blank edit that re-sets a label changed since the sender loaded.test_each_sender_keeps_its_own_mail_from_label_while_the_flag_is_offpins master's behavior with the flag off: a different label on create, an edit that leaves the sibling alone, and a verify with the sender's own label.test_verifying_any_sender_keeps_a_changed_mail_from_labelruns once with the flag rolled out to the organization and once with it on for the acting user only.test_editing_the_label_shares_it_when_the_flag_is_on_for_the_requesting_usersends a realPATCHand fails if the view stops passing the request user.TestCases because the shared label lives in storedIntegrationrows.TransactionTestCasewould be slow and timing-dependent. The lock follows the existing pattern inposthog/models/integration/codex.py.Ran locally on an isolated test database:
posthog/models/test/integration/test_email.py, the email tests inposthog/api/test/test_integration.py,posthog/test/test_domain_connect.py,products/workflows/backend/test/test_email_integration_ses.py(96 passed). Repo-widemypyandhogli ci:preflight --strictpassed.Not checked: a real SES account, and GitHub Actions CI. The fork's workflow runs wait for maintainer approval.
Release status
Fork handoff only. This task does not activate rollout flags. Deployment flag state was not inspected. Upstream CI and the unavailable fresh Opus review remain handoff gaps.
Docs update
None. No doc under
docs/covers the email sender MAIL FROM setting.🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Agent: Claude Code, Claude Opus 5.5 (1M context). Review panel: GPT-6.1 Sol (Codex) and Claude Opus 5.5.
/tdd,/writing-tests,/writing-user-facing-copy,/writing-pr-descriptions,/reviewing-with-coderabbit,qa-swarm,leaving-pr-comments.workflows-email-domain-agent-setup, so this PR and feat(workflows): let agents set up an email sending domain via mcp PostHog/posthog#109703 roll out together. Two more review rounds ran on the gating.EmailIntegration(the Domain Connect tests mock that class).gh pr list --search "mail from subdomain"found only feat(workflows): let agents set up an email sending domain via mcp PostHog/posthog#109703 and unrelated layers of its stack.Modernization used Codex,
gpt-6.1-sol. Skills: modernize-pr, pr, writing-pr-descriptions, writing-voice, merge-risk, unslop. The source replays onto master atb75567aca6cce9f0499e882463fc7e71d65a064c. Original authorship and prior evidence remain above. The required current Opus review could not run because the Claude API rate limit blocked it.Current review fixes and final upstream replay used Codex,
gpt-6.1-sol. The fresh cross-family review remains blocked by the known Claude rate limit.