Skip to content

feat(flags): read flag calls from flag_evaluations on the activity page - #110149

Merged
trunk-io[bot] merged 7 commits into
masterfrom
haacked/flag-calls-activity-filter
Oct 2, 2026
Merged

trunk-io[bot] merged 7 commits into
masterfrom
haacked/flag-calls-activity-filter

Conversation

@haacked

@haacked haacked commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Problem

  • An organization on the FLAG_EVALUATIONS_ONLY mode will stop getting $feature_flag_called in events. That mode is the planned default for new organizations.
  • After that, the Activity page and the Log section of a flag's Usage tab would show no flag calls, because both read events.
  • No organization uses this mode yet. This PR prepares the rollout.

Changes

  • An events list filtered to exactly $feature_flag_called reads posthog.flag_evaluations when the organization is on FLAG_EVALUATIONS_ONLY.
    • This covers the Activity page with that filter and the flag Usage tab's Log section.
    • Every other organization, filter, action query, and source query still reads events.
    • The mode lookup runs only after the cheap query checks pass.
  • On that mode, the Activity page shows a dismissible note when the list is not filtered to only flag calls. It points to the "Feature flag called" filter.
  • The staff mode modal and the mode's enum comment now say that mode 2 also switches these lists.

flag-calls-note

How the runner reads the table:

  • flag_evaluations.person carries only the id. The runner joins persons on the merge-corrected person_id, so the display name and person filters work.
  • The runner also adds group_0..group_4 joins on the $group_N keys, so group property filters, including test account filters, work.
  • That join is set on the runner's own database copy. Widening person in the shared schema was rejected, because fix(hogql): resolve flag_evaluations.person_id through person merges #98900 narrowed it on purpose.
  • An exact $feature_flag filter compiles to flag_key IN (...) in place of the properties JSON read. HogQL otherwise reads properties.$feature_flag from the JSON on this table, which reads the properties of every call of the flag in the date range. flag_key leads the sort key after team_id, so ClickHouse also skips other flags' rows.
  • * returns empty strings for elements_chain and person_mode, which the table lacks.
  • FROM carries an explicit alias. Without it, the lazy join resolver prints the persons join before the override join it reads. The root fix in lazy_tables.py belongs in a separate HogQL PR.

Known limits, left for follow-ups:

  • to_query() callers, such as behavioral cohorts, still read events. Only _calculate routes.
  • Display names and person filters read person.properties through a persons join, which deduplicates every person in the team on each shard. Email, name and username read from materialized person columns, so this stays cheap for the default display name. A custom display name or a test-account filter on a non-materialized person property parses every person's properties JSON instead, which gets expensive for very large teams. A follow-up PR stacked on this one moves display names to a lookup of only the page's persons.
  • Session property and element filters are handled in the stacked follow-up.
  • The query cache key does not include the mode. A cached events result survives a mode change until it expires.

How did you test this code?

Test rationale:

  • test_events_query_runner.py: one test runs the Log section's query on a FLAG_EVALUATIONS_ONLY org, with a person and a group property filter. It checks *, the display name, and the URL, library, and response columns.
  • A parameterized test checks that the org must be exactly on FLAG_EVALUATIONS_ONLY and that only an exact $feature_flag_called filter routes. The rows cover READ_FLAG_EVALUATIONS, no filter, a second event, and action steps.
  • Each table holds a different flag key, so the result names the table read. Breaking the alias or the group joins fails the main test, and so does reading $feature_flag from the JSON again.
  • eventsSceneLogic.test.ts: six cases decide when the note shows. No existing test covered this scene logic beyond URL parsing.
  • The screenshot comes from Storybook with the selector forced on, at 1280px and 800px. No story covers the mode, so the scratch story stayed out of the commit.
  • Not run locally: two existing tests in the same file, which fail locally without the change because the local ClickHouse lacks JSONDropKeys and a channel_definition grant.

👉 Stay up-to-date with PostHog coding conventions for a smoother review.

Release status

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

The per-organization flag_evaluations_mode setting gates it, and no organization is on FLAG_EVALUATIONS_ONLY.

Automatic notifications

  • Publish to changelog?

Docs update

None.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Agent: Claude Code, Opus 5.5 (main session). Haiku 4.5 and Sonnet 5.5 ran the commit, comment-cleanup, and PR-creation steps.

  • Built from an approved plan through the /go pipeline. Skills: /writing-tests, /writing-dataclasses, /writing-ui-components, /writing-user-facing-copy, /writing-kea-logics, /simplify, /comment-cleanup, /reviewing-with-coderabbit, /writing-pr-descriptions.
  • I added the flag_key filter after the simplify pass found the Log query never used the sort key. Deferred findings are under Known limits.
  • review-code raised 9 suggestions. 7 are fixed. The page-limited person lookup moves to the stacked follow-up. A shared persons-join helper in database.py was declined, because it would edit core HogQL for three lines. The run timed out while applying fixes, so I finished them from its saved findings.
  • CodeRabbit CLI ran with --deep. One finding: the person swap mutates the runner's shared database. Rejected, because the runner's other queries never read flag_evaluations, and a clone would sit in that same database.
  • New events schema: posthog/hogql_queries is already in .github/new-events-schema-targets.txt.
  • No duplicate PR found. All test data is invented.

https://claude.ai/code/session_01RSwfumDvtce35MsauEDYTn

@haacked haacked self-assigned this Oct 1, 2026
@trunk-io

trunk-io Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

😎 Stack merged successfully - details.

@haacked
haacked requested a balanced review from Copilot October 1, 2026 16:07
@posthog

posthog Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

🦔 PostHog Review reviewed this pull request

Found 0 must fix, 1 should fix, 0 consider.

Published 1 finding (view the review).

Resolved comments: 1 fixed, 2 declined, 1 already settled, 1 left for you

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

🤖 CI report

⚠️ Trunk lane — backend Python lane

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

⚠️ Complexity (TypeScript) — 2 functions above the limit (max 17)

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

Function Location Complexity Limit
sqlEditor frontend/src/scenes/urls.ts:74 17 10
EventRowActionsDropdown frontend/src/queries/nodes/DataTable/EventRowActions.tsx:47 12 10
✅ Duplication (Python) — clean

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

✅ Duplication (TypeScript) — clean

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

⚠️ Comment density — 4% of added code lines are comments (19 of 438)

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/hogql_queries/events_query_runner.py 15 141
frontend/src/scenes/scenes.ts 2 9
products/feature_flags/backend/facade/enums.py 2 2

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

⚠️ Bundle size — 🔺 +725 B (+0.0%)

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

Total: 70.70 MiB · 🔺 +725 B (+0.0%)

No file changed by more than 1000 B.

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

✅ Eager graph — within budget

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

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
1.62 MiB · 22 files no change █████████░ 87.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.58 MiB · 630 files 🔺 +66 B (+0.0%) █████████░ 88.7% of 4.03 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
7.75 MiB · 2,486 files 🔺 +66 B (+0.0%) █████████░ 93.0% of 8.34 MiB
dashboard scene
src/scenes/dashboard/Dashboard.tsx
11.67 MiB · 4,475 files 🔺 +82 B (+0.0%) █████████░ 86.6% of 13.48 MiB
project home scene
src/scenes/project-homepage/ProjectHomepage.tsx
14.44 MiB · 5,365 files 🔺 +74 B (+0.0%) █████████░ 87.8% of 16.44 MiB
events scene
src/scenes/activity/explore/EventsScene.tsx
10.90 MiB · 4,134 files 🔺 +659 B (+0.0%) █████████░ 86.3% of 12.64 MiB
replay detail scene
src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
13.79 MiB · 5,056 files 🔺 +74 B (+0.0%) █████████░ 87.7% 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
91.9 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
109.9 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
91.9 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
109.9 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
109.9 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
109.9 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
109.9 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.19 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.19 MiB · 19 files no change ████░░░░░░ 38.3% 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
830.3 KiB dist/toolbar/toolbar-app-JYLOHLGI.css
657.2 KiB dist/toolbar/chunk-chunk-D2KCVN5F.js
259.4 KiB dist/toolbar/chunk-chunk-VJB3SXU4.js
138.2 KiB dist/toolbar/chunk-chunk-FIGT27J5.js
131.8 KiB dist/toolbar/chunk-chunk-FDH2IBXT.js
75.2 KiB dist/toolbar/toolbar-app-I566ILMV.js
69.0 KiB dist/toolbar/chunk-chunk-TSAL54PB.js
35.6 KiB dist/toolbar/chunk-chunk-XLMY62WB.js
21.0 KiB dist/toolbar/chunk-chunk-7Q4HOLA2.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 — 🔺 +9.0 KiB (+0.0%)

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

Total: 965.92 MiB · 🔺 +9.0 KiB (+0.0%)

✅ Playwright — all passed

All tests passed.

View test results →

@greptile-apps

greptile-apps Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Retrigger

[Medium risk] Activity page now reads feature flag calls from a separate table.

The PR should not merge until explicit events-only column selections are handled on the routed path.

Reviews (1) · Last reviewed commit: "feat(flags): read flag calls from flag_e..."

@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
🧰 Additional context used
📚 Code guidelines (13)
.cursor/rules/react-typescript.mdc — auto-discovered
.agents/skills/using-kea-disposables/SKILL.md — configured
.agents/skills/writing-ui-components/SKILL.md — configured
.agents/security.md — configured
.agents/skills/sending-notifications/SKILL.md — configured
docs/published/handbook/engineering/type-system.md — configured
docs/internal/person-data-access.md — configured
.agents/skills/adopting-generated-api-types/SKILL.md — configured
.agents/skills/placing-product-frontend-code/SKILL.md — configured
.claude/commands/conventions.md — configured
.agents/skills/writing-kea-logics/SKILL.md — configured
.agents/skills/writing-user-facing-copy/SKILL.md — configured
.agents/skills/writing-code-comments/SKILL.md — configured
📝 Walkthrough

Walkthrough

In flag-evaluations-only mode, exact queries for $feature_flag_called can read from posthog.flag_evaluations. Other query shapes continue to use the events table. The Events scene displays a note when its query includes events beyond $feature_flag_called or has no event filter. Copied event links include the event name, which the redirect uses with the UUID filter to build the event query.

Priority: ⬇️ Low

Merge Risk: 🟡 Moderate · up to 447a8

Flag-call list pages in flag-evaluations-only mode can do work proportional to every person in the organization, risking growing latency and resource use as teams grow. Make the lookup page-scoped before broad rollout; the navigation test also remains dependent on a fixed delay.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 447a8

The change is narrowly limited to flag-call views for organizations using separate evaluation storage. No new authorization bypass was established, but project isolation and interrupted or concurrent query execution were not fully verified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The intended new exposure is evaluation data and associated person/group properties within the authenticated project's query context. Caller-controlled event fields influence source selection, but organization mode is obtained from the runner's team rather than accepted as a caller-supplied authority value. Cross-project isolation still depends on downstream table resolution and SQL generation.

Trust Boundaries and Controls

  • observed — The public query handler validates the request and passes its authenticated user and scoped team through the query service. The changed runner remains behind that existing path rather than exposing a direct evaluation-table access operation.

Resilience and Maintainability Implications

  • observed — Person/group schema augmentation targets the runner-owned database rather than the shared schema definition. Sequential user changes clear the runner's database and authorization snapshot before rebuilding. The augmentation itself consists of deterministic field assignments without transactional rollback; production observability of partial or concurrent mutation remains unresolved.
🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description is complete and follows the required template. It explains the problem, user-visible and technical changes, testing coverage and limitations, release gating, documentation status, and …
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
posthog/hogql_queries/test/test_events_query_runner.py (1)

1269-1269: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Snapshot the new analytics-query paths.

Add @snapshot_clickhouse_queries to this test and the parameterized routing test. The snapshots should expose the new source table, flag-key predicate, and persons joins for review.

As per coding guidelines: “Analytics-related queries should be covered by snapshot tests for ease of reviewing.”

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: PostHog/posthog/.coderabbit.yaml

Review profile: QUIET

Plan: Enterprise

Run ID: 5fa513cd-2acd-4c24-95cb-39ddcb520d1b

📥 Commits

Reviewing files that changed from the base of the PR and between 4d30b88 and 050b6c6.

📒 Files selected for processing (5)
  • frontend/src/scenes/activity/explore/EventsScene.tsx
  • frontend/src/scenes/activity/explore/eventsSceneLogic.test.ts
  • frontend/src/scenes/activity/explore/eventsSceneLogic.tsx
  • posthog/hogql_queries/events_query_runner.py
  • posthog/hogql_queries/test/test_events_query_runner.py

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Some supported query shapes can either hide the warning or fail when routed to the narrower table.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity

Open (2)
What changed in this PR

Routes feature flag call activity to flag_evaluations for organizations using the evaluations-only mode and informs users when calls are excluded.

Changes:

  • Selects the appropriate activity source based on query shape and organization mode.
  • Adds backend routing and frontend notice tests.
  • Displays an informational banner for affected Activity queries.
File Description
posthog/​hogql_queries/​events_query_runner.py Adds flag_evaluations routing and schema adaptation.
posthog/​hogql_queries/​test/​test_events_query_runner.py Tests routing and returned flag-call data.
frontend/​src/​scenes/​activity/​explore/​eventsSceneLogic.tsx Determines when to show the notice.
frontend/​src/​scenes/​activity/​explore/​eventsSceneLogic.test.ts Tests notice visibility conditions.
frontend/​src/​scenes/​activity/​explore/​EventsScene.tsx Renders the informational banner.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@trunk-io

trunk-io Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Static Badge   Static Badge   Static Badge

Failed Test Failure Summary Logs
Scenes/Code review Default play-test The test failed because a logic component was not mounted when accessed, and there were unhandled network requests intercepted by the mock service... Logs ↗︎

View Full Report ↗︎ ⋅ Docs

@posthog

posthog Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

PostHog Review alpha 🦔 If you find any issues helpful - please reply "valid", "invalid", etc., for evaluation purposes 🙏

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

PostHog Review

Found 1 should fix.

@posthog posthog Bot removed the reviewhog label Oct 1, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

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

🟡 Other comments (1)
frontend/src/scenes/sceneLogic.test.tsx-234-234 (1)

234-234: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Wait for redirect completion instead of a fixed delay.

The one-millisecond delay does not establish that the redirect has completed. If navigation takes longer, the assertions read stale router state and fail. Wait for the expected activity pathname before checking the query hash.

As per coding guidelines: “no time.sleep or arbitrary waits ... wait on a real condition.”

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: PostHog/posthog/.coderabbit.yaml

Review profile: QUIET

Plan: Enterprise

Run ID: d55d0c70-fa10-48d2-ac42-952577871df2

📥 Commits

Reviewing files that changed from the base of the PR and between 050b6c6 and dcbf731.

📒 Files selected for processing (6)
  • frontend/src/queries/nodes/DataTable/EventRowActions.tsx
  • frontend/src/scenes/sceneLogic.test.tsx
  • frontend/src/scenes/scenes.ts
  • frontend/src/scenes/urls.ts
  • posthog/hogql_queries/events_query_runner.py
  • posthog/hogql_queries/test/test_events_query_runner.py

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

@parameterai

parameterai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Risk: No findings

This increment only registers the new Views scene and a /replay/templates redirect in frontend routing, and rewords a comment on the FlagEvaluationsMode enum. No security-relevant surface is created or altered.

Sentinel reviewed 447a88c · Review settings

haacked and others added 3 commits October 1, 2026 16:33
An events list filtered to exactly $feature_flag_called reads
posthog.flag_evaluations when the organization is on
FLAG_EVALUATIONS_ONLY. The unfiltered Activity page shows a note that
points to the "Feature flag called" filter.

Claude-Session: https://claude.ai/code/session_01RSwfumDvtce35MsauEDYTn
"Copy link to event" now adds the event name to the /events/<uuid>/<timestamp> link, and the redirect sets it as the query's event filter. Without the event filter, the events query reads `events`, so a flag call stored only in flag_evaluations opened an empty list.

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

Generated-By: PostHog Desktop
Task-Id: 18023212-4ba4-4997-a120-6a82554ecdfe
An explicit select of elements_chain or person_mode failed with "Unable to resolve field" when the events list read flag_evaluations, because the table has neither column. The runner's database copy now defines both as empty strings, so "*" and explicit selects, filters and sorts resolve the same way. The select-star override on EventsListTable is no longer needed and is removed.

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

Generated-By: PostHog Desktop
Task-Id: a39f16b5-6c58-45e4-91f9-e6c78d1575ec
@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team October 1, 2026 23:34
@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested review from a team October 1, 2026 23:34
The note on the Activity page now shows for action, action step, and source queries, because those keep reading events even when filtered to $feature_flag_called.

Claude-Session: https://claude.ai/code/session_01RSwfumDvtce35MsauEDYTn
- products/dashboards/frontend/widgets/activity/ActivityEventsWidgetRow.tsx: a row link carries the event name, so a flag call in a widget filtered to $feature_flag_called opens its row
- posthog/hogql_queries/events_query_runner.py: the group joins loop over GROUP_TYPES_LIMIT and read the groups table once
- frontend/src/scenes/scenes.ts: the event redirect writes the event filter and time window onto one typed source
- frontend/src/scenes/activity/explore/eventsSceneLogic.tsx: the note selector reads the query source once

Claude-Session: https://claude.ai/code/session_01RSwfumDvtce35MsauEDYTn
On flag_evaluations, an exact $feature_flag filter now compiles to flag_key IN (...) alone. HogQL reads properties.$feature_flag from the properties JSON on this table, so the query read the properties blob of every call of the flag in the date range.

Claude-Session: https://claude.ai/code/session_01RSwfumDvtce35MsauEDYTn
@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

🦔 Hogbox preview · ✅ ready

▶ Open the preview

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

commit 447a88c · box box-ada832b32b24 · ready in 782s (push → usable) · build log · rebuilds on every push, torn down on close

@haacked haacked added the stamphog Request AI approval (no full review) label Oct 1, 2026

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Approved.

The change is gated by a per-organization mode that no organization uses, so the default events read path is unchanged. It has solid tests, and no showstoppers turned up. It sits outside risky territory.

  • Author wrote 10% of the modified lines and has 25 merged PRs in these paths (familiarity MODERATE).
  • 👍 on the PR from greptile-apps[bot].
  • Reviewer bots raised edge cases about events-only columns on the routed path. The runner now stubs elements_chain and person_mode, and the path is dormant until an organization is on mode 2.
  • Known limits are acknowledged in the description: the query cache key ignores the mode, and person joins can be costly for very large teams.
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✓ no deny categories matched
size ✓ 259L, 9F substantive, 518L/12F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1d-complex (518L, 12F, cross-cutting, feat)
stamphog 2.3.1 .stamphog/policy.yml @ 447a88c · reviewed head 447a88c

@github-project-automation github-project-automation Bot moved this to Approved in Feature Flags Oct 1, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)

🟠 Major · Make the person lookup page-scoped. · events_query_runner.py:399-421

posthog/hogql_queries/events_query_runner.py:399-421
🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

Make the person lookup page-scoped.

When FLAG_EVALUATIONS_ONLY selects person_display_name, the query joins the deduplicated persons table before pagination. The persons resolver cannot push the page limit into this join because the outer query reads flag_evaluations, not PersonsTable. Each page can therefore deduplicate the full team person set before returning limit + 1 rows. The Activity widget reaches this path for $feature_flag_called, and the staff mode endpoint can activate it.

Resolve display names in a second query restricted to the person IDs from the fetched page, or push those page IDs into the persons subquery before deduplication.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: PostHog/posthog/.coderabbit.yaml

Review profile: QUIET

Plan: Enterprise

Run ID: 5f4c4853-1dc2-44d3-b092-053bfcda1865

📥 Commits

Reviewing files that changed from the base of the PR and between 1a3a455 and 447a88c.

📒 Files selected for processing (2)
  • frontend/src/scenes/scenes.ts
  • products/feature_flags/backend/facade/enums.py

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

@trunk-io
trunk-io Bot merged commit 543c12b into master Oct 2, 2026
@trunk-io
trunk-io Bot deleted the haacked/flag-calls-activity-filter branch October 2, 2026 00:23
@deployment-status-posthog

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

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-10-02 00:45 UTC Run
prod-us ✅ Deployed 2026-10-02 00:54 UTC Run
prod-eu ✅ Deployed 2026-10-02 00:55 UTC Run

This branch was successfully deployed

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

Labels

feature/feature-flags Feature Tag: Feature flags stamphog Request AI approval (no full review) team/feature-flags

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

2 participants