Repository navigation
Conversation
Silthus
force-pushed
the
email-v2/11-first-code-send-event
branch
from
October 31, 2026 14:21
42bfa91 to
1867b69
Compare
Silthus
force-pushed
the
email-v2/12-send-graded-template-version
branch
from
October 31, 2026 14:30
99d058f to
5117bfd
Compare
Silthus
force-pushed
the
email-v2/11-first-code-send-event
branch
from
October 31, 2026 14:31
1867b69 to
5e72c0b
Compare
Silthus
force-pushed
the
email-v2/12-send-graded-template-version
branch
from
October 31, 2026 14:32
5117bfd to
8e26418
Compare
Silthus
force-pushed
the
email-v2/12-send-graded-template-version
branch
2 times, most recently
from
October 31, 2026 15:50
061e4b8 to
5b09e8f
Compare
… checked against Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Silthus
force-pushed
the
email-v2/11-first-code-send-event
branch
from
October 31, 2026 16:17
aea6a21 to
f98f829
Compare
Silthus
force-pushed
the
email-v2/12-send-graded-template-version
branch
from
October 31, 2026 16:21
5b09e8f to
2e19d2d
Compare
This was referenced Oct 31, 2026
…d-graded-template-version # Conflicts: # products/messaging/backend/facade/transactional_email.py # products/messaging/backend/presentation/views/message_templates.py
…s capture_engagement Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d-graded-template-version
…d-graded-template-version
…d-graded-template-version Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d-graded-template-version Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Silthus
added a commit
that referenced
this pull request
Nov 2, 2026
#585 reports a project's first code send through one operation; the merge keeps that operation and passes it this part's delivery_mode property. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d-graded-template-version Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d-graded-template-version Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…end sets capture_engagement Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d-graded-template-version
This was referenced Nov 2, 2026
Owner
Author
|
Moved to PostHog#127188. |
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; new in the rebuild, with no closed PR to replace: a template send delivers the content Django graded (decision D3 on map #461).
Problem
send/email/right after an upload can deliver the previous content, while Django answered that the caller'stemplate.hashis current.Impact
Pays into Grow Workflows through billed
workflows_emailsactions per paying org: a developer who uploads a template and sends it at once gets the new content. In scope: thesend/email/route.Merge Danger
Note
🟢 Merge risk: low · two-way door: a revert stops sending the version, and delivery works as today
send/email/only, behindworkflows-email-from-code. Rendered-content sends and the legacy template send route don't change.template.keynow goes to cdp-api's/transactional_email/send, which feat(cdp): deliver a code send's content on its own transactional email route #565 adds. Against cdp-api master, those sends answer 503delivery_unavailableand send nothing (cross-version proof below). They stay behindworkflows-email-from-code.template.keythat crosses a deploy of feat(messaging): capture email first code send once per project #583 without feat(messaging): send the workflow version a template send by key was checked against #585 answers 422, because the two pick different routes for one fingerprint. Key sends exist only inside this stack, so no stored fingerprint has that shape today.Changes
send.hogFlowVersion. Django reads it after grading the hash, from the writer database, so it is never older than the graded content.Unavailableto the caller), without a stack trace in the log.503 delivery_unavailableshape with its owndetail:{ "type": "server_error", "code": "delivery_unavailable", "detail": "This template's newest version hasn't reached the email pipeline yet, so nothing was sent. Retry in a few seconds, with the same Idempotency-Key if you sent one.", "attr": null }workflow_version_behind, never reaches the caller.template.keyposts the version, with or withoutfrom,reply_toorcapture_engagement, so it goes to the accepted-send route. Its defaultcaptureEngagement: truechanges nothing: the project's setting still decides.template.idkeeps master's route, which grades no hash and posts no version. One that setscapture_engagement(feat(messaging): send a stored template by key on send/email #574), which takesworkflows-email-from-code, goes to the accepted-send route and posts the version, like a send bytemplate.key. Only a field the caller sent counts, so a form body that omits it keeps master's route.How did you test this code?
Test seams: the DRF client on
send/email/, asserting the body posted to cdp-api (test_transactional_email.py).Test rationale, each run red first:
template.keythat posts no version, with or without the send options, or a version from before a save. A PATCH that saves new content is followed by a send that must post the new version.senddict, which pins that the field is absent there.workflow_version_behindanywhere in the body.Proofs (saved with
fleet proofonef44c9b):f9abed0841c), mail caught in maildev. Sends by id and bytemplate.key, uploads, the superseded and outdated hashes, and the send options all answer and deliver as on feat(messaging): capture email first code send once per project #583.5304e8d(cdp-api at feat(cdp): send a template from the workflow version Django graded #584's5d66b71): posthog-node (feat(node): upload local email templates by hash on send posthog-js#7) and posthog-python (feat: send email content from code with posthog.emails.send posthog-python#2) send a local template under a new key:409 template_outdated, upload201, send202, delivered. Onto a template edited in PostHog, the upload answers409 template_edited_in_posthog(feat(messaging): lock code-managed templates and adopt templates into code #631), the SDK reports it, and nothing is sent.template.keyanswer 503delivery_unavailableand send nothing. Sends by id deliver.Earlier live run, 2026-10-31
Django at
3a30f51c529, cdp-api and both CDP workers at #584 (f9abed0841c), mail caught in maildev.template.keytemplate_supersededwarning, the new content deliveredtemplate_outdated, nothing sentfrom,reply_toandcapture_engagementsupport@example.comCI: the fork has no Depot runners, so those jobs never start.
Semgrep Checks Passfails on every fork PR, because its path filter job runs only for the PostHog owner; semgrep with the repo's rules passes locally on the changed files. The lane check reportscross_lane=false.Release status
Docs update
None.
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Agent: Claude Code, Claude Opus 5.5 (claude-opus-5-5)
srv2of map Map: send code-managed email templates with one PostHog call #461, for decision D3; restacked as lanedj3onto feat(messaging): capture email first code send once per project #583 and master's refusal code, so the 503 drops itswhyandfix.modernize-pr,tdd,writing-tests,writing-user-facing-copy,qa-swarm,pr-description,writing-pr-descriptions.delivery_unavailablewith the version lag indetail; replaces a new publicworkflow_version_behindcodewhyandfix?detailtemplate.idwithcapture_engagementpost the version?Open: a send by
template.idposts no version, so one right after an upload can still use the cached copy. Recommendation: leave it; that send grades no hash.Involvement: one decision from Michael and one from the map's conductor; review round 1 (GPT-6.1 Sol, Opus fresh eyes) fixed, then final reads by GPT-6.1 Sol; dj3 round 1 (GPT-6.1 Sol) fixed, its final read raised the cdp-api route order, answered in the merge risk; live run captured; cross-version proof in #584. dj3 QA swarm (GPT-6.1 Sol review, Opus fresh eyes): one merge fix, two rejections; live run, shadow diff and cross-version on this head.
🤖 Generated with Claude Code