What changed, and why
Clicking "Sign out" did nothing visible. Only a page refresh revealed the session was actually gone โ and re-login then failed.
The cause was the scope of one call. signOut() ran with the default scope: 'global', and @supabase/auth-js makes that server revoke before it clears the local session. So a network or 5xx blip on the revoke rejected the promise, and the lines meant to follow โ re-anonymize, reload the balance โ never ran. The popover sat unchanged (nothing happened), while the session quietly went away by the next load.
The fix moves the reset into a small, resilient helper and switches to a local sign-out, so the UI always lands on a clean signed-out state.
The resilient reset โ utils/sign-out.ts
Two deliberate choices. First, scope: 'local' โ clear this session's tokens locally, with no global server revoke that can hang or reject. Second, every step is best-effort: neither the sign-out nor the re-anonymize is allowed to throw out of here, so whatever the network does, the caller's UI reset still runs.
A local sign-out, then a fresh anonymous session โ each wrapped so it can't abort the reset.
utils/sign-out.ts ยท 44 lines
โฏ 22 lines hidden (lines 1โ22)
Wiring it โ RunsBalance.vue
The component hands the reset to the helper through thin adapters over the live Supabase client, then reloads the balance so the gauge and popover reflect the new (anonymous) state. The popover is left open on purpose: it re-renders to the signed-out view, which is the visible feedback the old path never gave.
No more close(): the popover re-renders to the signed-out state in place.
components/RunsBalance.vue ยท 111 lines
โฏ 75 lines hidden (lines 1โ75)
โฏ 26 lines hidden (lines 86โ111)
Verification
The reset is pure over its injected client, so the test drives the exact failure that caused the bug: a signOut that throws must still reach the re-anonymize, and a throwing re-anonymize must still resolve cleanly.
The happy path plus both throw-paths that the old code aborted on.