fix: replace generation counter with cancelled flag in search effect (#192)#198
Conversation
…192) Co-authored-by: Ona <no-reply@ona.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
[ci-fix] This PR has failed CI 3+ times. Needs human investigation. Root cause: The The branch is based on Fix: Rebase the branch onto current |
…state Co-authored-by: Ona <no-reply@ona.com>
The Supabase CLI's db reset can lose the ON CONFLICT clause when splitting statements, causing a duplicate key error on the page-images bucket. Wrapping in a DO block with EXCEPTION WHEN unique_violation is immune to statement splitting. Co-authored-by: Ona <no-reply@ona.com>
|
✅ UI verification passed — design spec compliance confirmed. Changed UI file: This PR is a pure internal refactor (generation counter → cancelled flag). No JSX, CSS classes, or rendered output changed. Visual verification on the live site confirmed:
|
|
❌ Post-merge verification found 1 E2E test flake (not a production regression). E2E suite: 46/47 passed
Ad-hoc smoke tests: all passed
Filed #199 for the flaky E2E test. |
Closes #192
What
The search empty state ("No pages match your search") never renders in production — skeleton loaders stay visible indefinitely. This is the 7th recurrence of the same bug (issues #118, #126, #136, #144, #162, #178, #181, #192). Every previous fix passed unit tests but failed in production E2E because the root cause was never addressed.
The bug is in the generation counter pattern used to discard stale async callbacks. When the effect re-runs between fetch start and completion, it increments the counter. The old fetch's
finallyblock checkssearchGenRef.current === gen— since the counter was incremented, the check fails andsetSearchStatus("done")is never called, leaving the UI stuck at "loading".Additionally, the page icon E2E test ("user can remove a page icon") fails with a 404 due to Supabase replication lag between the client-side page insert and the server-side read.
How
Search fix: Replaced the generation counter (
searchGenRef) with a booleancancelledRefflag. The flag is set totruein the effect cleanup and checked in.then()/.catch()callbacks. Unlike a counter that can be "incremented past" the expected value by a concurrent effect run, a boolean flag is set once and stays true — the stale callback is always discarded, and the active cycle always completes.Page icon fix: Added retry logic to
navigateToEditorPage— if the editor page returns a 404, it reloads and retries up to 2 times before failing.Conventions update: Documented the cancelled flag pattern in
.agents/conventions.mdwith a clear ❌/✅ comparison to prevent future regressions.Testing
beforeEachto resetmockMaybeSingleeach test (was being cleared byvi.restoreAllMocks()).