welcome-download.mdtara.mdarete.mdnisaba.md03-pwa-behavior.mdJourney flow#
Generated from the authored steps below — click a node to jump to that section.
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
onlineevent. - 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 portal —
onlinefires 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#
apps/oshun/web/e2e/pwa-install-update-offline.spec.tscovers installation, update, fallback, and offline shell behavior.apps/oshun/web/e2e/offline-shell-routes.spec.tsproves the navigation-cache scope.apps/oshun/web/e2e/offline-background-sync.spec.tsverifies the explicit Arete queue and replay contract.apps/oshun/web/e2e/offline-write-deepening.spec.tscovers additional offline-write states and negative boundaries.- Coverage is deep for the declared shell and Arete queue, but partial for product-wide offline continuity because most APIs and several local-first writes are intentionally outside that contract.
Per-view files touched#
shell/03-pwa-behavior.md— service-worker and offline banner behavior.customer/01-entry/welcome-download.md— install-pre-cached entry surface.customer/04-arete/arete.md— explicit replay example.customer/07-nisaba/nisaba.md— bounded cached API reads.customer/03-tara/tara.md— local reflection counterexample.
Cross-references#
install-as-pwa.md— installation and update lifecycle.first-tara-sit.md— Tara completion/reflection boundaries.library-save-collection-share.md— optimistic library state.- Code:
apps/oshun/web/public/sw.js,apps/oshun/web/src/components/pwa/PwaBootstrap.tsx, andapps/oshun/web/src/components/pwa/OfflineBanner.tsx.
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?