Files
rippr/docs/v3/V3-14-gpx-interop.md
Dylan 342a8f8382 Break v3 into 16 tickets
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>
2026-08-17 10:41:35 -05:00

2.3 KiB

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.