Skip to content

feat(workflows): send a broadcast to people who have a feature flag - #113009

Merged
trunk-io[bot] merged 22 commits into
masterfrom
posthog/message-audience-feature-flag
Oct 20, 2026
Merged

trunk-io[bot] merged 22 commits into
masterfrom
posthog/message-audience-feature-flag

Conversation

@dmarchuk

@dmarchuk dmarchuk commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Changes

  • A saved flag page gets Send a broadcast in the side panel actions, and File → Create → Broadcast in the scene menu bar.
  • A click snapshots the people who match the flag into a static cohort, the same way Create cohort does. The static cohort comes from the flags service, so it respects rollout percentage, variants and every condition set.
  • The page shows a "Finding the people who have this flag…" loading toast, waits until the cohort fills, then opens a new broadcast:
    • Audience: that cohort.
    • Name: " announcement".
    • entry_source: feature_flag.
  • The wait polls the cohort every 2 seconds for up to 60 seconds:
    • Still filling after that: a toast says so and links to the cohort, and no broadcast opens.
    • Filling failed: an error toast, and no broadcast opens.
  • The loading toast closes on success, on failure, and when the flag page unmounts.
  • When the person has left the flag page before the cohort fills, the broadcast does not open on its own. A toast offers Open broadcast instead.
  • The entry is disabled with a reason in these cases:
    • The person has less than Editor access to Workflows. A broadcast save would fail for them anyway.
    • The flag targets groups. The cohort task skips group flags, so the cohort would stay empty.
    • The flag is disabled, archived, or deleted. An archived flag says to unarchive it first, because the API refuses to enable it until then.
    • A snapshot is already in progress.
  • A click first reads the flag from the server. A teammate's edit or a scheduled change after the page loaded counts, and a group or disabled flag stops the click with an error toast.
  • A click reuses a recent snapshot instead of making a new one. The rules:
    • The lookup lists the current user's static cohorts, newest first, with no search. A search ranked by relevance could miss the newest snapshot.
    • The cohort must belong to this flag, be newer than the flag's last save, and have no fill errors.
    • A filled cohort must be under an hour old. A cohort still filling is reused for up to 4 hours, the fill task's soft limit, so a later click shares the running scan instead of starting a second one.
    • A cohort another member made is never reused.
    • A cohort with any activity log entry is never reused. Creating and filling a snapshot logs nothing, so an entry means someone edited it, for example by adding or removing people by hand. The check reads the cohort's own activity endpoint, which works without the audit logs add-on. If the history can't be read, a fresh snapshot is made.
    • The lookup pages past 20 cohorts while they are still young enough to reuse.
  • The wait and its final toast keep running after the person leaves the flag page, which unmounts the flag logic.
  • message audience clicked fires on every click, before the wait. It used to fire only when the broadcast opened.
  • The cohort list endpoint now declares its existing type and created_by_id filters, so the generated client can send them. The generated TypeScript and MCP files are regenerated output.
  • The entry is behind feature-flag-cohort-creation, like Create cohort, and shows only for v1 flags. New flags and edit mode use a different form, so the entry does not appear there.
  • Stacked on chore(feature-flags): declare the static cohort endpoint schema #113684, which declares the schema of the create-cohort endpoint. Merge chore(feature-flags): declare the static cohort endpoint schema #113684 first.
Waiting (loading toast) Still filling after 60 s
ffb-07-progress-toast ffb-08-still-filling-toast
Group flag: disabled No Workflow access: disabled
ffb-06-group-flag-disabled ffb-11-no-workflow-access-menu
Prefilled broadcast Review before the real send
ffb-04-broadcast-prefilled ffb-13-review
Earlier screenshots (before the review fixes)
Side panel Menu bar (long key)
xp-ff-dark-1-panel xp-ff-longkey-1-menubar
Prefilled broadcast Disabled flag
xp-ff-dark-3-broadcast xp-ff-disabled-2-disabled
Waiting for the cohort Still filling after 60 s
xp-ff-waiting xp-ff-timeout

How did you test this code?

Head a46154af, on an isolated local stack: own Postgres clone, Redis db 9/10, Celery worker, feature-flags service, Vite, and CDP API, batch-resolve, hogflow and email workers on a clone of cyclotron_node. Playwright clicked through Chromium. Persons came in through local capture with example.com emails, plus the SES success simulator address for the send. Most runs use a small project (8 persons), because a flag cohort on the 27k-person project takes about 2.5 minutes to fill.

Step Observed Storage
Person flag, File → Create → Broadcast Loading toast within 25 ms. The broadcast opened after 2.7 s, named " announcement", source feature_flag posthog_cohort 242: static, count 3, is_calculating=f. ClickHouse person_static_cohort: 3 rows
Second click, side panel Opened with cohort 242 No new cohort row
Flag edited through the API after the page loaded, then a click from that stale page Opened with a new cohort 243 243: count 4, matching the new condition
Double click on a fresh flag Button disabled during the wait, one broadcast One cohort row (244)
Group flag (aggregation_group_type_index: 0) Menu item and panel button disabled: "This flag targets groups, not people, so there is no one to email." Forced click did nothing No cohort row
Disabled flag Button disabled
Celery paused, click, wait 62 s "Still finding…" toast with View cohort, which opened /cohorts/245. The loading toast closed 245: is_calculating=t
Fill fails during the wait (errors_calculating=1 set in Postgres) "Couldn't find the people who have this flag…" error toast, no navigation
Retry with that failed cohort still recent New cohort 247, broadcast opened 246 errors_calculating=1 skipped
Leave the flag page mid-wait (Celery paused) Loading toast gone, page stayed on Cohorts Cohort 248 filled later, no navigation
Member with Workflows set to Viewer (access control row) Both entries disabled: "Your access level (viewer) doesn't meet the required level (editor)". Forced click created nothing Cohort count for the project unchanged
27k-person project: first click Loading toast, then "Still finding…" after 60 s Cohort 250 filled after about 2.5 min, count 1
Same flag, click again, then Send now from Review Opened with cohort 250. Launch from the Review step Batch job completed. app_metrics2 for the job: email_sent 1. log_entries: "Email sent to success@simulator.amazonses.com", through real SES from the test sender
  • message audience clicked: a spy wrapped posthog.capture in the page. One call with {source: 'feature_flag', destination: 'broadcast'} on a click that timed out, and one on the click that opened the broadcast.
  • Simulated: the failed fill and its retry set errors_calculating in Postgres. The email subject and body for the real send were written through the hog flow API, because the visual editor is not drivable from Playwright. The sender, the audience and the launch went through the UI.
  • Not tested: the progress toast across a full page reload. The base branch was not re-run for the access and group cases, so their "before" comes from the review, not a browser run.
  • Test rationale: featureFlagLogic.test.ts cases now fail when someone removes the poll loop (a cohort that fills on the third check), the errors_calculating reuse check, or the server read of the flag. The outcome cases assert the toast text, the View cohort target, the broadcast name and source, the captured event, the list query parameters, and the loading toast dismissal.
Earlier runs (before the review fixes)

Local dev stack, project 1, Playwright driving Chromium, with feature-flag-cohort-creation and scene-menu-bar turned on through PERSISTED_FEATURE_FLAGS.

  1. Created four flags through the API: dark-mode-rollout (active, person property condition), the same condition but disabled, one that matches nobody, and one with a 101-character key.

  2. Opened dark-mode-rollout, opened the side panel, clicked Send a broadcast. A static cohort row appeared with is_calculating = t. After 6 s the page had not navigated and the item read "Finding the people who have this flag…".

  3. Simulated the population task finishing with update posthog_cohort set is_calculating=false, count=3 where id=153 (the local flags service cannot fill the cohort, see below). The page then opened /broadcasts/new with "dark-mode-rollout announcement" and the cohort as its audience. The reach reads 0 because the simulated update wrote no member rows.

  4. Left a cohort calculating: after 60 s the toast "Still finding the people who have this flag. Send the broadcast from the cohort once it is ready." appeared with View cohort, the page stayed put, and the item re-enabled.

  5. Long key, through File → Create → Broadcast: the broadcast name carries the full key.

  6. Disabled flag: the item is disabled with "This flag is disabled, so no one has it. Enable it first." A forced click created no cohort and stayed on the page.

  7. Double click: delayed the cohort request 4 s in the browser. During the request the item was disabled with "Finding the people who have this flag…". A second click did nothing, and exactly 1 cohort was created.

  8. Failed request: answered the cohort request with a 500. The toast read "Create broadcast cohort failed: Simulated failure", the page stayed put, and the item re-enabled.

  9. Left the flag page while the cohort was calculating, came back, then marked the cohort filled in Postgres. The page stayed on the flag and did not open a broadcast.

  10. Gating flag off: neither Create cohort nor Send a broadcast appears in the side panel.

  11. Cohort reuse, on new flags beta and beta-2. Each new cohort was marked filled by hand, as in step 3. Ran this after each step:

    select id, name, is_static, is_calculating, errors_calculating, created_at from posthog_cohort where team_id=1 and name like 'Users with feature flag beta%' order by id;
    
    • Two clicks on beta, each on a fresh page visit: one row (160). The second visit opened the broadcast for cohort 160.
    • Aged row 160 by 61 minutes (update posthog_cohort set created_at = created_at - interval '61 minutes' where id=160), clicked beta again: a new row, 161.
    • Clicked beta-2: a new row, 162. Clicking beta again reused 161, not the newer beta-2 cohort. Final rows:
     160 | Users with feature flag beta enabled at 2026-10-07 09:32:59   | t | f | 0 | 2026-10-07 08:31:59+00
     161 | Users with feature flag beta enabled at 2026-10-07 09:33:38   | t | f | 0 | 2026-10-07 09:33:38+00
     162 | Users with feature flag beta-2 enabled at 2026-10-07 09:33:57 | t | f | 0 | 2026-10-07 09:33:57+00
    
    • Saved the flag (PATCH of its name, which bumps updated_at to 10:02:14), then clicked twice: the first click made row 164 although row 163 (10:02:04) was 10 s old, and the second reused 164. Row 161 was skipped throughout because the local population task had since marked it errors_calculating = 1.
      xp-ff-reuse-visit2
  12. Reuse is limited to the current user's cohorts (after the Veria finding). Setup: soft-deleted the leftover row 167, then made decoy row 166 look like a fresh beta snapshot owned by another org member:

    update posthog_cohort set created_by_id=15, created_at=now(), name='Users with feature flag beta enabled at 2026-10-07 19:10:00' where id=166;
    
    • The cohort list API returns created_by with id and uuid for every row (166 → user 15, the rest → user 1).
    • First click on beta as test@posthog.com: a new row 168 appeared and the decoy 166 was not used. Celery had not filled 168 after 30 s, so it was marked filled in Postgres (update posthog_cohort set is_calculating=false, count=0 where id=168). The page then opened the broadcast with cohort 168.
    • Second click, on a fresh page visit: opened the broadcast with cohort 168 after 1 s, with no new row.
    select id, created_by_id, is_calculating, count, deleted from posthog_cohort where team_id=1 and name like 'Users with feature flag beta enabled at %' and id >= 166 order by id;
     166 | 15 | f | 0 | f
     167 |  1 | t |   | t
     168 |  1 | f | 0 | f
    
  • With chore(feature-flags): declare the static cohort endpoint schema #113684's schema change in place, on flag beta, the first click sent POST create_static_cohort_for_flag/ with no body, created cohort 169 and opened the broadcast with it. The second click sent no create request and reused 169. select id, name, is_static, created_by_id from posthog_cohort where team_id=1 and name like 'Users with feature flag beta enabled%' order by id desc returned 169 | … 19:39:00 | t | 1, then the earlier rows 168 and 167. I marked 169 filled in Postgres, because the local Celery does not fill flag cohorts.

Not tested:

  • Step 12 was not repeated on the pre-fix commit, so the decoy being reused before the fix is shown by the unit test only.

  • Real cohort population. The local flags service answered the batch evaluation with 409 for newly created flags, and the run against an older flag did not finish, so step 3 flips the row by hand. Population is the existing Create cohort task and this PR does not change it.

  • The message audience clicked analytics event: local dev sends no analytics capture requests.

Test rationale: Added two it.each blocks to featureFlagLogic.test.ts. No existing test covered this loader.

  • Cohort ready, failed, or still filling: catches the broadcast opening before the cohort fills, which would send to no one. Removing the still-filling check fails it.
  • Cohort reuse: a recent cohort for this flag made by the current user is reused with no create call. One over an hour old, one made before the flag's last save, one another user made, or one for <key>-2 leads to a create. A cohort still filling at 90 minutes is reused, and one still filling past 4 hours leads to a create. Removing the age check fails the stale case.
  • Archived flag: the click makes no cohort and shows the unarchive message.
  • Leaving the flag page during the wait: the logic unmounts, the wait carries on, and the "Open broadcast" toast still appears. A test unmounts the logic mid-wait.
  • A status check that stalls ends at the 60-second deadline and points to the cohort. A dropped connection shows an error toast; an HTTP error keeps the shared loader toast only.
  • Paging: a reusable cohort behind 20 newer static cohorts is still found. Paging stops once a page reaches cohorts older than the 4-hour fill limit.
  • Edited snapshot: a recent cohort with an activity entry leads to a create. Making the check always pass fails this case.
  • Unreadable history: the cohort activity endpoint answering 402 leads to a create instead of a stuck click.
  • Activity signal against the local backend (API calls, no browser), checked through the project activity log before the check moved to cohorts/:id/activity/, which reads the same rows: create_static_cohort_for_flag on flag qa-snapshot-reuse made cohort 249. GET activity_log/?scope=Cohort&item_id=249&page_size=1 returned []. After PATCH cohorts/249/ it returned ["updated"], and select scope, item_id, activity from posthog_activitylog where scope='Cohort' and item_id='249' returned Cohort | 249 | updated.
  • Not run locally: add_persons_to_static_cohort and remove_person_from_static_cohort return 500 on the local stack, because personhog is not configured there. Both call log_activity with the same scope and item id.
  • Not tested in the browser: the archived message, the reuse of a cohort still filling, and skipping an edited snapshot. All are covered by the unit cases only.

Release status

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

Docs update

None.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

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

  • Skills invoked: /writing-ui-components, /writing-user-facing-copy, /adopting-generated-api-types, /improving-drf-endpoints, /writing-tests, /writing-code-comments, /stacking-prs, /writing-pr-descriptions.
  • The first version opened the broadcast as soon as the cohort row existed. A CodeRabbit finding pointed out the cohort could still be empty, so the page now waits for it.
  • CodeRabbit CLI pass skipped: the CLI is signed out.
  • Chose the static cohort over translating release conditions, because only the flags service evaluates rollout percentage and multiple condition sets correctly.
  • Duplicate search found no other open PR for this entry point.
  • CodeRabbit thread (don't reuse a cohort after the flag changes): fixed. A reused cohort must be newer than the flag's last save.
  • CodeRabbit review (check breakpoints after each cohort request): fixed.
  • CodeRabbit nitpick (a test that unmounts the logic while a request is pending): not taken. It would test kea's breakpoint cancellation rather than this loader.
  • CodeRabbit nitpick (unit tests for the cohort-to-broadcast flow): added in featureFlagLogic.test.ts.
  • PostHog Review thread (reused snapshot after a membership edit): fixed. A snapshot with any activity entry is skipped.
  • haacked review: fixed all 11 points. Group flags, Workflow access, the progress toast and page guard, the list query, the server read of the flag, the rename to findOrCreateBroadcastCohort, the event on every click, the shared canCreateFlagCohort gate, and the three test gaps.
  • Merged chore(feature-flags): declare the static cohort endpoint schema #113684's head into this branch, which brought the generated api.zod.ts in line. hogli build:openapi then changed only the cohort list files.
  • Veria thread (a member can plant a cohort named like a flag snapshot, and the broadcast reuses it): fixed. Reuse now requires the cohort's created_by to be the current user, so another member's cohort leads to a new snapshot. Kept reuse for the same user so repeated clicks do not create duplicates.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BR6LoqsDMB9Wfnftcy2KiG

@dmarchuk dmarchuk self-assigned this Oct 7, 2026
@github-actions

github-actions Bot commented Oct 7, 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) — 14 functions above the limit (max 53)

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
FeatureFlag frontend/src/scenes/feature-flags/FeatureFlag.tsx:145 53 10
loadFeatureFlag frontend/src/scenes/feature-flags/featureFlagLogic.ts:3333 25 10
<anonymous> frontend/src/scenes/feature-flags/featureFlagLogic.ts:5799 25 10
saveFeatureFlagFailure frontend/src/scenes/feature-flags/featureFlagLogic.ts:4402 19 10
saveFeatureFlagSuccess frontend/src/scenes/feature-flags/featureFlagLogic.ts:4359 15 10
copyNewFlagToAdditionalProjects frontend/src/scenes/feature-flags/featureFlagLogic.ts:759 14 10
findOrCreateBroadcastCohort frontend/src/scenes/feature-flags/featureFlagLogic.ts:3829 14 10
saveTagsInline frontend/src/scenes/feature-flags/featureFlagLogic.ts:5078 14 10
saveFeatureFlag frontend/src/scenes/feature-flags/featureFlagLogic.ts:3515 13 10
refreshFeatureFlagSuccess frontend/src/scenes/feature-flags/featureFlagLogic.ts:4560 12 10
loadCopyDependencyRequirements frontend/src/scenes/feature-flags/featureFlagLogic.ts:4093 11 10
createPairedSchedule frontend/src/scenes/feature-flags/featureFlagLogic.ts:4210 11 10
loadFeatureFlagSuccess frontend/src/scenes/feature-flags/featureFlagLogic.ts:4749 11 10
applyTemplate frontend/src/scenes/feature-flags/featureFlagLogic.ts:4787 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 — 3% of added code lines are comments (21 of 609)

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
frontend/src/scenes/feature-flags/featureFlagLogic.ts 20 244
products/feature_flags/backend/api/feature_flag.py 1 1

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

⚠️ Bundle size — 🔺 +4.3 KiB (+0.0%)

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

Total: 76.63 MiB · 🔺 +4.3 KiB (+0.0%)

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

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

⚠️ Eager graph — 2 over budget

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

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
1.00 MiB · 24 files no change █████████░ 87.5% of 1.14 MiB
logged-out boot: index + App + bootApp (preloaded by every page, including /login)
src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
2.90 MiB · 609 files no change ██████████ 95.5% of 3.03 MiB
🟡 authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
7.56 MiB · 2,556 files 🔺 +470 B (+0.0%) ██████████ 100.9% of 7.50 MiB
dashboard scene
src/scenes/dashboard/Dashboard.tsx
9.57 MiB · 3,635 files 🔺 +470 B (+0.0%) ██████████ 95.6% of 10.01 MiB
🟡 today home path
src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
7.95 MiB · 2,893 files 🔺 +470 B (+0.0%) ██████████ 102.8% of 7.74 MiB
events scene
src/scenes/activity/explore/EventsScene.tsx
9.12 MiB · 3,447 files 🔺 +470 B (+0.0%) ██████████ 95.1% of 9.59 MiB
web analytics scene
src/scenes/web-analytics/WebAnalyticsScene.tsx
9.97 MiB · 3,664 files 🔺 +470 B (+0.0%) █████████░ 92.0% of 10.83 MiB
replay detail scene
src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
8.68 MiB · 3,261 files 🔺 +470 B (+0.0%) █████████░ 91.6% of 9.47 MiB

🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/layout/navigation-3000/navigationLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/dashboard/dashboardLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/lemon-ui/LemonMarkdown/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/RichContentEditor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/CodeSnippet/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/taxonomy/core-filter-definitions-by-group.json stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/zod/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/billing/billingLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/ProductEmptyState/productSetupPreloadLogic.ts stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/dashboards/frontend/widgets/previews/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/wizard/frontend/runs/WizardRunDetailsDrawer.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 zod/v4/locales/de.js stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/queries/validators.js stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/scenes/project-homepage/today/TodayReportPage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/queries/Query/Query.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/queries/validators.js stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/scenes/web-analytics/LiveMetricsDashboard/LiveWebAnalyticsMetrics.tsx stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 products/product_analytics/frontend/insights/trends/WorldMap/countryVectors.tsx stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 src/scenes/settings/SettingsMap.tsx stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
🟢 @jspm/core/nodelibs/browser/fs stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx

Largest files eagerly shipped from src/index.tsx
Size File
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
24.6 KiB ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
6.3 KiB ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js
4.5 KiB ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js
3.9 KiB ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js
1.4 KiB ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
1.4 KiB src/index.tsx
1.3 KiB src/RootErrorBoundary.tsx
1.0 KiB src/lib/utils/bootTiming.ts
912 B ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
Size File
321.6 KiB ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
221.3 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.2 KiB src/products.tsx
99.2 KiB src/lib/api.ts
70.3 KiB src/lib/lemon-ui/icons/icons.tsx
38.7 KiB ../node_modules/.pnpm/@dnd-kit+core@6.0.8_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@dnd-kit/core/dist/core.esm.js
33.9 KiB ../node_modules/.pnpm/kea@4.0.0-pre.6_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js
29.2 KiB src/scenes/scenes.ts
27.6 KiB src/lib/constants.tsx
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
321.6 KiB ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
279.5 KiB src/taxonomy/core-filter-definitions-by-group.json
237.4 KiB ../packages/quill/packages/quill/dist/index.js
221.3 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
142.1 KiB ../packages/quill/packages/primitives/dist/index.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.2 KiB src/products.tsx
99.2 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/dashboard/Dashboard.tsx
Size File
321.6 KiB ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
279.5 KiB src/taxonomy/core-filter-definitions-by-group.json
237.4 KiB ../packages/quill/packages/quill/dist/index.js
221.3 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
142.1 KiB ../packages/quill/packages/primitives/dist/index.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.2 KiB src/products.tsx
99.2 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
Size File
321.6 KiB ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
279.5 KiB src/taxonomy/core-filter-definitions-by-group.json
237.4 KiB ../packages/quill/packages/quill/dist/index.js
221.3 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
142.1 KiB ../packages/quill/packages/primitives/dist/index.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.2 KiB src/products.tsx
99.2 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/activity/explore/EventsScene.tsx
Size File
321.6 KiB ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
279.5 KiB src/taxonomy/core-filter-definitions-by-group.json
237.4 KiB ../packages/quill/packages/quill/dist/index.js
221.3 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
142.1 KiB ../packages/quill/packages/primitives/dist/index.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.2 KiB src/products.tsx
99.2 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/web-analytics/WebAnalyticsScene.tsx
Size File
321.6 KiB ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
279.5 KiB src/taxonomy/core-filter-definitions-by-group.json
237.4 KiB ../packages/quill/packages/quill/dist/index.js
221.3 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
182.6 KiB src/queries/validators.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
142.1 KiB ../packages/quill/packages/primitives/dist/index.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.2 KiB src/products.tsx
99.2 KiB src/lib/api.ts
Largest files eagerly shipped from src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
Size File
321.6 KiB ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
315.9 KiB ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js
279.5 KiB src/taxonomy/core-filter-definitions-by-group.json
237.4 KiB ../packages/quill/packages/quill/dist/index.js
221.3 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.2 KiB src/products.tsx
99.2 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.25 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.25 MiB · 19 files no change ████░░░░░░ 39.3% of 5.72 MiB
Deferred (lazy) 2.10 MiB · 40 files no change n/a — loads on demand
Loader dist/toolbar.js 1.2 KiB no change █░░░░░░░░░ 6.0% of 19.5 KiB
Largest eagerly-shipped chunks
Size File
887.0 KiB dist/toolbar/toolbar-app-LXZHKE22.css
666.3 KiB dist/toolbar/chunk-chunk-TSAMOE4J.js
258.3 KiB dist/toolbar/chunk-chunk-DRFI2VGN.js
131.8 KiB dist/toolbar/chunk-chunk-FDH2IBXT.js
129.6 KiB dist/toolbar/chunk-chunk-WGAF5EFS.js
74.8 KiB dist/toolbar/toolbar-app-Q7GHQJI2.js
69.0 KiB dist/toolbar/chunk-chunk-TSAL54PB.js
35.6 KiB dist/toolbar/chunk-chunk-7DZQZN7M.js
21.7 KiB dist/toolbar/chunk-chunk-23QQX5TR.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 — 🔺 +95.9 KiB (+0.0%)

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

Total: 1018.73 MiB · 🔺 +95.9 KiB (+0.0%)

ℹ️ MCP UI apps size — 33 app(s), 27023.9 KB JS

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

App JS CSS
debug 838.9 KB 207.8 KB
action 762.9 KB 207.8 KB
action-list 829.8 KB 207.8 KB
cohort 761.9 KB 207.8 KB
cohort-list 828.8 KB 207.8 KB
email-template 761.8 KB 207.8 KB
error-details 766.8 KB 207.8 KB
error-issue 763.3 KB 207.8 KB
error-issue-list 830.4 KB 207.8 KB
experiment 827.0 KB 207.8 KB
experiment-list 830.6 KB 207.8 KB
experiment-results 829.5 KB 207.8 KB
feature-flag 829.2 KB 207.8 KB
feature-flag-list 833.0 KB 207.8 KB
feature-flag-testing 765.1 KB 207.8 KB
inline-scan 762.4 KB 207.8 KB
insight-actors 828.3 KB 207.8 KB
llm-costs 825.6 KB 207.8 KB
session-recording 764.1 KB 207.8 KB
survey 763.5 KB 207.8 KB
survey-global-stats 825.6 KB 207.8 KB
survey-list 830.5 KB 207.8 KB
survey-stats 825.6 KB 207.8 KB
trace-span 762.3 KB 207.8 KB
trace-span-list 829.7 KB 207.8 KB
vision-observation-list 828.9 KB 207.8 KB
workflow 762.2 KB 207.8 KB
workflow-list 829.2 KB 207.8 KB
workflow-setup-status 761.9 KB 207.8 KB
query-results 1080.3 KB 207.8 KB
render-ui 1153.3 KB 207.8 KB
visual-review-snapshots 766.8 KB 207.8 KB
workflow-data-suggestions 764.6 KB 207.8 KB
ℹ️ MCP agent API — agent-facing tool changes

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

Tools changed (1):

Tool Params Scopes Annotations Schema chars
cohorts-list +created_by_id +type 734 -> 994
✅ 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 ce7407b · box box-adba4d5f00f3 · ready in 1426s (push → usable) · build log · rebuilds on every push, torn down on close

@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
📝 Walkthrough

The feature flag scene adds broadcast actions to the scene actions and File menu for eligible V1 flags. The actions are disabled while cohort creation is loading, or when the flag is deleted or inactive. Feature flag logic reuses a qualifying recent cohort or creates a cohort for a persisted flag, then polls its status. After successful calculation, it tracks a broadcast audience click and navigates to Message Audience with cohort audience properties.

Priority: ⬇️ Low

Merge Risk: 🟡 Moderate · up to ffe04

Sending a broadcast from a feature flag can reuse a cohort built before the flag's targeting last changed. The announcement could then reach people who no longer have the flag, or miss people who now do. The feature is behind a flag, but this should be fixed before release, for example by not reusing cohorts created before the flag's last update.

Security Architecture Review

Security architecture risk: 🟡 Moderate · up to ffe04

The readiness checks prevent opening a broadcast while its audience is still calculating. However, audience reuse relies on an editable cohort name rather than verified flag provenance and current targeting, allowing an unrelated or outdated audience to be selected. Sending still requires a separate broadcast launch.

Retained concerns

  • Medium · security · inferred: The new reuse path treats an editable cohort-name prefix and creation age as evidence that membership represents the current feature flag. A user permitted to create or rename a recent static cohort visible to the sender can make an unrelated audience eligible for selection. A genuine snapshot also remains eligible after same-key targeting changes. The selected ID becomes the broadcast audience without checking flag provenance or configuration identity. Readiness checks cannot detect either mismatch; an incorrect recipient set can consequently reach an authorized broadcast launch.

Security review details

Security Blast Radius

  • inferred — The supported attack scope is a cohort visible within the sender’s project, not arbitrary cross-project access. Cohort-write permission is needed to plant or rename an unrelated candidate, and a broadcaster must subsequently launch. Potential impact is incorrect recipient selection and disclosure of announcement content, constrained by the existing broadcast audience limit.

Security Findings and Attack Paths

  • inferred — An eligible attacker-chosen cohort name flows from cohort creation or rename into list results, passes the new reuse predicate, and supplies the persisted ID serialized into the broadcast audience. Retrieval establishes calculation readiness, not whether those people actually match the flag. A same-key targeting change produces the corresponding stale-snapshot path without an attacker.

Trust Boundaries and Controls

  • observed — The ordinary project routes retain authentication, API scope, membership, access-level filtering, and object-permission checks. The handoff opens the existing wizard rather than sending directly; its launch path saves the draft, requests a fresh audience preview, checks the audience limit, and forwards a confirmation token for immediate batch dispatch. These controls do not verify feature-flag provenance.

Resilience and Maintainability Implications

  • observed — Preparation failure or timeout stops navigation but does not roll back cohort creation. Timeout offers a link to the retained cohort. This provides a manual recovery path; the inspected flow does not establish automatic cleanup or server-side deduplication for concurrent creators.

Hardening Proposals

  • proposed — Use authoritative snapshot provenance tied to flag identity, evaluation scope, and configuration version when authorizing reuse. If that contract is unavailable, create a new flag-derived snapshot rather than inferring membership from a display name. Revalidate that binding before accepting the cohort for broadcast preparation.
Pre-merge checks: Passed 1
Check name Status Explanation
Description check Passed The description is complete and follows the repository template. It explains the problem, user-visible changes, screenshots, test coverage and limitations, release status, docs status, agent context, …
Full details: Description check

Explanation

The description is complete and follows the repository template. It explains the problem, user-visible changes, screenshots, test coverage and limitations, release status, docs status, agent context, and implementation decisions.

✨ 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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

🧹 Nitpick comments (1)
frontend/src/scenes/feature-flags/featureFlagLogic.ts (1)

3585-3587: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Add a unit test for the new cohort-to-broadcast flow.

Test that a successful cohort response opens a broadcast with that cohort as its audience. Test that a failed request does not navigate and clears the loading state. These cases catch regressions in the new async action and its destination URL. As per coding guidelines, “All new packages and most new significant functionality should come with unit tests.”

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
  • Review profile: QUIET
  • Plan: Enterprise
  • Run ID: aebb5b96-8708-406b-8ee3-6decdfc744ee
📥 Commits

Reviewing files that changed from the base of the PR and between de168c1 and fd6bd49.

📒 Files selected for processing (2)
  • frontend/src/scenes/feature-flags/FeatureFlag.tsx
  • frontend/src/scenes/feature-flags/featureFlagLogic.ts

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 4 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.

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (1)
frontend/src/scenes/feature-flags/featureFlagLogic.ts-3596-3596 (1)

3596-3596: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Check breakpoints after each cohort request.

If the flag scene unmounts while a request is pending and the same flag mounts again before it resolves, the old loader can dispatch its success action to the remounted logic. A ready cohort can then send the user to Message Audience. Check breakpoint() after each request and before returning.

Suggested fix
                     const { cohort } = await api.featureFlags.createStaticCohort(props.id)
+                    breakpoint()
                     // The cohort fills in the background. Opening the broadcast earlier would let it send to no one.
                     const deadline = Date.now() + BROADCAST_COHORT_WAIT_MS
                     let status = await cohortsRetrieve(projectId, cohort.id as number)
+                    breakpoint()
                     while (status.is_calculating && Date.now() < deadline) {
                         await breakpoint(BROADCAST_COHORT_POLL_MS)
                         status = await cohortsRetrieve(projectId, status.id)
+                        breakpoint()
                     }
+                    breakpoint()
                     return status

ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
  • Review profile: QUIET
  • Plan: Enterprise
  • Run ID: cf7daff1-f232-444b-a630-76a62929275f
📥 Commits

Reviewing files that changed from the base of the PR and between fd6bd49 and 091c70c.

📒 Files selected for processing (2)
  • frontend/src/scenes/feature-flags/featureFlagLogic.test.ts
  • frontend/src/scenes/feature-flags/featureFlagLogic.ts

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

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

🧹 Nitpick comments (1)
frontend/src/scenes/feature-flags/featureFlagLogic.test.ts (1)

899-924: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a pending-request unmount test.

The new cases let creation and retrieval complete without unmounting the logic. Removing the cancellation breakpoints would not make those tests fail. Hold either request open, wait until it starts, unmount the logic, resolve the request, and assert that the old run does not navigate.


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
  • Review profile: QUIET
  • Plan: Enterprise
  • Run ID: 33e40f8f-0453-41b6-a58e-2a7cccf3e821
📥 Commits

Reviewing files that changed from the base of the PR and between 091c70c and 79b259f.

📒 Files selected for processing (1)
  • frontend/src/scenes/feature-flags/featureFlagLogic.ts

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

@dmarchuk
dmarchuk force-pushed the posthog/message-audience-feature-flag branch from 79b259f to bbab28b Compare October 7, 2026 02:11

@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: 0a35f91d-b77a-4cd9-b278-9c8898ded424
📥 Commits

Reviewing files that changed from the base of the PR and between bbab28b and ffe042e.

📒 Files selected for processing (2)
  • frontend/src/scenes/feature-flags/featureFlagLogic.test.ts
  • frontend/src/scenes/feature-flags/featureFlagLogic.ts

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

Base automatically changed from posthog/message-audience-plumbing to master October 7, 2026 16:30
@trunk-io

trunk-io Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

😎 Merged successfully - details.

@dmarchuk
dmarchuk force-pushed the posthog/message-audience-feature-flag branch from 4ac581f to 52ca0ac Compare October 7, 2026 16:35
@dmarchuk
dmarchuk marked this pull request as ready for review October 7, 2026 17:57
@parameterai

parameterai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Risk: No findings

The only change since the last review is regenerated OpenAPI output: a CohortsActivityRetrieveParams type for the cohort activity endpoint. It matches the endpoint's existing validated query params and no MCP tool consumes it, so no new attack surface is introduced.

Sentinel reviewed 93773a6 · Review settings

@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 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 diff adds a nosemgrep suppression for a devex rule (prefer-codegen-api-namespaced-feature_flags, defined in .semgrep/rules/devex/). Suppressing house-pattern guards is a team-devex decision, so it can't be auto-approved.

  • featureFlagLogic.ts adds a nosemgrep: prefer-codegen-api-namespaced-feature_flags marker on the api.featureFlags.createStaticCohort call. That rule lives in .semgrep/rules/devex/, and suppressing it needs team-devex sign-off.
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✓ no deny categories matched
size ✓ 166L, 2F substantive, 288L/3F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1c-medium (288L, 3F, single-area, feat)
stamphog 2.3.1 .stamphog/policy.yml @ cb83e20 · reviewed head cb83e20

@stamphog stamphog Bot removed the stamphog Request AI approval (no full review) label Oct 7, 2026
@dmarchuk dmarchuk added 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 — escalated to a human reviewer.

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

The API schema change lacks a current-head review, and the author has neither owning-team membership nor strong familiarity; the earlier cancellation concern is fixed by checks after requests. Request review from @PostHog/team-feature-flags, with Julian Bez or Gustavo H. Strassburger as suggested reviewers, before re-requesting.

  • Author wrote 0% of the modified lines and has 6 merged PRs in these paths (familiarity MODERATE).
  • Risky territory without independent assurance: Public API contracts — products/feature_flags/backend/api/feature_flag.py declares the cohort-creation endpoint's request and response schema, changing its generated client signature in products/feature_flags/frontend/generated/api.ts and adding a response type in services/mcp/src/api/generated.ts.
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✓ no deny categories matched
size ✓ 174L, 3F substantive, 670L/8F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1d-complex (670L, 8F, cross-cutting, feat)
stamphog 2.4.0 .stamphog/policy.yml @ 7c9ba91 · reviewed head 7c9ba91

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

@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 9925ce0: 3 Should fix.

# Conflicts:
#	frontend/src/scenes/feature-flags/FeatureFlag.tsx
#	frontend/src/scenes/feature-flags/featureFlagLogic.ts
#	products/feature_flags/backend/api/feature_flag.py
#	products/feature_flags/frontend/generated/api.schemas.ts
#	services/mcp/src/api/generated.ts

@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 48e28e8: 2 Should fix.

@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 0a09ea4: 1 Should fix.

@posthog

posthog Bot commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

👋 Visual changes detected for this PR.

Review and approve in PostHog Visual Review

If these changes are unexpected, they may be caused by a flaky test or a broken snapshot on master. Don't approve — rerun the job or wait for a fix.

Install the Visual Review Chrome extension to see visual review results at the top of your pull requests.

@dmarchuk

Copy link
Copy Markdown
Contributor Author

/trunk merge

@trunk-io
trunk-io Bot merged commit 2b8ca36 into master Oct 20, 2026
293 checks passed
@trunk-io
trunk-io Bot deleted the posthog/message-audience-feature-flag branch October 20, 2026 12:26
@github-project-automation github-project-automation Bot moved this from Approved to Done in Feature Flags Oct 20, 2026
@deployment-status-posthog

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

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-10-20 12:57 UTC Run
prod-us ✅ Deployed 2026-10-20 13:13 UTC Run
prod-eu ✅ Deployed 2026-10-20 13:14 UTC Run

This branch was successfully deployed

2 active (1 outdated) deployments
hogbox-preview — ce7407b4 Deployed Oct 20, 2026 by github-actions[bot]
preview-pr-113009 — a46154af Deployed Oct 10, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants