Skip to content

fix(workflows): drop suppressed cc/bcc instead of skipping the email - #118456

Merged
trunk-io[bot] merged 1 commit into
masterfrom
posthog/workflows-partial-suppression
Oct 27, 2026
Merged

trunk-io[bot] merged 1 commit into
masterfrom
posthog/workflows-partial-suppression

Conversation

@mayteio

@mayteio mayteio commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Problem

  • An email sent to a group, such as every admin on an account, goes to nobody once one address on it hard-bounces.
  • The send-time check skips the whole email when any address in to, cc or bcc is on the suppression list.
  • Teams then miss onboarding and lifecycle emails for whole accounts, with only a skip line in the run log.

Raised in Slack: https://posthog.slack.com/archives/C06GG249PR6/p1792145215385039

Changes

  • A suppressed cc or bcc address is removed, and the email goes to the other recipients. The run log shows a warning that names the removed addresses.
  • A suppressed to address is replaced by the first deliverable cc address. The send is skipped only when no cc address is left.
  • A bcc address never becomes the to address, so hidden recipients stay hidden.
  • No email reaches a suppressed address, the same as before.
  • The workflow pre-check (RecipientPreferencesService) and the send-time check (EmailService) share one helper, removeSuppressedRecipients, so they cannot disagree about when to skip.
  • The send limit and the tracking consent check now count only the addresses that receive the email.

Note

A workflow that addresses one person in to and a colleague in cc now sends to the colleague when the person's address is suppressed. Before, the step was skipped.

How did you test this code?

  • helpers/suppressed-recipients.test.ts is new and covers the removal and promotion rules at the cheapest level. No existing test covered partial delivery.
  • email.service.test.ts gets one wiring test that checks the SES destination after promotion. It catches a send-time path that ignores the helper result.
  • In recipient-preferences.service.test.ts, the cases that expected a suppressed cc or bcc to skip the step now expect the step to run. A new case checks that a step with only bcc left is still skipped.
  • Ran the four affected suites locally against the dev stack. Not run: the rest of the CDP suite.

Test rationale: the new cases fail if a suppressed cc again blocks the whole email, or if a bcc address is promoted to to.

Release status

  • No feature flag controls this change

Docs update

None. No doc in docs/ covers suppression behaviour.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Agent: PostHog Desktop (Claude Code), claude-opus-5-5

  • Skills invoked: /writing-tests, /writing-code-comments, /writing-pr-descriptions.
  • The first version only removed suppressed cc and bcc addresses. Promotion of a cc address was added after review, because a bounced to address still stopped group emails.
  • Duplicate check: no open PR changes this behaviour.
  • The test data is invented.

Created with PostHog Desktop

🤖 Generated with Claude Code

@mayteio mayteio self-assigned this Oct 16, 2026
@trunk-io

trunk-io Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

😎 Merged successfully - details.

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

🤖 CI report

✅ Trunk lane — non-backend lane

This PR is assigned to the non-backend lane. It does not run backend Python tests and may merge in parallel with PRs in other lanes.

✅ Duplication (Python) — clean

New Python code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

✅ Duplication (TypeScript) — clean

New TypeScript code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

🚨 Comment density — 11% of added code lines are comments (30 of 284)

This section warns when comments are more than 3% of the code lines a PR adds, and alerts above 6%. Before agent-assisted PRs, the typical share was about 2%. Only full-line comments count. Docstrings, generated files, snapshots, migrations, and workflow files are left out.

Comments that restate the code, record how the change came about, or narrate the next line add noise for the next reader. Keep the comments that explain a reason the code cannot show, and remove the rest. See .agents/skills/writing-code-comments/SKILL.md for the house rules.

Files with the most added comment lines:

File Comment lines Added lines
nodejs/src/cdp/services/messaging/helpers/suppressed-recipients.ts 20 105
nodejs/src/cdp/services/messaging/email.service.ts 4 43
nodejs/src/cdp/services/messaging/recipient-preferences.service.test.ts 4 21
nodejs/src/cdp/services/messaging/recipient-preferences.service.ts 2 12

This check does not block merging. It updates on every push and clears when the share drops.

@trunk-io

trunk-io Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Static Badge   Static Badge   Static Badge

Failed Test Failure Summary Logs
EmailService native email sending with maildev should send an email The email test expected one message but received two, indicating an unexpected extra email was sent. Logs ↗︎

View Full Report ↗︎ ⋅ Docs

@mayteio
mayteio marked this pull request as ready for review October 19, 2026 14:46
@parameterai

parameterai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Risk: No findings

This increment closes the quoted-local-part bypass in to: the suppression check, the tracking-consent lookup, and the one-click-unsubscribe identifier all now unquote, and the delivered to is rebuilt through the same quote/unquote pair so the checked address is exactly the one SES delivers to. The previously reported finding is fixed; I found no new issues in the changes since the last review.

Sentinel reviewed 900f249 · Review settings

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team October 19, 2026 14:47
@mayteio mayteio added the stamphog Request AI approval (no full review) label Oct 20, 2026 — with PostHog

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not approved — this change needs a human reviewer.

Re-add the stamphog label to request another review once you have addressed this.

Owning-team authorship provides assurance for the risky changes, but @parameterai[bot]'s substantive parser concern remains unaddressed. Fix the suppression parsing mismatch and obtain review from @PostHog/team-workflows before re-requesting; Meikel Ratz or Arthur Moreira de Deus are suggested reviewers.

  • @parameterai[bot]'s discussion concern remains in nodejs/src/cdp/services/messaging/helpers/suppressed-recipients.ts:25-30: a bracketed address followed by non-whitespace text is checked as the entire entry instead of its bare address. EmailSuppressionService only trims and lowercases that lookup, while email.service.ts:1129-1140 forwards the retained entry to SES unchanged. This leaves the reported suppression-check mismatch unfixed. Normalize supported address forms consistently with delivery parsing and add a trailing-comment regression test; request review from @PostHog/team-workflows before re-requesting.
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✓ no deny categories matched
size ✓ 200L, 3F substantive, 355L/6F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1d-complex (355L, 6F, single-area, fix)
stamphog 2.6.0 .stamphog/policy.yml @ 944c494 · reviewed head 944c494

@stamphog stamphog Bot removed the stamphog Request AI approval (no full review) label Oct 20, 2026
@mayteio mayteio added the stamphog Request AI approval (no full review) label Oct 20, 2026 — with PostHog
@stamphog stamphog Bot removed the stamphog Request AI approval (no full review) label Oct 20, 2026
@stamphog

stamphog Bot commented Oct 20, 2026

Copy link
Copy Markdown

No review started. stamphog started a review of this pull request less than 10 minutes ago, and a label can request at most one review per 10 minutes. The stamphog label has been removed. Add it again at 06:20 UTC (in about 6 minutes) to request a fresh review.

@veria-ai

veria-ai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 1 · PR risk: 0/10

@mayteio mayteio added the stamphog Request AI approval (no full review) label Oct 20, 2026 — with PostHog
@stamphog stamphog Bot removed the stamphog Request AI approval (no full review) label Oct 20, 2026

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not approved — this change needs a human reviewer.

Re-add the stamphog label to request another review once you have addressed this.

Owning-team authorship supplies assurance, but both reviewers' substantive suppression-bypass concerns remain unresolved. Address @parameterai's and @veria-ai's comments with quote-aware mailbox parsing shared by suppression and delivery, add regression coverage, and request review from @PostHog/team-workflows before re-requesting.

  • @parameterai on nodejs/src/cdp/services/messaging/helpers/suppressed-recipients.ts:20: the parser takes the first angle-bracket group, including one inside a quoted display name, rather than the actual mailbox. Lines 57–75 check that incorrect identifier and retain the original entry; email.service.ts:328–336 and 1130–1140 forward it to SES without correcting the mailbox, leaving the reported suppression bypass unfixed.
  • @veria-ai on nodejs/src/cdp/services/messaging/helpers/suppressed-recipients.ts:20: the same quoted-display-name mismatch permits a blocked CC or BCC mailbox to survive suppression filtering when the display name contains an unsuppressed address. The current code does not reconcile the checked identifier with the delivered mailbox.
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✓ no deny categories matched
size ✓ 201L, 3F substantive, 361L/6F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1d-complex (361L, 6F, single-area, fix)
stamphog 2.6.0 .stamphog/policy.yml @ c49c0d9 · reviewed head c49c0d9

@mayteio mayteio added the stamphog Request AI approval (no full review) label Oct 20, 2026 — with PostHog

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not approved — this change needs a human reviewer.

Re-add the stamphog label to request another review once you have addressed this.

Owning-team authorship provides assurance for the risky changes, and the earlier quoted-display-name concern is addressed. Address @parameterai[bot]'s remaining parser/delivery mismatch concern and request review from @PostHog/team-workflows before re-requesting.

  • @parameterai[bot], discussion comment concerning nodejs/src/cdp/services/messaging/email.service.ts and helpers/suppressed-recipients.ts: the no-removals path still discards the helper's reconstructed recipient lists and sends the original strings. This leaves the reported quoted-local-part parsing concern unaddressed; suppression lookup only trims and lowercases identifiers, while delivery receives a different representation. Use a consistent mailbox representation for suppression and delivery on every path, and add a quoted-local-part regression test before re-requesting.
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✓ no deny categories matched
size ✓ 212L, 3F substantive, 380L/6F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1d-complex (380L, 6F, single-area, fix)
stamphog 2.6.0 .stamphog/policy.yml @ 34bdd8d · reviewed head 34bdd8d

@stamphog stamphog Bot removed the stamphog Request AI approval (no full review) label Oct 20, 2026
@mayteio mayteio added the stamphog Request AI approval (no full review) label Oct 20, 2026 — with PostHog

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not approved — this change needs a human reviewer.

Re-add the stamphog label to request another review once you have addressed this.

The author belongs to @PostHog/team-workflows, but @parameterai's substantive concern remains present in the current code. Address that thread and request follow-up review from @PostHog/team-workflows before re-requesting; Meikel Ratz and Arthur Moreira de Deus are suggested reviewers.

  • @parameterai on nodejs/src/cdp/services/messaging/helpers/suppressed-recipients.ts: the quoted-local-part concern remains unfixed. The helper checks the primary recipient without unquoting it, while EmailSuppressionService only trims and lowercases identifiers. email.service.ts also passes that quoted identifier to tracking-consent lookup and SES delivery, so a stored bare-address suppression or tracking opt-out is not matched. Normalize the primary recipient consistently for suppression and consent, preserve valid delivery formatting, and add regression tests before re-requesting review.
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✓ no deny categories matched
size ✓ 237L, 3F substantive, 410L/6F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1d-complex (410L, 6F, single-area, fix)
stamphog 2.6.0 .stamphog/policy.yml @ 7e60173 · reviewed head 7e60173

@stamphog stamphog Bot removed the stamphog Request AI approval (no full review) label Oct 20, 2026
@mayteio mayteio added the stamphog Request AI approval (no full review) label Oct 20, 2026 — with PostHog
stamphog[bot]

This comment was marked as outdated.

@scheduled-actions-posthog

Copy link
Copy Markdown
Contributor

This PR hasn't seen activity in a week! Should it be merged, closed, or further worked on? If you want to keep it open, please remove the stale label – otherwise this will be closed in another week. If you want to permanently keep it open, use the waiting label.

A suppressed address in cc or bcc no longer stops delivery to everyone else on the email. Suppressed cc and bcc addresses are removed at send time. When the to address is suppressed, the first deliverable cc address takes its place. The send is skipped only when no cc address is left. Bcc addresses are never promoted, so hidden recipients stay hidden.
@mayteio
mayteio force-pushed the posthog/workflows-partial-suppression branch from 900f249 to 1e9acf7 Compare October 27, 2026 17:03

mayteio commented Oct 27, 2026

Copy link
Copy Markdown
Contributor Author

/trunk merge

@stamphog
stamphog Bot dismissed their stale review October 27, 2026 17:03

A new stamphog review started for this PR — the fresh verdict replaces this approval.

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved.

The change enters risky territory, but owning-team authorship supplies the policy's assurance path and the diff includes focused tests. No unresolved substantive concern was established from the current code; the reported maildev test failure alone does not demonstrate a production defect.

  • Author wrote 8% of the modified lines and has 5 merged PRs in these paths (familiarity MODERATE).
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✓ no deny categories matched
size ✓ 251L, 3F substantive, 435L/6F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1d-complex (435L, 6F, single-area, fix)
stamphog 2.6.1 .stamphog/policy.yml @ 1e9acf7 · reviewed head 1e9acf7

@trunk-io
trunk-io Bot merged commit 51da20e into master Oct 27, 2026
206 of 208 checks passed
@trunk-io
trunk-io Bot deleted the posthog/workflows-partial-suppression branch October 27, 2026 19:30
@deployment-status-posthog

deployment-status-posthog Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-10-27 20:01 UTC Run
prod-us ✅ Deployed 2026-10-27 20:53 UTC Run
prod-eu ✅ Deployed 2026-10-27 20:38 UTC Run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale stamphog Request AI approval (no full review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant