Skip to content

feat(workflows): let other products open a broadcast or workflow for an audience - #112985

Merged
trunk-io[bot] merged 7 commits into
masterfrom
posthog/message-audience-plumbing
Oct 7, 2026
Merged

trunk-io[bot] merged 7 commits into
masterfrom
posthog/message-audience-plumbing

Conversation

@dmarchuk

@dmarchuk dmarchuk commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Problem

  • Other products can't send someone from "here are these people" to a broadcast. A link to /broadcasts/new always opens an empty audience, so people rebuild it by hand.
  • Workflows already has a trigger prefill (?trigger=), but nothing for Broadcasts, and no shared way for a product to offer both.
  • This PR is the plumbing. Small follow-up PRs add the entry points (cohorts, insight cohorts, early access, surveys, error tracking, feature flags, funnels), each in its own PR on top of this one.

Changes

  • /broadcasts/new?audience=<filters>&name=<name>&source=<surface> opens the wizard with the audience and name filled in. The params are removed from the URL after load.
  • Opening the link doesn't create a draft. The draft is created on Continue, as it is without a link.
  • Only person and cohort filters are accepted, the ones the Recipients step supports. Each filter is rebuilt from its key, type, operator, value and cohort name, so a hidden field the backend would reject (such as group_type_index) can't make it drop the filter. A workflow link's batch audience goes through the same check. Each filter must be one the backend can apply: a cohort needs its id, a person filter needs a value unless it is is set or is not set. A link with any other filter adds no recipients. The Recipients step then shows a warning and blocks Continue while the audience is empty, until the person adds a condition or picks "Send to everyone", so a broken link never quietly widens the audience to everyone. The link's name still applies.
  • source is kept on the wizard, carried to the saved draft for the tab, and sent as entry_source on broadcast launched, plus a broadcast prefilled from link event, so each entry point's launches can be measured.
  • A workflow opened from a ?trigger= link can be created as a draft right away. Before, "Create as draft" said "No changes to save" until something was edited, the same as the cohort page's existing workflow link. The trigger param is also accepted when kea-router has already parsed it into an object.
  • New products/workflows/frontend/MessageAudience/: messageAudienceUrl builds the broadcast or workflow link for an audience, captureMessageAudienceClicked tracks the click, and MessageAudienceButton is a "Message these people" button with "Send a broadcast" and "Start a workflow". Nothing uses them in this PR. The entry-point PRs do.

Recipients step opened from a link with a cohort audience and name

How did you test this code?

Local app, Playwright, Postgres read back:

  1. Created a static cohort "Beta testers" (1 person) and opened /broadcasts/new with that cohort as audience, name=Beta is now live, source=e2e. The Recipients step showed the cohort card ("Beta testers, List, 1 person"), the title read "Beta is now live", and the URL was back to /broadcasts/new.
  2. Broadcast count in posthog_hogflow before and after opening the link: 308 and 308, so no draft was created on open.
  3. Clicked Continue. The new draft row has name Beta is now live and trigger filters [{"key": "id", "type": "cohort", "value": 124, …}].
  4. audience=not-json: the wizard opened empty, and the params were removed.
  5. An event filter ($browser) as the audience was ignored.
  6. audience=[{"key":"email","type":"person","operator":"exact"}] (no value) with name=Bad link test: no filter added, the title read "Bad link test", the warning banner showed, and Continue was disabled with "Fix errors before continuing". Clicking "Send to everyone" removed the banner and enabled Continue. In a second run, adding a condition did the same. Removing that condition again brought the banner back and disabled Continue. Broadcast rows in posthog_hogflow stayed at 326 and 326.
  7. audience=[{"key":"email","type":"person","operator":"is_set"}]: the "Email address is set" filter was added and the reach dropped to 251 of 2,403 demo persons.

A link whose audience can't be used: no filter is added, a warning stays on the step, and Continue is blocked.

Broadcast opened from a malformed audience link

  1. Workflow trigger link before and after. Opened /workflows/new/workflow?trigger=<$exception event trigger>. Before: "Create as draft" was disabled with "No changes to save", and posthog_hogflow stayed at 1453 rows. After: the button was enabled. Clicking it saved a draft whose trigger action config is {"type": "event", "filters": {"events": [{"id": "$exception", …}], "bytecode": […]}}. The same check passed with the link builder's exact URL. /workflows/new/workflow without a trigger still shows "No changes to save".
  2. Opened a cohort link with source=cohort and clicked Continue: the URL moved to the new draft (broadcast rows 326 to 327), and session storage broadcasts-entry-sources held cohort for that draft id, also after a reload.
  3. Opened audience=[{"key":"email","type":"person","operator":"exact","value":"<one person's email>","group_type_index":-1}]: the reach read "approximately 1 of 2,403 people" (not everyone), and after Continue the saved trigger filters were [{"key": "email", "type": "person", "value": "…", "operator": "exact"}], with no group_type_index.

Hidden field link reaching one person

Not checked in the browser: the two analytics events. Local dev sends no capture requests (only /flags), so they are covered by reading the code only. MessageAudienceButton is exercised in the entry-point PRs.

Test rationale: broadcastAudiencePrefill.test.ts covers the URL round trip and the rejected inputs (missing, malformed, empty, object, event filter, a person filter without a value, a cohort without an id, one bad filter next to a good one) and the value-less is set filter that must still pass. The new broadcastWizardLogic case catches a regression where opening a prefilled link creates a draft or drops the name or source.

Release status

  • No feature flag controls this change
  • This change is behind a feature flag and is not available to users
  • This change makes a previously flagged feature available to everyone

Automatic notifications

  • Publish to changelog?

Docs update

None. The entry points will be documented once they land.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

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

  • Skills invoked: /writing-ui-components, /writing-tests, /writing-pr-descriptions.
  • CodeRabbit CLI pass skipped: the CLI is signed out on this machine.
  • Base PR for a set of small entry-point PRs, each of which branches from this one.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BR6LoqsDMB9Wfnftcy2KiG

@dmarchuk dmarchuk self-assigned this Oct 7, 2026
@trunk-io

trunk-io Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

😎 Merged successfully - details.

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

🤖 CI report

✅ Trunk lane — non-backend lane (fe:product:workflows)

This PR is assigned to the non-backend lane (fe:product:workflows). It does not run backend Python tests and may merge in parallel with PRs in other lanes.

⚠️ Complexity (TypeScript) — 10 functions above the limit (max 38)

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
WorkflowSceneHeader products/workflows/frontend/Workflows/WorkflowSceneHeader.tsx:35 38 10
toAudienceFilter products/workflows/frontend/Broadcasts/broadcastAudiencePrefill.ts:55 25 10
launchBroadcast products/workflows/frontend/Broadcasts/broadcastWizardLogic.ts:1459 25 10
WorkflowScene products/workflows/frontend/Workflows/WorkflowScene.tsx:60 23 10
<anonymous> products/workflows/frontend/Broadcasts/broadcastWizardLogic.ts:986 22 10
<anonymous> products/workflows/frontend/Broadcasts/broadcastWizardLogic.ts:1738 15 10
snapshotBroadcast products/workflows/frontend/Broadcasts/broadcastUsage.ts:63 14 10
setEmail products/workflows/frontend/Broadcasts/broadcastWizardLogic.ts:1245 12 10
saveName products/workflows/frontend/Broadcasts/broadcastWizardLogic.ts:1301 11 10
moveToDraft products/workflows/frontend/Broadcasts/broadcastWizardLogic.ts:1626 11 10
✅ 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 — 4% of added code lines are comments (16 of 439)

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/frontend/Broadcasts/broadcastAudiencePrefill.ts 5 87
products/workflows/frontend/Broadcasts/broadcastWizardLogic.ts 4 88
products/workflows/frontend/Broadcasts/broadcastUsage.ts 3 24
products/workflows/frontend/MessageAudience/messageAudience.ts 2 39
products/workflows/frontend/Workflows/workflowTriggerPrefill.ts 2 14

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

⚠️ Bundle size — 🔺 +64.6 KiB (+0.1%)

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

Total: 76.45 MiB · 🔺 +64.6 KiB (+0.1%)

File Size Δ vs base
render-query/src/render-query/render-query.js 25.51 MiB 🔺 +31.7 KiB (+0.1%)
posthog-app/_parent/products/business_intelligence/frontend/BusinessIntelligenceScene.js 96.7 KiB 🔺 +14.5 KiB (+17.7%)
posthog-app/src/scenes/saved-insights/SavedInsights.js 106.0 KiB 🔺 +13.4 KiB (+14.4%)
posthog-app/_parent/products/metrics/frontend/MetricsScene.js 31.6 KiB 🟢 -8.1 KiB (-20.5%)
posthog-app/_parent/products/business_intelligence/frontend/BIWorksheetsScene.js 7.7 KiB 🔺 +7.7 KiB (new)
posthog-app/src/scenes/insights/InsightScene.js 53.7 KiB 🔺 +4.0 KiB (+8.0%)
posthog-app/src/layout/today/TodayProductsSidebar.js 3.9 KiB 🔺 +3.9 KiB (new)
posthog-app/_parent/products/alerts/frontend/views/InsightAlerts.js 13.3 KiB 🔺 +3.3 KiB (+33.4%)
posthog-app/src/layout/today/TodayToolsSidebar.js removed 🟢 -3.1 KiB (-100.0%)
posthog-app/src/layout/today/TodayLibrarySidebar.js removed 🟢 -2.4 KiB (-100.0%)
posthog-app/_parent/products/managed_migrations/frontend/ManagedMigration.js 31.1 KiB 🔺 +2.0 KiB (+7.0%)
posthog-app/src/layout/today/TodayMoreSidebar.js removed 🟢 -2.0 KiB (-100.0%)
posthog-app/_parent/products/autoresearch/frontend/AutoresearchScene.js 16.0 KiB 🔺 +1.7 KiB (+11.7%)
posthog-app/_parent/products/workflows/frontend/Broadcasts/BroadcastScene.js 87.4 KiB 🔺 +1.6 KiB (+1.9%)
exporter/src/exporter/scenes/ExporterNotebookScene.js 3.73 MiB 🔺 +1.6 KiB (+0.0%)
posthog-app/_parent/products/cross_project_dashboards/frontend/CrossProjectDashboardScene.js 36.6 KiB 🔺 +1.4 KiB (+4.0%)
exporter/_parent/products/workflows/frontend/Workflows/Workflow.js 126.3 KiB 🔺 +1.4 KiB (+1.1%)
exporter/src/queries/schema.js 1.25 MiB 🔺 +1.2 KiB (+0.1%)
posthog-app/src/queries/schema.js 1.25 MiB 🔺 +1.2 KiB (+0.1%)
posthog-app/_parent/products/autoresearch/frontend/AutoresearchPipelineScene.js 55.5 KiB 🔺 +1.1 KiB (+2.0%)
posthog-app/src/scenes/feature-flags/FeatureFlag.js 130.5 KiB 🔺 +1.1 KiB (+0.8%)
posthog-app/_parent/products/tracing/frontend/TracingScene.js 188.8 KiB 🔺 +1.1 KiB (+0.6%)

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.65 MiB · 23 files 🔺 +5.7 KiB (+0.3%) █████████░ 89.6% of 1.84 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.78 MiB · 670 files 🔺 +42.2 KiB (+1.1%) █████████░ 93.9% of 4.03 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
7.68 MiB · 2,422 files 🔺 +17.3 KiB (+0.2%) █████████░ 92.0% of 8.34 MiB
dashboard scene
src/scenes/dashboard/Dashboard.tsx
9.91 MiB · 3,502 files 🔺 +32.3 KiB (+0.3%) █████████░ 90.8% of 10.92 MiB
today home path
src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
7.70 MiB · 2,434 files 🔺 +18.5 KiB (+0.2%) █████████░ 89.7% of 8.58 MiB
events scene
src/scenes/activity/explore/EventsScene.tsx
9.53 MiB · 3,353 files 🔺 +29.6 KiB (+0.3%) █████████░ 90.7% of 10.51 MiB
replay detail scene
src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
12.24 MiB · 4,161 files 🔺 +17.6 KiB (+0.1%) ████████░░ 77.9% of 15.72 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/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
🟢 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/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
🟢 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

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.3 KiB src/index.tsx
1.3 KiB src/RootErrorBoundary.tsx
912 B ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js
839 B src/scenes/ChunkLoadErrorBoundary.tsx
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
Size File
316.7 KiB ../node_modules/.pnpm/posthog-js@1.438.1_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
221.0 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
99.7 KiB src/lib/api.ts
93.3 KiB src/products.tsx
68.7 KiB src/lib/lemon-ui/icons/icons.tsx
40.7 KiB src/lib/utils/eventUsageLogic.ts
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.0 KiB ../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/schemas.js
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
316.7 KiB ../node_modules/.pnpm/posthog-js@1.438.1_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
281.0 KiB src/taxonomy/core-filter-definitions-by-group.json
221.0 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
112.3 KiB ../packages/quill/packages/quill/dist/index.js
99.7 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
93.3 KiB src/products.tsx
90.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
Largest files eagerly shipped from src/scenes/dashboard/Dashboard.tsx
Size File
316.7 KiB ../node_modules/.pnpm/posthog-js@1.438.1_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
281.0 KiB src/taxonomy/core-filter-definitions-by-group.json
221.0 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
181.9 KiB src/queries/validators.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
112.3 KiB ../packages/quill/packages/quill/dist/index.js
99.7 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
93.3 KiB src/products.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
316.7 KiB ../node_modules/.pnpm/posthog-js@1.438.1_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
281.0 KiB src/taxonomy/core-filter-definitions-by-group.json
221.0 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
112.3 KiB ../packages/quill/packages/quill/dist/index.js
99.7 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
93.3 KiB src/products.tsx
90.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
Largest files eagerly shipped from src/scenes/activity/explore/EventsScene.tsx
Size File
316.7 KiB ../node_modules/.pnpm/posthog-js@1.438.1_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
281.0 KiB src/taxonomy/core-filter-definitions-by-group.json
221.0 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
181.9 KiB src/queries/validators.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
112.3 KiB ../packages/quill/packages/quill/dist/index.js
99.7 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
93.3 KiB src/products.tsx
Largest files eagerly shipped from src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
Size File
316.7 KiB ../node_modules/.pnpm/posthog-js@1.438.1_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
316.0 KiB ../node_modules/.pnpm/posthog-js@1.438.1_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js
281.0 KiB src/taxonomy/core-filter-definitions-by-group.json
221.0 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
181.9 KiB src/queries/validators.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
112.3 KiB ../packages/quill/packages/quill/dist/index.js
99.7 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 #32479

✅ Toolbar bundle — eager 2.23 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.23 MiB · 19 files 🔺 +3.9 KiB (+0.2%) ████░░░░░░ 39.0% of 5.72 MiB
Deferred (lazy) 2.11 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
858.2 KiB dist/toolbar/toolbar-app-TSGDYHMJ.css
668.2 KiB dist/toolbar/chunk-chunk-KNXWAVPL.js
259.4 KiB dist/toolbar/chunk-chunk-5UE4YDI5.js
138.2 KiB dist/toolbar/chunk-chunk-BGWYB7NC.js
131.8 KiB dist/toolbar/chunk-chunk-FDH2IBXT.js
75.2 KiB dist/toolbar/toolbar-app-PKSA4YXK.js
69.0 KiB dist/toolbar/chunk-chunk-TSAL54PB.js
35.6 KiB dist/toolbar/chunk-chunk-Z5EJQRZQ.js
21.0 KiB dist/toolbar/chunk-chunk-QIQG3WQ7.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 — 🔺 +1.55 MiB (+0.2%)

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

Total: 1004.65 MiB · 🔺 +1.55 MiB (+0.2%)

✅ Playwright — all passed

All tests passed.

View test results →

✅ Hogbox preview — ready, open the preview

▶ Open the preview

🔑 Login test@posthog.com / 12345678 (demo data)
🧩 Running this PR's backend and frontend, on the PostHog :master base
🔗 Link stable across rebuilds: a re-push swaps the box underneath, the URL stays
🔒 Access tailnet only (PostHog VPN)
🛠️ Admin inspect & debug state in hogland
💤 Idle sleeps after ~30 min idle (snapshot to S3, zero node cost) and wakes on your next visit in ~30s, behind a brief "waking up" screen

commit 606cdaf · box box-8f15285e334b · ready in 821s (push → usable) · build log · rebuilds on every push, torn down on close

@hosthog

hosthog Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

HostHog preview — posthog-desktop-web

The previews for this PR have been torn down and no longer serve.

@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
  • Review profile: QUIET
  • Plan: Enterprise
  • Run ID: a85db486-a27e-4153-aa1f-5365a88d5f23
📥 Commits

Reviewing files that changed from the base of the PR and between ff7f8bb and 0530e5c.

📒 Files selected for processing (4)
  • products/workflows/frontend/Broadcasts/broadcastUsage.ts
  • products/workflows/frontend/Broadcasts/broadcastWizardLogic.test.ts
  • products/workflows/frontend/Broadcasts/broadcastWizardLogic.ts
  • products/workflows/frontend/Broadcasts/steps/BroadcastRecipientsStep.tsx

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 10 remain after this review.


📝 Walkthrough

Walkthrough

The change adds message audience links for broadcast and workflow destinations. Broadcast links can include audience filters, a name, and a source. The broadcast wizard parses audience filters from the URL, applies available prefill values, records source analytics, clears prefill parameters, and loads the audience preview. If the audience cannot be parsed, the wizard shows a warning and offers a send-to-everyone action. The wizard also stores entry sources and includes them in launch analytics. Workflow trigger-prefill parsing accepts non-string values, and the workflow header recognizes new workflows created from valid trigger-prefill links.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to 0530e

Link-opened broadcasts retain their source for launch analytics, and an empty rejected audience remains blocked until the sender explicitly chooses to send to everyone. No concrete merge-blocking risk remains.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 0530e

Existing access checks, audience previews, sending limits, and explicit launch actions constrain exposure. However, reloading a rejected audience link after its parameters are removed loses the rejection warning and permits the normal send-to-everyone flow.

Retained concerns

  • Medium · security · inferred: Rejected linked-audience intent is transient while its recovery input is removed from the URL. After opening an invalid audience link and reloading the cleaned /broadcasts/new URL, the wizard initializes with empty filters and rejection=false. Continue can then create an unrestricted draft without the rejection-specific “Send to everyone” choice. An authorized user must still configure and explicitly launch the broadcast, and the review screen identifies the unrestricted audience; nevertheless, the new fail-closed recipient safeguard does not survive this interruption.
Security review details

Security Blast Radius

  • inferred — The recovery concern can widen one authorized launch to the current project's unfiltered recipient population, subject to sending limits and existing email configuration. It does not establish access to another project's recipients or allow an unauthenticated link author to send messages directly.

Security Findings and Attack Paths

  • inferred — A malformed external audience link can establish rejected recipient intent that disappears after URL cleanup and a subsequent reload. The resulting empty audience is eligible for the ordinary creation flow. Exploitation requires an authorized operator to continue and launch despite the displayed unrestricted audience; no automatic-send path was established.

Trust Boundaries and Controls

  • observed — URL values enter frontend state but do not replace backend authority. Audience preview requires resource-level workflow viewer access; API-token preview and dispatch scopes include person:read. Batch dispatch requires an active workflow and object lookup, and cohort membership queries constrain project or team identity. These inspected backend paths are unchanged by the full PR comparison.

Resilience and Maintainability Implications

  • observed — Broadcast Continue waits for pending saves and stops on detected conflicting edits. Launch likewise checks pending-save conflicts. These existing ordering controls protect saved content, but they do not persist the new pre-draft recipient rejection state across a reload.

Hardening Proposals

  • proposed — Preserve or reconstruct rejected recipient intent across URL cleanup and wizard recovery, or retain a rejection marker until the operator supplies an audience or explicitly chooses everyone. Scope any persisted marker to the relevant project and creation attempt.
🚥 Pre-merge checks | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the problem, user-visible changes, testing, screenshots, release status, docs, and agent context. However, the release status section retains checkbox syntax and all three optio… Replace the release status section with exactly one plain bullet and remove the other options. Add reviewer-facing results for the duplicate search, patch coverage, new-events-schema applicability and outcome, and public-artifact check. Sta…
Full details: Description check

Explanation

The description covers the problem, user-visible changes, testing, screenshots, release status, docs, and agent context. However, the release status section retains checkbox syntax and all three options instead of one plain bullet. The agent context also omits the required duplicate-PR, patch-coverage, new-events-schema, and public-artifact gate results.

Resolution

Replace the release status section with exactly one plain bullet and remove the other options. Add reviewer-facing results for the duplicate search, patch coverage, new-events-schema applicability and outcome, and public-artifact check. State any applicable labels or explain why each gate does not apply.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
  • Review profile: QUIET
  • Plan: Enterprise
  • Run ID: c7839c38-8ab1-4aaf-a3ad-68266a689fda
📥 Commits

Reviewing files that changed from the base of the PR and between 87633d9 and 1e052ff.

📒 Files selected for processing (6)
  • products/workflows/frontend/Broadcasts/broadcastAudiencePrefill.test.ts
  • products/workflows/frontend/Broadcasts/broadcastAudiencePrefill.ts
  • products/workflows/frontend/Broadcasts/broadcastWizardLogic.test.ts
  • products/workflows/frontend/Broadcasts/broadcastWizardLogic.ts
  • products/workflows/frontend/MessageAudience/MessageAudienceButton.tsx
  • products/workflows/frontend/MessageAudience/messageAudience.ts

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 10 remain after this review.

@trunk-io

trunk-io Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Static Badge   Static Badge   Static Badge

View Full Report ↗︎ ⋅ Docs

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

🟡 Minor · Preserve entrySource when continuing from a link-opened… · broadcastWizardLogic.ts:1575

products/workflows/frontend/Broadcasts/broadcastWizardLogic.ts:1575
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve entrySource when continuing from a link-opened broadcast.

When Continue creates a draft, the wizard replaces /broadcasts/new with the draft URL. The route ID changes the broadcastWizardLogic key, so the new logic instance starts entrySource as null and draft hydration does not restore it. A later broadcast launched event can therefore send entry_source: null. Carry the source into the saved-draft logic instance or persist and restore it with the draft.


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
  • Review profile: QUIET
  • Plan: Enterprise
  • Run ID: e96bbb82-b2ed-4567-ab95-4a98dc38842d
📥 Commits

Reviewing files that changed from the base of the PR and between 274d37a and ff7f8bb.

📒 Files selected for processing (3)
  • products/workflows/frontend/Broadcasts/broadcastWizardLogic.test.ts
  • products/workflows/frontend/Broadcasts/broadcastWizardLogic.ts
  • products/workflows/frontend/Broadcasts/steps/BroadcastRecipientsStep.tsx

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 10 remain after this review.

@dmarchuk dmarchuk added stamphog Request AI approval (no full review) and removed stamphog Request AI approval (no full review) labels Oct 7, 2026
@dmarchuk
dmarchuk marked this pull request as ready for review October 7, 2026 15:06
@dmarchuk dmarchuk added the stamphog Request AI approval (no full review) label Oct 7, 2026
@parameterai

parameterai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Risk: No findings

This increment hardens the broadcast/workflow audience prefill parsing: filters from links are rebuilt from a known field allowlist, operators must be known enum values, cohort ids must be positive integers, and values must be non-empty scalars or arrays of scalars; batch workflow trigger audiences now get the same check. I traced every silent-audience-widening hypothesis to the backend and each path fails closed with a visible error, so I found no new security issues in these changes.

Sentinel reviewed 606cdaf · Review settings

@github-actions
github-actions Bot requested a deployment to preview-pr-112985 October 7, 2026 15:17 In progress
@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team October 7, 2026 15:18
@veria-ai

veria-ai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 1 · PR risk: 0/10

@stamphog stamphog Bot removed the stamphog Request AI approval (no full review) label Oct 7, 2026

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

An unresolved security comment from veria-ai on broadcastAudiencePrefill.ts is valid, and the PR hasn't addressed it. A link filter carrying a hidden invalid field, such as a negative group_type_index, passes the frontend check. The backend then drops that filter, so the broadcast audience can widen to everyone while the filter still shows in the wizard. This is the exact case the PR says it prevents.

  • Author wrote 69% of the modified lines and has 30 merged PRs in these paths (familiarity MODERATE).
  • products/workflows/frontend/Broadcasts/broadcastAudiencePrefill.ts: isUsableAudienceFilter returns the raw filter objects, so extra fields like group_type_index: -1 pass through. In posthog/models/property/parse.py, parse_properties drops any filter that Property.init rejects, which widens the audience without setting linkAudienceRejected.
  • The unresolved veria-ai inline thread has no author reply or fix.
  • Suggested fix: rebuild each accepted filter from an allowlist of fields (key, type, operator, value, and cohort_name for cohorts), or reject filters with unknown fields. Add a test for a person filter with group_type_index: -1.
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✓ no deny categories matched
size ✓ 329L, 9F substantive, 414L/12F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1d-complex (414L, 12F, single-area, feat)
stamphog 2.3.1 .stamphog/policy.yml @ abe0646 · reviewed head abe0646

@github-actions
github-actions Bot requested a deployment to preview-pr-112985 October 7, 2026 15:24 In progress

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

Frontend-only change in the workflows product, by an author on the owning team. It touches no auth, billing, or API contracts, and it adds tests. The earlier CodeRabbit concern about losing the entry source on draft creation is addressed with session storage.

  • Author wrote 64% of the modified lines and has 30 merged PRs in these paths (familiarity MODERATE).
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✓ no deny categories matched
size ✓ 376L, 9F substantive, 494L/12F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1d-complex (494L, 12F, single-area, feat)
stamphog 2.3.1 .stamphog/policy.yml @ 606cdaf · reviewed head 606cdaf

@dmarchuk

dmarchuk commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

/trunk merge

@trunk-io
trunk-io Bot merged commit 0fe88d3 into master Oct 7, 2026
@trunk-io
trunk-io Bot deleted the posthog/message-audience-plumbing branch October 7, 2026 16:30
@deployment-status-posthog

deployment-status-posthog Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-10-07 17:14 UTC Run
prod-us ⏳ Pending
prod-eu ✅ Deployed 2026-10-07 17:26 UTC Run

This branch was successfully deployed

1 active deployment
preview-pr-112985 — 606cdafd Deployed Oct 7, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stamphog Request AI approval (no full review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant