Scout — Full Product Context → feature documentation

The Plan tab (visiting info)

The Plan tab on a patch's detail screen: is the place open right now, what it costs, whether you need tickets, the best months to go, how long to allow, and what to know first, each dated and linked to the page it came from. Live for the 64 National Parks; no feature flag.

Summary

The Plan tab answers "can I actually go, and what should I know first?" for one patch. It sits right after Overview on the patch detail screen and shows:

The footnote is the point of the feature. Hours and prices go stale, so everything the tab shows was researched on a stated date from named pages, and nothing is shown that could not be verified from a fetched source: a fact the research could not confirm is left out rather than guessed.

Status

Live for the 64 National Parks. There is no feature flag: the tab appears once an app build containing it is installed and a patch's visit-info record is published. Both are true today (verified 2026-11-04):

Planned next: the City Challenge patches, city by city. A patch with no published record shows no Plan tab at all.

User-facing surfaces

How it works

Which patches show the tab. The catalog sync (GET /api/sync/content) carries visitInfoIds, the ids of patches with a published record that the public may see (backend/src/sync/sync.service.ts:222-227,293). The app keeps that list with the rest of the synced catalog (mobile/src/query/queries/content.ts:37-41,85-104) and the detail screen checks it (useHasVisitInfo, mobile/src/query/queries/visit-info.ts:30-34). The list is replaced in full on every sync, so a record that is unpublished disappears from the list on the next sync.

Loading the record. When the tab is going to show, the screen prefetches the record (PatchDetailScreenV3.tsx:90-93) from GET /api/sync/patch/:id/visit-info, so the tab is usually instant. While it loads the tab shows the house skeleton (a headline, a bar, four tiles), never a spinner (mobile/src/components/patch-detail-v3/plan/PlanBody.tsx:142-161).

The place's clock. Every "is it open" rule reads the place's time zone, never the phone's: a scout in New York at 11:30 PM opening Yellowstone sees Denver's 9:30 PM (placeNow, mobile/src/domain/visit-info.ts:185-190). Dates are handled as plain YYYY-MM-DD strings so no time zone can shift a day.

The headline (openState, mobile/src/domain/visit-info.ts:279-301), in order:

  1. A researched whole closure that is in effect now: "Closed", with "Closed until Nov 18" under it. Only this can turn the headline to Closed.
  2. The way the place opens: always open reads "Open 24 hours"; view-only places read "View any time"; a place recorded as closed reads "Closed".
  3. Otherwise today's hours: "Opens at 9 AM", "Open now · Until 5 PM · last entry 4 PM", "Closed for the day" with when it next opens, or "Closed for the season" with the date it reopens. A dated exception (a holiday) beats the usual week, and its name leads the line ("Thanksgiving · Opens tomorrow at 8 AM"; just "Thanksgiving" when there is no opening to name).

"When it next opens" skips every day a whole closure covers, so "Opens tomorrow at 9 AM" never contradicts a closure that starts tomorrow, and an out-of-season "Opens May 15" moves past a closure over the season's first days; a partial closure never moves it (nextOpen, visit-info.ts:215-231). It looks 62 days ahead on an ordinary closed day and, failing that, a whole year, the same horizon as out of season.

The hours that drive this are those of the grounds: the place the patch unlocks on. A visitor center, museum or tour inside the site is a venue with its own week in "The details" and never changes the headline.

Closures. A researched closure that is on now, or starts within 30 days, gets a one-line strip under the bar ("Closes Oct 26 – Nov 18 · In 3 days") and a callout in the details (activeClosure, visit-info.ts:234-269). When none is on now, the soonest coming up wins, and on the same start date a whole closure beats a partial one. A partial closure (a road, a pass, an entrance) is a heads-up only: the place stays "Open". Only a whole closure shows "Closed".

National Park Service alerts. For a National Park Service unit, the live alerts for its park code show as a quiet "NPS" strip under the headline (only "Park Closure" and "Danger" alerts earn the strip, npsStripAlerts, visit-info.ts:346-347) and in full, each linked, in "From the National Park Service" in the details. The alerts only inform. They never decide open or closed: the Park Service's alert feed has no start or end dates and a "Park Closure" is often a single trail. Only a researched, dated closure changes the headline. Alerts attach only where the researcher marked the record as the park unit's own (alertsFromNps: true) and the patch has a park code (backend/src/visit-info/visit-info.service.ts:41-58). Other patches that share the unit's park code, and its park-service id too (the Lincoln Memorial, Arlington National Cemetery and the USS Maine all carry linc), get none; their records leave the flag off. All 64 National Parks records carry it.

Tickets. The tile reads "Book ahead" only when reservations are needed today; outside a reservation window it reads "Walk in" with the window beneath ("Reservations needed May 22 – Oct 11") (ticketState, visit-info.ts:310-316). A callout with a booking link appears whenever reservations apply some time in the year.

Best time. Twelve months, each rated best / fine / avoid, are shown as the best runs ("Mar – May · Sep – Oct"; a run over New Year reads as one run) (bestRuns, visit-info.ts:319-340). A tile with nothing true to say is dropped; the last of an odd count spans the row (planTiles, visit-info.ts:394-405).

The footnote and freshness. "Checked Oct 23, 2026 against nps.gov, recreation.gov." Each site is a link to the page used; the first two are named and "+N" opens the rest. The Park Service's data API is a machine endpoint, not a page, so it is never named or linked: only pages a person can read are (sourceHosts, visit-info.ts:421-433). "Confirm on <first site>" links to the official page. Every link on the tab (the source sites, "Confirm on …", the booking link and the Park Service's alerts) opens in the in-app browser (expo-web-browser, as the patch screen's image search does), so you are one swipe from the patch; if the browser can't open, the URL is shown instead (openPlanLink, mobile/src/components/patch-detail-v3/plan/openLink.ts:11-15; PlanFootnote.tsx:38,66, PlanDetails.tsx:119,295). A record checked more than 183 days ago says so ("Last checked Mar 2, 2026 against nps.gov, over 6 months ago. Things may have changed.") with the date in the accent (PlanFootnote.tsx:58-63); it does not hide the details. Records are meant to be re-checked well before that (every 91 days for a place with hours or prices, every 182 for an always-open or view-only place, sooner if the current hours season ends first), so what you see can be up to roughly three to six months old.

"Something wrong? Tell us" opens the Feedback screen carrying the patch and the screen (screen: 'plan'), so the report reaches the support inbox already attached to the right place (PlanFootnote.tsx:71-79; see feedback-conversations).

Errors. If the record can't be read the tab shows the house error state, never a half-drawn tab:

Data model

One researched record per patch, in the patch_visit_info table (backend/prisma/schema.prisma:588-608). In the API it is flattened to (backend/src/visit-info/visit-info.types.ts:118-122):

Field Meaning
checkedOn, recheckAfter When it was researched, and when it is next due
access hours, always_open, seasonal, reservation_only, view_only or closed
accessNote, timezone One line on how the place opens; the IANA zone its hours are in
hours The grounds' weekly hours as dated periods (a season can wrap New Year), dated exceptions, last entry, a note
venues Up to three places on the site with their own hours (a visitor center, a museum)
closures Dated closures, each whole or partial
cost, tickets Free or a list of prices and pass notes; whether reservations are needed, for what, in which window, and a booking link
bestTime, duration Twelve month ratings and why; a short "time needed"
know, notes Three to six "know before you go" items; labelled notes
sources Every page used, with the date it was fetched
npsAlerts Live Park Service alerts, for a record marked as the park unit's own (not stored in the record; the alertsFromNps mark itself is review-side and not sent)

The app's copy of the shape is mobile/src/domain/visit-info.ts:94-119.

API surface

Endpoint Auth Notes
GET /api/sync/patch/:id/visit-info None The published record plus the park's live alerts (backend/src/visit-info/visit-info.controller.ts:10-13). 404 when there is no published record, or the patch is not publicly visible (unlike GET /api/sync/patch/:id, which is unfiltered), so the endpoint reveals nothing the catalog does not. Review-only fields never leave the server (visit-info.service.ts:19-39).
GET /api/sync/content None Now also carries visitInfoIds (see above).

Key files

Configuration and flags

Edge cases and known limits

What this feature does NOT do

Tests that cover it

Open questions