--- phase: quick-260611-tfc plan: "01" subsystem: pwa-sw tags: [service-worker, push-notifications, bug-fix, wr-01] dependency_graph: requires: [] provides: [reachable-openWindow-fallback-notificationclick] affects: [apps/pwa/src/sw.ts] tech_stack: added: [] patterns: [promise-chain-catch-fallback] key_files: created: [] modified: - apps/pwa/src/sw.ts decisions: - "Canonical fix shape: .then(navigated => openWindow if null) + .catch(() => openWindow) on the focus/navigate chain — keeps the returned promise intact for no-floating-promises" metrics: duration: ~6 min completed: "2026-06-12" --- # Quick Task 260611-tfc: Fix WR-01 sw.ts notificationclick openWindow Fallback **One-liner:** notificationclick handler extended with `.then(navigated===null → openWindow)` + `.catch(→ openWindow)` so the new-window fallback is reachable on both focus rejection and null navigate. ## What Was Done Fixed WR-01 from `.planning/phases/13-real-lint-gate-eslint/13-REVIEW.md`. The focusable-client `return` inside the `for (const client of clientList)` loop was: ```ts return client.focus().then(() => client.navigate(url)); ``` Two failure modes made the `openWindow` fallback unreachable: 1. `navigate()` resolving `null` (browser rejected the navigation) — the chain resolved successfully, so the post-loop fallback was never reached. 2. `focus()` or `navigate()` rejecting (window closed between `matchAll` and `focus`) — the rejection propagated into `event.waitUntil`, marking the click as handled without opening a new window. Applied the canonical replacement per the review: ```ts return client .focus() .then(() => client.navigate(url)) .then((navigated) => { if (navigated === null && self.clients.openWindow) return self.clients.openWindow(url); }) .catch(() => { if (self.clients.openWindow) return self.clients.openWindow(url); }); ``` All surrounding behaviour preserved: `event.notification.close()`, `url` extraction + eslint-disable comments, `matchAll` options, and the post-loop no-focusable-client fallback. ## Verification — Exit Codes | Command | Exit | |---------|------| | `pnpm lint` | 0 | | `pnpm typecheck` | 0 | | `pnpm --filter @familysync/pwa test` | 0 (191/191 pass) | | `pnpm --filter @familysync/pwa build` | 0 (dist/sw.js emitted) | ## Deviations from Plan None — plan executed exactly as written. ## Self-Check: PASSED - `apps/pwa/src/sw.ts` modified: confirmed - Commit `af78ccc` exists: confirmed - `git diff --diff-filter=D HEAD~1 HEAD`: no deletions - Only `apps/pwa/src/sw.ts` changed (`git diff --stat` = 1 file, 10 insertions, 1 deletion)