nexus/.planning/phases/26-pwa-performance/26-00-PLAN.md

9 KiB

phase plan type wave depends_on files_modified autonomous requirements must_haves
26-pwa-performance 00 execute 1
ui/public/sw.js
ui/src/types/pwa.d.ts
ui/src/hooks/useOfflineQueue.test.ts
ui/src/hooks/useInstallPrompt.test.ts
ui/src/hooks/usePushNotifications.test.ts
ui/src/components/PullToRefresh.test.tsx
true
PWA-01
PWA-07
PERF-05
truths artifacts key_links
Service worker uses cache-first for static assets and cache-first for navigation
Old paperclip-v2 cache is deleted on SW activation
Service worker handles push events and notificationclick events
PWA TypeScript types exist for BeforeInstallPromptEvent
Wave 0 test stubs exist for all hooks and components planned in later waves
path provides contains
ui/public/sw.js Cache-first service worker with push handler nexus-v1
path provides contains
ui/src/types/pwa.d.ts BeforeInstallPromptEvent type declaration BeforeInstallPromptEvent
path provides contains
ui/src/hooks/useOfflineQueue.test.ts Test stub for offline queue hook it.todo
path provides contains
ui/src/hooks/useInstallPrompt.test.ts Test stub for install prompt hook it.todo
from to via pattern
ui/public/sw.js ui/src/main.tsx SW registration on load event (already wired) serviceWorker.register
Foundation for Phase 26: rewrite the service worker from network-first to cache-first, install dependencies (idb, web-push), create PWA TypeScript types, and scaffold Wave 0 test stubs for all hooks/components coming in later plans.

Purpose: Provides the upgraded SW that enables PERF-05 (cached load < 1s) and the test infrastructure for TDD in subsequent plans. Output: Upgraded sw.js, pwa.d.ts type declarations, 4 test stub files, idb + web-push installed.

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/26-pwa-performance/26-RESEARCH.md @.planning/phases/26-pwa-performance/26-UI-SPEC.md @ui/public/sw.js @ui/src/main.tsx Task 1: Install dependencies, create PWA types, and rewrite service worker ui/public/sw.js, ui/src/types/pwa.d.ts - ui/public/sw.js - ui/src/main.tsx - ui/public/site.webmanifest - .planning/phases/26-pwa-performance/26-RESEARCH.md 1. Install dependencies: - `pnpm --filter @paperclipai/ui add idb` - `pnpm --filter @paperclipai/server add web-push` - `pnpm --filter @paperclipai/server add --save-dev @types/web-push`
  1. Create ui/src/types/pwa.d.ts with:

    • BeforeInstallPromptEvent interface extending Event with prompt(): Promise<void> and userChoice: Promise<{ outcome: "accepted" | "dismissed" }> properties
    • Global WindowEventMap augmentation adding beforeinstallprompt: BeforeInstallPromptEvent
  2. Rewrite ui/public/sw.js — replace the entire file with a cache-first strategy:

    • Cache name: "nexus-v1" (replaces "paperclip-v2")
    • install event: call self.skipWaiting(), pre-cache ["/", "/index.html"] into nexus-v1
    • activate event: delete ALL caches whose name is NOT "nexus-v1" (this busts the old paperclip-v2 cache), then call self.clients.claim()
    • fetch event handler: a. If url.pathname.startsWith("/api") — return immediately (network-only, no interception) b. If request.mode === "navigate" — cache-first: try caches.match("/"), fall back to fetch(request) c. If URL matches static extension regex /\.(js|css|woff2?|png|svg|ico|webmanifest)$/ — cache-first: try caches.match(request), on miss fetch and clone into nexus-v1 cache, return response d. All other requests: pass through (no interception)
    • push event handler: parse event.data.json() for { title, body, icon, data }, call self.registration.showNotification(title, { body, icon: icon || "/android-chrome-192x192.png", badge: "/favicon-32x32.png", data })
    • notificationclick event handler: close notification, clients.openWindow(event.notification.data?.url || "/")

Do NOT modify ui/src/main.tsx — SW registration is already correct. Do NOT modify ui/public/site.webmanifest — manifest is already complete (PWA-02, PWA-07 already satisfied). grep -q "nexus-v1" ui/public/sw.js && grep -q "BeforeInstallPromptEvent" ui/src/types/pwa.d.ts && echo "PASS" <acceptance_criteria> - grep "nexus-v1" ui/public/sw.js returns the cache name - grep "paperclip" ui/public/sw.js returns nothing (old name fully removed) - grep "cache-first" ui/public/sw.js or the cache-first pattern (caches.match before fetch) is present for static assets - grep "push" ui/public/sw.js shows push event listener - grep "notificationclick" ui/public/sw.js shows notification click handler - grep "BeforeInstallPromptEvent" ui/src/types/pwa.d.ts returns the type declaration - pnpm --filter @paperclipai/ui exec -- node -e "require('idb')" succeeds (idb installed) - pnpm --filter @paperclipai/server exec -- node -e "require('web-push')" succeeds (web-push installed) </acceptance_criteria> Service worker rewritten with cache-first strategy and nexus-v1 cache name. idb and web-push installed. PWA types declared.

Task 2: Create Wave 0 test stubs for Phase 26 hooks and components ui/src/hooks/useOfflineQueue.test.ts, ui/src/hooks/useInstallPrompt.test.ts, ui/src/hooks/usePushNotifications.test.ts, ui/src/components/PullToRefresh.test.tsx - ui/src/components/SwipeToArchive.test.tsx - .planning/phases/26-pwa-performance/26-RESEARCH.md Create 4 test stub files using `it.todo()` (not `it.skip()`) — consistent with Phase 21-25 convention.
  1. ui/src/hooks/useOfflineQueue.test.ts:

    • describe("useOfflineQueue") with:
      • it.todo("enqueues message when offline")
      • it.todo("flushes queue on online event")
      • it.todo("stops flushing on first failed POST")
      • it.todo("returns queued message count")
  2. ui/src/hooks/useInstallPrompt.test.ts:

    • describe("useInstallPrompt") with:
      • it.todo("captures beforeinstallprompt event")
      • it.todo("returns canInstall=true when event captured and not standalone")
      • it.todo("returns canInstall=false when already installed (standalone)")
      • it.todo("calls prompt() on the deferred event when promptInstall is called")
  3. ui/src/hooks/usePushNotifications.test.ts:

    • describe("usePushNotifications") with:
      • it.todo("subscribes when permission is granted")
      • it.todo("does not subscribe when permission is denied")
      • it.todo("sends subscription to server via POST /api/push/subscribe")
  4. ui/src/components/PullToRefresh.test.tsx:

    • Add // @vitest-environment jsdom pragma at top (mirrors SwipeToArchive.test.tsx pattern)
    • describe("PullToRefresh") with:
      • it.todo("calls onRefresh after drag exceeds 64px threshold")
      • it.todo("does not trigger when scrollTop is not 0")
      • it.todo("resets pull distance on touch end below threshold")

All test stubs should have minimal imports — no service mocks until implementation plans wire them up.

NOTE: No MobileNavBar.test.tsx is created — the existing MobileBottomNav in Layout.tsx already handles global mobile navigation. If tests are needed for MobileBottomNav, they would belong to a separate plan. pnpm --filter @paperclipai/ui test --run useOfflineQueue useInstallPrompt usePushNotifications PullToRefresh 2>&1 | grep -E "todo|Tests" | head -10 <acceptance_criteria> - All 4 test files exist at the specified paths - Each file uses it.todo() not it.skip() - PullToRefresh.test.tsx has // @vitest-environment jsdom at top - pnpm --filter @paperclipai/ui test --run passes (todos are not failures) </acceptance_criteria> 4 Wave 0 test stubs created covering PWA-01, PWA-03, PWA-06, PWA-08. All test files use it.todo() and pass vitest run.

- `grep "nexus-v1" ui/public/sw.js` confirms cache name - `grep -c "paperclip" ui/public/sw.js` returns 0 - All 4 test stub files exist and use `it.todo()` - `pnpm --filter @paperclipai/ui test --run` passes - `idb` appears in `ui/package.json` dependencies - `web-push` appears in `server/package.json` dependencies

<success_criteria> Service worker upgraded to cache-first with nexus-v1 cache. Dependencies installed. PWA types declared. All Wave 0 test stubs created and passing. </success_criteria>

After completion, create `.planning/phases/26-pwa-performance/26-00-SUMMARY.md`