Summary
When you arrive somewhere new (you travelled at least 2 km, then stayed in one spot for a couple of minutes — not a red light, not driving past) and a patch you don't have yet is within about 5 miles, Scout shows one banner: "Your next patch is 2 mi away", the patch's name underneath, and its art on the right (iOS). A tap opens that patch, or Near me when several are close. At most once a day, never at night, and never about the same patch twice.
It is a local notification decided on the phone, like the unlock banner: nothing is pushed from a server. Built 2026-11-04 to give people a reason to open Scout while they are out (the analytics read that day: 88% of installs finish onboarding, only 48% come back on a later day).
Status
- Built, not in a released app yet. On branch
feat/retention-alerts(PR todevelop, 2026-11-04); reaches phones with the next store build. No feature flag; a per-device switch, on by default. - Not yet tested in the real world. The arrival rule depends on how often iOS and Android deliver background fixes while you stand still; unit tests prove the rule, not the OS. Needs a real walk (see Open questions).
User-facing surfaces
- The banner (
mobile/src/services/nearbyAlerts.ts:75): title "Your next patch is under 1 mi away" or "… N mi away" (one line), body the patch name, plus ", plus N more close by" when others are close (nearbyMessage,mobile/src/domain/nearbyAlert.ts:113). On iOS the patch art is attached as the banner's thumbnail (downloaded to the cache first; if that fails, the banner goes without it). Android shows no image (Expo local notifications have no image slot there). - A tap (
handleNearbyPatchResponse,mobile/src/trips-v2/notificationActions.ts:55): the patch (/patch-modal/<id>) when it is the only one close, otherwise/near-me. - Settings → Notifications → On this device → "Nearby patches" ("When you stop somewhere with a
patch you don't have close by. At most once a day."), testID
notif-toggle-nearby(mobile/app/notification-settings.tsx:540). When it can't fire, the row says why, with a button (nearbyBlocker,nearbyAlert.ts:141):- notifications off: "Notifications are off for Scout, so this can't reach you." (Turn on notifications / Open Settings);
- no location: "Needs your location. Scout can't tell where you are." (Open Settings);
- location only while using the app: "Needs location set to Always, so Scout can notice when you arrive somewhere with the app closed." (Change to Always → the OS settings). Hidden on web.
- Design:
mobile/research/nearby-alerts-lab.html(settings A and banner B picked by Alan).
How it works
- Every background fix. The background location task (
mobile/src/services/location/tracker.ts:143) hands every fix of each batch, with its timestamp, torunNearbyPass. - The arrival detector (
arrivalStep,nearbyAlert.ts:62), its memory kept between wakes in MMKV (scout-nearby-alerts):- the anchor is the last place you settled (the first fix ever, then each arrival);
- a fix worse than 100 m (a cell-tower jump) or with no usable time is ignored;
- a fix at driving speed (over 3 m/s) ends any stay — stop-and-go traffic never settles;
- a stay starts at a fix and lasts while later fixes stay within 250 m;
- a stay of 2 minutes or more over at least 3 fixes, at least 2 km (
ARRIVAL_TRAVEL_KM) from the anchor, is settling somewhere: the new anchor. (A stopped car gives one fix at the stop and the next as it pulls away, so even a long red light never reaches 3); - it is an arrival only if it is new: not within 2 km of one of the last 20 places you settled, so home and work stop counting after the first time.
- The guards (
runNearbyPass): the switch is on; the app is not open (AppStateactive — Near me already shows it); no Just drive trip is live (its own "patch ahead" alert covers the road,services/nearbyAlerts.ts:94); not 21:00–08:00 local and not already today (mayAlertAt,nearbyAlert.ts:125); the OS already allows notifications (osAllowsLocalNotifications,mobile/src/services/localNotify.ts— it never prompts); the account's patches have loaded (a cold background wake before they have would not know which you hold, so it holds). - The pick (
pickNearby,nearbyAlert.ts:98): from the tracker's uncollected list, minus any patch you hold (the list keeps a live trip's stops even when collected, so they are dropped again,nearbyAlerts.ts:98), minus cities, states, regions and airports (isSuggestable: you are inside a city when you arrive, and it unlocks), minus every patch already alerted about: the closest within 5 miles straight line (NEARBY_RADIUS_MI). - The banner (
trigger: null, data{ type: 'nearby_patch', patchId, others }), then the day and the patch are remembered (the newest 500 patches) andnearby_alert_sentis logged (distance to 0.1 mi, the count close by, whether the art attached — never where).
Data model
None on the server. On the phone: MMKV scout-nearby-alerts (the detector's anchor, current stay,
recent places, the last alert's day, the alerted patch ids) and the store switch nearbyAlertsEnabled
(persisted, default true, mobile/src/domain/store.ts:1639).
The MMKV memory belongs to whoever is signed in: signing out, or signing in to a different existing
account, clears it (resetNearbyState, mobile/src/providers/AuthProvider.tsx), so the next
person on the phone neither inherits the day's alert nor the list of patches already named.
API surface
None.
Key files
mobile/src/domain/nearbyAlert.ts— the rules (pure): arrival, pick, message, once-a-day and quiet hours, the Settings blocker.mobile/src/services/nearbyAlerts.ts— the background pass: memory, guards, art, notification.mobile/src/services/location/tracker.ts— the background task feeds it.mobile/src/services/localNotify.ts— the shared "may we notify" check (also trip reminders).mobile/src/trips-v2/notificationActions.ts,useTripNotificationActions.ts— the tap.mobile/app/notification-settings.tsx— the switch and its warning.
Configuration and flags
No flag. The device switch nearbyAlertsEnabled (default on). The constants (5 mi, 2 km, 250 m,
2 min, once a day, 21:00–08:00) are in nearbyAlert.ts.
Edge cases and known limits
- It needs "Always" location (the background task does not run otherwise). About half of reporting devices had it on 2026-11-04 (101 of 194); for the rest the row says so.
- Sitting still in a parked car may not count: the background task gets a new fix only after ~25 m of movement, so an arrival is noticed when you walk around a place, not while you sit. Unverified on a device (Open questions).
- The very first place after install is "known" (it is where tracking started), and the first place you settle after travelling can be home on day one — once; it is known from then on.
- In a city there is nearly always something within 5 miles (15–20 uncollected patches downtown, per the lab's real-catalog check): the once-a-day cap is what keeps it quiet.
- The art download happens in the background wake, capped at 8 s. Only PNG, JPEG or GIF art is attached (what iOS accepts); if iOS still refuses it, the banner is posted again without it. Slow, offline or another format: the banner goes without the image.
What this feature does NOT do
- It is NOT a push from a server. Scout's server never learns where you are for this; the phone decides and posts the banner itself. ("No 'we'll ping you near a patch'" in push-notifications is about server push and stays true.)
- It does NOT fire while you drive past something. Only on arriving and staying somewhere new.
- It does NOT work without "Always" location, and it never asks for it or for notifications by itself; the Settings row points to the OS settings.
- It does NOT show an image on Android.
- It does NOT alert more than once a day, at night, about a city or state, about a patch you have, or about the same patch twice.
- It does NOT run on web.
- It is NOT tested in the real world yet (see Status).
Tests that cover it
mobile/src/domain/__tests__/nearbyAlert.test.ts— arrival vs red light, drive-by, staying home, a short hop, staying on, coming back to a known place; the pick (closest, others, no city/state/ airport/alerted, nothing within 5 mi); the message; quiet hours; once a day; the 500 cap; the Settings blocker (mutation-checked).mobile/src/services/__tests__/nearbyAlerts.test.ts— the banner, art, once a day, never twice, never a collected patch, switch off, app open, Just drive, notifications off, night, a stay across two wakes, no art offline or on Android (mutation-checked).mobile/src/services/location/__tests__/tracker.test.ts(feeds the nearby pass) — every fix of a batch with its time; a failing nearby pass never stops the unlock pass.mobile/src/trips-v2/__tests__/notificationActions.test.ts(Nearby patch taps).mobile/src/providers/__tests__/AuthProvider.test.tsx— sign-out and a password sign-in to an existing account clear the nearby memory; a guest promoted to a new account keeps it (mutation-checked). The Apple/Google existing-account branch makes the same call, untested.mobile/screen-tests/notification-settings.test.tsx(Nearby patches row) — on by default, persists, the warnings (paired with none).
Open questions
- How often does iOS deliver background fixes while you walk around one place? The rule needs two fixes 2 minutes apart within 250 m. A real walk is needed before release — Alan records: install the build, set location to Always, drive 3+ km to somewhere with an uncollected patch within 5 miles, walk around for 3–5 minutes with the app closed, then note whether and when the banner came.
- Whether the iOS thumbnail renders from a cached PNG on all iOS versions (expected; not seen).