trainlog/docs/sync_exchange.md

5.4 KiB

Synchronization exchange

1. Status

DIRECT_MTP_TRANSPORT=PASS
ANDROID_TO_PC_IMPORT=PASS
PC_TO_ANDROID_CATALOG=PASS
COMMON_SYNC_ENGINE=PASS
TRAINLOG_SYNCD=PASS
ANDROID_TRIGGERED_SYNC=PASS
ANDROID_SYNC_RECEIPT=PASS
TUI_SYNC_LOG_SHOW=PASS
BIDIRECTIONAL_SYNC_V1=PASS

TRAINLOG_FORMAT_V1=FROZEN_UNCHANGED

Synchronization artifacts are separate from the frozen Trainlog session JSON v1 format.

2. Exchange directory

Canonical Android shared-storage directory:

Download/Trainlog

Desktop accesses this directory through direct MTP.

Android accesses PC-created artifacts through a persistent Storage Access Framework folder grant.

3. Artifact table

Direction File Format
Android -> PC trainlog-mobile-export-v1.json trainlog-mobile-export v1
PC -> Android trainlog-pc-catalog-v1.json trainlog-pc-catalog v1
Android -> PC agent trainlog-sync-request-v1.json trainlog-sync-request v1
PC agent -> Android trainlog-sync-receipt-v1.json trainlog-sync-receipt v1

No SQLite file is transferred.

4. Android -> PC mobile snapshot

Header:

{
  "format": "trainlog-mobile-export",
  "version": 1
}

The artifact is a complete idempotent mobile snapshot containing:

exercises
sessions
body_observations

Exercise profile fields:

exercise_id
name
recording_mode
tracking_mode
data_fields

Set-based session exercise actuals use ordered:

sets[]

with either:

reps

or:

duration_seconds

Heterogeneous repetition values are valid.

Continuous actuals use:

continuous
    duration_seconds
    speed_kmh optional
    distance_km optional

No synthetic set is created for continuous work.

5. Desktop mobile importer

Reference importer:

tools/import_mobile_export.py

Properties:

strict full-snapshot validation
transactional import
stable-ID idempotence
catalog reconciliation
profile conflict rejection
heterogeneous performed-set preservation
targetless schema-v5 import when no true target exists
continuous activity kept separate

The importer never invents a uniform target merely to fit desktop persistence.

6. PC -> Android catalog

The desktop publishes:

trainlog-pc-catalog-v1.json

It is a canonical exercise catalog snapshot containing stable profile metadata.

Android reconciles the received catalog into its local exercise catalog.

This direction does not overload frozen Trainlog session JSON v1.

7. Android sync request

Android writes:

trainlog-sync-request-v1.json

Header:

{
  "format": "trainlog-sync-request",
  "version": 1
}

Required synchronization identity:

request_id = sr_<uuid-v4>

The artifact also carries the request timestamp.

A new request_id represents a new synchronization request.

8. PC sync receipt

After processing an Android request, the PC publishes:

trainlog-sync-receipt-v1.json

Header:

{
  "format": "trainlog-sync-receipt",
  "version": 1
}

The receipt contains:

request_id
sync_id
status
summary
Android -> PC counts
PC -> Android catalog count

Android accepts a receipt only when its request_id matches the pending request.

9. Shared desktop engine

Canonical implementation:

trainlog_sync_run()

TUI path:

TUI
-> shared engine

Android-triggered path:

Android request
-> trainlog-syncd
-> shared engine
-> receipt

One synchronization transaction performs:

mobile snapshot download
-> mobile import
-> PC catalog export
-> PC catalog MTP publication
-> optional receipt publication
-> structured run history

10. Concurrency and request consumption

Synchronization owns:

$XDG_DATA_HOME/trainlog/sync.lock

The daemon uses non-blocking acquisition while polling.

The TUI manual action waits for the active synchronization lock.

After a request is completed and its receipt is published, the request ID is recorded locally so the same request is not processed as a new request again.

11. Structured sync history

Every real run has:

sy_<uuid-v4>

Artifacts:

$XDG_DATA_HOME/trainlog/sync_runs/sy_*.json
$XDG_DATA_HOME/trainlog/sync_runs/sy_*.txt
$XDG_DATA_HOME/trainlog/sync_history.log

The TUI presents newest runs in a selectable list and opens the detail file with Enter.

Legacy history rows without a sync_id remain readable as list entries but cannot have structured detail.

12. PC user service

Install or refresh:

bash tools/install_syncd_user.sh

Check:

systemctl --user is-active trainlog-syncd.service
systemctl --user --no-pager --full status trainlog-syncd.service

Daemon log:

tail -f ~/.local/state/trainlog/syncd.log

No root privilege is required.

13. Transport invariants

Do not regress to:

SQLite database copying
mandatory GVFS/FUSE mounts
exercise-name identity heuristics
fake sets for continuous activity
fake uniform targets for heterogeneous actual sets
overloading frozen Trainlog JSON v1

14. Hardware validation

Validated on the physical Android device:

MTP device discovery PASS
storage access PASS
read/write/list/delete PASS
Android mobile snapshot download PASS
desktop idempotent import PASS
PC catalog publication PASS
Android request detection PASS
trainlog-syncd processing PASS
receipt publication/readback PASS
multiple distinct Android request IDs PASS