Repository navigation
feat(messaging): store a sender and reply-to on transactional templates - #121871
Conversation
- Template content takes `from` (sender, custom name and address) and `replyTo`. Each publish copies them into the transactional workflow, and making a template transactional stores the sender it was made with. - A custom address must be one address of at most 254 characters on the sender's verified domain. The API rejects it on publish, and conversion rejects one left over from another sender. - A sender string or empty value, from blank templates, older templates or a destination's editor, means no sender, on reads and writes. - A sender without an integration ID is a 400. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: 90d9a51d-beba-47ba-8f50-b351327676f9
|
😎 This pull request was merged. |
🤖 CI report
|
| File | Comment lines | Added lines |
|---|---|---|
products/messaging/backend/services/transactional_email.py |
8 | 53 |
products/messaging/backend/presentation/views/message_templates.py |
6 | 63 |
products/messaging/backend/tests/api/test_message_templates.py |
2 | 26 |
products/messaging/backend/tests/api/test_transactional_email.py |
1 | 78 |
products/workflows/backend/facade/contracts.py |
1 | 4 |
This check does not block merging. It updates on every push and clears when the share drops.
✅ Bundle size — no change
Uncompressed size of every built .js bundle, compared against the base branch.
Total: 75.83 MiB · no change
No file changed by more than 1000 B.
Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report
⚠️ Eager graph — 2 over 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.01 MiB · 24 files | no change | █████████░ 88.6% 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.93 MiB · 615 files | no change | ██████████ 96.8% of 3.03 MiB |
🟡 authenticated shell (every logged-in page)src/scenes/AuthenticatedShell.tsx |
7.59 MiB · 2,580 files | no change | ██████████ 101.2% of 7.50 MiB |
dashboard scenesrc/scenes/dashboard/Dashboard.tsx |
9.61 MiB · 3,646 files | no change | ██████████ 96.0% of 10.01 MiB |
🟡 today home pathsrc/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx |
7.99 MiB · 2,919 files | no change | ██████████ 103.3% of 7.74 MiB |
events scenesrc/scenes/activity/explore/EventsScene.tsx |
9.09 MiB · 3,437 files | no change | █████████░ 94.8% of 9.59 MiB |
web analytics scenesrc/scenes/web-analytics/WebAnalyticsScene.tsx |
9.93 MiB · 3,652 files | no change | █████████░ 91.7% of 10.83 MiB |
replay detail scenesrc/scenes/session-recordings/detail/SessionRecordingDetail.tsx |
8.16 MiB · 3,021 files | no change | █████████░ 86.2% of 9.47 MiB |
🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers 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/scenes/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/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers 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
🟢 products/wizard/frontend/runs/WizardRunDetailsDrawer.tsx 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
🟢 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/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
🟢 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/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/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
Largest files eagerly shipped from src/index.tsx
| Size | File |
|---|---|
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 24.6 KiB | ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js |
| 6.3 KiB | ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js |
| 4.5 KiB | ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js |
| 3.9 KiB | ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js |
| 1.4 KiB | ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js |
| 1.4 KiB | src/index.tsx |
| 1.3 KiB | src/RootErrorBoundary.tsx |
| 1.3 KiB | src/lib/utils/bootTiming.ts |
| 912 B | ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js |
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
| Size | File |
|---|---|
| 321.6 KiB | ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 220.9 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 106.8 KiB | src/products.tsx |
| 98.3 KiB | src/lib/api.ts |
| 69.4 KiB | src/lib/lemon-ui/icons/icons.tsx |
| 38.7 KiB | ../node_modules/.pnpm/@dnd-kit+core@6.0.8_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@dnd-kit/core/dist/core.esm.js |
| 33.9 KiB | ../node_modules/.pnpm/kea@4.0.0-pre.6_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js |
| 29.4 KiB | src/scenes/scenes.ts |
| 26.8 KiB | src/lib/constants.tsx |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
| Size | File |
|---|---|
| 321.6 KiB | ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 280.7 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 238.5 KiB | ../packages/quill/packages/quill/dist/index.js |
| 220.9 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 153.7 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js |
| 143.2 KiB | ../packages/quill/packages/primitives/dist/index.js |
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 106.8 KiB | src/products.tsx |
| 98.3 KiB | src/lib/api.ts |
| 93.3 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
Largest files eagerly shipped from src/scenes/dashboard/Dashboard.tsx
| Size | File |
|---|---|
| 321.6 KiB | ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 280.7 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 238.5 KiB | ../packages/quill/packages/quill/dist/index.js |
| 220.9 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 153.7 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js |
| 143.2 KiB | ../packages/quill/packages/primitives/dist/index.js |
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 106.8 KiB | src/products.tsx |
| 98.3 KiB | src/lib/api.ts |
| 93.3 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
| Size | File |
|---|---|
| 321.6 KiB | ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 280.7 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 238.5 KiB | ../packages/quill/packages/quill/dist/index.js |
| 220.9 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 153.7 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js |
| 143.2 KiB | ../packages/quill/packages/primitives/dist/index.js |
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 106.8 KiB | src/products.tsx |
| 98.3 KiB | src/lib/api.ts |
| 93.3 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
Largest files eagerly shipped from src/scenes/activity/explore/EventsScene.tsx
| Size | File |
|---|---|
| 321.6 KiB | ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 280.7 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 238.5 KiB | ../packages/quill/packages/quill/dist/index.js |
| 220.9 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 153.7 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js |
| 143.2 KiB | ../packages/quill/packages/primitives/dist/index.js |
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 106.8 KiB | src/products.tsx |
| 98.3 KiB | src/lib/api.ts |
| 93.3 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
Largest files eagerly shipped from src/scenes/web-analytics/WebAnalyticsScene.tsx
| Size | File |
|---|---|
| 321.6 KiB | ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 280.7 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 238.5 KiB | ../packages/quill/packages/quill/dist/index.js |
| 220.9 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 183.2 KiB | src/queries/validators.js |
| 153.7 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js |
| 143.2 KiB | ../packages/quill/packages/primitives/dist/index.js |
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 106.8 KiB | src/products.tsx |
| 98.3 KiB | src/lib/api.ts |
Largest files eagerly shipped from src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
| Size | File |
|---|---|
| 321.6 KiB | ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 315.9 KiB | ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js |
| 280.7 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 238.5 KiB | ../packages/quill/packages/quill/dist/index.js |
| 220.9 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 153.7 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js |
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 106.8 KiB | src/products.tsx |
| 98.3 KiB | src/lib/api.ts |
| 93.3 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479
✅ Toolbar bundle — eager 2.28 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.28 MiB · 19 files | no change | ████░░░░░░ 39.9% of 5.72 MiB |
| Deferred (lazy) | 2.10 MiB · 40 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 |
|---|---|
| 898.5 KiB | dist/toolbar/toolbar-app-LIRVHRPD.css |
| 691.0 KiB | dist/toolbar/chunk-chunk-HVEQZXL4.js |
| 258.3 KiB | dist/toolbar/chunk-chunk-YUTUHJHZ.js |
| 131.8 KiB | dist/toolbar/chunk-chunk-FDH2IBXT.js |
| 130.4 KiB | dist/toolbar/chunk-chunk-MGILAAF6.js |
| 74.8 KiB | dist/toolbar/toolbar-app-F7Q25OJS.js |
| 69.0 KiB | dist/toolbar/chunk-chunk-TSAL54PB.js |
| 35.6 KiB | dist/toolbar/chunk-chunk-CWKXICRU.js |
| 21.7 KiB | dist/toolbar/chunk-chunk-XK3VNHDG.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 — no change
Total size of the built frontend/dist folder (all assets), compared against the base branch.
Total: 1016.05 MiB · no change
ℹ️ MCP UI apps size — 34 app(s), 27836.6 KB JS
Built size of each MCP UI app (main.js + styles.css).
| App | JS | CSS |
|---|---|---|
| debug | 840.2 KB | 209.0 KB |
| action | 764.3 KB | 209.0 KB |
| action-list | 831.2 KB | 209.0 KB |
| cohort | 763.3 KB | 209.0 KB |
| cohort-list | 830.2 KB | 209.0 KB |
| email-template | 763.1 KB | 209.0 KB |
| error-details | 768.1 KB | 209.0 KB |
| error-issue | 764.6 KB | 209.0 KB |
| error-issue-list | 831.8 KB | 209.0 KB |
| experiment | 828.3 KB | 209.0 KB |
| experiment-list | 832.0 KB | 209.0 KB |
| experiment-results | 830.8 KB | 209.0 KB |
| feature-flag | 830.6 KB | 209.0 KB |
| feature-flag-list | 834.3 KB | 209.0 KB |
| feature-flag-testing | 766.5 KB | 209.0 KB |
| inline-scan | 763.7 KB | 209.0 KB |
| insight-actors | 829.7 KB | 209.0 KB |
| llm-costs | 827.0 KB | 209.0 KB |
| session-recording | 765.5 KB | 209.0 KB |
| survey | 764.9 KB | 209.0 KB |
| survey-global-stats | 827.0 KB | 209.0 KB |
| survey-list | 831.9 KB | 209.0 KB |
| survey-stats | 827.0 KB | 209.0 KB |
| trace-span | 763.6 KB | 209.0 KB |
| trace-span-list | 831.1 KB | 209.0 KB |
| vision-observation-list | 830.3 KB | 209.0 KB |
| workflow | 763.6 KB | 209.0 KB |
| workflow-list | 830.6 KB | 209.0 KB |
| workflow-setup-status | 763.3 KB | 209.0 KB |
| query-results | 1082.2 KB | 209.0 KB |
| render-ui | 1157.0 KB | 209.0 KB |
| visual-review-snapshots | 768.1 KB | 209.0 KB |
| workflow-data-suggestions | 765.9 KB | 209.0 KB |
| workflow-sending-rules | 764.6 KB | 209.0 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 |
|---|---|---|---|---|
workflows-create-email-template |
3,354 -> 4,347 | |||
workflows-update-email-template |
3,440 -> 4,433 |
⚠️ Backend coverage — 97.0% of changed backend lines covered — 3 uncovered
🧪 Backend test coverage
Patch coverage — changed backend lines (products + core): ███████████████████░ 97.0% (113 / 116)
| File | Patch | Uncovered changed lines |
|---|---|---|
products/messaging/backend/services/transactional_email.py |
90.6% | 79, 85, 95 |
🤖 Agents: add a test only if an uncovered line exposes a realistic regression that existing tests miss. Otherwise explain why no new test is needed under "How did you test this code?". Gap list: the patch-coverage artifact on this run (gh run download 502843202383296 -n patch-coverage), or the coverage-data block at the end of this comment.
Per-product line coverage (touched products)
| Product | Coverage | Lines |
|---|---|---|
demo |
███████████░░░░░░░░░ 56.4% |
1,542 / 2,736 |
batch_exports |
████████████████░░░░ 81.6% |
22,832 / 27,996 |
engineering_analytics |
█████████████████░░░ 85.0% |
11,463 / 13,482 |
commands |
█████████████████░░░ 85.5% |
1,618 / 1,893 |
mcp_analytics |
█████████████████░░░ 86.1% |
5,699 / 6,617 |
warehouse_suggestions |
██████████████████░░ 87.7% |
2,422 / 2,762 |
posthog_ai |
██████████████████░░ 87.9% |
4,287 / 4,875 |
dashboards |
██████████████████░░ 88.3% |
7,327 / 8,300 |
notebooks |
██████████████████░░ 88.5% |
15,287 / 17,281 |
mcp_registry |
██████████████████░░ 89.0% |
1,596 / 1,794 |
cohorts |
██████████████████░░ 89.6% |
8,568 / 9,559 |
product_tours |
██████████████████░░ 89.7% |
1,355 / 1,510 |
signals |
██████████████████░░ 90.6% |
80,008 / 88,328 |
tasks |
██████████████████░░ 90.7% |
79,609 / 87,743 |
data_warehouse |
██████████████████░░ 90.8% |
14,722 / 16,216 |
canvas |
██████████████████░░ 91.1% |
7,998 / 8,776 |
today |
██████████████████░░ 91.2% |
3,798 / 4,163 |
cdp |
██████████████████░░ 91.4% |
10,166 / 11,119 |
autoresearch |
██████████████████░░ 91.4% |
11,606 / 12,693 |
slack_app |
██████████████████░░ 91.6% |
20,010 / 21,852 |
metrics |
██████████████████░░ 91.6% |
10,231 / 11,171 |
business_knowledge |
██████████████████░░ 91.6% |
9,965 / 10,878 |
data_modeling |
██████████████████░░ 91.7% |
10,072 / 10,985 |
wizard |
██████████████████░░ 91.7% |
6,870 / 7,490 |
managed_warehouse |
██████████████████░░ 91.8% |
11,923 / 12,995 |
error_tracking |
██████████████████░░ 91.9% |
17,572 / 19,120 |
streamlit_apps |
██████████████████░░ 91.9% |
3,139 / 3,415 |
conversations |
██████████████████░░ 92.2% |
30,218 / 32,778 |
review_hog |
██████████████████░░ 92.2% |
17,726 / 19,221 |
stamphog |
███████████████████░ 92.5% |
8,776 / 9,483 |
early_access_features |
███████████████████░ 92.6% |
1,339 / 1,446 |
alerts_platform |
███████████████████░ 92.8% |
7,386 / 7,962 |
notifications |
███████████████████░ 93.0% |
1,199 / 1,289 |
web_analytics |
███████████████████░ 93.2% |
25,607 / 27,465 |
alerts |
███████████████████░ 93.3% |
9,442 / 10,118 |
surveys |
███████████████████░ 93.4% |
6,918 / 7,406 |
cross_project_dashboards |
███████████████████░ 93.4% |
880 / 942 |
approvals |
███████████████████░ 93.4% |
4,576 / 4,898 |
exports |
███████████████████░ 93.6% |
4,125 / 4,406 |
billing_alerts |
███████████████████░ 93.7% |
2,093 / 2,233 |
context_layer |
███████████████████░ 93.8% |
3,946 / 4,208 |
messaging |
███████████████████░ 93.8% |
4,692 / 5,002 |
mcp_store |
███████████████████░ 93.9% |
9,084 / 9,676 |
marketing_analytics |
███████████████████░ 93.9% |
21,083 / 22,454 |
customer_analytics |
███████████████████░ 94.0% |
27,699 / 29,453 |
workflows |
███████████████████░ 94.1% |
22,330 / 23,719 |
ai_observability |
███████████████████░ 94.2% |
27,744 / 29,447 |
logs |
███████████████████░ 94.3% |
19,216 / 20,369 |
replay_vision |
███████████████████░ 94.5% |
31,999 / 33,861 |
actions |
███████████████████░ 94.7% |
1,004 / 1,060 |
reminders |
███████████████████░ 94.8% |
760 / 802 |
growth |
███████████████████░ 94.9% |
11,347 / 11,959 |
endpoints |
███████████████████░ 95.1% |
9,658 / 10,160 |
annotations |
███████████████████░ 95.1% |
1,095 / 1,151 |
skills |
███████████████████░ 95.2% |
7,336 / 7,707 |
experiments |
███████████████████░ 95.3% |
38,842 / 40,739 |
access_control |
███████████████████░ 95.5% |
8,079 / 8,459 |
data_catalog |
███████████████████░ 95.6% |
4,513 / 4,721 |
product_analytics |
███████████████████░ 95.8% |
29,661 / 30,966 |
revenue_analytics |
███████████████████░ 95.9% |
1,877 / 1,958 |
data_quality |
███████████████████░ 96.0% |
8,523 / 8,878 |
feature_flags |
███████████████████░ 96.2% |
27,805 / 28,914 |
subscriptions |
███████████████████░ 96.3% |
12,031 / 12,494 |
tracing |
███████████████████░ 96.4% |
4,360 / 4,524 |
warehouse_sources |
███████████████████░ 96.5% |
467,816 / 484,734 |
account_audits |
███████████████████░ 96.7% |
768 / 794 |
pulse |
███████████████████░ 97.1% |
2,023 / 2,083 |
analytics_platform |
████████████████████ 98.0% |
2,798 / 2,855 |
groups |
████████████████████ 98.2% |
1,823 / 1,856 |
Report-only. Patch coverage = changed backend lines covered vs origin/master. Sorted lowest first.
Known gaps: lines covered only by Temporal tests show as uncovered; core line numbers may drift if master changed the same file.
mypy flagged a possibly missing integration ID in the sender lookup. Without a sender there is no domain to check against, and the sender field already rejects that case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: 90d9a51d-beba-47ba-8f50-b351327676f9
|
Risk: No findings This PR adds a per-template email sender (integration ID, display name, custom from address) and reply-to to transactional email templates, copies them into the backing workflow on conversion and every publish, and validates the custom address against the sender's verified domain. I traced the authoring API, the conversion/publish service, and the nodejs send path end to end and found the new fields fail closed at each boundary: integration lookups are team-scoped, unverified senders are rejected at publish and send, and an off-domain or malformed custom address (including one produced by Liquid at send time) is discarded in favor of the sender's own address. Sentinel reviewed |
There was a problem hiding this comment.
Approved.
The disclosed API, write-path and sender-validation changes enter risky territory, with assurance supplied by owning-team authorship and STRONG familiarity. Regression tests cover sender changes, invalid integrations and legacy content; there are no outstanding review concerns or identified refusal grounds.
- Author wrote 92% of the modified lines and has 38 merged PRs in these paths (familiarity STRONG).
Gate mechanics and policy version
| Gate | Result | |
|---|---|---|
| prerequisites | ✓ | all clear |
| deny-list | ✓ | no deny categories matched |
| size | ✓ | 163L, 6F substantive, 623L/14F incl. docs/generated/snapshots — within ceiling |
| tier | ✓ | T1-agent / T1d-complex (623L, 14F, two-areas, feat) |
| stamphog 2.6.0 | .stamphog/policy.yml @ f799de5 · reviewed head f799de5 |
|
This pull request was merged into |
Problem
This is the API half of the feature. The editor is #121079, stacked on this PR.
Changes
from(sender, custom name and address) andreplyTo. The API and the MCP email template tools accept both.frommeans no sender, on reads and writes. That covers blank templates, templates saved before this change, and templates saved from a destination's email editor.How did you test this code?
The full feature was tested manually on a local stack, with the editor from #121079. A transactional template got a custom sender, display name, address and reply-to, published, and was sent through the API. MailDev received
From: Hedgebox Security Team <security-team@posthog.com>andReply-To: security@posthog.com.Test rationale:
test_saving_a_transactional_template_writes_a_new_workflow_versionswitches the sender and sets a name, a Liquid address and a reply-to. It fails if the serializer drops a field or the sync ignores one.test_rejects_an_update_that_would_stop_a_transactional_template_sendinggains these cases: another project's sender, an unverified sender, an off-domain address, a non-address, an address that is too long, and a sender without an ID.test_rejects_making_a_template_transactional_it_cannot_sendgains a custom address left on another sender's domain.test_retrieve_message_templatelists and reads a template stored with each legacyfromshape. Each one crashed the list before.test_create_email_template_with_subject_succeedscreates with the editor's blankfromand with a destination's sender string.Release status
Transactional templates sit behind
workflows-transactional-email-api.Docs update
None. No existing doc covers transactional template settings.
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Agent: PostHog Desktop with Claude Code, Claude Opus 5.5 (
claude-opus-5-5)improving-drf-endpoints,writing-tests,stacking-prs,writing-pr-descriptions.Created with PostHog Desktop
🤖 Generated with Claude Code