Skip to content

fix(workflows): keep one mail from label per email sending domain - #569

Closed
Silthus wants to merge 7 commits into
email-domain-agent-setupfrom
email-mail-from-label
Closed

Silthus wants to merge 7 commits into
email-domain-agent-setupfrom
email-mail-from-label

Conversation

@Silthus

@Silthus Silthus commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Moved here from #116864, closed on 2026-10-25 so this fork stack is the only copy. It opens upstream again after review.

This revision

  • Restacked on feat(workflows): let agents set up an email sending domain via mcp #568, which now sits on master 43333f81da9. Master's memoized workflows config is now an extension per Team instance, so this PR no longer clears it on commit and no longer touches default_email_sender.py.
  • The flag is workflows-email-from-code, the one rollout flag for the Workflows email work.
  • Review fix dc05208509: a shared label edit locks every sender of the domain, the edited one included, in id order. Before, it locked its own row first and could deadlock against a verification.
  • Two-family panel fixes, 2026-11-01:
    • d6080d93ad: an edit moves SES to the chosen label when another sender on the domain holds a different one, even if the edited sender already stores it.
    • d6080d93ad, 1a311bf509: the sender page's name and setup-method saves, and the Configure modal unless its label changed, leave the label out. A page loaded before a label edit can no longer revert it.
  • Merged feat(workflows): let agents set up an email sending domain via mcp #568 at 641380bd8c (4064cb8b50, 4f59ef47cd). The email flag helper in posthog/models/integration/email.py now builds its inputs with flag_identity.

Problem

  • Two Workflows email senders on one domain can quietly undo each other's MAIL FROM subdomain (the Return-Path label, for example feedback).
  • SES stores that label on the domain identity, so every sender on the domain shares one label.
  • A new sender with a different mail_from_subdomain re-sets the label in SES for every sender on the domain.
  • Editing the label on one sender stores it on that sender only. The next verify of any other sender re-sets SES to that sender's old label.

This recreates the fork draft Silthus#345 on top of #568. Its earlier upstream draft, PostHog#111367, is closed.

Impact

Pays into Grow Workflows through organizations that send from a verified domain: a sender whose MAIL FROM label moves under it no longer matches the DNS records its owner published. Not measured on its own; it guards the email domain verified path of #568. Expected move: none on its own. Run cost: n/a.

Caution

🔴 Merge risk: high · two-way door (merge-risk rubric)

  • With workflows-email-from-code on, an edit rewrites the shared delivery setting of every sender on the domain in the organization, other projects included, and moves SES with it. A wrong label breaks the domain's MAIL FROM until its DNS records match.
  • Create, edit and verify hold a per-domain Postgres advisory lock across SES calls, and an edit takes row locks in id order.
  • The flag is off by default, and with it off the three paths keep master's logic, including the inline worker reload. The flag-off additions are one flag evaluation, one Team lookup on create, and the label left out of name-only saves in the web app.

Changes

Everything below runs only when the workflows-email-from-code flag is on, the flag that also gates the agent sender tools in #568.

  • A new sender takes the domain's existing label when the request omits it.
  • A new sender that asks for a different label gets a 400 that names the label in use. SES is not called.
  • When senders on a domain already have different labels, a new sender gets a 400 that says to save one label on an existing sender first.
  • Editing the label on one sender writes it to every sender on that domain in the organization, so a later verify of any of them keeps the new label.
  • An edit also moves SES when another sender on the domain holds a different label. That repairs a domain whose senders disagree when the person saves one label, as the 400 asks.
  • An edit with a blank label keeps the current label.
  • Create, edit and verify each run in one transaction under a per-domain advisory lock, and re-read the sender under it. A verify that loaded its sender before an edit cannot re-set the old label.
  • An edit locks the domain's senders, the edited one included, in one SELECT ... FOR UPDATE OF posthog_integration ... ORDER BY id. That is the order a verification locks them in, so a flag-off verification and a flag-on edit cannot deadlock. No Team row is locked.
  • The worker reload after an edit or a successful verify runs on commit, so workers never reload a sender before its row commits. With the flag off it runs inline, as on master.
  • Mechanical: one constant holds the default label, and one helper next to EmailIntegration evaluates the flag for both the API gate and the model. It sends the web app's person and group inputs (flag_identity), so the project's id property is the team id, not its UUID. The status route's check of workflows-email-domain-wizard uses the same inputs. Domain Connect reads the label through EmailIntegration.

Warning

Roll the flag out by organization. A teammate with the flag off still edits only their own sender, and their verify can re-set the old label for the domain.

The setup modal pre-fills feedback. On a domain that already uses another label, the user sees the 400 in a toast and edits the field. The sender page and the Configure modal send the label only when the person edits it. No screen looks different.

Read first: EmailIntegration._domain_guard, _lock_edited_sender and create_native_integration in posthog/models/integration/email.py.

How did you test this code?

Test seams: the public methods of EmailIntegration (create_native_integration, update_native_integration, verify) and the email edit endpoint. The SES facade and posthoganalytics.feature_enabled are mocked as external boundaries.

Test rationale:

  • test_new_sender_keeps_the_domain_mail_from_label and test_new_sender_cannot_move_the_domain_mail_from_label: catch the create path, including a legacy blank label. No existing test sent a label on create.
  • test_verifying_any_sender_keeps_a_changed_mail_from_label: catches the edit-then-verify path through instances loaded before the edit. It fails without the sibling write and without the re-read under the lock.
  • test_editing_a_sender_without_a_label_keeps_the_current_domain_label: catches a name-only edit that re-sets a label changed since the sender loaded.
  • test_each_sender_keeps_its_own_mail_from_label_while_the_flag_is_off: pins master's behavior with the flag off.
  • test_editing_the_label_shares_it_when_the_flag_is_on_for_the_requesting_user: sends a real PATCH and fails if the view stops passing the request user.
  • test_saving_a_label_a_sibling_lacks_moves_ses_to_it (resent label, name-only edit): fails if SES stays on a sibling's label while the rows move. Red before d6080d93ad.
  • leaves the MAIL FROM label out when saving the sender name / recording the setup method (emailDomainLogic.test.ts) and leaves out an unchanged MAIL FROM label (emailSetupModalLogic.test.ts): fail if a name-only save resends a stale label. Red before d6080d93ad and 1a311bf509.
  • test_flag_released_to_a_project_id_reaches_the_sender_update (from feat(workflows): let agents set up an email sending domain via mcp #568): fails if the flag check stops sending the project's numeric id.
  • TestEmailIntegrationConcurrentLabelEdits.test_a_shared_label_edit_waits_for_a_lower_sender_before_locking_its_own_row (TransactionTestCase, three connections): one connection holds the lower-id sender, the edit of the higher-id sender waits on a row lock, and a NOWAIT lock of the edited row from a third connection must succeed. Red before dc05208509: the edit already held its own row.

Lane gate on 48c043fd9c: the integration API, integration model, email status and eval tests (1,243 passed); the MCP unit tests (5,363 passed); the EmailDomain and EmailSetup frontend tests (105 passed); the frontend typecheck; mypy and ruff on the changed Python. Before the last merge, the wider sweep with products/workflows/backend and Domain Connect passed except test_ses_provider.py::test_delete_identity_removes_tenant_associations_first, a master test outside this diff that fails in that ordering on the pre-fix head as well and passes alone.

CI on the fork: the depot jobs never get a runner, so the gates that depend on them fail. The lane gate stands in until the PR opens upstream.

Proofs per qa-swarm PROOF.md:

  • Live run: gap. It needs a real SES account and a domain under DNS control. The lock order test runs real concurrent transactions, but not the SES calls.
  • Prod shape: gap, accepted by standing rule until 2026-11-02.
  • Shadow diff: gap. Comparing base and head label edits needs SES and real sender rows. The model tests run the flag-off and flag-on paths over the same sender rows.
  • Post-deploy watch: owed after merge.

👉 Stay up-to-date with PostHog coding conventions for a smoother review.

Release status

  • This change is behind a feature flag and is not available to users

Docs update

None. No doc under docs/ covers the email sender MAIL FROM setting.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Agent: Claude Code, Claude Opus 5.5 (claude-opus-5-5)

Decisions

Question Answer Who Where
Which flag gates the shared label? workflows-email-from-code, the one rollout flag for the Workflows email work 👤 Human map
Does the label rule follow the flag per acting user or per project? Per acting user, like the MCP tools and the API gate; the rollout warning limits it to organization targeting 🤖 Agent pending approval
How does a shared label edit lock the senders? All of them in id order in one query, matching the verification's order; the flag-off edit keeps master's single-row lock 🤖 Agent pending approval
Does an edit on a mixed-label domain move SES? Yes, to the saved label, and an SES error fails the edit before any row changes 🤖 Agent pending approval
Do name-only saves send the label? No; the web app sends it only when the person edits it 🤖 Agent pending approval

Involvement: no grilling rounds, no prototypes. GPT-6.1 Sol (high) reviewed the restack in round 1 (one finding, fixed) and in four final reads (one deadlock finding, fixed). On 2026-11-01 a two-family panel reviewed the whole diff: nine GPT-6.1 Sol (high) lenses and a Claude Opus 5.5 (high) fresh-eyes lane. Round 2 raised 5 distinct findings: 3 fixed, 1 rejected, 1 follow-up. Round 3 checked the fixes: 5 findings, 3 fixed, 2 rejected. Final reads follow. Proof: command output; live run and shadow diff owed.

🤖 Generated with Claude Code

Silthus and others added 2 commits November 1, 2026 13:17
With workflows-email-from-code on, a new sender takes the domain's
existing MAIL FROM label, an edit shares the label with every sender on
the domain, and create, edit and verify hold a per-domain advisory lock.
Worker reloads wait for that lock's commit. The flag helper now lives
next to EmailIntegration so the API gate and the model evaluate it the
same way.

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

The edit locked its own sender, then the other senders of the domain.
A verification on the same domain locks them all in id order, so the
two could deadlock. The edit now locks every sender of the domain,
its own included, in one query ordered by id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Silthus
Silthus force-pushed the email-mail-from-label branch from 94f9bae to dc05208 Compare November 1, 2026 13:17
Silthus and others added 2 commits November 1, 2026 17:26
The email flag helper now lives in posthog/models/integration/email.py and
builds its inputs with flag_identity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- an edit that saves the label the edited sender already stores still moves
  SES to it when another sender on the domain holds a different label, since
  SES may hold that other label
- the sender page's name and setup-method saves leave the label out, so a
  page loaded before a label edit cannot revert the domain to its old label

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

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

🤖 CI report

✅ 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.

✅ Bundle size — no change

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

Total: 75.13 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 — within budget

How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
1.02 MiB · 25 files no change █████████░ 88.8% 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 no change ██████████ 98.5% of 3.03 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
6.46 MiB · 2,235 files no change ██████████ 95.4% of 6.77 MiB
dashboard scene
src/scenes/dashboard/Dashboard.tsx
9.97 MiB · 3,728 files no change ██████████ 99.5% of 10.01 MiB
today home path
src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
6.71 MiB · 2,337 files no change ██████████ 96.4% of 6.96 MiB
AI-first home path
src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
7.28 MiB · 2,426 files no change ██████████ 96.7% of 7.53 MiB
events scene
src/scenes/activity/explore/EventsScene.tsx
9.40 MiB · 3,500 files no change ██████████ 98.0% of 9.59 MiB
web analytics scene
src/scenes/web-analytics/WebAnalyticsScene.tsx
10.36 MiB · 3,767 files no change ██████████ 95.6% of 10.83 MiB
replay detail scene
src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
7.99 MiB · 2,968 files no change ████████░░ 84.4% 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
🟢 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
🟢 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
🟢 packages/quill/packages/charts/ stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.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
🟢 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.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.7 KiB src/index.tsx
1.7 KiB ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
1.7 KiB src/lib/utils/bootTiming.ts
1.2 KiB src/RootErrorBoundary.tsx
949 B src/scenes/ChunkLoadErrorBoundary.tsx
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
Size File
325.0 KiB ../node_modules/.pnpm/posthog-js@1.443.0_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
217.5 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
102.9 KiB src/products.tsx
86.6 KiB src/lib/api.ts
67.9 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
27.0 KiB ../node_modules/.pnpm/tailwind-merge@2.2.2/node_modules/tailwind-merge/dist/bundle-mjs.mjs
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
325.0 KiB ../node_modules/.pnpm/posthog-js@1.443.0_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 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
153.8 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
102.9 KiB src/products.tsx
94.5 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.5 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
86.6 KiB src/lib/api.ts
Largest files eagerly shipped from src/scenes/dashboard/Dashboard.tsx
Size File
325.0 KiB ../node_modules/.pnpm/posthog-js@1.443.0_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 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
153.8 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
144.1 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
102.9 KiB src/products.tsx
94.5 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.5 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
325.0 KiB ../node_modules/.pnpm/posthog-js@1.443.0_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 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
153.8 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
102.9 KiB src/products.tsx
94.5 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.5 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
86.6 KiB src/lib/api.ts
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
Size File
325.0 KiB ../node_modules/.pnpm/posthog-js@1.443.0_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 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
153.8 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
144.1 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
102.9 KiB src/products.tsx
94.5 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.5 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
325.0 KiB ../node_modules/.pnpm/posthog-js@1.443.0_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 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
153.8 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
144.1 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
102.9 KiB src/products.tsx
94.5 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.5 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
325.0 KiB ../node_modules/.pnpm/posthog-js@1.443.0_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 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
187.9 KiB src/queries/validators.js
153.8 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
144.1 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
102.9 KiB src/products.tsx
94.5 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
Largest files eagerly shipped from src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
Size File
325.0 KiB ../node_modules/.pnpm/posthog-js@1.443.0_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
316.5 KiB ../node_modules/.pnpm/posthog-js@1.443.0_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 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
153.8 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
102.9 KiB src/products.tsx
94.5 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.5 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 PostHog#32479

✅ Toolbar bundle — eager 2.33 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.33 MiB · 19 files no change ████░░░░░░ 40.8% 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
941.7 KiB dist/toolbar/toolbar-app-UY2NCWYC.css
695.8 KiB dist/toolbar/chunk-chunk-NUWPCNXY.js
261.2 KiB dist/toolbar/chunk-chunk-T5DKNJQU.js
131.8 KiB dist/toolbar/chunk-chunk-FDH2IBXT.js
130.4 KiB dist/toolbar/chunk-chunk-KDUJEIKP.js
75.7 KiB dist/toolbar/toolbar-app-TP2JYJFO.js
69.0 KiB dist/toolbar/chunk-chunk-TSAL54PB.js
33.8 KiB dist/toolbar/chunk-chunk-FR2SFTP5.js
21.7 KiB dist/toolbar/chunk-chunk-2P7IMPQ2.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 — 🔺 +2.4 KiB (+0.0%)

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

Total: 966.34 MiB · 🔺 +2.4 KiB (+0.0%)

Silthus and others added 3 commits November 1, 2026 18:01
…en edited

The Configure modal resent the label it loaded, so a save made after
another sender changed the domain label reverted it.

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

Silthus commented Nov 2, 2026

Copy link
Copy Markdown
Owner Author

Moved to PostHog#127298.

@Silthus Silthus closed this Nov 2, 2026
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.

1 participant