chore(openspec): archive fit-parsing-hardening
Sync the wahoo-import "FIT to GPX conversion" requirement into the main spec and move the completed change to openspec/changes/archive/2026-07-16-fit-parsing-hardening. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
e262b671e2
commit
0e04c48225
6 changed files with 19 additions and 3 deletions
|
|
@ -1,9 +1,7 @@
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
Provider-agnostic activity sync framework with Wahoo as the first provider, supporting OAuth connection, webhook-based automatic sync, manual import, and FIT-to-GPX conversion.
|
Provider-agnostic activity sync framework with Wahoo as the first provider, supporting OAuth connection, webhook-based automatic sync, manual import, and FIT-to-GPX conversion.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
### Requirement: Provider-agnostic sync framework
|
### Requirement: Provider-agnostic sync framework
|
||||||
The system SHALL provide capability-shaped seams for external activity sync providers. Each provider declares a manifest at `providers/<name>/manifest.ts` listing its `credential_kind` (`oauth | web-login | device`) and the capability adapters it implements (`Importer`, `RoutePusher`, `WebhookReceiver`). There SHALL NOT be a unified `SyncProvider` interface containing every capability with optional methods.
|
The system SHALL provide capability-shaped seams for external activity sync providers. Each provider declares a manifest at `providers/<name>/manifest.ts` listing its `credential_kind` (`oauth | web-login | device`) and the capability adapters it implements (`Importer`, `RoutePusher`, `WebhookReceiver`). There SHALL NOT be a unified `SyncProvider` interface containing every capability with optional methods.
|
||||||
|
|
||||||
|
|
@ -95,7 +93,7 @@ Imported activities SHALL show their origin in the UI.
|
||||||
- **AND** the workout appears as importable again on the import page
|
- **AND** the workout appears as importable again on the import page
|
||||||
|
|
||||||
### Requirement: FIT to GPX conversion
|
### Requirement: FIT to GPX conversion
|
||||||
The system SHALL convert FIT binary files to GPX format. The conversion logic lives at `apps/journal/app/lib/connected-services/fit.ts` — a provider-agnostic location shared across any future provider that produces FIT files (Garmin, Coros, etc.). Wahoo's importer and webhook both import from this shared module; they do not contain their own copy.
|
The system SHALL convert FIT binary files to GPX format. The conversion logic lives at `apps/journal/app/lib/connected-services/fit.ts` — a provider-agnostic location shared across any future provider that produces FIT files (Garmin, Coros, etc.). Wahoo's importer and webhook both import from this shared module; they do not contain their own copy. The conversion SHALL be pause- and session-aware: timer stop/start events (or record gaps over 5 minutes in files without events) and session boundaries each start a new track segment. The converter SHALL prefer enhanced (corrected) altitude and speed fields, drop records with missing timestamps or non-finite/out-of-range coordinates, treat non-finite altitude as absent, and expose the file's session sport mapped to the Journal's sport types alongside the GPX.
|
||||||
|
|
||||||
#### Scenario: Convert FIT with GPS data
|
#### Scenario: Convert FIT with GPS data
|
||||||
- **WHEN** a FIT file contains GPS track records
|
- **WHEN** a FIT file contains GPS track records
|
||||||
|
|
@ -110,3 +108,21 @@ The system SHALL convert FIT binary files to GPX format. The conversion logic li
|
||||||
#### Scenario: Workout without FIT file
|
#### Scenario: Workout without FIT file
|
||||||
- **WHEN** a workout has no file URL (e.g., aborted recording, third-party app data)
|
- **WHEN** a workout has no file URL (e.g., aborted recording, third-party app data)
|
||||||
- **THEN** the activity is created without GPX or geometry
|
- **THEN** the activity is created without GPX or geometry
|
||||||
|
|
||||||
|
#### Scenario: Pause becomes a segment boundary
|
||||||
|
- **WHEN** a FIT file contains a timer stop event followed by a start event 20 minutes later
|
||||||
|
- **THEN** the GPX contains two track segments split at the pause
|
||||||
|
- **AND** moving time computed downstream does not include the paused span
|
||||||
|
|
||||||
|
#### Scenario: Multisport file keeps one activity with per-session segments
|
||||||
|
- **WHEN** a FIT file contains multiple sessions
|
||||||
|
- **THEN** one GPX is produced with one or more track segments per session, records sliced to each session's time window
|
||||||
|
|
||||||
|
#### Scenario: Corrupt records dropped
|
||||||
|
- **WHEN** a FIT file contains records with out-of-range coordinates or missing timestamps
|
||||||
|
- **THEN** those records are excluded and the remaining track converts normally
|
||||||
|
|
||||||
|
#### Scenario: Sport surfaced from the file
|
||||||
|
- **WHEN** a FIT file's session declares sport "cycling" with sub-sport "gravel_cycling"
|
||||||
|
- **THEN** the converter reports sport `gravel` for the importer to use when the provider sends no explicit type
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue