# OneSignal Push Notifications

## Objective

Send push notifications to the right devices, with user preferences respected.

A push delivery path through OneSignal covering subscription registration, audience targeting, per-channel preferences, and delivery outcomes recorded against the app's own records.

## Before You Begin

This feature is being added to an application that already exists and already
works. Do not scaffold a new project, and do not assume a blank slate.

Inspect the codebase first and establish:

- The existing application structure and where code of this kind already lives.
- The framework and version in use.
- The existing design system — colours, spacing, typography, and component conventions.
- Existing UI components you can reuse instead of writing new ones.
- The existing database structure, if this feature needs to persist anything.
- The existing authentication and authorization system, if this feature is user-scoped.
- Dependencies already installed, so you don't add a library that duplicates one.
- The existing test setup and conventions.

Only start writing code once you understand the above. If the application
already implements part of this feature, extend it rather than replacing it.

## Implementation Instructions

1. Find the events the app already notifies on by email or in-app, and treat push as another channel for those same events rather than a separate notification system with its own triggers.
2. Record the link between a push subscription and an account on the server, established at sign-in and severed at sign-out, so a device that changes hands does not keep receiving the previous user's notifications.
3. Hold the provider credentials server-side and send every message from the app's existing background-job system, so a slow or failing provider never blocks a web request.
4. Ask for the browser or device permission at a moment the user has chosen, after they have enabled push in settings, and never on first page load. A denied permission is usually permanent and cannot be re-prompted.
5. Do not treat the provider's segments as the source of truth for who should be notified. Decide the audience from the app's own data and preferences, then address the send to specific subscriptions, or overlapping segments will deliver the same event two or three times.

## UI and UX Requirements

Match the application's existing design system exactly. Reuse its components,
spacing, and typography. This feature should look like it was always there.

## Responsive Requirements

Works on mobile, tablet, and desktop. Touch targets are large enough to hit on a
phone, and nothing overflows horizontally at 320px.

## Accessibility Requirements

- Fully keyboard navigable.
- Correct semantic elements and ARIA roles.
- Visible focus states.
- Meets WCAG AA contrast.
- Dynamic changes are announced to screen readers.
- Respects prefers-reduced-motion.

## Edge Cases

- A shared or borrowed device can accumulate subscriptions for more than one account. Bind a subscription to the account that was signed in when it was created and clear it at sign-out, rather than merging every subscription seen on that device into one user.
- Users who have muted a category, or muted notifications entirely, must be filtered out before the send is composed. Push must obey the same per-channel preferences and quiet hours as email, not bypass them.
- The provider will report subscriptions that have expired, been revoked, or belong to an uninstalled app. Mark those inactive on the next failure rather than retrying them forever, and treat a duplicate registration for the same device as a replacement rather than a second recipient.
- A notification that deep-links into the app must re-check permission when it is opened. The recipient may have lost access to the record, or the record may have been deleted, so the destination needs a graceful fallback instead of an error page.
- The same event reaching a user through two overlapping audiences must be collapsed into one delivery. Deduplicate on the event identifier and the recipient before anything is handed to the provider.
- When the provider is unreachable or rate-limits the send, queue and retry with increasing delays rather than dropping the notification, and stop retrying anything time-sensitive once it is no longer worth delivering.
- Notification content is visible on a lock screen. Keep names, amounts, and message bodies out of the payload unless the user has opted into showing them.
- Some browsers and platforms do not support push at all. The settings screen must say so plainly instead of offering a toggle that silently does nothing.

## Testing

Exercise the feature end to end in the running application. Cover every edge case
above, then run the existing test suite and confirm nothing regressed.

## Acceptance Criteria

- [ ] Push subscriptions are bound to the account that registered them and are released at sign-out.
- [ ] Per-category and per-channel preferences are applied before a send is composed, and muted users receive nothing.
- [ ] Expired, revoked, and duplicate subscriptions are marked inactive after provider failures rather than retried indefinitely.
- [ ] A single event produces exactly one notification per recipient regardless of how many audiences match.
- [ ] Opening a notification for a deleted or now-forbidden record lands on a clear message rather than an error.
- [ ] Provider outages and rate limits are absorbed by retries in the background job system with no failed web requests.
- [ ] No provider credential is present in anything served to the browser.
- [ ] The feature matches the existing design system.
- [ ] No existing functionality is broken.

## Adaptation Rules

- Match the existing design system. Do not introduce a new colour palette,
  spacing scale, or component library.
- Reuse existing components and utilities wherever they fit.
- Follow the naming, file layout, and code style already present.
- Do not upgrade, replace, or remove existing dependencies to make this
  feature fit. Adapt the feature to the app, not the app to the feature.
- Do not break existing functionality. If a change is genuinely required in
  existing code, make the smallest one that works and say so.
- If something in these instructions conflicts with how the application is
  built, follow the application and explain the deviation.

## Final Verification

Before you report the work as done:

1. Re-read the acceptance criteria above and check each one against what you
   actually built.
2. Run the application and exercise the feature end to end.
3. Run the existing test suite and confirm you have broken nothing.
4. Check the feature on mobile, tablet, and desktop widths.
5. Check keyboard navigation and focus handling.
6. Summarize what changed: files added, files modified, and anything you
   deliberately did differently because of how this application is built.

If any acceptance criterion is unmet, fix it before reporting completion.
