Skip to content

feat(push): harden web push delivery reliability (t095)#9

Merged
duongdev merged 2 commits into
mainfrom
feat/t095-harden-web-push-delivery
Jun 19, 2026
Merged

feat(push): harden web push delivery reliability (t095)#9
duongdev merged 2 commits into
mainfrom
feat/t095-harden-web-push-delivery

Conversation

@duongdev

Copy link
Copy Markdown
Owner

Task: docs/tasks/done/095-harden-web-push-delivery-reliability.md

Makes the web-build Web Push subscription stay alive, recoverable, and timely on the daily-driver iPad PWA. Bundles one reliability story — "a push subscription that survives the things iOS does to it".

What changed

  • E1 — revocation-proof service worker. public/sw.js's push handler now always calls showNotification (a generic "New message" fallback on !e.data, a parse failure, or any processing error). On iOS a push that renders nothing is a userVisibleOnly violation and WebKit revokes the subscription with no documented grace count. Content shaping is the pure, tested buildNotificationContent in src/lib/push-notification.ts, mirrored into the static SW (the sw-cache-name.ts pattern).
  • E0 — endpoint-reconciled per-device identity (ADR-0014). deviceId is now server-authoritative, reconciled by push subscription endpoint (core/push-subscriptions.js#reconcileDeviceId). POST /api/notifications/subscribe returns the reconciled { deviceId }; the renderer adopts it as the single source for device-keyed ui-state. A storage wipe + re-subscribe on the same endpoint recovers the prior deviceId (and thus prior mutes/master) — the push endpoint is the only identity that outlives an iOS script-storage eviction.
  • E1b — foreground re-validate. The app re-validates the subscription on visibilitychange (the only recovery hook iOS leaves, since pushsubscriptionchange is dead on iOS PWAs), gated once-per-foreground by the pure createPushRevalidateGate (src/lib/push-revalidate.ts).
  • E2 — delivery tuning. webpush.sendNotification now sends { urgency: "high", TTL: 1800 } from the pure pushSendOptions(), so a triage ping isn't battery-deferred and an undeliverable one doesn't linger ~4 weeks and resurrect stale.
  • E4 — regression guard. Locks the t093 Slack health-alert muteKey stamping.

Tests

  • Layer 1 (pure, TDD): buildNotificationContent, reconcileDeviceId, the foreground-revalidate gate, pushSendOptions, the muteKey guard.
  • Layer 2 (e2e keystone): test/e2e/server.e2e.test.ts proves the E0 reconcile end-to-end through the fake-CDP + server.mjs harness — same endpoint → same deviceId + no duplicate sub record; new endpoint → distinct id; client cached id ignored.
  • All gates green: pnpm test (909), pnpm test:e2e (43), pnpm typecheck, pnpm build, node --check web/server.mjs, pnpm web boot.

Not in this PR (non-blocking)

Device-only iOS confirmations (no-revocation on malformed push, storage-wipe recovery, foreground re-validate firing) — logged in the task for the next real-device session. The AFK gates prove the logic + contracts; the iOS behavioral guarantees can't be asserted headlessly.

https://claude.ai/code/session_01NnSM463eFUgkLNtYBQwe6u

duongdev added 2 commits June 20, 2026 01:12
- E1: revocation-proof SW push handler — always show notification
- E0: endpoint-reconciled per-device identity — survives storage wipe
- E1b: once-per-foreground re-validation gate — iOS PWA recovery
- E2: push send options (urgency:high, TTL:1800) for timely delivery
- E4: regression guard on Slack health-alert muteKey stamping
- Layer 1: pure tests for E1/E0/E1b/E2/E4 components
- Layer 2: e2e reconcile keystone test validates endpoint-deduping
- All gates green: tests + typecheck + build + web server boot
@dokploy-2026-03-01-6ei3s7

dokploy-2026-03-01-6ei3s7 Bot commented Jun 19, 2026

Copy link
Copy Markdown

Dokploy Preview Deployment

Name Status Preview Updated (UTC)
cdp-browser-app ✅ Done Preview URL 2026-06-19T18:27:32.267Z

@duongdev duongdev merged commit d018f3c into main Jun 19, 2026
3 checks passed
@duongdev duongdev deleted the feat/t095-harden-web-push-delivery branch June 19, 2026 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant