Repository navigation
Conversation
Silthus
force-pushed
the
email-v2/08-admission-checks
branch
from
October 25, 2026 18:50
c55794e to
66e7f10
Compare
Silthus
force-pushed
the
email-v2/09-route-idempotency-cdp
branch
4 times, most recently
from
October 25, 2026 21:20
021c5f0 to
1a43bdd
Compare
This was referenced Oct 25, 2026
A keyed retry of a code send replays after the project's default sender changed, and after Django resolved a named address to another sender. The key leaves out a default sender (`from.isDefault`) and keys a named address on the address and name the caller sent, not on the integration Django picked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Silthus
force-pushed
the
email-v2/09-route-idempotency-cdp
branch
from
October 31, 2026 08:41
1a43bdd to
38613ba
Compare
Silthus
changed the base branch from
email-v2/08-admission-checks
to
email-v2/01-cdp-code-send
October 31, 2026 08:41
This was referenced Oct 31, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner
Author
|
Moved to PostHog#127129. |
This was referenced Nov 2, 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.
Part of the code-first rebuild of the email send stack; replaces parts of PostHog#121867 (one send per idempotency key on
send/email/).Problem
send/email/can get422 idempotency_key_reusedinstead of a replay, after the project's default sender changed in between.isDefault. This is the cdp-api half.Impact
Pays into Grow Workflows through billed
workflows_emailsactions per paying org: callers can retry without double sends. In scope: CDP email sends.Merge Danger
Warning
🔴 Merge risk: high · two-way door: a revert puts the resolved sender back in the key
transactional_email/sendchange, and onlysend/email/makes them, behindworkflows-email-from-code. The template route is unchanged.422instead of a replay, and a revert does the reverse.send.from.isDefaultis new and optional. An older Django omits it, and the key then binds the integration as before.Changes
fromout of the key when Django marks it as the project's default sender (isDefault).isDefault: falseand a missing flag give the same key, so a retry that leaves the flag out still replays.43333f8. The earlier version also moved the active check after the key lookup and gave the inactive refusal its own why and fix. feat(cdp): deliver a code send's content on its own transactional email route #565 now does the first, and Django on master maps cdp-api'snot_sendableitself (feat(messaging): refuse transactional sends the email worker would skip PostHog/posthog#123019), so both left this PR.How did you test this code?
Test seams: cdp-api's accepted-send route over HTTP (
cdp-api.serial.test.ts).Test rationale:
returns the first send for a retry of %s and queues nothing new, one table for every replay, run red first: a retry after the default sender or the integration behind a named address changed that answers422, and a retry that leaves outisDefault: false. The two newrejects the key for a request with %srows pin that another named sender, or a named sender instead of the default, still answers422.Proofs at
64d8940:typescript:check, eslint, oxlint and prettier pass, andcdp-api.serial.test.tspasses (168 tests).cdp-api.serial.test.tsat HEAD, run against feat(cdp): deliver a code send's content on its own transactional email route #565's head920e7c9, moves two outcomes: the two sender-change retries replay instead of answering422. The changed-content, other-route and other-sender rows still answer422. Evidence is saved withfleet proof.dj3of map Map: send code-managed email templates with one PostHog call #461 runs the live stack. Not run here.Proofs at the pre-rebase head
1a43bdd, with Django at #57879e619breplayed: true, one email in totalreplayed: false, two emailsidempotency_key_reused, nothing sentfrom, then its retryreplayed: true, one email in totalreplayed: true, one email in totalfrom, retry after the default sender changedreplayed: true, one email in totalThe paused-key rows of that table moved with the active check to #565.
CI: the fork has no Depot runners, so those jobs never start, and no secret for the project-board app, so
flags-project-boardfails. The lane check passes: against master the PR changes onlynodejs/(cross_lane=false).Release status
Docs update
None in this PR. The send API docs follow when the flag opens.
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Agent: Claude Code, Claude Opus 5.5 (claude-opus-5-5)
Skills:
tdd,writing-tests,stacking-prs,modernize-pr,pr-description,writing-pr-descriptionstemplate.hashreplay?not_sendableto its own answernode3, restack of 2026-10-31isDefaultkey on the address?isDefaultonly when the caller named no address, so the address there is the default's and must stay out of the keyInvolvement: rounds 1 and 2 and two final reads before the restack; after it, one GPT-6.1 Sol review, a full two-family panel on 2026-11-01 (8 lanes: 2 fixed, 1 rejected, shadow diff owed and run) and a narrow round (2 lanes: description fixes, 2 rejected); pre-rebase live and cross-version proof kept above.
More context for AI agents
node3of map Map: send code-managed email templates with one PostHog call #461, Claude Code.senderTheCallerChoseinnodejs/src/cdp/cdp-api.ts.email-v2/01-cdp-code-send; they are feat(cdp): deliver a code send's content on its own transactional email route #565's diff, not this PR's.🤖 Generated with Claude Code