Skip to content

feat(mobile): add the native posthog triage app - #118584

Open
cvolzer3 wants to merge 105 commits into
masterfrom
codex/mobile-app
Open

cvolzer3 wants to merge 105 commits into
masterfrom
codex/mobile-app

Conversation

@cvolzer3

@cvolzer3 cvolzer3 commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Problem

PostHog users need to triage reports, respond to agents, and inspect their dashboards from iOS and Android.

Mobile Sessions and Errors can delay initial results while optional recommendations or list chart data load.

Changes

A mobile companion to the core PostHog experience, intended to complement workflows for users that are on the go.

It will not handle the long-tail of user actions that are possible in the PostHog web experience.

posthog-mobile-preview

@cvolzer3 cvolzer3 self-assigned this Oct 16, 2026
@trunk-io

trunk-io Bot commented Oct 16, 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

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

🤖 CI report

🚨 Trunk lane — universal lane

This PR is assigned to the universal lane. It cannot merge in parallel with other PRs, so it can take longer to merge. Ask dev-ex if you think this is wrong.

⚠️ Complexity (TypeScript) — 148 functions above the limit (max 47)

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
ChatSheet products/mobile/src/features/chat/ChatSheet.tsx:95 47 10
followRun products/mobile/src/features/handoff/useTaskRun.ts:238 47 10
actionBarStateFor products/mobile/src/features/handoff/actionBarState.ts:6 44 10
reduceFrame products/mobile/src/features/handoff/runStream.ts:324 40 10
useInsightScreen products/mobile/src/features/views/viewHooks.ts:364 39 10
useSession products/mobile/src/features/chat/session/useSession.ts:557 36 10
insightSnapshot products/mobile/src/model/adapters/insightSnapshot.ts:444 35 10
<anonymous> products/mobile/src/features/chat/session/useSession.ts:1109 34 10
<anonymous> products/mobile/src/features/chat/session/useSession.ts:1276 34 10
ActionBar products/mobile/src/features/handoff/ActionBar.tsx:36 32 10
ChartPin products/mobile/src/features/views/list/PinCard.tsx:261 29 10
reduceNotification products/mobile/src/features/handoff/runStream.ts:248 28 10
ViewsBody products/mobile/src/features/views/list/ViewsScreen.tsx:148 28 10
InsightTile products/mobile/src/features/views/dashboard/DashboardTile.tsx:66 27 10
activityText products/mobile/src/model/adapters/report.ts:384 27 10
formatInsightValue products/mobile/src/model/adapters/insightFormatting.ts:103 26 10
reduceSessionUpdate products/mobile/src/features/handoff/runStream.ts:175 25 10
<anonymous> products/mobile/src/features/chat/session/useSession.ts:777 24 10
toolCallUpdate products/mobile/src/features/chat/session/sessionReducer.ts:582 23 10
followSessionRun products/mobile/src/features/chat/session/useSession.ts:474 23 10
<anonymous> products/mobile/src/features/chat/session/useSession.ts:1446 23 10
mergeLiveRun products/mobile/src/features/handoff/liveReport.ts:73 23 10
parsePermissionRequest products/mobile/src/features/handoff/permissionRequest.ts:92 23 10
shouldPresent products/mobile/src/features/notifications/notificationPresentation.ts:13 23 10
syncOnce products/mobile/src/features/notifications/pushRegistration.ts:110 23 10
SqlTable products/mobile/src/ui/chart/SqlTable.tsx:134 23 10
ErrorsListBody products/mobile/src/features/errors/ErrorsListScreen.tsx:71 22 10
useLiveReport products/mobile/src/features/report/hooks.ts:32 22 10
SignInScreen products/mobile/src/auth/SignInScreen.tsx:33 21 10
<anonymous> products/mobile/src/features/chat/objectTags.ts:122 21 10
applyFrame products/mobile/src/features/chat/session/sessionReducer.ts:841 21 10
accept products/mobile/src/features/handoff/runStream.ts:460 21 10
openFromNotification products/mobile/src/features/notifications/useNotificationRouting.ts:18 21 10
ReplayListBody products/mobile/src/features/replay/ReplayListScreen.tsx:60 21 10
sessionListRows products/mobile/src/features/replay/sessionAdapters.ts:433 21 10
SettingsScreen products/mobile/src/features/settings/SettingsScreen.tsx:30 20 10
DayCell products/mobile/src/features/views/range/RangeCalendar.tsx:204 20 10
<anonymous> products/mobile/src/features/chat/objectTags.ts:150 19 10
IssueAssigneeSheet products/mobile/src/features/errors/IssueAssigneeSheet.tsx:31 19 10
PushStatusGroup products/mobile/src/features/settings/PushStatusGroup.tsx:49 19 10
<anonymous> products/mobile/src/features/chat/session/useSession.ts:715 18 10
handoffErrorMessage products/mobile/src/features/handoff/kickoff.ts:184 18 10
usePullRequestChecks products/mobile/src/features/handoff/usePullRequestChecks.ts:51 18 10
shouldShowWhileActive products/mobile/src/features/notifications/foregroundNotifications.ts:46 18 10
NumberPin products/mobile/src/features/views/list/PinCard.tsx:209 18 10
runQuery products/mobile/src/api/query.ts:79 17 10
Composer products/mobile/src/features/chat/Composer.tsx:82 17 10
summarizeTools products/mobile/src/features/chat/session/sessionDisplay.ts:62 17 10
TabsHeader products/mobile/src/features/tabs/TabsHeader.tsx:57 17 10
aboutContent products/mobile/src/features/views/about/aboutContent.ts:234 17 10
RetentionGrid products/mobile/src/ui/chart/RetentionGrid.tsx:92 17 10
taskRunError products/mobile/src/features/chat/chatErrors.ts:35 16 10
toChatError products/mobile/src/features/chat/chatErrors.ts:61 16 10
useLiveChatList products/mobile/src/features/chats/hooks.ts:25 16 10
IssueLoaded products/mobile/src/features/errors/IssueScreen.tsx:215 16 10
sameLive products/mobile/src/features/handoff/liveReport.ts:116 16 10
<anonymous> products/mobile/src/demo/views.ts:521 15 10
sessionThreadReducer products/mobile/src/features/chat/session/sessionReducer.ts:905 15 10
sendConfigOption products/mobile/src/features/chat/session/useSession.ts:264 15 10
PullRequestCard products/mobile/src/features/handoff/PullRequestCard.tsx:27 15 10
pullRequestChecksStatus products/mobile/src/features/handoff/usePullRequestChecks.ts:24 15 10
<anonymous> products/mobile/src/features/inbox/hooks.ts:355 15 10
ProjectSheet products/mobile/src/features/projects/ProjectSheet.tsx:55 15 10
AssigneeSheet products/mobile/src/features/reviewers/AssigneeSheet.tsx:70 15 10
AboutBody products/mobile/src/features/views/about/AboutSheet.tsx:52 15 10
webLinkTarget products/mobile/src/lib/deepLinkKind.ts:105 15 10
numberParts products/mobile/src/model/adapters/insightSnapshot.ts:366 15 10
reportView products/mobile/src/model/adapters/report.ts:612 15 10
<anonymous> products/mobile/src/auth/authStore.ts:200 14 10
checkAiApproval products/mobile/src/features/ai/aiApproval.ts:108 14 10
observe products/mobile/src/features/chat/session/sessionTurn.ts:51 14 10
HandoffSheet products/mobile/src/features/handoff/HandoffSheet.tsx:59 14 10
deliverAnswer products/mobile/src/features/handoff/hooks.ts:87 14 10
<anonymous> products/mobile/src/features/handoff/hooks.ts:210 14 10
<anonymous> products/mobile/src/features/handoff/useTaskRun.ts:179 14 10
planKeyframes products/mobile/src/features/hedgehog/choreography.ts:213 14 10
entryText products/mobile/src/features/replay/sessionAdapters.ts:207 14 10
<anonymous> products/mobile/src/features/views/InsightVisual.tsx:161 14 10
blockedState products/mobile/src/features/views/freshness.ts:140 14 10
FullScreenBody products/mobile/src/features/views/fullScreen/FullScreenChart.tsx:83 14 10
usePins products/mobile/src/features/views/pinHooks.ts:92 14 10
<anonymous> products/mobile/src/features/views/useViewRefresh.ts:341 14 10
parseMermaidMessage products/mobile/src/lib/mermaid.ts:42 14 10
formatMetricValue products/mobile/src/model/adapters/metric.ts:41 14 10
cohortView products/mobile/src/model/adapters/retention.ts:29 14 10
sqlScalar products/mobile/src/model/adapters/sqlVisualization.ts:105 14 10
sqlResultDisplay products/mobile/src/model/adapters/sqlVisualization.ts:324 14 10
refreshOnce products/mobile/src/auth/authStore.ts:492 13 10
human products/mobile/src/features/chat/session/sessionReducer.ts:449 13 10
applySessionUpdate products/mobile/src/features/chat/session/sessionReducer.ts:806 13 10
<anonymous> products/mobile/src/features/chat/session/useSession.ts:1530 13 10
<anonymous> products/mobile/src/features/chat/useChatDraft.ts:92 13 10
highlightStart products/mobile/src/features/errors/issueModel.ts:118 13 10
emptyCopy products/mobile/src/features/errors/issueModel.ts:336 13 10
planAnswer products/mobile/src/features/handoff/permissionRequest.ts:206 13 10
resolveToolCall products/mobile/src/features/handoff/toolPolicy.ts:77 13 10
InboxScreen products/mobile/src/features/inbox/InboxScreen.tsx:34 13 10
ReportContextMenu products/mobile/src/features/inbox/ReportContextMenu.tsx:98 13 10
useLiveInboxItems products/mobile/src/features/inbox/hooks.ts:119 13 10
SessionBody products/mobile/src/features/replay/SessionScreen.tsx:176 13 10
parseCustomSnooze products/mobile/src/features/snooze/snoozeTimes.ts:60 13 10
BrowseList products/mobile/src/features/views/browse/BrowseListScreen.tsx:98 13 10
InsightBody products/mobile/src/features/views/insight/InsightScreen.tsx:107 13 10
writeTiles products/mobile/src/features/views/items.ts:324 13 10
TileOption products/mobile/src/features/views/list/ChooseTileSheet.tsx:215 13 10
<anonymous> products/mobile/src/features/views/list/ViewsScreen.tsx:461 13 10
authResultError products/mobile/src/auth/oauth.ts:155 12 10
authorize products/mobile/src/auth/oauth.ts:179 12 10
InsightCard products/mobile/src/features/chat/InsightCard.tsx:12 12 10
progress products/mobile/src/features/chat/session/sessionReducer.ts:667 12 10
useIssuesList products/mobile/src/features/errors/hooks.ts:95 12 10
ModelPage products/mobile/src/features/handoff/ModelPage.tsx:67 12 10
handoffFailureReason products/mobile/src/features/handoff/kickoff.ts:150 12 10
<anonymous> products/mobile/src/features/hedgehog/choreography.ts:313 12 10
RowActions products/mobile/src/features/inbox/InboxRowActions.tsx:94 12 10
SessionContent products/mobile/src/features/replay/SessionScreen.tsx:74 12 10
Header products/mobile/src/features/replay/SessionScreen.tsx:360 12 10
tileMeta products/mobile/src/features/views/dashboard/DashboardTile.tsx:213 12 10
rangeLabel products/mobile/src/features/views/rangePresets.ts:156 12 10
<anonymous> products/mobile/src/features/views/useViewRefresh.ts:382 12 10
kindLabelOf products/mobile/src/model/adapters/insightSnapshot.ts:151 12 10
<anonymous> products/mobile/src/model/adapters/report.ts:345 12 10
webOverviewSnapshot products/mobile/src/model/adapters/webOverview.ts:53 12 10
ChartOverlay products/mobile/src/ui/chart/ChartOverlay.tsx:38 12 10
send products/mobile/src/api/client.ts:150 11 10
<anonymous> products/mobile/src/auth/authStore.ts:285 11 10
validateHost products/mobile/src/auth/regions.ts:70 11 10
AiApprovalDialog products/mobile/src/features/ai/AiApprovalDialog.tsx:14 11 10
ChatAccessGate products/mobile/src/features/chat/ChatAccessGate.tsx:19 11 10
queueReducer products/mobile/src/features/chat/messageQueue.ts:48 11 10
matchTag products/mobile/src/features/chat/objectTags.ts:35 11 10
SessionThread products/mobile/src/features/chat/session/SessionThread.tsx:54 11 10
userAttachment products/mobile/src/features/chat/session/sessionReducer.ts:147 11 10
noteAttachment products/mobile/src/features/chat/session/sessionReducer.ts:479 11 10
<anonymous> products/mobile/src/features/chat/session/useSession.ts:413 11 10
IssueListRow products/mobile/src/features/errors/IssueListRow.tsx:27 11 10
sampleSessions products/mobile/src/features/errors/issueModel.ts:236 11 10
ProjectSwitcher products/mobile/src/features/settings/ProjectSwitcher.tsx:18 11 10
TabsChrome products/mobile/src/features/tabs/TabsLayout.tsx:90 11 10
<anonymous> products/mobile/src/features/views/list/ModeSegment.tsx:38 11 10
<anonymous> products/mobile/src/features/views/list/ViewsScreen.tsx:342 11 10
formatRangeSpan products/mobile/src/features/views/rangePresets.ts:124 11 10
<anonymous> products/mobile/src/features/views/useViewRefresh.ts:257 11 10
<anonymous> products/mobile/src/features/views/useViewRefresh.ts:305 11 10
<anonymous> products/mobile/src/features/views/useViewRefresh.ts:426 11 10
sqlValuePresentation products/mobile/src/model/adapters/sqlVisualization.ts:280 11 10
<anonymous> products/mobile/src/ui/chart/gestures/useChartGestures.ts:127 11 10
formatSqlCell products/mobile/src/ui/chart/sqlCellFormat.ts:42 11 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) — 30 new duplicated blocks (worst 627 tokens)

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

First copy Second copy Lines Tokens
products/desktop/packages/core/src/inbox/objectKinds.generated.ts:5 products/mobile/src/features/chat/objectKinds.generated.ts:5 174 627
products/mobile/src/features/chat/session/sessionDisplay.ts:78 products/posthog_ai/frontend/utils/toolRunSummary.ts:36 24 264
products/mobile/src/features/chat/attachments/attachmentRules.ts:6 products/posthog_ai/frontend/utils/attachments.ts:10 48 189
products/mobile/src/features/chat/ContextChip.tsx:43 products/mobile/src/features/chat/ObjectContextChip.tsx:30 38 171
products/mobile/src/features/chat/useChatDraft.test.tsx:99 products/mobile/src/features/chat/useChatDraft.test.tsx:143 14 167
products/mobile/src/features/errors/listPagination.test.tsx:18 products/mobile/src/features/replay/listPagination.test.tsx:20 16 154
products/mobile/src/features/chat/session/sessionDisplay.ts:47 products/posthog_ai/frontend/utils/toolRunSummary.ts:4 13 109
products/mobile/src/features/priority/PrioritySheet.tsx:124 products/mobile/src/features/snooze/SnoozeSheet.tsx:134 29 108
products/mobile/src/features/inbox/ReportContextMenu.tsx:121 products/mobile/src/features/views/list/ViewsMenu.tsx:43 19 95
products/mobile/src/features/errors/IssueAssigneeSheet.tsx:115 products/mobile/src/features/verdict/VerdictSheet.tsx:152 24 94
products/mobile/src/ui/chart/ChartTooltip.tsx:114 products/mobile/src/ui/chart/RangeTooltip.tsx:55 23 92
products/mobile/src/features/projects/ProjectSheet.tsx:234 products/mobile/src/features/reviewers/AssigneeSheet.tsx:302 24 88
products/mobile/src/features/views/insight/InsightScreen.tsx:146 products/mobile/src/features/views/webAnalytics/WebAnalyticsScreen.tsx:117 19 87
products/mobile/src/features/chat/ChatMarkdown.tsx:124 products/mobile/src/features/report/details/ReportMarkdown.tsx:86 25 84
products/mobile/src/features/chat/session/sessionReducer.ts:167 products/posthog_ai/frontend/logics/runStreamLogic.ts:480 12 84
products/mobile/src/features/projects/ProjectSheet.tsx:205 products/mobile/src/features/reviewers/AssigneeSheet.tsx:243 23 84
products/mobile/src/features/chat/InsightCard.tsx:51 products/mobile/src/features/chat/QueryCard.tsx:38 15 83
products/mobile/src/features/errors/ErrorsListScreen.tsx:313 products/mobile/src/features/replay/ReplayListScreen.tsx:251 23 83
products/mobile/src/features/handoff/actionBarState.ts:7 products/mobile/src/features/handoff/liveReport.ts:87 11 83
products/mobile/src/features/inbox/useRowMenuTrigger.ts:21 products/mobile/src/features/views/list/useMenuTrigger.ts:14 13 83
products/mobile/src/features/errors/IssueActionBar.tsx:104 products/mobile/src/features/handoff/ActionPill.tsx:51 21 82
nodejs/src/ingestion/common/persons/batch-writing-person-store.model.test.ts:591 products/mobile/src/features/hedgehog/choreography.ts:102 12 81
products/mobile/src/features/snooze/CustomSnoozePicker.tsx:97 products/mobile/src/features/snooze/CustomSnoozePicker.web.tsx:81 23 81
products/mobile/src/features/errors/IssueAssigneeSheet.tsx:113 products/mobile/src/features/errors/SuppressSheet.tsx:56 20 79
products/mobile/src/features/report/ReportScreen.tsx:163 products/mobile/src/features/report/details/ReportDetailsScreen.tsx:89 11 76
products/mobile/src/features/chats/ChatRow.tsx:84 products/mobile/src/features/errors/IssueListRow.tsx:77 21 75
products/mobile/src/features/chat/attachments/AttachMenuSheet.tsx:48 products/mobile/src/features/views/screen/ViewMoreSheet.tsx:59 19 71
products/mobile/src/features/chat/session/sessionDisplay.ts:62 products/posthog_ai/frontend/utils/toolRunSummary.ts:19 14 70
products/mobile/src/features/chats/ChatRow.tsx:90 products/mobile/src/features/inbox/InboxRow.tsx:161 19 70
products/mobile/src/features/snooze/CustomSnoozePicker.tsx:61 products/mobile/src/features/snooze/CustomSnoozePicker.web.tsx:42 11 70
✅ Bundle size — no change

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

Total: 77.01 MiB · no change

No file changed by more than 1000 B.

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

⚠️ Eager graph — 1 over 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.01 MiB · 24 files no change █████████░ 87.9% of 1.14 MiB
logged-out boot: index + App + bootApp (preloaded by every page, including /login)
src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
2.97 MiB · 620 files no change ██████████ 98.0% of 3.03 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
7.50 MiB · 2,563 files no change ██████████ 100.0% of 7.50 MiB
dashboard scene
src/scenes/dashboard/Dashboard.tsx
9.95 MiB · 3,730 files no change ██████████ 99.3% of 10.01 MiB
today home path
src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
7.68 MiB · 2,656 files no change ██████████ 99.2% of 7.74 MiB
🟡 AI-first home path
src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
7.92 MiB · 2,649 files no change ██████████ 100.2% of 7.91 MiB
events scene
src/scenes/activity/explore/EventsScene.tsx
9.39 MiB · 3,512 files no change ██████████ 97.9% of 9.59 MiB
web analytics scene
src/scenes/web-analytics/WebAnalyticsScene.tsx
10.33 MiB · 3,763 files no change ██████████ 95.3% of 10.83 MiB
replay detail scene
src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
8.41 MiB · 3,088 files no change █████████░ 88.8% of 9.47 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/zod/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/billing/billingLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/ProductEmptyState/productSetupPreloadLogic.ts 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
🟢 products/wizard/frontend/runs/WizardRunDetailsDrawer.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/TodayShell.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/TodayContextPanelFloating.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/TodaySidePanel.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/TodayTopBar.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/todayRailItems.tsx 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/queries/validators.js 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
🟢 packages/quill/packages/charts/ stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 products/posthog_ai/frontend/components/tool/widgets/VisualizationWidget.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 products/posthog_ai/frontend/components/tool/widgets/RecordingsWidget.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 src/queries/Query/Query.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 products/posthog_ai/frontend/api/primitives.ts stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 src/scenes/notebooks/Notebook/notebookLogic.ts stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.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
🟢 src/queries/validators.js stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/scenes/web-analytics/LiveMetricsDashboard/LiveWebAnalyticsMetrics.tsx stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 products/product_analytics/frontend/insights/trends/WorldMap/countryVectors.tsx stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 src/scenes/settings/SettingsMap.tsx stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
🟢 @jspm/core/nodelibs/browser/fs stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.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.7 KiB ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js
4.0 KiB ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js
1.7 KiB ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
1.6 KiB src/lib/utils/bootTiming.ts
1.6 KiB src/index.tsx
1.2 KiB src/RootErrorBoundary.tsx
949 B src/scenes/ChunkLoadErrorBoundary.tsx
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
Size File
322.9 KiB ../node_modules/.pnpm/posthog-js@1.438.23_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
217.5 KiB ../node_modules/.pnpm/@posthog+icons@0.39.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
102.1 KiB src/products.tsx
86.1 KiB src/lib/api.ts
68.3 KiB src/lib/lemon-ui/icons/icons.tsx
41.9 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
36.4 KiB ../node_modules/.pnpm/kea@4.0.0-pre.6_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js
27.0 KiB ../node_modules/.pnpm/tailwind-merge@2.2.2/node_modules/tailwind-merge/dist/bundle-mjs.mjs
24.6 KiB ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
322.9 KiB ../node_modules/.pnpm/posthog-js@1.438.23_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.8 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
102.1 KiB src/products.tsx
94.5 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.5 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
86.1 KiB src/lib/api.ts
Largest files eagerly shipped from src/scenes/dashboard/Dashboard.tsx
Size File
322.9 KiB ../node_modules/.pnpm/posthog-js@1.438.23_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.8 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
144.1 KiB ../packages/quill/packages/primitives/dist/index.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
102.1 KiB src/products.tsx
94.5 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.5 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
322.9 KiB ../node_modules/.pnpm/posthog-js@1.438.23_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.8 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
102.1 KiB src/products.tsx
94.5 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.5 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
86.1 KiB src/lib/api.ts
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
Size File
322.9 KiB ../node_modules/.pnpm/posthog-js@1.438.23_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.8 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
144.1 KiB ../packages/quill/packages/primitives/dist/index.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
102.1 KiB src/products.tsx
94.5 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.5 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
322.9 KiB ../node_modules/.pnpm/posthog-js@1.438.23_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.8 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
144.1 KiB ../packages/quill/packages/primitives/dist/index.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
102.1 KiB src/products.tsx
94.5 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.5 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/web-analytics/WebAnalyticsScene.tsx
Size File
322.9 KiB ../node_modules/.pnpm/posthog-js@1.438.23_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
182.9 KiB src/queries/validators.js
153.8 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
144.1 KiB ../packages/quill/packages/primitives/dist/index.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
102.1 KiB src/products.tsx
94.5 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/session-recordings/detail/SessionRecordingDetail.tsx
Size File
322.9 KiB ../node_modules/.pnpm/posthog-js@1.438.23_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
316.1 KiB ../node_modules/.pnpm/posthog-js@1.438.23_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.8 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
102.1 KiB src/products.tsx
94.5 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.5 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.30 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.30 MiB · 18 files no change ████░░░░░░ 40.2% of 5.72 MiB
Deferred (lazy) 2.11 MiB · 40 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
912.8 KiB dist/toolbar/toolbar-app-5QWAE5P2.css
694.7 KiB dist/toolbar/chunk-chunk-ZOYRVWMI.js
259.9 KiB dist/toolbar/chunk-chunk-ONRHJSKR.js
131.8 KiB dist/toolbar/chunk-chunk-FDH2IBXT.js
130.4 KiB dist/toolbar/chunk-chunk-O6NJ623V.js
74.8 KiB dist/toolbar/toolbar-app-PAZXMELH.js
69.0 KiB dist/toolbar/chunk-chunk-TSAL54PB.js
35.6 KiB dist/toolbar/chunk-chunk-ZA2V5YRY.js
21.7 KiB dist/toolbar/chunk-chunk-PW4LOT2W.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 — 🔺 +156 B (+0.0%)

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

Total: 970.95 MiB · 🔺 +156 B (+0.0%)

⚠️ Playwright — 1 flaky

🎭 Playwright report · View test results →

⚠️ 1 flaky test:

  • Save view (chromium)

These issues are not necessarily caused by your changes.
Annoyed by this section? Help fix flakies and failures and it will go green!

@hosthog

hosthog Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

HostHog preview — storybook-quill

Latest build (99a45d6): https://a7b8f770a3424468b30c9dc54c8cf3f7.hosthog.dev

Earlier builds of this PR, still serving:

Employee-gated; every push gets a fresh URL whose content never changes. All previews stop serving when the PR closes.

@greptile-apps

greptile-apps Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in Greptile

[High impact] Adds a new mobile app as a standalone workspace.

Fix the missing CI dependency and the push-registration race before merging, and satisfy the repository's missing-result rule.

Reviews (1) · Last reviewed commit: "Merge commit 'cd3e77c84bcc0166cf8a90ebcc..." · Reviewed by Greptile

@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 · Standard · reviewed fb2a704: 5 Must fix, 15 Should fix. This pull request is large, so the review ran in 4 parts with less depth than usual.

This is the only automatic review while this pull request is a draft. Comment @posthog review to review new changes sooner. Otherwise, a review of the new changes runs when you mark it ready for review.

Comment thread products/mobile/src/api/reports.ts Outdated
Comment thread products/mobile/src/auth/authStore.ts Outdated
Comment thread products/mobile/src/features/handoff/toolPolicy.ts Outdated
Comment thread products/mobile/src/features/notifications/pushRegistration.ts Outdated
Comment thread products/mobile/src/features/notifications/pushRegistration.ts Outdated
Comment thread products/mobile/src/features/views/useViewRefresh.ts Outdated
Comment thread products/mobile/src/features/views/refreshQueue.ts Outdated
Comment on lines +118 to +120
{isEmpty ? <Text style={styles.quiet}>The agent hasn't said anything yet.</Text> : null}
{displayItems.map(renderItem)}
{pullRequests.length > 0 ? <PullRequestCard pullRequests={pullRequests} /> : null}

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.

P2 · Virtualize the session transcript

Opening a long resumed chat renders its entire history through displayItems.map inside a ScrollView. Every offscreen Markdown tree and Mermaid WebView stays mounted, and offscreen InsightCard components start requests immediately. Memory and initial rendering work grow with the full conversation. Use a virtualized transcript with a bounded render window.

if (!compareAllowed(dateFrom)) {
return null
}
return { ...source, compareFilter: { compare: true }, dateRange }

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.

P2 · Restrict comparison requests to query types that support comparison

Enabling comparison on a SQL or lifecycle insight adds compareFilter to its source unconditionally. HogQLQuery also rejects dateRange, and both schemas forbid unknown fields, so POST query/ rejects the request and the insight shows an error instead of data. Gate comparison by source kind and disable the control for unsupported queries.

Comment thread products/mobile/src/features/reviewers/hooks.ts Outdated
Comment thread products/mobile/src/api/reports.ts Outdated
Comment thread products/mobile/src/features/notifications/pushRegistration.ts Outdated
Comment on lines +111 to +115
export function projectWebUrl(path: string): string | undefined {
try {
return apiUrl(projectPath(path).replace(/^\/api\/projects\//, '/project/'))
} catch {
return undefined

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.

P2 Missing results use undefined

projectWebUrl deliberately returns undefined when no account or project is available. The repository requires null for an intended lack of result. Return null and update the return type before merging. Apply the same change to fetchInsightByShortId, which also uses undefined for a missing result.

Rule Used: Use 'null' instead of 'undefined' to indicate an intended lack of result in functions. (source)

Learned From
PostHog/posthog#32556

Prompt To Fix With AI
This is a comment left during a code review.
Path: products/mobile/src/api/client.ts
Line: 111-115

Comment:
**Missing results use undefined**

`projectWebUrl` deliberately returns `undefined` when no account or project is available. The repository requires `null` for an intended lack of result. Return `null` and update the return type before merging. Apply the same change to `fetchInsightByShortId`, which also uses `undefined` for a missing result.

**Rule Used:** Use 'null' instead of 'undefined' to indicate an intended lack of result in functions. ([source](https://app.greptile.com/posthog-org-19734/-/custom-context?memory=0631667e-2260-42be-a589-0d8da9ef007d))

**Learned From**
[PostHog/posthog#32556](https://github.com/PostHog/posthog/pull/32556)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@trunk-io

trunk-io Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Static Badge   Static Badge   Static Badge

Failed Test Failure Summary Logs
ReportContextMenu routes the dismiss submenu's note button through the dialog instead of applying it The ReportInterviewInvite component failed because the Kea store does not contain the featureFlagLogic path required to check a feature flag. Logs ↗︎

View Full Report ↗︎ ⋅ Docs

@gantoine gantoine left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

everything outside products/mobile looks good 👍🏼

@cvolzer3
cvolzer3 marked this pull request as ready for review October 27, 2026 17:55
@parameterai

parameterai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Risk: No findings

This delta hardens the mobile release pipeline (OTA manifest signing with a GitHub-secret private key verified against a pinned certificate, gated build workflow) and tightens two privacy/device semantics: push tokens now transfer device ownership between users atomically under an advisory lock, and analytics only include the user's email when they have explicitly opted out of anonymization while stripping the launch URL from lifecycle events. All changes fail closed where a secret or certificate is missing, and no new vulnerability was found.

Sentinel reviewed 99a45d6 · Review settings

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team October 27, 2026 17:56
@pr-assigner-resolver-posthog

Copy link
Copy Markdown

👀 Auto-assigned reviewers

These owners were selected because this PR adds a new path where owners.yaml names owners of additions. The path after each owner is the new path:

  • @PostHog/team-devex (products/mobile)

Soft owners come from each directory's owners.yaml and each product's product.yaml (resolved nearest-file-wins). For a skipped owner, the locator is the file that decided it. Generated files and lockfiles are ignored when deciding ownership.

@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 · Standard · reviewed 168b0e2: no findings. This pull request is large, so the review ran in 4 parts with less depth than usual.

@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 · Standard · reviewed 9b97f44: 1 Must fix. This pull request is large, so the review ran in 4 parts with less depth than usual.

const host = useAuthStore((state) => state.host)
const signedIn = status === 'signed_in' && userUuid !== null

useEffect(() => addSignOutTask(unregisterPush), [])

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.

P1 · Expire push subscriptions when their account session ends

When OAuth refresh expires, endSession('expired') skips signOutTasks, so this cleanup never runs. Signing into another account overwrites registeredFor while the backend retains both (user, token) rows. The device continues receiving the previous user's private task and report titles. Bind subscription revocation to session expiry and account replacement.

@feliperalmeida feliperalmeida 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.

🤖 Automated security review (Claude) covering CI/CD, OAuth and tokens, mobile platform surface, rendering and AI chat, and data handling. Inline: 1 high, 1 medium, 1 low, and 3 hardening notes. Everything else checked out, including the mermaid WebView, markdown links, deep links, push routing, widgets, token storage, PKCE/state, and workflow triggers.

'./plugins/withIosSceneLifecycle',
'@bacons/apple-targets',
],
updates: {

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.

🤖 Automated security review: High. OTA updates are unsigned, and a branch can run code with the Expo token.

  • mobile-build.yml is workflow_dispatch on any ref. It runs scripts/eas-build.sh from that ref with secrets.EXPO_TOKEN and no environment.
  • A person with write access (or a leaked write token) can change the script on a branch to run eas update --channel production. Devices accept the update because it is not signed.
  • Before this PR, the token could only build and submit. With expo-updates, it can push JS to every production install.

Fix: turn on update code signing (updates.codeSigningCertificate and codeSigningMetadata). Keep the private key only in the mobile-production environment, and sign in scripts/releases/release.mjs. Also consider moving the Expo token into environment secrets so only gated jobs can read it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🤖 Automated reply by Codex.

Implemented the code checks in 2db630183999. Production OTA releases require signatures. Internal builds require master and separate environment credentials. All 15 release tests pass.

This thread stays open until signing credentials are configured and the old Expo token is retired.

Comment thread products/mobile/docs/AUTH.md Outdated
| Name | PostHog mobile |
| Client type | Public |
| Authorization grant type | Authorization code |
| Redirect URIs | `posthog://callback`, plus `posthog-dev://callback` and `posthog-preview://callback` for internal builds |

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.

🤖 Automated security review: Medium. Please don't register posthog-dev://callback and posthog-preview://callback on the production first-party apps.

First-party apps skip the consent screen. Store builds never claim these schemes, so another app on the device can claim them, receive the authorization code, and redeem it with its own PKCE verifier. Use a separate non-first-party app for internal builds, like the local dev app.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🤖 Automated reply by Codex.

Updated in 2db630183999. Internal builds now require separate OAuth client IDs, with no production fallback. Tests pass.

This thread stays open until non-first-party clients are configured and production registrations exclude internal callback schemes.

Comment thread products/mobile/src/auth/authStore.ts
Comment thread products/mobile/src/lib/posthogClient.ts
Comment thread products/mobile/src/lib/posthogClient.ts Outdated
Comment thread .github/workflows/mobile-release.yml

@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 · Standard · reviewed f4bd948: 1 Must fix. This pull request is large, so the review ran in 4 parts with less depth than usual.

This is review round 3 on this pull request. When findings keep coming back in the same code, the approach is usually the problem. Before the next fix, check whether a different design would remove the whole class of findings.

Comment on lines +181 to +188
if (pending.kind === 'plan') {
return { id: record.requestId, question: 'Approve the plan?', options: planOptions(record) }
}
const subject = record.title ?? record.toolName
return {
id: record.requestId,
question: subject ? `Allow ${subject}?` : 'Allow this action?',
options: record.options.map((option) => ({ id: option.optionId, title: optionTitle(option) })),

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.

P1 · Show the requested action before granting permission

For connected-project or external MCP approvals, decisionQuestionFor drops rawToolCall.input and shows only an agent-supplied title; plan approvals likewise discard input.plan. Users authorize operations without seeing the target, arguments, or proposed plan, so an innocuous title can conceal a destructive call. Include the payload in the approval view.

@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 · Standard · reviewed 74b1ab9: 2 Must fix. This pull request is large, so the review ran in 4 parts with less depth than usual.

This is review round 4 on this pull request. When findings keep coming back in the same code, the approach is usually the problem. Before the next fix, check whether a different design would remove the whole class of findings.

Comment on lines +261 to +264
controller.abort()
deliveries.delete(reportId)
useHandoffStore.getState().setAnswer(reportId, undefined)
track('mobile_decision_undone', { report_id: reportId })

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.

P1 · Make approval Undo operate before command dispatch

When Undo is tapped after permission_response reaches the server but before its reply arrives, controller.abort() only stops the client wait. The server still delivers the approval, while setAnswer(undefined) presents it as withdrawn. The agent can execute an action the user thinks they cancelled; delay dispatch until Undo expires or remove Undo once dispatched.

Comment on lines +65 to +71
const notificationId = await scheduleSnoozeReminder({
reportId,
projectId: scope.projectId,
title: cachedTitle(queryClient, scope, reportId),
until,
}).catch(() => undefined)
useSnoozeStore.getState().put(key, {

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.

P1 · Cancel pending snooze reminders when their session ends

If sign-out completes while scheduleSnoozeReminder is pending, session cleanup finds no entry to cancel. snoozeUntil then stores the notification after cleanup, leaving the previous account's report title scheduled on the device, including its lock screen after another user signs in. Bind scheduling to the originating session and cancel late results.

@cvolzer3
cvolzer3 requested a review from a team as a code owner October 27, 2026 20:15
@posthog-security-review-bot posthog-security-review-bot Bot added the security-review Request a security review label Oct 27, 2026
@posthog-security-review-bot

Copy link
Copy Markdown

⏭️ Security review skipped — this PR is too large — 99a45d643009

1252 files, 113357 lines and 105 commits exceeds the review limit (100 files or 10,000 lines, combined with more than 20 commits).

A pull request this size is usually the result of a bad merge or rebase rather than a change this large. This PR has not been reviewed — treat it as unreviewed, not as clean.

If the size is genuine and you want a review, split it, or ask #team-security to run one by hand.

Add the security-review label after rebasing to try again.

@posthog-security-review-bot posthog-security-review-bot Bot removed the security-review Request a security review label Oct 27, 2026

@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 · Standard · reviewed 99a45d6: 3 Must fix. This pull request is large, so the review ran in 4 parts with less depth than usual.

This pull request reached its limit of 20 automatic review findings, so this review left out 1 lower-priority finding. Must-fix findings still post. Comment @posthog review --deep for a review without the limit.

This is review round 5 on this pull request. When findings keep coming back in the same code, the approach is usually the problem. Before the next fix, check whether a different design would remove the whole class of findings.

Comment on lines +144 to +148
if (isExec) {
if (innerToolName !== undefined && isConnectedProjectSubTool(innerToolName)) {
return 'prompt'
}
return 'auto_allow'

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.

P1 · Preserve approval prompts for destructive PostHog operations

When an interactive run in Auto requests feature-flag-delete, the agent server explicitly relays that destructive operation for approval. defaultPermissionDecision instead returns auto_allow, so viewing the run authorizes deletion without a user choice, violating the mode's live-data approval guarantee. Keep these relayed approvals manual outside full auto.

Comment on lines +176 to +177
const fullAuto = isFullAutoMode(context.mode) && !record.questions?.length && !isConnectedProjectTool(record)
const decision = fullAuto ? 'auto_allow' : defaultPermissionDecision(record)

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.

P1 · Require explicit approval for relayed external MCP tools

When mobile follows a desktop run in bypassPermissions, fullAuto automatically approves external MCP requests. The agent server deliberately relays these requests regardless of mode because they execute on the user's machine with local privileges. Opening the run therefore bypasses that explicit-consent boundary. Exclude external MCP tools from automatic approval.

Comment on lines +123 to +130
const last = useRef<T | undefined>(value)
if (value !== undefined) {
last.current = value
}
if (value !== undefined || error) {
return { value, held: false }
}
return { value: last.current, held: last.current !== undefined }

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.

P1 · Clear retained chart snapshots when the account changes

When session expiry replaces a screen above an insight with sign-in, the insight stays mounted. Another user's sign-in clears the query cache but leaves last.current intact, so navigating back displays the previous user's chart, indefinitely while offline. Bind retained snapshots to the session, host, project and insight, and clear them when that identity changes.

@feliperalmeida feliperalmeida 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.

Things still to do outside the PR:

  1. Secrets aren't set up yet. MOBILE_BUILD_EXPO_TOKEN, MOBILE_PRODUCTION_EXPO_TOKEN and MOBILE_OTA_SIGNING_KEY need to be added as environment secrets.
  2. The old org EXPO_TOKEN is still there. Make sure its Expo account has no role on the new EAS project (4eb57b2f…). Otherwise a writer could still start builds or submissions for the new app. Unsigned OTA updates are blocked either way once signing is on.

This branch has not been deployed

No deployments
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.

3 participants