Conversation
🦔 PostHog Review reviewed this pull requestFound 3 must fix, 7 should fix, 7 consider. Published 17 findings (view the review). Resolving comments: 6/8 · 3 fixed, 2 left for you Safe fixes are committed to the branch; every settled thread gets a reply. This line updates as threads settle. |
🤖 CI report
|
| Function | Location | Complexity | Limit |
|---|---|---|---|
saveGroupedRules |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/accessControlsLogic.ts:1406 |
26 | 10 |
WorkflowScene |
products/workflows/frontend/Workflows/WorkflowScene.tsx:60 |
21 | 10 |
WorkflowSuggestionEvidence |
products/workflows/frontend/Workflows/suggestions/WorkflowSuggestionEvidence.tsx:13 |
21 | 10 |
WorkflowSuggestions |
products/workflows/frontend/Workflows/suggestions/WorkflowSuggestions.tsx:35 |
21 | 10 |
WorkflowAppliedOutcome |
products/workflows/frontend/Workflows/suggestions/WorkflowAppliedOutcome.tsx:38 |
12 | 10 |
⚠️ Duplication (Python) — 2 new duplicated blocks (worst 180 tokens)
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.
| First copy | Second copy | Lines | Tokens |
|---|---|---|---|
products/review_hog/backend/reviewer/lazy_seed.py:156 |
products/signals/backend/scout_harness/lazy_seed.py:451 |
33 | 180 |
posthog/api/app_metrics2.py:507 |
posthog/api/app_metrics2.py:574 |
14 | 87 |
⚠️ Duplication (TypeScript) — 27 new duplicated blocks (worst 494 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 |
|---|---|---|---|
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/groupedAccessControlRuleModalLogic.ts:974 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/groupedAccessControlRuleModalLogic.ts:1725 |
245 | 494 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/accessControlsLogic.ts:381 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/groupedAccessControlRuleModalLogic.ts:542 |
133 | 291 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/groupedAccessControlRuleModalLogic.ts:1098 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/groupedAccessControlRuleModalLogic.ts:1591 |
139 | 282 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/accessDetailLogic.ts:209 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/addObjectOverrideModalLogic.ts:179 |
127 | 262 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/accessDetailLogic.ts:210 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/groupedAccessControlRuleModalLogic.ts:688 |
125 | 255 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/groupedAccessControlRuleModalLogic.ts:1096 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/groupedAccessControlRuleModalLogic.ts:1342 |
126 | 255 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/accessControlsLogic.ts:719 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/accessControlsLogic.ts:845 |
123 | 246 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/groupedAccessControlRuleModalLogic.ts:22 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/groupedAccessControlRuleModalLogic.ts:1221 |
122 | 246 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/addObjectOverrideModalLogic.ts:59 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/groupedAccessControlRuleModalLogic.ts:414 |
122 | 245 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/addObjectOverrideModalLogic.ts:59 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/accessControlLogic.ts:664 |
122 | 245 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/groupedAccessControlRuleModalLogic.ts:975 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/groupedAccessControlRuleModalLogic.ts:1469 |
122 | 245 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/accessControlsLogic.ts:389 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/groupedAccessControlRuleModalLogic.ts:975 |
121 | 244 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/addObjectOverrideModalLogic.ts:59 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/groupedAccessControlRuleModalLogic.ts:157 |
122 | 244 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/addObjectOverrideModalLogic.ts:59 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/groupedAccessControlRuleModalLogic.ts:1098 |
122 | 244 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/addObjectOverrideModalLogic.ts:59 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/accessControlLogic.ts:314 |
122 | 244 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/addObjectOverrideModalLogic.ts:59 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/groupedAccessControlRuleModalLogic.ts:22 |
121 | 243 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/addObjectOverrideModalLogic.ts:59 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/groupedAccessControlRuleModalLogic.ts:279 |
121 | 243 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/accessControlsLogic.ts:389 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/groupedAccessControlRuleModalLogic.ts:814 |
120 | 242 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/addObjectOverrideModalLogic.ts:734 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/accessControlLogic.ts:541 |
121 | 242 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/accessDetailLogic.ts:214 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/addObjectOverrideModalLogic.ts:60 |
121 | 241 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/accessDetailLogic.ts:214 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/addObjectOverrideModalLogic.ts:458 |
121 | 241 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/accessDetailLogic.ts:214 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/addObjectOverrideModalLogic.ts:586 |
121 | 241 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/accessDetailLogic.ts:214 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/addObjectOverrideModalLogic.ts:735 |
121 | 241 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/accessDetailLogic.ts:214 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/addObjectOverrideModalLogic.ts:329 |
120 | 240 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/accessControlsLogic.ts:389 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/accessControlsLogic.ts:720 |
120 | 239 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/accessControlsLogic.ts:390 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/accessControlsLogic.ts:567 |
119 | 238 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/accessControlsLogic.ts:390 |
frontend/src/layout/navigation-3000/sidepanel/panels/access_control/ResourceAccessControlsV2/accessDetailLogic.ts:215 |
119 | 238 |
⚠️ Comment density — 4% of added code lines are comments (86 of 1983)
This section warns when comments are more than 3% of the code lines a PR adds, and alerts above 6%. Before agent-assisted PRs, the typical share was about 2%. Only full-line comments count. Docstrings, generated files, snapshots, migrations, and workflow files are left out.
Comments that restate the code, record how the change came about, or narrate the next line add noise for the next reader. Keep the comments that explain a reason the code cannot show, and remove the rest. See .agents/skills/writing-code-comments/SKILL.md for the house rules.
Files with the most added comment lines:
| File | Comment lines | Added lines |
|---|---|---|
products/workflows/backend/api/hog_flow.py |
27 | 443 |
posthog/temporal/oauth.py |
12 | 14 |
products/workflows/backend/api/test/test_workflow_proposals.py |
8 | 290 |
posthog/scopes.py |
5 | 7 |
posthog/api/app_metrics2.py |
4 | 60 |
products/signals/backend/scout_harness/runner.py |
4 | 23 |
products/workflows/frontend/Workflows/suggestions/WorkflowAppliedOutcome.tsx |
4 | 253 |
products/signals/backend/scout_harness/config_registry.py |
3 | 6 |
This check does not block merging. It updates on every push and clears when the share drops.
⚠️ Bundle size — 🔺 +12.3 KiB (+0.0%)
Uncompressed size of every built .js bundle, compared against the base branch.
Total: 68.93 MiB · 🔺 +12.3 KiB (+0.0%)
| File | Size | Δ vs base |
|---|---|---|
posthog-app/_parent/products/workflows/frontend/Workflows/WorkflowScene.js |
65.5 KiB | 🔺 +11.7 KiB (+21.8%) |
Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report
✅ Eager graph — within budget
How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.
| Root | Eager (shipped) | Δ vs base | Budget |
|---|---|---|---|
entry (logged-out pages, app bootstrap)src/index.tsx |
1.57 MiB · 22 files | no change | █████████░ 85.4% of 1.84 MiB |
logged-out boot: index + App + bootApp (preloaded by every page, including /login)src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts |
3.58 MiB · 629 files | no change | █████████░ 88.9% of 4.03 MiB |
authenticated shell (every logged-in page)src/scenes/AuthenticatedShell.tsx |
7.39 MiB · 2,330 files | no change | █████████░ 88.6% of 8.34 MiB |
🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/layout/navigation-3000/navigationLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/dashboard/dashboardLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/lemon-ui/LemonMarkdown/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/RichContentEditor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/CodeSnippet/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/taxonomy/core-filter-definitions-by-group.json stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
Largest files eagerly shipped from src/index.tsx
| Size | File |
|---|---|
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 24.6 KiB | ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js |
| 6.3 KiB | ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js |
| 4.5 KiB | ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js |
| 3.9 KiB | ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js |
| 1.4 KiB | ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js |
| 1.3 KiB | src/index.tsx |
| 1.3 KiB | src/RootErrorBoundary.tsx |
| 912 B | ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js |
| 854 B | src/scenes/ChunkLoadErrorBoundary.tsx |
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
| Size | File |
|---|---|
| 301.8 KiB | ../node_modules/.pnpm/posthog-js@1.434.14_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 267.6 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 100.4 KiB | src/lib/api.ts |
| 88.0 KiB | src/products.tsx |
| 69.4 KiB | src/lib/lemon-ui/icons/icons.tsx |
| 63.9 KiB | src/lib/utils/eventUsageLogic.ts |
| 38.7 KiB | ../node_modules/.pnpm/@dnd-kit+core@6.0.8_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@dnd-kit/core/dist/core.esm.js |
| 33.9 KiB | ../node_modules/.pnpm/kea@4.0.0-pre.6_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js |
| 28.4 KiB | src/scenes/scenes.ts |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
| Size | File |
|---|---|
| 301.8 KiB | ../node_modules/.pnpm/posthog-js@1.434.14_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 271.7 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 267.6 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 153.7 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js |
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 100.4 KiB | src/lib/api.ts |
| 98.5 KiB | ../packages/quill/packages/quill/dist/index.js |
| 93.3 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
| 90.6 KiB | ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js |
| 88.0 KiB | src/products.tsx |
Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479
✅ Toolbar bundle — eager 2.37 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.37 MiB · 19 files | no change | ████░░░░░░ 41.5% of 5.72 MiB |
| Deferred (lazy) | 2.10 MiB · 44 files | no change | n/a — loads on demand |
Loader dist/toolbar.js |
1.2 KiB | no change | █░░░░░░░░░ 6.0% of 19.5 KiB |
Largest eagerly-shipped chunks
| Size | File |
|---|---|
| 797.6 KiB | dist/toolbar/toolbar-app-YQD34LXY.css |
| 650.9 KiB | dist/toolbar/chunk-chunk-MBDQVUJS.js |
| 483.6 KiB | dist/toolbar/chunk-chunk-QC2J4IAJ.js |
| 138.3 KiB | dist/toolbar/chunk-chunk-E3F6BIJI.js |
| 131.8 KiB | dist/toolbar/chunk-chunk-FDH2IBXT.js |
| 75.2 KiB | dist/toolbar/toolbar-app-6RNL4UFA.js |
| 69.0 KiB | dist/toolbar/chunk-chunk-TSAL54PB.js |
| 35.6 KiB | dist/toolbar/chunk-chunk-EWVWELWB.js |
| 21.0 KiB | dist/toolbar/chunk-chunk-FS2KIEZ4.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 — 🔺 +127.2 KiB (+0.0%)
Total size of the built frontend/dist folder (all assets), compared against the base branch.
Total: 946.01 MiB · 🔺 +127.2 KiB (+0.0%)
ℹ️ MCP UI apps size — 33 app(s), 17630.1 KB JS
Built size of each MCP UI app (main.js + styles.css).
| App | JS | CSS |
|---|---|---|
| debug | 597.9 KB | 196.2 KB |
| action | 454.1 KB | 196.2 KB |
| action-list | 564.2 KB | 196.2 KB |
| cohort | 453.1 KB | 196.2 KB |
| cohort-list | 563.2 KB | 196.2 KB |
| email-template | 452.9 KB | 196.2 KB |
| error-details | 469.1 KB | 196.2 KB |
| error-issue | 453.8 KB | 196.2 KB |
| error-issue-list | 564.1 KB | 196.2 KB |
| experiment | 561.3 KB | 196.2 KB |
| experiment-list | 564.9 KB | 196.2 KB |
| experiment-results | 566.3 KB | 196.2 KB |
| feature-flag | 566.8 KB | 196.2 KB |
| feature-flag-list | 570.5 KB | 196.2 KB |
| feature-flag-testing | 457.3 KB | 196.2 KB |
| inline-scan | 453.6 KB | 196.2 KB |
| insight-actors | 562.3 KB | 196.2 KB |
| invite-email-preview | 452.3 KB | 196.2 KB |
| llm-costs | 559.3 KB | 196.2 KB |
| session-recording | 455.3 KB | 196.2 KB |
| survey | 454.7 KB | 196.2 KB |
| survey-global-stats | 561.9 KB | 196.2 KB |
| survey-list | 564.9 KB | 196.2 KB |
| survey-stats | 561.9 KB | 196.2 KB |
| trace-span | 453.5 KB | 196.2 KB |
| trace-span-list | 564.1 KB | 196.2 KB |
| vision-observation-list | 563.3 KB | 196.2 KB |
| workflow | 453.4 KB | 196.2 KB |
| workflow-list | 563.5 KB | 196.2 KB |
| loops-review | 457.8 KB | 196.2 KB |
| query-results | 774.1 KB | 196.2 KB |
| render-ui | 857.0 KB | 196.2 KB |
| visual-review-snapshots | 457.9 KB | 196.2 KB |
⚠️ Playwright — 1 failed
🎭 Playwright report · View test results →
❌ 1 failed 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!
⚠️ Backend coverage — 94.0% of changed backend lines covered — 35 uncovered
🧪 Backend test coverage
Patch coverage — changed backend lines (products + core): ███████████████████░ 94.0% (572 / 607)
| File | Patch | Uncovered changed lines |
|---|---|---|
posthog/api/app_metrics2.py |
45.8% | 509, 532, 551, 554–556, 566–570, 576, 599 |
products/signals/backend/scout_harness/lazy_seed.py |
83.3% | 204, 598–600 |
products/workflows/backend/models/hog_flow_optimization.py |
92.9% | 38 |
products/workflows/backend/api/hog_flow.py |
93.0% | 4373, 4393, 4402, 4421, 4480–4481, 4645, 6265, 6299, 6362, 6394–6396, 6398, 6406, 6493–6494 |
🤖 Agents: add a test covering the lines above, or note why under "How did you test this code?". Machine-readable gap list: the patch-coverage artifact on this run (gh run download 521070109436081 -n patch-coverage), or the coverage-data block at the end of this comment.
Per-product line coverage (touched products)
| Product | Coverage | Lines |
|---|---|---|
platform_features |
██░░░░░░░░░░░░░░░░░░ 12.1% |
7 / 58 |
warehouse_sources_queue |
██████░░░░░░░░░░░░░░ 29.1% |
92 / 316 |
demo |
████████████░░░░░░░░ 57.9% |
1,547 / 2,673 |
data_tools |
████████████░░░░░░░░ 61.2% |
90 / 147 |
aeo |
██████████████░░░░░░ 70.5% |
467 / 662 |
ai_gateway |
███████████████░░░░░ 75.0% |
9 / 12 |
batch_exports |
████████████████░░░░ 81.2% |
21,458 / 26,431 |
apm |
█████████████████░░░ 84.1% |
1,306 / 1,553 |
ml_inference |
█████████████████░░░ 87.2% |
482 / 553 |
cdp |
██████████████████░░ 88.2% |
4,548 / 5,155 |
mcp_analytics |
██████████████████░░ 88.9% |
4,910 / 5,523 |
product_tours |
██████████████████░░ 89.3% |
1,331 / 1,491 |
dashboards |
██████████████████░░ 89.5% |
6,839 / 7,641 |
signals |
██████████████████░░ 89.9% |
54,714 / 60,874 |
data_warehouse |
██████████████████░░ 89.9% |
13,912 / 15,470 |
notebooks |
██████████████████░░ 90.2% |
15,287 / 16,945 |
cohorts |
██████████████████░░ 90.4% |
8,420 / 9,316 |
streamlit_apps |
██████████████████░░ 90.7% |
2,625 / 2,895 |
managed_warehouse |
██████████████████░░ 90.9% |
10,215 / 11,234 |
tasks |
██████████████████░░ 91.1% |
73,993 / 81,212 |
data_modeling |
██████████████████░░ 91.5% |
10,525 / 11,498 |
business_knowledge |
██████████████████░░ 91.6% |
6,899 / 7,528 |
engineering_analytics |
██████████████████░░ 91.7% |
11,017 / 12,014 |
exports |
██████████████████░░ 91.8% |
9,685 / 10,555 |
ai_training |
██████████████████░░ 92.2% |
356 / 386 |
conversations |
███████████████████░ 92.5% |
28,726 / 31,047 |
early_access_features |
███████████████████░ 92.6% |
1,341 / 1,448 |
managed_migrations |
███████████████████░ 92.7% |
1,581 / 1,705 |
visual_review |
███████████████████░ 92.8% |
9,244 / 9,966 |
canvas |
███████████████████░ 92.8% |
6,877 / 7,409 |
approvals |
███████████████████░ 93.0% |
3,919 / 4,214 |
mcp_registry |
███████████████████░ 93.1% |
1,670 / 1,794 |
error_tracking |
███████████████████░ 93.1% |
15,843 / 17,010 |
notifications |
███████████████████░ 93.2% |
1,145 / 1,229 |
slack_app |
███████████████████░ 93.2% |
13,677 / 14,674 |
stamphog |
███████████████████░ 93.2% |
7,885 / 8,456 |
surveys |
███████████████████░ 93.3% |
6,571 / 7,040 |
context_layer |
███████████████████░ 93.8% |
3,373 / 3,595 |
web_analytics |
███████████████████░ 93.9% |
21,653 / 23,051 |
alerts |
███████████████████░ 94.0% |
8,541 / 9,082 |
billing_alerts |
███████████████████░ 94.1% |
2,094 / 2,226 |
mcp_store |
███████████████████░ 94.4% |
8,940 / 9,472 |
ai_observability |
███████████████████░ 94.4% |
22,534 / 23,870 |
wizard |
███████████████████░ 94.7% |
6,151 / 6,496 |
reminders |
███████████████████░ 94.8% |
760 / 802 |
review_hog |
███████████████████░ 94.9% |
11,490 / 12,109 |
workflows |
███████████████████░ 94.9% |
14,781 / 15,574 |
annotations |
███████████████████░ 95.1% |
817 / 859 |
endpoints |
███████████████████░ 95.1% |
9,211 / 9,681 |
customer_analytics |
███████████████████░ 95.2% |
24,899 / 26,167 |
legal_documents |
███████████████████░ 95.2% |
2,311 / 2,427 |
marketing_analytics |
███████████████████░ 95.3% |
19,214 / 20,161 |
posthog_ai |
███████████████████░ 95.4% |
2,489 / 2,610 |
experiments |
███████████████████░ 95.4% |
32,645 / 34,211 |
growth |
███████████████████░ 95.4% |
9,812 / 10,282 |
logs |
███████████████████░ 95.4% |
15,290 / 16,022 |
actions |
███████████████████░ 95.5% |
756 / 792 |
data_catalog |
███████████████████░ 95.6% |
4,402 / 4,606 |
tracing |
███████████████████░ 95.6% |
3,518 / 3,680 |
autoresearch |
███████████████████░ 95.7% |
8,481 / 8,865 |
messaging |
███████████████████░ 95.8% |
3,798 / 3,963 |
skills |
███████████████████░ 95.8% |
6,972 / 7,274 |
replay_vision |
███████████████████░ 95.9% |
27,154 / 28,310 |
product_analytics |
███████████████████░ 96.2% |
28,495 / 29,617 |
revenue_analytics |
███████████████████░ 96.4% |
1,876 / 1,946 |
access_control |
███████████████████░ 96.4% |
7,122 / 7,386 |
user_interviews |
███████████████████░ 96.5% |
2,859 / 2,963 |
feature_flags |
███████████████████░ 96.5% |
25,499 / 26,416 |
warehouse_sources |
███████████████████░ 97.2% |
452,812 / 465,679 |
data_quality |
████████████████████ 97.7% |
7,592 / 7,774 |
links |
████████████████████ 97.9% |
234 / 239 |
security |
████████████████████ 98.0% |
1,203 / 1,228 |
metrics |
████████████████████ 98.1% |
4,085 / 4,166 |
analytics_platform |
████████████████████ 98.3% |
2,783 / 2,832 |
pulse |
████████████████████ 98.5% |
2,043 / 2,075 |
live_debugger |
████████████████████ 99.2% |
626 / 631 |
field_notes |
████████████████████ 99.4% |
172 / 173 |
Report-only. Patch coverage = changed backend lines covered vs origin/master. Sorted lowest first.
Known gaps: lines covered only by Temporal tests show as uncovered; core line numbers may drift if master changed the same file.
⚠️ MCP snapshots — 1 updated (1 modified, 0 added, 0 deleted)
Snapshots: MCP unit test snapshots updated
Changes: 1 snapshots (1 modified, 0 added, 0 deleted)
What this means:
- Snapshots have been automatically updated to match current output
Next steps:
- Review the changes to ensure they're intentional
- If unexpected, investigate what caused the output to change
✅ 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
workflows.0027_hogflowoptimisation
└─ #1 ✅ CreateModel
Creating new table is safe
model: HogFlowOptimisation
│
└──> ℹ️ INFO:
ℹ️ Skipped operations on newly created tables (empty tables
don't cause lock contention).
Last updated: 2026-09-23 11:17 UTC (afceb8a)
✅ ClickHouse migration SQL — none
No ClickHouse migrations in the latest push.
ℹ️ Docs preview — preview build triggered
Docs from this PR will be published at posthog.com.
| Project | Preview | Updated (UTC) |
|---|---|---|
| posthog.com | Open preview | Sep 1, 2026, 9:51 AM |
The preview should be ready in about 10 minutes. Open the preview at /handbook/engineering/.
|
|
PostHog Review alpha 🦔 If you find any issues helpful - please reply "valid", "invalid", etc., for evaluation purposes 🙏 |
79c01d5 to
19f1a84
Compare
c190110 to
0a1ff44
Compare
b925e7e to
f11a98b
Compare
f11a98b to
e1a7aa2
Compare
eccd3dd to
908eebb
Compare
786404f to
437065a
Compare
437065a to
b0b3193
Compare
b0b3193 to
f323a5a
Compare
4dcd002 to
bc46d7a
Compare
|
One stale-approval hole seems to remain outside the action/whole-list conflict logic.
That leaves a lost-update path. For example:
That seems to violate the PR's own stale-suggestion contract: an approval should not silently undo a newer edit in the same part of the workflow. I would make stale checking follow merge semantics, rather than only graph shape:
And require Useful regression cases:
The same check should cover |
|
🕓 This approval covered an earlier revision. There are new visual changes to review in the newer comment below. ✅ Visual changes approved by @dmarchuk — baseline updated in 12 changed. |
927b455 to
e9e460a
Compare
|
[Medium risk] Adds workflow suggestion feature with new access scope. The PR is not safe to merge until secret-input proposals and legacy conversion windows retain their intended behavior. Reviews (3) · Last reviewed commit: "feat(workflows): add the signals scout t..." |
ede98be to
c1e457a
Compare
Nakagawa-master
left a comment
There was a problem hiding this comment.
One capability-boundary hole remains in the new proposal scope.
The PR says hog_flow_proposal:write is a per-scout grant and that the scope is no longer offered on personal API keys, so PostHog's scout is the only filer. But the backend boundary does not currently enforce that.
This PR adds hog_flow_proposal to APIScopeObject. validate_personal_api_key_scopes() accepts any <object>:<action> whose object is in API_SCOPE_OBJECTS and not in INTERNAL_API_SCOPE_OBJECTS. There is no separate deny for hog_flow_proposal.
That means a session-authenticated user can bypass the UI selector and create a PAT through the API with:
{"scopes": ["hog_flow_proposal:write"]}and the proposal endpoint will accept it. The PR's own test_suggesting_takes_its_own_scope_not_workflow_write demonstrates that a PersonalAPIKey carrying this scope can file a proposal.
There is a second exposure: unless this object is filtered from get_scope_descriptions(), it is also in the generated OAuth scope catalog, so third-party OAuth clients can discover/request a capability the PR describes as scout-only.
This seems like the same distinction the dedicated scope was meant to create: narrow capability != user-grantable capability. Hiding a scope in one UI does not make the underlying authority server-only.
Could the scope be made non-user-mintable at the backend boundary — for example by using the existing server-only/internal classification if that is compatible with scout-config grants, or by explicitly rejecting hog_flow_proposal in PAT/OAuth scope validation while still allowing the scout runner to mint it?
Useful regression coverage:
- session user POSTs a personal API key with
hog_flow_proposal:write→ 400; - OAuth metadata/consent does not advertise the scout-only scope;
- server-minted scout token with the configured grant →
workflows-suggestremains available; - that token still lacks
hog_flow:write, so publish/update/test-send remain unavailable.
That would make the stated "scout can suggest, person approves, scout cannot publish" contract an actual authority boundary rather than a selector/UI convention.
|
🕓 This approval covered an earlier revision. There are new visual changes to review in the newer comment below. ✅ Visual changes approved by @dmarchuk — baseline updated in 4 changed. |
|
👋 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. |
PR overviewAll previously flagged issues have been addressed. No open security concerns remain on this pull request. Security reviewNo open security issues remain on this pull request. Fixed/addressed: 2 · PR risk: 0/10 |
🦔 Hogbox preview · ❌ build failedThe preview didn't come up for commit Previews are optional and never block merging. A failure here is often a hogland or tailnet hiccup rather than anything in your PR, so the check stays green and this comment is the status. |
|
Replaced by #107802. Outcome measurement and the evidence surfaces moved into #107795, which #107802 now targets, so neither PR rewrites the other's components. This PR could not be retargeted in place: GitHub refuses to change the base branch of a stacked pull request, and the stack cannot be dissolved because it holds merged PRs. |
Comments Outside DiffThese findings sit on lines the diff does not cover, so they could not be posted inline. Each one leaves this list once its file changes.
|
Problem
Nothing produces a suggestion yet. The record, API, page, tools and opt-in exist (#91708 to #91795); this adds the producer and the surfaces a person needs to judge what it files. Layer 6 of 6.
Everything is behind the
self-optimising-workflowsflag. Tracking issue with the whole picture and the decisions: #92154. This PR targets the branch below it so its diff shows only this layer; the stack merges tomastertogether. The scout itself is held back untilsignals-scout-workflowsis added to thesignals-scoutflag'swithheld_skills(see the rollout note on #92154).Changes
signals-scout-workflows, a Signals scout that reads the workflows whose owner turned on suggestions, reads each email step's per-version metrics, and files one concrete change throughworkflows-suggest. It files no inbox report and emits no signal; it never approves.metricsandmetrics/totalstakeversion=<n>, which is how a producer answers "did the last change help". Workflow metrics only; a hog function answers 400.workflows-list-versionslists a workflow's published versions with the time each went live, so the scout waits two days of opens before judging a version.hog_flow_proposal:writeleaves the fleet floor; the skill declares it, the harness seeds it, a person can revoke it in scout settings. The API key modal offers it write-only.base_version(the same read as the outcome's "before" side) and stores them underevidence.measured. The card shows that reading with "Measured by PostHog"; if the scout's headline number or denominator differs, the card says so. A suggestion PostHog could not measure shows the scout's numbers under "Unverified". Raw details labelsource_idas "Scout run".pending_suggestions, list only) and the name cell shows "1 suggestion" linking to the tab.workflow_not_live), and the scout's work list is active workflows only.source_type,resolution_note,created_viaandcreated_byare gone, andsource_idremains.hog_flow_proposalis an internal scope object, so no personal key, OAuth app or logged-in session can file a suggestion; PostHog's scout is the only filer.base_versionis required on every suggestion.suggesttreated as an action verb in the exec command index (keeps the serialized schema under the 16 KB registry cap), the scout's display name.How did you test this code?
measuredreading (7.6% on 132) matched its claim; the list showed the badge; approve → "Staged as draft" → Open draft → Discard → back in queue → approve → publish → Applied, with the outcome table reading 7.6% → 12.9% after seeding v2, and the badge gone once applied. Over real HTTP with the test session: a ghoststep_id, a missingbase_versionand a missinguniteach answered 400; a suggestion on an opted-out workflow answered 409; the samesource_idtwice returned one row;source_typeandresolution_notein a payload are ignored. A suggestion filed with wrong numbers got PostHog's reading stored, and the card reads "This suggestion claimed 50.0% on 40, which is not what PostHog measured." The API key modal no longer offers the scope.unit: rate,n: 132,base_versionset), skipped workflows with a pending or applied suggestion, and refused to suggest when complaints were elevated.is_stalefalse and the suggestion approves.measuredfrom seeded per-version rows beside the producer's own numbers (TestHogFlowVersionedMetrics);evidenceDisagreesrows in Jest; the list counts suggested changes per workflow and the detail read leaves the field out; field-level staleness (409 once the field moved, still approves after an unrelated edit, 400 withoutbase_version); the versioned read on seeded rows and its 400 on a hog function;scout-write-scopesparse, reject, seed, and the team-fallback grant;describeSuggestedChangesand the proposals logic in Jest.👉 Stay up-to-date with PostHog coding conventions for a smoother review.
Automatic notifications
Docs update
Customer docs live in PostHog/posthog.com#19863 (draft until the rollout starts).
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Agent: Claude Code, Fable 5.1
Skills invoked:
/authoring-scouts,/writing-tests,/writing-ui-components,/writing-user-facing-copy,/reviewing-with-coderabbit(CLI signed out; skipped),/stacking-prs,/writing-pr-descriptions. Theversionparameter came from dogfooding the scout body:workflows-statsmerged every version, so the scout could not tell whether a change helped. A reviewer's stale-approval finding (single-value fields bypassing the collision check) is fixed here. Bot threads on the PR are resolved.