Skip to content

Sandbox sender: keep sends out of tier history and record the first sandbox delivery #236

Closed

Description

@Silthus

Map: Map: let teams send a first email before verifying their domain
Spec: Spec: sandbox sender

Cast: GPT-6.1 Sol (Michael, 2026-10-03)

Question

Do sandbox sends stay out of sending-tier history, and does the first sandbox delivery show up as its own funnel step?

Delivers end to end. Spec section "Sending tiers and funnel":

  • Node pushes email_sandbox_sent alongside email_sent for every live sandbox send (test sends push no app metrics, as today).
  • The tier sweep subtracts email_sandbox_sent from email_sent per team per day, floored at zero. Bounces and complaints keep counting.
  • The SES webhook captures workflows sandbox email delivered (is_test) when a Delivery event's configuration set is SES_SANDBOX_CONFIGURATION_SET, for the team named by the verified tracking code. Test sends included.
  • List the funnel in the PR body: workflows sandbox sender provisioned → workflows sandbox email sent → workflows sandbox email delivered (first time), with workflows sandbox email blocked as the drop-off. State that it is not activation.

Test seams. Node: EmailService.executeSendEmail for the companion metric; EmailTrackingService.handleSesWebhook with signed SNS Delivery events on the sandbox and the normal set. Django: recompute_email_sending_tier_for_team over app metrics rows in ClickHouse.

Write scope. nodejs/src/cdp/services/messaging/email.service.ts, nodejs/src/cdp/services/messaging/helpers/ses.ts or email-tracking.service.ts, products/workflows/backend/services/email_sending_tier.py, products/workflows/backend/metrics.py if the metric name belongs there, and tests.

Acceptance criteria

  • A team with only sandbox sends computes the same tier as a team with no sends.
  • A team with own and sandbox sends counts only the own sends toward history.
  • A delivery on the sandbox set fires one delivered event; a delivery on the normal set fires none.

How to build (map #225)

  • /tdd red-green-refactor, outside-in, at the seams named above. Real Postgres, Valkey and ClickHouse; mock only the SES client (Node and boto3) and the PostHog analytics client.
  • Repo skills apply where the diff touches their area: /writing-tests, /improving-drf-endpoints, /django-migrations, /writing-ui-components, /writing-kea-logics, /writing-user-facing-copy, /writing-code-comments.
  • Public repo: state sandbox rules as product rules. Cap values come from the private note the map points at; never quote it. Fixture addresses use example.com.
  • Shipped means a draft PR handed off by /qa-swarm (see the map's Notes for remotes and branches). Never mark ready, enqueue or merge.

Activity

  1. Silthus commented on Oct 3, 2026

    @Silthus
    OwnerAuthor

    Scope addition (conductor, from the outside-code checklist): SES_SANDBOX_CONFIGURATION_SET must also reach the Node service that serves the SES webhook, not only the email worker. Without it the workflows sandbox email delivered event never fires. Cover that service's config in this slice and its test.

  2. self-assigned this
    on Oct 5, 2026
  3. Silthus commented on Oct 5, 2026

    @Silthus
    OwnerAuthor

    Started by ticket worker impl-236 on branch feat/workflows-sandbox-first-delivery (stacked on draft PR PostHog#111820 at a66bb200f89).

    • Cap: 4 hours, until 16:15 UTC.
    • CodeRabbit: slot 1 of 3.
    • Casting: Claude Opus 5.5 owns the lane; GPT-6.1 Sol implements and reviews; Claude Sonnet 5.5 takes the fresh-eyes review lane.
  4. Silthus commented on Oct 5, 2026

    @Silthus
    OwnerAuthor

    Note

    🤖 Claude Sonnet 5.5 responding on behalf of Michael

    Lane owner changed to impl-236b (Claude Sonnet 5.5) after Opus 5.5 hit rate limits. The implementation and gate on bc81df2ec2c stand; QA round 1 on draft PR PostHog#111904 is starting.

  5. Silthus commented on Oct 5, 2026

    @Silthus
    OwnerAuthor

    Note

    🤖 Claude Sonnet 5.5 responding on behalf of Michael

    Shipped in draft PR #111904: QA handoff at d9ef8d19c62, round 2 came back clean with no fix item. Merge risk is low.

    • Tier history: the tier sweep subtracts email_sandbox_sent from email_sent per team per day, floored at zero. Bounces and complaints keep counting. A sandbox-only team gets the same tier as a team with no sends.
    • First delivery: the SES webhook captures workflows sandbox email delivered (is_test) for a Delivery on the sandbox configuration set, for the team named by the signed tracking code. A delivery on any other set, or with an unsigned code, captures nothing.
    • Proof: the touched pytest files (73 passed) and the touched Jest files (370 passed), repo-wide mypy, ruff, prettier, eslint, scoped semgrep and strict preflight pass. Each new test case failed once against a deliberate mutation of the code, then passed. Node-wide TypeScript reports only the unbuilt @posthog/replay-anonymizer package, which this PR does not touch.
    • QA: two cold rounds, seven lenses each (six on GPT-6.1 Sol, fresh eyes on Claude Sonnet 5.5). Round 1 found missing tests for the signed-code gate, the per-day floor and sandbox feedback, plus one nested ternary. All four are fixed. Round 2 found nothing new to fix.
    • Calls: a duplicate delivered event on SNS redelivery and a swallowed team lookup are not fixed. The funnel needs one event, and capture stays best-effort like the sent and blocked events.

    New per-environment setting: SES_SANDBOX_CONFIGURATION_SET must reach the CDP API deployment, not only the email worker. Without it the delivered event never fires.

    Follow-ups, outside this ticket:

    Gaps: fork CI approval is still pending, as on the earlier tickets. CodeRabbit CLI was signed out, so the local pass was skipped.

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

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions