Skip to content

feat(integrations): add an email sender over MCP - #125535

Merged
trunk-io[bot] merged 26 commits into
masterfrom
feat/mcp-create-email-sender
Nov 8, 2026
Merged

trunk-io[bot] merged 26 commits into
masterfrom
feat/mcp-create-email-sender

Conversation

@meikelmosby

@meikelmosby meikelmosby commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

An agent setting up workflow email cannot create the sender. A user interview named this handoff back to the app as where setup stalls.

In the app, re-adding an address reset the sender to unverified, and a second sender moved SES's bounce domain (MAIL FROM) for the others.

Warning

🟡 Merge risk: medium · two-way door (merge-risk rubric)

  • The app's sender create is unflagged. A bug fails the create or sets a wrong bounce domain.
  • Revert: git revert the merge commit.

Changes

  • integrations-email-create lets an agent add an email sender, behind a flag.
  • Re-adding an address now fails instead of overwriting the sender.
  • A new sender on a domain takes the MAIL FROM SES uses now.
  • Simultaneous creates on one domain run in turn.
  • After Continue, the legacy setup modal shows the stored MAIL FROM, and Save keeps it. Its help text says that changing it changes it for every sender on the domain.
  • Mechanical: regenerated OpenAPI and MCP definitions.

Email sender created over MCP, shown in Workflows > Messaging

Before After
Legacy setup modal before Legacy setup modal after

How did you test this code?

Test rationale: tests catch a tool body without kind: 'email', an overwritten sender, a second sender ignoring SES, a dropped domain lock, and a stale Save.

  • Live runs with SES faked at boto3 reproduced each bug on master and its fix here.
  • A browser walk of a served build showed a second sender keeping its domain's MAIL FROM through Continue and Save.
  • Not run: a create against real SES. Michael accepted that gap.

Release status

  • No feature flag controls the create-path fix. The MCP tool is behind workflows-email-domain-agent-setup.

Docs update

  • None.

🤖 Agent context

Autonomy: Human-driven (agent-assisted). Built by meikelmosby; Michael directed the takeover.

Question Answer Who Where
New sender's MAIL FROM SES's live value, not the oldest row 🤖 comment
Sync edits across senders No, as on master 🤖 comment
Simultaneous creates Per-domain lock, not a documented window 🤖 comment
Stale MAIL FROM in the modal Fixed here, not deferred 🤖 comment

Involvement: QA swarm (3 rounds and three final reads), review bots, live-run, UI walk and prod-shape proof.

Agent: Claude Code, Opus 5 and Opus 5.5

Skills: /writing-tests, /writing-code-comments, /improving-drf-endpoints, /writing-pr-descriptions

More context for AI agents
Earlier evidence (before the takeover)

A shadow run sent 15 API journeys through master's create path and this one. Edits and verifies matched; the 4 differing creates are the changes above.

🤖 Generated with Claude Code

Reading a sending domain's DNS records and verifying it are both MCP
tools, but creating the sender in the first place was not, so setting up
email still began in the app. An agent asked to set up workflow email had
to hand the person back to the UI for the first step and pick the thread
up afterwards.

integrations-email-create exposes the existing create endpoint for the
email kind only: `kind` is dropped from the advertised schema and
hardcoded in the handler, so the tool cannot make any other integration.
The sender starts unverified, as it does from the app.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@trunk-io

trunk-io Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

😎 Merged successfully - details.

@parameterai

parameterai Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Risk: Low · 2 low

Since the last review the branch only merged master and regenerated the MCP schema files; the PR-authored delta is one comment line plus a behavior-identical extraction of the Slack channel-lookup budget helper, which introduces no new security surface. I re-checked the previously reported SES MAIL FROM paths in posthog/models/integration/email.py: verify() at lines 178/187 still passes the stored subdomain into set_identity_mail_from_domain (ses.py:706), and update_native_integration at line 152 still writes SES without the domain lock the create path takes at line 77 — both remain in the code unchanged by this increment.

Sentinel reviewed b3edd6d · Review settings

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team October 29, 2026 14:31

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PostHog Review · Standard · reviewed 5f9257d: 1 Should fix.

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.

Comment thread products/integrations/mcp/tools.yaml Resolved
@posthog posthog Bot removed the posthog-review label Oct 29, 2026
@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

🤖 CI report

⚠️ Trunk lane — backend Python lane

This PR is assigned to the backend Python lane. It runs backend Python tests and may merge in parallel with PRs in other lanes.

⚠️ Complexity (TypeScript) — 1 function above the limit (max 21)

Cyclomatic complexity above the limit in changed typescript files (10 for production files, 15 for test files). Warn only: worth simplifying when you next touch these functions.

Function Location Complexity Limit
LegacyEmailSetupModal products/workflows/frontend/Channels/EmailSetup/EmailSetupModal.tsx:37 21 10
✅ Duplication (Python) — clean

New Python code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

✅ Duplication (TypeScript) — clean

New TypeScript code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

✅ Comment density — 0% of added code lines are comments (1 of 236)

This section warns when comments are more than 3% of the code lines a PR adds, and alerts above 6%. Before agent-assisted PRs, the typical share was about 2%. Only full-line comments count. Docstrings, generated files, snapshots, migrations, and workflow files are left out.

Comments that restate the code, record how the change came about, or narrate the next line add noise for the next reader. Keep the comments that explain a reason the code cannot show, and remove the rest. See .agents/skills/writing-code-comments/SKILL.md for the house rules.

Files with the most added comment lines:

File Comment lines Added lines
posthog/api/integration.py 1 1

This check does not block merging. It updates on every push and clears when the share drops.

⚠️ Bundle size — 🔺 +10.0 KiB (+0.0%)

Uncompressed size of every built .js bundle, compared against the base branch.

Total: 77.44 MiB · 🔺 +10.0 KiB (+0.0%)

File Size Δ vs base
posthog-app/_parent/products/signals/frontend/inbox/InboxScene.js 538.9 KiB 🔺 +9.0 KiB (+1.7%)

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.03 MiB · 25 files no change █████████░ 90.1% 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
3.01 MiB · 625 files no change ██████████ 99.2% of 3.03 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
3.83 MiB · 1,228 files no change ██████████ 95.6% of 4.01 MiB
dashboard scene
src/scenes/dashboard/Dashboard.tsx
7.61 MiB · 2,941 files no change ████████░░ 77.4% of 9.84 MiB
today home path
src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
4.70 MiB · 1,759 files 🔺 +48 B (+0.0%) ██████████ 95.4% of 4.93 MiB
AI-first home path
src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
6.00 MiB · 2,265 files 🔺 +48 B (+0.0%) ██████████ 95.7% of 6.27 MiB
events scene
src/scenes/activity/explore/EventsScene.tsx
6.69 MiB · 2,519 files no change ███████░░░ 71.1% of 9.41 MiB
web analytics scene
src/scenes/web-analytics/WebAnalyticsScene.tsx
7.71 MiB · 2,884 files no change ███████░░░ 73.5% of 10.49 MiB
replay detail scene
src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
6.91 MiB · 2,605 files no change █████████░ 85.0% 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/cohorts/CohortFilters/constants.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/persons/frontend/logics/personsLogic.tsx 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
🟢 node_modules/@tiptap/ stays out of src/scenes/dashboard/Dashboard.tsx
🟢 node_modules/prosemirror- stays out of src/scenes/dashboard/Dashboard.tsx
🟢 node_modules/marked/ stays out of src/scenes/dashboard/Dashboard.tsx
🟢 node_modules/frimousse/ 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
🟢 node_modules/@tiptap/ stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 node_modules/prosemirror- stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 node_modules/marked/ stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 node_modules/frimousse/ 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
🟢 node_modules/@tiptap/ stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 node_modules/prosemirror- stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 node_modules/marked/ stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 node_modules/frimousse/ 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
🟢 node_modules/@tiptap/ stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
🟢 node_modules/prosemirror- stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
🟢 node_modules/marked/ 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/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.7 KiB ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js
4.0 KiB ../node_modules/.pnpm/scheduler@0.23.2/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/base64-js@1.5.1/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
359.8 KiB ../node_modules/.pnpm/posthog-js@1.447.1_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
224.1 KiB ../node_modules/.pnpm/@posthog+icons@0.39.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
105.7 KiB src/products.tsx
87.4 KiB src/lib/api.ts
68.7 KiB src/lib/lemon-ui/icons/icons.tsx
41.9 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
36.4 KiB ../node_modules/.pnpm/kea@4.0.0-pre.6_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js
28.6 KiB src/lib/constants.tsx
24.6 KiB ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
359.8 KiB ../node_modules/.pnpm/posthog-js@1.447.1_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
224.1 KiB ../node_modules/.pnpm/@posthog+icons@0.39.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
105.7 KiB src/products.tsx
87.4 KiB src/lib/api.ts
68.7 KiB src/lib/lemon-ui/icons/icons.tsx
41.9 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
36.4 KiB ../node_modules/.pnpm/kea@4.0.0-pre.6_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js
32.5 KiB src/lib/utils/eventUsageLogic.ts
28.6 KiB src/lib/constants.tsx
Largest files eagerly shipped from src/scenes/dashboard/Dashboard.tsx
Size File
359.8 KiB ../node_modules/.pnpm/posthog-js@1.447.1_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
224.1 KiB ../node_modules/.pnpm/@posthog+icons@0.39.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
115.8 KiB ../node_modules/.pnpm/posthog-js@1.447.1_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/surveys-preview.js
111.3 KiB ../packages/quill/packages/primitives/dist/index.js
105.7 KiB src/products.tsx
96.1 KiB ../packages/quill/packages/components/dist/index.js
87.4 KiB src/lib/api.ts
79.0 KiB src/scenes/dashboard/dashboardLogic.tsx
68.7 KiB src/lib/lemon-ui/icons/icons.tsx
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
Size File
359.8 KiB ../node_modules/.pnpm/posthog-js@1.447.1_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
224.1 KiB ../node_modules/.pnpm/@posthog+icons@0.39.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
111.3 KiB ../packages/quill/packages/primitives/dist/index.js
105.7 KiB src/products.tsx
87.4 KiB src/lib/api.ts
68.7 KiB src/lib/lemon-ui/icons/icons.tsx
68.5 KiB ../products/posthog_ai/frontend/logics/runStreamLogic.ts
41.9 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
36.4 KiB ../node_modules/.pnpm/kea@4.0.0-pre.6_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
359.8 KiB ../node_modules/.pnpm/posthog-js@1.447.1_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
224.1 KiB ../node_modules/.pnpm/@posthog+icons@0.39.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
111.3 KiB ../packages/quill/packages/primitives/dist/index.js
105.7 KiB src/products.tsx
87.4 KiB src/lib/api.ts
68.7 KiB src/lib/lemon-ui/icons/icons.tsx
68.5 KiB ../products/posthog_ai/frontend/logics/runStreamLogic.ts
41.9 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
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
359.8 KiB ../node_modules/.pnpm/posthog-js@1.447.1_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
224.1 KiB ../node_modules/.pnpm/@posthog+icons@0.39.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
111.3 KiB ../packages/quill/packages/primitives/dist/index.js
105.7 KiB src/products.tsx
96.1 KiB ../packages/quill/packages/components/dist/index.js
87.4 KiB src/lib/api.ts
79.0 KiB src/scenes/dashboard/dashboardLogic.tsx
68.7 KiB src/lib/lemon-ui/icons/icons.tsx
68.5 KiB ../products/posthog_ai/frontend/logics/runStreamLogic.ts
Largest files eagerly shipped from src/scenes/web-analytics/WebAnalyticsScene.tsx
Size File
359.8 KiB ../node_modules/.pnpm/posthog-js@1.447.1_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
224.1 KiB ../node_modules/.pnpm/@posthog+icons@0.39.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
115.8 KiB ../node_modules/.pnpm/posthog-js@1.447.1_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/surveys-preview.js
111.3 KiB ../packages/quill/packages/primitives/dist/index.js
105.7 KiB src/products.tsx
96.1 KiB ../packages/quill/packages/components/dist/index.js
87.4 KiB src/lib/api.ts
79.0 KiB src/scenes/dashboard/dashboardLogic.tsx
68.7 KiB src/lib/lemon-ui/icons/icons.tsx
Largest files eagerly shipped from src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
Size File
359.8 KiB ../node_modules/.pnpm/posthog-js@1.447.1_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
321.1 KiB ../node_modules/.pnpm/posthog-js@1.447.1_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js
224.1 KiB ../node_modules/.pnpm/@posthog+icons@0.39.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
111.3 KiB ../packages/quill/packages/primitives/dist/index.js
105.7 KiB src/products.tsx
96.1 KiB ../packages/quill/packages/components/dist/index.js
87.4 KiB src/lib/api.ts
68.7 KiB src/lib/lemon-ui/icons/icons.tsx
68.5 KiB ../products/posthog_ai/frontend/logics/runStreamLogic.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.98 MiB no change
shell 2.36 MiB no change
query 56.2 KiB no change
features 273.8 KiB no change

Download the interactive layer graph for this PR.

Posted automatically by check-layers

✅ Toolbar bundle — eager 2.43 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.43 MiB · 19 files no change ████░░░░░░ 42.4% of 5.72 MiB
Deferred (lazy) 2.64 MiB · 44 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
967.6 KiB dist/toolbar/toolbar-app-DPGLK7O2.css
732.3 KiB dist/toolbar/chunk-chunk-J5PBHNM7.js
290.5 KiB dist/toolbar/chunk-chunk-CBC3BDP6.js
137.0 KiB dist/toolbar/chunk-chunk-HTOXCPKS.js
131.8 KiB dist/toolbar/chunk-chunk-FQJO25M6.js
75.7 KiB dist/toolbar/toolbar-app-BQQKEFBC.js
69.0 KiB dist/toolbar/chunk-chunk-BFAXZMC5.js
33.8 KiB dist/toolbar/chunk-chunk-6EBAJOMK.js
21.9 KiB dist/toolbar/chunk-chunk-6Y3VU5HE.js
6.8 KiB dist/toolbar/chunk-chunk-RVLGRPCH.js

Posted automatically by check-toolbar-size · sizes are toolbar output bytes (shipped, post-tree-shake) from the esbuild metafile

✅ Dist folder size — 🔺 +101.0 KiB (+0.0%)

Total size of the built frontend/dist folder (all assets), compared against the base branch.

Total: 991.35 MiB · 🔺 +101.0 KiB (+0.0%)

ℹ️ MCP UI apps size — 35 app(s), 29918.4 KB JS

Built size of each MCP UI app (main.js + styles.css).

App JS CSS
debug 864.4 KB 222.2 KB
explain-chart 1143.1 KB 222.2 KB
action 793.1 KB 222.2 KB
action-list 855.4 KB 222.2 KB
cohort 792.1 KB 222.2 KB
cohort-list 854.4 KB 222.2 KB
email-template 792.0 KB 222.2 KB
error-details 797.0 KB 222.2 KB
error-issue 794.1 KB 222.2 KB
error-issue-list 856.6 KB 222.2 KB
experiment 852.5 KB 222.2 KB
experiment-list 856.2 KB 222.2 KB
experiment-results 854.4 KB 222.2 KB
feature-flag 854.8 KB 222.2 KB
feature-flag-list 858.5 KB 222.2 KB
feature-flag-testing 795.4 KB 222.2 KB
inline-scan 792.6 KB 222.2 KB
insight-actors 853.9 KB 222.2 KB
llm-costs 851.2 KB 222.2 KB
session-recording 794.4 KB 222.2 KB
survey 793.7 KB 222.2 KB
survey-global-stats 851.2 KB 222.2 KB
survey-list 856.1 KB 222.2 KB
survey-stats 851.2 KB 222.2 KB
trace-span 792.5 KB 222.2 KB
trace-span-list 855.3 KB 222.2 KB
vision-observation-list 854.5 KB 222.2 KB
workflow 792.4 KB 222.2 KB
workflow-list 854.8 KB 222.2 KB
workflow-setup-status 792.1 KB 222.2 KB
query-results 1114.5 KB 222.2 KB
render-ui 1218.8 KB 222.2 KB
visual-review-snapshots 797.0 KB 222.2 KB
workflow-data-suggestions 794.8 KB 222.2 KB
workflow-sending-rules 793.5 KB 222.2 KB
ℹ️ MCP agent API — agent-facing tool changes

What this PR changes for agents, from the tool schema snapshots and tool definitions.

Tools added (1): integrations-email-create

✅ Playwright — all passed

All tests passed.

View test results →

@hosthog

hosthog Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

HostHog preview — posthog-desktop-web

The previews for this PR have been torn down and no longer serve.

@meikelmosby
meikelmosby requested a review from a team October 29, 2026 14:38
meikelmosby and others added 3 commits October 29, 2026 15:41
create_native_integration keys update_or_create on the sender address, so
an address that already has a sender is overwritten and its config.verified
is reset. On SES that stops a workflow sending from it until the domain is
verified again.

The tool is now annotated destructive, so a host that guards destructive
calls asks first, and the description tells the caller to check
integrations-list rather than call this to retry or amend a sender.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ting it

create_native_integration keyed update_or_create on the sender address, so
adding an address that already had a sender rewrote the row and reset
config.verified. On SES that stops every workflow sending from it until the
domain is verified again, and nothing told the caller it had happened.

A duplicate address is now refused, which the app surfaces as the error on
the add-sender form. Editing a sender already has its own endpoint, so the
overwrite was reachable only by mistake. The MCP tool goes back to
non-destructive, since it can no longer damage an existing sender.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment thread posthog/models/integration/email.py Resolved
tests-posthog Bot and others added 2 commits October 29, 2026 15:01
…n upsert

The duplicate check runs before the provider call, which takes seconds, so
two creates for one address can both pass it. update_or_create then resolved
the collision as an update, rewriting the winner's row and resetting its
verified flag: the overwrite the check was added to stop.

The row is now inserted, and the unique constraint on team, kind and
integration_id settles the race. The early check stays so an obvious
duplicate never reaches the provider.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PostHog Review · Standard · reviewed 700b077: 1 Should fix.

Comment thread products/integrations/mcp/tools.yaml Resolved
SES keeps one MAIL FROM per domain identity, shared by every sender on it
and across the organization's projects. A second sender on an existing
domain carried its own subdomain, defaulting to feedback, which repointed
bounce handling for the senders already using that domain.

A new sender on a domain that already has one now inherits its subdomain.
The add-sender form passes the existing value when it knows it; the rule
now holds wherever the sender is created from.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PostHog Review · Standard · reviewed ccc95ab: 1 Should fix.

This is review round 3 on this pull request. When findings keep coming back in the same code, the approach is usually the problem. Before the next fix, check whether a different design would remove the whole class of findings.

Comment thread posthog/models/integration/email.py Resolved
…MAIL FROM

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PostHog Review · Standard · reviewed 5c140ba: 1 Should fix.

This is review round 4 on this pull request. When findings keep coming back in the same code, the approach is usually the problem. Before the next fix, check whether a different design would remove the whole class of findings.

Comment thread posthog/models/integration/email.py Resolved
Comment thread posthog/models/integration/email.py Resolved
…side the org

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PostHog Review · Standard · reviewed c79983f: 1 Should fix.

This is review round 5 on this pull request. When findings keep coming back in the same code, the approach is usually the problem. Before the next fix, check whether a different design would remove the whole class of findings.

Comment thread posthog/models/integration/email.py Resolved
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
meikelmosby and others added 3 commits November 5, 2026 12:34
…its as before

A second sender on a domain now takes the MAIL FROM subdomain SES uses for
the identity, read through the workflows facade, instead of a stored row that
can disagree with SES. Editing a sender goes back to the base behavior: it
updates that one sender and no siblings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread posthog/models/integration/email.py Resolved

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PostHog Review · Standard · reviewed 61e2426: no findings.

Silthus and others added 3 commits November 6, 2026 09:36
- Take a per-domain advisory lock in create_native_integration, so two
  creates on one domain run in turn: a repeated address is refused before
  SES, and a new address takes the MAIL FROM SES now has.
- Refresh the legacy setup form from the created sender, so Save sends the
  stored MAIL FROM and does not repoint SES for the domain.
- Share the duplicate-sender message and narrow the serializer comment on
  upserts, which no longer holds for email.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Hold the domain lock on a second connection and check that a create on
  that domain waits, while a create on another domain goes through.
- Tell the legacy setup modal's user that every sender on a domain shares
  one MAIL FROM subdomain, which is why the field can show the stored value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t changes

The lock test now takes the lock through the same helper as the model,
so the key cannot drift between them. The help text says that changing
the MAIL FROM subdomain changes it for every sender on the domain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Silthus

Silthus commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Note

🤖 Claude Opus 5.5 responding on behalf of Michael

Decisions from the takeover and its review rounds:

  • 🤖 New sender's MAIL FROM SES's live value for the domain, not the oldest stored row.
  • 🤖 Sync edits across senders No, as on master. The help text says an edit changes SES for every sender on the domain.
  • 🤖 Simultaneous creates on one domain A per-domain advisory lock (lock_sender_domain), not a documented race window.
  • 🤖 Stale MAIL FROM in the legacy modal Fixed here, not deferred. After Continue the form takes the stored value, so Save cannot send the typed one back to SES.

Silthus and others added 2 commits November 6, 2026 11:51
Names and tests carry the intent: lock_sender_domain, the duplicate
refusal before the provider call, and the modal test for the stored
MAIL FROM.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… docstring

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@posthog

posthog Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

PostHog Review · Deep · reviewed 75eb979

Nothing to post.

Step Status Result
Prepare the diff done 11 files, 1 chunk
Pick perspectives done 4 perspectives
Run review passes done 2 issues (+1 blind-spot)
Merge overlapping findings done 3 → 3
Validate findings done 0 kept, 3 dismissed
Publish done Nothing to post

The dancing man in the red room from Twin Peaks

@Silthus

Silthus commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

/trunk merge

1 similar comment
@Silthus

Silthus commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

/trunk merge

@trunk-io
trunk-io Bot merged commit 89aea3c into master Nov 8, 2026
288 checks passed
@trunk-io
trunk-io Bot deleted the feat/mcp-create-email-sender branch November 8, 2026 19:52
@deployment-status-posthog

deployment-status-posthog Bot commented Nov 8, 2026 •

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-11-08 20:13 UTC Run
prod-us ✅ Deployed 2026-11-08 20:31 UTC Run
prod-eu ✅ Deployed 2026-11-08 20:29 UTC Run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants