Skip to content

feat(workflows): test templates with the sandbox sender - #451

Closed
Silthus wants to merge 1 commit into
workflows-sandbox/uifrom
workflows-sandbox/ui-test-sends
Closed

Silthus wants to merge 1 commit into
workflows-sandbox/uifrom
workflows-sandbox/ui-test-sends

Conversation

@Silthus

@Silthus Silthus commented Oct 12, 2026 •

Copy link
Copy Markdown
Owner

Problem

An author who just built an email template cannot see it in a real inbox until their domain is verified. This layer lets them send a test of the template to themselves or a teammate with the sandbox sender.

Supersedes PostHog#111695 (left open as the original draft), together with the preceding sender-picker layer.

Stack: layer 7 of 12, stacked on #450. Next: #452.

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

Warning

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

  • Template test sends deliver real emails; sent messages cannot be recalled.
  • The sandbox flag, verified-member restriction, and worker delivery guards limit the recipients and volume.

Changes

  • Editor test sends to a suppressed recipient, or from a suspended sender, report skipped with no metrics, for any sender. Live own-sender sends keep their billing and attribution outcomes.
  • With the flag on, Send to starts empty and opens the verified member suggestions, with the signed-in user first. With the flag off, the modal prefills the user as before.
  • The modal preserves API error details and keeps Connect email sender available beside errors.
  • Addresses outside visible member suggestions get advisory wording because restricted members may not see every eligible recipient.
  • Test sends prefer verified project senders and offer the sandbox sender when its flag is enabled.
  • The recipient picker refreshes visible verified teammates; the delivery worker authorizes typed addresses because visibility can hide eligible members.
  • Sandbox tests remove Reply-To; all template tests remove cc and bcc so only the visible recipient receives the message.
  • Skipped sends keep the modal open with the server reason; only successful sends close it.
  • With the sandbox sender selected, a signed-in user whose own address is not verified gets "Verify your email address to send test emails to yourself." when they enter that address.
  • Test invocations report skipped email steps without changing normal workflow continuation.
  • Both editor panes strip action prefixes and show explicit delivery skip reasons. Address-only rows cannot replace suppression, opt-out, pause or membership-check failures.
  • Failed sandbox setup offers a retry, and changing the recipient or sender clears the previous outcome.
  • Recipient and sender inputs stay disabled while a test delivery is pending.

Before:

flowchart LR
    Editor[Template editor] --> Own[Verified own sender] --> Worker[Delivery worker]
    classDef phBlue fill:#1d4aff,stroke:#1d4aff,color:#fff;
    class Editor,Own,Worker phBlue;
Loading

After:

flowchart LR
    Editor[Template editor] --> Picker[Own or sandbox sender]
    Picker --> Members[Visible member suggestions] --> Worker[Delivery worker authorization]
    classDef phBlue fill:#1d4aff,stroke:#1d4aff,color:#fff;
    classDef phYellow fill:#f9bd2b,stroke:#f9bd2b,color:#000;
    class Editor,Picker,Worker phBlue;
    class Members phYellow;
Loading

UI walk at 8ac8853cc82. Captures from a local preview of the stack tip 8ac8853cc82, flag workflows-sandbox-sender on, demo data. The test modal lives on the email template editor ("Send test"), with the sandbox sender chosen in From. A workflow email step has no test modal. Real SES is unreachable in this preview.

Send test on the email template editor: hint for your own unverified address

Send test on the email template editor, with your own address as the recipient and that address not verified: the modal shows "Verify your email address to send test emails to yourself." The members-only warning does not show.

Send test on the email template editor: refusal for your own unverified address

Sending anyway: the modal stays open with the worker's refusal, which names the unverified address and says "Verify your email address in PostHog, or ask these members to verify theirs."

Send test on the email template editor: no hint once your address is verified

After the address is verified: the same recipient gets no hint and no members-only warning. A send then passes the members-only check and stops at the SES call, which this preview cannot reach.

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: test modal without a sender

Before (flag off): without an own sender, the test modal has no sender and Send test email stays disabled.

After: test modal suggests verified members

After (flag on): Send to starts empty and suggests verified members. From is the sandbox sender.

After: verified member passes the members-only check

A verified member passes the members-only check. The send then stops at the SES call, which this preview cannot reach.

After: non-member gets the warning and the skip reason

A non-member gets the advisory warning before sending and the members-only reason after it. The modal stays open.

Before round 4: refusal for an unverified member's own address

At fa33d78fb36, before round 4: the refusal for a member's own unverified address asked for a verified domain. Round 4 replaces it with the verify-your-email hint and refusal shown above.

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

Template test send warning for an address outside the visible verified members

Send test from a template with the sandbox sender: an address outside the visible verified members gets the reworded warning before sending. Taken at 0146520f5eb in the combined live preview of both stacks.

Test result banner showing the sandbox member-check skip reason

A sandbox step test to an unverified member: the result banner now shows the member-check reason instead of a generic skip. Taken at 0146520f5eb in the combined live preview of both stacks.

Test result banner showing the per-recipient daily cap skip reason

A sandbox send over the daily cap for one address: the result banner shows the per-recipient cap reason. Taken at 0146520f5eb in the combined live preview of both stacks.

MailDev inbox with a sandbox test send relayed through the local SES fake

A template test send to a verified member arrived in MailDev through the local SES fake, from the sandbox sender. Taken at 51636b2b504 in the combined live preview of both stacks.

How did you test this code?

Current evidence, 2026-10-24, head fcd39c49e0f.

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: cases 2a to 2d produce the skip reasons the editor shows: members-only for 2a, 2b and 2d, and verify-your-email for 2c. The UI walk shows them in the modal and the step test.
  • UI walk: local preview of the stack tip 8ac8853cc82, flag workflows-sandbox-sender on, demo data. Round 4 walked only the surfaces it changed: the test modal for an unverified and a verified own address. The captures sit in Changes, above the fa33d78fb36 captures, which still hold for the rest of the modal.

Changes in this layer since the 2026-10-19 heads (restacked onto fork master 448d802: hog_function.ts keeps master's opt-out tracking and the test skip; the template test-send logic keeps master's custom From and template sender preference, and a sandbox test keeps a bare From):

  • Round 1 fixed: an own-sender editor test send during suspension reports skipped, not success (reports editor test sends as skipped while suspended).
  • Round 1 fixed: one shared skip-reason helper serves both result banners; Try again no longer spins during a test send; the member suggestions build from one list. The existing test-send suites cover them.
  • Round 2 fixed: an own-sender editor test send to a suppressed recipient reports skipped without recording metrics (reports editor test sends to a suppressed recipient as skipped without recording metrics, red then green).
  • Final read: GPT-6.1 Sol read the G3 layer range-diffs and found nothing.
  • Round 4 fixed (UI-F3): with the sandbox sender selected and the signed-in user's own unverified address as recipient, the test modal shows "Verify your email address to send test emails to yourself." (SendTestEmailModal.test.tsx, new it.each over verified and unverified, red then green).
  • 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: rendered modal with real Kea logic/useMocks, test-send actions, EmailService outcomes, and the result message shared by both editor panes.

Test rationale: Own-sender cases protect billing outcomes. Real modal cases prove details, recovery actions and advisory copy. Rendered results with real Kea logic prove that action prefixes and address-only rows cannot hide delivery skip reasons. Existing pane logic tests protect continuation.

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: 37ac25636a1. Stack tip fa33d78fb36.

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

  • 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.
  • 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 57f4673d9ec.

  • Drift: Master split the test event panel out of HogFlowEditorPanelTest.tsx into HogFlowEditorTestEvent.
  • Adapted: Import merge only. Both test panels still render getHogFlowTestResultMessage.
  • This layer: Jest for the template test-send modal and logic and the hog flow test panels passes at the tip (below).
  • 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 result and modal verification
Action-prefixed result banner: 6 failed before selector repair
getHogFlowTestResultMessage.test.ts: 10 passed after repair
Modal connect actions: 3 failed before repair; passed afterward
Modal advisory wording: 2 failed before repair; passed afterward
SendTestEmailModal.test.tsx: 5 passed
Frontend typecheck: No errors reported by tsgo.
Frontend build: exit 0
ci:preflight --strict --against workflows-sandbox/ui: failures 0
Pre-push ci:preflight --strict: failures 0, no new semgrep findings
Observed earlier skip-reason fix verification
Red: blocked/capped sandbox sends showed the generic skipped message
Red: final skipped email step showed the event-filter message
getHogFlowTestResultMessage.test.ts: 7 passed
hogFlowEditorNotificationTestLogic.test.ts: 8 passed
hogFlowEditorTestLogic.test.ts: 32 passed
Frontend typecheck: No errors reported by tsgo.
ci:preflight --strict: 121 changed files, failures 0
Observed targeted verification after QA fixes
SendTestEmailModal.test.tsx: 5 passed
messageTemplateTestSendLogic.test.ts: 20 passed
Affected suppression and suspension delivery cases: 12 passed, 189 skipped
Observed gates (Node and mypy results reused from the preceding QA pass)
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

The preview worker demonstrated MailDev delivery through an independent SES fake. This stack does not add that fake.

👉 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

The preceding layers document sandbox delivery configuration. No existing document covers this modal.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Decisions

Question Answer Who Where
Copy for a member whose own email address is not verified (UI-F3) Tell them to verify their email address, not a domain. Replaces the recommendation to keep the domain wording until the prod-shape read 👤 Human decision comment
Send button for a recipient outside the member list Warn and keep Send enabled; the server check decides and its skip message shows prominently. Alternative: disable Send, as the spec words it 🤖 Agent QA handoff
Prefill the signed-in user in the test modal with the flag on (G3-9, UI-F2) No. The empty field opens the member suggestions with the user first, and a test pins it 🤖 Agent decision comment
Own-sender editor test sends during suspension or to a suppressed recipient (G3-2, R2-5) Reported as skipped with no metrics, because test sends are not billed. Live own-sender sends keep their billing and attribution outcomes 🤖 Agent decision comment
Structured skip reason over the wire (G3-5) Follow-up. Both result banners share one helper that reads the worker's skip wording 🤖 Agent decision comment
Test modal hint for an unverified own address With the sandbox sender selected and your own unverified address as the recipient, the modal shows "Verify your email address to send test emails to yourself." 🤖 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, writing-kea-logics, adopting-generated-api-types, 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 template test-send interaction forms a separate layer from shared sender provisioning and selection. The existing invocation client stays because its generated equivalent lacks the synthetic request and result contract. 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 15:28
@Silthus Silthus self-assigned this Oct 12, 2026
@Silthus
Silthus force-pushed the workflows-sandbox/ui-test-sends branch from 51636b2 to ccd292f Compare October 12, 2026 23:14
@Silthus
Silthus force-pushed the workflows-sandbox/ui-test-sends branch 2 times, most recently from 0146520 to 57f4673 Compare October 19, 2026 10:58
@Silthus
Silthus force-pushed the workflows-sandbox/ui-test-sends branch from 57f4673 to 2d76177 Compare October 19, 2026 13:11
@Silthus
Silthus force-pushed the workflows-sandbox/ui-test-sends branch 2 times, most recently from 387cec0 to 37ac256 Compare October 23, 2026 16:26
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

🤖 CI report

⚠️ Complexity (TypeScript) — 2 functions above the limit (max 26)

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
EmailActionTestContent products/workflows/frontend/Workflows/hogflows/panel/testing/HogFlowEditorNotificationPanelTest.tsx:36 26 10
SendTestEmailModal products/workflows/frontend/TemplateLibrary/SendTestEmailModal.tsx:47 16 10
⚠️ Bundle size — 🔺 +6.0 KiB (+0.0%)

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

Total: 76.02 MiB · 🔺 +6.0 KiB (+0.0%)

File Size Δ vs base
posthog-app/_parent/products/workflows/frontend/TemplateLibrary/MessageTemplate.js 42.8 KiB 🔺 +3.6 KiB (+9.3%)

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

⚠️ Eager graph — 2 over budget

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

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
1.01 MiB · 24 files no change █████████░ 88.6% of 1.14 MiB
logged-out boot: index + App + bootApp (preloaded by every page, including /login)
src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
2.94 MiB · 620 files 🔺 +119 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 🟢 -537 B (-0.0%) ██████████ 102.1% of 7.50 MiB
dashboard scene
src/scenes/dashboard/Dashboard.tsx
9.68 MiB · 3,703 files 🟢 -1.1 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 🟢 -537 B (-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 🟢 -537 B (-0.0%) █████████░ 88.2% of 8.96 MiB
events scene
src/scenes/activity/explore/EventsScene.tsx
9.15 MiB · 3,495 files 🟢 -500 B (-0.0%) ██████████ 95.4% of 9.59 MiB
web analytics scene
src/scenes/web-analytics/WebAnalyticsScene.tsx
10.01 MiB · 3,713 files 🟢 -500 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 🟢 -432 B (-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 no change ████░░░░░░ 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 — 🔺 +47.5 KiB (+0.0%)

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

Total: 1020.81 MiB · 🔺 +47.5 KiB (+0.0%)

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

  • 👤 Copy for a member whose own email address is not verified (UI-F3) Tell them to verify their email address, not a domain. Replaces the recommendation to keep the domain wording until the prod-shape read.
  • 🤖 Prefill the signed-in user in the test modal with the flag on (G3-9, UI-F2) No. The empty field opens the member suggestions with the user first, and a test pins it
  • 🤖 Own-sender editor test sends during suspension or to a suppressed recipient (G3-2, R2-5) Reported as skipped with no metrics, because test sends are not billed. Live own-sender sends keep their billing and attribution outcomes
  • 🤖 Structured skip reason over the wire (G3-5) Follow-up. Both result banners share one helper that reads the worker's skip wording
  • 🤖 Test modal hint for an unverified own address With the sandbox sender selected and your own unverified address as the recipient, the modal shows "Verify your email address to send test emails to yourself."

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: #624. 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