Repository navigation
feat(dashboards): add tile groups and badges to the dashboard API and MCP - #114783
trunk-io[bot] merged 8 commits into
Conversation
|
😎 This pull request was merged. |
There was a problem hiding this comment.
Not approved — this change needs a human reviewer.
Re-adding the stamphog label gives the same result unless the changed files change.
Two gates refused this pull request: the deny-list gate, because it touches migrations (0026_dashboardtile_group_key_badge.py and max_migration.txt) and public API surface (the dashboard PATCH serializers, generated API schemas, and MCP tool definitions), and the tier gate, which classified it as T2-never given its size and cross-cutting reach (781 lines across 23 files, including backend, frontend, and MCP). This decision is final and is not a judgment on the quality of the change.
The author can ask a human reviewer to look at it. Splitting it up, for example into the migration and model change, then the API and MCP changes, then the UI, would also make each piece easier to review.
Gate mechanics and policy version
| Gate | Result | |
|---|---|---|
| prerequisites | ✓ | all clear |
| deny-list | ✗ | matches: migrations, public_api |
| size | ✓ | 378L, 13F substantive, 781L/23F incl. docs/generated/snapshots — within ceiling |
| tier | ✗ | classified as T2-never: T2-never (781L, 23F, cross-cutting, feat) |
| stamphog 2.4.1 | .stamphog/policy.yml @ unknown · reviewed head af5dc8d |
There was a problem hiding this comment.
Not approved — this change needs a human reviewer.
Re-adding the stamphog label gives the same result unless the changed files change.
Two gates refused this pull request: the deny-list gate and the tier gate.
- Deny-list: the PR touches protected areas. It adds a migration (
0026_dashboardtile_group_key_badge.py, plusmax_migration.txt) and changes the public API surface (the dashboard PATCH, the generated API schemas and the MCP tool definitions). - Tier: it was classified as T2-never because it is a cross-cutting feature, at 781 lines across 23 files.
A human reviewer needs to look at this one. The author can request a review from a team member. Splitting it up would help too, for example the migration and model change in one PR, the API and MCP changes in another, and the UI decorations in a third. That would not clear the deny-list match on its own, but it would give reviewers smaller pieces.
Gate mechanics and policy version
| Gate | Result | |
|---|---|---|
| prerequisites | ✓ | all clear |
| deny-list | ✗ | matches: migrations, public_api |
| size | ✓ | 378L, 13F substantive, 781L/23F incl. docs/generated/snapshots — within ceiling |
| tier | ✗ | classified as T2-never: T2-never (781L, 23F, cross-cutting, feat) |
| stamphog 2.4.1 | .stamphog/policy.yml @ unknown · reviewed head 2d0dfc2 |
🤖 CI report
|
| Root | Eager (shipped) | Δ vs base | Budget |
|---|---|---|---|
entry (logged-out pages, app bootstrap)src/index.tsx |
1.67 MiB · 23 files | no change | █████████░ 90.6% 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.83 MiB · 675 files | no change | █████████░ 95.0% of 4.03 MiB |
authenticated shell (every logged-in page)src/scenes/AuthenticatedShell.tsx |
7.79 MiB · 2,463 files | no change | █████████░ 93.4% of 8.34 MiB |
dashboard scenesrc/scenes/dashboard/Dashboard.tsx |
9.94 MiB · 3,529 files | no change | █████████░ 91.1% of 10.92 MiB |
today home pathsrc/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx |
7.80 MiB · 2,473 files | no change | █████████░ 90.9% of 8.58 MiB |
events scenesrc/scenes/activity/explore/EventsScene.tsx |
9.55 MiB · 3,373 files | no change | █████████░ 90.9% of 10.51 MiB |
replay detail scenesrc/scenes/session-recordings/detail/SessionRecordingDetail.tsx |
12.36 MiB · 4,204 files | no change | ████████░░ 78.6% 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
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/layout/navigation-3000/navigationLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/dashboard/dashboardLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/lemon-ui/LemonMarkdown/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/RichContentEditor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/CodeSnippet/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/taxonomy/core-filter-definitions-by-group.json stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/dashboards/frontend/widgets/previews/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 zod/v4/locales/de.js stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/scenes/project-homepage/today/TodayReportPage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/queries/Query/Query.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
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 |
| 839 B | src/scenes/ChunkLoadErrorBoundary.tsx |
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
| Size | File |
|---|---|
| 317.7 KiB | ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 220.8 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 |
| 101.6 KiB | src/products.tsx |
| 99.6 KiB | src/lib/api.ts |
| 69.0 KiB | src/lib/lemon-ui/icons/icons.tsx |
| 40.7 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 |
| 29.0 KiB | ../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/schemas.js |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
| Size | File |
|---|---|
| 317.7 KiB | ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 280.8 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 220.8 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 |
| 112.1 KiB | ../packages/quill/packages/quill/dist/index.js |
| 101.6 KiB | src/products.tsx |
| 99.6 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 |
Largest files eagerly shipped from src/scenes/dashboard/Dashboard.tsx
| Size | File |
|---|---|
| 317.7 KiB | ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 280.8 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 220.8 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.9 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 |
| 112.1 KiB | ../packages/quill/packages/quill/dist/index.js |
| 101.6 KiB | src/products.tsx |
| 99.6 KiB | src/lib/api.ts |
| 93.3 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
| Size | File |
|---|---|
| 317.7 KiB | ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 280.8 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 220.8 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 |
| 112.1 KiB | ../packages/quill/packages/quill/dist/index.js |
| 101.6 KiB | src/products.tsx |
| 99.6 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 |
Largest files eagerly shipped from src/scenes/activity/explore/EventsScene.tsx
| Size | File |
|---|---|
| 317.7 KiB | ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 280.8 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 220.8 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.9 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 |
| 112.1 KiB | ../packages/quill/packages/quill/dist/index.js |
| 101.6 KiB | src/products.tsx |
| 99.6 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 |
|---|---|
| 317.7 KiB | ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 316.0 KiB | ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js |
| 280.8 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 220.8 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.9 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 |
| 112.1 KiB | ../packages/quill/packages/quill/dist/index.js |
| 101.6 KiB | src/products.tsx |
| 99.6 KiB | src/lib/api.ts |
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.24 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.24 MiB · 19 files | no change | ████░░░░░░ 39.2% of 5.72 MiB |
| Deferred (lazy) | 2.19 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 |
|---|---|
| 865.8 KiB | dist/toolbar/toolbar-app-URUTSZGS.css |
| 671.1 KiB | dist/toolbar/chunk-chunk-L74JAGOA.js |
| 259.5 KiB | dist/toolbar/chunk-chunk-K2FSNWIW.js |
| 138.9 KiB | dist/toolbar/chunk-chunk-QRZBNVWO.js |
| 131.8 KiB | dist/toolbar/chunk-chunk-FDH2IBXT.js |
| 75.2 KiB | dist/toolbar/toolbar-app-6GJ26EEZ.js |
| 69.0 KiB | dist/toolbar/chunk-chunk-TSAL54PB.js |
| 35.6 KiB | dist/toolbar/chunk-chunk-2A27PFLG.js |
| 21.7 KiB | dist/toolbar/chunk-chunk-CQDVTV3N.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 — no change
Total size of the built frontend/dist folder (all assets), compared against the base branch.
Total: 1007.71 MiB · no change
ℹ️ MCP UI apps size — 32 app(s), 17262.8 KB JS
Built size of each MCP UI app (main.js + styles.css).
| App | JS | CSS |
|---|---|---|
| debug | 597.9 KB | 203.4 KB |
| action | 454.2 KB | 203.4 KB |
| action-list | 564.3 KB | 203.4 KB |
| cohort | 453.2 KB | 203.4 KB |
| cohort-list | 563.3 KB | 203.4 KB |
| email-template | 453.0 KB | 203.4 KB |
| error-details | 469.7 KB | 203.4 KB |
| error-issue | 454.6 KB | 203.4 KB |
| error-issue-list | 564.9 KB | 203.4 KB |
| experiment | 561.4 KB | 203.4 KB |
| experiment-list | 565.0 KB | 203.4 KB |
| experiment-results | 566.4 KB | 203.4 KB |
| feature-flag | 566.9 KB | 203.4 KB |
| feature-flag-list | 570.6 KB | 203.4 KB |
| feature-flag-testing | 457.4 KB | 203.4 KB |
| inline-scan | 453.7 KB | 203.4 KB |
| insight-actors | 562.4 KB | 203.4 KB |
| llm-costs | 559.4 KB | 203.4 KB |
| session-recording | 455.4 KB | 203.4 KB |
| survey | 454.8 KB | 203.4 KB |
| survey-global-stats | 562.0 KB | 203.4 KB |
| survey-list | 565.0 KB | 203.4 KB |
| survey-stats | 562.0 KB | 203.4 KB |
| trace-span | 453.6 KB | 203.4 KB |
| trace-span-list | 564.2 KB | 203.4 KB |
| vision-observation-list | 563.4 KB | 203.4 KB |
| workflow | 453.5 KB | 203.4 KB |
| workflow-list | 563.6 KB | 203.4 KB |
| loops-review | 457.9 KB | 203.4 KB |
| query-results | 813.9 KB | 203.4 KB |
| render-ui | 897.5 KB | 203.4 KB |
| visual-review-snapshots | 458.0 KB | 203.4 KB |
ℹ️ MCP agent API — agent-facing tool changes
What this PR changes for agents, from the tool schema snapshots and tool definitions.
Tools changed (2):
| Tool | Params | Scopes | Annotations | Schema chars |
|---|---|---|---|---|
dashboard-create |
+group_titles |
3,314 -> 3,671 | ||
dashboard-update |
+group_titles |
description changed | 19,697 -> 20,827 |
⚠️ Playwright — 2 flaky
🎭 Playwright report · View test results →
- Logout in another tab results in logout in the current tab too (chromium)
- Creating a SQL insight with a variable and overriding it on a dashboard (chromium)
These issues are not necessarily caused by your changes.
Annoyed by this section? Help fix flakies and failures and it will go green!
⚠️ Backend snapshots — 1 updated (1 modified, 0 added, 0 deleted)
Query snapshots: Backend query snapshots updated
Changes: 1 snapshots (1 modified, 0 added, 0 deleted)
What this means:
- Query snapshots have been automatically updated to match current output
- These changes reflect modifications to database queries or schema
Next steps:
- Review the query changes to ensure they're intentional
- If unexpected, investigate what caused the query to change
⚠️ Django migration SQL — 1 new migration to review
We've detected new migrations on this PR. Review the SQL output for each migration:
products/dashboards/backend/migrations/0026_dashboardtile_group_key_badge.py
/home/runner/work/tmp/tool_cache/Python/3.14.7/x64/lib/python3.14/site-packages/anyio/from_thread.py:119: SyntaxWarning: 'return' in a 'finally' block
return result
/home/runner/work/tmp/tool_cache/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-10-10T18:32:37.281377Z [error ] Path must be a valid database or directory containing databases. [posthog.exceptions_capture] pid=5482 tid=139987938036608
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 "/home/runner/work/tmp/tool_cache/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.
/home/runner/work/tmp/tool_cache/Python/3.14.7/x64/lib/python3.14/site-packages/sshtunnel.py:1040: SyntaxWarning: 'return' in a 'finally' block
return (ssh_host,
/home/runner/work/tmp/tool_cache/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 badge to dashboardtile
--
ALTER TABLE "posthog_dashboardtile" ADD COLUMN "badge" varchar(20) NULL;
--
-- Add field group_key to dashboardtile
--
ALTER TABLE "posthog_dashboardtile" ADD COLUMN "group_key" varchar(100) NULL;
COMMIT;Last updated: 2026-10-10 18:33 UTC (8a40127)
✅ Django migration risk — migration analysis complete
We've analyzed your migrations for potential risks.
Summary: 1 Safe | 0 Needs Review | 0 Blocked
✅ Safe
Brief or no lock, backwards compatible
dashboards.0026_dashboardtile_group_key_badge
└─ #1 ✅ AddField
Adding nullable field requires brief lock
model: dashboardtile, field: badge
└─ #2 ✅ AddField
Adding nullable field requires brief lock
model: dashboardtile, field: group_key
📚 How to Deploy These Changes Safely
AddField:
This operation acquires a brief lock but doesn't rewrite the table.
Deployment uses lock timeouts with automatic retries, so lock contention will cause retries rather than connection pile-up.
Last updated: 2026-10-10 18:33 UTC (8a40127)
✅ ClickHouse migration SQL — none
No ClickHouse migrations in the latest push.
There was a problem hiding this comment.
Not approved — this change needs a human reviewer.
Re-adding the stamphog label gives the same result unless the changed files change.
stamphog can't auto-approve this pull request because two gates refused it. The deny-list gate matched it on migrations (the new 0026_dashboardtile_group_key_badge.py and its max_migration.txt update) and on public API (the dashboard PATCH and MCP tool changes). The tier gate classified it as T2-never because it is a cross-cutting feature spanning 23 files and 785 lines.
The author should ask a human reviewer to look at it. Splitting it up, for example the migration and model change, the API and MCP surface, and the UI decorations as separate pull requests, would also make it easier to review.
Gate mechanics and policy version
| Gate | Result | |
|---|---|---|
| prerequisites | ✓ | all clear |
| deny-list | ✗ | matches: migrations, public_api |
| size | ✓ | 382L, 13F substantive, 785L/23F incl. docs/generated/snapshots — within ceiling |
| tier | ✗ | classified as T2-never: T2-never (785L, 23F, cross-cutting, feat) |
| stamphog 2.4.1 | .stamphog/policy.yml @ unknown · reviewed head bcc6068 |
|
PostHog Review alpha 🦔 If you find any issues helpful - please reply "valid", "invalid", etc., for evaluation purposes 🙏 |
|
👋 Visual changes detected for this PR. Review and approve in PostHog Visual Review If these changes are unexpected, they may be caused by a flaky test or a broken snapshot on master. Don't approve — rerun the job or wait for a fix. Install the Visual Review Chrome extension to see visual review results at the top of your pull requests. |
|
pauldambra
left a comment
There was a problem hiding this comment.
QA Swarm review complete. See inline comments and the summary comment.
|
Note 🤖 Automated comment by QA Swarm — not written by a human Review of this layer by one stronger-model reviewer (opus) or a cheap first pass (sonnet). This PR now holds the backend layer only. The UI is in #114811. Verdict: ✅ APPROVE (round 3 @ e5d845b)No blocking findings. The review found three issues in the API, and all three are fixed. One item is left for a person on the UI layer. Key findings
Checked and cleanMigration 0026 (nullable, no default, no hot table), edit permission and tenancy on the PATCH path, partial updates, clearing a badge with null, copy and transfer of both fields, the analytics event carries no free text, and the MCP text. Previous rounds (2)round 1 @ bcc6068: REQUEST CHANGES on the one-PR version: clipped decorations in view mode, a round 2 @ 5e9cc36: APPROVE, LOW and NIT items only. The PR was then split into this backend layer and the UI layer #114811. The split reviews (two rounds on the layers) found and fixed the items above. Automated by QA Swarm — not a human review |
There was a problem hiding this comment.
Not approved — this change needs a human reviewer.
Re-adding the stamphog label gives the same result unless the changed files change.
stamphog can't auto-approve this pull request. The deny-list gate refused it because it adds a database migration (0026_dashboardtile_group_key_badge.py) and changes the public API surface (the dashboard PATCH, the MCP tool definitions, and the generated API schemas). The tier gate also classified it as T2-never, because it is a cross-cutting feature touching 28 files and about 1,000 lines once docs, generated files and snapshots are counted.
Please ask a human reviewer to look at it. Splitting it into a migration and API change, plus a separate frontend change, would also make it easier to review.
Gate mechanics and policy version
| Gate | Result | |
|---|---|---|
| prerequisites | ✓ | all clear |
| deny-list | ✗ | matches: migrations, public_api |
| size | ✓ | 458L, 15F substantive, 1009L/28F incl. docs/generated/snapshots — within ceiling |
| tier | ✗ | classified as T2-never: T2-never (1009L, 28F, cross-cutting, feat) |
| stamphog 2.4.1 | .stamphog/policy.yml @ unknown · reviewed head 5e9cc36 |
|
Note 🤖 Automated comment by PR Shepherd — not written by a human Screenshots: tile decorations in view modeThese are real renders of the dashboard scene in Storybook, using fake data. The first column is the PR before commit 5e9cc36. The second column is after. In view mode the crown, group title and hog were clipped by
At about 520px wide (after the fix):
The crown overhangs the card's left edge into the page margin at this width, but it is not cut off. Not checked in a browser: groups whose top-left tile is a hidden widget, and compaction with stored vertical gaps. Unit tests cover both. |
|
Risk: No findings The delta since the last review is a two-line generated-type addition ( Sentinel reviewed |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5e9cc36bce
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Not approved — this change needs a human reviewer.
Re-adding the stamphog label gives the same result unless the changed files change.
stamphog can't auto-review this pull request. Two gates refused it: the deny-list gate matched it on migrations (products/dashboards/backend/migrations/0026_dashboardtile_group_key_badge.py) and on public API (the dashboard PATCH and MCP tool schema changes). The tier gate classified it as T2-never because it is a cross-cutting feature touching 29 files and 1013 lines.
The author should ask a human reviewer to look at it. Splitting it could also help, for example the migration and model change in one pull request and the API/MCP and UI changes in another.
Gate mechanics and policy version
| Gate | Result | |
|---|---|---|
| prerequisites | ✓ | all clear |
| deny-list | ✗ | matches: migrations, public_api |
| size | ✓ | 458L, 15F substantive, 1013L/29F incl. docs/generated/snapshots — within ceiling |
| tier | ✗ | classified as T2-never: T2-never (1013L, 29F, cross-cutting, feat) |
| stamphog 2.4.1 | .stamphog/policy.yml @ unknown · reviewed head 33eb6e4 |
There was a problem hiding this comment.
Not approved — this change needs a human reviewer.
Re-adding the stamphog label gives the same result unless the changed files change.
stamphog can't auto-review this pull request because two gates refused it. The deny-list gate flagged it for touching migrations (0026_dashboardtile_group_key_badge.py and max_migration.txt) and public API surface (the dashboard API and the generated API and MCP schemas). The tier gate classified it as T2-never: a cross-cutting feature across 25 files and 912 lines. The size gate itself passed.
Please ask a human reviewer to look at it. Splitting the migration and the public API changes into separate, smaller pull requests may also make review easier.
Gate mechanics and policy version
| Gate | Result | |
|---|---|---|
| prerequisites | ✓ | all clear |
| deny-list | ✗ | matches: migrations, public_api |
| size | ✓ | 300L, 10F substantive, 912L/25F incl. docs/generated/snapshots — within ceiling |
| tier | ✗ | classified as T2-never: T2-never (912L, 25F, cross-cutting, feat) |
| stamphog 2.4.1 | .stamphog/policy.yml @ unknown · reviewed head b0ba45a |
|
stamphog does not review bot-authored pull requests, so the trigger label has been removed. This change needs a human reviewer. |
There was a problem hiding this comment.
Not approved — this change needs a human reviewer.
Re-adding the stamphog label gives the same result unless the changed files change.
stamphog can't auto-review this pull request because two gates refused it. The deny-list gate flagged it for touching migrations (products/dashboards/backend/migrations/0026_dashboardtile_group_key_badge.py) and public API surface (the dashboard API in products/dashboards/backend/api/dashboard.py and the MCP tool definitions). The tier gate also classified it as T2-never, since it is a cross-cutting feature change spanning 25 files and 914 lines. The size gate itself passed.
The next step is to ask a human reviewer to look at it, with extra attention on the migration. Splitting the schema change from the API and MCP changes could also make the review easier.
Gate mechanics and policy version
| Gate | Result | |
|---|---|---|
| prerequisites | ✓ | all clear |
| deny-list | ✗ | matches: migrations, public_api |
| size | ✓ | 300L, 10F substantive, 914L/25F incl. docs/generated/snapshots — within ceiling |
| tier | ✗ | classified as T2-never: T2-never (914L, 25F, cross-cutting, feat) |
| stamphog 2.4.1 | .stamphog/policy.yml @ unknown · reviewed head 13b701e |
sampennington
left a comment
There was a problem hiding this comment.
I like it, it's fun, but I am still wondering a bit where this will be used by users 😆
I can't think of much other than doing a/b tests and tracking it on a dashboard and not in experiments
We can see if it gets used, I guess? :)
… MCP Dashboard tiles get an optional group_key and a badge (winner or cheeky-hog). A dashboard can give each group key a title through customization.group_titles. The columns are nullable, so the migration is safe to run before the code. The dashboard PATCH and the MCP tools can set all three. Nothing draws them yet. Generated-By: PostHog Desktop Task-Id: a0588627-3b8c-4fc5-b331-716af1994715
…group keys Address review findings on the tile groups API. - Load the widget with the tile when a PATCH marks widget tiles, so each marked tile no longer costs a query - Reject NUL characters in group keys and group title keys with a 400, not a 500 - Validate every tile badge and group key before any write, so a bad value cannot leave earlier tiles saved Generated-By: PostHog Desktop Task-Id: a0588627-3b8c-4fc5-b331-716af1994715
The early check only covered tiles that edit display fields. A tile that also edited text, a button or a widget could carry a bad badge or group key, and the 400 came after the dashboard and the content were saved. Every tile in the payload is now checked before any write. Generated-By: PostHog Desktop Task-Id: a0588627-3b8c-4fc5-b331-716af1994715
…oved A group belongs to a tile and its dashboard, and the group titles live on the dashboard. Copying or moving one tile to another dashboard breaks that pairing, so the tile arrives without a group key. Whole dashboard duplication keeps the key, because the titles are copied with the dashboard. The badge is kept in every case. Generated-By: PostHog Desktop Task-Id: a0588627-3b8c-4fc5-b331-716af1994715
…ts types The API only clears a badge with null, but blank=True made the generated types accept an empty string. The column is new and unmerged, so the migration is edited in place. Generated API and MCP types are regenerated. Generated-By: PostHog Desktop Task-Id: a0588627-3b8c-4fc5-b331-716af1994715
The badge column no longer allows an empty string, so the tile inline in the Django admin made the badge a required field. Every unmarked tile then failed to save with "This field is required". The inline now treats the badge as optional, and a blank choice saves as null. Generated-By: PostHog Desktop Task-Id: a0588627-3b8c-4fc5-b331-716af1994715
The test class for the tile inline reads no row, so it extends SimpleTestCase. The repo check for test classes that take a database they never use failed on the base class. Generated-By: PostHog Desktop Task-Id: a0588627-3b8c-4fc5-b331-716af1994715
13b701e to
8a40127
Compare
There was a problem hiding this comment.
Not approved — this change needs a human reviewer.
Re-adding the stamphog label gives the same result unless the changed files change.
stamphog can't auto-review this pull request because two gates refused it. The deny-list gate flagged it for touching migrations (products/dashboards/backend/migrations/0026_dashboardtile_group_key_badge.py and max_migration.txt) and for changing the public API (the dashboard serializers and the generated API and MCP schemas). The tier gate classified it as T2-never: it is a cross-cutting feat of 914 lines across 25 files, and that class is never auto-reviewed.
Please ask a human reviewer to take it. Splitting the migration and model change from the API and MCP changes could make each piece easier to review, though the migration and public API changes would still need a human either way.
- sampennington reviewed the current head.
Gate mechanics and policy version
| Gate | Result | |
|---|---|---|
| prerequisites | ✓ | all clear |
| deny-list | ✗ | matches: migrations, public_api |
| size | ✓ | 300L, 10F substantive, 914L/25F incl. docs/generated/snapshots — within ceiling |
| tier | ✗ | classified as T2-never: T2-never (914L, 25F, cross-cutting, feat) |
| stamphog 2.4.1 | .stamphog/policy.yml @ unknown · reviewed head 8a40127 |
|
Stacked PR 114881 failed testing in the merge queue. Please investigate the failure and re-submit the stack. |
|
This pull request was merged into |







Problem
This is layer 1 of 2. It stores the fields and exposes them in the API and MCP. Nothing draws them yet. Layer 2 (#114811) adds the UI and closes the issue. Refs #114760.
Origin
8e23010Changes
group_keyand abadge(winnerorcheeky-hog). The dashboard PATCH and the MCP update tools set them.customization.group_titles. Keys are stripped like tile keys. Blank keys, duplicate keys and more than 100 titles are rejected.group_keyandbadge, and the titles come with it. Copying or moving one tile to another dashboard drops itsgroup_keyand keeps the badge, because a group belongs to the tile and its dashboard.badgeorgroup_keyon any tile returns 400 before anything is saved. A null character in a group key or title key also returns 400. Onlynullclears a badge, and an empty string is rejected.dashboard tile marking changedevent fires when a PATCH changes a tile'sbadgeorgroup_key.No UI changes in this layer, so there are no screenshots.
How did you test this code?
Test rationale: The group title tests catch a padded title key that saves but never matches a tile, and the rejection cases catch blank, duplicate and over-limit input. The existing group title test only covered the length check.
test_dashboard.py, parameterized: group title keys (padded keys match, old padded keys read back stripped, blank, duplicate, too long and null character rejected); tilegroup_keyandbadgeset, kept on duplicate, cleared and rejected; a bad marking on one tile saves nothing, also when the tile edits text; single tile copy and move dropgroup_keyand keep the badge.test_dashboard_widgets.py: marking widget tiles loads no widget on its own; widget copy and move dropgroup_key.DashboardTilefield snapshot and the MCP tool schema snapshots cover the new fields.makemigrations --checkandhogli build:openapilocally. The only failures aretest_shared_dashboardandtest_shared_dashboard_does_not_expose_where_it_is_filed, which need a frontend build in the sandbox.Release status
Docs update
None. The MCP tool descriptions carry the new fields.
🤖 Agent context
Autonomy: Human-driven (agent-assisted). A self-driving run opened this PR, and a person then took it over.
Agent: Claude Code, Sonnet 5 (main loop and runners) and Opus 5.5 (one review and one frontend fix runner)
Created with PostHog Desktop