Skip to content

[bug] HealthBar notify race: rapid toggle during async permission prompt #95

@fedorovvvv

Description

@fedorovvvv

Source: PRD-018 SC-2 audit, Logic [W1] + Security [W1].

template/src/widgets/health-bar/ui/HealthBar.svelte:39-48

async function onToggleNotify(next: boolean) {
  if (next && permission === 'default') {
    const result = await Notification.requestPermission();
    permission = result;
    notify = result === 'granted';
    return;
  }
  notify = next && permission === 'granted';
}

Between await Notification.requestPermission() and the assignment to notify, the user can re-toggle the Toggle. The second invocation reads stale permission and may write the wrong final state. bits-ui will also optimistically flip pressed → true during the await, then snap back if denied — which feels glitchy.

Repro

  1. Click "🔔 Notify" with permission === 'default'.
  2. Before the permission prompt resolves, click again.
  3. Observe inconsistent final state vs the actual permission outcome.

Fix

  • Disable the Toggle while a request is in flight (disabled={requesting}); set requesting = true before await, false after.
  • Bind pressed strictly to a $derived of notify && permission === 'granted' so it never optimistically flips.
  • Confirm Toggle.pressed non-bindable usage matches the primitive's contract (declared pressed = $bindable(false) — see Logic [W2]).

Acceptance

  • Rapid double-click on the Toggle during the permission prompt cannot produce a state where pressed and permission disagree.
  • No visual flicker between optimistic pressed=true and the final value.

Audit traceability

  • Reviewers: Logic, Security

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:appbugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions