fix(replay-vision): prevent daily digest card flash on turn-on - #71272
Conversation
Turning on a scanner's daily digest refetched the whole actions list, pushing the card through two null-rendering windows (visionActionsLoading before the digest arrived, then latestRunLoading) so it unmounted and flashed before showing the "next digest arrives" state. Insert the created digest optimistically (visionActionsCreate already returns the full object) and resolve the run state locally, mirroring the existing toggleActionEnabled optimistic pattern. The card now swaps in place with no flash. Generated-By: PostHog Code Task-Id: fb5af707-790d-4d37-9ac9-b69e1cd7b319
There was a problem hiding this comment.
Small, contained frontend logic fix within the author's own team's files, diff matches the description, includes a test covering the regression, and the author has strong familiarity with this exact code — no showstoppers.
- Author wrote 100% of the modified lines and has 36 merged PRs in these paths (familiarity STRONG).
- 👍 on the PR from hex-security-app[bot].
Gate mechanics and policy version
| Gate | Result | |
|---|---|---|
| prerequisites | ✓ | all clear |
| deny-list | ✓ | no deny categories matched |
| size | ✓ | 13L, 2F substantive, 22L/3F incl. docs/generated/snapshots — within ceiling |
| tier | ✓ | T1-agent / T1b-small (22L, 3F, single-area, fix) |
| stamphog 2.0.0b3 | .stamphog/policy.yml @ 1195540 · reviewed head d87100b |
🦔 Hogbox preview · ❌ build failedThe preview didn't come up for commit |
🤖 CI report
|
| Root | Eager (shipped) | Δ vs base | Budget |
|---|---|---|---|
entry (logged-out pages, app bootstrap)src/index.tsx |
1.22 MiB · 22 files | no change | ███░░░░░░░ 28.4% of 4.29 MiB |
authenticated shell (every logged-in page)src/scenes/AuthenticatedShell.tsx |
8.15 MiB · 2,979 files | no change | █████████░ 88.1% of 9.25 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/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers 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 |
| 912 B | ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js |
| 789 B | src/scenes/ChunkLoadErrorBoundary.tsx |
| 762 B | src/index.tsx |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
| Size | File |
|---|---|
| 281.3 KiB | ../node_modules/.pnpm/posthog-js@1.402.2/node_modules/posthog-js/dist/rrweb.js |
| 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 |
| 235.5 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 222.9 KiB | ../node_modules/.pnpm/posthog-js@1.402.2/node_modules/posthog-js/dist/module.js |
| 164.0 KiB | src/queries/validators.js |
| 154.3 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 |
| 105.9 KiB | src/lib/api.ts |
| 93.3 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
| 92.7 KiB | ../packages/quill/packages/quill/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
✅ Dist folder size — 🔺 +896 B (+0.0%)
Total size of the built frontend/dist folder (all assets), compared against the base branch.
Total: 1309.56 MiB · 🔺 +896 B (+0.0%)

Problem
On a Replay Vision scanner page, clicking "Turn on daily digest" made the digest hero card flash: it fully disappeared, then reappeared in the "next digest arrives at 8am" state. The blink reads like an error and is jarring right after a deliberate click.
Changes
The card was unmounting because
createDigestrefetched the whole actions list after the create POST. That refetch drove the card through twonull-rendering windows inScannerDigestCard:visionActionsLoadingflips true whiledigestis still absent (the created action isn't in the list until the reload lands) → card returnsnull.loadActionsSuccessfiresloadLatestRun, flippinglatestRunLoadingtrue whilelatestRunis null → card returnsnullagain.The refetch was unnecessary.
visionActionsCreatealready returns the fully serialized created action (including the read-onlynext_run_atthe copy needs), and a brand-new digest has no runs. So instead of reloading:addActionaction + reducer tovisionActionsLogicthat appends a created action to the list, mirroring the existing optimistictoggleActionEnabledpattern.createDigestnow captures the created object and dispatchesaddAction(created)+loadLatestRunSuccess(null)instead ofloadActions().digestresolves immediately and the run state settles locally, so neithernullwindow opens.The card now swaps in place: the button shows its own loading spinner, then it transitions straight to the "next digest arrives" state.
No backend or serializer changes, so no generated-type regeneration needed.
How did you test this code?
scannerDigestLogic.test.tsto assert the new dispatch chain (createDigestSuccess,addAction,loadLatestRunSuccess) and, crucially, that after settlingdigestis populated andlatestRunLoadingis false — the two conditions that guarantee no flash. This catches a regression back to the refetch behavior. Ran it: 3/3 pass.*LogicTypefiles for the new action).Automatic notifications
Docs update
No user-facing docs cover this card, so nothing to update.
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Kim reported the flash and directed the fix; I (Claude, via PostHog Code) traced the cause and implemented it. I explored the scanner-page logic to find that the flash came from
createDigestcallingloadActions(), then chose the optimistic-insert approach becausevisionActionsLogicalready uses that exact pattern fortoggleActionEnabledandvisionActionsCreatereturns the full created object — so no refetch is needed at all. Considered instead loosening thevisionActionsLoading && !digestguard in the component, but that only closed one of the two flash windows, so I went with the logic-side fix that closes both.Skills invoked:
/writing-tests(before editing the logic test).Created with PostHog Code