feat(actions): warn when an action's event stopped arriving - #95863
feat(actions): warn when an action's event stopped arriving#95863posthog[bot] wants to merge 5 commits into
Conversation
An action step points at an event by name. If that event stops being sent, or its definition is deleted in data management, the action keeps looking healthy while it matches nothing. Show a "Stale" or "Not seen" tag next to the affected step, on the actions list and in the step editor. Staleness reuses `isDefinitionStale`, the same helper the taxonomic filter uses on its dropdown rows. Generated-By: PostHog Desktop Task-Id: 6f8463f7-4957-4a9a-9495-33c76d308a6d
|
Merging to
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 |
🦔 PostHog Review reviewed this pull requestFound 0 must fix, 4 should fix, 0 consider. Published 4 findings (view the review). Resolved comments: 4 fixed |
🤖 CI report✅ Trunk lane — non-backend laneThis PR is assigned to the non-backend lane. It does not run backend Python tests and may merge in parallel with PRs in other lanes.
|
| Function | Location | Complexity | Limit |
|---|---|---|---|
handleFetch |
frontend/src/lib/api.ts:7537 |
16 | 10 |
ActionStep |
products/actions/frontend/components/ActionStep.tsx:48 |
16 | 10 |
listLegacy |
frontend/src/lib/api.ts:2650 |
14 | 10 |
ScreenNameField |
products/actions/frontend/components/ActionStep.tsx:434 |
11 | 10 |
⚠️ Bundle size — 🔺 +2.2 KiB (+0.0%)
Uncompressed size of every built .js bundle, compared against the base branch.
Total: 68.62 MiB · 🔺 +2.2 KiB (+0.0%)
| File | Size | Δ vs base |
|---|---|---|
exporter/src/exporter/scenes/ExporterNotebookScene.js |
3.46 MiB | 🔺 +2.2 KiB (+0.1%) |
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.50 MiB · 22 files | no change | ███░░░░░░░ 33.2% of 4.51 MiB |
logged-out boot: index + App + bootApp (preloaded by every page, including /login)src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts |
5.25 MiB · 668 files | 🔺 +3 B (+0.0%) | █████████░ 93.3% of 5.63 MiB |
authenticated shell (every logged-in page)src/scenes/AuthenticatedShell.tsx |
7.67 MiB · 2,677 files | 🔺 +3 B (+0.0%) | █████████░ 87.4% of 8.77 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
🟢 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
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/RootErrorBoundary.tsx |
| 1000 B | src/index.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 |
|---|---|
| 276.1 KiB | ../node_modules/.pnpm/posthog-js@1.427.2_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 267.7 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 |
| 255.2 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 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 |
| 104.4 KiB | src/lib/api.ts |
| 79.1 KiB | src/products.tsx |
| 68.8 KiB | src/lib/lemon-ui/icons/icons.tsx |
| 62.5 KiB | src/lib/utils/eventUsageLogic.ts |
| 38.8 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 |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
| Size | File |
|---|---|
| 276.1 KiB | ../node_modules/.pnpm/posthog-js@1.427.2_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 267.7 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 |
| 255.2 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 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 |
| 104.4 KiB | src/lib/api.ts |
| 95.8 KiB | ../packages/quill/packages/quill/dist/index.js |
| 93.3 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
| 90.6 KiB | ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js |
| 79.1 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.28 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.28 MiB · 18 files | no change | ████░░░░░░ 39.9% of 5.72 MiB |
| Deferred (lazy) | 2.11 MiB · 45 files | no change | n/a — loads on demand |
Loader dist/toolbar.js |
1.1 KiB | no change | █░░░░░░░░░ 5.8% of 19.5 KiB |
Largest eagerly-shipped chunks
| Size | File |
|---|---|
| 756.3 KiB | dist/toolbar/toolbar-app-IHRJKSXI.css |
| 603.1 KiB | dist/toolbar/chunk-chunk-NCE4QL3M.js |
| 484.7 KiB | dist/toolbar/chunk-chunk-D33R7XDZ.js |
| 136.5 KiB | dist/toolbar/chunk-chunk-BXI27EUT.js |
| 131.8 KiB | dist/toolbar/chunk-chunk-FDH2IBXT.js |
| 72.2 KiB | dist/toolbar/toolbar-app-CJMMARKZ.js |
| 69.0 KiB | dist/toolbar/chunk-chunk-TSAL54PB.js |
| 35.6 KiB | dist/toolbar/chunk-chunk-VLYPUESV.js |
| 21.0 KiB | dist/toolbar/chunk-chunk-LPBZV4EM.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 — 🔺 +27.5 KiB (+0.0%)
Total size of the built frontend/dist folder (all assets), compared against the base branch.
Total: 1457.46 MiB · 🔺 +27.5 KiB (+0.0%)
There was a problem hiding this comment.
Approved.
Additive frontend-only feature (new warning component + batching logic) with no data model, API contract, auth, billing, or CI changes; covered by new unit tests and stories, so it's outside risky territory and safe on its own merits despite lacking a human review.
Gate mechanics and policy version
| Gate | Result | |
|---|---|---|
| prerequisites | ✓ | all clear |
| deny-list | ✓ | no deny categories matched |
| size | ✓ | 275L, 7F substantive, 343L/8F incl. docs/generated/snapshots — within ceiling |
| tier | ✓ | T1-agent / T1d-complex (343L, 8F, two-areas, feat) |
| stamphog 2.0.0b4 | .stamphog/policy.yml @ 2c9456b · reviewed head 2c9456b |
|
👋 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. |
|
PostHog Review alpha 🦔 If you find any issues helpful - please reply "valid", "invalid", etc., for evaluation purposes 🙏 |
The `names` filter on `event_definitions` splits every value it receives on commas, so an event name that contains one is queried as two separate names and never comes back. The health logic pre-filled each requested name with `null`, so such a name kept its `null` and drew a "Not seen" tag claiming the action matches nothing, on a step that matches fine. The logic now leaves those names out of the batch, so no verdict is reached and no tag is drawn. Changing the endpoint's documented comma-separated contract is out of scope here. Generated-By: PostHog Desktop Task-Id: d85c83d9-9ca8-483b-a793-1e66cdc9b620
New commits were pushed — dismissing the stamphog approval from an earlier head. This PR no longer qualifies for automatic review.
Action match groups are OR-ed at query time, so one group whose event stopped arriving does not stop the other groups from matching. Both tooltip branches claimed the whole action matches nothing, which is false on any multi-group action. The stale branch also claimed insights stay empty. An event is stale once PostHog last saw it more than 30 days ago, so its history is still there and an insight whose date range covers that history still returns rows. The tooltip now speaks about the match group it sits next to, says the group stops matching new events, and says recent results can be incomplete instead of empty. Generated-By: PostHog Desktop Task-Id: d85c83d9-9ca8-483b-a793-1e66cdc9b620
Event names come from the customer, so one can be `constructor`, `toString`, or `__proto__`. Held in normal objects those names broke three ways at once: - `name in values.definitions` was true before anything resolved, because `in` reads the prototype chain, so the name was dropped from the batch and never requested. - `eventHealthIssues[name]` returned the inherited value, which is truthy, so the component drew a "Not seen" tag on a healthy event. - `resolved['__proto__'] = definition` re-parented the map instead of adding a key, so `__proto__` could never resolve and went out on every later burst. Both maps are now created with `Object.create(null)`, which fixes the two reads and the write together, so `in` needs no change. Generated-By: PostHog Desktop Task-Id: d85c83d9-9ca8-483b-a793-1e66cdc9b620
The loader put every unresolved event name into one GET. A page holds 50 actions, each action holds any number of match groups, and an event name holds up to 400 characters, so a mature project could build a request line past the ~4k a proxy accepts. Nothing wrote to `definitions` on failure while `requestedNames` kept every name, so every tag on the page stayed absent and the next burst rebuilt the same oversized URL. `propertyDefinitionsModel` batches against the same limit with a count cap of 50. A count cap does not bound this URL, because one 400-character name costs as much as thirteen short ones, so the batch is split by encoded length instead. Requests run one after another and merge into the same map, so a burst that fits in one URL still goes out as one request. Generated-By: PostHog Desktop Task-Id: d85c83d9-9ca8-483b-a793-1e66cdc9b620
Problem
isDefinitionStalealready answers this, and the taxonomic filter already draws a "Stale" tag with it. Nothing underproducts/actions/used it.Changes
StaleNot seenactionEventHealthLogicbatches every event name on a page into oneevent_definitionsrequest;api.eventDefinitions.listgains thenamesfilter the endpoint already supports.Actions list
Step editor
Tooltip
Not in this PR: the background health check in
posthog/temporal/health_checks/, and a guard onEventDefinitionViewSet.destroyfor definitions that action steps reference.How did you test this code?
EventHealthWarningstory and two newAction.stories.tsxstories.isDefinitionStalewiring would flip: fresh event silent, old event stale, unknown event missing. No existing test covered any of them.products/actionsJest suite andhogli ci:preflight --fix. Frontend typecheck reports only errors that predate this branch, all in unrelated quill imports.Automatic notifications
Docs update
None. No documented workflow changes.
🤖 Agent context
Autonomy: Fully autonomous
/writing-ui-components,/writing-tests, plus the frontend and copy conventions inAGENTS.md.gh pr list --state open --searchover stale-event and action keywords found nothing covering this.Created with PostHog Desktop from this inbox report.