Repository navigation
Build strand: the code-content send API (Resend parity) #535
Copy link
Copy link
Open
Labels
Description
Map: Map: send code-managed email templates with one PostHog call
Spec: Spec: Resend parity, section "Slicing: builders per strand"
Contract: PostHog/posthog#120921
Repository: PostHog/posthog
Lane: one build lane, Opus 5.5 high, its own worktree, wall-time cap 12 h (waiting on other strands excluded). Prompt: lanes/send-api.md
Blocked by (strands): none
Waits on (Harley): PostHog#120446, PostHog#120392 and PostHog#120510 merged, before slice 05
Slices, in order
- Slice 01: Send attribution: count posthog-python as an SDK, and attribute the contract's send route. PR: new commits on the restacked draft feat(messaging): attribute transactional sends to their source PostHog/posthog#120628.
- Slice 02: Email send policy: one check for admission and delivery (R1). PR: a draft PR stacked on feat(messaging): upload, send and lock code-managed email templates PostHog/posthog#118924 with
gh stack. - Slice 03: EmailSendCommand and the typed CDP outcome (R2). PR: a draft PR stacked on 02.
- Slice 04: Send route: the code form, rendered content and local templates. PR: a draft PR stacked on 03. Opened as feat(messaging): send email rendered by the caller's code by key PostHog/posthog#121272.
- Slice 07: Send route: run the send checks before the 202. PR: a draft PR stacked on 04. Opened as feat(messaging): refuse a transactional email the send checks would skip PostHog/posthog#121850.
- Slice 05: idempotentEnqueue over the idempotency store (R3). PR: a draft PR stacked on 07, built once Harley's store lands. No PR: master already has the idempotent enqueue.
- Slice 06: Send route: idempotency for both forms. PR: a draft PR stacked on 05. Opened as feat(messaging): send each email once per idempotency key on send/email PostHog/posthog#121867.
- Slice 08: Email status record and GET emails/{message_id}. PR: its own draft PR on
masteronce slice 04 merges, so the stack stays shallow. Opened as three stacked layers: feat(messaging): add the email_status clickhouse table PostHog/posthog#121896 (table), feat(cdp): record each transactional email's status PostHog/posthog#121897 (writer), feat(messaging): read a transactional email's status by id or recipient PostHog/posthog#121898 (read).
Done when
- Every slice has its draft PR with its red/green test and owed proofs.
- One GPT-6.1 Sol
highreview of the strand's whole diff, findings fixed or rejected with a reason. - The lane's handoff, with its measurements, reached the map conductor.
Reactions are currently unavailable
Activity
Silthus commentedon Oct 21, 2026
Dispatched as the strand lane (builders-per-strand trial): Claude Opus 5.5, high, new worktree from
spec/code-managed-templates, 12 h building cap. Slices in the order of this issue's checklist.Silthus commentedon Oct 21, 2026
Claimed by the send-api lane (Opus 5.5, high). Starting with slice 01 on the restacked PostHog#120628.
Silthus commentedon Oct 21, 2026
Parked after slice 04, before slice 07.
Slice 04 is up as draft PostHog#121272: the send route's code form, the fixed slot in the worker, and the v3 tracking reader. Its proofs are on the PR: tests, the cross-version run, and a live run that landed every email in maildev.
The stack is now four layers: PostHog#118924 → PostHog#121083 → PostHog#121084 → PostHog#121272. Slice 07 would make it five.
The question: PostHog#118924 conflicts with
master(messaging migration number, template model and services, the template library frontend; about ten files), and every layer above inherits the conflict. How do you want to unblock the stack?master(recommended). I force-push all four branches,spec/code-managed-templatesincluded, rerun the gates, and you then review and merge feat(messaging): upload, send and lock code-managed email templates PostHog/posthog#118924, refactor(cdp): move the email send checks into one send policy PostHog/posthog#121083 and refactor(messaging): send transactional email through one command and a typed outcome PostHog/posthog#121084. After they land, I restack feat(messaging): send email rendered by the caller's code by key PostHog/posthog#121272 onmasterand go on with 07.Also open on PostHog#121272, decided by me and listed in its Decisions table: v3 tracking codes ship with minting off, to close a cross-version bug where an old parser files a bounce under the wrong team. Turning minting on is a one-line follow-up after the deploy.
Slice 05 is unblocked (the idempotency store is on
master), so 07, 05, 06 and 08 follow once the base is settled.Silthus commentedon Oct 21, 2026
Unparked: the stack is rebased onto
master(option 1).All four branches are force-pushed onto
masterate23fa706bd0:756474ff699619d7421be07f44351eeab5eb8d2ca083mastermoved again while the first rebase ran its gates: template tags (PostHog#120931) conflicted with the base, so the base was rebased a second time. The gates pass on every layer, and each of the three lower PRs has a GPT-6.1 Sol final read with every finding fixed. PostHog#118769 is superseded: PostHog#118924 carries its migration as0009and0010.Decisions made during the rebase:
master's drafts? An upload clears a draft, and the lock refuses a draft save, so a code-managed template never holds one.PATCHof onlysensitivepasses, because the setting protects what PostHog keeps about each email and is not part of the uploaded file.PATCHof onlytagspasses, because tags organize the library and are not part of the uploaded file.origin_productenum. A separate create-only enum clashed withmaster's broadcast wizard types.Next: PostHog#118924, PostHog#121083 and PostHog#121084 wait for review. After they land, I restack PostHog#121272 on
masterand go on with slices 07, 05, 06 and 08.