Claude Cursor GitHub Copilot Skill

pwa-offline-readiness-review

Validates installability against W3C manifest criteria and tests real offline navigation behavior end to end, rejecting a manifest-schema-valid but practically non-installable or non-functional-offline PWA.

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download vincentchuwaichow-vanguard-frontier-agentic-skills_frontend_pwa-offline-readiness-review-febe32a.zip · 12 KB
Part of vincentchuwaichow/vanguard-frontier-agentic — 293 skills

Install

skills CLI npx skills add https://github.com/VincentChuWaiChow/vanguard-frontier-agentic/tree/master/skills/frontend/pwa-offline-readiness-review
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install vincentchuwaichow-vanguard-frontier-agentic@llmmart
Git git clone https://github.com/VincentChuWaiChow/vanguard-frontier-agentic.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole vincentchuwaichow/vanguard-frontier-agentic collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

PWA Offline Readiness Review

Purpose

A manifest.json that passes JSON-schema validation, and a Lighthouse PWA badge that reads 100, both prove far less than they appear to. The W3C manifest spec, beforeinstallprompt behavior, and offline-fallback rendering are three separate systems that must each work — a pass on one says nothing about the other two. The common failure mode is "checklist theater": a team ships a schema-valid manifest and a registered service worker, calls the app a PWA, and only discovers in production that the install banner never fires (display: browser left at a framework default) or that a network drop shows the browser's stock offline error page instead of a designed fallback. This skill performs the end-to-end verification — install criteria against the live origin, service-worker activation state, and an actual offline-throttle navigation test — that a static manifest read cannot substitute for.

When to use

Use this skill when the user asks to:

  • audit whether an app is a "real" installable PWA, not just manifest-schema-valid,
  • debug why the install prompt / beforeinstallprompt never fires despite a Lighthouse PWA pass,
  • review offline-fallback behavior, or debug a network drop showing the browser's default error page instead of a designed offline page,
  • validate manifest fields (name, icons, start_url, scope, display) against W3C installability criteria before a release.

When not to use

  • Caching-strategy correctness for an already-installable app (route classification, CacheFirst vs. NetworkFirst, authenticated-response caching risk) — hand off to service-worker-cache-strategy-review; that skill owns caching-strategy depth and this one does not duplicate it.
  • General HTTP Cache-Control/CDN caching review with no manifest or service-worker installability question involved.

Context7 Documentation Protocol

Manifest-injection defaults, precache-manifest generation, and offline-fallback wiring differ by framework PWA plugin and by version — never assert a gap is "the app's fault" without checking the plugin's current documented default first.

  1. Call ToolSearch with query "context7" (or "select:mcp__Context7__resolve-library-id,mcp__Context7__query-docs") to load the Context7 tools if not already loaded in this session.
  2. Always ground manifest-field and installability-criteria claims against the W3C manifest spec directly (official_docs), not a vendor summary of it — vendor blog posts routinely lag or simplify the spec's display/purpose/icon-size language.
  3. If the project uses vite-plugin-pwa, resolve /websites/vite-pwa-org_netlify_app and query its manifest-generation, generateSW/injectManifest, and precache globPatterns/maximumFileSizeToCacheInBytes behavior before asserting an asset or route is missing from precache "because the app didn't configure it" — the plugin has documented defaults and size ceilings that silently exclude assets.
  4. If the project uses next-pwa (@ducanh2912/next-pwa), resolve /ducanhgh/next-pwa and query its fallbacks config (document/data/image/audio/video/font) and default-offline-page conventions (pages/_offline.tsx / app/~offline/page.tsx) before concluding a fallback route is absent — the plugin auto-wires a default path if the file exists at the conventional location.
  5. If the underlying precaching/fallback mechanism is hand-rolled Workbox (not a framework plugin), resolve /googlechrome/workbox and query offlineFallback/setCatchHandler/precacheAndRoute semantics — confirm whether the fallback handler checks the precache before an offline-fallbacks cache, since a bug in that check order is a common cause of the fallback silently failing.
  6. Query for the exact behavior in question per review, not from memory of a prior session — plugin defaults and Workbox recipe internals change across major versions.
  7. If Context7 is unavailable or has no relevant match, fall back to official_docs / the bundled references, and mark the claim documentation-based (Context7 unavailable) rather than presenting it as freshly verified.
  8. Never invent a manifest field, plugin config key, or Workbox API that no queried source confirms.

Lean operating rules

  • Verify HTTPS on the actual target origin first — the W3C-implementing browsers refuse installability outright without it (localhost is the only common documented exception); if HTTPS is not confirmed, stop there, because every downstream installability check is moot until it is fixed.
  • Treat display: browser as a deliberate product choice to surface and confirm with the user, not a silent bug to "fix" — it is valid per spec, but it disqualifies the install-prompt flow by design, and teams frequently leave it at a framework default without realizing the consequence.
  • Do not declare an app "installable" from manifest JSON validity alone — confirm HTTPS, a service worker that reaches the activate state, and a qualifying display value together; each is independently necessary and none is sufficient alone.
  • Do not declare an app "offline-ready" from precache-manifest file lists or service-worker registration code alone — perform an actual DevTools offline-throttle navigation to a previously unvisited route and observe what renders; precaching and route-matching are separate concerns, and a precached fallback page with an unmatched route still fails offline.
  • Confirm the offline-fallback page is itself guaranteed precached, including its own dependencies (fonts, inline API calls, images) — a fallback page that depends on an uncached asset fails to render exactly when it is needed.
  • Treat the Lighthouse PWA category score as a secondary, corroborating signal only — it can pass on stale audit state and does not reproduce the live beforeinstallprompt browser signal; require the manual browser-session listener test as the primary proof of installability.
  • Flag any start_url, icon, or fallback-asset reference pointing to an HTTP origin or an unpinned third-party CDN — a mixed-content or unverifiable icon source undermines both installability and the fallback page's guarantee of availability.
  • Never recommend caching user-specific or authenticated content inside the offline-fallback route — it must render identically and safely for any unauthenticated network-offline visitor; if the app's fallback design leaks account state, that is a blocker, not a caching-strategy nuance for the sibling skill to solve.
  • Label every claim as live evidence, spec-cited, documentation-based, or inference so the reviewer knows what was actually tested versus reasoned about from file contents.

References

Load these only when needed:

  • W3C manifest installability checklist — use when validating manifest field-by-field against W3C installability criteria (name/short_name, icons, start_url/scope, display) and when diagnosing a missing install prompt.
  • Offline fallback precache patterns — use when reviewing how the offline-fallback route is registered and precached, across hand-rolled Workbox, vite-plugin-pwa, and next-pwa conventions.
  • Live installability and offline verification protocol — use for the step-by-step manual test procedure (HTTPS check, service-worker activation, beforeinstallprompt listener, DevTools offline-throttle navigation) and for interpreting Lighthouse-vs-live divergence.

Response minimum

Return, at minimum:

  • pass/fail verdict per W3C installability field (HTTPS, name/short_name, icons, start_url/scope, display), each labeled with its evidence level,
  • service-worker registration and activation status (not just "registered" — confirm activate was reached),
  • the actual (not inferred) offline-navigation test result for at least one previously unvisited route, and the offline-fallback page's precache/dependency-precache status,
  • explicit note on whether display disqualifies install prompts by design and whether that was confirmed as intentional with the user,
  • a handoff note to service-worker-cache-strategy-review if the gap identified is a caching-strategy implementation detail rather than an installability/offline-navigation gap.
Files (vanguard-frontier-agentic)
  • references
    • live-verification-protocol.md 6.2 KB
      # Live Installability and Offline Verification Protocol
      
      Use this reference for the step-by-step manual test procedure that turns a static-file read into an evidence-backed verdict, and for interpreting divergence between the Lighthouse PWA category score and live browser behavior.
      
      ## What people get wrong
      
      The naive story is:
      
      > "Lighthouse gave the PWA category a 100, so it's installable and offline-ready."
      
      Wrong, or at least insufficient. Lighthouse's PWA audit historically diverges from live installability criteria for several documented reasons: it can run against a cached or stale service-worker state, it evaluates a fixed audit checklist that has changed across Lighthouse versions (some historical PWA-specific badge criteria were deprecated/removed from newer Lighthouse releases entirely), and — critically — it does not reproduce the actual `beforeinstallprompt` event firing in a live user session, which is the real signal a browser uses to decide whether to show an install affordance. A high Lighthouse score is a corroborating signal, not proof.
      
      ## Step-by-step protocol
      
      ### 1. Confirm HTTPS on the actual deployed origin
      
      Check the origin Lighthouse (or any static review) was run against versus the actual production/target origin. It is a common gap for a review to validate a staging or preview URL that differs in TLS configuration from the real deployment target. Record this as `live evidence` only if checked against the real target origin; otherwise label `documentation-based` / `inference`.
      
      ### 2. Confirm service-worker registration reaches `activate`
      
      In a real browser session (DevTools > Application > Service Workers), confirm:
      - a service worker is listed for the correct scope,
      - its status is `activated and is running` (not merely `installed` or stuck in `waiting`),
      - there are no registration errors in the console (common causes of registration failure: scope mismatch between the registering script's location and the intended `scope`, a JavaScript error thrown during script evaluation, or an incorrect MIME type served for the service-worker script causing the browser to refuse it).
      
      A service worker that never reaches `activate` cannot serve precached content or a fallback route, regardless of what the source code says it should do.
      
      ### 3. Listen for `beforeinstallprompt` directly
      
      In a real browser session on the target origin, add a listener before any other interaction:
      
      ```js
      window.addEventListener('beforeinstallprompt', (e) => {
        console.log('beforeinstallprompt fired', e);
      });
      ```
      
      If this event never fires despite all manifest and service-worker criteria appearing correct on paper, treat that as the authoritative negative signal and re-check (in order): `display` value, HTTPS on the exact origin under test, service-worker `activate` state, and whether the browser under test has already recorded a prior install/dismissal for this origin (some browsers suppress repeat prompts for a cooldown period after a user dismisses one — check for a documented per-browser dismissal-cooldown behavior before concluding the app itself is broken).
      
      ### 4. Offline-throttle navigation test (the non-negotiable step)
      
      This is the step most reviews skip, and skipping it is the single most common cause of a false "offline-ready" verdict.
      
      1. Load the app normally first (to allow the service worker to install/activate and precache assets).
      2. In DevTools > Network, set throttling to **Offline** (not just slow 3G — must be a hard offline state to exercise the fallback path, not a slow-network path).
      3. Navigate to a route the user has **not** previously visited in this session (revisiting an already-cached page proves only that specific page's cache entry works, not the fallback mechanism).
      4. Observe what renders:
         - **The browser's default offline error page (e.g., the dinosaur game / "No internet" interstitial)** — the fallback route/catch-handler is missing, misconfigured, or the fallback document itself was not precached. This is a hard fail; report per `references/offline-fallback-precache-pattern.md`.
         - **The app's designed offline page** — record as `live evidence` pass. Additionally confirm the page rendered with its full intended styling/images (a fallback page that renders as unstyled HTML because its CSS was not precached is a partial fail, not a full pass).
      5. While still offline, attempt navigation to a second previously-unvisited route to confirm the fallback is not a one-off fluke tied to a specific route's cache warm state.
      
      ### 5. Confirm the fallback page's own dependencies
      
      With DevTools still offline, inspect the rendered fallback page's network panel for any failed sub-resource requests (fonts, inline API calls, tracking scripts). A fallback page that fails to render its intended appearance because of an uncached font or a blocking synchronous API call is a partial failure — report it distinctly from a fully missing fallback.
      
      ## Interpreting Lighthouse-vs-live divergence
      
      | Lighthouse PWA signal | Live signal | Verdict |
      |---|---|---|
      | High score | `beforeinstallprompt` fires, offline test passes | Corroborated pass — cite both. |
      | High score | `beforeinstallprompt` never fires | Live signal wins. Report the divergence explicitly and investigate `display`, origin mismatch, or per-browser dismissal-cooldown before concluding a manifest defect. |
      | High score | Offline navigation shows default browser error page | Live signal wins. This is the checklist-theater failure mode this skill exists to catch — report as a blocker regardless of the Lighthouse score. |
      | Low/missing score | Live signals pass | Investigate why Lighthouse diverges (stale audit run, outdated Lighthouse version, audit run against wrong origin) before treating the live pass as unreliable — a passing live test is still the stronger evidence. |
      
      ## Verdict discipline
      
      Never present a Lighthouse score alone as proof of either installability or offline readiness in the final response. Every "offline-ready" or "installable" verdict in the response must cite the specific live test performed (service-worker activation check, `beforeinstallprompt` listener result, offline-throttle navigation outcome) as the primary evidence, with Lighthouse noted only as a secondary, corroborating data point.
      
    • manifest-installability-checklist.md 4.7 KB
      # W3C Manifest Installability Checklist
      
      Use this reference when validating a `manifest.json` field-by-field against W3C installability criteria, or when diagnosing why an install prompt never appears.
      
      ## What people get wrong
      
      The common bad assumption is:
      
      > "My `manifest.json` validates against the schema, so my app is installable."
      
      That is incomplete. Schema validity (correct JSON shape, valid enum values) and installability (the specific subset of fields and runtime conditions a browser actually requires before it will fire `beforeinstallprompt`) are different bars. A manifest can be perfectly schema-valid and still never trigger an install prompt, because installability additionally depends on:
      
      1. **transport** — the manifest and its origin must be served over HTTPS,
      2. **service-worker presence** — most implementing browsers additionally require an active, fetch-handling service worker before treating the app as installable, not the manifest alone,
      3. **specific field values**, not just field presence — a `display` value of `browser` is schema-valid but disqualifies the install flow by design.
      
      ## Field-by-field checklist (W3C manifest spec)
      
      Work through each field and record a pass/fail with evidence label (`spec-cited`, `live evidence`, `documentation-based`, or `inference`):
      
      - **`name` / `short_name`** — at least one must be present and non-empty; `short_name` is used where display space is constrained (home-screen label). Missing both is an outright installability blocker.
      - **`icons`** — at least one icon meeting the browser's minimum size requirement (commonly a 192×192 and/or 512×512 PNG/SVG/WebP entry is expected by major implementations, though the spec itself does not hardcode a single universal minimum — cite the specific browser's documented threshold, do not assume one number applies everywhere). Check `purpose` values (`any`, `maskable`, `monochrome`) are correctly set — a maskable-only icon set with no `any` fallback can render badly on platforms that do not support maskable icons.
      - **`start_url`** — must resolve to a same-origin (or explicitly permitted scope-relative) URL. Verify it is not a URL that 404s or redirects to a different origin.
      - **`scope`** — must contain `start_url`. If `scope` is narrower than the app's actual navigable routes, navigations outside `scope` will open in a regular browser tab/window even from an installed app, breaking the installed-app experience.
      - **`display`** — must be one of `standalone`, `fullscreen`, or `minimal-ui` for the app to be eligible for the install-prompt flow at all. `display: browser` is spec-valid but is explicitly the "opt out of app-like display" value; if found, treat it as a deliberate choice to confirm with the user, not a bug to silently patch.
      - **`theme_color` / `background_color`** — not installability-blocking per spec, but their absence produces a visibly broken splash-screen/status-bar experience during install and first launch; flag as a quality issue, not a hard blocker.
      - **`id`** (where supported) — used to distinguish app identity across updates to `start_url`; note if absent on a manifest that has changed `start_url` historically, since that can cause duplicate installs.
      
      ## HTTPS is not optional
      
      The W3C manifest spec and every major implementing browser refuse to treat an app as installable over plain HTTP (the common documented exception is `localhost` for local development). This is a transport-layer precondition, not a manifest field — check it first, separately from the field-by-field pass, because no amount of manifest correctness compensates for a missing HTTPS origin.
      
      ## Service worker as a co-requirement
      
      A manifest satisfying every field above does not by itself make most browsers fire `beforeinstallprompt`. The commonly documented additional requirement is a registered service worker that reaches the `activate` lifecycle state and handles at least the `fetch` event for the page. Confirm activation state directly (see `references/live-verification-protocol.md`) rather than assuming registration code in the source implies a running, activated worker in the deployed environment.
      
      ## Common false negatives to rule out before blaming the manifest
      
      - The manifest `<link rel="manifest">` tag is present in HTML but points to a 404 or a path blocked by CSP/robots rules.
      - The manifest is served with an incorrect `Content-Type` that the browser refuses to parse as a manifest.
      - A framework PWA plugin (see `references/offline-fallback-precache-pattern.md`) generated a manifest with `display: browser` as its own default, unrelated to any explicit app configuration — verify the plugin's current documented default via Context7 before concluding the app team made this choice deliberately.
      
    • offline-fallback-precache-pattern.md 6.9 KB
      # Offline Fallback Precache Patterns
      
      Use this reference when reviewing how an app's offline-fallback route is registered and guaranteed-precached, across hand-rolled Workbox, `vite-plugin-pwa`, and `next-pwa` conventions. Query Context7 for the specific plugin/version in scope before asserting a default (see the Context7 Documentation Protocol in `SKILL.md`) — the exact config keys and default file paths below are current as queried, but plugin defaults change across major versions.
      
      ## What people get wrong
      
      The naive story is:
      
      > "I have a service worker and it precaches my app shell, so offline works."
      
      Wrong. Precaching an asset and having a route match a failed navigation to a fallback are two independent mechanisms. An app can precache a hundred files and still show the browser's default offline error page on every dropped-network navigation, because nothing registered a catch handler that serves a specific fallback document for failed `document`-destination requests. Conversely, an app can have a beautifully designed `/offline.html` page that is never precached, so the one time it is needed — when the network is down — the browser cannot fetch it either.
      
      Both halves — a precached fallback document, and a route/catch-handler that serves it on navigation failure — must be verified independently.
      
      ## Officially grounded shape (per queried sources)
      
      ### Hand-rolled Workbox (`workbox-recipes` `offlineFallback` / `workbox-routing` `setCatchHandler`)
      
      The documented pattern (`workbox-recipes/src/offlineFallback.ts`) sets a global catch handler keyed on `request.destination`:
      
      - for `document` (navigation) requests, it looks up the fallback page first via `matchPrecache(pageFallback)`, then falls back to a secondary `workbox-offline-fallbacks` cache,
      - optionally does the same for `image` and `font` destinations,
      - returns `Response.error()` if neither matches — meaning if the fallback page itself was never precached and never separately cached, the catch handler produces a network error rather than a rendered page.
      
      Review checklist for a hand-rolled implementation:
      - Confirm the fallback page path passed to `offlineFallback()` (or an equivalent hand-written `setCatchHandler`) is one of the entries in the `precacheAndRoute()` manifest, not just a same-looking path.
      - Confirm `precacheAndRoute()` was actually called (not just `precache()` alone) — `precache()` without `addRoute()`/`precacheAndRoute()` populates the cache but does not register the intercepting route, meaning normal navigations to precached pages would fall through to network first (relevant to caching-strategy review, but also affects whether the fallback lookup via `matchPrecache` finds a served entry).
      - Confirm the catch handler is registered globally (`setCatchHandler`), not only on a subset of routes, or navigations outside those routes will surface the raw browser error page.
      
      ### `vite-plugin-pwa` (Vite / `generateSW` or `injectManifest`)
      
      `vite-plugin-pwa` does not auto-designate an offline-fallback page by default — precache coverage is controlled by `workbox.globPatterns` (which files get swept into the precache manifest) and `workbox.maximumFileSizeToCacheInBytes` (default documented ceiling around 2 MiB; assets above it are silently excluded from precache even if `globPatterns` would otherwise match them). Review checklist:
      - Confirm the offline-fallback HTML file's glob pattern is actually included in `globPatterns` (a common gap: `globPatterns: ['**/*.{js,css,html}']` looks like it covers an `offline.html`, but if the file lives outside the configured `public`/build output directory scanned by the plugin, it will not appear in the manifest).
      - Confirm the fallback file is under the size ceiling, or that `maximumFileSizeToCacheInBytes` was explicitly raised for it.
      - Because `vite-plugin-pwa` does not wire the catch-handler/fallback-routing logic for you by default under `generateSW`, confirm the project either uses `injectManifest` with a custom service-worker source implementing `offlineFallback`/`setCatchHandler`, or has added equivalent `runtimeCaching` navigation-fallback configuration. Do not assume "precached" implies "served as a navigation fallback" — verify the routing half separately, per the "what people get wrong" framing above.
      
      ### `next-pwa` (`@ducanh2912/next-pwa`)
      
      This plugin's documented `fallbacks` option explicitly separates fallback routes by destination (`document`, `data`, `image`, `audio`, `video`, `font`), each pointing to a precached path, e.g.:
      
      ```js
      fallbacks: {
        document: "/~offline",
        data: "/fallback.json",
        image: "/fallback.webp",
      }
      ```
      
      Its internal fallback handler (`self.fallback`) branches on `request.destination` and matches against those precached paths via `caches.match(fallbackResponse, { ignoreSearch: true })`, returning `Response.error()` if no destination-specific fallback is configured. Review checklist:
      - If no explicit `fallbacks.document` is configured, confirm whether the project instead relies on the plugin's documented convention path (`pages/_offline.tsx` or `app/~offline/page.tsx`) — the plugin auto-wires a default at that conventional location if the file exists; absence of explicit config is not automatically a gap if the convention file is present.
      - If neither explicit `fallbacks.document` nor the convention file exists, the app has no navigation fallback at all — this is the direct cause of a raw browser offline error page and should be reported as a blocker, not a style nit.
      - Confirm the fallback path itself is excluded from any auth-gating middleware — a fallback route that requires authentication to render defeats its own purpose during an offline navigation.
      
      ## Minimal safe verification flow
      
      1. Identify which of the three patterns above (hand-rolled Workbox, `vite-plugin-pwa`, `next-pwa`) the project uses; do not assume, check the dependency and config file.
      2. Confirm the specific fallback document path is present in the generated/inspectable precache manifest (build output, or DevTools Application > Cache Storage after first load).
      3. Confirm a catch-handler/fallback route actually exists and targets that exact path — not merely that the path is precached.
      4. Run the live offline-throttle navigation test from `references/live-verification-protocol.md` to confirm the fallback actually renders end to end; steps 1–3 alone are necessary but not sufficient proof.
      
      ## High-risk assumptions to kill
      
      - "It's in the precache manifest, so offline works." — precache and fallback-routing are separate mechanisms; both must be verified.
      - "The plugin handles this by default." — only true for the specific documented convention path/config key for that plugin and version; verify via Context7, do not assume parity across `vite-plugin-pwa` and `next-pwa` defaults.
      - "The fallback page has no external dependencies, so it's safe offline." — verify this; a fallback page pulling a web font from a CDN, or making an inline API call for personalization, fails exactly when offline.
      
  • metadata.json 1.4 KB
    {
      "id": "pwa-offline-readiness-review",
      "name": "PWA Offline Readiness Review",
      "type": "skill",
      "provider": "frontend",
      "harnesses": [
        "claude-code",
        "cursor",
        "codex",
        "gemini",
        "kiro",
        "other"
      ],
      "summary": "Validates installability against W3C manifest criteria and tests real offline navigation behavior end to end, rejecting a manifest-schema-valid but practically non-installable or non-functional-offline PWA.",
      "source_type": "original",
      "official_docs": [
        "https://w3c.github.io/manifest/",
        "https://web.dev/articles/installable-manifest",
        "https://web.dev/articles/offline-fallback-page",
        "https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Manifest",
        "https://developer.mozilla.org/en-US/docs/Web/API/BeforeInstallPromptEvent",
        "https://web.dev/articles/lighthouse-pwa"
      ],
      "security_notes": "Manifest and service-worker files must be served over HTTPS (a hard W3C/browser installability requirement, not a preference); flag any start_url or icon reference pointing to an HTTP origin or third-party CDN without integrity guarantees. Do not recommend caching the offline-fallback page's embedded data if it contains anything user-specific/authenticated -- the fallback route must be static/public content only.",
      "last_verified": "2026-07-02",
      "path": "skills/frontend/pwa-offline-readiness-review",
      "author": "github: VincentChuWaiChow",
      "version": "0.1.0"
    }
    
  • SKILL.md 8.6 KB
    ---
    name: pwa-offline-readiness-review
    description: Validates installability against W3C manifest criteria and tests real offline navigation behavior end to end, rejecting a manifest-schema-valid but practically non-installable or non-functional-offline PWA.
    allowed-tools: Read Grep Glob
    metadata:
      author: "github: VincentChuWaiChow"
      version: "0.1.0"
      updated: "2026-07-02"
      category: operational
    ---
    
    # PWA Offline Readiness Review
    
    ## Purpose
    
    A `manifest.json` that passes JSON-schema validation, and a Lighthouse PWA badge that reads 100, both prove far less than they appear to. The W3C manifest spec, `beforeinstallprompt` behavior, and offline-fallback rendering are three separate systems that must each work — a pass on one says nothing about the other two. The common failure mode is "checklist theater": a team ships a schema-valid manifest and a registered service worker, calls the app a PWA, and only discovers in production that the install banner never fires (`display: browser` left at a framework default) or that a network drop shows the browser's stock offline error page instead of a designed fallback. This skill performs the end-to-end verification — install criteria against the live origin, service-worker activation state, and an actual offline-throttle navigation test — that a static manifest read cannot substitute for.
    
    ## When to use
    
    Use this skill when the user asks to:
    
    - audit whether an app is a "real" installable PWA, not just manifest-schema-valid,
    - debug why the install prompt / `beforeinstallprompt` never fires despite a Lighthouse PWA pass,
    - review offline-fallback behavior, or debug a network drop showing the browser's default error page instead of a designed offline page,
    - validate manifest fields (`name`, icons, `start_url`, `scope`, `display`) against W3C installability criteria before a release.
    
    ## When not to use
    
    - Caching-strategy correctness for an already-installable app (route classification, `CacheFirst` vs. `NetworkFirst`, authenticated-response caching risk) — hand off to `service-worker-cache-strategy-review`; that skill owns caching-strategy depth and this one does not duplicate it.
    - General HTTP `Cache-Control`/CDN caching review with no manifest or service-worker installability question involved.
    
    ## Context7 Documentation Protocol
    
    Manifest-injection defaults, precache-manifest generation, and offline-fallback wiring differ by framework PWA plugin and by version — never assert a gap is "the app's fault" without checking the plugin's current documented default first.
    
    1. Call `ToolSearch` with query `"context7"` (or `"select:mcp__Context7__resolve-library-id,mcp__Context7__query-docs"`) to load the Context7 tools if not already loaded in this session.
    2. Always ground manifest-field and installability-criteria claims against the W3C manifest spec directly (`official_docs`), not a vendor summary of it — vendor blog posts routinely lag or simplify the spec's `display`/`purpose`/icon-size language.
    3. If the project uses `vite-plugin-pwa`, resolve `/websites/vite-pwa-org_netlify_app` and query its manifest-generation, `generateSW`/`injectManifest`, and precache `globPatterns`/`maximumFileSizeToCacheInBytes` behavior before asserting an asset or route is missing from precache "because the app didn't configure it" — the plugin has documented defaults and size ceilings that silently exclude assets.
    4. If the project uses `next-pwa` (`@ducanh2912/next-pwa`), resolve `/ducanhgh/next-pwa` and query its `fallbacks` config (`document`/`data`/`image`/`audio`/`video`/`font`) and default-offline-page conventions (`pages/_offline.tsx` / `app/~offline/page.tsx`) before concluding a fallback route is absent — the plugin auto-wires a default path if the file exists at the conventional location.
    5. If the underlying precaching/fallback mechanism is hand-rolled Workbox (not a framework plugin), resolve `/googlechrome/workbox` and query `offlineFallback`/`setCatchHandler`/`precacheAndRoute` semantics — confirm whether the fallback handler checks the precache before an `offline-fallbacks` cache, since a bug in that check order is a common cause of the fallback silently failing.
    6. Query for the exact behavior in question per review, not from memory of a prior session — plugin defaults and Workbox recipe internals change across major versions.
    7. If Context7 is unavailable or has no relevant match, fall back to `official_docs` / the bundled references, and mark the claim `documentation-based (Context7 unavailable)` rather than presenting it as freshly verified.
    8. Never invent a manifest field, plugin config key, or Workbox API that no queried source confirms.
    
    ## Lean operating rules
    
    - Verify HTTPS on the actual target origin first — the W3C-implementing browsers refuse installability outright without it (localhost is the only common documented exception); if HTTPS is not confirmed, stop there, because every downstream installability check is moot until it is fixed.
    - Treat `display: browser` as a deliberate product choice to surface and confirm with the user, not a silent bug to "fix" — it is valid per spec, but it disqualifies the install-prompt flow by design, and teams frequently leave it at a framework default without realizing the consequence.
    - Do not declare an app "installable" from manifest JSON validity alone — confirm HTTPS, a service worker that reaches the `activate` state, and a qualifying `display` value together; each is independently necessary and none is sufficient alone.
    - Do not declare an app "offline-ready" from precache-manifest file lists or service-worker registration code alone — perform an actual DevTools offline-throttle navigation to a previously unvisited route and observe what renders; precaching and route-matching are separate concerns, and a precached fallback page with an unmatched route still fails offline.
    - Confirm the offline-fallback page is itself guaranteed precached, including its own dependencies (fonts, inline API calls, images) — a fallback page that depends on an uncached asset fails to render exactly when it is needed.
    - Treat the Lighthouse PWA category score as a secondary, corroborating signal only — it can pass on stale audit state and does not reproduce the live `beforeinstallprompt` browser signal; require the manual browser-session listener test as the primary proof of installability.
    - Flag any `start_url`, icon, or fallback-asset reference pointing to an HTTP origin or an unpinned third-party CDN — a mixed-content or unverifiable icon source undermines both installability and the fallback page's guarantee of availability.
    - Never recommend caching user-specific or authenticated content inside the offline-fallback route — it must render identically and safely for any unauthenticated network-offline visitor; if the app's fallback design leaks account state, that is a blocker, not a caching-strategy nuance for the sibling skill to solve.
    - Label every claim as `live evidence`, `spec-cited`, `documentation-based`, or `inference` so the reviewer knows what was actually tested versus reasoned about from file contents.
    
    ## References
    
    Load these only when needed:
    
    - [W3C manifest installability checklist](references/manifest-installability-checklist.md) — use when validating manifest field-by-field against W3C installability criteria (`name`/`short_name`, icons, `start_url`/`scope`, `display`) and when diagnosing a missing install prompt.
    - [Offline fallback precache patterns](references/offline-fallback-precache-pattern.md) — use when reviewing how the offline-fallback route is registered and precached, across hand-rolled Workbox, `vite-plugin-pwa`, and `next-pwa` conventions.
    - [Live installability and offline verification protocol](references/live-verification-protocol.md) — use for the step-by-step manual test procedure (HTTPS check, service-worker activation, `beforeinstallprompt` listener, DevTools offline-throttle navigation) and for interpreting Lighthouse-vs-live divergence.
    
    ## Response minimum
    
    Return, at minimum:
    
    - pass/fail verdict per W3C installability field (HTTPS, `name`/`short_name`, icons, `start_url`/`scope`, `display`), each labeled with its evidence level,
    - service-worker registration and activation status (not just "registered" — confirm `activate` was reached),
    - the actual (not inferred) offline-navigation test result for at least one previously unvisited route, and the offline-fallback page's precache/dependency-precache status,
    - explicit note on whether `display` disqualifies install prompts by design and whether that was confirmed as intentional with the user,
    - a handoff note to `service-worker-cache-strategy-review` if the gap identified is a caching-strategy implementation detail rather than an installability/offline-navigation gap.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related