Skip to content

Sandbox sender: send through the sandbox route with the fixed sender identity #232

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

Can a send from the sandbox sender go out through the sandbox route, from the fixed address, with the PostHog-built display name and nothing the step overrides?

Delivers end to end. A workflow send, a workflow test run and a template test send on the sandbox sender reach SES (spec section "Send path"):

  • Prefactor first, no behavior change: sesRouteFor(integration, teamId, trackingEnabled) returns { tenantName, configurationSetName }; own-sender sends stay byte-identical.
  • provider = sandbox: TenantName = SES_SANDBOX_TENANT_NAME, ConfigurationSetName = SES_SANDBOX_CONFIGURATION_SET, From SES_SANDBOX_FROM_ADDRESS with the row's name, FeedbackForwardingEmailAddress the same address, no Reply-To. Step from.email, from.name and replyTo are ignored with one info log line.
  • Sandbox sends are untracked: no pixel, no link rewrite, email_untracked alongside email_sent for live sends as for any untracked send.
  • Global switch WORKFLOWS_SANDBOX_SENDER_ENABLED (default off): off skips every sandbox send with a clear log line, skipped = true, and { success: false } on the VM stack.
  • The team-level tier cap does not apply to sandbox sends and is not drained by them.
  • Analytics: workflows sandbox email sent on SES acceptance (is_test, recipient_count, source) and workflows sandbox email blocked with reason = switch_off. One small helper owns both events; later tickets add reasons.
  • New Node config keys in nodejs/src/cdp/config.ts: WORKFLOWS_SANDBOX_SENDER_ENABLED, SES_SANDBOX_TENANT_NAME, SES_SANDBOX_CONFIGURATION_SET, SES_SANDBOX_FROM_ADDRESS.

Test seams. EmailService.executeSendEmail with real Postgres fixtures, the real rate limiter and sesV2Client.send mocked; assert the SendEmailCommand input, logs, skipped, metrics and captured events. Parameterize test vs live where the behavior is the same.

Write scope. nodejs/src/cdp/services/messaging/email.service.ts, a new sandbox module beside it, nodejs/src/cdp/config.ts, and their tests.

Acceptance criteria

  • Own-sender sends produce the same SendEmailCommand as before (existing tests stay green unchanged).
  • A sandbox send uses the sandbox tenant, set, address and display name, whatever overrides the step carries.
  • Switch off: no SES call, a skip log line, and a blocked event with switch_off.
  • A sandbox send never claims the tier cap buckets.

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): every sandbox send carries a short identification footer that PostHog renders after templating, so a recipient can tell it came from the PostHog test sender and which organization sent it. The team cannot edit or remove it. No List-Unsubscribe header: recipients are members of the sending organization. Write the copy with /writing-user-facing-copy.

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

    @Silthus
    OwnerAuthor

    Note

    🤖 GPT-6.1 Sol responding on behalf of Michael

    Runtime: Codex native subagent
    Worker: /root/impl_232_sol
    Worktree: /home/coder/.t3/worktrees/posthog/sandbox-232-route
    Branch: feat/workflows-sandbox-route
    Cap: four hours, deadline 23:13 UTC on 2026-10-03; the deadline wake removes itself on completion or first wake.
    CodeRabbit slot: 2 of 3

    The per-project integration row shipped in draft PR PostHog#111368 at 72ded8d. This worker owns this ticket alone and builds on that head. Heavy checks share /tmp/heavy-check.lock; review panels share a separate process-held lock so they cannot overlap.

  4. Silthus commented on Oct 3, 2026

    @Silthus
    OwnerAuthor

    Note

    🤖 GPT-6.1 Sol responding on behalf of Michael

    Claimed for implementation at EmailService.executeSendEmail. I will keep the sending switch off by default and add the fixed route, identity and mandatory organization footer here; recipient membership, sandbox caps and the shared pause gate remain in their own tickets. The production constructor wiring in cdp-services.ts is included so the new Node settings reach the sender.

  5. Silthus commented on Oct 3, 2026

    @Silthus
    OwnerAuthor

    Note

    🤖 GPT-6.1 Sol responding on behalf of Michael

    Draft PR #111401 adds the sandbox send route, fixed identity, organization footer and default-off sending switch. The EmailService.executeSendEmail tests, Node typecheck and lint pass; fresh QA is running on 7b73ca18ba8.

  6. Silthus commented on Oct 3, 2026

    @Silthus
    OwnerAuthor

    Note

    🤖 GPT-6.1 Sol responding on behalf of Michael

    The four-hour deadline fired and was removed. The previous native worker is absent from the current registry, with no active check or review lock. Its commits, proof and QA ledger are preserved. The lane is orphaned pending recovery; this does not resolve the ticket or imply a ready review handoff.

  7. assigned and unassigned on Oct 3, 2026
  8. Silthus commented on Oct 3, 2026

    @Silthus
    OwnerAuthor

    Note

    🤖 GPT-6.1 Sol responding on behalf of Michael

    Runtime: Codex native subagent
    Worker: /root/recover_232_sol
    Cap: two hours; deadline 01:16 UTC on 2026-10-04. The deadline task removes itself on first wake or when both workers report.
    CodeRabbit slot: 2 of 3

    Recovering preserved code, proof and review state from the orphaned lane. Existing completed review panels are retained; fresh panels run only where new code requires them. No merge authority.

  9. Silthus commented on Oct 4, 2026

    @Silthus
    OwnerAuthor

    Note

    🤖 GPT-6.1 Sol responding on behalf of Michael

    The two-hour recovery deadline fired and was removed. The recovery worker is absent from the registry, and no check or review lock is active. Its clean branch at 8dc512c, 130 passing tests and completed review reports are retained. QA round 3 is incomplete, so this ticket remains open. Recovering the remaining review and final fix without repeating completed lanes.

  10. assigned and unassigned on Oct 4, 2026
  11. Silthus commented on Oct 4, 2026

    @Silthus
    OwnerAuthor

    Note

    🤖 GPT-6.1 Sol responding on behalf of Michael

    Runtime: T3 Code app-owned child, Codex GPT-6.1 Sol
    Task: node:delegated-task:command%3Amcp%3Aecac12db-1ef4-434a-b75a-80da090a501e%3Adelegate-task%3Asandbox232-final-review-recovery-20261004
    Child: thread:delegated-task:command%3Amcp%3Aecac12db-1ef4-434a-b75a-80da090a501e%3Adelegate-task%3Asandbox232-final-review-recovery-20261004
    Cap: two hours; parent deadline wake removes itself on completion or first wake.
    CodeRabbit slot: 2 of 3

    Continuing the remaining QA lenses on 8dc512c and the final review round after verified fixes. Completed code, tests and review reports are retained. The T3-owned task replaces the orphaned native lane after reviewer dispatch limits. It operates only on the existing route branch and this ticket; no merge authority.

  12. Silthus commented on Oct 4, 2026

    @Silthus
    OwnerAuthor

    Note

    🤖 GPT-6.1 Sol responding on behalf of Michael

    Sandbox routing, fixed sender identity and mandatory organization identification remain the agreed scope; draft PR #111401 is blocked after four QA rounds on cf108096cd162f9d17cd89e7effde552241a3090 because malformed HTML can lose the footer.
    Pending decision: authorize a scoped follow-up that rejects sandbox sends unless finalized HTML reparses with the identification footer directly in the body in both scripting modes?
    The recommendation is yes; the issue stays open and the PR stays draft while awaiting Michael, with the worker terminal and no fifth round started.

  13. Silthus commented on Oct 4, 2026

    @Silthus
    OwnerAuthor

    Note

    🤖 GPT-6.1 Sol responding on behalf of Michael

    Michael authorized the scoped follow-up on 2026-10-04: enforce the identification-footer contract on finalized HTML and reject unsafe sandbox output before sending. The previous four QA rounds and blocked handoff remain preserved; this authorized follow-up receives a fresh bounded QA cycle.

    Runtime: T3 Code app-owned child, Codex GPT-6.1 Sol
    Task: node:delegated-task:command%3Amcp%3A253588e1-b06d-459d-9078-ae7221b04ba9%3Adelegate-task%3Asandbox232-footer-invariant-followup-20261004
    Child: thread:delegated-task:command%3Amcp%3A253588e1-b06d-459d-9078-ae7221b04ba9%3Adelegate-task%3Asandbox232-footer-invariant-followup-20261004
    Worktree: /home/coder/.t3/worktrees/posthog/sandbox-232-route
    Branch: feat/workflows-sandbox-route
    Start head: cf10809
    Cap: four hours, with a deadline wake that deletes itself on first wake or terminal completion.
    CodeRabbit slot: 2 of 3

    The worker owns this ticket and draft PR #111401, with test-first proof and fresh QA. The conductor owns map coordination; no merge authority.

  14. Silthus commented on Oct 4, 2026

    @Silthus
    OwnerAuthor

    Note

    🤖 GPT-6.1 Sol responding on behalf of Michael

    Resolved in draft PR #111401 at 0007a4f79c7: sandbox messages now retain organization identification or fail before delivery.

    The public send tests and native browser proof of captured SES HTML passed; the fresh GPT-6.1 Sol / Claude Sonnet 5.5 panel has no new in-scope fix or decision.

    Fork Node.js CI awaits maintainer approval, and the ancillary reviewer workflow failed before any jobs and denied a rerun for insufficient rights; sending stays off pending the mapped safeguards.

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