Conversation
🤖 CI report
|
| First copy | Second copy | Lines | Tokens |
|---|---|---|---|
products/tasks/backend/logic/services/agent_server_launcher.py:671 |
products/tasks/backend/logic/services/sandbox.py:649 |
40 | 278 |
products/tasks/backend/logic/services/agent_server_launcher.py:304 |
products/tasks/backend/logic/services/docker_sandbox.py:950 |
38 | 246 |
products/warehouse_sources/backend/temporal/data_imports/pipelines/core/account_property_row_sink_test.py:129 |
products/warehouse_sources/backend/temporal/data_imports/pipelines/core/account_property_row_sink_test.py:251 |
44 | 238 |
products/warehouse_sources/backend/temporal/data_imports/sources/fintoc/source.py:1 |
products/warehouse_sources/backend/temporal/data_imports/sources/woocommerce/source.py:1 |
29 | 195 |
products/warehouse_sources/backend/temporal/data_imports/sources/fintoc/source.py:5 |
products/warehouse_sources/backend/temporal/data_imports/sources/instantly/source.py:8 |
25 | 187 |
products/warehouse_sources/backend/temporal/data_imports/sources/fintoc/source.py:5 |
products/warehouse_sources/backend/temporal/data_imports/sources/paymongo/source.py:5 |
25 | 187 |
products/warehouse_sources/backend/temporal/data_imports/sources/fintoc/source.py:5 |
products/warehouse_sources/backend/temporal/data_imports/sources/postmark/source.py:3 |
24 | 182 |
products/warehouse_sources/backend/temporal/data_imports/sources/fintoc/source.py:5 |
products/warehouse_sources/backend/temporal/data_imports/sources/webflow/source.py:5 |
24 | 182 |
products/canvas/backend/presentation/serializers.py:1545 |
products/tasks/backend/presentation/serializers.py:3145 |
13 | 168 |
products/warehouse_sources/backend/temporal/data_imports/sources/growthbook/source.py:1 |
products/warehouse_sources/backend/temporal/data_imports/sources/productive/source.py:1 |
23 | 168 |
products/warehouse_sources/backend/temporal/data_imports/sources/growthbook/source.py:1 |
products/warehouse_sources/backend/temporal/data_imports/sources/sevdesk/source.py:2 |
23 | 168 |
products/warehouse_sources/backend/temporal/data_imports/sources/docuseal/source.py:1 |
products/warehouse_sources/backend/temporal/data_imports/sources/revolut_merchant/source.py:1 |
21 | 166 |
products/warehouse_sources/backend/temporal/data_imports/sources/givebutter/source.py:1 |
products/warehouse_sources/backend/temporal/data_imports/sources/heygen/source.py:1 |
21 | 162 |
products/warehouse_sources/backend/temporal/data_imports/sources/givebutter/source.py:1 |
products/warehouse_sources/backend/temporal/data_imports/sources/kapa_ai/source.py:1 |
21 | 162 |
products/warehouse_sources/backend/temporal/data_imports/sources/givebutter/source.py:1 |
products/warehouse_sources/backend/temporal/data_imports/sources/lexware_office/source.py:1 |
21 | 162 |
products/warehouse_sources/backend/temporal/data_imports/sources/givebutter/source.py:1 |
products/warehouse_sources/backend/temporal/data_imports/sources/retell_ai/source.py:1 |
21 | 162 |
products/warehouse_sources/backend/temporal/data_imports/sources/givebutter/source.py:1 |
products/warehouse_sources/backend/temporal/data_imports/sources/sim/source.py:1 |
21 | 162 |
products/warehouse_sources/backend/temporal/data_imports/sources/browse_ai/source.py:22 |
products/warehouse_sources/backend/temporal/data_imports/sources/fintoc/source.py:10 |
22 | 159 |
products/warehouse_sources/backend/temporal/data_imports/sources/browse_ai/source.py:24 |
products/warehouse_sources/backend/temporal/data_imports/sources/revenuecat/source.py:16 |
20 | 159 |
products/warehouse_sources/backend/temporal/data_imports/sources/companycam/source.py:1 |
products/warehouse_sources/backend/temporal/data_imports/sources/givebutter/source.py:1 |
21 | 159 |
products/warehouse_sources/backend/temporal/data_imports/sources/companycam/source.py:3 |
products/warehouse_sources/backend/temporal/data_imports/sources/growthbook/source.py:5 |
19 | 158 |
products/warehouse_sources/backend/temporal/data_imports/sources/givebutter/source.py:1 |
products/warehouse_sources/backend/temporal/data_imports/sources/papersign/source.py:1 |
21 | 157 |
products/warehouse_sources/backend/temporal/data_imports/sources/givebutter/source.py:1 |
products/warehouse_sources/backend/temporal/data_imports/sources/turso/source.py:1 |
21 | 157 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/adroll/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/aha_ideas/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/aircall/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/airwallex/source.py:17 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/alegra/source.py:20 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/alguna/source.py:16 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/apitally/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/apollo/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/asaas/source.py:17 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/asana/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/autumn/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/avalara/source.py:17 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/azure_devops/source.py:17 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/babelforce/source.py:16 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/back_market/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/bamboohr/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/beehiiv/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/bettermode/source.py:17 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/bigeye/source.py:16 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/bill_com/source.py:17 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/billomat/source.py:20 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/bluesky/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/braintree/source.py:21 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/brevo/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/bugherd/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/buildbetter/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/buttondown/source.py:16 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/campaign_monitor/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/canvas_lms/source.py:16 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/capsule_crm/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/cast_ai/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/census/source.py:18 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/chargebee/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/chartmogul/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/clari/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/cliniko/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/clockify/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/cloudability/source.py:17 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/cloudinary/source.py:17 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/cloudsmith/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/codemagic/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/codescene/source.py:15 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/commercetools/source.py:17 |
16 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/browse_ai/source.py:26 |
products/warehouse_sources/backend/temporal/data_imports/sources/zapsign/source.py:13 |
18 | 144 |
products/warehouse_sources/backend/temporal/data_imports/sources/adjust/source.py:20 |
products/warehouse_sources/backend/temporal/data_imports/sources/buy_me_a_coffee/source.py:21 |
16 | 141 |
products/warehouse_sources/backend/temporal/data_imports/sources/browse_ai/source.py:27 |
products/warehouse_sources/backend/temporal/data_imports/sources/calendly/source.py:32 |
17 | 141 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:17 |
products/warehouse_sources/backend/temporal/data_imports/sources/adjust/source.py:21 |
15 | 140 |
products/warehouse_sources/backend/temporal/data_imports/sources/convertkit/convertkit.py:4 |
products/warehouse_sources/backend/temporal/data_imports/sources/gainsight_px/gainsight_px.py:7 |
19 | 140 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/asknicely/source.py:16 |
16 | 139 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/churnkey/source.py:15 |
16 | 139 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/circleci/source.py:15 |
16 | 139 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/codecov/source.py:17 |
16 | 139 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:16 |
products/warehouse_sources/backend/temporal/data_imports/sources/coinmarketcap/source.py:15 |
16 | 139 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:17 |
products/warehouse_sources/backend/temporal/data_imports/sources/canny/source.py:18 |
15 | 139 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/aftership/source.py:20 |
14 | 137 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/apify_dataset/source.py:22 |
14 | 137 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/app_store_connect/source.py:29 |
14 | 137 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_organizations/source.py:20 |
14 | 137 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/bigcommerce/source.py:18 |
14 | 137 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/browser_use/source.py:18 |
14 | 137 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/buildkite/source.py:23 |
14 | 137 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/cal_com/source.py:24 |
14 | 137 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/campfire/source.py:18 |
14 | 137 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/clickup/source.py:18 |
14 | 137 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/cloudzero/source.py:24 |
14 | 137 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/companycam/source.py:8 |
14 | 135 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/ecb_data_portal/source.py:6 |
14 | 135 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:20 |
products/warehouse_sources/backend/temporal/data_imports/sources/agilecrm/source.py:16 |
12 | 135 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:20 |
products/warehouse_sources/backend/temporal/data_imports/sources/aha/source.py:16 |
12 | 135 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_ads/source.py:20 |
14 | 132 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/amplitude/source.py:19 |
14 | 132 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/anthropic/source.py:27 |
14 | 132 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/appdynamics/source.py:25 |
14 | 132 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/automox/source.py:19 |
14 | 132 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/aviationstack/source.py:17 |
14 | 132 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/awin/source.py:22 |
14 | 132 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/azure_cost_management/source.py:16 |
14 | 132 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/concord/source.py:10 |
14 | 130 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:20 |
products/warehouse_sources/backend/temporal/data_imports/sources/clerk/source.py:17 |
12 | 130 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:20 |
products/warehouse_sources/backend/temporal/data_imports/sources/close/source.py:17 |
12 | 130 |
products/warehouse_sources/backend/temporal/data_imports/sources/givebutter/source.py:1 |
products/warehouse_sources/backend/temporal/data_imports/sources/ynab/source.py:1 |
18 | 126 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/acculynx.py:32 |
products/warehouse_sources/backend/temporal/data_imports/sources/firecrawl/firecrawl.py:12 |
18 | 119 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/acculynx.py:32 |
products/warehouse_sources/backend/temporal/data_imports/sources/snyk/snyk.py:16 |
18 | 118 |
products/warehouse_sources/backend/temporal/data_imports/sources/brex/brex.py:8 |
products/warehouse_sources/backend/temporal/data_imports/sources/gitbook/gitbook.py:6 |
18 | 117 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/acculynx.py:33 |
products/warehouse_sources/backend/temporal/data_imports/sources/openaq/openaq.py:15 |
17 | 116 |
products/warehouse_sources/backend/temporal/data_imports/sources/ably/source.py:29 |
products/warehouse_sources/backend/temporal/data_imports/sources/browse_ai/source.py:30 |
13 | 115 |
products/warehouse_sources/backend/temporal/data_imports/sources/ably/source.py:31 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/source.py:21 |
11 | 115 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/acculynx.py:35 |
products/warehouse_sources/backend/temporal/data_imports/sources/eventzilla/eventzilla.py:36 |
15 | 114 |
posthog/models/property/util.py:138 |
posthog/models/test/test_event_model.py:356 |
11 | 113 |
products/warehouse_sources/backend/temporal/data_imports/sources/buy_me_a_coffee/buy_me_a_coffee.py:11 |
products/warehouse_sources/backend/temporal/data_imports/sources/descope/descope.py:9 |
11 | 107 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/acculynx.py:32 |
products/warehouse_sources/backend/temporal/data_imports/sources/campaign_monitor/campaign_monitor.py:18 |
12 | 105 |
products/workflows/backend/models/hog_flow/hog_flow.py:150 |
products/workflows/backend/models/hog_flow/hog_flow_template.py:45 |
14 | 105 |
products/warehouse_sources/backend/temporal/data_imports/sources/algolia/algolia.py:20 |
products/warehouse_sources/backend/temporal/data_imports/sources/fullstory/fullstory.py:17 |
11 | 104 |
products/replay_vision/backend/temporal/backfill_workflow.py:145 |
products/replay_vision/backend/temporal/sweep_workflow.py:233 |
18 | 101 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/acculynx.py:36 |
products/warehouse_sources/backend/temporal/data_imports/sources/sim/sim.py:13 |
18 | 98 |
products/warehouse_sources/backend/temporal/data_imports/sources/gitbook/gitbook.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/metronome/metronome.py:34 |
12 | 98 |
products/warehouse_sources/backend/temporal/data_imports/sources/gitbook/gitbook.py:18 |
products/warehouse_sources/backend/temporal/data_imports/sources/reverb/reverb.py:21 |
12 | 98 |
posthog/hogql_queries/insight_actors_query_options_runner.py:32 |
posthog/hogql_queries/insight_actors_query_runner.py:51 |
16 | 97 |
products/canvas/backend/presentation/serializers.py:1499 |
products/tasks/backend/presentation/serializers.py:3090 |
23 | 97 |
products/warehouse_sources/backend/temporal/data_imports/sources/convex/source.py:3 |
products/warehouse_sources/backend/temporal/data_imports/sources/pinecone/source.py:5 |
11 | 96 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/acculynx.py:21 |
products/warehouse_sources/backend/temporal/data_imports/sources/revolut_merchant/revolut_merchant.py:8 |
11 | 90 |
products/warehouse_sources/backend/temporal/data_imports/sources/aha_ideas/aha_ideas.py:25 |
products/warehouse_sources/backend/temporal/data_imports/sources/freshservice/freshservice.py:19 |
15 | 90 |
products/warehouse_sources/backend/temporal/data_imports/sources/appdynamics/source.py:220 |
products/warehouse_sources/backend/temporal/data_imports/sources/gainsight_px/source.py:64 |
13 | 89 |
products/warehouse_sources/backend/temporal/data_imports/sources/buy_me_a_coffee/buy_me_a_coffee.py:21 |
products/warehouse_sources/backend/temporal/data_imports/sources/kapa_ai/kapa_ai.py:15 |
11 | 87 |
products/warehouse_sources/backend/temporal/data_imports/sources/brex/brex.py:17 |
products/warehouse_sources/backend/temporal/data_imports/sources/retell_ai/retell_ai.py:13 |
12 | 85 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/acculynx.py:32 |
products/warehouse_sources/backend/temporal/data_imports/sources/baseten/baseten.py:18 |
11 | 84 |
products/warehouse_sources/backend/temporal/data_imports/sources/acculynx/acculynx.py:32 |
products/warehouse_sources/backend/temporal/data_imports/sources/yousign/yousign.py:29 |
11 | 84 |
products/tasks/backend/logic/services/agent_server_launcher.py:342 |
products/tasks/backend/logic/services/docker_sandbox.py:992 |
20 | 82 |
products/warehouse_sources/backend/temporal/data_imports/pipelines/pipeline_v2/pipeline.py:70 |
products/warehouse_sources/backend/temporal/data_imports/pipelines/pipeline_v3/pipeline.py:87 |
11 | 82 |
products/warehouse_sources/backend/temporal/data_imports/sources/fullstory/fullstory.py:172 |
products/warehouse_sources/backend/temporal/data_imports/sources/zoom/zoom.py:79 |
11 | 79 |
products/warehouse_sources/backend/temporal/data_imports/sources/lexware_office/lexware_office.py:5 |
products/warehouse_sources/backend/temporal/data_imports/sources/turso/turso.py:6 |
14 | 78 |
products/warehouse_sources/backend/temporal/data_imports/sources/kapa_ai/source.py:53 |
products/warehouse_sources/backend/temporal/data_imports/sources/lexware_office/source.py:53 |
14 | 76 |
products/metrics/backend/metric_event_samples_query_runner.py:13 |
products/metrics/backend/metric_query_runner.py:15 |
11 | 72 |
products/metrics/backend/metric_query_runner.py:435 |
products/metrics/backend/metric_samples_query_runner.py:25 |
12 | 72 |
products/warehouse_sources/backend/temporal/data_imports/sources/givebutter/givebutter.py:93 |
products/warehouse_sources/backend/temporal/data_imports/sources/ynab/ynab.py:40 |
15 | 70 |
⚠️ Duplication (TypeScript) — 4 new duplicated blocks (worst 139 tokens)
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.
| First copy | Second copy | Lines | Tokens |
|---|---|---|---|
products/desktop/packages/ui/src/features/canvas/freeform/BuiltCanvas.tsx:196 |
products/desktop/packages/ui/src/features/canvas/freeform/FreeformCanvas.tsx:162 |
20 | 139 |
products/desktop/packages/core/src/sessions/processKilledNotice.ts:9 |
products/desktop/packages/ui/src/features/sessions/components/buildConversationItems.ts:774 |
24 | 130 |
products/ai_observability/frontend/redesign/trace/sampleFixtures/anthropicMessagesThinkingToolUse.ts:146 |
products/ai_observability/frontend/redesign/trace/sampleFixtures/langchainCerebrasImageError.ts:122 |
20 | 121 |
products/metrics/frontend/components/MetricsSamplesPanel.tsx:55 |
products/metrics/frontend/components/TraceMetricSamples.tsx:77 |
21 | 96 |
🚨 Comment density — 11% of added code lines are comments (69 of 622)
This section warns when comments are more than 3% of the code lines a PR adds, and alerts above 6%. Before agent-assisted PRs, the typical share was about 2%. Only full-line comments count. Docstrings, generated files, snapshots, migrations, and workflow files are left out.
Comments that restate the code, record how the change came about, or narrate the next line add noise for the next reader. Keep the comments that explain a reason the code cannot show, and remove the rest. See .agents/skills/writing-code-comments/SKILL.md for the house rules.
Files with the most added comment lines:
| File | Comment lines | Added lines |
|---|---|---|
products/replay_vision/backend/tests/test_variant_sampling.py |
15 | 128 |
products/replay_vision/backend/queries/variant_sampling.py |
13 | 175 |
products/replay_vision/backend/tests/test_scanner_candidate_query.py |
8 | 115 |
products/replay_vision/backend/queries/scanner_candidate_query.py |
5 | 58 |
posthog/session_recordings/queries/session_recording_list_from_query.py |
4 | 22 |
products/replay_vision/backend/queries/scanner_volume_estimate.py |
3 | 3 |
products/replay_vision/backend/temporal/activities/backfill.py |
3 | 15 |
products/replay_vision/backend/temporal/snapshots.py |
3 | 4 |
This check does not block merging. It updates on every push and clears when the share drops.
⚠️ Bundle size — 🔺 +203.3 KiB (+0.3%)
Uncompressed size of every built .js bundle, compared against the base branch.
Total: 69.22 MiB · 🔺 +203.3 KiB (+0.3%)
| File | Size | Δ vs base |
|---|---|---|
posthog-app/_parent/products/autoresearch/frontend/AutoresearchPipelineScene.js |
53.0 KiB | 🔺 +53.0 KiB (new) |
posthog-app/_parent/products/ai_observability/frontend/AIObservabilityTraceScene.js |
179.4 KiB | 🔺 +41.1 KiB (+29.7%) |
posthog-app/_parent/products/tasks/frontend/spaces/SpaceScene.js |
21.7 KiB | 🔺 +21.7 KiB (new) |
posthog-app/_parent/products/autoresearch/frontend/AutoresearchNewScene.js |
16.1 KiB | 🔺 +16.1 KiB (new) |
render-query/src/render-query/render-query.js |
20.19 MiB | 🔺 +15.0 KiB (+0.1%) |
posthog-app/_parent/products/posthog_ai/frontend/scenes/TaskTracker/components/EmbeddedTaskComposerImpl.js |
12.9 KiB | 🔺 +12.9 KiB (new) |
posthog-app/_parent/products/tasks/frontend/spaces/SpacesScene.js |
9.1 KiB | 🔺 +9.1 KiB (new) |
toolbar/src/toolbar/debug/chunk-EventDebugMenu.js |
300.2 KiB | 🔺 +7.4 KiB (+2.5%) |
posthog-app/_parent/products/signals/frontend/inbox/InboxScene.js |
457.3 KiB | 🔺 +5.3 KiB (+1.2%) |
exporter/_parent/products/posthog_ai/frontend/scenes/TaskTracker/components/EmbeddedTaskComposerImpl.js |
5.1 KiB | 🔺 +5.1 KiB (new) |
posthog-app/_parent/products/replay_vision/frontend/replay_scanners/ReplayScannersScene.js |
98.0 KiB | 🔺 +4.8 KiB (+5.1%) |
posthog-app/src/scenes/AuthenticatedShell.js |
272.5 KiB | 🔺 +3.3 KiB (+1.2%) |
posthog-app/_parent/products/workflows/frontend/Workflows/WorkflowScene.js |
53.8 KiB | 🔺 +3.1 KiB (+6.1%) |
posthog-app/_parent/products/autoresearch/frontend/AutoresearchScene.js |
13.9 KiB | 🟢 -1.3 KiB (-8.8%) |
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.60 MiB · 22 files | 🔺 +3.7 KiB (+0.2%) | █████████░ 86.9% 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.54 MiB · 629 files | 🔺 +4.5 KiB (+0.1%) | █████████░ 87.9% of 4.03 MiB |
authenticated shell (every logged-in page)src/scenes/AuthenticatedShell.tsx |
7.45 MiB · 2,385 files | 🔺 +44.7 KiB (+0.6%) | █████████░ 89.3% of 8.34 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 |
|---|---|
| 301.8 KiB | ../node_modules/.pnpm/posthog-js@1.434.14_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 216.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 |
| 89.6 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.5 KiB | src/scenes/scenes.ts |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
| Size | File |
|---|---|
| 301.8 KiB | ../node_modules/.pnpm/posthog-js@1.434.14_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 279.7 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 216.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 |
| 102.7 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 |
| 90.6 KiB | ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js |
| 89.6 KiB | src/products.tsx |
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.17 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.17 MiB · 19 files | 🔺 +4.9 KiB (+0.2%) | ████░░░░░░ 38.0% of 5.72 MiB |
| Deferred (lazy) | 2.10 MiB · 44 files | 🔺 +7.4 KiB (+0.3%) | 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 |
|---|---|
| 813.3 KiB | dist/toolbar/toolbar-app-BW77XXDE.css |
| 651.8 KiB | dist/toolbar/chunk-chunk-XQRTS2G5.js |
| 259.4 KiB | dist/toolbar/chunk-chunk-4EKE7GSM.js |
| 138.3 KiB | dist/toolbar/chunk-chunk-ZLUZVIUR.js |
| 131.8 KiB | dist/toolbar/chunk-chunk-FDH2IBXT.js |
| 75.2 KiB | dist/toolbar/toolbar-app-47JXF64O.js |
| 69.0 KiB | dist/toolbar/chunk-chunk-TSAL54PB.js |
| 35.6 KiB | dist/toolbar/chunk-chunk-S2X4KXBC.js |
| 21.0 KiB | dist/toolbar/chunk-chunk-AM7RRUA3.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 — 🔺 +2.37 MiB (+0.2%)
Total size of the built frontend/dist folder (all assets), compared against the base branch.
Total: 951.74 MiB · 🔺 +2.37 MiB (+0.2%)
ℹ️ 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.2 KB |
| action | 454.1 KB | 199.2 KB |
| action-list | 564.2 KB | 199.2 KB |
| cohort | 453.1 KB | 199.2 KB |
| cohort-list | 563.2 KB | 199.2 KB |
| email-template | 452.9 KB | 199.2 KB |
| error-details | 469.6 KB | 199.2 KB |
| error-issue | 454.5 KB | 199.2 KB |
| error-issue-list | 564.8 KB | 199.2 KB |
| experiment | 561.3 KB | 199.2 KB |
| experiment-list | 564.9 KB | 199.2 KB |
| experiment-results | 566.3 KB | 199.2 KB |
| feature-flag | 566.8 KB | 199.2 KB |
| feature-flag-list | 570.5 KB | 199.2 KB |
| feature-flag-testing | 457.3 KB | 199.2 KB |
| inline-scan | 453.6 KB | 199.2 KB |
| insight-actors | 562.3 KB | 199.2 KB |
| invite-email-preview | 452.3 KB | 199.2 KB |
| llm-costs | 559.3 KB | 199.2 KB |
| session-recording | 455.3 KB | 199.2 KB |
| survey | 454.7 KB | 199.2 KB |
| survey-global-stats | 561.9 KB | 199.2 KB |
| survey-list | 564.9 KB | 199.2 KB |
| survey-stats | 561.9 KB | 199.2 KB |
| trace-span | 453.5 KB | 199.2 KB |
| trace-span-list | 564.1 KB | 199.2 KB |
| vision-observation-list | 563.3 KB | 199.2 KB |
| workflow | 453.4 KB | 199.2 KB |
| workflow-list | 563.5 KB | 199.2 KB |
| loops-review | 457.8 KB | 199.2 KB |
| query-results | 774.1 KB | 199.2 KB |
| render-ui | 858.1 KB | 199.2 KB |
| visual-review-snapshots | 457.9 KB | 199.2 KB |
✅ Django migration risk — migration analysis complete
We've analyzed your migrations for potential risks.
Summary: 0 Safe | 2 Needs Review | 0 Blocked
⚠️ Needs Review
May have performance impact
replay_vision.0102_alter_replayscanner_scanner_type
└─ #1 ⚠️ AlterField
Field alteration may cause table locks or data loss (check if changing type or constraints)
model: replayscanner, field: scanner_type, field_type: CharField
replay_vision.0103_alter_replayobservation_error_reason
└─ #1 ⚠️ AlterField
Field alteration may cause table locks or data loss (check if changing type or constraints)
model: replayobservation, field: error_reason, field_type: TextField
Last updated: 2026-09-30 15:54 UTC (6b49f8c)
|
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughExperiment scanners can derive per-variant sampling rates from exposure counts and apply them in candidate queries. Sweep and backfill workflows pass those rates to child scanner inputs. Observation snapshots and API types include optional sampling rates. Session recording queries can opt into projecting experiment exposure attribution. Priority: ⬇️ Low Merge Risk: 🔵 Low · up to Balanced sampling preserves downstream access checks, but exposure counting should also validate the scanner or backfill creator. The remaining risk is bounded to an internal unauthorized read, with no established disclosure to unauthorized users. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to A new background exposure-count read does not check the experiment permissions of the person it runs for. Later checks block unauthorized candidate delivery and observation access, limiting the demonstrated exposure to internal reads within one project. Retained concerns
Security review detailsSecurity Blast Radius
Security Findings and Attack Paths
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.
Actionable comments posted: 3
Note
Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.
🟡 Other comments (1)
products/replay_vision/backend/temporal/activities/backfill.py-198-198 (1)
198-198: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winPass the variant sampling plan to the backfill count.
When balanced sampling applies,
_enumerateomitsvariant_sampling_rates, soWindowedCandidateQueryuses the single-rate predicate. Backfill ticks use per-variant rates. The count stored astotal_countcan therefore differ from the candidates walked, skewing progress and credit estimates. Build the plan from the snapshot and pass its rates to the count query.🐛 Suggested fix
from products.replay_vision.backend.queries.scanner_candidate_query import ( BACKFILL_CANDIDATE_QUERY_TYPE, BACKFILL_COUNT_QUERY_TYPE, WindowedCandidateQuery, ) +from products.replay_vision.backend.queries.variant_sampling import variant_sampling_plan_for_scope @@ ) -> int: snapshot = BackfillScannerSnapshot.from_scanner(scanner) + variant_plan = variant_sampling_plan_for_scope( + self.team, + scope=snapshot.experiment_scope(), + scanner_config=snapshot.scanner_config, + sampling_rate=snapshot.sampling_rate, + ) return WindowedCandidateQuery( @@ sampling_mode=snapshot.sampling_mode, exclude_observed_by_scanner=str(scanner.id) if exclude_observed else None, max_execution_time_seconds=ENUMERATION_MAX_EXECUTION_SECONDS, + variant_sampling_rates=variant_plan.rates if variant_plan is not None else None, ).count(query_type=BACKFILL_COUNT_QUERY_TYPE)
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
Review profile: QUIET
Plan: Enterprise
Run ID: 6b4e0244-3a0e-4ac6-ab70-d9afa29f24aa
⛔ Files ignored due to path filters (1)
products/replay_vision/frontend/generated/api.schemas.tsis excluded by!**/generated/**
📒 Files selected for processing (20)
posthog/session_recordings/queries/session_recording_list_from_query.pyproducts/experiments/backend/facade/replay.pyproducts/replay_vision/backend/api/observations.pyproducts/replay_vision/backend/queries/scanner_candidate_query.pyproducts/replay_vision/backend/queries/scanner_volume_estimate.pyproducts/replay_vision/backend/queries/variant_sampling.pyproducts/replay_vision/backend/temporal/activities/backfill.pyproducts/replay_vision/backend/temporal/activities/create_observation.pyproducts/replay_vision/backend/temporal/activities/find_scanner_candidates.pyproducts/replay_vision/backend/temporal/backfill_types.pyproducts/replay_vision/backend/temporal/backfill_workflow.pyproducts/replay_vision/backend/temporal/snapshots.pyproducts/replay_vision/backend/temporal/sweep_types.pyproducts/replay_vision/backend/temporal/sweep_workflow.pyproducts/replay_vision/backend/temporal/types.pyproducts/replay_vision/backend/temporal/workflow.pyproducts/replay_vision/backend/tests/test_scanner_candidate_query.pyproducts/replay_vision/backend/tests/test_temporal.pyproducts/replay_vision/backend/tests/test_variant_sampling.pyservices/mcp/src/api/generated.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.
2682501 to
44db3f8
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
products/replay_vision/backend/tests/test_variant_sampling.py (1)
96-97: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMove the test imports to module scope.
Add
variant_sampling_plan_for_scopeto the existing planner import. Movecreate_experimentto the import section.As per coding guidelines: “Always place imports at the top of the file (module level), never inside functions or methods (local imports).”
Proposed change
- from products.replay_vision.backend.queries.variant_sampling import variant_sampling_plan_for_scope - from products.replay_vision.backend.tests.helpers import create_experimentUpdate the module-level imports:
from products.replay_vision.backend.queries.variant_sampling import ( plan_variant_sampling, variant_sampling_plan_for_scope, ) from products.replay_vision.backend.tests.helpers import create_experimentSource: Coding guidelines
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
Review profile: QUIET
Plan: Enterprise
Run ID: 88ef3214-6f3c-4ded-bf53-08cc8adbca45
⛔ Files ignored due to path filters (1)
products/replay_vision/frontend/generated/api.schemas.tsis excluded by!**/generated/**
📒 Files selected for processing (5)
products/replay_vision/backend/api/observations.pyproducts/replay_vision/backend/queries/variant_sampling.pyproducts/replay_vision/backend/temporal/activities/find_scanner_candidates.pyproducts/replay_vision/backend/tests/test_variant_sampling.pyservices/mcp/src/api/generated.ts
Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 9 remain after this review.
🦔 Hogbox preview · ✅ ready▶ Open the preview
commit |
👀 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 |
A new stamphog review started for this PR — the fresh verdict replaces this approval.
7fd0c82 to
5580112
Compare
5580112 to
9c36f24
Compare
9c36f24 to
527ad5a
Compare
527ad5a to
3ff4959
Compare
| the shares can't be read (the caller then falls back to plain sampling; the scan itself stays | ||
| the loud path for an unresolvable experiment). | ||
| """ | ||
| experiment_id = (scope or {}).get("experiment_id") |
There was a problem hiding this comment.
This turns on balancing for any scanner that watches an experiment, including the old types that target one through the column. Those scanners have no balance_variants key, so they'd switch to balanced sampling on deploy. Should this only apply to scanner_type == EXPERIMENT?
There was a problem hiding this comment.
Fixed in f4ffdce: the plan is gated on scanner_type == EXPERIMENT; column-targeted scanners keep plain sampling, with a test.
| from products.experiments.backend.facade.replay import variant_rollout_shares # noqa: PLC0415 | ||
|
|
||
| try: | ||
| shares = variant_rollout_shares(team, experiment_id=experiment_id) |
There was a problem hiding this comment.
The plan said to use rollout shares here, but I think that was a mistake on my part. The sessions we sample come from the whole experiment window, so if the rollout changed partway through, the actual mix can be very different. For example, after a change from 90/10 to 10/90, a 10% rate ends up scanning about 46% of sessions, and the estimate never sees it. A variant ramped down to 0% also gets sampled at 100%. Counting exposures per variant from the exposure query would fix both.
There was a problem hiding this comment.
Fixed in f4ffdce: shares now come from counting exposed persons per watched variant over the window (an aggregate over exposed_persons_select, metered as the scanner's reads, 60s budget, failing open to plain sampling). A ramped-down variant keeps its real weight, and the rates match the mix actually being sampled. Watched variants with no exposures zero-fill.
| if experiment_id is None: | ||
| return None | ||
| config = scanner_config if isinstance(scanner_config, dict) else {} | ||
| if config.get("balance_variants") is False: |
There was a problem hiding this comment.
Turning balance_variants on or off doesn't mark the estimate as stale, and the scout cost check misses it too.
There was a problem hiding this comment.
With exposure-count shares this turned out to be a non-issue: redistribution spends exactly the configured budget (effective_rate ≡ rate, cap or no cap — new invariant test in f4ffdce), so toggling balance_variants changes how the budget is spread, never the total. The estimate and the scout's cost projection don't move, so there's nothing to invalidate; the estimate now projects with the plain rate again.
There was a problem hiding this comment.
Could we add a test that the estimate comes out the same with balancing on and off (until a cap kicks in)? Right now only effective_rate is tested.
There was a problem hiding this comment.
Added in f4ffdce: test_balancing_never_changes_the_projected_volume asserts the projection is identical with balancing on and off, including when a cap binds — redistribution preserves the total, so the parenthetical never arrives.
3ff4959 to
9cb9886
Compare
A new stamphog review started for this PR — the fresh verdict replaces this approval.
There was a problem hiding this comment.
Note
Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.
🟡 Other comments (1)
products/replay_vision/backend/queries/variant_sampling.py-154-159 (1)
154-159: 🔒 Security & Privacy | 🛡️ Detected with Advanced Tier | 🟡 Minor | ⚡ Quick winAuthorization Bypass
Reachability: Internal
Exploitability: Difficult
CWE: CWE-863 — Incorrect AuthorizationAuthorize the exposure-count query with the scanner principal.
_variant_exposure_countsreads experiment exposure counts without a principal orvalidate_experiment_exposure_access. A scanner creator who loses experiment access can therefore trigger an internal unauthorized count read before candidate dispatch is rejected.The rates do not reach unauthorized observation readers. Candidate queries run as the scanner or backfill creator, and observation reads enforce both current scanner experiment access and the experiment recorded in each snapshot.
Pass
scanner.created_byorbackfill.created_bythroughvariant_sampling_plan_for_scope. Validate that principal before resolving the linkage, and returnNonewhen the principal is missing or denied.
🧹 Nitpick comments (1)
products/replay_vision/backend/tests/test_variant_sampling.py (1)
85-88: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMove the local imports to module scope.
ScannerVolumeEstimate,project_monthly_observations, andvariant_sampling_plan_for_scopeare imported inside test methods. Line 12 already imports fromvariant_samplingat module level, so an import cycle does not explain these local imports.As per coding guidelines: "Always place imports at the top of the file (module level), never inside functions or methods (local imports)."
Also applies to: 122-122
Source: Coding guidelines
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
Review profile: QUIET
Plan: Enterprise
Run ID: 78228884-9923-4aa0-9ae9-52a6b9d3ed6c
⛔ Files ignored due to path filters (1)
products/replay_vision/frontend/generated/api.schemas.tsis excluded by!**/generated/**
📒 Files selected for processing (7)
products/experiments/backend/facade/replay.pyproducts/replay_vision/backend/queries/scanner_volume_estimate.pyproducts/replay_vision/backend/queries/variant_sampling.pyproducts/replay_vision/backend/temporal/activities/backfill.pyproducts/replay_vision/backend/temporal/activities/find_scanner_candidates.pyproducts/replay_vision/backend/tests/test_scanner_candidate_query.pyproducts/replay_vision/backend/tests/test_variant_sampling.py
Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 11 remain after this review.
A new stamphog review started for this PR — the fresh verdict replaces this approval.
There was a problem hiding this comment.
Approved.
Contained change inside the flag-gated replay-vision product. The access-control concern was fixed, with the exposure-count query now running the experiment access check. The reviewer threads I read have replies showing they were addressed, and new tests cover the sampling logic and the query path. The backfill total-count query may not use the per-variant rates, which could skew progress and credit estimates slightly. That is minor and not a blocker.
- Author wrote 0% of the modified lines and has 34 merged PRs in these paths (familiarity MODERATE).
- Minor: the backfill total-count query may not use the balanced per-variant rates, so the stored total count could differ slightly from the sessions actually walked (CodeRabbit raised this). Consider a follow-up.
Gate mechanics and policy version
| Gate | Result | |
|---|---|---|
| prerequisites | ✓ | all clear |
| deny-list | ✓ | no deny categories matched |
| size | ✓ | 417L, 16F substantive, 749L/22F incl. docs/generated/snapshots — within ceiling |
| tier | ✓ | T1-agent / T1d-complex (749L, 22F, cross-cutting, feat) |
| stamphog 2.3.1 | .stamphog/policy.yml @ d5f273a · reviewed head d5f273a |
d5f273a to
39c1904
Compare
Balanced per-variant sampling for experiment scanners, default on: each sweep computes one salted-hash rate per watched variant from the flag's live rollout shares (r / (k · share), capped at 1, with a capped variant's unspent budget redistributed), so an uneven rollout no longer starves the small arm. The exposure join projects the attributed variant when balancing is on, backfills share the same query class and salt, the volume estimate projects with the plan's effective rate, and each observation's snapshot records the rates it was sampled at. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: c2db2a38-d03f-4578-af4a-40f61dc700a4
…variant scope Two review findings: a paused scanner (rate 0) now yields all-zero per-variant rates instead of sampling a zero-share variant whole, and a legacy column scope's singular `variant` counts as the one watched arm, so single-arm targeting gets no balancing plan. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: c2db2a38-d03f-4578-af4a-40f61dc700a4
Review cleanup: the balanced-sampling ClickHouse test imported User, Experiment, and FeatureFlag inside the test body; a test file has no import cycle to break, so they belong in the import section. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: c2db2a38-d03f-4578-af4a-40f61dc700a4
…iment type only Per review: the plan's shares now come from counting the exposed persons per watched variant over the experiment window (an aggregate over the exposure select, metered as the scanner's reads, failing open to plain sampling), because the flag's rollout percentages diverge from the window's real mix whenever the rollout changed mid-experiment. The plan is also gated to the experiment scanner type, so legacy column-targeted scanners keep plain sampling on deploy. The volume estimate goes back to projecting with the plain rate: redistribution spends exactly the configured budget (effective_rate always equals the rate), so balancing never moves the projection — the invariant now has its own test, which also answers why toggling balance_variants needs no estimate or scout cost invalidation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: c2db2a38-d03f-4578-af4a-40f61dc700a4
…uery-kind literal The counts query now runs the experiment's object-level access check as the scanner's creator (backfills: the launcher), the same principal the candidate query authorizes, failing open to plain sampling when denied or missing. Also swaps the malformed-query sweep test's payload off a real product query kind: the activity's module now reaches a HogQL dispatcher through the sampling plan, so the repo's model-crossing guard read the old TrendsQuery literal as this test driving another product's runner and cancelled Backend CI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: c2db2a38-d03f-4578-af4a-40f61dc700a4
39c1904 to
6b49f8c
Compare
Problem
An experiment scanner samples with one rate, so coverage follows rollout: at a 10% rate over 1,000 exposed sessions on a 90/10 split, a team gets ~90 control observations and ~10 test. The small arm — usually the variant under test — starves, and the comparison the scanner exists for has nothing to compare. Layer 4 of stack #108815, per the "Experiment scanner: backend plan" design doc; sits on #108862.
Changes
balance_variantsin the scanner config, added in the type's first layer): each sweep counts the exposed persons per watched variant over the experiment window and computes one rate per variant,r / (k · share), capped at 1. Counts rather than the flag's rollout percentages, so a rollout that changed mid-experiment plans against the mix actually being sampled (per review). A capped variant's unspent budget redistributes to the others, so a small variant can never shrink the total; the same example now yields ~50 observations per arm for the same spend.multiIf), so decisions stay stable across sweeps and backfills the way plain sampling does.effective_rate≡ rate, cap or no cap), so balancing never moves the projection — the invariant has its own test.Balancing cannot create sessions a small variant does not have: a 5% arm on a low-traffic page is sampled whole and stays thin.
How did you test this code?
test_variant_sampling.py: the doc's 90/10-at-10% example gives even coverage at the same total; a capped small variant frees its budget; watched-subset normalization; zero-share and single-variant edge cases; themultiIfpredicate's per-variant thresholds and its no-op when every rate is 1.test_scanner_candidate_query.py: with rates control=0/test=1 only the test-variant session survives the sweep query, and the unbalanced control run keeps both, so the join projection and the per-variant gate are exercised end to end.test_temporal.py: the dispatching tick's rates land on the observation's snapshot.TestScannerCandidateQueryAgainstClickHousehas order-dependent flakes in this sandbox that reproduce on a clean checkout (2–5 failures vary per run, pass in isolation); not a regression from this diff.hogli build:openapiregenerated.Release status
The
replay-visionflag still gates the whole product, and only experiment scanners (no UI entry point yet) reach this path.Automatic notifications
Docs update
None: no doc under
docs/covers scanner sampling.🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Agent: Claude Code (PostHog Desktop cloud task), Claude Fable 5
Created with PostHog Desktop
🤖 Generated with Claude Code