trainlog/docs/sync_exchange.md

284 lines
5.3 KiB
Markdown

# Trainlog synchronization exchange
## Status
```text
MOBILE_EXPORT_V1=FROZEN_FOR_IMPLEMENTATION
TRAINLOG_FORMAT_V1=FROZEN_UNCHANGED
```
This document defines a synchronization artifact. It is not the frozen
Trainlog session JSON v1 format.
## Android → PC artifact
Shared-storage path:
```text
Download/Trainlog/trainlog-mobile-export-v1.json
```
Format header:
```json
{
"format": "trainlog-mobile-export",
"version": 1
}
```
The artifact is a complete idempotent mobile snapshot containing:
```text
exercises
sessions
body_observations
```
### Exercises
Each exercise contains:
```text
exercise_id
name
recording_mode
tracking_mode
data_fields
```
### Sessions
Each session contains:
```text
session_id
started_at
session_type
exercises
```
Each session exercise snapshots:
```text
exercise_id
name
recording_mode
tracking_mode
data_fields
load_mode
rest_seconds
```
For `SETS`:
```text
sets[]
reps
or
duration_seconds
```
For `CONTINUOUS`:
```text
continuous
duration_seconds
speed_kmh optional
distance_km optional
```
A continuous exercise has no synthetic set.
### Body observations
Each body observation contains:
```text
observation_id
observed_at
only the metrics actually measured
```
## Transport
Android writes its own export into shared Downloads storage.
Desktop reads the artifact through direct libmtp transport.
No filesystem mount is required.
No SQLite database file is transferred.
## PC → Android
A separate canonical catalog artifact will be defined and implemented after
Android → PC export is validated on physical hardware.
The PC → Android path must not overload frozen Trainlog JSON v1.
<!-- TRAINLOG_DESKTOP_MOBILE_IMPORT_V1 -->
## Desktop import of mobile export v1
The desktop importer is:
```text
tools/import_mobile_export.py
```
It validates the complete mobile snapshot before opening a write transaction.
Properties:
```text
transactional
idempotent by stable IDs
exercise reconciliation by normalized name
profile conflicts rejected
unknown JSON fields rejected
no SQLite file copying
```
For mobile `SETS` v1, the Android form records one uniform set metric. The
desktop importer derives:
```text
target_sets = number of logged sets
target_reps or target_duration = uniform logged value
```
and preserves all performed sets separately.
A v1 mobile session with heterogeneous set metrics or `0 reps` is rejected
rather than inventing a desktop target.
Continuous activities remain target-less and are imported only into
`continuous_activity`.
Recommended validation sequence:
```bash
python tools/import_mobile_export.py /tmp/trainlog-mobile-export-v1.json --dry-run
python tools/import_mobile_export.py /tmp/trainlog-mobile-export-v1.json
```
Running the real import a second time must import nothing new and report the
existing IDs as skipped.
<!-- TRAINLOG_DESKTOP_MOBILE_IMPORT_V1 _END -->
<!-- TRAINLOG_BIDIRECTIONAL_SYNC_V1 -->
## Bidirectional synchronization v1
One desktop Sync action now performs both directions:
```text
Android → PC
direct-MTP download
strict transactional import
PC → Android
canonical PC exercise catalog export
direct-MTP publication
```
The Android app obtains one persistent Storage Access Framework grant for:
```text
Download/Trainlog
```
After this one-time grant, Android can import the PC-created catalog without
broad storage permissions.
The Sync page displays persistent synchronization history instead of remote
snapshot counts. A snapshot remaining present is not a pending queue item and
must not be shown as a "candidate".
User-facing session history timestamps are displayed as:
```text
DD/MM/YYYY HH:MM
```
Canonical RFC3339 storage remains unchanged.
<!-- TRAINLOG_BIDIRECTIONAL_SYNC_V1 _END -->
<!-- TRAINLOG_ANDROID_AUTO_OUTBOX_REQUEST -->
## Automatic Android outbox and sync request
Android no longer requires a manual export action.
The mobile snapshot is refreshed automatically on:
```text
application start
exercise save
session save
body observation save
PC catalog apply
```
The Android Sync screen exposes:
```text
Synchroniser maintenant
```
This writes:
```text
Download/Trainlog/trainlog-sync-request-v1.json
```
with a stable request ID and timestamp.
The next PC-agent slice consumes this request and writes a sync receipt.
<!-- TRAINLOG_ANDROID_AUTO_OUTBOX_REQUEST _END -->
<!-- TRAINLOG_BIDIRECTIONAL_VALIDATED_CHECKPOINT -->
## Validated bidirectional transport checkpoint
Validated on the physical Samsung device:
```text
ANDROID_TO_PC_MTP=PASS
DESKTOP_MOBILE_IMPORT_V1=PASS
DESKTOP_MOBILE_IMPORT_IDEMPOTENT=PASS
PC_CATALOG_EXPORT_V1=PASS
PC_TO_ANDROID_MTP_PUBLISH=PASS
```
Artifacts:
```text
Android → PC
Download/Trainlog/trainlog-mobile-export-v1.json
PC → Android
Download/Trainlog/trainlog-pc-catalog-v1.json
```
Both are synchronization artifacts and remain separate from frozen
`TRAINLOG_FORMAT_V1`.
The Android Storage Access Framework folder grant must target:
```text
Download/Trainlog
```
and the UI must permit changing the stored folder selection.
Remaining synchronization work:
```text
persistent structured sync history
selectable sync detail
common sync engine
trainlog-syncd
Android-triggered request/receipt workflow
automatic mobile snapshot maintenance
```
<!-- TRAINLOG_BIDIRECTIONAL_VALIDATED_CHECKPOINT _END -->