Skip to content

feat(workflows): offer the sandbox sender in workflow email steps - #450

Closed
Silthus wants to merge 1 commit into
workflows-sandbox/daily-capsfrom
workflows-sandbox/ui
Closed

Silthus wants to merge 1 commit into
workflows-sandbox/daily-capsfrom
workflows-sandbox/ui

Conversation

@Silthus

@Silthus Silthus commented Oct 12, 2026 •

Copy link
Copy Markdown
Owner

Problem

A workflow author without a verified domain has no sender to pick in an email step, so they cannot build a workflow that emails anyone. This layer offers the sandbox sender in the step's sender picker.

Supersedes PostHog#111695 (left open as the original draft). The template test-send interaction follows in a separate layer.

Stack: layer 6 of 12, stacked on #441. Next: #451.

This fork PR moves to an upstream PostHog/posthog PR after review.

Warning

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

  • Sender selection affects workflow configuration; the sandbox feature flag limits new selections.
  • Save-time restrictions and delivery guards remain enforced by the preceding layers.

Changes

  • The "Before you send" check for a cohort no longer counts the sandbox sender as a verified sender. When it is the only sender, the dialog says it only delivers to organization members and offers "Set up email".
  • Failed sender loading or sandbox setup keeps both Try again and Connect email sender available.
  • Selecting sandbox confirms replacement of multiple senders and names them. Custom From, sender name or Reply-To values also require confirmation.
  • Channels keeps the loaded grid after a failed refresh and shows a small stale notice with a retry.
  • An initial Channels load failure shows the API error detail.
  • Email workflow steps offer the sandbox sender alongside project senders and explain the verified-member restriction.
  • Sandbox remains exclusive from sender rotation.
  • Transactional email templates list only the project's own senders, not the sandbox sender.
  • Channels shows the managed sender separately from user-managed connections and preserves the existing channel catalog.
  • Saved sandbox selections keep their restrictions after flag rollback; broadcasts and setup status consider only user-managed senders.
  • Provisioning avoids duplicate requests and ignores results from another project; an explicit request retries transient failures.
  • Mechanical: sender-picker component extraction, inline type generation, and the missing HogVM AST type export required by master's existing frontend import.

UI walk at fa33d78fb36. Captures from a local preview of the stack tip fa33d78fb36, flag workflows-sandbox-sender, demo data. Flag off is the before. Real SES is unreachable in this preview, so sends stop at the SES call, shown as ECONNREFUSED.

Before: Channels without the sandbox sender

Before (flag off): Channels shows Email as not set up, and there is no sandbox sender.

After: Channels with the sandbox sender row

After (flag on): Channels provisions the sandbox sender and shows it in its own row, with the members-only copy and Add your own sender.

Before: email step with no sender to pick

Before (flag off): an email step in a project without senders has nothing to pick.

After: picker offers the sandbox sender

After (flag on): the picker offers the sandbox sender with a Sandbox tag.

After: sandbox sender selected with its hint

After picking it: the hint says it delivers only to verified members, and Reply-To and the custom sender fields go away.

Narrow: picker menu with the side panel open

Narrow (side panel open, about 547px of scene): the picker, hint and menu fit without clipping.

Follow-up: sandbox row with a verified own sender

Known follow-up: with a verified own sender, the sandbox row still offers Add your own sender.

Earlier captures. Each caption names the commit it was taken at.

Email step sender picker listing the sandbox sender with its Sandbox tag

The email step sender picker lists the sandbox sender with a Sandbox tag, and the step says who it delivers to. Taken at 7f003f71b3c in the combined live preview of both stacks.

Messaging channels page with the sandbox sender row

The Messaging channels page shows the sandbox sender row above the project's own connections. Taken at 7f003f71b3c in the combined live preview of both stacks.

Email step sender picker showing the load error next to Connect email sender

When senders fail to load, the email step keeps Connect email sender next to the error and its Try again. Taken at 2a8c88382f0 in the combined live preview of both stacks.

Channels page keeping the loaded grid with a stale-refresh notice

When a background refresh fails, Channels keeps the loaded grid and shows a small stale notice with Try again. Taken at 2a8c88382f0 in the combined live preview of both stacks.

Channels page showing the API error detail when the first load fails

When the first load fails, Channels shows the API error detail. Both failures were simulated by failing the integrations request in the browser. Taken at 2a8c88382f0 in the combined live preview of both stacks.

Rotating senders with the replaces-the-selection warning

With two own senders rotating, the picker warns that choosing the sandbox sender replaces the selection. Taken at 2a8c88382f0 in the combined live preview of both stacks.

Confirmation naming both rotating senders before switching to the sandbox sender

Picking the sandbox sender then asks for confirmation and names both senders it replaces. Taken at 2a8c88382f0 in the combined live preview of both stacks.

Known follow-ups (found in the UI walk, not fixed here):

  • The Channels sandbox row keeps offering "Add your own sender" after a verified own sender exists.

How did you test this code?

Current evidence, 2026-10-24, head 0cff277a9df.

Owed proofs:

  • Live run: rerun at the stack tip 8ac8853cc82, which contains this layer. The stack's cdp-api sent through the workflow test-run endpoint to a local SES fake that records each request. 9 of 9 cases pass. 12 of 13 recorded cases match the run at fa33d78fb36. Case 2c (an unverified member on To) now shows the verify-your-email guidance with reason recipient_unverified, and makes no SES request. For this layer: case 1 sends from a step that selected the sandbox sender. The UI walk shows the picker.
  • UI walk: local preview of the stack tip fa33d78fb36, flag workflows-sandbox-sender off (before) and on (after), demo data. The captures sit in Changes, beside the behavior each one shows. Round 4 changes no UI in this layer, so these fa33d78fb36 captures still hold at the round 4 head.

Changes in this layer since the 2026-10-19 heads (restacked onto fork master 448d802: no conflict in this layer):

  • Round 1 fixed: the transactional template sender list no longer shows the sandbox row as a normal sender, a list master added since the old base (EmailSenderSummary.test.tsx).
  • Round 1 fixed: sender load errors keep the API detail or fall back to copy per surface, so the Channels fallback is reachable again. One sandboxEmailSenderSetupError selector replaces three copies of the rule (integrationsLogic test with and without API detail).
  • Round 1 fixed: copy: the audience warning says "verified" again, and a helper replaces the nested ternary for the rotation hint.
  • No change in this layer since round 2.
  • Final read: GPT-6.1 Sol read the G3 layer range-diffs and found nothing.
  • Round 4: no change in this layer.
  • Final read 2: GPT-6.1 Sol read the round 4 range-diffs per group and found nothing.
Local gate at the stack tip 8ac8853cc82, shared by every layer

Test seams: real integration-loader and channel-catalogue actions, templater selection, the rendered sender picker and the transactional template sender list.

Test rationale: Existing picker cases cover failed loads, failed setup and explicit 404 retries. The rotation case confirms replacement and names both senders. Rendered picker and catalog cases prove recovery actions, preserved connections and API error detail.

Gaps: real AWS SES delivery and the AWS-side tenant pause (every proof uses a local SES fake or an unreachable SES), clustered Valkey, the production data shape (prod-shape gap until production read access on 2026-11-02), and fork CI, which fails during setup: path selection fails without a GitHub App client ID, and the Security aggregate rejects its fork-skipped path-filter job. Application CI is a verification gap. The UI walk could not create an own sender through the UI, because SES was unreachable; it seeded own senders in the database instead.

Earlier evidence (superseded heads)

Round 3 heads, 2026-10-23. This layer's head: 1ff130bc716. Stack tip fa33d78fb36.

Whole-stack QA swarm, 2026-10-19. Reviewed tip cfee7e148ae. This layer's head: 4964ee37062.

  • Live run at b9bd346ed0e (this stack's cdp-api against a local SES fake, workflow test-run endpoint): a sandbox send to a verified member went out from the fixed identity through the sandbox tenant and configuration set. The step's From name, From address and Reply-To were ignored with a log line. A non-member on CC and an unverified member were blocked with the clear message. A paused tenant state skipped sandbox sends while own-sender sends continued.
  • Shadow diff: an own-sender send produced the same SES request and the same stored message on master and on this stack.
  • Cross-version: a worker on master reports a sandbox send as sent without calling SES. See the deploy order in feat(workflows): provision a managed sandbox email sender #426's merge risk.
  • Checks at the tip (Node suites at 1a0fe3663c2; later fixes touch no Node code): Node CDP messaging, members and rate limiter suites (including email-tracking.service.test.ts, which now runs), the cdp-api suite, the Python sandbox, tenant, tier, reputation and validation suites, the frontend Jest suites of this stack, ruff, and the strict pre-push preflight: pass.
  • Fixed in this layer: the message audience sender check ignores the sandbox row and explains a sandbox-only project. New parameterized cases for broadcast and workflow destinations failed before the fix.
  • Gaps: real AWS SES delivery, clustered Valkey, and the production data shape (no production read access yet). Fork CI still fails during setup.

Rebased onto master 734468db6f9 (2026-10-19), head 1c24925856f.

  • Drift: Master added the email media library to emailTemplaterLogic (feature flag and project connects, emailMediaLibraryEnabled, unlayerEditorUser), a scope-shortfall event to integrationsLogic, and new workflow flags in constants.tsx.
  • Adapted: The templater keeps master's media library selectors and this layer's supportedFields-based visibleFields and hiddenAdvancedFields. integrationsLogic connects both master's event and this layer's setFeatureFlags. WORKFLOWS_SANDBOX_SENDER keeps alphabetical order.
  • This layer: Jest at this layer for the email templater, integrations, and Channels: 11 suites, 154 passed.
  • Gaps at modernize time: email-tracking.service.test.ts did not run then. It runs now; see the QA swarm block above. Fork CI still fails during setup, as noted below. Real AWS delivery and clustered Valkey remain unchecked.
Checks at the stack tip

Stack tip b9bd346ed0e (#456), shared by every layer:

  • Python, 354 passed: sandbox sender, sender validation, CDP validation, reputation, SES tenant state and events, sending tier.
  • Node CDP messaging, managers, and rate limiter: 17 suites, 599 passed.
  • Frontend Jest, 37 suites, 487 passed. Five suites timed out under host load in the batch run and passed when rerun alone.
  • Frontend tsgo: no errors. Node tsgo: no errors in CDP. The 23 errors it reports are all in session replay files that need the unbuilt Rust @posthog/replay-anonymizer module.
  • ruff, oxfmt, oxlint, prettier, eslint, product:lint (workflows, cdp), IDOR coverage, hogli build:openapi (no diff), and the strict pre-push preflight: pass.
  • Review: GPT-6.1 Sol read every conflict resolution in the stack and found no issues.
Observed final-round error recovery and rotation verification
Missing connect action: red before repair; green afterward
Failed refresh retaining the grid: red before repair; green afterward
Initial load showing API detail: red before repair; green afterward
Rotation warning and confirmation: red before repair; green afterward
emailTemplaterLogic.test.ts -t 'sender picker': 18 passed
EmailTemplater.test.tsx: 9 passed
channelCatalogLogic.test.ts: 3 passed
Frontend typecheck and build: exit 0
ci:preflight --strict --against workflows-sandbox/daily-caps: failures 0
Observed targeted verification after QA fixes
Sandbox integration cases: 12 passed
Sender-picker cases: 15 passed
Channel catalogue cases: 3 passed
Observed combined-stack gate
Node.js typecheck and build: exit 0
Frontend typecheck and build: exit 0
Success: no issues found in 23388 source files
product:lint workflows: All checks passed
ci:preflight --strict: failures 0

👉 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

No existing document covers the picker and Channels interaction. The preceding layers document sandbox delivery configuration.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Decisions

Question Answer Who Where
Picker with both a controlled value and a logic-bound contract (G3-13) Follow-up component API refactor. Review found no defect 🤖 Agent decision comment
Sandbox row keeps "Add your own sender" after a verified own sender exists (UI-F4) Known follow-up. The banner and the picker already move users to the own sender 🤖 Agent decision comment

Involvement: restack onto fork master 448d802 on 2026-10-23; round 1 with GPT-6.1 Sol lanes plus Claude Opus 5.5 fresh eyes for group G3; round 2 on the fix diff; final read 1 by GPT-6.1 Sol per group, with no findings; proofs captured; final read 2 by GPT-6.1 Sol on the round 4 decisions, with no findings; the round 4 live run and a UI walk of the two changed surfaces. Before that: the 2026-10-19 whole-stack QA swarm and the modernize rebase (see Earlier evidence). No review bots run on the fork.

Agent: Codex with GPT-6.1 Sol (gpt-6.1-sol) built the original layers, orchestrated by Claude Code with Claude Opus 5.5 (claude-opus-5-5). Restack, review fixes and this description refresh: Claude Opus 5.5 (claude-opus-5-5). Review: GPT-6.1 Sol (gpt-6.1-sol). Fresh eyes: Claude Opus 5.5 (claude-opus-5-5).

Skills: tdd, pr, stacking-prs, writing-pr-descriptions, merge-risk, qa-swarm, writing-ui-components, placing-product-frontend-code, writing-kea-logics, running-ci-preflight, writing-voice, writing-user-facing-copy, and writing-tests. The description refresh used pr-description, writing-pr-descriptions, writing-voice and merge-risk.

The public source draft is adapted to master's channel catalog and shared templater. UI provisioning and template test sends are separate reviewable layers. Committed fixtures are synthetic; no private session material is included.

🤖 Generated with Claude Code

@Silthus
Silthus added this pull request to stack #429 October 12, 2026 14:39
@Silthus Silthus self-assigned this Oct 12, 2026
@Silthus
Silthus force-pushed the workflows-sandbox/ui branch from 7f003f7 to d6d4efe Compare October 12, 2026 23:14
@Silthus
Silthus force-pushed the workflows-sandbox/ui branch 2 times, most recently from 9a2aacf to 2a8c883 Compare October 13, 2026 03:35
@Silthus
Silthus force-pushed the workflows-sandbox/ui branch from 2a8c883 to 1c24925 Compare October 19, 2026 10:58
@Silthus
Silthus force-pushed the workflows-sandbox/ui branch from 1c24925 to 5b5e9d0 Compare October 19, 2026 13:11
@Silthus
Silthus force-pushed the workflows-sandbox/ui branch from 5b5e9d0 to 4964ee3 Compare October 19, 2026 13:36
@Silthus
Silthus force-pushed the workflows-sandbox/ui branch from 4964ee3 to 1ff130b Compare October 23, 2026 16:26
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

🤖 CI report

⚠️ Complexity (TypeScript) — 11 functions above the limit (max 27)

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
handleOauthCallback frontend/src/lib/integrations/integrationsLogic.ts:1412 27 10
CyclotronJobInputRenderer frontend/src/lib/components/CyclotronJob/CyclotronJobInputs.tsx:769 26 10
getIntegrationNameFromKind frontend/src/lib/integrations/utils.ts:147 26 10
CyclotronJobInputWithSchema frontend/src/lib/components/CyclotronJob/CyclotronJobInputs.tsx:1104 25 10
HogFlowFunctionConfiguration products/workflows/frontend/Workflows/hogflows/steps/components/HogFlowFunctionConfiguration.tsx:194 21 10
NativeEmailIntegrationChoice frontend/src/scenes/hog-functions/email-templater/NativeEmailIntegrationChoice.tsx:37 20 10
BroadcastContentStep products/workflows/frontend/Broadcasts/steps/BroadcastContentStep.tsx:26 18 10
NativeEmailTemplaterForm frontend/src/scenes/hog-functions/email-templater/EmailTemplater.tsx:292 15 10
<anonymous> products/workflows/frontend/setupGuide/wizard/workflowsOnboardingWizardLogic.ts:565 13 10
<anonymous> frontend/src/scenes/hog-functions/email-templater/emailTemplaterLogic.tsx:1168 12 10
EmailSenderSummary frontend/src/scenes/hog-functions/email-templater/EmailSenderSummary.tsx:11 11 10
✅ Bundle size — 🟢 -6.8 KiB (-0.0%)

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

Total: 76.02 MiB · 🟢 -6.8 KiB (-0.0%)

File Size Δ vs base
render-query/src/render-query/render-query.js 24.64 MiB 🔺 +3.6 KiB (+0.0%)

Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report

⚠️ Eager graph — 2 over budget

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

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
1.01 MiB · 24 files 🔺 +47 B (+0.0%) █████████░ 88.6% of 1.14 MiB
logged-out boot: index + App + bootApp (preloaded by every page, including /login)
src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
2.94 MiB · 620 files 🔺 +218 B (+0.0%) ██████████ 96.9% of 3.03 MiB
🟡 authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
7.65 MiB · 2,633 files 🔺 +2.2 KiB (+0.0%) ██████████ 102.1% of 7.50 MiB
dashboard scene
src/scenes/dashboard/Dashboard.tsx
9.68 MiB · 3,703 files 🟢 -1.0 KiB (-0.0%) ██████████ 96.7% of 10.01 MiB
🟡 today home path
src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
8.06 MiB · 2,977 files 🔺 +2.0 KiB (+0.0%) ██████████ 104.1% of 7.74 MiB
AI-first home path
src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
7.90 MiB · 2,706 files 🔺 +2.0 KiB (+0.0%) █████████░ 88.2% of 8.96 MiB
events scene
src/scenes/activity/explore/EventsScene.tsx
9.15 MiB · 3,495 files 🟢 -372 B (-0.0%) ██████████ 95.4% of 9.59 MiB
web analytics scene
src/scenes/web-analytics/WebAnalyticsScene.tsx
10.01 MiB · 3,713 files 🟢 -372 B (-0.0%) █████████░ 92.4% of 10.83 MiB
replay detail scene
src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
8.23 MiB · 3,075 files 🔺 +2.4 KiB (+0.0%) █████████░ 86.9% of 9.47 MiB

🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/layout/navigation-3000/navigationLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/dashboard/dashboardLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/lemon-ui/LemonMarkdown/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/RichContentEditor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/CodeSnippet/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/taxonomy/core-filter-definitions-by-group.json stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/zod/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/billing/billingLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/ProductEmptyState/productSetupPreloadLogic.ts stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/dashboards/frontend/widgets/previews/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/wizard/frontend/runs/WizardRunDetailsDrawer.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 zod/v4/locales/de.js stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/queries/validators.js stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/scenes/project-homepage/today/TodayReportPage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/queries/Query/Query.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 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.5 KiB ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js
3.9 KiB ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js
1.4 KiB ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
1.4 KiB src/index.tsx
1.3 KiB src/RootErrorBoundary.tsx
1.3 KiB src/lib/utils/bootTiming.ts
912 B ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
Size File
321.9 KiB ../node_modules/.pnpm/posthog-js@1.438.18_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
220.9 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.3 KiB src/products.tsx
98.9 KiB src/lib/api.ts
69.4 KiB src/lib/lemon-ui/icons/icons.tsx
38.7 KiB ../node_modules/.pnpm/@dnd-kit+core@6.0.8_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@dnd-kit/core/dist/core.esm.js
33.9 KiB ../node_modules/.pnpm/kea@4.0.0-pre.6_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js
29.4 KiB src/scenes/scenes.ts
27.0 KiB src/lib/constants.tsx
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
321.9 KiB ../node_modules/.pnpm/posthog-js@1.438.18_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.8 KiB src/taxonomy/core-filter-definitions-by-group.json
238.5 KiB ../packages/quill/packages/quill/dist/index.js
220.9 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
143.2 KiB ../packages/quill/packages/primitives/dist/index.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.3 KiB src/products.tsx
98.9 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/dashboard/Dashboard.tsx
Size File
321.9 KiB ../node_modules/.pnpm/posthog-js@1.438.18_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.8 KiB src/taxonomy/core-filter-definitions-by-group.json
238.5 KiB ../packages/quill/packages/quill/dist/index.js
220.9 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
143.2 KiB ../packages/quill/packages/primitives/dist/index.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.3 KiB src/products.tsx
98.9 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
Size File
321.9 KiB ../node_modules/.pnpm/posthog-js@1.438.18_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.8 KiB src/taxonomy/core-filter-definitions-by-group.json
238.5 KiB ../packages/quill/packages/quill/dist/index.js
220.9 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
143.2 KiB ../packages/quill/packages/primitives/dist/index.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.3 KiB src/products.tsx
98.9 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
Size File
321.9 KiB ../node_modules/.pnpm/posthog-js@1.438.18_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.8 KiB src/taxonomy/core-filter-definitions-by-group.json
238.5 KiB ../packages/quill/packages/quill/dist/index.js
220.9 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
143.2 KiB ../packages/quill/packages/primitives/dist/index.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.3 KiB src/products.tsx
98.9 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/activity/explore/EventsScene.tsx
Size File
321.9 KiB ../node_modules/.pnpm/posthog-js@1.438.18_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.8 KiB src/taxonomy/core-filter-definitions-by-group.json
238.5 KiB ../packages/quill/packages/quill/dist/index.js
220.9 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
143.2 KiB ../packages/quill/packages/primitives/dist/index.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.3 KiB src/products.tsx
98.9 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/web-analytics/WebAnalyticsScene.tsx
Size File
321.9 KiB ../node_modules/.pnpm/posthog-js@1.438.18_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.8 KiB src/taxonomy/core-filter-definitions-by-group.json
238.5 KiB ../packages/quill/packages/quill/dist/index.js
220.9 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
183.2 KiB src/queries/validators.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
143.2 KiB ../packages/quill/packages/primitives/dist/index.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.3 KiB src/products.tsx
98.9 KiB src/lib/api.ts
Largest files eagerly shipped from src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
Size File
321.9 KiB ../node_modules/.pnpm/posthog-js@1.438.18_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
316.1 KiB ../node_modules/.pnpm/posthog-js@1.438.18_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js
286.8 KiB src/taxonomy/core-filter-definitions-by-group.json
238.5 KiB ../packages/quill/packages/quill/dist/index.js
220.9 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.3 KiB src/products.tsx
98.9 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js

Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of PostHog#32479

✅ Toolbar bundle — eager 2.29 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.29 MiB · 18 files 🔺 +99 B (+0.0%) ████░░░░░░ 39.9% of 5.72 MiB
Deferred (lazy) 2.11 MiB · 40 files no change n/a — loads on demand
Loader dist/toolbar.js 1.2 KiB no change █░░░░░░░░░ 6.0% of 19.5 KiB
Largest eagerly-shipped chunks
Size File
899.4 KiB dist/toolbar/toolbar-app-FHAKV2CJ.css
691.5 KiB dist/toolbar/chunk-chunk-P2Q5W6MF.js
260.7 KiB dist/toolbar/chunk-chunk-HRRIOF34.js
131.8 KiB dist/toolbar/chunk-chunk-FDH2IBXT.js
130.4 KiB dist/toolbar/chunk-chunk-56VOFFKM.js
74.8 KiB dist/toolbar/toolbar-app-Y5KIAIV6.js
69.0 KiB dist/toolbar/chunk-chunk-TSAL54PB.js
35.6 KiB dist/toolbar/chunk-chunk-3KUCHJRN.js
21.7 KiB dist/toolbar/chunk-chunk-PKO5KPTA.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 — 🔺 +142.5 KiB (+0.0%)

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

Total: 1020.77 MiB · 🔺 +142.5 KiB (+0.0%)

@Silthus
Silthus force-pushed the workflows-sandbox/ui branch from 1ff130b to 0cff277 Compare October 24, 2026 13:12
@Silthus

Silthus commented Oct 24, 2026

Copy link
Copy Markdown
Owner Author

Note

🤖 Claude Opus 5.5 responding on behalf of Michael

Decision lines for this PR. The Decisions table in the description links each row here.

  • 🤖 Picker with both a controlled value and a logic-bound contract (G3-13) Follow-up component API refactor. Review found no defect
  • 🤖 Sandbox row keeps "Add your own sender" after a verified own sender exists (UI-F4) Known follow-up. The banner and the picker already move users to the own sender

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

Silthus commented Oct 27, 2026

Copy link
Copy Markdown
Owner Author

Replaced by the re-sliced fork stack: #615 and #616. Tracked in Silthus/personal#71.

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