V1 Web PWA · Journey

Journey: Offline warm-cache continuity and bounded write replay

A source-reconciled journey record for V1 Web PWA — shipped behavior, state boundaries, failure modes, and the automation evidence available today.

automated · 4 specs verdict: partial· 2026-05-29
9sections6 minread

On this page
Journey at a glance
ActorsReturning PWA memberOffline readerOffline writerRecovery tester
Automation4 E2E spec(s) · verdict: partial· 2026-05-29

Journey flow#

Generated from the authored steps below — click a node to jump to that section.

flowchart TD s1["1. Warm the authenticated shell"] s2["2. Warm page-specific data"] s3["3. Go offline and inspect the shell"] s4["4. Distinguish cached navigation from cached…"] s5["5. Queue an explicitly supported write"] s6["6. Reconnect and verify the side effect"] s1 --> s2 --> s3 --> s4 --> s5 --> s6 click s1 href "#1-warm-the-authenticated-shell" click s2 href "#2-warm-page-specific-data" click s3 href "#3-go-offline-and-inspect-the-shell" click s4 href "#4-distinguish-cached-navigation-from-cached-data" click s5 href "#5-queue-an-explicitly-supported-write" click s6 href "#6-reconnect-and-verify-the-side-effect" s6 --> v(["verdict: partial"]) click v href "../results/offline-first-time-use.html" style v stroke:#d6a531,color:#d6a531

Oshun's PWA supports useful offline continuity after an online warm-up; it does not provide a cold-install copy of every customer route or a universal offline write queue. The service worker pre-caches the welcome/download shell, warms a declared authenticated-route set, caches navigation responses, and permits offline API reads only for specific Nisaba endpoints. Arete uses the explicit background-sync queue. Other local-first flows may retain browser state without ever entering that queue. This journey tests those exact boundaries.

Personas#

  • Returning PWA member — has already loaded the authenticated shell and selected routes online.
  • Offline reader — revisits an already fetched navigation or allowed Nisaba response.
  • Offline writer — performs an Arete action that explicitly posts to the service-worker queue.
  • Recovery tester — restores connectivity and verifies the concrete server side effect, not just the disappearance of an offline banner.

Pre-conditions#

  • Install or run the PWA with the service worker controlling the page.
  • Sign in online before testing protected routes. The manifest start URL is /?surface=pwa, while / itself is intentionally not install-pre-cached because it participates in redirect/session resolution.
  • Visit the exact pages and data that must work offline. This is a warm-cache journey, not a fresh-install/cold-cache claim.
  • Use a supported browser with service worker, Cache Storage, and IndexedDB.
  • For replay verification, choose an action whose implementation sends OSHUN_QUEUE_ACTION; Arete has named coverage for this seam.

Steps#

1. Warm the authenticated shell#

PwaBootstrap asks the service worker to warm the allowlisted shell routes with serial fetches as soon as the component initializes. The warming is not deferred through requestIdleCallback. Navigation requests use network-first caching for the declared exact/prefix routes; the install cache itself contains /welcome, /welcome/download, and static assets rather than the complete customer app.

2. Warm page-specific data#

Fetch the items needed during the offline period. API reads are network-only by default. The service worker makes explicit exceptions for Nisaba daily-passage and notebook endpoints and retains those responses for the configured cache window. There is no Keep offline control; eligibility and prior fetch are what matter.

Activity is not in the API cache allowlist. A cached activity HTML navigation can still load, but that is not proof that its BFF data is available offline. Page-local stores or honest fallbacks may determine what the user sees.

3. Go offline and inspect the shell#

When the browser fires its offline event, OfflineBanner renders an assertive role="alert". It performs an initial health check and responds to browser online/offline events; it does not poll continuously. Retry uses a five-second abort timeout and a health sentinel to avoid treating a captive portal page as recovery.

The banner is not automatically wired to the reduced-motion preference, so do not claim its animation is disabled merely because the OS preference is set.

4. Distinguish cached navigation from cached data#

Open only routes warmed in step 1 and confirm the honest offline surface. The service worker can mark a cached home document for PwaOfflineFallback, after which hydrated local content may take over. Repeat with one warmed Nisaba allowlist response and one non-allowlisted API to prove the boundary.

5. Queue an explicitly supported write#

Perform the covered Arete offline action. The page posts OSHUN_QUEUE_ACTION; the service worker stores it in the oshun-sync-queue IndexedDB database and can request background sync. Inspect pending count/state rather than assuming every localStorage write joins this queue.

Library saves are optimistic and browser local first; failed server pushes are swallowed and are not posted to the service-worker queue. Tara reflections use tara:reflection:<sitting.id> localStorage and currently have no replay consumer. Their local confirmation must not say that background sync will necessarily deliver them.

6. Reconnect and verify the side effect#

Restore a healthy network, trigger retry or allow the browser sync event, and verify the Arete record through its BFF/read surface. pwa_sync_queued is a real client analytics event for pending actions. The audited service worker does not emit a matching pwa_sync_complete analytics event; replay completion is observable through state/read-back and logs.

Post-conditions#

  • Warm shell routes and explicitly allowed cached data are identified by exact scope.
  • At least one explicitly queued Arete action is verified after server read-back.
  • Browser-only library and Tara state are not described as queued writes.
  • Banner recovery is tied to a successful health sentinel, not merely a browser online event.
  • No storage-quota warning is claimed: cache trimming removes older keys but exposes no quota hint to the member.

Failure modes#

  • Cold cache — a route or data response was never fetched while online.
  • Protected shell without session — a cached document cannot manufacture a valid authenticated session.
  • API outside allowlist — navigation loads but required live data remains unavailable.
  • Captive portalonline fires while the health sentinel fails; the banner must remain.
  • Unsupported queued-write claim — a localStorage update is mistaken for an IndexedDB background-sync entry.
  • Replay authorization failure — the queued request no longer has usable credentials and requires explicit recovery.
  • Motion/accessibility mismatch — banner animation is assumed to obey a preference it does not currently read.

E2E coverage#

Per-view files touched#

Cross-references#

Open questions#

  • Which additional API reads are safe, useful, and privacy-appropriate to add to the explicit offline allowlist?
  • Should Library and Tara adopt the service-worker queue or change their copy to describe local-only retention?
  • How should the UI expose exact cached inventory, age, size, and eviction?
  • Should reduced-motion state be passed into OfflineBanner?
  • What recovery UI should handle expired credentials or permanently rejected queued writes?