---
status: reconciled-partial
coverage_depth: deep
last_reconciled: 2026-07-18
specs:
  - apps/oshun/web/e2e/pwa-smoke.spec.ts
  - apps/oshun/web/e2e/pwa-install-update-offline.spec.ts
  - apps/oshun/web/e2e/pwa-lifecycle-deepening.spec.ts
  - apps/oshun/web/e2e/pwa-failure-modes.spec.ts
source: WALKTHROUGH/journeys/install-as-pwa.md
---

# Journey result: Install as PWA

- **Walked**: 2026-05-29 by Claude against commit `eff6655269`; that pass
  verified the repaired registration, manifest, prompt surface, and relaunch
  cookie in the then-current build.
- **Reconciled**: 2026-07-18 against the current manifest/service worker,
  install education, native-prompt boundary, standalone launch, offline/update
  runtime, failure modes, push routing, and deep browser suites. This was a
  source reconciliation, not a fresh runtime walk.
- **Verdict**: **partial** — deep automation proves the application-controlled
  PWA contract from manifest assets and install-prompt handling through
  standalone relaunch, offline shell/read/write behavior, update coordination,
  Background Sync, and failure recovery. OS-level installation/uninstall, Safari
  Add to Home Screen, deterministic storage-quota exhaustion, and real device
  push permission/delivery remain platform or manual boundaries.
- **Primary specs**: `pwa-smoke.spec.ts`, `pwa-install-update-offline.spec.ts`,
  `pwa-lifecycle-deepening.spec.ts`, and `pwa-failure-modes.spec.ts`.

## Result at a glance

| Evidence lane                | Current result                                                                                       | Authority limit                                                |
| ---------------------------- | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| Installability assets        | Manifest metadata, icons, screenshots, shortcuts, mobile tags, and JavaScript service worker         | Browser contract; not store/launcher review                    |
| Install prompt and education | Captured `beforeinstallprompt`, accept/dismiss/cooldown/installed states, and Safari manual copy     | Browser event is simulated; OS install UI is not automated     |
| Standalone launch            | Signed-in and anonymous cold launch, saved route, iOS/standard detection, and prompt suppression     | Emulated standalone context, not physical launcher lifecycle   |
| Offline read/write           | Warm shell, uncached fallback, health/captive-portal guards, local writes, and real Background Sync  | Named routes/actions; not universal offline parity             |
| Service-worker update        | Waiting worker, deferral, reading-safe state, refresh apply, multi-tab coordination, and cache purge | Harness-controlled worker lifecycle                            |
| Failure and push boundaries  | Registration blocked, stuck apply, reload-loop guard, device registration, and push/click routing    | Browser/API shims; no real user permission or carrier delivery |

## Evidence map

The automated contract reaches the browser/OS boundary and then continues from
an emulated standalone launch. It cannot click the launcher or Safari share
sheet, so those steps remain dashed rather than inferred.

```mermaid
flowchart LR
    A[Manifest and service worker] --> B[Browser install eligibility]
    B --> C[Captured install prompt]
    C --> D[Accepted or dismissed app state]
    C -. OS-controlled .-> E[Launcher installation]
    F[Safari education] -. manual share sheet .-> E
    E -. emulated boundary .-> G[Standalone cold launch]
    G --> H[Offline shell and fallback]
    H --> I[Local writes and Background Sync]
    G --> J[Waiting-worker update]
    J --> K[Deferral or coordinated apply]
    K --> L[Route-preserving reload and cache purge]
```

## Proven observations

### Install and standalone boundary

- The document links one manifest; required icons, maskable assets, screenshots,
  shortcuts, theme/display fields, mobile metadata, and `sw.js` content type are
  pinned by the smoke suite.
- `/welcome/download` explains Chrome/Edge/Android prompt behavior and Safari's
  Share → Add to Home Screen path. The shared install trigger consumes the
  captured `beforeinstallprompt` event when the browser supplies it.
- Accept, dismiss, 14-day cooldown, installed state, standard standalone media
  query, and iOS `navigator.standalone` branches are covered. Signed-in launch
  restores the saved route; anonymous launch preserves it through `/welcome`.

### Offline and update runtime

- Warm Home, Explore, Activity, and Library routes remain available offline. An
  uncached deep route receives the real service-worker document fallback and
  hydrates explicit retry, shell shortcuts, and recent-content states.
- BFF health failure and captive-portal-shaped HTML keep the offline state
  honest. A valid health sentinel clears it.
- Local Library/Nisaba/Tara writes expose their current queued state. A real
  service-worker Arete queue survives reload, replays through Chromium
  Background Sync, reaches the live BFF, and drains.
- Waiting-worker prompts respect unresolved consent, standard versus reading
  copy, deferral, next-launch reappearance, queued writes, reduced motion,
  explicit refresh, controller change, multi-tab coordination, route/scroll
  preservation, and stale-cache purge.

### Failure and push seams

- A blocked service-worker registration leaves a usable normal-web fallback.
  Offline and update overlays can coexist, a stuck apply exposes manual reload,
  and the controller-change guard prevents repeated reload loops.
- Push-device registration and service-worker push/click routing have focused
  browser coverage. These tests exercise application and worker handlers, not a
  physical device's permission UX or external push network.

## Boundaries and gaps

- **A captured prompt is not an OS installation.** Playwright can drive the
  application event handler and accepted state, but it cannot certify launcher
  icon creation, OS uninstall, app settings, or browser-specific install UI.
- **Safari instructions are not Safari installation evidence.** Add to Home
  Screen is an OS/share-sheet path with no headless `beforeinstallprompt`.
- **Emulated standalone is not a device matrix.** Media-query and
  `navigator.standalone` branches prove routing logic, not every Android/iOS/
  desktop shell, safe-area, keyboard, orientation, and resume behavior.
- **Named offline paths are not universal offline parity.** The suite covers
  core shell routes, specific cached content, and selected writes. Other dynamic
  pages, uploads, conflicts, and multi-user data remain online or need their own
  contracts.
- **Queued is not synchronized.** Some domain features preserve local writes
  without a drain; only the Background Sync action proves replay and removal.
- **Harnessed update state is not every browser race.** Real worker install
  timing, storage eviction, quota pressure, process death, and browser policy
  can still differ.
- **Push routing is not push delivery.** App registration/worker handlers do not
  prove user permission comprehension, APNs/FCM delivery, OS notification
  presentation, or production click attribution.
- **Targeted checks replace a score, not product judgment.** Lighthouse PWA
  scoring is intentionally not the release gate, while manual real-device
  installation remains necessary.

## Re-run evidence

The core application-controlled PWA suite can be run serially with:

```bash
PW_BROWSER_CHANNEL=chrome pnpm exec playwright test \
  -c apps/oshun/web/playwright.config.ts \
  apps/oshun/web/e2e/pwa-smoke.spec.ts \
  apps/oshun/web/e2e/pwa-install-update-offline.spec.ts \
  apps/oshun/web/e2e/pwa-lifecycle-deepening.spec.ts \
  apps/oshun/web/e2e/pwa-failure-modes.spec.ts \
  --workers=1
```

Additional focused offline-write and push suites own their respective deep
branches. A green run proves the browser/application PWA contract; it must not
be summarized as a completed physical-device install or universal offline and
push certification.

## Source trail

- [Source-reconciled journey](../journeys/install-as-pwa.md)
- [PWA smoke suite](../../apps/oshun/web/e2e/pwa-smoke.spec.ts)
- [Install/update/offline suite](../../apps/oshun/web/e2e/pwa-install-update-offline.spec.ts)
- [Lifecycle deepening suite](../../apps/oshun/web/e2e/pwa-lifecycle-deepening.spec.ts)
- [Failure-mode suite](../../apps/oshun/web/e2e/pwa-failure-modes.spec.ts)
- [Web manifest](../../apps/oshun/web/public/manifest.json)
- [Service worker](../../apps/oshun/web/public/sw.js)
- [PWA browser support helpers](../../apps/oshun/web/e2e/support/pwa.ts)

## Cross-references

- [First-time visitor result](./first-time-anonymous-visitor.md)
- [Offline first-use journey](../journeys/offline-first-time-use.md)
- [PWA update-flow journey](../journeys/pwa-update-flow.md)
- [PWA behavior view](../shell/03-pwa-behavior.md)
- [Authentication/session view](../shell/04-auth-session.md)
- [Notifications view](../shell/05-notifications.md)

## Open questions

- What recurring physical-device matrix records Chrome/Edge/Android install,
  Safari Add to Home Screen, launcher relaunch, safe areas, orientation, update,
  and uninstall behavior?
- Which routes and mutation types must join the offline contract, and how will
  each expose freshness, conflict, retry, and durable replay receipts?
- How will deterministic quota/eviction testing complement cross-browser manual
  storage-pressure sessions?
- Which production push gate covers permission UX, subscription rotation,
  APNs/FCM delivery, OS presentation, click routing, and revocation?
- Where should service-worker/update telemetry and versioned cache inventories
  be retained so real-world failures can be compared with the deterministic
  harness?
