Repository navigation
chore(experiments): drop the exposure-event rollout flag check - #106333
posthog[bot] wants to merge 12 commits into
Conversation
The experiment-exposure-event flag reached full rollout, so resolve_default_exposure_event no longer evaluates it. A post-cutoff experiment now always counts exposures on $experiment_exposure, decided from the experiment start date alone. This also removes the silent inconsistency the gate carried: the check ran with only_evaluate_locally, so an unresolved local evaluation quietly returned the legacy event on the query hot path. The `team` argument becomes unused and is dropped from resolve_default_exposure_event and the two builder helpers that only threaded it through. The frontend gate and its FEATURE_FLAGS entry go with it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: 565b560e-1c84-4501-9b76-73bf9f72f117
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
🤖 CI report
|
| Function | Location | Complexity | Limit |
|---|---|---|---|
ExposureCriteriaPanel |
frontend/src/scenes/experiments/ExperimentForm/ExposureCriteriaPanel.tsx:243 |
12 | 10 |
⚠️ Duplication (Python) — 2 new duplicated blocks (worst 168 tokens)
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.
| First copy | Second copy | Lines | Tokens |
|---|---|---|---|
products/experiments/backend/hogql_queries/test/test_experiment_mean_metric_events_preaggregation.py:35 |
products/experiments/backend/hogql_queries/test/test_experiment_retention_metric_events_preaggregation.py:64 |
25 | 168 |
products/experiments/backend/hogql_queries/test/test_experiment_funnel_metric_events_preaggregation.py:54 |
products/experiments/backend/hogql_queries/test/test_experiment_mean_metric_events_preaggregation.py:36 |
25 | 164 |
✅ 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 — 21% of added code lines are comments (40 of 187)
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 |
|---|---|---|
posthog/settings/ingestion.py |
7 | 14 |
frontend/src/scenes/experiments/exposureContract.ts |
4 | 6 |
products/experiments/backend/session_buckets.py |
4 | 5 |
products/experiments/backend/session_event_deltas.py |
4 | 5 |
products/experiments/backend/test/test_freeze_exposure_clickhouse.py |
3 | 19 |
frontend/src/scenes/experiments/ExperimentForm/ExposureCriteriaPanel.tsx |
2 | 3 |
nodejs/src/ingestion/config.ts |
2 | 3 |
products/experiments/backend/hogql_queries/exposure_query_logic.py |
2 | 13 |
This check does not block merging. It updates on every push and clears when the share drops.
⚠️ Bundle size — 🔺 +691.6 KiB (+1.0%)
Uncompressed size of every built .js bundle, compared against the base branch.
Total: 70.77 MiB · 🔺 +691.6 KiB (+1.0%)
| File | Size | Δ vs base |
|---|---|---|
exporter/src/lib/hog.js |
435.1 KiB | 🔺 +435.1 KiB (new) |
posthog-app/_parent/products/canvas/frontend/scene/CanvasScene.js |
54.6 KiB | 🔺 +54.6 KiB (new) |
posthog-app/_parent/products/canvas/frontend/sidePanel/CanvasSidePanel.js |
51.4 KiB | 🔺 +51.4 KiB (new) |
exporter/_parent/products/posthog_ai/frontend/scenes/TaskTracker/TaskTracker.js |
77.3 KiB | 🔺 +33.9 KiB (+78.2%) |
posthog-app/_parent/products/posthog_ai/frontend/scenes/TaskTracker/TaskTracker.js |
79.6 KiB | 🔺 +28.4 KiB (+55.3%) |
posthog-app/_parent/products/tasks/frontend/spaces/SpaceScene.js |
87.0 KiB | 🔺 +20.4 KiB (+30.7%) |
render-query/src/render-query/render-query.js |
20.23 MiB | 🔺 +14.6 KiB (+0.1%) |
posthog-app/src/scenes/web-analytics/WebAnalyticsScene.js |
253.7 KiB | 🔺 +10.2 KiB (+4.2%) |
posthog-app/src/scenes/views/Views.js |
9.8 KiB | 🔺 +9.8 KiB (new) |
posthog-app/_parent/products/ai_observability/frontend/aiObservabilityColumnRenderers.js |
5.1 KiB | 🔺 +5.1 KiB (new) |
posthog-app/_parent/products/canvas/frontend/newCanvas/CanvasNewScene.js |
4.2 KiB | 🔺 +4.2 KiB (new) |
posthog-app/src/scenes/AuthenticatedShell.js |
320.3 KiB | 🔺 +3.9 KiB (+1.2%) |
posthog-app/_parent/products/visual_review/frontend/scenes/VisualReviewRunScene.js |
54.2 KiB | 🟢 -3.6 KiB (-6.3%) |
posthog-app/_parent/products/replay_vision/frontend/replay_scanners/ReplayScannersScene.js |
101.5 KiB | 🔺 +3.4 KiB (+3.4%) |
posthog-app/_parent/products/mcp_analytics/frontend/MCPAnalyticsScene.js |
187.8 KiB | 🔺 +3.0 KiB (+1.6%) |
exporter/_parent/products/ai_observability/frontend/aiObservabilityColumnRenderers.js |
3.0 KiB | 🔺 +3.0 KiB (new) |
posthog-app/_parent/products/workflows/frontend/Broadcasts/BroadcastScene.js |
84.0 KiB | 🔺 +2.5 KiB (+3.0%) |
exporter/src/exporter/scenes/ExporterNotebookScene.js |
3.70 MiB | 🔺 +2.4 KiB (+0.1%) |
posthog-app/_parent/products/signals/frontend/inbox/InboxScene.js |
483.2 KiB | 🔺 +1.6 KiB (+0.3%) |
posthog-app/_parent/products/autoresearch/frontend/AutoresearchPipelineScene.js |
54.7 KiB | 🔺 +1.5 KiB (+2.8%) |
exporter/_parent/products/alerts/frontend/views/EditAlertModal.js |
114.1 KiB | 🔺 +1.4 KiB (+1.2%) |
posthog-app/_parent/products/feature_flags/frontend/staff/FeatureFlagsStaffToolsScene.js |
60.3 KiB | 🔺 +1.2 KiB (+2.0%) |
posthog-app/src/lib/hog.js |
1.0 KiB | 🔺 +1.0 KiB (new) |
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.62 MiB · 22 files | 🔺 +9.2 KiB (+0.6%) | █████████░ 88.0% 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.58 MiB · 630 files | 🔺 +13.7 KiB (+0.4%) | █████████░ 88.8% of 4.03 MiB |
authenticated shell (every logged-in page)src/scenes/AuthenticatedShell.tsx |
7.76 MiB · 2,488 files | 🔺 +165.9 KiB (+2.1%) | █████████░ 93.1% of 8.34 MiB |
dashboard scenesrc/scenes/dashboard/Dashboard.tsx |
11.68 MiB · 4,476 files | (no base measurement) | █████████░ 86.7% of 13.48 MiB |
project home scenesrc/scenes/project-homepage/ProjectHomepage.tsx |
14.45 MiB · 5,366 files | (no base measurement) | █████████░ 87.9% of 16.44 MiB |
events scenesrc/scenes/activity/explore/EventsScene.tsx |
10.91 MiB · 4,135 files | (no base measurement) | █████████░ 86.3% of 12.64 MiB |
replay detail scenesrc/scenes/session-recordings/detail/SessionRecordingDetail.tsx |
13.80 MiB · 5,057 files | (no base measurement) | █████████░ 87.8% 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
🟢 [object Object] stays out of src/index.tsx
🟢 [object Object] 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
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.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 |
| 854 B | src/scenes/ChunkLoadErrorBoundary.tsx |
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
| Size | File |
|---|---|
| 306.2 KiB | ../node_modules/.pnpm/posthog-js@1.435.5_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 219.9 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 100.5 KiB | src/lib/api.ts |
| 92.0 KiB | src/products.tsx |
| 69.4 KiB | src/lib/lemon-ui/icons/icons.tsx |
| 40.1 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 |
| 28.7 KiB | src/scenes/scenes.ts |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
| Size | File |
|---|---|
| 306.2 KiB | ../node_modules/.pnpm/posthog-js@1.435.5_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 279.9 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 219.9 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 153.7 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js |
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 110.0 KiB | ../packages/quill/packages/quill/dist/index.js |
| 100.5 KiB | src/lib/api.ts |
| 93.3 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
| 92.0 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 |
|---|---|
| 315.5 KiB | ../node_modules/.pnpm/posthog-js@1.435.5_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js |
| 306.2 KiB | ../node_modules/.pnpm/posthog-js@1.435.5_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 279.9 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 219.9 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 181.8 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 |
| 110.0 KiB | ../packages/quill/packages/quill/dist/index.js |
| 100.5 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/project-homepage/ProjectHomepage.tsx
| Size | File |
|---|---|
| 315.5 KiB | ../node_modules/.pnpm/posthog-js@1.435.5_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js |
| 306.2 KiB | ../node_modules/.pnpm/posthog-js@1.435.5_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 279.9 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 219.9 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 181.8 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 |
| 110.0 KiB | ../packages/quill/packages/quill/dist/index.js |
| 100.5 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 |
|---|---|
| 315.5 KiB | ../node_modules/.pnpm/posthog-js@1.435.5_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js |
| 306.2 KiB | ../node_modules/.pnpm/posthog-js@1.435.5_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 279.9 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 219.9 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 181.8 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 |
| 110.0 KiB | ../packages/quill/packages/quill/dist/index.js |
| 100.5 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/session-recordings/detail/SessionRecordingDetail.tsx
| Size | File |
|---|---|
| 315.5 KiB | ../node_modules/.pnpm/posthog-js@1.435.5_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js |
| 306.2 KiB | ../node_modules/.pnpm/posthog-js@1.435.5_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 279.9 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 219.9 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 181.8 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 |
| 110.0 KiB | ../packages/quill/packages/quill/dist/index.js |
| 100.5 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.20 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.20 MiB · 19 files | 🔺 +8.5 KiB (+0.4%) | ████░░░░░░ 38.4% of 5.72 MiB |
| Deferred (lazy) | 2.10 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 |
|---|---|
| 832.8 KiB | dist/toolbar/toolbar-app-Z7UAQKGH.css |
| 657.2 KiB | dist/toolbar/chunk-chunk-ADXIJEJ4.js |
| 259.4 KiB | dist/toolbar/chunk-chunk-XDFV6AJ3.js |
| 138.2 KiB | dist/toolbar/chunk-chunk-IJ6DVG7Q.js |
| 131.8 KiB | dist/toolbar/chunk-chunk-FDH2IBXT.js |
| 75.2 KiB | dist/toolbar/toolbar-app-XJWG3ZHP.js |
| 69.0 KiB | dist/toolbar/chunk-chunk-TSAL54PB.js |
| 35.6 KiB | dist/toolbar/chunk-chunk-5CU3M5BS.js |
| 21.0 KiB | dist/toolbar/chunk-chunk-7DSGG7S4.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 — 🔺 +4.67 MiB (+0.5%)
Total size of the built frontend/dist folder (all assets), compared against the base branch.
Total: 966.77 MiB · 🔺 +4.67 MiB (+0.5%)
ℹ️ MCP UI apps size — 33 app(s), 17633.1 KB JS
Built size of each MCP UI app (main.js + styles.css).
| App | JS | CSS |
|---|---|---|
| debug | 597.9 KB | 199.4 KB |
| action | 454.1 KB | 199.4 KB |
| action-list | 564.2 KB | 199.4 KB |
| cohort | 453.1 KB | 199.4 KB |
| cohort-list | 563.2 KB | 199.4 KB |
| email-template | 452.9 KB | 199.4 KB |
| error-details | 469.6 KB | 199.4 KB |
| error-issue | 454.5 KB | 199.4 KB |
| error-issue-list | 564.8 KB | 199.4 KB |
| experiment | 561.3 KB | 199.4 KB |
| experiment-list | 564.9 KB | 199.4 KB |
| experiment-results | 566.3 KB | 199.4 KB |
| feature-flag | 566.8 KB | 199.4 KB |
| feature-flag-list | 570.5 KB | 199.4 KB |
| feature-flag-testing | 457.3 KB | 199.4 KB |
| inline-scan | 453.6 KB | 199.4 KB |
| insight-actors | 562.3 KB | 199.4 KB |
| invite-email-preview | 452.3 KB | 199.4 KB |
| llm-costs | 559.3 KB | 199.4 KB |
| session-recording | 455.3 KB | 199.4 KB |
| survey | 454.7 KB | 199.4 KB |
| survey-global-stats | 561.9 KB | 199.4 KB |
| survey-list | 564.9 KB | 199.4 KB |
| survey-stats | 561.9 KB | 199.4 KB |
| trace-span | 453.5 KB | 199.4 KB |
| trace-span-list | 564.1 KB | 199.4 KB |
| vision-observation-list | 563.3 KB | 199.4 KB |
| workflow | 453.4 KB | 199.4 KB |
| workflow-list | 563.5 KB | 199.4 KB |
| loops-review | 457.8 KB | 199.4 KB |
| query-results | 774.1 KB | 199.4 KB |
| render-ui | 858.1 KB | 199.4 KB |
| visual-review-snapshots | 457.9 KB | 199.4 KB |
⚠️ Playwright — 1 flaky
🎭 Playwright report · View test results →
- Creating a SQL insight with a variable and overriding it on a dashboard (chromium)
These issues are not necessarily caused by your changes.
Annoyed by this section? Help fix flakies and failures and it will go green!
⚠️ Backend coverage — 96.0% of changed backend lines covered — 2 uncovered
🧪 Backend test coverage
Patch coverage — changed backend lines (products + core): ███████████████████░ 96.0% (64 / 66)
| File | Patch | Uncovered changed lines |
|---|---|---|
posthog/settings/ingestion.py |
60.0% | 170–171 |
🤖 Agents: add a test only if an uncovered line exposes a realistic regression that existing tests miss. Otherwise explain why no new test is needed under "How did you test this code?". Gap list: the patch-coverage artifact on this run (gh run download 177721791857876 -n patch-coverage), or the coverage-data block at the end of this comment.
Per-product line coverage (touched products)
| Product | Coverage | Lines |
|---|---|---|
platform_features |
██░░░░░░░░░░░░░░░░░░ 12.1% |
7 / 58 |
demo |
███████████░░░░░░░░░ 53.4% |
1,445 / 2,707 |
data_tools |
████████████░░░░░░░░ 61.2% |
90 / 147 |
warehouse_sources_queue |
█████████████░░░░░░░ 65.9% |
1,611 / 2,446 |
ai_gateway |
███████████████░░░░░ 75.0% |
9 / 12 |
aeo |
███████████████░░░░░ 76.3% |
617 / 809 |
batch_exports |
████████████████░░░░ 81.2% |
21,571 / 26,552 |
apm |
█████████████████░░░ 84.1% |
1,306 / 1,553 |
cdp |
██████████████████░░ 88.3% |
4,559 / 5,164 |
ml_inference |
██████████████████░░ 88.8% |
539 / 607 |
mcp_analytics |
██████████████████░░ 89.2% |
5,038 / 5,651 |
product_tours |
██████████████████░░ 89.3% |
1,340 / 1,500 |
dashboards |
██████████████████░░ 89.6% |
6,924 / 7,727 |
notebooks |
██████████████████░░ 90.2% |
15,304 / 16,971 |
signals |
██████████████████░░ 90.4% |
59,704 / 66,064 |
cohorts |
██████████████████░░ 90.5% |
8,534 / 9,434 |
data_warehouse |
██████████████████░░ 90.6% |
14,375 / 15,860 |
streamlit_apps |
██████████████████░░ 90.8% |
2,684 / 2,956 |
managed_warehouse |
██████████████████░░ 91.0% |
10,252 / 11,263 |
today |
██████████████████░░ 91.1% |
891 / 978 |
data_modeling |
██████████████████░░ 91.2% |
10,562 / 11,584 |
tasks |
██████████████████░░ 91.3% |
79,044 / 86,610 |
exports |
██████████████████░░ 91.7% |
9,684 / 10,566 |
business_knowledge |
██████████████████░░ 92.0% |
8,447 / 9,181 |
engineering_analytics |
██████████████████░░ 92.2% |
11,497 / 12,475 |
ai_training |
██████████████████░░ 92.2% |
356 / 386 |
conversations |
███████████████████░ 92.6% |
29,137 / 31,467 |
early_access_features |
███████████████████░ 92.6% |
1,339 / 1,446 |
managed_migrations |
███████████████████░ 92.7% |
1,581 / 1,705 |
stamphog |
███████████████████░ 92.8% |
8,109 / 8,742 |
visual_review |
███████████████████░ 92.9% |
9,534 / 10,265 |
canvas |
███████████████████░ 92.9% |
7,155 / 7,703 |
approvals |
███████████████████░ 93.0% |
3,974 / 4,271 |
mcp_registry |
███████████████████░ 93.1% |
1,670 / 1,794 |
notifications |
███████████████████░ 93.2% |
1,144 / 1,228 |
error_tracking |
███████████████████░ 93.2% |
16,370 / 17,557 |
surveys |
███████████████████░ 93.4% |
6,644 / 7,113 |
slack_app |
███████████████████░ 93.6% |
14,560 / 15,557 |
autoresearch |
███████████████████░ 93.6% |
8,837 / 9,442 |
context_layer |
███████████████████░ 93.8% |
3,415 / 3,639 |
web_analytics |
███████████████████░ 93.9% |
23,680 / 25,229 |
billing_alerts |
███████████████████░ 94.1% |
2,094 / 2,226 |
mcp_store |
███████████████████░ 94.3% |
8,959 / 9,501 |
ai_observability |
███████████████████░ 94.6% |
24,974 / 26,409 |
wizard |
███████████████████░ 94.7% |
6,150 / 6,496 |
alerts |
███████████████████░ 94.7% |
9,321 / 9,845 |
workflows |
███████████████████░ 94.7% |
15,164 / 16,011 |
reminders |
███████████████████░ 94.8% |
760 / 802 |
review_hog |
███████████████████░ 95.0% |
11,750 / 12,362 |
annotations |
███████████████████░ 95.1% |
817 / 859 |
endpoints |
███████████████████░ 95.1% |
9,231 / 9,703 |
customer_analytics |
███████████████████░ 95.2% |
25,992 / 27,302 |
legal_documents |
███████████████████░ 95.2% |
2,311 / 2,427 |
marketing_analytics |
███████████████████░ 95.3% |
19,450 / 20,413 |
posthog_ai |
███████████████████░ 95.3% |
2,491 / 2,614 |
actions |
███████████████████░ 95.5% |
756 / 792 |
logs |
███████████████████░ 95.5% |
15,468 / 16,200 |
experiments |
███████████████████░ 95.5% |
32,937 / 34,486 |
data_catalog |
███████████████████░ 95.6% |
4,402 / 4,606 |
tracing |
███████████████████░ 95.6% |
3,536 / 3,699 |
replay_vision |
███████████████████░ 95.6% |
29,371 / 30,708 |
growth |
███████████████████░ 95.7% |
11,381 / 11,888 |
skills |
███████████████████░ 95.8% |
6,972 / 7,274 |
messaging |
███████████████████░ 95.9% |
3,824 / 3,989 |
product_analytics |
███████████████████░ 96.0% |
28,521 / 29,696 |
revenue_analytics |
███████████████████░ 96.4% |
1,889 / 1,959 |
user_interviews |
███████████████████░ 96.5% |
2,870 / 2,974 |
feature_flags |
███████████████████░ 96.6% |
26,904 / 27,856 |
access_control |
███████████████████░ 96.7% |
7,738 / 8,005 |
warehouse_sources |
███████████████████░ 97.3% |
468,810 / 481,716 |
data_quality |
████████████████████ 97.5% |
7,701 / 7,895 |
links |
████████████████████ 97.9% |
234 / 239 |
security |
████████████████████ 98.0% |
1,283 / 1,309 |
metrics |
████████████████████ 98.0% |
4,252 / 4,338 |
analytics_platform |
████████████████████ 98.3% |
2,784 / 2,833 |
pulse |
████████████████████ 98.5% |
2,046 / 2,078 |
live_debugger |
████████████████████ 99.2% |
626 / 631 |
field_notes |
████████████████████ 99.4% |
172 / 173 |
Report-only. Patch coverage = changed backend lines covered vs origin/master. Sorted lowest first.
Known gaps: lines covered only by Temporal tests show as uncovered; core line numbers may drift if master changed the same file.
resolvedExposureEvent now returns $experiment_exposure for an experiment with no server-resolved event, instead of taking a per-call-site fallback. Such an experiment is always a draft built locally, which would start after the cutoff, so the criteria panel no longer has to override the shared helper while the metric form and the recordings linkability check name the legacy event for the same draft. Also hoists the duplicated default exposure config in get_exposure_config_params_for_builder and corrects comments that still described the removed flag as a per-team rollout. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: 565b560e-1c84-4501-9b76-73bf9f72f117
|
Follow-up commit from the cleanup pass (eda1bc1):
Verified after this commit: frontend |
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: PostHog/posthog/.coderabbit.yaml Review profile: QUIET Plan: Enterprise Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (4)
Included review availability: Your plan provides up to 12 included reviews per hour; 6 remain after this review. 📝 WalkthroughWalkthroughDefault exposure-event selection now uses experiment start date rather than team feature-flag state. Backend callers and exposure configuration helpers no longer pass a team to resolve the default event. The frontend form and exposure contract use the updated event resolution, and tests cover cutoff-based selection and draft fallbacks. Priority: ⚪ Not assessed Merge Risk: ⚪ Minimal · up to The API description now matches date-based exposure-event selection. No outstanding issue identified here prevents merging after normal checks. Security Architecture ReviewSecurity architecture risk: 🟡 Moderate · up to Experiment results and the ability to freeze exposure could be affected if some teams do not receive the event that queries now select. Whether that configuration exists in production is unverified; no security vulnerability was established. Retained concerns
Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 1✅ Passed checks (1 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to GitHub limitations.
🟡 Minor · Update the stale resolved_exposure_event help_text. · serializers.py:440-448
products/experiments/backend/presentation/serializers.py:440-448
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winUpdate the stale
resolved_exposure_eventhelp_text.This help_text still says
$experiment_exposureapplies "once the team is in the rollout and the experiment started at or after the cutoff."resolve_default_exposure_eventno longer checks any team rollout state — it resolves purely from the start date (see line 580 andresolve_default_exposure_eventinexposure_query_logic.py). This text feeds the generated OpenAPI schema and misleads API and MCP consumers about the field's actual semantics.Drop the rollout clause so the text matches the date-only resolution.
📝 Proposed fix
resolved_exposure_event = serializers.SerializerMethodField( help_text=( "The event exposures are actually counted on when the experiment doesn't configure a " - "custom one — `$feature_flag_called`, or `$experiment_exposure` once the team is in the " - "rollout and the experiment started at or after the cutoff. Resolved server-side so " - "clients display the same event the results queries read. For a draft, this is what the " - "experiment would resolve to if launched now." + "custom one — `$feature_flag_called`, or `$experiment_exposure` once the experiment " + "started at or after the cutoff. Resolved server-side so clients display the same event " + "the results queries read. For a draft, this is what the experiment would resolve to if " + "launched now." ) )
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
Review profile: QUIET
Plan: Enterprise
Run ID: 94174ca6-5607-483e-bb9c-7cae6cbbc09b
📒 Files selected for processing (25)
frontend/src/lib/constants.tsxfrontend/src/scenes/experiments/ExperimentForm/ExposureCriteriaPanel.tsxfrontend/src/scenes/experiments/exposureContract.test.tsfrontend/src/scenes/experiments/exposureContract.tsfrontend/src/types.tsproducts/experiments/backend/experiment_service.pyproducts/experiments/backend/hogql_queries/experiment_exposures_query_runner.pyproducts/experiments/backend/hogql_queries/experiment_query_builder.pyproducts/experiments/backend/hogql_queries/experiment_query_runner.pyproducts/experiments/backend/hogql_queries/exposure_query_logic.pyproducts/experiments/backend/hogql_queries/test/experiment_query_runner/test_experiment_exposures_query_runner.pyproducts/experiments/backend/hogql_queries/test/test_experiment_exposure_preaggregation.pyproducts/experiments/backend/hogql_queries/test/test_experiment_funnel_metric_events_preaggregation.pyproducts/experiments/backend/hogql_queries/test/test_experiment_mean_metric_events_preaggregation.pyproducts/experiments/backend/hogql_queries/test/test_experiment_retention_metric_events_preaggregation.pyproducts/experiments/backend/presentation/serializers.pyproducts/experiments/backend/replay_linkage.pyproducts/experiments/backend/session_buckets.pyproducts/experiments/backend/session_event_deltas.pyproducts/experiments/backend/session_exposure.pyproducts/experiments/backend/setup_context.pyproducts/experiments/backend/test/test_freeze_exposure_clickhouse.pyproducts/experiments/backend/test/test_presentation_api.pyproducts/experiments/backend/test/test_session_buckets_api.pyproducts/experiments/backend/test/test_session_exposure.py
💤 Files with no reviewable changes (1)
- frontend/src/lib/constants.tsx
Included review availability: Your plan provides up to 12 included reviews per hour; 2 remain after this review.
|
Two Jest fixtures built an experiment without resolved_exposure_event, so they took the draft fallback and now resolve $experiment_exposure. Real experiments from the API always carry the field, so the fixtures now set it explicitly to the event their assertions mean. Also drops the team-rollout clause from the resolved_exposure_event help_text, which fed the generated OpenAPI schema, and regenerates the types. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: 565b560e-1c84-4501-9b76-73bf9f72f117
|
Pushed b84ec88. CodeRabbit — fixed. The Jest (app-1, app-2) — fixed. Two fixtures ( Visual review (2 changed) — not from this PR. Both snapshots are |
|
👋 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. |
resolvedExposureEvent fell back to $experiment_exposure for any payload missing resolved_exposure_event. That assumed every such payload is a locally-built draft, but ExperimentBasicSerializer omits the field, so a list-sourced experiment took the fallback too — and a pre-cutoff experiment was then named on the wrong event. The shared helper takes the legacy event again, and only the creation form, which knows its experiment is new, passes $experiment_exposure explicitly. This reverts the two test fixtures that were edited to accommodate the earlier fallback. Three fixtures needing a server-resolved field they never carried was the signal that the helper default was wrong, not the fixtures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: 565b560e-1c84-4501-9b76-73bf9f72f117
…ve-the-fully-ee3bbb Generated-By: PostHog Desktop Task-Id: 565b560e-1c84-4501-9b76-73bf9f72f117
…g/choreexperiments-remove-the-fully-ee3bbb # Conflicts: # products/experiments/backend/hogql_queries/experiment_query_builder.py # products/experiments/backend/hogql_queries/exposure_query_logic.py
Dropping the flag check moved every deployment to $experiment_exposure for experiments started after the cutoff. Only PostHog Cloud ingests that event: the plugin server copies $feature_flag_called into it for the teams in EXPERIMENT_EXPOSURE_DUPLICATION_TEAMS, which is empty by default, and no SDK sends it. A self-hosted install would count zero exposures, and freezing enrollment would snapshot nobody. The flag never resolved on self-hosted installs, so they always counted $feature_flag_called. The new EXPERIMENT_EXPOSURE_EVENT_INGESTED setting keeps that: it defaults to false in hobby run mode and true on Cloud, in local dev and in tests. The creation form names the legacy event on hobby to match.
Local dev resolves post-cutoff experiments to $experiment_exposure like Cloud does, but its ingestion did not make the event, so new local experiments showed no exposures. Default EXPERIMENT_EXPOSURE_DUPLICATION_TEAMS to all teams in the dev env, the same way PROPERTY_DEFS_CONSUMER_ENABLED_TEAMS does.
- Drop a feature_enabled mock that only existed for the removed flag. It now switched unrelated flags on for the test. - Say "cutoff" instead of "rollout" in comments and the session replays skill. - Correct why the session cache keys carry the resolved exposure event: it follows the start date and the EXPERIMENT_EXPOSURE_EVENT_INGESTED setting, which can change while an entry is warm.
|
Risk: No findings This increment replaces the boolean EXPERIMENT_EXPOSURE_EVENT_INGESTED setting with a configurable EXPERIMENT_EXPOSURE_EVENT_CUTOFF datetime, and makes the nodejs ingestion duplicate $feature_flag_called into $experiment_exposure for all teams on every deployment instead of dev-only. I traced every surface in the delta — event resolution, HogQL builders, cache keys, the nodejs copy step, and the frontend contract — and found no security issue: the resolved event is always one of two fixed constants, cache keys still include it, and no attacker-controlled input reaches the resolution logic across a trust boundary. Sentinel reviewed |
👀 Auto-assigned reviewersThese soft owners were skipped because they only have minor changes here. Nothing blocks merge, so self-assign if you'd like a look:
Soft owners come from each directory's |
The cutoff moves from a constant to the EXPERIMENT_EXPOSURE_EVENT_CUTOFF setting, an ISO 8601 value read from the environment. It defaults to 2026-09-01 UTC, when PostHog Cloud started copying $feature_flag_called into $experiment_exposure, and Cloud does not override it. A self-hosted install that starts the copy later sets it to that time, so its older experiments keep counting $feature_flag_called. This replaces EXPERIMENT_EXPOSURE_EVENT_INGESTED. Ingestion now makes the copy on every deployment, so self-hosted installs no longer need a separate switch, and the creation form names $experiment_exposure everywhere again.
EXPERIMENT_EXPOSURE_DUPLICATION_TEAMS now defaults to all teams on every deployment, so self-hosted installs, local dev and tests make $experiment_exposure the same way PostHog Cloud does. generate_experiment_data drops its --exposure-event option. The data it captures goes through ingestion, which now makes the copy, so the option would count every exposure twice.
|
This PR hasn't seen activity in a week! Should it be merged, closed, or further worked on? If you want to keep it open, please remove the |
|
This PR was closed due to lack of activity. Feel free to reopen if it's still relevant. |
Problem
only_evaluate_locally, and an unresolved local evaluation silently fell back to$feature_flag_called. The same experiment could be counted on either event depending on whether local definitions happened to be loaded.Origin
Changes
$experiment_exposure.resolve_default_exposure_eventdecides from the start date, with no flag evaluation on the query path.EXPERIMENT_EXPOSURE_EVENT_CUTOFFsetting, an ISO 8601 value from the environment. It defaults to 2026-09-01 UTC, when PostHog Cloud started the copy, and Cloud does not override it.$feature_flag_calledinto$experiment_exposurefor all teams on every deployment: self-hosted, local dev and tests. Before, only Cloud setEXPERIMENT_EXPOSURE_DUPLICATION_TEAMS, and new self-hosted experiments would have counted an event that never arrived.generate_experiment_datadrops its--exposure-eventoption, because ingestion now makes the copy and the option would count every exposure twice.$experiment_exposure, and now passes that event explicitly instead of reading it from a flag.resolvedExposureEventkeeps$feature_flag_calledas its fallback. This is the one bullet with behavioral risk, so it is worth checking: the fallback fires for any payload missingresolved_exposure_event, andExperimentBasicSerializeromits the field, so list-sourced experiments hit it. A pre-cutoff experiment must not be named on the new event, so only the creation form overrides the fallback.teambecame unused and is dropped fromresolve_default_exposure_event,resolve_exposure_config_for_builder, andget_exposure_config_params_for_builder; theFEATURE_FLAGSentry, theEXPERIMENT_EXPOSURE_EVENT_FLAGconstant and a test mock that only served the flag are gone; theresolved_exposure_eventhelp_textdrops its team-rollout clause, and the generated OpenAPI types follow it; tests read the cutoff from settings; comments and the session replays skill that described the gate as a rollout now describe the cutoff.Warning
A self-hosted install starts making
$experiment_exposurewhen it upgrades to this change. With the default cutoff, an experiment it started between 2026-09-01 and the upgrade counts only the exposures after the upgrade. SettingEXPERIMENT_EXPOSURE_EVENT_CUTOFFto the upgrade time, for example2026-10-15T00:00:00Z, keeps those experiments on$feature_flag_called.The PostHog flag itself is untouched. It can be archived once this deploys.
Nothing looks different on Cloud. On a self-hosted install, the exposure criteria panel now names
$experiment_exposurefor a new draft, which matches what the backend counts.How did you test this code?
products/experiments/backendsuite and the experiments Jest suites locally. CI reports the counts with more authority, so they are not repeated here.$feature_flag_called. They passed before only because an unpatchedposthoganalytics.feature_enabledreturned nothing in tests, which is exactly the fallback this PR removes._expose_personin the freeze test now defaults to$experiment_exposure, and the two cases that mean "the legacy event specifically" say so explicitly.*_flag_disabledcases are removed rather than replaced. They asserted a branch that no longer exists; the cutoff cases they sat beside still cover both events.exposureContract.test.tscovers the fallback three ways: no field, caller-supplied override, and a server-resolved value winning over both. The regression it catches is a pre-cutoff experiment being named on$experiment_exposure.$feature_flag_calledpass unmodified, which is the check on the fallback bullet above.TestResolveDefaultExposureEventoverrides the cutoff and checks both sides of it. It catches a resolver that ignores the setting, which would put a self-hosted install's older experiments on an event it did not have yet.products/experiments/backendfails 92 tests, and the same 92 fail on a clean master worktree. The causes are local: ClickHouse lacks theaggregate_funnel_arrayUDF, and data warehouse snapshots carry a different storage path. No other test fails.@posthog/hogvmmodule errors in an unrelated debug scene.Release status
Automatic notifications
Docs update
None in this repository. No doc under
docs/lists self-hosted environment variables, soEXPERIMENT_EXPOSURE_EVENT_CUTOFFis documented by the comment on the setting and the warning above.🤖 Agent context
Autonomy: Fully autonomous
Agent: Claude Code, Opus 5
Skills invoked:
/cleaning-up-stale-feature-flags(which set the code-first ordering and the "retained path" rule),/writing-pr-descriptions, and the repo's Simplified Technical English guidance.Decisions along the way:
EXPERIMENT_EXPOSURE_EVENT_CUTOFF. That does not change the fix.teamparameter was a judgment call. It widens the diff to a dozen call sites but avoids leaving plumbing that exists only to feed a deleted flag check.resolvedExposureEventto fall back to$experiment_exposure, on the assumption that only a local draft can lack the field. That was wrong, for the reason given under Changes. Three test fixtures needing a field they never carried was the signal, and the fallback is now back to the legacy event. An earlier comment on this PR describes that intermediate state and is obsolete.default_exposure_eventstays in the session bucket and delta cache keys. It follows the start date and theEXPERIMENT_EXPOSURE_EVENT_INGESTEDsetting, and the setting can change while an entry is warm.resolved_exposure_eventhelp_text, which feeds the generated OpenAPI schema. Fixed, and the generated types were regenerated with it.scenes-app-signals-artefactloglistvisual diffs came from a stale baseline, not from this PR, which touches no signals code. The branch predated #106388, which pins the date in those stories. Master is merged in, so the next run rebuilds them against the pinned story.gh pr list --state open --searchover open PRs (drafts included) found no PR removing this flag.Follow-up commits: Claude Code, Claude Opus 5.5 (
claude-opus-5-5), directed by a reviewer who asked for a master merge, a review and fixes.Skills invoked:
/writing-tests,/writing-code-comments,/writing-skills,/reviewing-with-coderabbit,/writing-pr-descriptions.experiment_query_builder.pyandexposure_query_logic.py, kept this PR's resolver and master's newer docstring.$feature_flag_calledthrough anEXPERIMENT_EXPOSURE_EVENT_INGESTEDsetting. The reviewer chose a different design: copy the event on every deployment, and let self-hosted installs move the cutoff instead. That setting is gone.--deep, ran on the first fix with one finding: keep the resolved event in the session bucket cache key, because a setting can change it. Fixed. The point still holds for the configurable cutoff.Created with PostHog Desktop from this inbox report.