One file per feature, in the v2 shape that worked: goal, context, design, implementation, acceptance criteria, tests, risks, out of scope -- written before implementing so the risks are on paper rather than walked into. Three carry more weight than their size suggests. V3-01 ships the port's first real migration, and since the destructive fallback is gone, getting addColumn and a v1-database test right matters more than the feature. V3-08 forces a routing-engine decision with ongoing cost, so it sits behind a RoutingService interface mirroring what LocationSource did for GPS. V3-13 is not code at all -- it answers the three questions open since v2, and V3-15 may close unbuilt as a result, which is a legitimate outcome. Several tickets record constraints that are easy to lose: no activity picker in front of Start, because the founding premise is press-and-go with gloves; the Route-to-Trip foreign key must not cascade, or deleting an old plan deletes the ride; V3-14 will deliberately break the parity harness by adding GPX <type>, and that expectation should be updated rather than the check dropped; and V3-16 must not regress the explicit text colours that exist because of the black-on-black bug. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
52 lines
2.3 KiB
Markdown
52 lines
2.3 KiB
Markdown
# V3-14 — GPX interoperability
|
|
|
|
**Phase** Quality · **Depends on** V3-01 for `<type>` · **Size** S · **Status** Not started
|
|
|
|
## Goal
|
|
Confirm an exported ride actually imports into Strava, Garmin Connect and Google Earth —
|
|
and add the activity type so it lands as the right kind of activity.
|
|
|
|
## Context
|
|
Export is well tested: 15 tests, parsed with a real XML parser, and **byte-identical to the
|
|
Kotlin original** under the parity harness. But every one of those tests proves *structural
|
|
validity*, and structural validity does not mean a consumer accepts the file. That gap has
|
|
been open since v2.
|
|
|
|
## Design
|
|
Two parts.
|
|
|
|
**Verification** — export a real ride and import it into each of Strava, Garmin Connect and
|
|
Google Earth. Record what each does with pauses, elevation and timestamps. Pauses are the
|
|
interesting case: `<trkseg>` per segment is the correct GPX representation, but consumers
|
|
vary in whether they honour it.
|
|
|
|
**`<type>` on `<trk>`** — Strava and Garmin read it to decide the activity. Without it a
|
|
bicycle ride may import as a run. Needs V3-01's activity, mapped to each consumer's
|
|
vocabulary (Strava uses `ride`, `run`, and so on).
|
|
|
|
## Implementation
|
|
1. Add `<type>` to the `<trk>` element, from the trip's activity
|
|
2. Map the internal enum to GPX conventions; document the mapping in the code
|
|
3. Export a real multi-segment ride and import it into all three consumers
|
|
4. Write the findings into this file, including anything that surprises
|
|
|
|
## Acceptance criteria
|
|
- [ ] A real ride imports into Strava with the right activity type
|
|
- [ ] It imports into Garmin Connect
|
|
- [ ] It opens in Google Earth with the path in the right place
|
|
- [ ] Pause behaviour in each consumer is documented, whatever it turns out to be
|
|
- [ ] Existing export tests still pass, including the byte-identical parity check —
|
|
**this one will need updating, since `<type>` changes the output**
|
|
|
|
## Tests
|
|
- `<type>` present and correct per activity
|
|
- Absent, not empty, when the activity is `other`
|
|
- **The parity harness will now differ from Kotlin here. That is expected and correct —
|
|
update its expectation and note why, rather than dropping the check.**
|
|
|
|
## Risks
|
|
Silently breaking the parity harness by changing export output. Update it deliberately.
|
|
|
|
## Out of scope
|
|
GPX import into Rippr. FIT and TCX formats.
|