Skip to content

feat(workflows): validate and provision sandbox email steps - #131840

Merged
trunk-io[bot] merged 31 commits into
masterfrom
workflows-sandbox/p2-validate-provision
Nov 11, 2026
Merged

trunk-io[bot] merged 31 commits into
masterfrom
workflows-sandbox/p2-validate-provision

Conversation

@Silthus

@Silthus Silthus commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Problem

  • Teams that can't verify a domain stall at the email sender step of a workflow.
  • The email owner asked for sandbox email to stay fully isolated from ordinary email.
  • #131749 stores the sandbox sender. Nothing validates a sandbox step or creates its sender yet.

Replaces #128948, #129061, #130911, and parts of #129058 (the poll) and #129059 (the admin).

Caution

🔴 Merge risk: high · one-way door at merge: the workflow_email_sandbox status and provision endpoints and the email_sandbox step type enter the public API schema and the workflows MCP tools

  • Every save of a workflow email step or a CDP function with an email input, in both regions, runs the new sender-kind rule through the shared input serializer. It refuses a newly picked sender that isn't an email row. A function save treats every stored sender as new. Ordinary senders and the custom-domain check answer as on the base: live run B, base vs head, and test_validation.py. No stored workflow step, single sender or rotation list, and no function points at a non-email row in either region: prod shape.
  • The integrations API's email/verify and PATCH email/ now answer 400 for any row that isn't an email row, in both regions, live on deploy. On the base the same request crashed with a 500 and wrote nothing, so no caller loses a working path. Email rows pass as before. Live run A6, base vs head.
  • Every live save, activation and test run checks for a sandbox step. Only workflows holding one change, and none exist yet. Live runs C and D.
  • Product activation no longer counts a team whose only active workflows use the sandbox sender. No team has one yet. Live run E, base vs head.
  • The workflows list's type filter now counts an email_sandbox step as messaging, so a workflow holding one lists under Messaging. The list's tag still calls it Automation until U1 (#131916) adds the step to the frontend messaging types. Only flagged teams can save such a step, and no stored workflow in either region holds one: prod shape. Uncovered: a mismatched tag, cosmetic.
  • status answers whatever the flag says and writes nothing.
  • provision writes one email_sandbox row per team. The flag refuses it when off. Two requests that race keep the first creator: a concurrent live run, base vs head.
  • Staff block rows stay until staff delete them in admin. Live run F.
  • The tenant poll writes one mirror row a minute while SES_SANDBOX_TENANT_NAME is set. Unset, it sends and writes nothing. Nothing reads the row yet. Live run G.
  • Uncovered: the tenant poll against real SES. A local SESv2 stub answered instead, so a wrong response shape could store a wrong mirror status.
  • Uncovered: old and new pods side by side. No stored format changes.
  • Uncovered: database load. A save that picks a new sender reads its row once more, and the activation check filters active workflows on their step types. A slow read slows those saves and intent registration.
  • Switch-off: workflows-sandbox-sender gates new sandbox steps and provisioning. WORKFLOWS_SANDBOX_EMAIL_RUNTIME_ENABLED (off by default) keeps them from going live or running as tests. An unset SES_SANDBOX_TENANT_NAME keeps the poll off. The sender-kind rule, the runtime check and the activation query have none: live on deploy, fix forward.

Rollout

  • Stages: P1's migrations deploy in both regions first. This PR's activation exclusion deploys before the flag turns on. After N1 (#131766), the sandbox worker, deploys in both regions, a charts change sets WORKFLOWS_SANDBOX_EMAIL_RUNTIME_ENABLED and SES_SANDBOX_TENANT_NAME. The flag turns on after that.
  • Watch: 400 responses on workflow saves. No alert pages on them.

Changes

  • Workflows accept an email_sandbox step behind the adoption flag. Stored steps stay editable without it.
  • A workflow with a sandbox step saves only as a draft until WORKFLOWS_SANDBOX_EMAIL_RUNTIME_ENABLED is on. Activating it, publishing it, and test-running it return a 400, because the worker can't run the step yet.
  • The step takes only recipient, subject, content, and category. The server compiles it and ignores client bytecode.
  • Templates and other products' workflows refuse the step.
  • An ordinary email step can't newly pick a non-email sender row.
  • New workflow_email_sandbox status and provision endpoints create the team's sender row without an SES call.
  • A minute task mirrors the sandbox SES tenant status on PostHog Cloud and in development, not on hobby installs.
  • Staff block organizations in Django admin, which records the signed-in staff member as the creator.
  • Sandbox-only workflows no longer count as activation.
  • Mechanical: docs and regenerated types.

Sandbox sender isolation, P2 highlighted

This PR builds the parts outlined in blue: the email step's sender validation, the email_sandbox step type, and the provisioned sender row.

How did you test this code?

Test rationale: each new test file covers one contract that no older test checks.

  • Not checked: the tenant poll against real SES (a local SESv2 stub stood in), and old and new pods side by side (no stored format changes; N1 proves the Node side).
Evidence, tests and gaps
  • Live run at 1c7edf5, four servers (head with the flag on and off, head with the runtime switch on, and the P1 base) and a local SESv2 stub:
    • A: status with the flag off and on; provisioning 403s with the flag off, and twice with it on makes one row and sends SES nothing. The generic email endpoints refuse the row.
    • B: base and head accept the verified sender and refuse a custom address on another domain. Only head refuses the sandbox row as an email step's sender.
    • C: flag off refuses a new sandbox step. Forged client bytecode is recompiled, and the stored subject renders "Hi Ada" on the HogVM. A reply-to, a template and another product's workflow are refused. Flag off, the stored step stays editable.
    • D: switch off refuses activation and a test run; switch on activates the same draft.
    • E: with only the sandbox workflow active, base counts the team as activated and head does not. With an ordinary workflow active too, both do.
    • F: a staff admin POST with a forged creator stores the signed-in staff member. The block shows in status and refuses provisioning, and deleting it clears status.
    • G: the poll with no tenant name sends nothing and writes no row; it stores UNKNOWN, the disabled aggregate, and ENABLED; a refused read keeps the stored row.
  • Production shape, US and EU: no stored sender, single or rotation, in a workflow or a hog function, has another kind. No workflow holds a sandbox step yet, and no sandbox sender row exists.
  • Live run at the merge head: two provisions per team released together against real Postgres. The previous code overwrote the first creator in most rounds; this head never does, and every round stores one row.
  • CI report: comment density is 1%; the seven hog_flow.py lines are existing comments re-indented when the inputs context moved into a variable.
  • CI report: the two MCP snapshot updates are the workflows and broadcasts create schemas gaining the new step.
  • CI report: backend coverage is 95%. test_workflow_email_sandbox.py and test_product_intent.py run the integration.py and facade/api.py lines it lists as uncovered. The other gaps are guards for malformed input.
  • CI report: bundle size is unchanged.
  • CI report: the Hogbox preview is optional and never blocks merging; this PR changes no preview setup.
  • test_sandbox_email_validation.py (no database): the allowlists, IDs, null and wrongly typed text, and completeness. Hog steps run their compiled templates on the HogVM and assert the rendered email, the warehouse record alias included. A step sent with another step's bytecode still renders its own template.
  • test_sandbox_email_steps.py: a hog step saved with forged bytecode renders its own template on the HogVM, the flag, grandfathering, product refusal on save and on a test run's override, and the sender-kind rule on publish and on a web draft save. The draft-only and web-draft cases are mutation-checked.
  • test_sandbox_email_steps.py, runtime switch: create, activate and publish refused, both test-run forms refused, draft saves. Removing each of the three guards turns its own cases red.
  • products/cdp/backend/test/test_validation.py: the sender-kind rule, and malformed draft inputs reaching the serializer instead of a 500.
  • test_workflow_email_sandbox.py: provisioning makes one row and SES receives no request, through a recording SESv2 fake at boto3's HTTP layer. It also covers access control, and that a staff POST to the admin cannot forge or omit a block's creator. A request that loses the race between its lookup and its insert returns the winner's row and keeps the winner as creator; the previous code turns it red.
  • test_sandbox_sender_tenant_state.py: every tenant state, failed reads, stale writes, and an unset tenant name, all through the same SESv2 fake, asserting the stored row and the requests SES received.
  • Mutation checks: keeping a client's bytecode turns both forged-bytecode cases red. Ignoring the warehouse alias, compiling the design, and a wrong subject turn the render cases red. Removing the unset-tenant guard, the stale-write guard or the aggregate, or adding an SES read to provisioning, turns its own test red.
  • A full workflows run on a devbox showed order-dependent failures in test_ses_provider.py and test_message_assets.py. Both files pass alone and are untouched here.

Release status

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

Docs update

  • The internal sending-tier and auto-pause docs cover the sandbox sender.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Agent: Claude Code, claude-opus-5-5

Skills: /improving-drf-endpoints, /writing-tests, /writing-pr-descriptions, /merge-risk, /pr-description, /qa-swarm (design pass)

Question Answer Who Where
Compiled keys a client resends Dropped and recompiled, not refused 🤖 Agent decisions
Completeness on draft saves Strict saves only 🤖 Agent decisions
Sender-kind grandfathering Live ids only, unlike the spec's "every stored variant" 🤖 Agent decisions
Restored revision with a sandbox step Grandfathered; the block and pause apply at send 🤖 Agent decisions
Test-run ownership The saved owner decides 🤖 Agent decisions
Null text in a sandbox step Refused 🤖 Agent decisions
Tenant poll schedule Minute crontab with expiry 🤖 Agent decisions
Sandbox steps before the worker ships Refused in code by a runtime setting, not left to the flag 👤 Human review thread

Involvement: one design pass, a GPT-6.1 Sol full review and eight final reads, PostHog Review on each push, live runs, an admin UI walk, and a US and EU production-shape check.

More context for AI agents
  • Session: P2 of the isolated sandbox sender plan. Its base, feat(workflows): store the sandbox sender kind and state #131749, is in master.
  • Decisions: in the table above and the linked comment.
  • Follow-ups: U1 (#131916) adds email_sandbox to the frontend kind list, icon map, and messaging types, which aligns the list tag with the backend type filter.
  • Head: b9fe013 merges master after N1 landed; only imports in facade/api.py conflicted. bd7bad9 keeps the first creator when provisioning races. 51cd006 reverts an unrelated warehouse test stub, which moves to its own PR. 953fe2b renders a forged-bytecode step instead of checking the stored envelope. 9cab2cd merges master after feat(workflows): store the sandbox sender kind and state #131749 landed, with no conflicts. 1c7edf5 moves the sandbox tests outside in: rendered templates and a recording SESv2 fake. 0e84119 added the runtime setting, which every live write and the test-run endpoint check.
  • Read closely: products/cdp/backend/validation.py sender-kind rule; facade/api.py activation query; services/hog_flow_writes.py runtime check.

🤖 Generated with Claude Code

Silthus and others added 4 commits November 9, 2026 11:37
Add the email_sandbox integration kind with credentials as its only auth
type, and the two tables the sandbox sender reads: a mirror of the shared
SES tenant's sending status and a staff-set organization block. Nothing
reads them yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds the email_sandbox step type and its server-side contract: an
allowlisted config that cannot name a sender or delivery settings, an
adoption flag for new steps with stored steps grandfathered, and a
refusal in broadcasts, other products' workflows and templates.

Adds the project's sandbox sender endpoints (status and provision), the
minute poll that mirrors the sandbox SES tenant status, a staff admin
for organization blocks, and the activation exclusion for workflows
whose only messages go through the sandbox. Ordinary email steps can no
longer newly pick a sender row that is not an email sender.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Keep the sandbox action type in the hog flow model module so the
  workflows facade stays off the Django startup import path.
- Grandfather the wrong-kind sender rule only from the live workflow; a
  lenient web draft never raised it, so its draft value must not pass
  publish unchecked.
- Compile sandbox Hog templates with the warehouse record alias when a
  warehouse table triggers the workflow.
- Serialize the sender status DTO directly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Silthus Silthus added the run-ci-backend Force ci-backend's full test matrices to run even on a draft PR label Nov 9, 2026
@Silthus Silthus self-assigned this Nov 9, 2026
@Silthus Silthus added the hogbox-preview Spin up a per-PR PostHog preview on a hogland hogbox label Nov 9, 2026
@Silthus
Silthus added this pull request to stack #131841 November 9, 2026 13:41
@github-actions

github-actions Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

🤖 CI report

⚠️ Trunk lane — backend Python lane

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

✅ Complexity (TypeScript) — clean

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

✅ Duplication (Python) — clean

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

✅ Duplication (TypeScript) — clean

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

✅ Comment density — 1% of added code lines are comments (8 of 1352)

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

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

Files with the most added comment lines:

File Comment lines Added lines
products/workflows/backend/presentation/views/hog_flow.py 8 76

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

✅ Bundle size — no change

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

Total: 78.37 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.04 MiB · 25 files no change █████████░ 91.2% of 1.14 MiB
logged-out boot: index + App + bootApp (preloaded by every page, including /login)
src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
3.03 MiB · 627 files no change ██████████ 100.0% of 3.03 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
3.86 MiB · 1,235 files no change ██████████ 96.4% of 4.01 MiB
dashboard scene
src/scenes/dashboard/Dashboard.tsx
7.77 MiB · 2,997 files no change ████████░░ 79.0% of 9.84 MiB
today home path
src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
4.84 MiB · 1,859 files no change ██████████ 98.2% of 4.93 MiB
AI-first home path
src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
6.06 MiB · 2,290 files no change ██████████ 96.7% of 6.27 MiB
events scene
src/scenes/activity/explore/EventsScene.tsx
6.79 MiB · 2,557 files no change ███████░░░ 72.1% of 9.41 MiB
web analytics scene
src/scenes/web-analytics/WebAnalyticsScene.tsx
7.85 MiB · 2,937 files no change ███████░░░ 74.8% of 10.49 MiB
replay detail scene
src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
6.97 MiB · 2,613 files no change █████████░ 85.7% of 8.13 MiB

🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 products/product_analytics/frontend/insights/activityDescriptions stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/layout/navigation-3000/navigationLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/dashboard/dashboardLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/lemon-ui/LemonMarkdown/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/RichContentEditor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/CodeSnippet/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/taxonomy/core-filter-definitions-by-group.json stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/zod/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/logic/billing/billingLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/ProductEmptyState/productSetupPreloadLogic.ts stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/kea-test-utils/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/product_analytics/frontend/insights/activityDescriptions stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/kea-test-utils/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/dashboards/frontend/widgets/previews/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/panel-layout/ProjectTree/menus/SessionReplayMenuItems.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/taxonomy/core-filter-definitions-by-group.json stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/navigation/projectNoticeLogic.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/navigation/ImpersonationNotice/ImpersonationNotice.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/cohorts/cohortSavedToast.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/wizard/frontend/runs/WizardRunDetailsDrawer.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/TodayShell.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/TodayContextPanelFloating.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/TodaySidePanel.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/TodayTopBar.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/todayRailItems.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/subscriptions/frontend/components/Subscriptions/exportNudge/exportNudgeLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/billing/paymentEntryLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/conversations/frontend/components/SidePanel/sidepanelTicketsLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/zod/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/authentication/auth/loginLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/authentication/shared/passkeyLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/cohorts/CohortFilters/constants.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/persons/frontend/logics/personsLogic.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/surveys/constants.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@tiptap/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/prosemirror- stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/re2js/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/react-markdown/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/Command/Command.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 packages/quill/packages/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 zod/v4/locales/de.js stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 packages/quill/packages/quill/dist/ stays out of src/scenes/dashboard/Dashboard.tsx
🟢 node_modules/@tiptap/ stays out of src/scenes/dashboard/Dashboard.tsx
🟢 node_modules/prosemirror- stays out of src/scenes/dashboard/Dashboard.tsx
🟢 node_modules/marked/ stays out of src/scenes/dashboard/Dashboard.tsx
🟢 node_modules/frimousse/ stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/queries/validators.js stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/queries/web-analytics-validators.js stays out of src/scenes/dashboard/Dashboard.tsx
🟢 node_modules/re2js/ stays out of src/scenes/dashboard/Dashboard.tsx
🟢 node_modules/kea-test-utils/ stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/debug/HogDebug.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/queries/QueryEditor/QueryEditor.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 packages/quill/packages/quill/dist/ stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/scenes/project-homepage/today/TodayReportPage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/queries/Query/Query.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 packages/quill/packages/charts/ stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 packages/quill/packages/quill/dist/ stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 products/posthog_ai/frontend/components/tool/widgets/VisualizationWidget.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 products/posthog_ai/frontend/components/tool/widgets/RecordingsWidget.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 src/queries/Query/Query.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 products/posthog_ai/frontend/api/primitives.ts stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 src/scenes/notebooks/Notebook/notebookLogic.ts stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 packages/quill/packages/quill/dist/ stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 node_modules/@tiptap/ stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 node_modules/prosemirror- stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 node_modules/marked/ stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 node_modules/frimousse/ stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/queries/validators.js stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/queries/web-analytics-validators.js stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 node_modules/re2js/ stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 node_modules/kea-test-utils/ stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/queries/nodes/InsightViz/InsightViz.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/queries/nodes/DataVisualization/DataVisualization.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/queries/nodes/WebOverview/WebOverview.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/scenes/debug/HogDebug.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/queries/QueryEditor/QueryEditor.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 packages/quill/packages/quill/dist/ stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 node_modules/@tiptap/ stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 node_modules/prosemirror- stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 node_modules/marked/ stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 node_modules/frimousse/ stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 src/scenes/web-analytics/LiveMetricsDashboard/LiveWebAnalyticsMetrics.tsx stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 products/product_analytics/frontend/insights/trends/WorldMap/countryVectors.tsx stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 src/queries/validators.js stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 node_modules/re2js/ stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 node_modules/kea-test-utils/ stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 src/scenes/debug/HogDebug.tsx stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 src/queries/QueryEditor/QueryEditor.tsx stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 packages/quill/packages/quill/dist/ stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
🟢 node_modules/@tiptap/ stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
🟢 node_modules/prosemirror- stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
🟢 node_modules/marked/ stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
🟢 src/scenes/settings/SettingsMap.tsx stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
🟢 @jspm/core/nodelibs/browser/fs stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
🟢 node_modules/re2js/ stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
🟢 node_modules/kea-test-utils/ stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
🟢 node_modules/d3/src/ stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx

Largest files eagerly shipped from src/index.tsx
Size File
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
24.6 KiB ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
6.3 KiB ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js
4.7 KiB ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js
4.0 KiB ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js
1.9 KiB src/lib/utils/bootTiming.ts
1.7 KiB src/index.tsx
1.7 KiB ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
1.2 KiB src/RootErrorBoundary.tsx
1.1 KiB src/lib/utils/deferredAppContext.ts
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
Size File
362.7 KiB ../node_modules/.pnpm/posthog-js@1.447.12_@types+react@18.3.31_react@18.3.1/node_modules/posthog-js/dist/module.mjs
224.1 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.4 KiB src/products.tsx
87.7 KiB src/lib/api.ts
69.0 KiB src/lib/lemon-ui/icons/icons.tsx
42.7 KiB ../node_modules/.pnpm/@dnd-kit+core@6.3.0_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
29.0 KiB src/lib/constants.tsx
24.6 KiB ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
362.7 KiB ../node_modules/.pnpm/posthog-js@1.447.12_@types+react@18.3.31_react@18.3.1/node_modules/posthog-js/dist/module.mjs
224.1 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.4 KiB src/products.tsx
87.7 KiB src/lib/api.ts
69.0 KiB src/lib/lemon-ui/icons/icons.tsx
42.7 KiB ../node_modules/.pnpm/@dnd-kit+core@6.3.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@dnd-kit/core/dist/core.esm.js
36.4 KiB ../node_modules/.pnpm/kea@4.0.0-pre.6_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js
32.6 KiB src/lib/utils/eventUsageLogic.ts
29.0 KiB src/lib/constants.tsx
Largest files eagerly shipped from src/scenes/dashboard/Dashboard.tsx
Size File
362.7 KiB ../node_modules/.pnpm/posthog-js@1.447.12_@types+react@18.3.31_react@18.3.1/node_modules/posthog-js/dist/module.mjs
224.1 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
116.4 KiB ../node_modules/.pnpm/posthog-js@1.447.12_@types+react@18.3.31_react@18.3.1/node_modules/posthog-js/dist/surveys-preview.js
112.5 KiB ../packages/quill/packages/primitives/dist/index.js
106.4 KiB src/products.tsx
98.2 KiB ../packages/quill/packages/components/dist/index.js
87.7 KiB src/lib/api.ts
78.8 KiB src/scenes/dashboard/dashboardLogic.tsx
69.0 KiB src/lib/lemon-ui/icons/icons.tsx
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
Size File
362.7 KiB ../node_modules/.pnpm/posthog-js@1.447.12_@types+react@18.3.31_react@18.3.1/node_modules/posthog-js/dist/module.mjs
224.1 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
112.5 KiB ../packages/quill/packages/primitives/dist/index.js
106.4 KiB src/products.tsx
87.7 KiB src/lib/api.ts
69.0 KiB src/lib/lemon-ui/icons/icons.tsx
68.5 KiB ../products/posthog_ai/frontend/logics/runStreamLogic.ts
42.7 KiB ../node_modules/.pnpm/@dnd-kit+core@6.3.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@dnd-kit/core/dist/core.esm.js
36.4 KiB ../node_modules/.pnpm/kea@4.0.0-pre.6_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
Size File
362.7 KiB ../node_modules/.pnpm/posthog-js@1.447.12_@types+react@18.3.31_react@18.3.1/node_modules/posthog-js/dist/module.mjs
224.1 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
112.5 KiB ../packages/quill/packages/primitives/dist/index.js
106.4 KiB src/products.tsx
87.7 KiB src/lib/api.ts
69.0 KiB src/lib/lemon-ui/icons/icons.tsx
68.5 KiB ../products/posthog_ai/frontend/logics/runStreamLogic.ts
42.7 KiB ../node_modules/.pnpm/@dnd-kit+core@6.3.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@dnd-kit/core/dist/core.esm.js
41.7 KiB src/scenes/hog-functions/sub-templates/sub-templates.ts
Largest files eagerly shipped from src/scenes/activity/explore/EventsScene.tsx
Size File
362.7 KiB ../node_modules/.pnpm/posthog-js@1.447.12_@types+react@18.3.31_react@18.3.1/node_modules/posthog-js/dist/module.mjs
224.1 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
112.5 KiB ../packages/quill/packages/primitives/dist/index.js
106.4 KiB src/products.tsx
98.2 KiB ../packages/quill/packages/components/dist/index.js
87.7 KiB src/lib/api.ts
78.8 KiB src/scenes/dashboard/dashboardLogic.tsx
69.0 KiB src/lib/lemon-ui/icons/icons.tsx
68.5 KiB ../products/posthog_ai/frontend/logics/runStreamLogic.ts
Largest files eagerly shipped from src/scenes/web-analytics/WebAnalyticsScene.tsx
Size File
362.7 KiB ../node_modules/.pnpm/posthog-js@1.447.12_@types+react@18.3.31_react@18.3.1/node_modules/posthog-js/dist/module.mjs
224.1 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
116.4 KiB ../node_modules/.pnpm/posthog-js@1.447.12_@types+react@18.3.31_react@18.3.1/node_modules/posthog-js/dist/surveys-preview.js
112.5 KiB ../packages/quill/packages/primitives/dist/index.js
106.4 KiB src/products.tsx
98.2 KiB ../packages/quill/packages/components/dist/index.js
87.7 KiB src/lib/api.ts
78.8 KiB src/scenes/dashboard/dashboardLogic.tsx
69.0 KiB src/lib/lemon-ui/icons/icons.tsx
Largest files eagerly shipped from src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
Size File
362.7 KiB ../node_modules/.pnpm/posthog-js@1.447.12_@types+react@18.3.31_react@18.3.1/node_modules/posthog-js/dist/module.mjs
329.1 KiB ../node_modules/.pnpm/posthog-js@1.447.12_@types+react@18.3.31_react@18.3.1/node_modules/posthog-js/dist/rrweb.js
224.1 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
112.5 KiB ../packages/quill/packages/primitives/dist/index.js
106.4 KiB src/products.tsx
98.2 KiB ../packages/quill/packages/components/dist/index.js
87.7 KiB src/lib/api.ts
69.0 KiB src/lib/lemon-ui/icons/icons.tsx
68.5 KiB ../products/posthog_ai/frontend/logics/runStreamLogic.ts

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

✅ Frontend layers — no new upward imports, 0 removed

Static imports from a lower frontend layer into a higher one (layers, lowest first: kernel, shell, query, features). Compared with the base branch, so an import that a PR also adds to layer-baseline.txt still shows here. Sizes are the source bytes on the eager path of every logged-in page.

Layer Eager source bytes Δ vs base
kernel 3.00 MiB no change
shell 2.41 MiB no change
query 56.9 KiB no change
features 274.1 KiB no change

Download the interactive layer graph for this PR.

Posted automatically by check-layers

✅ Toolbar bundle — eager 2.44 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.44 MiB · 19 files no change ████░░░░░░ 42.7% of 5.72 MiB
Deferred (lazy) 2.64 MiB · 44 files no change n/a — loads on demand
Loader dist/toolbar.js 1.2 KiB no change █░░░░░░░░░ 6.0% of 19.5 KiB
Largest eagerly-shipped chunks
Size File
980.1 KiB dist/toolbar/toolbar-app-N27Y2VQV.css
738.4 KiB dist/toolbar/chunk-chunk-IWQ5X2PG.js
290.5 KiB dist/toolbar/chunk-chunk-QRQPQULA.js
137.6 KiB dist/toolbar/chunk-chunk-JCO5XNTJ.js
131.8 KiB dist/toolbar/chunk-chunk-FQJO25M6.js
75.7 KiB dist/toolbar/toolbar-app-OEB77UQQ.js
69.0 KiB dist/toolbar/chunk-chunk-BFAXZMC5.js
33.8 KiB dist/toolbar/chunk-chunk-RDAX246Y.js
21.9 KiB dist/toolbar/chunk-chunk-63VUMQXG.js
6.8 KiB dist/toolbar/chunk-chunk-RVLGRPCH.js

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

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

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

Total: 1005.97 MiB · 🔺 +9.2 KiB (+0.0%)

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

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

App JS CSS
debug 1067.1 KB 224.2 KB
explain-chart 1349.4 KB 224.2 KB
action 994.3 KB 224.2 KB
action-list 1058.0 KB 224.2 KB
cohort 993.3 KB 224.2 KB
cohort-list 1057.0 KB 224.2 KB
email-template 993.1 KB 224.2 KB
error-details 998.2 KB 224.2 KB
error-issue 995.2 KB 224.2 KB
error-issue-list 1059.2 KB 224.2 KB
experiment 1055.2 KB 224.2 KB
experiment-list 1058.8 KB 224.2 KB
experiment-results 1057.2 KB 224.2 KB
feature-flag 1057.4 KB 224.2 KB
feature-flag-list 1061.2 KB 224.2 KB
feature-flag-testing 996.5 KB 224.2 KB
inline-scan 993.8 KB 224.2 KB
insight-actors 1056.5 KB 224.2 KB
llm-costs 1053.8 KB 224.2 KB
session-recording 995.5 KB 224.2 KB
survey 994.9 KB 224.2 KB
survey-global-stats 1053.8 KB 224.2 KB
survey-list 1058.7 KB 224.2 KB
survey-stats 1053.8 KB 224.2 KB
trace-span 993.7 KB 224.2 KB
trace-span-list 1057.9 KB 224.2 KB
vision-observation-list 1057.1 KB 224.2 KB
workflow 993.6 KB 224.2 KB
workflow-list 1057.4 KB 224.2 KB
workflow-setup-status 993.3 KB 224.2 KB
query-results 1318.8 KB 224.2 KB
render-ui 1425.3 KB 224.2 KB
visual-review-snapshots 998.2 KB 224.2 KB
workflow-data-suggestions 996.0 KB 224.2 KB
workflow-sending-rules 994.6 KB 224.2 KB
ℹ️ MCP agent API — agent-facing tool changes

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

Tools changed (2):

Tool Params Scopes Annotations Schema chars
broadcasts-create 20,088 -> 20,584
workflows-create 20,088 -> 20,584
✅ Playwright — all passed

All tests passed.

View test results →

⚠️ Backend coverage — 95.0% of changed backend lines covered — 37 uncovered

🧪 Backend test coverage

Patch coverage — changed backend lines (products + core): ███████████████████░ 95.0% (772 / 809)

File Patch Uncovered changed lines
posthog/api/integration.py 0.0% 2979–2980, 3048–3049
products/workflows/backend/facade/api.py 25.0% 319–324
products/workflows/backend/facade/contracts.py 28.6% 1225–1229
products/workflows/backend/admin/sandbox_sender_organization_block_admin.py 89.7% 21, 25, 36
products/workflows/backend/tests/fakes/ses_endpoint.py 89.9% 20–23, 28–29, 84
products/cdp/backend/validation.py 91.7% 121–122, 168, 173
products/workflows/backend/providers/ses.py 94.4% 483
products/workflows/backend/services/sandbox_email/validation.py 94.8% 54, 62, 73, 77, 110
products/workflows/backend/services/sandbox_email/provisioning.py 96.7% 41
products/workflows/backend/presentation/views/workflow_email_sandbox.py 97.7% 77

🤖 Agents: add a test only if an uncovered line exposes a realistic regression that existing tests miss. Otherwise explain why no new test is needed under "How did you test this code?". Gap list: the patch-coverage artifact on this run (gh run download 111502720069175 -n patch-coverage), or the coverage-data block at the end of this comment.

Per-product line coverage (touched products)
Product Coverage Lines
platform_features ██░░░░░░░░░░░░░░░░░░ 12.1% 7 / 58
data_tools ████████████░░░░░░░░ 57.6% 95 / 165
warehouse_sources_queue █████████████░░░░░░░ 65.9% 2,194 / 3,330
demo ██████████████░░░░░░ 71.3% 1,978 / 2,775
knowledge_writer ████████████████░░░░ 77.7% 1,409 / 1,813
batch_exports █████████████████░░░ 82.9% 24,777 / 29,885
engineering_analytics █████████████████░░░ 85.6% 12,515 / 14,615
ml_inference █████████████████░░░ 86.3% 524 / 607
commands █████████████████░░░ 86.5% 1,645 / 1,901
mcp_analytics ██████████████████░░ 87.8% 6,827 / 7,777
posthog_ai ██████████████████░░ 88.2% 4,514 / 5,116
apm ██████████████████░░ 88.2% 1,973 / 2,236
notebooks ██████████████████░░ 88.6% 15,594 / 17,599
dashboards ██████████████████░░ 89.5% 8,969 / 10,017
cohorts ██████████████████░░ 90.7% 9,866 / 10,883
warehouse_suggestions ██████████████████░░ 90.7% 4,264 / 4,700
actions ██████████████████░░ 90.9% 1,524 / 1,677
visual_review ██████████████████░░ 90.9% 10,634 / 11,698
data_warehouse ██████████████████░░ 91.1% 15,984 / 17,546
signals ██████████████████░░ 91.2% 112,460 / 123,292
cdp ██████████████████░░ 91.4% 10,896 / 11,920
today ██████████████████░░ 91.5% 4,136 / 4,521
canvas ██████████████████░░ 91.5% 8,559 / 9,351
tasks ██████████████████░░ 91.6% 86,248 / 94,204
autoresearch ██████████████████░░ 91.6% 12,203 / 13,318
data_modeling ██████████████████░░ 91.7% 12,686 / 13,831
streamlit_apps ██████████████████░░ 92.0% 3,200 / 3,478
alerts_platform ██████████████████░░ 92.0% 10,654 / 11,576
release ██████████████████░░ 92.1% 453 / 492
metrics ██████████████████░░ 92.1% 12,068 / 13,102
business_knowledge ██████████████████░░ 92.2% 10,900 / 11,824
review_hog ██████████████████░░ 92.3% 18,397 / 19,930
conversations ██████████████████░░ 92.4% 31,728 / 34,335
wizard ██████████████████░░ 92.4% 7,323 / 7,922
error_tracking ███████████████████░ 92.5% 20,976 / 22,675
slack_app ███████████████████░ 92.6% 25,544 / 27,600
early_access_features ███████████████████░ 92.7% 1,377 / 1,486
notifications ███████████████████░ 92.8% 1,230 / 1,326
legal_documents ███████████████████░ 93.0% 2,380 / 2,558
ai_training ███████████████████░ 93.1% 605 / 650
managed_warehouse ███████████████████░ 93.2% 15,792 / 16,947
alerts ███████████████████░ 93.2% 13,752 / 14,756
authorization ███████████████████░ 93.2% 850 / 912
webmcp ███████████████████░ 93.3% 277 / 297
stamphog ███████████████████░ 93.3% 11,443 / 12,265
web_analytics ███████████████████░ 93.5% 27,059 / 28,934
surveys ███████████████████░ 93.6% 7,847 / 8,388
exports ███████████████████░ 93.7% 4,474 / 4,777
approvals ███████████████████░ 93.7% 5,020 / 5,359
cross_project_dashboards ███████████████████░ 93.7% 867 / 925
context_layer ███████████████████░ 93.9% 4,075 / 4,342
messaging ███████████████████░ 93.9% 9,228 / 9,827
marketing_analytics ███████████████████░ 93.9% 22,429 / 23,882
mcp_store ███████████████████░ 94.0% 9,391 / 9,991
ai_gateway ███████████████████░ 94.0% 1,472 / 1,566
billing_alerts ███████████████████░ 94.0% 2,332 / 2,480
workflows ███████████████████░ 94.1% 30,351 / 32,270
annotations ███████████████████░ 94.1% 1,460 / 1,552
managed_migrations ███████████████████░ 94.2% 1,886 / 2,003
customer_analytics ███████████████████░ 94.2% 31,699 / 33,658
ai_observability ███████████████████░ 94.3% 38,322 / 40,635
access_control ███████████████████░ 94.3% 8,606 / 9,122
replay_vision ███████████████████░ 94.4% 33,432 / 35,422
logs ███████████████████░ 94.4% 20,533 / 21,748
reminders ███████████████████░ 94.5% 837 / 886
experiments ███████████████████░ 95.0% 47,368 / 49,882
warehouse_sources ███████████████████░ 95.0% 142,320 / 149,854
growth ███████████████████░ 95.0% 11,602 / 12,216
endpoints ███████████████████░ 95.0% 9,725 / 10,239
product_tours ███████████████████░ 95.1% 1,271 / 1,337
skills ███████████████████░ 95.1% 7,658 / 8,053
uptime_monitoring ███████████████████░ 95.3% 306 / 321
data_catalog ███████████████████░ 95.7% 4,750 / 4,965
tracing ███████████████████░ 95.7% 5,087 / 5,315
ui_customization ███████████████████░ 95.7% 714 / 746
subscriptions ███████████████████░ 95.7% 14,176 / 14,811
product_analytics ███████████████████░ 95.8% 31,450 / 32,836
feature_flags ███████████████████░ 95.8% 30,752 / 32,104
revenue_analytics ███████████████████░ 95.9% 1,877 / 1,958
aeo ███████████████████░ 95.9% 4,131 / 4,308
data_quality ███████████████████░ 96.0% 8,583 / 8,937
groups ███████████████████░ 96.4% 2,068 / 2,145
account_audits ███████████████████░ 96.5% 823 / 853
security ███████████████████░ 96.8% 1,597 / 1,650
pulse ███████████████████░ 97.0% 2,026 / 2,089
analytics_platform ████████████████████ 98.0% 2,962 / 3,022
field_notes ████████████████████ 98.4% 181 / 184

Report-only. Patch coverage = changed backend lines covered vs origin/master. Sorted lowest first.
Known gaps: lines covered only by Temporal tests show as uncovered; core line numbers may drift if master changed the same file.

⚠️ MCP snapshots — 2 updated (2 modified, 0 added, 0 deleted)

Snapshots: MCP unit test snapshots updated

Changes: 2 snapshots (2 modified, 0 added, 0 deleted)

What this means:

  • Snapshots have been automatically updated to match current output

Next steps:

  • Review the changes to ensure they're intentional
  • If unexpected, investigate what caused the output to change

Review snapshot changes →

✅ Django migration risk — no migrations to analyze

No Django migrations need risk analysis.

❌ Hogbox preview — build failed

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

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

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

PostHog Review · Standard · reviewed 4a3ebff: 2 Should fix.

See this review and its findings in PostHog Review. Comment @posthog review --deep for a Deep review. A Deep review takes longer and looks deeper than a Standard review.

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

PostHog Review · Standard · reviewed 67d14af: no findings.

@github-actions

github-actions Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit b9fe013.

Silthus and others added 2 commits November 9, 2026 14:01
A web draft save tolerates unfinished steps, so the sender-kind rule never
ran there. The sender then reached the workflow's live actions and was
grandfathered when it published. A wrong-kind sender is never an unfinished
edit, so every save now refuses it.

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

A sandbox email step now refuses a null recipient name, subject, preheader,
text or HTML, which the sender's renderer cannot take. A test run's
configuration override on a workflow another product owns is now refused
like a save, because the ownership check reads the flow from the context.

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

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Silthus and others added 2 commits November 9, 2026 14:15
A test run's configuration override could claim no owning product and
send a sandbox step for a workflow another product owns. The owner is
set on create and never changes, so the saved value now decides.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A web draft save with malformed function inputs crashed the sender-kind
check before the serializer could tolerate them. The check now leaves
such inputs to the serializer.

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

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

PostHog Review · Standard · reviewed 1fca5a2: no findings.

@Silthus

Silthus commented Nov 9, 2026

Copy link
Copy Markdown
Contributor Author

Note

Decision record for this PR, written by the agent that built it (Claude Code, claude-opus-5-5). Each line holds a call made in the agent session or a review round.

  • 🤖 Compiled keys a client resends Dropped and recompiled on every save, not refused, because the editor sends the stored envelope back. transpiled is refused.
  • 🤖 Completeness on draft saves Checked on strict saves only, so a web draft may stay unfinished. Every other sandbox check runs on every save.
  • 🤖 Adoption check without a request Skipped when no team is in context, because those internal re-saves only re-persist accepted steps.
  • 🤖 Sender-kind grandfathering Live sender ids only, not drafts, which differs from the spec's "every stored variant". Approving a suggestion or restoring a revision writes a draft without the check, and every web draft save now enforces it.
  • 🤖 Restored revision with a sandbox step Grandfathered like any stored step. The organization block and the tenant pause apply at send.
  • 🤖 Tenant poll schedule A minute crontab with an expiry, so a slow run never stacks.
  • 🤖 Test-run ownership A test run's override is judged by the saved workflow's owner, never by the owner it submits.
  • 🤖 Null text in a sandbox step Refused, not dropped, because the sender's renderer takes a missing field but never a null one.

The draft sender check now reaches cdp through its presentation
re-exports, as the facade contract requires. The tenant poll is marked
exempt from the team scope audit, because its model is platform state
with no team.

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

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

PostHog Review · Standard · reviewed 4d585be: no findings.

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Not approved — this change needs a human reviewer.

Re-add the stamphog label to request another review once you have addressed this.

The author belongs to the workflows owning team, but @PostHog's substantive activation concern remains unfixed and has not been accepted by another reviewer. Resolve that thread and request review from @PostHog/team-workflows; Harley Alexander and Daniel Marchuk are suggested reviewers.

  • Author wrote 10% of the modified lines and has 18 merged PRs in these paths (familiarity MODERATE).
  • 1 of the 41 changed files are governed by products/desktop/AGENT_APPROVALS.md.
  • @PostHog on products/workflows/backend/presentation/views/hog_flow.py: the activation concern remains unaddressed. With adoption enabled, the current serializer accepts active sandbox workflows, but nodejs/src/cdp/services/hogflows/hogflow-executor.service.ts has no sandbox action handler and throws for unsupported actions; no sandbox support exists in the Node checkout. The author's rollout explanation is not independent acceptance. Address this thread by preventing activation until runtime support is present, or obtain non-author acceptance of the rollout dependency before re-requesting.
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✓ no deny categories matched
size ✓ 623L, 22F substantive, 1553L/41F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1d-complex (1553L, 41F, cross-cutting, feat)
stamphog 2.7.0 .stamphog/policy.yml @ 90cddf7 · reviewed head 90cddf7

@Silthus
Silthus marked this pull request as draft November 11, 2026 08:24
Add WORKFLOWS_SANDBOX_EMAIL_RUNTIME_ENABLED, off by default. While it is
off, a workflow holding an email_sandbox step can't go live through any
live write (create, update, graph or email edit, publish) or run as a
test. Draft saves still work.

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

Render compiled sandbox templates on the HogVM and assert the rendered
email. Answer boto3's HTTP requests with a recording SESv2 fake, so the
provisioning and tenant poll tests assert the stored rows and the
requests SES received, not mocked SDK calls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Base automatically changed from workflows-sandbox/p1-sender-kind-state to master November 11, 2026 11:25
@trunk-io

trunk-io Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

😎 Stack merged successfully - details.

The sandbox step tests checked the stored envelope for a missing
bytecode key. They now send a hog step carrying another step's compiled
bytecode and render what the server stored on the HogVM, through the
validator and through the hog flows API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Silthus and others added 2 commits November 11, 2026 14:25
Two provision requests that both miss the sender row no longer let the
loser overwrite the winner's created_by: provisioning creates the row and,
on a conflict, returns the row that won.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	products/workflows/backend/facade/api.py

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Approved.

The author belongs to the workflows owning team, but this cross-team change also touches shared validation and API contracts; the trusted context explicitly excludes the incomplete current-head review as assurance. Request review covering those shared changes from @PostHog/team-context-and-mcp and @PostHog/team-surfaces, with Harley Alexander or Daniel Marchuk as suggested reviewers, before re-requesting.

  • Author wrote 9% of the modified lines and has 18 merged PRs in these paths (familiarity MODERATE).
  • PostHog Review (Standard) reviewed the current head (no findings) but skipped 1 changed file, so it does not count as assurance.
  • 1 of the 41 changed files are governed by products/desktop/AGENT_APPROVALS.md.
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✓ no deny categories matched
size ✓ 658L, 23F substantive, 1853L/41F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1d-complex (1853L, 41F, cross-cutting, feat)
stamphog 2.7.0 .stamphog/policy.yml @ b9fe013 · reviewed head b9fe013

@Silthus

Silthus commented Nov 11, 2026

Copy link
Copy Markdown
Contributor Author

/trunk merge

@posthog-security-review-bot

posthog-security-review-bot Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

🔒 Security review withheld — this PR was merged first — b9fe01387ac0

This PR was reviewed, but it merged before the review could post. Posting findings here would publish exploit detail against code that has already shipped, so nothing was posted to this PR. If the review had anything to report, the security team was notified directly.

This message is posted whenever a review finishes after its PR merges, regardless of whether anything was found.

@trunk-io
trunk-io Bot merged commit 5e6634a into master Nov 11, 2026
439 checks passed
@trunk-io
trunk-io Bot deleted the workflows-sandbox/p2-validate-provision branch November 11, 2026 17:27
@posthog-security-review-bot posthog-security-review-bot Bot removed the security-review Request a security review label Nov 11, 2026
@deployment-status-posthog

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

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-11-11 18:06 UTC Run
prod-us ✅ Deployed 2026-11-11 18:33 UTC Run
prod-eu ✅ Deployed 2026-11-11 18:33 UTC Run

This table shows the posthog release only. Hogstation shows every service that this PR reaches, Kargo services included.

This branch had an error being deployed

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

Labels

hogbox-preview Spin up a per-PR PostHog preview on a hogland hogbox run-ci-backend Force ci-backend's full test matrices to run even on a draft PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants