# Location is a City and a date window

The repository's architectural decision, rationale and consequences.

# Location is a City and a date window

**Status:** accepted · **Date:** 2026-11-13 · **Implements:** product ADR-05, RNF06, RN09, C8

**Enforcement:** `[type]` the contract accepts City ids (`z.enum(CITY_IDS)`) and ISO dates only · `[test]` `instructor.rules.test.ts` (fuzzing, map expiry, 30-day purge) and `instructor.procedures.test.ts` (US-35 preview) · `[convention]` no `expo-location`, no coordinate columns.

## Decision [#decision]

- The app never asks for or stores GPS, a hotel or a flight. Instructor location is a `City` id plus a `travel_window` (dates, no times).
- `instructor_profile` holds the visibility decision: audience `academies | network | nobody`, `fuzz_dates` (±1 day on each side), `tour_hidden`. Every change appends a `consent_record` (Art. 6(1)(a)) and an Audit Event.
- Going invisible stamps `invisible_since`. Alert matching and the who's-travelling map read the profile at query time, so an invisible Instructor drops out on the next read — well within C8's 60 s. Seminars already on sale stay public as the academy's event without linking the other stops.
- A window leaves the map the day after it ends (`isWindowOnMap`) and the itinerary is purged 30 days after (`itineraryPurgeDate`), by a cron job (ADR-0021).
- Reading another Instructor's itinerary from an Academy goes through `asSystem` with its own `SystemReason` and writes an Audit Event (RNF12).

