trainlog/docs/reviews/bidirectional_sync_checkpoint.md

223 lines
3.9 KiB
Markdown

# Bidirectional synchronization checkpoint
## Validated status
```text
ANDROID_LOCAL_WORKFLOWS=PASS
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
ANDROID_SAF_FOLDER_SELECTION=PASS
ANDROID_SAF_FOLDER_CHANGE=PASS
```
This checkpoint represents the validated synchronization foundation on the
physical Samsung device.
## Android → PC
Android produces:
```text
Download/Trainlog/trainlog-mobile-export-v1.json
```
The desktop retrieves it through direct libmtp.
No GVFS/FUSE mount is used.
The desktop importer:
```text
tools/import_mobile_export.py
```
is:
```text
strict
transactional
idempotent by stable IDs
profile-aware
```
Validated behavior:
```text
first import:
new exercise/session/body observation imported
second import:
no duplicates
existing stable IDs skipped
```
## PC → Android
The desktop produces:
```text
/tmp/trainlog-pc-catalog-v1.json
```
with:
```text
format = trainlog-pc-catalog
version = 1
```
The catalog is published by direct MTP to:
```text
Download/Trainlog/trainlog-pc-catalog-v1.json
```
Physical-device publication has been validated.
The Android application accesses the PC-created file through one persistent
Storage Access Framework grant.
The selected folder must be:
```text
Download/Trainlog
```
The Sync screen must always allow changing this folder because a wrong
persisted SAF grant must be recoverable without clearing application data.
## Important semantics
The synchronization layer exchanges versioned Trainlog domain artifacts.
It does not synchronize SQLite files.
The frozen legacy session format remains:
```text
TRAINLOG_FORMAT_V1=FROZEN
```
Profile-aware synchronization uses separate explicit synchronization artifacts.
## Current TUI state
The existing desktop Sync action can:
```text
Android → PC import
PC → Android catalog publication
```
The current status/count-oriented Sync presentation is temporary.
A snapshot file remaining on Android is not a pending queue item. Therefore
labels such as:
```text
1 JSON candidate
```
must not be considered a final synchronization UX.
## Next synchronization UX
The TUI Sync page must become a persistent synchronization history, similar to:
```text
git log
```
Example:
```text
06/09/2026 16:00 ✓ Android +1 session · PC catalog 3 exercises
06/09/2026 15:42 ✓ no changes
06/09/2026 15:31 ! device disconnected
```
A synchronization entry must be selectable.
`Enter` opens a detailed view analogous to:
```text
git show
```
The detail must include:
```text
sync ID
trigger
start/end local time
status
Android → PC
exercises imported/reconciled/skipped
sessions imported/skipped
body observations imported/skipped
PC → Android
catalog exercises published
Android catalog apply result when available
transport/artifact diagnostics
```
## Android-triggered synchronization
The final Android Sync UX must not require:
```text
Prepare export
then use the PC manually
```
Target behavior:
```text
Android data change
-> mobile snapshot maintained automatically
Android "Synchronize now"
-> synchronization request
PC trainlog-syncd
-> detects request over direct MTP
-> runs the same bidirectional sync engine
-> writes synchronization receipt
Android
-> reads receipt
-> applies PC catalog
-> displays final synchronization result
```
MTP remains host-initiated. Therefore Android-triggered synchronization needs
a small PC-side agent; it cannot directly command libmtp operations on the PC.
## Display normalization
User-facing session history must use local presentation:
```text
DD/MM/YYYY HH:MM
```
Canonical RFC3339 timestamps remain unchanged in persistence and exchange.
## Next cursor
```text
SYNC_HISTORY_GIT_LIKE=NEXT
COMMON_SYNC_ENGINE=NEXT
TRAINLOG_SYNCD=NEXT
ANDROID_SYNC_REQUEST_RECEIPT=AFTER
ANDROID_AUTO_OUTBOX=AFTER_AGENT_FOUNDATION
```