Help centre Families Coaches Support

Littles Baby Tracker

Help for families and coaches

Site Tools


technical:features:33_offline_cache_and_app_updates

33 — Offline, cache and app updates

Technical reference. For task-based help, see the corresponding user guide.
Documentation status: reviewed against implementation · Reviewed: 2026-07-21 · Primary users: family and coach · Surfaces: mobile web, native wrapper and platform API

Purpose

The product has three complementary resilience layers: a read cache for recently viewed family/activity data, a family-only outbox for idempotent activity creates, and a verified native content cache for the mobile-web application itself. Update policy then guides users to either restart downloaded web content or install a newer native wrapper.

Capability boundaries

Layer What it does What it does not do
Activity response cache Shows recent summary/activity data when refresh fails offline or with a server error. It is not an offline database and does not authorize stale access.
Activity-create outbox Saves eligible self-family activity creates for later replay. It does not queue edits, deletes, account changes or arbitrary file uploads.
Native mobile-web cache Runs a downloaded, checksum-verified web release from local storage. It does not cache the platform API or bypass API compatibility.
Native update policy Classifies wrapper version as current/recommended/required and opens the correct store. It does not install an App Store/Play update itself.

Cached reads

  1. Family summaries, activity pages and complete analysis loads are persisted in versioned local storage.
  2. Scope includes API environment, actor user and authentication session, preventing one account/environment from reading another scope's cache.
  3. Fresh summary lifetime is 5 minutes; fresh activity/page lifetime is 6 hours; all stale fallback data is discarded after 24 hours.
  4. Network fetch rejection/offline or API 5xx may fall back to still-readable stale data. Authentication, permission, validation and other client errors do not use stale fallback.
  5. Successful writes update/invalidate affected cached data. Sign-out/session change clears or moves to a different scoped cache.
  6. A failed background refresh displays Showing cached data with Retry; corrupt/unavailable/quota-limited storage is ignored because cache is an optimization.

Offline activity-create outbox

1. Save locally

  1. Only a genuine family actor writing their own family context is queue-eligible.
  2. The serializable activity-create request receives a UUID clientMutationId before the first direct attempt, so an uncertain response and later replay cannot create a duplicate server record.
  3. When a retryable connectivity/timeout failure occurs, the request is stored as pending with its family/baby/activity ordering scope and any local timer IDs that must be suppressed.
  4. UI copy distinguishes Saved on this phone. Waiting to sync from Waiting to retry.

2. Replay safely

  1. Replay runs on online, focus/visibility and authenticated API-success signals plus a 15-second sweep.
  2. A cross-tab lease prevents two browser contexts from replaying the same scope simultaneously.
  3. Entries are ordered per family + baby + activity type; a failing older entry blocks later entries in that ordering scope so history is not reordered silently.
  4. Network/server failures back off and remain pending. An authentication failure pauses replay until a later authenticated API success.
  5. The server's clientMutationId contract makes create replay idempotent, including “already saved then later deleted” terminal handling.

3. Ask for attention when unsafe

  1. Permission/access change, missing family/baby, validation rejection, idempotency conflict/result-gone or an unclassified permanent failure moves an entry to needsAttention.
  2. Any entry still automatic after seven days is also paused for explicit review.
  3. Tracking and shell surfaces provide Retry, Review and Discard. Discarding a queued timer-backed activity announces suppressed timer IDs so local/native timer restoration cannot resurrect it.
  4. Signing out with saved entries warns that they will be discarded; if safe local deletion fails, sign-out is stopped for retry.

Timer reconciliation

Active timers have local/native continuity and server state. A stopped timer that becomes an outbox activity keeps its mutation identity and suppresses the corresponding local timer while pending. Fresh server summaries win when newer; discard/replay signals prevent duplicate closure or reappearance. This protects continuity but does not make timers a general offline multi-device consensus system.

Mobile-web release startup

Release selection and verification

  1. Production wrapper requests GET /mobile-web/releases/latest?channel=<configured>&wrapperVersion=<native version>.
  2. Only published records are candidates. The platform sorts newest first and selects a release whose wrapper SemVer range and the serving API's compiled contract version are compatible.
  3. Manifest supplies web release version, zip URL, SHA-256, entry file, required wrapper/ranges and release date.
  4. Wrapper downloads the zip, computes SHA-256, rejects a mismatch, extracts into DocumentDirectory/mobile-web/releases/<version>, and writes the current release pointer only after successful installation.

Startup states

State Result
Debug with dev URL Uses the mobile-web development server and reports web version dev.
Online, no cache, compatible release Downloads/verifies/installs before opening the WebView.
Offline, valid cache Starts the cached local release.
Offline, no cache Shows first-launch offline state; there is no unverified remote fallback.
Manifest/install failure with valid cache Rolls back/continues with cache and retains a warning.
Cached release + newer compatible release Serves current cache now, downloads/verifies new content in background, then advertises it as installed.
Incompatible wrapper Surfaces update-required/incompatible state; another channel is not substituted.

The local static server opens the entry file with ?webReleaseVersion=<version> for cache busting. The bridge exposes only platform, wrapper version, web release version and initial path; the wrapper does not persist Supabase tokens.

Web and native update UX

Downloaded web update

  1. Wrappers at least 4.5.0 support release.checkForUpdate and release.restartWithInstalledUpdate.
  2. On family/coach home, mobile checks in the background. Once the wrapper has installed a newer compatible zip, the home Update control restarts the local server/WebView into that release.
  3. Restart failure attempts to restore the previous release and leaves a retryable warning/update state.

Native wrapper update

  1. Mobile calls GET /native-app/updates/check?channel=production&platform=ios|android&wrapperVersion=… on home.
  2. The newest active matching policy yields current when at/above latest, updateAvailable/recommended when below latest, or updateRequired/required when below minimum.
  3. Active policies require valid SemVer, minimum ≤ latest and the platform's App Store ID/Android package name. Only super-admin manages policies.
  4. Native update takes display priority over a pending web update. If the user declines/returns from the store prompt, the already-downloaded web update can be offered next; a later policy recheck that becomes required restores native priority.
  5. Current UI uses a confirm/store-opening flow for both recommended and required responses. “Required” is policy severity and prioritization, not yet a hard in-app lock.

Validation and user-visible states

State Behaviour
Stale cached activity Warning identifies cached content and offers retry.
Outbox pending Global/activity indicator reports saved count and sync/retry state.
Outbox needs attention Reason-specific access/missing/conflict/validation/7-day copy plus retry/discard.
Storage quota/corruption Cache/outbox persistence failure is contained and surfaced where durability cannot be guaranteed.
Bad zip/hash/entry Release is not promoted; valid prior cache remains.
Web update installed Home Update restarts locally; no store visit.
Native update available/required Confirmation opens App Store/Google Play; failure tells the user to update directly.

Platform API contract

Method and route Purpose
Activity GET/create routes Cache source of truth and idempotent clientMutationId activity creation.
GET /mobile-web/releases/latest Public manifest selected for channel, wrapper and serving API compatibility.
GET/POST/PATCH /admin/mobile-web-releases… Super-admin release registry operations.
GET /native-app/updates/check Public platform/channel/wrapper policy classification.
GET/POST/PATCH /admin/native-app-update-policies… Super-admin native update policy management.

Security, integrity and operational rules

  • Cached business data is local convenience scoped to actor/auth/API and expires; current API authorization must win whenever reachable.
  • Offline replay never broadens actor scope and every replay is authorized/validated by the platform.
  • Release SHA-256 and wrapper/API ranges are mandatory integrity/compatibility gates, not advisory metadata.
  • A bad production web release should be paused in the registry so selection rolls back; do not mutate an old artifact under the same version.
  • Native wrapper and web release are separate versions. A web build needing a new native bridge must exclude old wrappers through its compatibility range.
Offline support is intentionally narrow: cached reads and self-family activity creates. Never promise that edits, deletes, attachments or all product screens work offline, and never turn a stale-cache response into permission to show data after an explicit 401/403.

Acceptance checklist

  1. [ ] Cache is actor/session/environment scoped with 5-minute summary, 6-hour activity and 24-hour maximum stale limits.
  2. [ ] Only network/5xx failures use stale fallback; auth/permission failures remain authoritative.
  3. [ ] Queued creates retain one clientMutationId, cross-tab lease and ordering guarantees.
  4. [ ] Permanent/7-day entries require attention and retry/discard handles timer suppression.
  5. [ ] Sign-out cannot silently leave another user's queued activity visible.
  6. [ ] First online install and every update verify SHA-256 before promotion.
  7. [ ] Offline/failed update uses a valid cache; no-cache first launch fails safely.
  8. [ ] Release selection enforces exact channel plus wrapper and serving-API compatibility.
  9. [ ] Web restart requires wrapper ≥4.5.0 and can restore the previous release on failure.
  10. [ ] Native policy distinguishes recommended/required accurately without documenting a nonexistent hard lock.

Source evidence

  • Mobile web: mobile-web/src/api/activity-cache.ts, mobile-web/src/offline/activity-create-outbox.ts, ActivityCreateOutboxContext.tsx, activity-create-outbox-runner.ts, mobile-web/src/routes/ActivityTrackingScreen.tsx, update handling in mobile-web/src/App.tsx
  • Native wrapper: mobile-wrapper/src/release/releaseResolver.ts, nativeReleaseDeps.ts, manifest.ts, mobile-wrapper/src/hooks/useMobileWebRelease.ts
  • Platform: platform/packages/shared/src/mobile-web-releases.ts, native-app-updates.ts, platform/apps/api/src/routes/mobile-web-release-routes.ts, native-app-update-routes.ts, release/update services