Skip to content

feat(messaging): give transactional email its own scope - #128166

Merged
trunk-io[bot] merged 4 commits into
feat/messaging-preference-scopefrom
feat/transactional-email-scope
Nov 3, 2026
Merged

trunk-io[bot] merged 4 commits into
feat/messaging-preference-scopefrom
feat/transactional-email-scope

Conversation

@Silthus

@Silthus Silthus commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Impact

Merge Danger

Caution

🔴 Merge risk: high · one-way door

  • Auth change on both send routes and the status route. Worst case: a hog_flow key gets 403, or a read key sends.
  • One-way: keys created with the new scope keep it after a revert, and then get 403.

Changes

  • New transactional_email scope, read and write. Pickers show "Transactional email" in a new Messaging group, and a "Send transactional email" key preset.
  • Personal keys and OAuth list it too, because both send routes accept personal keys. The status read stays project-secret-key only, and the row says so. feat(messaging): give recipient preference writes their own scope #128116 now hides messaging_preference there.
  • Both send routes need :write, the status route :read.
  • hog_flow keys keep working through LEGACY_SCOPE_FALLBACKS, permanently. The viewsets keep scope_object = "hog_flow", so roles and template access do not change.
  • Both events record authorized_by, through feat(messaging): give recipient preference writes their own scope #128116's helper. The status-read event now also records auth_method, like the send event.
  • The template's API tab, the MCP make-transactional answer and the sending skill now tell users to create a key with transactional_email:write instead of hog_flow:write.
  • Mechanical: OpenAPI text, generated types and projections.
Preset picks the scope Row says what it can't do Messaging group in the personal key picker
preset-picks-scope row-tooltip messaging-group
Template API tab names the new scope
api-tab

How did you test this code?

Test rationale: new scope cases catch a dropped hog_flow fallback, a read key that sends, a wrong authorized_by, or the send scope reaching template edits.

  • Live run: the new keys pass auth on their routes and get 403 elsewhere. hog_flow:write still passes. Not checked live: delivery, because the local email worker was not wired to the test database (503 after auth).
  • UI walk: seven stages, settings page to created key, the personal key picker, which no longer lists the preference scope, and the template's API tab.
  • Shadow diff against feat(messaging): give recipient preference writes their own scope #128116 on one database: hog_flow keys created by the old code still pass or fail as before on the new code; only new-scope keys and the 403 wording change.
  • CI report: comment density is 4%. The comments state why hog_flow stays a permanent fallback. The TypeScript duplication pairs the two oauth-scopes.generated.ts files, which hogli build:projections writes identical by design. The other duplication rows and the bundle size changes name files this PR does not change. The one flaky Playwright test covers early access features, which this PR does not touch, and it passed on retry.

Release status

  • No feature flag controls this change

Docs update

  • None here. posthog-node and posthog.com docs follow the deploy.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Agent: Claude Code, Claude Opus 5.5. Review: Codex, GPT-6.1 Sol.

Skills: /full-stop, /qa-swarm, /adding-api-scopes, /adding-project-secret-api-key-auth, /writing-tests, /writing-user-facing-copy, /writing-code-comments, /merge-risk, /pr-description, /writing-pr-descriptions, /stacking-prs

Decisions
Question Answer Who Where
Scope name transactional_email, read and write 👤 Human PostHog/michaelr-context#5
Routes Sends need write, status needs read; the upload route moves later 👤 Human PostHog/michaelr-context#5
Access control Viewsets keep scope_object = "hog_flow"; no role migration 👤 Human PostHog/michaelr-context#5
Old scope hog_flow keeps working, no end date 👤 Human PostHog/michaelr-context#5
Measure authorized_by on both events 👤 Human PostHog/michaelr-context#5
Picker Public scope, Messaging group, "Send transactional email" preset 👤 Human PostHog/michaelr-context#5
Messaging group Holds transactional_email alone after #128116 hid messaging_preference; broadcasts may join later 👤 Human PostHog/michaelr-context#1
Attribution Reuse legacy_scope_object_used from #128116 🤖 Agent PostHog/michaelr-context#6
Scope action lists Drop send and email from the write action lists, so required_scopes alone decides 🤖 Agent PostHog/michaelr-context#6
Read on personal keys Keep it offered, and say in the row that only project secret keys read status; no per-key-type picker 🤖 Agent PostHog/michaelr-context#6

Involvement: map grilled live, design pass (GPT-6.1 Sol), three review panels (the third a full eight-lens round), three final reads, live run, UI walk, shadow diff and cross-version proof captured.

Lane gate
  • hogli ci:preflight --strict, the scope, permission and messaging pytest suites, scopes.test.ts, the project settings Jest suites, MCP tool-filtering.test.ts, frontend typecheck.
  • Red on the base code, green on HEAD: 21 selected cases fail before the change and pass after it.
More context for AI agents
  • Session: T3 Code stack thread for PostHog/michaelr-context#6; Playwright UI walk, Python live run.
  • Decisions: the design pass found that a * plus hog_flow:write key reads as hog_flow; that edge lives in feat(messaging): give recipient preference writes their own scope #128116's helper and goes there as a follow-up.
  • Follow-ups: the * attribution edge in legacy_scope_object_used; posthog-node and posthog.com docs; move the email-v2 upload route to transactional_email:write.
  • Review: round 3 fixed the in-repo setup copy, auth_method on the status-read event, a duplicated test helper and a comment.
  • Read closely: posthog/scopes.py (LEGACY_SCOPE_FALLBACKS), message_templates.py (required_scopes), transactional_email_status.py.

🤖 Generated with Claude Code

@Silthus Silthus added hogbox-preview Spin up a per-PR PostHog preview on a hogland hogbox posthog-review labels Nov 3, 2026
@Silthus Silthus self-assigned this Nov 3, 2026
@Silthus
Silthus added this pull request to stack #128167 November 3, 2026 12:00
@github-actions

github-actions Bot commented Nov 3, 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) — clean

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.

⚠️ Duplication (Python) — 4 new duplicated blocks (worst 194 tokens)

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.

First copy Second copy Lines Tokens
products/messaging/backend/tests/api/test_message_preferences.py:42 products/messaging/backend/tests/test_message_preferences.py:15 28 194
posthog/tasks/test/test_usage_report.py:980 posthog/tasks/test/test_usage_report.py:1102 45 175
products/warehouse_sources/sources/my_hours/my_hours.py:241 products/warehouse_sources/sources/salesflare/salesflare.py:98 16 104
products/warehouse_sources/backend/temporal/data_imports/sources/vitally/vitally.py:411 products/warehouse_sources/sources/faire/faire.py:81 11 78
⚠️ Duplication (TypeScript) — 9 new duplicated blocks (worst 1706 tokens)

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.

First copy Second copy Lines Tokens
nodejs/src/ingestion/pipelines/sessionreplay/ai-research/image-fetch-v4/configuration-policy.ts:12 nodejs/src/ingestion/pipelines/sessionreplay/ml-mirror-image-fetch/configuration-policy.ts:12 249 1706
nodejs/src/ingestion/pipelines/sessionreplay/ai-research/image-fetch-v4/origin-request-scheduler.ts:12 nodejs/src/ingestion/pipelines/sessionreplay/ml-mirror-image-fetch/origin-request-scheduler.ts:12 181 897
frontend/src/lib/oauthScopes.generated.ts:12 services/mcp/src/lib/oauth-scopes.generated.ts:12 263 546
nodejs/src/cdp/consumers/cdp-data-warehouse-events.consumer.ts:55 nodejs/src/cdp/consumers/cdp-internal-event.consumer.ts:60 34 201
nodejs/src/cdp/consumers/cdp-cohort-membership-trigger.consumer.ts:97 nodejs/src/cdp/consumers/cdp-data-warehouse-events.consumer.ts:68 23 128
products/ai_observability/frontend/redesign/trace/sampleFixtures/anthropicMessagesThinkingToolUse.ts:147 products/ai_observability/frontend/redesign/trace/sampleFixtures/langchainCerebrasImageError.ts:123 18 113
nodejs/src/cdp/consumers/cdp-cohort-membership-trigger.consumer.ts:105 nodejs/src/cdp/consumers/cdp-person-updates-consumer.ts:41 15 85
nodejs/src/cdp/consumers/cdp-data-warehouse-events.consumer.ts:67 nodejs/src/cdp/consumers/cdp-events.consumer.ts:65 17 77
nodejs/src/cdp/consumers/cdp-data-warehouse-events.consumer.ts:67 nodejs/src/cdp/consumers/cdp-dlq-replay.consumer.ts:145 13 73
⚠️ Comment density — 4% of added code lines are comments (4 of 113)

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
products/messaging/backend/presentation/views/message_templates.py 3 15
posthog/scopes.py 1 5

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

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

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

Total: 76.72 MiB · 🔺 +21.7 KiB (+0.0%)

File Size Δ vs base
exporter/src/exporter/scenes/ExporterNotebookScene.js 4.03 MiB 🔺 +106.2 KiB (+2.6%)
posthog-app/_parent/products/ai_observability/frontend/AIObservabilityTraceScene.js 141.3 KiB 🟢 -88.3 KiB (-38.5%)
posthog-app/_parent/products/data_modeling/frontend/ModelsScene.js 91.7 KiB 🔺 +2.1 KiB (+2.4%)

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.01 MiB · 25 files 🔺 +745 B (+0.1%) █████████░ 88.5% 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 · 622 files 🔺 +809 B (+0.0%) ██████████ 98.6% of 3.03 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
3.86 MiB · 1,238 files 🔺 +809 B (+0.0%) ██████████ 96.4% of 4.01 MiB
dashboard scene
src/scenes/dashboard/Dashboard.tsx
8.32 MiB · 3,061 files 🔺 +1022 B (+0.0%) ████████░░ 84.6% 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.71 MiB · 1,761 files 🔺 +809 B (+0.0%) ██████████ 95.5% 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
5.95 MiB · 2,258 files 🔺 +809 B (+0.0%) █████████░ 94.9% of 6.27 MiB
events scene
src/scenes/activity/explore/EventsScene.tsx
7.34 MiB · 2,624 files 🔺 +1022 B (+0.0%) ████████░░ 78.0% of 9.41 MiB
web analytics scene
src/scenes/web-analytics/WebAnalyticsScene.tsx
8.25 MiB · 2,949 files 🔺 +1022 B (+0.0%) ████████░░ 78.7% of 10.49 MiB
replay detail scene
src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
7.40 MiB · 2,653 files 🔺 +1.2 KiB (+0.0%) █████████░ 91.1% 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/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
355.5 KiB ../node_modules/.pnpm/posthog-js@1.444.4_@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
104.2 KiB src/products.tsx
88.3 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.3 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
355.5 KiB ../node_modules/.pnpm/posthog-js@1.444.4_@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
104.2 KiB src/products.tsx
88.3 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.7 KiB src/lib/utils/eventUsageLogic.ts
28.3 KiB src/lib/constants.tsx
Largest files eagerly shipped from src/scenes/dashboard/Dashboard.tsx
Size File
355.5 KiB ../node_modules/.pnpm/posthog-js@1.444.4_@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
114.5 KiB ../node_modules/.pnpm/posthog-js@1.444.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/surveys-preview.js
108.9 KiB ../packages/quill/packages/primitives/dist/index.js
104.2 KiB src/products.tsx
94.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.6 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
90.4 KiB ../packages/quill/packages/components/dist/index.js
88.3 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
355.5 KiB ../node_modules/.pnpm/posthog-js@1.444.4_@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
108.9 KiB ../packages/quill/packages/primitives/dist/index.js
104.2 KiB src/products.tsx
88.3 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/@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
355.5 KiB ../node_modules/.pnpm/posthog-js@1.444.4_@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
108.9 KiB ../packages/quill/packages/primitives/dist/index.js
104.2 KiB src/products.tsx
88.3 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/@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
355.5 KiB ../node_modules/.pnpm/posthog-js@1.444.4_@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
108.9 KiB ../packages/quill/packages/primitives/dist/index.js
104.2 KiB src/products.tsx
94.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.6 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
90.4 KiB ../packages/quill/packages/components/dist/index.js
88.3 KiB src/lib/api.ts
75.3 KiB src/scenes/dashboard/dashboardLogic.tsx
Largest files eagerly shipped from src/scenes/web-analytics/WebAnalyticsScene.tsx
Size File
355.5 KiB ../node_modules/.pnpm/posthog-js@1.444.4_@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
114.5 KiB ../node_modules/.pnpm/posthog-js@1.444.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/surveys-preview.js
108.9 KiB ../packages/quill/packages/primitives/dist/index.js
104.2 KiB src/products.tsx
94.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.6 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
90.4 KiB ../packages/quill/packages/components/dist/index.js
88.3 KiB src/lib/api.ts
Largest files eagerly shipped from src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
Size File
355.5 KiB ../node_modules/.pnpm/posthog-js@1.444.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
321.5 KiB ../node_modules/.pnpm/posthog-js@1.444.4_@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
108.9 KiB ../packages/quill/packages/primitives/dist/index.js
104.2 KiB src/products.tsx
94.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.6 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
90.4 KiB ../packages/quill/packages/components/dist/index.js
88.3 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.97 MiB 🔺 +279 B (+0.0%)
shell 2.39 MiB 🔺 +524 B (+0.0%)
query 56.4 KiB no change
features 378.4 KiB no change

Download the interactive layer graph for this PR.

Posted automatically by check-layers

✅ Toolbar bundle — eager 2.37 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.37 MiB · 19 files 🔺 +693 B (+0.0%) ████░░░░░░ 41.4% 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
947.8 KiB dist/toolbar/toolbar-app-IGL4DHA7.css
726.8 KiB dist/toolbar/chunk-chunk-JTS2WDVZ.js
261.2 KiB dist/toolbar/chunk-chunk-FTMGICRW.js
131.8 KiB dist/toolbar/chunk-chunk-FDH2IBXT.js
130.3 KiB dist/toolbar/chunk-chunk-Z7V352IS.js
75.7 KiB dist/toolbar/toolbar-app-BRXABF4P.js
69.0 KiB dist/toolbar/chunk-chunk-TSAL54PB.js
33.8 KiB dist/toolbar/chunk-chunk-C2JAWSHT.js
21.7 KiB dist/toolbar/chunk-chunk-GBEZW6F6.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 — 🔺 +1016.6 KiB (+0.1%)

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

Total: 970.16 MiB · 🔺 +1016.6 KiB (+0.1%)

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

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

App JS CSS
debug 844.4 KB 211.6 KB
explain-chart 1119.2 KB 211.6 KB
action 767.8 KB 211.6 KB
action-list 835.4 KB 211.6 KB
cohort 766.8 KB 211.6 KB
cohort-list 834.4 KB 211.6 KB
email-template 766.6 KB 211.6 KB
error-details 771.7 KB 211.6 KB
error-issue 769.0 KB 211.6 KB
error-issue-list 836.8 KB 211.6 KB
experiment 832.5 KB 211.6 KB
experiment-list 836.1 KB 211.6 KB
experiment-results 834.3 KB 211.6 KB
feature-flag 834.8 KB 211.6 KB
feature-flag-list 838.5 KB 211.6 KB
feature-flag-testing 770.0 KB 211.6 KB
inline-scan 767.2 KB 211.6 KB
insight-actors 833.8 KB 211.6 KB
llm-costs 831.1 KB 211.6 KB
session-recording 769.0 KB 211.6 KB
survey 768.4 KB 211.6 KB
survey-global-stats 831.2 KB 211.6 KB
survey-list 836.1 KB 211.6 KB
survey-stats 831.2 KB 211.6 KB
trace-span 767.1 KB 211.6 KB
trace-span-list 835.3 KB 211.6 KB
vision-observation-list 834.5 KB 211.6 KB
workflow 767.1 KB 211.6 KB
workflow-list 834.7 KB 211.6 KB
workflow-setup-status 766.8 KB 211.6 KB
query-results 1090.8 KB 211.6 KB
render-ui 1195.1 KB 211.6 KB
visual-review-snapshots 771.6 KB 211.6 KB
workflow-data-suggestions 769.4 KB 211.6 KB
workflow-sending-rules 768.1 KB 211.6 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-make-email-template-transactional-execute description changed
workflows-make-email-template-transactional-prepare description changed
⚠️ Playwright — 1 flaky

🎭 Playwright report · View test results →

⚠️ 1 flaky test:

  • Early access feature new and list (chromium)

These issues are not necessarily caused by your changes.
Annoyed by this section? Help fix flakies and failures and it will go green!

❌ Hogbox preview — build failed

The preview didn't come up for commit c25063d. See the build log for the failing step. It'll retry on the next push.

Previews are optional and never block merging. A failure here is often a hogland or tailnet hiccup rather than anything in your PR, so the check stays green and this section is the status.

A project secret key with `transactional_email:write` sends transactional
email, and one with `transactional_email:read` reads a send's status,
without holding `hog_flow`. `hog_flow` keeps working on these routes
through LEGACY_SCOPE_FALLBACKS. The send and status events record
which scope authorized the call.

Ticket: PostHog/michaelr-context#6

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Silthus
Silthus force-pushed the feat/transactional-email-scope branch from 17f8927 to 1ab5dbd Compare November 3, 2026 13:09
The status route accepts only project secret API keys, so the scope row
now says Read works only with one. Personal keys and OAuth still list the
scope, because both send routes accept them.

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

parameterai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Risk: No findings

The incremental change since the last review is a pure line-wrap reflow of the MCP make-transactional answer description in email_templates.yaml; the text content, the scope requirements it states, and all auth-relevant code are unchanged, so it introduces no new risk.

Sentinel reviewed c25063d · Review settings

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team November 3, 2026 16:07
Silthus and others added 2 commits November 3, 2026 16:18
The in-app API example, the MCP make-transactional answer and the
sending skill still told users to create a key with hog_flow:write, so
new senders got the broad key. They now name transactional_email:write.

The status-read event now carries auth_method, so the adoption measure,
read for project secret keys, counts status reads too.

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

Silthus commented Nov 3, 2026

Copy link
Copy Markdown
Contributor Author

/trunk merge

@trunk-io

trunk-io Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

😎 Stack merged successfully - details.

@trunk-io
trunk-io Bot merged commit 03408c2 into master Nov 3, 2026
341 of 342 checks passed
@trunk-io
trunk-io Bot deleted the feat/transactional-email-scope branch November 3, 2026 20:42
@deployment-status-posthog

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

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-11-03 21:27 UTC Run
prod-us ✅ Deployed 2026-11-03 21:47 UTC Run
prod-eu ✅ Deployed 2026-11-03 21:49 UTC Run

This branch had an error being deployed

1 failed deployment
hogbox-preview — c25063df Deployed Nov 3, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

hogbox-preview Spin up a per-PR PostHog preview on a hogland hogbox

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants