From af05c5e061eeccec179b9df5acda7f4b3e0e404f Mon Sep 17 00:00:00 2001 From: Dylan Date: Mon, 17 Aug 2026 10:28:59 -0500 Subject: [PATCH] v3: promote activity type from an idea to a specified item It turned out to be a positioning decision rather than a feature -- it decides the store category, the screenshots and who finds the app, and cyclists are a much larger audience than motorcyclists. Far cheaper to settle before a store listing exists. Specified the schema (a text enum on Trip), and flagged that this is the first real migration the port will ship: the destructive fallback is gone, so it needs addColumn with a motorcycle default, schemaVersion 2, and a test that opens a v1 database and asserts the rides survive. Getting that path right matters more than the feature. The value is in driving per-activity defaults rather than labels: the 1.5 km/h noise floor is wrong for walking, 10 km/h histogram buckets are useless for running, and the elevation smoothing window was tuned for 2 Hz road speed. Also recorded a UI constraint: no picker in front of Start. The founding premise is press-and-go with gloves, so default to the last activity used and make it editable on trip detail afterwards. Co-Authored-By: Claude Opus 5 --- docs/V3-BACKLOG.md | 78 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 10 deletions(-) diff --git a/docs/V3-BACKLOG.md b/docs/V3-BACKLOG.md index ed13aea..8b8c6eb 100644 --- a/docs/V3-BACKLOG.md +++ b/docs/V3-BACKLOG.md @@ -104,7 +104,68 @@ What still holds regardless: --- -## 4. Smaller items +## 4. Activity type per ride + +Promoted out of the ideas list, because it turned out to be a **positioning decision** +rather than a feature. See [LAUNCH.md](LAUNCH.md). + +Everything user-facing says motorcycle, but the recording pipeline never did: it records +positions, speeds and altitudes, and nothing in it cares what you are sitting on. Dylan +already rides both bikes and motorcycles. + +**Why it matters beyond the feature:** it decides the store category, the screenshots and +who ever finds the app. Cyclists are a far larger audience than motorcyclists and are +already used to paying for ride apps. That question is much cheaper to settle before a +store listing exists than after. + +### Schema + +An `activity` column on `Trip`, stored as a text enum like `TripState`: + +``` +motorcycle · bicycle · scooter · skateboard · running · walking · other +``` + +This is the **first real migration** the port will ship. The destructive fallback is gone +for good, so it needs `m.addColumn(trips, trips.activity)` with a default of `motorcycle` +for existing rows, `schemaVersion` bumped to 2, and a migration test that opens a v1 +database and asserts the rides survive. Getting that path right once matters more than the +feature does — every later schema change depends on it. + +### Type should drive defaults, not just labels + +This is where the real value is, and it is easy to miss: + +| Setting | Why it differs | +|---|---| +| Speed noise floor (1.5 km/h) | Fine for a motorcycle; wrong for walking, where real movement lives near it | +| Speed histogram bucket (10 km/h) | Useless for running — everything lands in one bucket. Wants ~1 km/h. | +| Accuracy gate (50 m) | A motorcycle at speed can tolerate looser fixes than a walker | +| Elevation smoothing window | Tuned at 15 samples for 2 Hz road speed; a slower activity covers less ground per sample | +| Map fit zoom | A 2 km walk and a 200 km ride want different defaults | + +Treat these as a per-activity profile rather than scattering `if (activity == …)` through +the code. + +### Do not put a picker in front of Start + +The founding premise is press-and-go with gloves on. A modal asking "what are you doing?" +before recording begins would undo that. + +Better: **default to the last activity used**, and make it editable on the trip detail +screen afterwards, next to rename. Most people do the same thing most days, and the one +time they don't, they can fix it after. + +### Follow-ons, once the column exists + +- **Filter and group the trips list** by activity +- **GPX `` on ``** — Strava and Garmin read it, so an exported ride imports as + the right activity instead of defaulting to something wrong +- Per-activity totals, if a stats screen ever appears + +--- + +## 5. Smaller items | Item | Notes | |---|---| @@ -119,7 +180,7 @@ What still holds regardless: --- -## 5. Ideas +## 6. Ideas Terse on purpose. Unshaped, to be consolidated later. @@ -130,11 +191,6 @@ Terse on purpose. Unshaped, to be consolidated later. 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. - **Waypoint route planning.** Drop a series of pins on the map to "draw" a route, get @@ -151,7 +207,9 @@ Terse on purpose. Unshaped, to be consolidated later. 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. +Activity type (now section 4) and theming are independent and much cheaper — either could +ship alone, and activity type is the natural first v3 task because it forces the migration +path to be proven while the stakes are still low. Live map, handlebar mounting and waypoint following also cluster: all three assume a visible screen during the ride, and all three want the same map component. Route planning @@ -160,7 +218,7 @@ standalone. --- -## 6. Things that must not regress +## 7. Things that must not regress Hard-won and easy to undo by accident. Each has a comment in the code explaining why. @@ -181,7 +239,7 @@ Hard-won and easy to undo by accident. Each has a comment in the code explaining --- -## 7. Reading order for picking this up cold +## 8. 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