Skip to content

feat(customer-analytics): add default pinned account properties - #108076

Open
arthurdedeus wants to merge 11 commits into
masterfrom
feat/customer-analytics-default-pinned-account-properties
Open

arthurdedeus wants to merge 11 commits into
masterfrom
feat/customer-analytics-default-pinned-account-properties

Conversation

@arthurdedeus

@arthurdedeus arthurdedeus commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Project admins cannot choose the account properties people see before saving a personal sidebar selection.

Every user starts with an empty account sidebar and must configure the same useful properties independently.

Changes

  • Accounts settings now let project admins select, order, clear, and reopen default pinned properties.
  • Users inherit current project defaults until they save a personal selection. An explicit empty selection remains a personal override.
  • The API validates the shared 50-property limit, duplicates, target types, reference kinds, and project ownership.
  • Historical empty selections without legacy pins inherit defaults at read time. New personal saves carry an override marker, avoiding a blocking migration and rolling-deploy race.
  • Relationship definitions load across all pages, so saving defaults keeps pins beyond the first page.

Warning

Existing data cannot distinguish an auto-created empty selection from an intentional clear. Those users may need to clear the defaults once again.

Before this change, Accounts settings had no default-pins control. The configured state now shows the ordered project defaults:

default-pinned-properties

How did you test this code?

  • Backend API tests cover inheritance, dynamic default changes, personal overrides, validation, permissions, and environment canonicalization.
  • Backend API tests cover historical empty rows, saved empty overrides, and preserved legacy selections.
  • Frontend logic tests cover relationship definitions beyond the first page.
  • Frontend tests cover loading, ordered saves, clearing, stale references, load failures, and non-admin restrictions.
  • Storybook and headless Playwright verified the configured modal at a 1200 × 800 viewport.
  • pnpm --filter=@posthog/frontend typescript:check
  • uv run mypy --cache-fine-grained .
  • hogli product:lint customer_analytics
  • hogli ci:preflight --strict

The PR assignee manually verified the complete inheritance flow:

  • An admin selected multiple project defaults.
  • Another user with no personal selection saw those defaults on first load.
  • Removing one project default updated that user's inherited list after refresh.
  • Clearing every personal pin persisted an empty override after refresh instead of restoring project defaults.

👉 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

Automatic notifications

  • Publish to changelog?

Docs update

Updated the Customer analytics Accounts table guide and generated API descriptions. The Inkeep docs agent can assess published documentation changes.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Agent: pi, gpt-5.6-sol (initial code) and gpt-6-sol (follow-up fixes)

  • Duplicate search found no overlapping implementation. #107161 changes account views and tabs, not pinned-property defaults.
  • Skills used: /django-migrations, /improving-drf-endpoints, /implementing-mcp-tools, /writing-tests, /writing-ui-components, /writing-user-facing-copy, /writing-kea-logics, /editing-agents-md, /running-ci-preflight, /debugging-ci-failures, /announcing-behavior-changes, /orwell-writing, /write-snapshot-test, /setting-feature-flags-in-storybook, /writing-pr-descriptions, and /ship-it.
  • CodeRabbit CLI was signed out. The user chose to skip the local pass, so the PR opened without one.
  • The babysitting pass replaced the unsafe data migration with read-time handling, fixed relationship pagination and Storybook setup, and regenerated Kea types. No customer data or private operational material appears in the public artifacts.
  • Session link is unavailable in pi.

@arthurdedeus arthurdedeus self-assigned this Sep 28, 2026
@trunk-io

trunk-io Bot commented Sep 28, 2026

Copy link
Copy Markdown

Merging to master in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

@arthurdedeus

Copy link
Copy Markdown
Contributor Author

Game on, robots! @veria-ai @greptile-apps @parameterai

@parameterai

parameterai Bot commented Sep 28, 2026

Copy link
Copy Markdown

Took a look at the diff — the new default_pinned_properties path is admin-gated the same way as the rest of customer_analytics_config (it's part of TEAM_CONFIG_FIELDS_SET but not TEAM_CONFIG_MEMBER_FIELDS_SET, so posthog/api/team.py enforces "only project admins can modify these settings" via TEAM_CONFIG_ADMIN_FIELDS_SET), and validate_pinned_properties in account_property_pins.py correctly scopes custom-property/relationship lookups to resolve_effective_team_id(team_id) before checking references, so you can't pin an object belonging to another team. Nothing jumped out as a security issue in this pass — happy to do a deeper pass if you want one specifically.

@github-actions

github-actions Bot commented Sep 28, 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) — 5 functions above the limit (max 18)

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
errors products/customer_analytics/frontend/scenes/CustomerAnalyticsConfigurationScene/account/customPropertyDefinitionsLogic.ts:950 18 10
submit products/customer_analytics/frontend/scenes/CustomerAnalyticsConfigurationScene/account/customPropertyDefinitionsLogic.ts:984 17 10
openEditModal products/customer_analytics/frontend/scenes/CustomerAnalyticsConfigurationScene/account/customPropertyDefinitionsLogic.ts:1311 15 10
DefaultPinnedAccountProperties products/customer_analytics/frontend/scenes/CustomerAnalyticsConfigurationScene/account/DefaultPinnedAccountProperties.tsx:12 14 10
loadSelectedTableColumns products/customer_analytics/frontend/scenes/CustomerAnalyticsConfigurationScene/account/customPropertyDefinitionsLogic.ts:845 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.

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

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

Total: 68.97 MiB · 🔺 +870 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.58 MiB · 22 files no change █████████░ 85.8% 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.52 MiB · 629 files no change █████████░ 87.4% of 4.03 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
7.35 MiB · 2,339 files no change █████████░ 88.1% 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
88.5 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.4 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
272.3 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
100.5 KiB src/lib/api.ts
98.8 KiB ../packages/quill/packages/quill/dist/index.js
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
88.5 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.16 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.16 MiB · 19 files no change ████░░░░░░ 37.8% 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
805.5 KiB dist/toolbar/toolbar-app-ZATDHZQQ.css
651.7 KiB dist/toolbar/chunk-chunk-7ZORLPSY.js
259.4 KiB dist/toolbar/chunk-chunk-2FGAEFTI.js
138.3 KiB dist/toolbar/chunk-chunk-UA3V2PCC.js
131.8 KiB dist/toolbar/chunk-chunk-FDH2IBXT.js
75.2 KiB dist/toolbar/toolbar-app-SD4VQXWA.js
69.0 KiB dist/toolbar/chunk-chunk-TSAL54PB.js
35.6 KiB dist/toolbar/chunk-chunk-63EWNJRZ.js
21.0 KiB dist/toolbar/chunk-chunk-F5GDSM5D.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 — 🔺 +144.6 KiB (+0.0%)

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

Total: 947.93 MiB · 🔺 +144.6 KiB (+0.0%)

ℹ️ 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
✅ Playwright — all passed

All tests passed.

View test results →

⚠️ Backend coverage — 99.0% of changed backend lines covered — 1 uncovered

🧪 Backend test coverage

Patch coverage — changed backend lines (products + core): ████████████████████ 99.0% (171 / 172)

File Patch Uncovered changed lines
products/customer_analytics/backend/facade/account_property_pins.py 85.7% 9

🤖 Agents: add a test covering the lines above, or note why under "How did you test this code?". Machine-readable gap list: the patch-coverage artifact on this run (gh run download 36614227385 -n patch-coverage), or the coverage-data block at the end of this comment.

Per-product line coverage (touched products)
Product Coverage Lines
warehouse_sources_queue ██░░░░░░░░░░░░░░░░░░ 10.5% 187 / 1,777
platform_features ██░░░░░░░░░░░░░░░░░░ 12.1% 7 / 58
demo ███████████░░░░░░░░░ 52.8% 1,411 / 2,673
data_tools ████████████░░░░░░░░ 61.2% 90 / 147
ai_gateway ███████████████░░░░░ 75.0% 9 / 12
aeo ███████████████░░░░░ 76.3% 617 / 809
batch_exports ████████████████░░░░ 81.2% 21,525 / 26,502
apm █████████████████░░░ 84.1% 1,306 / 1,553
cdp ██████████████████░░ 88.2% 4,545 / 5,155
ml_inference ██████████████████░░ 88.4% 509 / 576
mcp_analytics ██████████████████░░ 89.2% 5,038 / 5,651
product_tours ██████████████████░░ 89.3% 1,331 / 1,491
dashboards ██████████████████░░ 89.5% 6,904 / 7,714
data_warehouse ██████████████████░░ 90.0% 14,027 / 15,589
notebooks ██████████████████░░ 90.2% 15,297 / 16,964
signals ██████████████████░░ 90.2% 57,836 / 64,097
cohorts ██████████████████░░ 90.4% 8,420 / 9,316
streamlit_apps ██████████████████░░ 90.8% 2,684 / 2,956
managed_warehouse ██████████████████░░ 91.0% 10,252 / 11,263
tasks ██████████████████░░ 91.1% 75,525 / 82,874
data_modeling ██████████████████░░ 91.4% 10,554 / 11,543
exports ██████████████████░░ 91.6% 9,680 / 10,562
engineering_analytics ██████████████████░░ 91.7% 11,032 / 12,030
ai_training ██████████████████░░ 92.2% 356 / 386
business_knowledge ██████████████████░░ 92.2% 7,684 / 8,330
conversations ███████████████████░ 92.5% 28,734 / 31,062
early_access_features ███████████████████░ 92.6% 1,332 / 1,439
managed_migrations ███████████████████░ 92.7% 1,581 / 1,705
visual_review ███████████████████░ 92.8% 9,247 / 9,969
stamphog ███████████████████░ 92.8% 8,109 / 8,742
canvas ███████████████████░ 92.8% 6,873 / 7,405
approvals ███████████████████░ 93.0% 3,974 / 4,271
mcp_registry ███████████████████░ 93.1% 1,670 / 1,794
notifications ███████████████████░ 93.2% 1,145 / 1,229
error_tracking ███████████████████░ 93.2% 16,359 / 17,547
surveys ███████████████████░ 93.3% 6,571 / 7,040
slack_app ███████████████████░ 93.4% 13,995 / 14,989
autoresearch ███████████████████░ 93.6% 8,481 / 9,061
context_layer ███████████████████░ 93.9% 3,415 / 3,638
web_analytics ███████████████████░ 94.0% 21,815 / 23,218
alerts ███████████████████░ 94.0% 8,570 / 9,114
billing_alerts ███████████████████░ 94.1% 2,094 / 2,226
mcp_store ███████████████████░ 94.4% 8,940 / 9,472
ai_observability ███████████████████░ 94.5% 24,473 / 25,896
workflows ███████████████████░ 94.6% 15,222 / 16,093
wizard ███████████████████░ 94.7% 6,150 / 6,496
reminders ███████████████████░ 94.8% 760 / 802
review_hog ███████████████████░ 95.0% 11,507 / 12,119
endpoints ███████████████████░ 95.1% 9,206 / 9,681
annotations ███████████████████░ 95.1% 817 / 859
customer_analytics ███████████████████░ 95.2% 25,708 / 27,011
legal_documents ███████████████████░ 95.2% 2,311 / 2,427
marketing_analytics ███████████████████░ 95.3% 19,566 / 20,528
posthog_ai ███████████████████░ 95.3% 2,488 / 2,610
experiments ███████████████████░ 95.4% 32,457 / 34,020
actions ███████████████████░ 95.5% 756 / 792
logs ███████████████████░ 95.5% 15,399 / 16,130
data_catalog ███████████████████░ 95.6% 4,402 / 4,606
tracing ███████████████████░ 95.6% 3,536 / 3,699
replay_vision ███████████████████░ 95.6% 27,675 / 28,939
growth ███████████████████░ 95.7% 11,228 / 11,734
messaging ███████████████████░ 95.8% 3,798 / 3,963
skills ███████████████████░ 95.8% 6,972 / 7,274
product_analytics ███████████████████░ 96.0% 28,470 / 29,647
access_control ███████████████████░ 96.3% 7,113 / 7,386
revenue_analytics ███████████████████░ 96.4% 1,876 / 1,946
user_interviews ███████████████████░ 96.5% 2,867 / 2,971
feature_flags ███████████████████░ 96.5% 25,588 / 26,509
warehouse_sources ███████████████████░ 97.3% 458,450 / 471,303
data_quality ████████████████████ 97.6% 7,587 / 7,774
links ████████████████████ 97.9% 234 / 239
security ████████████████████ 98.0% 1,286 / 1,312
metrics ████████████████████ 98.0% 4,084 / 4,166
analytics_platform ████████████████████ 98.3% 2,784 / 2,833
pulse ████████████████████ 98.5% 2,046 / 2,078
live_debugger ████████████████████ 99.2% 626 / 631
field_notes ████████████████████ 99.4% 172 / 173

Report-only. Patch coverage = changed backend lines covered vs origin/master. Sorted lowest first.
Known gaps: lines covered only by Temporal tests show as uncovered; core line numbers may drift if master changed the same file.

⚠️ MCP snapshots — 1 updated (1 modified, 0 added, 0 deleted)

Snapshots: MCP unit test snapshots updated

Changes: 1 snapshots (1 modified, 0 added, 0 deleted)

What this means:

  • Snapshots have been automatically updated to match current output

Next steps:

  • Review the changes to ensure they're intentional
  • If unexpected, investigate what caused the output to change

Review snapshot changes →

⚠️ Django migration SQL — 1 new migration to review

We've detected new migrations on this PR. Review the SQL output for each migration:

products/customer_analytics/backend/migrations/0060_teamcustomeranalyticsconfig_default_pinned_properties.py

/opt/hostedtoolcache/Python/3.14.7/x64/lib/python3.14/site-packages/anyio/from_thread.py:119: SyntaxWarning: 'return' in a 'finally' block
  return result
/opt/hostedtoolcache/Python/3.14.7/x64/lib/python3.14/site-packages/structlog/stdlib.py:1166: UserWarning: Remove `format_exc_info` from your processor chain if you want pretty exceptions.
  ed = p(logger, meth_name, ed)  # type: ignore[arg-type]
2026-09-29T18:48:14.759330Z [error    ] Path must be a valid database or directory containing databases. [posthog.exceptions_capture] pid=9337 tid=139744523258752
Traceback (most recent call last):
  File "/home/runner/work/posthog/posthog/posthog/geoip.py", line 15, in <module>
    geoip: Optional[GeoIP2] = GeoIP2(cache=8)
                              ~~~~~~^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.14.7/x64/lib/python3.14/site-packages/django/contrib/gis/geoip2.py", line 116, in __init__
    raise GeoIP2Exception(
        "Path must be a valid database or directory containing databases."
    )
django.contrib.gis.geoip2.GeoIP2Exception: Path must be a valid database or directory containing databases.
/opt/hostedtoolcache/Python/3.14.7/x64/lib/python3.14/site-packages/sshtunnel.py:1040: SyntaxWarning: 'return' in a 'finally' block
  return (ssh_host,
/opt/hostedtoolcache/Python/3.14.7/x64/lib/python3.14/site-packages/langchain_core/_api/deprecation.py:27: UserWarning: Core Pydantic V1 functionality isn't compatible with Python 3.14 or greater.
  from pydantic.v1.fields import FieldInfo as FieldInfoV1
System check identified some issues:

WARNINGS:
?: (axes.W001) You are using the django-axes cache handler for login attempt tracking. Your cache configuration is however invalid and will not work correctly with django-axes. This can leave security holes in your login systems as attempts are not tracked correctly. Reconfigure settings.AXES_CACHE and settings.CACHES per django-axes configuration documentation.
?: (staticfiles.W004) The directory '/home/runner/work/posthog/posthog/frontend/dist' in the STATICFILES_DIRS setting does not exist.
BEGIN;
--
-- Add field default_pinned_properties to teamcustomeranalyticsconfig
--
ALTER TABLE "customer_analytics_teamcustomeranalyticsconfig" ADD COLUMN "default_pinned_properties" jsonb DEFAULT '[]'::jsonb NOT NULL;
ALTER TABLE "customer_analytics_teamcustomeranalyticsconfig" ALTER COLUMN "default_pinned_properties" DROP DEFAULT;
COMMIT;

Last updated: 2026-09-29 18:48 UTC (fe319b8)

✅ Django migration risk — migration analysis complete

We've analyzed your migrations for potential risks.

Summary: 0 Safe | 1 Needs Review | 0 Blocked

⚠️ Needs Review

May have performance impact

customer_analytics.0060_teamcustomeranalyticsconfig_default_pinned_properties
  └─ #1 ⚠️ AddField
     Adding NOT NULL field with callable default (list) - verify it's stable
     model: teamcustomeranalyticsconfig, field: default_pinned_properties, default: list

Last updated: 2026-09-29 18:49 UTC (fe319b8)

@greptile-apps

greptile-apps Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Retrigger

[High risk] Adds database schema field and API contract for account property configuration.

The PR appears safe to merge based on the reviewed changes and resolved previous findings.

Reviews (2) · Last reviewed commit: "fix(customer-analytics): keep pin kind e..."

@coderabbitai

coderabbitai Bot commented Sep 29, 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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

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

Review profile: QUIET

Plan: Enterprise

Run ID: 5b31bdb9-e890-4c20-b7ee-f57d5d502da5

📥 Commits

Reviewing files that changed from the base of the PR and between 778c72a and 767a2ff.

📒 Files selected for processing (1)
  • frontend/snapshots.yml

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


📝 Walkthrough

Walkthrough

Adds ordered project defaults for pinned account properties and user inheritance until a personal selection is saved. The team API validates references and stores the defaults. The account configuration interface lets admins edit defaults. Property-definition loading now reports failures, and relationship definitions load across pages.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to 767a2

Users inherit project defaults as intended, and the historical empty-list behavior is documented. No material merge risk remains.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 767a2

Project defaults remain admin-controlled and project-scoped. The main risk is that some users who previously cleared their pins may see the new defaults until they clear them again.

Retained concerns

  • Low · architecture · observed: An unmarked empty personal selection, including one intentionally saved before this change, now inherits administrator defaults. An older writer during a mixed-version rollout can produce the same state; affected users must clear their pins again to establish the new override marker.
Security review details

Security Blast Radius

  • inferred — An administrator's default change can affect every inheriting user in that project, but the inspected reference validation and lookup do not establish a cross-project expansion.

Trust Boundaries and Controls

  • observed — Default writes pass through the project settings serializer with access-control context and project-scoped reference validation; personal configuration lookup canonicalizes the effective team.

Resilience and Maintainability Implications

  • inferred — The override marker protects empty selections saved by the new writer, but cannot recover the intent of pre-existing unmarked empty selections or of such selections written by an older version during deployment overlap.

Hardening Proposals

  • proposed — If an empty personal selection is relied on to keep properties out of view, provide affected users a clear way to re-establish that preference and verify behavior during deployment overlap.
🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description follows the required template and covers the problem, user-visible changes, testing, release status, documentation, and agent context. Minor omissions include patch-coverage evidence a…
✨ Finishing Touches 💡 1
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch feat/customer-analytics-default-pinned-account-properties
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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: 3

Note

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

🟡 Other comments (3)
posthog/api/team.py-1142-1143 (1)

1142-1143: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Bind customer analytics validation before other config writes.

When a PATCH includes valid revenue or marketing settings and invalid default pins, TeamSerializer.update writes the earlier settings before _update_customer_analytics_config raises a validation error. The project endpoint does not add an atomic wrapper, and ATOMIC_REQUESTS is disabled. The API can therefore return HTTP 400 after persisting part of the request.

Bind the nested serializer to the existing customer analytics config during outer validation:

🐛 Suggested fix
-    @staticmethod
-    def validate_customer_analytics_config(value):
+    def validate_customer_analytics_config(self, value):
         if value is None:
             return None
 
-        serializer = TeamCustomerAnalyticsConfigSerializer(data=value)
+        instance = self.instance.customer_analytics_config if self.instance is not None else None
+        serializer = TeamCustomerAnalyticsConfigSerializer(instance=instance, data=value)
         if not serializer.is_valid():
             raise exceptions.ValidationError(_format_serializer_errors(serializer.errors))
         return serializer.validated_data
products/customer_analytics/backend/migrations/0060_inherit_default_pinned_properties.py-14-19 (1)

14-19: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

The migration risk check is blocking this RunSQL UPDATE.

The UPDATE scans customer_analytics_usercustomeranalyticsconfig and rewrites the matching rows in one transaction. This table has one row per user per project, so the row count grows with both. Take one of these actions to pass the check:

  • Confirm that the table is small and record the size in a comment that the check accepts.
  • Run the update in batches by primary key.
  • Move the update to a background job.

Source: Pipeline failures

products/customer_analytics/backend/logic/user_customer_analytics_config.py-72-77 (1)

72-77: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Write customer analytics defaults to the canonical team row.

get_or_create_config canonicalizes child team IDs, so read_pinned_properties reads the parent team's defaults. TeamSerializer._update_customer_analytics_config saves instance.customer_analytics_config directly. A PATCH for a child environment can therefore write the child row, while reads use the parent row. Resolve the canonical team before the write or before the default read.


ℹ️ Review info
⚙️ Run configuration

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

Review profile: QUIET

Plan: Enterprise

Run ID: 4a46d752-d5fa-401b-9528-63b9ecb821b8

📥 Commits

Reviewing files that changed from the base of the PR and between ba079f0 and eed080b.

⛔ Files ignored due to path filters (5)
  • frontend/src/generated/core/api.schemas.ts is excluded by !**/generated/**
  • products/customer_analytics/frontend/generated/api.schemas.ts is excluded by !**/generated/**
  • products/customer_analytics/frontend/generated/api.ts is excluded by !**/generated/**
  • products/experiments/frontend/generated/api.schemas.ts is excluded by !**/generated/**
  • services/mcp/src/generated/core/api.ts is excluded by !**/generated/**
📒 Files selected for processing (33)
  • docs/internal/customer-analytics/accounts-table.md
  • frontend/src/queries/schema.json
  • frontend/src/queries/schema/schema-general.ts
  • posthog/api/team.py
  • posthog/api/test/test_project.py
  • posthog/schema.py
  • posthog/schema_enums.py
  • products/customer_analytics/backend/facade/account_property_pins.py
  • products/customer_analytics/backend/facade/api.py
  • products/customer_analytics/backend/facade/contracts.py
  • products/customer_analytics/backend/facade/enums.py
  • products/customer_analytics/backend/logic/account_property_pins.py
  • products/customer_analytics/backend/logic/user_customer_analytics_config.py
  • products/customer_analytics/backend/migrations/0059_teamcustomeranalyticsconfig_default_pinned_properties.py
  • products/customer_analytics/backend/migrations/0060_inherit_default_pinned_properties.py
  • products/customer_analytics/backend/migrations/max_migration.txt
  • products/customer_analytics/backend/models/team_customer_analytics_config.py
  • products/customer_analytics/backend/presentation/views/serializers.py
  • products/customer_analytics/backend/presentation/views/views.py
  • products/customer_analytics/backend/test/test_default_pinned_properties_migration.py
  • products/customer_analytics/backend/test/test_user_customer_analytics_config_api.py
  • products/customer_analytics/frontend/components/Accounts/AGENTS.md
  • products/customer_analytics/frontend/scenes/CustomerAnalyticsAccountScene/components/AccountPropertyConfigurator.tsx
  • products/customer_analytics/frontend/scenes/CustomerAnalyticsConfigurationScene/account/CustomerAnalyticsAccountConfig.tsx
  • products/customer_analytics/frontend/scenes/CustomerAnalyticsConfigurationScene/account/DefaultPinnedAccountProperties.stories.tsx
  • products/customer_analytics/frontend/scenes/CustomerAnalyticsConfigurationScene/account/DefaultPinnedAccountProperties.test.tsx
  • products/customer_analytics/frontend/scenes/CustomerAnalyticsConfigurationScene/account/DefaultPinnedAccountProperties.tsx
  • products/customer_analytics/frontend/scenes/CustomerAnalyticsConfigurationScene/account/customPropertyDefinitionsLogic.ts
  • products/customer_analytics/frontend/scenes/CustomerAnalyticsConfigurationScene/account/defaultPinnedAccountPropertiesLogic.test.ts
  • products/customer_analytics/frontend/scenes/CustomerAnalyticsConfigurationScene/account/defaultPinnedAccountPropertiesLogic.ts
  • products/customer_analytics/frontend/scenes/CustomerAnalyticsConfigurationScene/account/relationshipDefinitionsLogic.ts
  • services/mcp/src/api/generated.ts
  • services/mcp/tests/unit/__snapshots__/tool-schemas/project-settings-update.json

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

Comment thread products/customer_analytics/backend/facade/enums.py Outdated
@trunk-io

trunk-io Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Static Badge   Static Badge   Static Badge

View Full Report ↗︎ ⋅ Docs

@arthurdedeus
arthurdedeus force-pushed the feat/customer-analytics-default-pinned-account-properties branch from eed080b to c0f749f Compare September 29, 2026 14:52

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
posthog/api/test/test_project.py (1)

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

Seed non-empty defaults before the permission check.

The stored list starts empty, so this assertion cannot detect an unauthorized clear that writes [] before returning HTTP 403. Set a valid non-empty list before the member request, then assert that the same list remains afterward.


ℹ️ Review info
⚙️ Run configuration

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

Review profile: QUIET

Plan: Enterprise

Run ID: f072b564-6129-433a-949e-f4b8d86f7eb2

📥 Commits

Reviewing files that changed from the base of the PR and between eed080b and c0f749f.

⛔ Files ignored due to path filters (5)
  • frontend/src/generated/core/api.schemas.ts is excluded by !**/generated/**
  • products/customer_analytics/frontend/generated/api.schemas.ts is excluded by !**/generated/**
  • products/customer_analytics/frontend/generated/api.ts is excluded by !**/generated/**
  • products/experiments/frontend/generated/api.schemas.ts is excluded by !**/generated/**
  • services/mcp/src/generated/core/api.ts is excluded by !**/generated/**
📒 Files selected for processing (12)
  • docs/internal/customer-analytics/accounts-table.md
  • posthog/api/team.py
  • posthog/api/test/test_project.py
  • posthog/schema.py
  • products/customer_analytics/backend/logic/user_customer_analytics_config.py
  • products/customer_analytics/backend/migrations/max_migration.txt
  • products/customer_analytics/backend/test/test_user_customer_analytics_config_api.py
  • products/customer_analytics/frontend/scenes/CustomerAnalyticsConfigurationScene/account/DefaultPinnedAccountProperties.stories.tsx
  • products/customer_analytics/frontend/scenes/CustomerAnalyticsConfigurationScene/account/defaultPinnedAccountPropertiesLogic.ts
  • products/customer_analytics/frontend/scenes/CustomerAnalyticsConfigurationScene/account/relationshipDefinitionsLogic.test.ts
  • products/customer_analytics/frontend/scenes/CustomerAnalyticsConfigurationScene/account/relationshipDefinitionsLogic.ts
  • services/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.

@arthurdedeus
arthurdedeus force-pushed the feat/customer-analytics-default-pinned-account-properties branch from c0f749f to 34332aa Compare September 29, 2026 15:42
@arthurdedeus
arthurdedeus marked this pull request as ready for review September 29, 2026 15:45
@github-actions

github-actions Bot commented Sep 29, 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 fe319b8 · box box-9135f7b0ee1c · ready in 1143s (push → usable) · build log · rebuilds on every push, torn down on close

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team September 29, 2026 15:45
…ount-properties

Generated-By: PostHog Desktop
Task-Id: 7d64331d-8aa6-437f-a268-595a35079e5c

@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)
products/customer_analytics/frontend/components/Accounts/AGENTS.md-166-166 (1)

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

Document inheritance for historical empty lists.

read_pinned_properties also inherits project defaults when properties.pinned_properties is an empty list, the override marker is not true, and no legacy pinned IDs exist. Add this case to the guide so readers do not treat every stored empty list as a personal override. As per coding guidelines, “Whenever you change the Accounts area … update this file in the same change so it stays an accurate map.”

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

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

Review profile: QUIET

Plan: Enterprise

Run ID: 03d6718d-bde0-4b81-9f1a-fc457801113b

📥 Commits

Reviewing files that changed from the base of the PR and between 34332aa and 778c72a.

⛔ Files ignored due to path filters (5)
  • frontend/src/generated/core/api.schemas.ts is excluded by !**/generated/**
  • products/customer_analytics/frontend/generated/api.schemas.ts is excluded by !**/generated/**
  • products/customer_analytics/frontend/generated/api.ts is excluded by !**/generated/**
  • products/experiments/frontend/generated/api.schemas.ts is excluded by !**/generated/**
  • services/mcp/src/generated/core/api.ts is excluded by !**/generated/**
📒 Files selected for processing (4)
  • products/customer_analytics/backend/facade/api.py
  • products/customer_analytics/backend/presentation/views/serializers.py
  • products/customer_analytics/frontend/components/Accounts/AGENTS.md
  • services/mcp/src/api/generated.ts

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

@posthog

posthog Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

🕓 This approval covered an earlier revision. There are new visual changes to review in the newer comment below.

✅ Visual changes approved by @arthurdedeus — baseline updated in 767a2ff.

View this run in PostHog

2 new.

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

2 updated
Run: 71f6e950-6763-448f-97d7-b812383b4daa

Co-authored-by: arthurdedeus <54866778+arthurdedeus@users.noreply.github.com>
# Conflicts:
#	products/customer_analytics/backend/facade/enums.py
#	products/customer_analytics/backend/migrations/max_migration.txt
@posthog

posthog Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

✅ Visual changes approved by @arthurdedeus — baseline updated in fe319b8.

View this run in PostHog

1 changed, 2 new.

Changed

Snapshot Before After
products-data-modeling-models-overview--paginated--dark before after

New

Snapshot Before After
lemon-ui-object-tags--editable-tags-in-narrow-container--dark (none) after
lemon-ui-object-tags--editable-tags-in-narrow-container--light (none) after

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

3 updated
Run: 0c6c426a-8106-49a8-bc4b-9a659de9beef

Co-authored-by: arthurdedeus <54866778+arthurdedeus@users.noreply.github.com>

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Not approved yet — waiting on the conditions below.

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

stamphog won't auto-review this pull request because three gates refused it. The deny-list gate matched a migration (products/customer_analytics/backend/migrations/0060_teamcustomeranalyticsconfig_default_pinned_properties.py). The size gate found 864 substantive lines against an 800-line ceiling, and the tier gate classified the change as T2-never (cross-cutting feature, 39 files, 1404 lines including docs, generated files and snapshots).

Please ask a human reviewer to look at it. To make it smaller, you could split the work into separate pull requests, for example the migration and backend API first and the settings UI afterward. Even with a split, any part that includes the migration would still need human review.

  • 👍 on the PR from greptile-apps[bot].
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✗ matches: migrations
size ✗ too large for auto-review (864L substantive in global — ceiling is 800L; 864L, 23F total, 1404L/39F incl. docs/generated/snapshots)
tier ✗ classified as T2-never: T2-never (1404L, 39F, cross-cutting, feat)
stamphog 2.3.1 .stamphog/policy.yml @ unknown · reviewed head fe319b8

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

This branch was successfully deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant