Push Notifications v1 (waves / accepts / messages) - Merged โ
Date: 2026-07-25 PR: #715 (squash-merged to dev at 18:32 UTC, 27/27 checks green) Issue: Part of #161 (kept open: history, achievement/streak categories, quiet hours) Spec / plan: docs/planning/specs/2026-07-25-push-notifications-design.md, docs/planning/plans/2026-07-25-push-notifications.md (decision log D1-D18) Test plan: docs/engineering/testing/runs/push-notifications-v1/README.md (12 scenarios; live rows pending post-deploy)
What shipped โ
- Cloud Functions triggers for wave-created, wave-accepted, and message-created (5-minute per-connection throttle) sending FCM web push over the existing moderation-notice rail.
- DATA-ONLY payloads across ALL senders (product triggers and the auth-api moderation notice sender): the service worker is the sole displayer and owns collapse tags plus click routing. This fixed a verified FCM SDK double-display and a dead-end tap on the SDK copy.
- Privacy invariants enforced in payload construction: no message content, no counterpart identity, no venue names, inbox-level deep links, hashed collapse tags.
- Client:
registerForPush(alias kept), foreground toasts gated to product kinds with fixed copy,NotificationSettingsSection(Settings tab),EnablePushPromptpost-first-wave nudge, prefs inusers/{uid}.notificationPrefs. @lantern/shared/notifications: single source for category defaults and push kinds.- firestore.rules:
connections.pushStateis server-only at create AND update (mute-poisoning attack closed), with emulator tests.
Verification โ
npm run validate 31/31; /code-review round of 10 findings all fixed pre-merge; suites: 9 client vitest, 10 functions node:test, 178 rules, 437 auth-api; Storybook visual pass. One CI fix post-open: VitePress parsed a bare <venue> placeholder in the spec as an unclosed tag (backticked).
Post-merge deploy + live smoke (same day) โ
- The merge's deploy was silently dropped by the workflow_run pipeline (changelog bot
[skip ci]push + rapid sibling merges); recovered viaforce_full_deploydispatch. Filed #718 for the systemic gap. - Live smoke on dev.ourlantern.app (headless, test account): login automation works, the Notifications settings card renders (denied-state copy verified), new service worker confirmed serving.
- FOUND LIVE: the deployed build had no
VITE_FIREBASE_VAPID_KEY(deploy-dev.yml never passed it to the app build), so ALL push registration was silently disabled on deployed dev, including pre-existing moderation notices. Fixed by inlining the public VAPID key into the app build env (follow-up PR).
Follow-ups โ
- Live test-plan rows (3-9, 12) on dev once the VAPID fix deploys; needs one admin invite link for a second test account.
- Scenario 10 (iPhone home-screen install, lock-screen push) is operator-run.
- Deferred by design: venue-activity nudge (stretch), moderation sender adopting the shared helper, remaining #161 scope.