Repository navigation
feat(messaging): send an email with inline html from code - #127123
Conversation
…endpoint The single send endpoint now takes either a stored template (`template.id`) or content the caller's code rendered (`template_key`, `subject`, `html` or `text`, `from`, `reply_to`, `capture_engagement`). The code form is gated by `workflows-email-from-code` and goes out through cdp-api's accepted-send route, under one hidden workflow per key. A key that names a template uses that template's transactional workflow. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Both SDKs send distinct_id as a top-level field with template_key, so the code form refused their sends as an unknown field. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
…s off template sends - A code send the pipeline does not confirm answers 503 with a detail that says the email may still go out, so a caller checks before it sends again. - capture_engagement has no schema default, so a generated client no longer adds it to a template send, which the server refuses. - A template send honors a top-level distinct_id, like a code send does. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The 503 for an unconfirmed code send now says to send again with the same Idempotency-Key. The template_key help no longer uses a password reset as its example, because code sends track their links. A test covers a key whose workflow was deleted. Co-Authored-By: Claude Opus 5.5 <[email protected]>
The unconfirmed-send detail promised that a retry with the same Idempotency-Key never sends twice, but the key is kept for 24 hours. Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
😎 Stack merged successfully - details. |
🤖 CI report
|
| File | Size | Δ vs base |
|---|---|---|
render-query/src/render-query/render-query.js |
21.21 MiB | 🔺 +18.7 KiB (+0.1%) |
posthog-app/_parent/products/workflows/frontend/Broadcasts/BroadcastScene.js |
151.3 KiB | 🔺 +6.0 KiB (+4.1%) |
posthog-app/_parent/products/stamphog/frontend/scenes/StamphogScene/StamphogScene.js |
25.9 KiB | 🔺 +2.9 KiB (+12.8%) |
Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report
✅ Eager graph — within budget
How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.
| Root | Eager (shipped) | Δ vs base | Budget |
|---|---|---|---|
entry (logged-out pages, app bootstrap)src/index.tsx |
1.02 MiB · 25 files | 🟢 -98 B (-0.0%) | █████████░ 89.0% of 1.14 MiB |
logged-out boot: index + App + bootApp (preloaded by every page, including /login)src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts |
2.99 MiB · 623 files | 🟢 -98 B (-0.0%) | ██████████ 98.7% of 3.03 MiB |
authenticated shell (every logged-in page)src/scenes/AuthenticatedShell.tsx |
3.86 MiB · 1,233 files | 🟢 -98 B (-0.0%) | ██████████ 96.2% of 4.01 MiB |
dashboard scenesrc/scenes/dashboard/Dashboard.tsx |
8.25 MiB · 3,033 files | 🟢 -98 B (-0.0%) | ████████░░ 83.9% of 9.84 MiB |
today home pathsrc/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx |
4.71 MiB · 1,757 files | 🟢 -98 B (-0.0%) | ██████████ 95.5% of 4.93 MiB |
AI-first home pathsrc/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx |
5.96 MiB · 2,255 files | 🟢 -98 B (-0.0%) | ██████████ 95.1% of 6.27 MiB |
events scenesrc/scenes/activity/explore/EventsScene.tsx |
7.29 MiB · 2,614 files | 🟢 -98 B (-0.0%) | ████████░░ 77.5% of 9.41 MiB |
web analytics scenesrc/scenes/web-analytics/WebAnalyticsScene.tsx |
8.20 MiB · 2,941 files | 🟢 -98 B (-0.0%) | ████████░░ 78.2% of 10.49 MiB |
replay detail scenesrc/scenes/session-recordings/detail/SessionRecordingDetail.tsx |
7.47 MiB · 2,681 files | 🔺 +17.2 KiB (+0.2%) | █████████░ 91.9% of 8.13 MiB |
🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 products/product_analytics/frontend/insights/activityDescriptions stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/layout/navigation-3000/navigationLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/dashboard/dashboardLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/lemon-ui/LemonMarkdown/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/RichContentEditor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/CodeSnippet/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/taxonomy/core-filter-definitions-by-group.json stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/zod/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/logic/billing/billingLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/ProductEmptyState/productSetupPreloadLogic.ts stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/kea-test-utils/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/product_analytics/frontend/insights/activityDescriptions stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/kea-test-utils/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/dashboards/frontend/widgets/previews/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/panel-layout/ProjectTree/menus/SessionReplayMenuItems.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/taxonomy/core-filter-definitions-by-group.json stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/navigation/projectNoticeLogic.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/navigation/ImpersonationNotice/ImpersonationNotice.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/cohorts/cohortSavedToast.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/wizard/frontend/runs/WizardRunDetailsDrawer.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/TodayShell.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/TodayContextPanelFloating.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/TodaySidePanel.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/TodayTopBar.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/todayRailItems.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/subscriptions/frontend/components/Subscriptions/exportNudge/exportNudgeLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/billing/paymentEntryLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/conversations/frontend/components/SidePanel/sidepanelTicketsLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/zod/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/authentication/auth/loginLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/authentication/shared/passkeyLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/surveys/constants.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@tiptap/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/prosemirror- stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/re2js/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/react-markdown/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/Command/Command.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 packages/quill/packages/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 zod/v4/locales/de.js stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 packages/quill/packages/quill/dist/ stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/queries/validators.js stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/queries/web-analytics-validators.js stays out of src/scenes/dashboard/Dashboard.tsx
🟢 node_modules/re2js/ stays out of src/scenes/dashboard/Dashboard.tsx
🟢 node_modules/kea-test-utils/ stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/debug/HogDebug.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/queries/QueryEditor/QueryEditor.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 packages/quill/packages/quill/dist/ stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/scenes/project-homepage/today/TodayReportPage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/queries/Query/Query.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 packages/quill/packages/charts/ stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 packages/quill/packages/quill/dist/ stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 products/posthog_ai/frontend/components/tool/widgets/VisualizationWidget.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 products/posthog_ai/frontend/components/tool/widgets/RecordingsWidget.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 src/queries/Query/Query.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 products/posthog_ai/frontend/api/primitives.ts stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 src/scenes/notebooks/Notebook/notebookLogic.ts stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 packages/quill/packages/quill/dist/ stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/queries/validators.js stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/queries/web-analytics-validators.js stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 node_modules/re2js/ stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 node_modules/kea-test-utils/ stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/queries/nodes/InsightViz/InsightViz.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/queries/nodes/DataVisualization/DataVisualization.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/queries/nodes/WebOverview/WebOverview.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/scenes/debug/HogDebug.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/queries/QueryEditor/QueryEditor.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 packages/quill/packages/quill/dist/ stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 src/scenes/web-analytics/LiveMetricsDashboard/LiveWebAnalyticsMetrics.tsx stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 products/product_analytics/frontend/insights/trends/WorldMap/countryVectors.tsx stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 src/queries/validators.js stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 node_modules/re2js/ stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 node_modules/kea-test-utils/ stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 src/scenes/debug/HogDebug.tsx stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 src/queries/QueryEditor/QueryEditor.tsx stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 packages/quill/packages/quill/dist/ stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
🟢 src/scenes/settings/SettingsMap.tsx stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
🟢 @jspm/core/nodelibs/browser/fs stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
🟢 node_modules/re2js/ stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
🟢 node_modules/kea-test-utils/ stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
🟢 node_modules/d3/src/ stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
Largest files eagerly shipped from src/index.tsx
| Size | File |
|---|---|
| 126.8 KiB | ../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.production.min.js |
| 24.6 KiB | ../node_modules/.pnpm/[email protected]/node_modules/buffer/index.js |
| 6.3 KiB | ../node_modules/.pnpm/[email protected]/node_modules/react/cjs/react.production.min.js |
| 4.7 KiB | ../node_modules/.pnpm/@[email protected]/node_modules/@jspm/core/nodelibs/browser/process.js |
| 4.0 KiB | ../node_modules/.pnpm/[email protected]/node_modules/scheduler/cjs/scheduler.production.min.js |
| 1.9 KiB | src/lib/utils/bootTiming.ts |
| 1.7 KiB | src/index.tsx |
| 1.7 KiB | ../node_modules/.pnpm/[email protected]/node_modules/base64-js/index.js |
| 1.2 KiB | src/RootErrorBoundary.tsx |
| 1.1 KiB | src/lib/utils/deferredAppContext.ts |
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
| Size | File |
|---|---|
| 356.2 KiB | ../node_modules/.pnpm/[email protected]_@[email protected][email protected]/node_modules/posthog-js/dist/module.mjs |
| 224.1 KiB | ../node_modules/.pnpm/@[email protected][email protected][email protected][email protected]/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 126.8 KiB | ../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.production.min.js |
| 104.6 KiB | src/products.tsx |
| 89.6 KiB | src/lib/api.ts |
| 68.7 KiB | src/lib/lemon-ui/icons/icons.tsx |
| 41.9 KiB | ../node_modules/.pnpm/@[email protected][email protected][email protected][email protected]/node_modules/@dnd-kit/core/dist/core.esm.js |
| 36.4 KiB | ../node_modules/.pnpm/[email protected]_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js |
| 28.4 KiB | src/lib/constants.tsx |
| 24.6 KiB | ../node_modules/.pnpm/[email protected]/node_modules/buffer/index.js |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
| Size | File |
|---|---|
| 356.2 KiB | ../node_modules/.pnpm/[email protected]_@[email protected][email protected]/node_modules/posthog-js/dist/module.mjs |
| 224.1 KiB | ../node_modules/.pnpm/@[email protected][email protected][email protected][email protected]/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 126.8 KiB | ../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.production.min.js |
| 104.6 KiB | src/products.tsx |
| 89.6 KiB | src/lib/api.ts |
| 68.7 KiB | src/lib/lemon-ui/icons/icons.tsx |
| 41.9 KiB | ../node_modules/.pnpm/@[email protected][email protected][email protected][email protected]/node_modules/@dnd-kit/core/dist/core.esm.js |
| 36.4 KiB | ../node_modules/.pnpm/[email protected]_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js |
| 32.3 KiB | src/lib/utils/eventUsageLogic.ts |
| 28.4 KiB | src/lib/constants.tsx |
Largest files eagerly shipped from src/scenes/dashboard/Dashboard.tsx
| Size | File |
|---|---|
| 356.2 KiB | ../node_modules/.pnpm/[email protected]_@[email protected][email protected]/node_modules/posthog-js/dist/module.mjs |
| 224.1 KiB | ../node_modules/.pnpm/@[email protected][email protected][email protected][email protected]/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 126.8 KiB | ../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.production.min.js |
| 114.5 KiB | ../node_modules/.pnpm/[email protected]_@[email protected][email protected]/node_modules/posthog-js/dist/surveys-preview.js |
| 110.8 KiB | ../packages/quill/packages/primitives/dist/index.js |
| 104.6 KiB | src/products.tsx |
| 96.1 KiB | ../packages/quill/packages/components/dist/index.js |
| 94.6 KiB | ../node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@tiptap/core/dist/index.js |
| 92.6 KiB | ../node_modules/.pnpm/[email protected]/node_modules/prosemirror-view/dist/index.js |
| 89.6 KiB | src/lib/api.ts |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
| Size | File |
|---|---|
| 356.2 KiB | ../node_modules/.pnpm/[email protected]_@[email protected][email protected]/node_modules/posthog-js/dist/module.mjs |
| 224.1 KiB | ../node_modules/.pnpm/@[email protected][email protected][email protected][email protected]/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 126.8 KiB | ../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.production.min.js |
| 110.8 KiB | ../packages/quill/packages/primitives/dist/index.js |
| 104.6 KiB | src/products.tsx |
| 89.6 KiB | src/lib/api.ts |
| 68.7 KiB | src/lib/lemon-ui/icons/icons.tsx |
| 68.0 KiB | ../products/posthog_ai/frontend/logics/runStreamLogic.ts |
| 41.9 KiB | ../node_modules/.pnpm/@[email protected][email protected][email protected][email protected]/node_modules/@dnd-kit/core/dist/core.esm.js |
| 36.4 KiB | ../node_modules/.pnpm/[email protected]_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
| Size | File |
|---|---|
| 356.2 KiB | ../node_modules/.pnpm/[email protected]_@[email protected][email protected]/node_modules/posthog-js/dist/module.mjs |
| 224.1 KiB | ../node_modules/.pnpm/@[email protected][email protected][email protected][email protected]/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 126.8 KiB | ../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.production.min.js |
| 110.8 KiB | ../packages/quill/packages/primitives/dist/index.js |
| 104.6 KiB | src/products.tsx |
| 89.6 KiB | src/lib/api.ts |
| 68.7 KiB | src/lib/lemon-ui/icons/icons.tsx |
| 68.0 KiB | ../products/posthog_ai/frontend/logics/runStreamLogic.ts |
| 41.9 KiB | ../node_modules/.pnpm/@[email protected][email protected][email protected][email protected]/node_modules/@dnd-kit/core/dist/core.esm.js |
| 41.7 KiB | src/scenes/hog-functions/sub-templates/sub-templates.ts |
Largest files eagerly shipped from src/scenes/activity/explore/EventsScene.tsx
| Size | File |
|---|---|
| 356.2 KiB | ../node_modules/.pnpm/[email protected]_@[email protected][email protected]/node_modules/posthog-js/dist/module.mjs |
| 224.1 KiB | ../node_modules/.pnpm/@[email protected][email protected][email protected][email protected]/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 126.8 KiB | ../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.production.min.js |
| 110.8 KiB | ../packages/quill/packages/primitives/dist/index.js |
| 104.6 KiB | src/products.tsx |
| 96.1 KiB | ../packages/quill/packages/components/dist/index.js |
| 94.6 KiB | ../node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@tiptap/core/dist/index.js |
| 92.6 KiB | ../node_modules/.pnpm/[email protected]/node_modules/prosemirror-view/dist/index.js |
| 89.6 KiB | src/lib/api.ts |
| 75.5 KiB | src/scenes/dashboard/dashboardLogic.tsx |
Largest files eagerly shipped from src/scenes/web-analytics/WebAnalyticsScene.tsx
| Size | File |
|---|---|
| 356.2 KiB | ../node_modules/.pnpm/[email protected]_@[email protected][email protected]/node_modules/posthog-js/dist/module.mjs |
| 224.1 KiB | ../node_modules/.pnpm/@[email protected][email protected][email protected][email protected]/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 126.8 KiB | ../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.production.min.js |
| 114.5 KiB | ../node_modules/.pnpm/[email protected]_@[email protected][email protected]/node_modules/posthog-js/dist/surveys-preview.js |
| 110.8 KiB | ../packages/quill/packages/primitives/dist/index.js |
| 104.6 KiB | src/products.tsx |
| 96.1 KiB | ../packages/quill/packages/components/dist/index.js |
| 94.6 KiB | ../node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@tiptap/core/dist/index.js |
| 92.6 KiB | ../node_modules/.pnpm/[email protected]/node_modules/prosemirror-view/dist/index.js |
| 89.6 KiB | src/lib/api.ts |
Largest files eagerly shipped from src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
| Size | File |
|---|---|
| 356.2 KiB | ../node_modules/.pnpm/[email protected]_@[email protected][email protected]/node_modules/posthog-js/dist/module.mjs |
| 321.5 KiB | ../node_modules/.pnpm/[email protected]_@[email protected][email protected]/node_modules/posthog-js/dist/rrweb.js |
| 224.1 KiB | ../node_modules/.pnpm/@[email protected][email protected][email protected][email protected]/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 126.8 KiB | ../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.production.min.js |
| 110.8 KiB | ../packages/quill/packages/primitives/dist/index.js |
| 104.6 KiB | src/products.tsx |
| 96.1 KiB | ../packages/quill/packages/components/dist/index.js |
| 94.6 KiB | ../node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@tiptap/core/dist/index.js |
| 92.6 KiB | ../node_modules/.pnpm/[email protected]/node_modules/prosemirror-view/dist/index.js |
| 89.6 KiB | src/lib/api.ts |
Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479
✅ Frontend layers — no new upward imports, 0 removed
Static imports from a lower frontend layer into a higher one (layers, lowest first: kernel, shell, query, features). Compared with the base branch, so an import that a PR also adds to layer-baseline.txt still shows here. Sizes are the source bytes on the eager path of every logged-in page.
| Layer | Eager source bytes | Δ vs base |
|---|---|---|
| kernel | 2.99 MiB | no change |
| shell | 2.38 MiB | no change |
| query | 56.4 KiB | no change |
| features | 356.2 KiB | no change |
Download the interactive layer graph for this PR.
Posted automatically by check-layers
✅ Toolbar bundle — eager 2.38 MiB within budget
What the toolbar ships to customer pages, measured from the esbuild output (minified, post-tree-shake). The eager set is the entry plus everything statically imported from it — fetched before any feature runs; deferred chunks load lazily. The eager guardrail is 5.72 MiB. Each output file must also stay below 10 MB, where CloudFront stops compressing it. The module boundary is enforced separately by check-toolbar-graph.
| Metric | Size | Δ vs base | Budget |
|---|---|---|---|
| Eager (shipped) entry + static imports |
2.38 MiB · 19 files | 🟢 -98 B (-0.0%) | ████░░░░░░ 41.6% of 5.72 MiB |
| Deferred (lazy) | 2.11 MiB · 42 files | no change | n/a — loads on demand |
Loader dist/toolbar.js |
1.2 KiB | no change | █░░░░░░░░░ 6.0% of 19.5 KiB |
Largest eagerly-shipped chunks
| Size | File |
|---|---|
| 954.1 KiB | dist/toolbar/toolbar-app-R2Z72QDW.css |
| 727.8 KiB | dist/toolbar/chunk-chunk-P74TPHVK.js |
| 261.3 KiB | dist/toolbar/chunk-chunk-LPZVU6KG.js |
| 131.8 KiB | dist/toolbar/chunk-chunk-FDH2IBXT.js |
| 130.3 KiB | dist/toolbar/chunk-chunk-ZIENG33C.js |
| 75.7 KiB | dist/toolbar/toolbar-app-76MY5FO6.js |
| 69.0 KiB | dist/toolbar/chunk-chunk-TSAL54PB.js |
| 33.8 KiB | dist/toolbar/chunk-chunk-RNAY4ENA.js |
| 21.7 KiB | dist/toolbar/chunk-chunk-3FRNZBT5.js |
| 6.8 KiB | dist/toolbar/chunk-chunk-DV7IWQNF.js |
Posted automatically by check-toolbar-size · sizes are toolbar output bytes (shipped, post-tree-shake) from the esbuild metafile
✅ Dist folder size — 🔺 +448.7 KiB (+0.0%)
Total size of the built frontend/dist folder (all assets), compared against the base branch.
Total: 972.17 MiB · 🔺 +448.7 KiB (+0.0%)
ℹ️ MCP UI apps size — 35 app(s), 29272.4 KB JS
Built size of each MCP UI app (main.js + styles.css).
| App | JS | CSS |
|---|---|---|
| debug | 848.8 KB | 214.1 KB |
| explain-chart | 1123.9 KB | 214.1 KB |
| action | 771.5 KB | 214.1 KB |
| action-list | 839.8 KB | 214.1 KB |
| cohort | 770.5 KB | 214.1 KB |
| cohort-list | 838.8 KB | 214.1 KB |
| email-template | 770.4 KB | 214.1 KB |
| error-details | 775.4 KB | 214.1 KB |
| error-issue | 772.7 KB | 214.1 KB |
| error-issue-list | 841.2 KB | 214.1 KB |
| experiment | 836.9 KB | 214.1 KB |
| experiment-list | 840.5 KB | 214.1 KB |
| experiment-results | 838.7 KB | 214.1 KB |
| feature-flag | 839.2 KB | 214.1 KB |
| feature-flag-list | 842.9 KB | 214.1 KB |
| feature-flag-testing | 773.8 KB | 214.1 KB |
| inline-scan | 771.0 KB | 214.1 KB |
| insight-actors | 838.3 KB | 214.1 KB |
| llm-costs | 835.6 KB | 214.1 KB |
| session-recording | 772.8 KB | 214.1 KB |
| survey | 772.1 KB | 214.1 KB |
| survey-global-stats | 835.6 KB | 214.1 KB |
| survey-list | 840.5 KB | 214.1 KB |
| survey-stats | 835.6 KB | 214.1 KB |
| trace-span | 770.9 KB | 214.1 KB |
| trace-span-list | 839.7 KB | 214.1 KB |
| vision-observation-list | 838.9 KB | 214.1 KB |
| workflow | 770.9 KB | 214.1 KB |
| workflow-list | 839.1 KB | 214.1 KB |
| workflow-setup-status | 770.6 KB | 214.1 KB |
| query-results | 1095.4 KB | 214.1 KB |
| render-ui | 1199.9 KB | 214.1 KB |
| visual-review-snapshots | 775.4 KB | 214.1 KB |
| workflow-data-suggestions | 773.2 KB | 214.1 KB |
| workflow-sending-rules | 771.9 KB | 214.1 KB |
ℹ️ MCP agent API — agent-facing tool changes
What this PR changes for agents, from the tool schema snapshots and tool definitions.
Tools changed (2):
| Tool | Params | Scopes | Annotations | Schema chars |
|---|---|---|---|---|
broadcasts-create |
19,872 -> 20,088 | |||
workflows-create |
19,872 -> 20,088 |
⚠️ Playwright — 2 flaky
🎭 Playwright report · View test results →
- create experiment via wizard, add metrics, and launch (chromium)
- password-protected insight sharing (chromium)
These issues are not necessarily caused by your changes.
Annoyed by this section? Help fix flakies and failures and it will go green!
⚠️ MCP snapshots — 1 updated (1 modified, 0 added, 0 deleted)
Snapshots: MCP unit test snapshots updated
Changes: 1 snapshots (1 modified, 0 added, 0 deleted)
What this means:
- Snapshots have been automatically updated to match current output
Next steps:
- Review the changes to ensure they're intentional
- If unexpected, investigate what caused the output to change
⚠️ Django migration SQL — 3 new migrations to review
We've detected new migrations on this PR. Review the SQL output for each migration:
products/messaging/backend/migrations/0013_messagetemplate_key.py
/home/runner/work/tmp/tool_cache/Python/3.14.7/x64/lib/python3.14/site-packages/anyio/from_thread.py:119: SyntaxWarning: 'return' in a 'finally' block
return result
2026-11-04T14:03:37.847667Z [error ] Path must be a valid database or directory containing databases. [posthog.exceptions_capture] pid=6314 tid=140359376313216
Traceback (most recent call last):
File "/home/runner/work/posthog/posthog/posthog/geoip.py", line 15, in <module>
geoip: Optional[GeoIP2] = GeoIP2(cache=8)
~~~~~~^^^^^^^^^
File "/home/runner/work/tmp/tool_cache/Python/3.14.7/x64/lib/python3.14/site-packages/django/contrib/gis/geoip2.py", line 114, in __init__
raise GeoIP2Exception(
"Path must be a valid database or directory containing databases."
)
django.contrib.gis.geoip2.GeoIP2Exception: Path must be a valid database or directory containing databases.
/home/runner/work/tmp/tool_cache/Python/3.14.7/x64/lib/python3.14/site-packages/sshtunnel.py:1040: SyntaxWarning: 'return' in a 'finally' block
return (ssh_host,
System check identified some issues:
WARNINGS:
?: (axes.W001) You are using the django-axes cache handler for login attempt tracking. Your cache configuration is however invalid and will not work correctly with django-axes. This can leave security holes in your login systems as attempts are not tracked correctly. Reconfigure settings.AXES_CACHE and settings.CACHES per django-axes configuration documentation.
?: (staticfiles.W004) The directory '/home/runner/work/posthog/posthog/frontend/dist' in the STATICFILES_DIRS setting does not exist.
BEGIN;
--
-- Add field key to messagetemplate
--
ALTER TABLE "posthog_messagetemplate" ADD COLUMN "key" varchar(400) NULL;
COMMIT;products/messaging/backend/migrations/0014_messagetemplate_unique_key_per_team.py
2026-11-04T14:04:24.016550Z [error ] Path must be a valid database or directory containing databases. [posthog.exceptions_capture] pid=7480 tid=140470137600896
Traceback (most recent call last):
File "/home/runner/work/posthog/posthog/posthog/geoip.py", line 15, in <module>
geoip: Optional[GeoIP2] = GeoIP2(cache=8)
~~~~~~^^^^^^^^^
File "/home/runner/work/tmp/tool_cache/Python/3.14.7/x64/lib/python3.14/site-packages/django/contrib/gis/geoip2.py", line 114, in __init__
raise GeoIP2Exception(
"Path must be a valid database or directory containing databases."
)
django.contrib.gis.geoip2.GeoIP2Exception: Path must be a valid database or directory containing databases.
System check identified some issues:
WARNINGS:
?: (axes.W001) You are using the django-axes cache handler for login attempt tracking. Your cache configuration is however invalid and will not work correctly with django-axes. This can leave security holes in your login systems as attempts are not tracked correctly. Reconfigure settings.AXES_CACHE and settings.CACHES per django-axes configuration documentation.
?: (staticfiles.W004) The directory '/home/runner/work/posthog/posthog/frontend/dist' in the STATICFILES_DIRS setting does not exist.
--
-- Custom state/database change combination
--
SET lock_timeout = 0;
SET statement_timeout = 0;
CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "unique_message_template_key_per_team" ON "posthog_messagetemplate" ("team_id", "key") WHERE (NOT "deleted" AND "key" IS NOT NULL);products/messaging/backend/migrations/0015_transactional_email_key.py
2026-11-04T14:04:43.543900Z [error ] Path must be a valid database or directory containing databases. [posthog.exceptions_capture] pid=7905 tid=140247952915328
Traceback (most recent call last):
File "/home/runner/work/posthog/posthog/posthog/geoip.py", line 15, in <module>
geoip: Optional[GeoIP2] = GeoIP2(cache=8)
~~~~~~^^^^^^^^^
File "/home/runner/work/tmp/tool_cache/Python/3.14.7/x64/lib/python3.14/site-packages/django/contrib/gis/geoip2.py", line 114, in __init__
raise GeoIP2Exception(
"Path must be a valid database or directory containing databases."
)
django.contrib.gis.geoip2.GeoIP2Exception: Path must be a valid database or directory containing databases.
System check identified some issues:
WARNINGS:
?: (axes.W001) You are using the django-axes cache handler for login attempt tracking. Your cache configuration is however invalid and will not work correctly with django-axes. This can leave security holes in your login systems as attempts are not tracked correctly. Reconfigure settings.AXES_CACHE and settings.CACHES per django-axes configuration documentation.
?: (staticfiles.W004) The directory '/home/runner/work/posthog/posthog/frontend/dist' in the STATICFILES_DIRS setting does not exist.
BEGIN;
--
-- Create model TransactionalEmailKey
--
CREATE TABLE "messaging_transactionalemailkey" ("id" uuid NOT NULL PRIMARY KEY, "key" varchar(400) NOT NULL, "hog_flow_id" uuid NOT NULL, "created_at" timestamp with time zone NOT NULL, "team_id" integer NOT NULL, CONSTRAINT "unique_transactional_email_key_per_team" UNIQUE ("team_id", "key"));
CREATE INDEX "messaging_transactionalemailkey_team_id_41b9e2b2" ON "messaging_transactionalemailkey" ("team_id");
COMMIT;Last updated: 2026-11-04 14:05 UTC (c81b9e0)
✅ Django migration risk — migration analysis complete
We've analyzed your migrations for potential risks.
Summary: 3 Safe | 0 Needs Review | 0 Blocked
✅ Safe
Brief or no lock, backwards compatible
messaging.0013_messagetemplate_key
└─ #1 ✅ AddField
Adding nullable field requires brief lock
model: messagetemplate, field: key
messaging.0014_messagetemplate_unique_key_per_team
└─ #1 ✅ SeparateDatabaseAndState
Wrapper operation - see nested operations for risk: CreateIndexConcurrently
database_operations: CreateIndexConcurrently
└─ #2 ✅ CreateIndexConcurrently
PostHog concurrent-index helper: idempotent (timeout disabling + invalid-leftover recovery)
model: None, index: unique_message_template_key_per_team
messaging.0015_transactional_email_key
└─ #1 ✅ CreateModel
Creating new table is safe
model: TransactionalEmailKey
│
└──> ℹ️ INFO:
ℹ️ Skipped operations on newly created tables (empty tables
don't cause lock contention).
📚 How to Deploy These Changes Safely
AddField:
This operation acquires a brief lock but doesn't rewrite the table.
Deployment uses lock timeouts with automatic retries, so lock contention will cause retries rather than connection pile-up.
Last updated: 2026-11-04 14:05 UTC (c81b9e0)
ℹ️ Hogbox preview — building
Spinning up PostHog for this PR on a hogland hogbox. This section updates in place when it's ready (usually a few minutes).
commit c81b9e0 · build log
|
React Doctor found no issues in the changed files. 🎉 Reviewed by React Doctor for commit |
There was a problem hiding this comment.
PostHog Review · Standard · reviewed 0d164b2: 2 Should fix.
This is the only automatic review while this pull request is a draft. Comment @posthog review to review new changes sooner. Otherwise, a review of the new changes runs when you mark it ready for review.
See this review and its findings in PostHog Review. Comment @posthog review --deep for a Deep review. A Deep review takes longer and looks deeper than a Standard review.
HostHog preview —
|
…d tests Co-Authored-By: Claude Opus 5.5 <[email protected]>
…o-code-send Takes master's flat send body from #126287: template_id or template_key, and a flat distinct_id, with master's nested fallback kept for released SDKs. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Master added messaging migration 0012_email_hard_bounce, so the layer's messaging migrations move to 0013-0015 on top of it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
There was a problem hiding this comment.
Not approved yet — waiting on the conditions below.
The Migration risk check has not finished for this commit, so stamphog cannot tell a safe migration from a risky one yet. The review runs again on the next push, or you can re-request it once the check reports.
- 1 of the 39 changed files are governed by
products/desktop/AGENT_APPROVALS.md.
Gate mechanics and policy version
| Gate | Result | |
|---|---|---|
| prerequisites | ✓ | all clear |
| deny-list | ✗ | matches: migrations |
| size | ✓ | 1078L, 21F substantive, 1984L/39F incl. docs/generated/snapshots — within ceiling |
| tier | ✗ | classified as T2-never: T2-never (1984L, 39F, cross-cutting, feat) |
| stamphog 2.7.0 | .stamphog/policy.yml @ unknown · reviewed head 99bc511 |
Master's #125785 passes the loaded template into send_transactional_email. The layer's template route and its key lookup now pass the template too. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
ty refuses a MessageTemplate in the UUID | TransactionalTemplate parameter, so the UUID branch holds the loaded row in its own variable, as master does. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
parseaddr() is quadratic on group addresses, and validate_from() ran it on an unbounded value before the flag and sender checks. The field now takes at most 1,024 characters. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]> # Conflicts: # products/messaging/frontend/generated/api.zod.ts
|
/trunk merge |
…irst-code-send-event Brings in #127123's master merge, which adds messaging migration 0012_email_hard_bounce and renumbers the code-send key migration to 0015_transactional_email_key. The first code send migration becomes 0016_first_code_send on top of it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Problem
Warning
Merge risk: medium. Migrations 0012 to 0014 add a nullable
keycolumn with a unique index per team, built concurrently, and the key table. They stay after a revert.workflows-email-from-code.202and delivers nothing.template_idandtemplate_key, or neither, answers400ontemplate_id. A send bytemplate_idthat also carriessubject,html,text,from,reply_toorcapture_engagementanswers400on that field. Master ignored those undocumented fields.Changes
POST /api/projects/:id/messaging/send/email/now also sends content the caller rendered. No template has to exist first:The code form is flat:
template_key,to,subject,htmland/ortext, plus optionalfrom,reply_to,distinct_idandcapture_engagement.The template form is master's flat body from feat(messaging): take template_id and distinct_id at the top of the send body #126287:
template_id,message_dataand a flatdistinct_id. A send names eithertemplate_idortemplate_key.The nested
template.idandidentifiers.distinct_idfallback is master's, kept for released SDKs. A code send refusestemplateandidentifiers.A
fromoff the project's verified domains answers400 sender_not_verified. Afromnested too deep to parse answers400, not500.A code send without
fromuses the project's default sender. With no default it uses the only verified sender, and with several it answers400 sender_required.A code send with a field it doesn't take, an empty key included, answers
400naming the field. No email goes out without it.An address cdp-api can't send to answers
400up front. Both forms checkto, and the code form eachreply_to, withPIPELINE_ADDRESS_PATTERN, the pattern feat(cdp): deliver a code send's content on its own transactional email route #127122 checks. IP literals, non-ASCII domains,@localhostand dots that lead, trail or repeat answer400on the field, and cdp-api gets no request.A
400from cdp-api reaches the caller as400 invalid_request. Master answered503 delivery_unavailable, so a caller retried a send that could never succeed. Other unknown answers, such as401or5xx, stay503.The send endpoints take master's
transactional_email:writescope from feat(messaging): give transactional email its own scope #128166. A key with only that scope can send code under a new key, which creates the key's workflow withouthog_flow:write.htmlandtextare each capped at 5 MiB. A413from cdp-api answers400 content_too_large, not a retryable503.The same
Idempotency-Keyanswers202withreplayed: trueand sends nothing new.An unconfirmed code send answers
503and says how to retry safely.Each key gets one unlisted workflow on its first send. A project holds at most 1000 keys: a new key past that answers
400 too_many_keys, and existing keys keep sending. First sends of new keys take a per-project lock, so racing sends can't pass the limit together.A key's own workflow is sensitive. A code send under a key without a template keeps its content out of the 30-day saved run history, and a rerun skips it with a log line saying why (feat(cdp): deliver a code send's content on its own transactional email route #127122). A key that names a template follows that template's setting. The send API has no
sensitivefield.Each environment of a project keeps its own workflow for a key. The key row stores the environment's own
team_id, like the workflow it points to.A key's own workflow names its key in the email step as
template_key, as template workflows name theirs intemplate_uuid. Delivery events can then report the key, and no key workflow needs a backfill.Text outside ASCII reaches cdp-api as UTF-8, so the 5 MiB cap counts the bytes cdp-api reads.
Plain text made from
htmlstarts a new line after a block, so<div>Order 123</div>shippedreads as two lines.The text comes from one streaming pass over
html, not a full DOM, so a 5 MiB or deeply nested body converts in bounded memory.Code-send errors name the email's workflow, not a template, and the
413text no longer points tomessage_data.A code send's status reads under either send flag, so a team with only
workflows-email-from-codecan poll themessage_idit got back. The code form extends the existing send view, so its auth, access checks and metrics cover both forms.The flag gates every way into the queue. A rerun tells CDP
replay_code_sendsfromworkflows-email-from-code, and CDP (#127122) skips a stored code send while it is false. This covers a code send through an existing template's workflow, which a check on the workflow's origin missed.Django mints the code-send token with
kind: code_send. feat(cdp): deliver a code send's content on its own transactional email route #127122's code send route requires it, and its template route refuses any token with akind, so neither token works on the other route.The workflows list filter no longer offers
transactional_email_keys, which always came back empty. The create schema's help text says create never takes it.Mechanical: the regenerated OpenAPI, frontend, desktop and MCP types, and the key model in the IDOR rule list. Their only runtime frontend code is the regenerated messaging and workflows
api.zod.tsandapi.ts, about 65 changed lines. Since the master merge, the CI report's only bundle row isrender-query.jsat +1.7 KiB, from the regenerated API clients above.How did you test this code?
Test rationale: the route through the DRF client with cdp-api mocked (
tests/api/test_transactional_email.py), the route's OpenAPI schema, the key constraint, and the workflow per key. Each test names its regression: code-send fields dropped before cdp-api, a schema default a client copies into a template send, a timeout that says just retry, and two racing first sends making two workflows.test_a_new_key_counts_a_key_another_sender_committed_while_it_waited_for_the_lock(test_transactional_email_key_lock.py, outside a test transaction): two first sends of different keys both counting 999 keys and passing the limit, or a lock taken outside a transaction, which Postgres releases at once.test_a_code_send_goes_out_as_is_under_the_keys_workflownow checks the email step'ssensitive: a key's own workflow storing a code send's content in run history, or a template key losing its template's setting.test_a_code_send_takes_the_addresses_the_pipeline_takesruns 13 addresses through the endpoint fortoandreply_to, the table #127122 tests: Django accepting an address cdp-api refuses, or refusing one it sends.test_reports_a_send_the_pipeline_refusesgainsinvalid_requestand an unknown400: a cdp-api400turning into a retryable503.These cases fail without their fix: a status read under
workflows-email-from-codealone, afromnested 1000 parentheses deep, a rerun with the flag off on a key and on a template workflow, one key sent from a project and its child environment, and a code send with text outside ASCII.Status read, live at the head
213fad6with #127122'seb26545and onlyworkflows-email-from-codeon: a code send delivers once, andGET /api/projects/@current/messaging/emails/<message_id>/answers200withsent. An unknown ID answers404.Sensitive key workflows, live at
503f4bawith #127122's20fb623, and before ate2722a6withccdd9ff: a code send under a new key delivers once, its email step is sensitive, and its stored run has nosend, subject or login code. A rerun answersskipped=1, sends nothing, and logs "Not re-run: this run's message is sensitive…" on the run, for a succeeded and a failed run. A send under a template key keepssensitive: false, storessend, and a rerun of its failed run sends once.Addresses, live at the head
7571429with cdp-api and both workers at #127122's3639e5d: code sends to[email protected](Reply-Too'[email protected]) and to an address with every symbol email allows, a template send to[email protected], and a code send with a key holding onlytransactional_email:writeeach deliver once. An IP-literaltoand a leading-dotreply_toanswer400on the field, with no request to cdp-api.The other proofs ran at
5ec28fe. Since then the branch deleted code comments, changed one help text, added the address check and the400mapping, and merged master with #128166. They ran with cdp-api and both workers at #127122's head2eac306. The Django runs used31dc832and427bea9, which differ from it only in the IDOR rule list and the desktop app's generated client:capture_engagementon and off on a maildev and an SES sender, all threefromfallbacks, and two environments sending one key each deliver as described above.404and creates no key, workflow or job. A template send still delivers.5ec28fe. 39 answer differently, and each is a change listed above.503 delivery_unavailableand queues nothing. Template sends deliver in both rollout orders.fromfallback.CI report warnings that do not apply: the bundle size growth is in
render-query,StamphogSceneandBroadcastScene, which this PR does not touch; its only frontend change is regenerated API types. The two flaky Playwright tests cover experiments and insight sharing. The MCP snapshot update and the three migrations are this PR's own changes.Release status
Docs update
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Agent: Claude Code, Claude Opus 5.5 (claude-opus-5-5)
Skills:
/tdd,/django-migrations,/improving-drf-endpoints,/writing-tests,/stacking-prs,/recreating-fork-prs,/writing-pr-descriptionsMore context for AI agents
51409d9b86bmergesmasterwith feat(messaging): take template_id and distinct_id at the top of the send body #126287. The code form keeps HEAD's shape, and the template form, its nested fallback and the either/or check take master's, with the check ontemplate_idvstemplate_key.c29fba4imports the workflows models in the send tests through the workflows facade, because master'shogli lint:tachrefuses the internal path. A GPT-6.1 Sol final read found nothing.workflows-email-from-code, set by Michael.fromfallback,400with several; the flag must cover every entry point, so every rerun passes it and CDP skips stored code sends while it is off.sensitivefield on the send API, and contract Transactional email SDK contract for posthog-python and posthog-node #120921 says so.tocheck, share one address pattern with Django, and answer a cdp-api400with400; feat(messaging): send cc, bcc and attachments on a code send #127988 imports the pattern forccandbcc.distinct_id; a send by id refusesfromwith a400, and a send with both or neither oftemplate_idandtemplate_keyanswers400ontemplate_id.31dc832,5ec28feand23f8c06found nothing. Final read 9 found the address table bypassed the endpoint, fixed in0d9fb11; final read 10 at7571429, master merge included, found nothing. Final read 11 ate2722a6found nothing. The proof judge ate2722a6found racing first sends of different keys could pass the 1000-key limit, fixed in164c0db. Final read 12 found the race test did not pin the lock before the count, fixed in503f4ba; final read 13 found nothing. feat(workflows): send an email with inline html from an MCP tool #127167's final read 9 found the test ran inside the test transaction, fixed in213fad6; final read 14 found nothing. PostHog Review atc4b7fb2found the status flag and the deepfrom, both fixed in43988f7.capture_engagement: falsesends; once the sandbox sender lands, feat(cdp): send sandbox email through its ses identity to org members #128250 wires it so that a code send withoutfromand with no verified sender sends from it.213fad6: refused for unbounded HTML-to-text memory, fixed in3a7a7aawith a streaming parser, and final read 15's quadratic scan on unmatched closing tags fixed inea00975; final read 16 found nothing; and for route-crossing tokens, fixed in feat(cdp): deliver a code send's content on its own transactional email route #127122. The scope widening for inline content is accepted for the internal test phase (Michael, 2026-11-04) and gets reworked before the flag goes past the PostHog org.message_templates.py.🤖 Generated with Claude Code