Parent Epic
#104
Problem
docs/architecture.md explains how the system works but not why specific approaches were chosen over alternatives. Architecture Decision Records (ADRs) document trade-offs explicitly — this is the core signal of "progressive systems design" to technical reviewers.
Current State
Architecture is documented as implementation descriptions. Design rationale is scattered in code comments or not recorded at all.
Desired State
Create docs/adr/ directory with 5 ADRs covering the project's key design decisions:
ADR-001: Single Mutable State vs. Event Bus / Redux
- Context: App needs shared state across 10+ modules (GPS, recording, controls, geocoding)
- Decision: Single
AppState object threaded by reference; no event bus, no Redux
- Alternatives rejected: Redux (too much boilerplate at this scale), Event emitter (indirection without type safety), React Context (would require React)
- Consequences: Simple, zero-dependency state management; TypeScript strict mode prevents footguns; scales to current complexity but would need rethinking at ~50 modules
ADR-002: Refcount-Based GPS Polling vs. Pub/Sub
- Context: Two features (locate + recording) independently need GPS polling; must not interfere
- Decision: Integer refcount (
updateCallback) — increment to start, decrement to stop, timer runs when > 0
- Alternatives rejected: Pub/sub (overkill for 2 consumers), Separate timers (wasteful, race conditions), RxJS observables (heavy dependency)
- Consequences: Zero-allocation, zero-dependency coordination; trivially correct for N consumers
ADR-003: offsetHeight vs. dvh Units for iOS Safari Viewport
- Context: CSS
vh units and window.innerHeight disagree on older iOS Safari — the info-panel bottom sheet bled ~80px into view
- Decision: Use
el.offsetHeight (actual rendered height) for all snap-point calculations
- Alternatives rejected:
dvh units (not supported on older iOS), -webkit-fill-available (inconsistent), JavaScript viewport polyfill (adds complexity)
- Consequences: Works across all iOS versions; matches the pattern already used by geocode-bar
ADR-004: Local-Only Data Architecture
- Context: GPS trail data is sensitive (reveals home, work, daily patterns); app needs to handle it responsibly
- Decision: All data stays in the browser (localStorage, Cache API, IndexedDB). No server-side storage, no accounts, no analytics.
- Alternatives rejected: Cloud sync (requires accounts, server infra, privacy policy complexity), Anonymous telemetry (still collects data), Hybrid (complexity without clear user benefit at this scale)
- Consequences: Zero backend infrastructure; users own their data completely; no sync across devices (acceptable trade-off)
ADR-005: Service Worker + Cache API for Offline Support
- Context: Outdoor mapping app must work where connectivity is unreliable
- Decision: Two-tier offline strategy — passive Workbox caching (tiles viewed) + proactive region pre-download (Cache API)
- Alternatives rejected: IndexedDB tile storage (complex, no native URL matching), App shell only (tiles are the main content), Native app wrapper (loses PWA benefits)
- Consequences: Works in all modern browsers; Safari ~50MB quota is the main constraint; pre-download fills gaps that passive caching misses
ADR Template Format
# ADR-NNN: Title
## Status
Accepted | Superseded | Deprecated
## Context
What is the issue we're facing? What forces are at play?
## Decision
What did we decide to do?
## Alternatives Considered
What other options were evaluated and why were they rejected?
## Consequences
What are the results of the decision? Both positive and negative.
Suggested Prompt
Create Architecture Decision Records for webmap.dev in docs/adr/:
1. Create docs/adr/README.md with a brief explanation of what ADRs are
and an index linking to each ADR.
2. Create these 5 ADRs using the template format (Status, Context,
Decision, Alternatives Considered, Consequences):
- ADR-001-single-mutable-state.md — Why AppState is a single mutable
object instead of Redux/event bus. Read src/types.ts and src/main.ts
for context on how state flows through modules.
- ADR-002-refcount-gps-polling.md — Why GPS polling uses integer
refcount instead of pub/sub. Read src/timer.ts and the
activatePolling/deactivatePolling pattern in src/main.ts.
- ADR-003-offsetheight-ios-safari.md — Why snap-point math uses
offsetHeight instead of dvh units. Read the fullHeightPx() function
in src/bottom-sheet.ts and the iOS Safari vh bug it fixes.
- ADR-004-local-only-data.md — Why all data stays in the browser with
no server-side storage. Consider the privacy implications of GPS
trail data.
- ADR-005-offline-tile-strategy.md — Why offline uses two tiers
(Workbox passive + Cache API pre-download). Read
src/offline-download.ts and vite.config.ts workbox config.
3. Read the actual source files referenced above to ground each ADR in
real implementation details, not generic descriptions.
Commit all files and push.
Acceptance Criteria
Parent Epic
#104
Problem
docs/architecture.mdexplains how the system works but not why specific approaches were chosen over alternatives. Architecture Decision Records (ADRs) document trade-offs explicitly — this is the core signal of "progressive systems design" to technical reviewers.Current State
Architecture is documented as implementation descriptions. Design rationale is scattered in code comments or not recorded at all.
Desired State
Create
docs/adr/directory with 5 ADRs covering the project's key design decisions:ADR-001: Single Mutable State vs. Event Bus / Redux
AppStateobject threaded by reference; no event bus, no ReduxADR-002: Refcount-Based GPS Polling vs. Pub/Sub
updateCallback) — increment to start, decrement to stop, timer runs when > 0ADR-003: offsetHeight vs. dvh Units for iOS Safari Viewport
vhunits andwindow.innerHeightdisagree on older iOS Safari — the info-panel bottom sheet bled ~80px into viewel.offsetHeight(actual rendered height) for all snap-point calculationsdvhunits (not supported on older iOS),-webkit-fill-available(inconsistent), JavaScript viewport polyfill (adds complexity)ADR-004: Local-Only Data Architecture
ADR-005: Service Worker + Cache API for Offline Support
ADR Template Format
Suggested Prompt
Acceptance Criteria
docs/adr/directory exists with README.md index