trails/openspec/changes/wahoo-production-cutover/proposal.md
Ullrich Schäfer 6a752e4933
Add openspec changes for Wahoo production cutover and route updates
- wahoo-production-cutover: ops checklist for moving the Wahoo Cloud
  API integration from sandbox to production tier (new app
  registration, logo upload, forced reauthorization).
- wahoo-route-update: switch the route push pipeline from POST-per-
  version to POST-then-PUT so re-pushing an edited route updates the
  existing Wahoo route instead of creating a duplicate.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 11:37:42 +02:00

2.8 KiB

Why

The trails.cool Wahoo Cloud API integration is currently registered as a sandbox application. Sandbox apps are rate-limited (250 req/day, 100/hr, 25 per 5 min) and Wahoo does not surface our logo or treat us as a first-class fitness app. To support real users at scale we need to be promoted to the production tier, which requires a Wahoo-side review of our app, scopes, redirect URI, and branding assets — none of which are visible in this codebase. This change captures the cutover as an auditable checklist so we don't lose track of the external steps and so we have a record of the sandbox-only quirks we already worked around.

What Changes

  • Submit our Wahoo app for production review and obtain production rate-limit tier (sandbox 250/day → production 5000/day; 25 per 5 min → 200; 100/hr → 1000/hr).
  • Confirm all five requested scopes are configured on the production app: workouts_read, user_read, offline_data, routes_read, routes_write.
  • Upload a trails.cool logo to Wahoo's developer console so it appears next to routes pushed by us in the Wahoo mobile app (logo is keyed off fitness_app_id, currently 2156 in sandbox).
  • Verify the production app's redirect URI is exactly https://trails.cool/api/sync/callback/wahoo (Wahoo is byte-strict on this).
  • Register a separate production app (Wahoo issues new client_id/client_secret) and plan a coordinated reauthorization for already-connected users — every existing Wahoo connection breaks at cutover and users are prompted to reconnect. Document the user-visible disruption.
  • Document, for posterity, the sandbox-only workarounds we already shipped so future maintainers don't repeat the discovery cost: per-(app, user) unrevoked-token cap (revoke on disconnect — #346), route[file] must be a data:application/vnd.fit;base64,... URI (#349), FIT Course capabilities must include valid|distance|position not just time (#348).

This is an operations change, not a code change. No spec deltas — wahoo-import and wahoo-route-push describe behaviors that don't change between sandbox and production.

Capabilities

New Capabilities

Modified Capabilities

Impact

  • No application code changes.
  • Secrets: infrastructure/secrets.app.env is updated with the new production client_id/client_secret and the journal is redeployed.
  • Existing users: every connected Wahoo user has to reauthorize once at cutover; the disconnect/connect UX already handles this, but we should communicate it ahead of time.
  • Wahoo developer console: branding (logo), redirect URI, scopes — all set externally.
  • Documentation: apps/journal/.env.example and CLAUDE.md may want a one-line note about which tier the app is on.