# v3 backlog Everything known-outstanding as of v2.0.1, with enough context to pick up cold. Nothing here is committed to — it is a menu, roughly ordered by value. **Before planning anything: run the real-ride checklist in [../TESTING.md](../TESTING.md).** Several items below may turn out to be non-issues, and others may appear that nobody has thought of. --- ## 1. Carried over from v2 — the honest debt ### Elevation gain accuracy · *needs real data first* ~30 m of phantom gain per ten stationary minutes against synthetic ±8 m uniform noise. Real GPS altitude error is *correlated* rather than uniform, so the true behaviour is unknown. The current implementation is a 15-sample moving average plus reversal hysteresis (see [../ARCHITECTURE.md](../ARCHITECTURE.md)). A naive version reported 1498 m over a parked bike, so the guard rails matter. **Do not tune this blind.** Record a flat ride, check whether the reported gain is plausible, and only then adjust. If it needs work, options are a longer smoothing window, a larger threshold, or using barometric pressure where available (much more accurate than GPS altitude, and most phones have the sensor). ### Compose UI tests · *the largest coverage gap* Zero UI tests across six screens. Everything was verified by manual screenshot. Worth covering: navigation record→trips→detail→back, rotation/state retention, empty states, `NotFound`, chart degradation below two points, selection mode enabling Merge only at two. ### Unmeasured, and probably should be - **Battery drain** over a multi-hour ride — never measured, and it is the thing most likely to make the app unusable in practice - **Map memory across repeated navigation** — the osmdroid lifecycle is a known hazard and the wiring was never leak-tested - **GPX import into Strava/Garmin** — validated against an XML parser, but schema validity does not guarantee a consumer accepts it --- ## 2. The original v3 candidate — live group ride view Deferred from v2 as "needs real server work". This was in the **v1** brief's goal statement, so it has been the intended destination all along. Already in place: - `TelemetryUploader` — batched POST, retry, offline-safe, cannot stall recording - `synced` column and backlog semantics - `trip_id` / `segment_id` per point, so a server can reconstruct rides and pauses - `Config.deviceId` — stable per-install id to distinguish riders Missing: - **A server.** Nothing exists. This is the actual work. - **UI for the endpoint** — currently only reachable via `Config.setUploadEndpoint()` - Other riders' positions on a map, and a live map at all (see below) - Auth, rider identity, group membership **Worth deciding early:** this is the point where Rippr stops being a local-only app. That brings hosting, privacy, and location-sharing consent into scope. --- ## 3. Live map on the recording screen — **decision reversed** v2 deliberately shipped no live map, on the reasoning that the phone rides in a pocket. Dylan has since asked for one anyway, for visual appeal. Treat the v2 stance as superseded. What still holds and should not be given up: - **`TrackingService` must never reference a map.** Rendering belongs to the Compose lifecycle of a visible screen, not the service. - **Screen-on only.** No tile fetch or redraw while backgrounded. - Battery cost sits on top of GPS and a wake lock — worth measuring before and after. --- ## 4. Smaller items | Item | Notes | |---|---| | **Trip splitting** | Merge exists; split does not. The natural counterpart. | | **SAF export** | Dropped in T16 as unnecessary — share sheet covers it. Add if a real need appears. | | **Auto-pause** | Detect a stop and pause automatically. Rejected in v2 as unreliable in traffic; revisit only with real ride data showing it would help. | | **Distance units** | Metric only, hardcoded. Trivial to add a preference. | | **Settings screen** | None exists. `Config` has endpoint, deviceId, mapEnabled — the map toggle currently lives on trip detail because one switch did not justify a screen. | | **Offline tile pre-download** | osmdroid caches what it renders; a mountain ride with no signal shows blank tiles. Respect OSM's usage policy — no bulk prefetch of their public servers. | | **Notification live stats** | Show distance/duration in the ongoing notification, readable without unlocking. | | **Crash reporting** | None. A recorder that dies mid-ride currently leaves no trace beyond logcat. | --- ## 5. Ideas Terse on purpose. Unshaped, to be consolidated later. - **Live map while recording.** More visually appealing than a numbers screen. Reverses the v2 decision — see section 3 for the constraints that survive it. - **Pick a real theme.** The current look is functional dark + safety orange, chosen to match the icon. Decide on an actual visual identity and push the UI toward something polished rather than merely clean. - **User sign-up and accounts.** Register people, give their data somewhere to live. Prerequisite for anything cloud-side, and pairs with the group-ride server in section 2. - **Activity type per ride.** Motorcycle, bicycle, skateboard, running, other. The app is not inherently motorcycle-only — the recording pipeline is activity-agnostic already. Note: adds a column to `Trip`, so it needs a real `Migration` (the destructive fallback is gone). Type could also drive sensible defaults — speed noise floor, map zoom, elevation smoothing. - **Paid cloud backup.** Ongoing storage of rides over time. Needs accounts first, plus a decision on hosting, pricing, and what happens to data when someone stops paying. ### Threads running through these Sign-up, cloud backup and group ride are one programme, not three: they all need a server, identity, and a privacy stance. Worth scoping together rather than separately. Activity type and theming are independent and much cheaper — either could ship alone. --- ## 6. Things that must not regress Hard-won and easy to undo by accident. Each has a comment in the code explaining why. 1. **The unbounded `Channel` + single batched writer.** Do not write to the database from the location callback. 2. **Points stamped with `tripId`/`segmentId` at creation.** Refactoring this into a write-time lookup breaks the pause guarantee silently. 3. **Recording state derived from the database.** Never reintroduce an in-memory flag. 4. **`fallbackToDestructiveMigration()` stays removed.** Any schema change ships a `Migration` against `app/schemas/com.rippr.data.AppDatabase/2.json`. 5. **Decimation is render-only.** It must never reach storage or export. 6. **`RipprTheme`'s `Surface`.** It sets `LocalContentColor`; without it, text without an explicit colour renders black-on-black and disappears. 7. **The map zoom clamp.** `zoomToBoundingBox` ignores `maxZoomLevel`; a short ride will render an empty grid without it. 8. **Instrumented tests use in-memory databases.** One previously wiped the real device database in `setUp`. --- ## 7. Reading order for picking this up cold 1. [../../README.md](../../README.md) — what the app is and its current state 2. [../ARCHITECTURE.md](../ARCHITECTURE.md) — why it is built this way 3. [../v2/PROGRESS.md](../v2/PROGRESS.md) — every bug found during v2 and how 4. [../TESTING.md](../TESTING.md) — **especially "What the emulator cannot verify"** 5. [../DEVELOPMENT.md](../DEVELOPMENT.md) — when you actually need to build something The v2 planning approach worked well and is worth repeating: one document per task with goal, context, design, acceptance criteria and risks, written *before* implementing, plus a running progress log recording what actually went wrong. Several bugs were caught precisely because the risk had been written down first — and one (the osmdroid lifecycle) was written down and then walked into anyway, which is its own lesson.