# Roadmap ## Gate 0 — Project contract Status: PASS ## Gate 1 — Exchange format v1 freeze Status: PASS ```text TRAINLOG_FORMAT_V1=FROZEN ``` ## Gate 2 — Persistence + usable TUI Status: IN PROGRESS ```text FIRST_USABLE_TUI=PASS TUI_V0_2_POLISH=IMPLEMENTED GATE_2=IN_PROGRESS ``` Current TUI capabilities: - direct workout entry; - exercise catalog; - body tracking; - weight graph; - colored dashboard; - arrow/F-key navigation; - navigable history; - Unicode anti-duplicate exercise names. Current remaining Gate 2 direction: 1. detect Android over USB/ADB; 2. build the minimal Android recorder; 3. transfer/export one frozen Trainlog JSON v1 document over USB; 4. import it transactionally into the canonical SQLite store; 5. validate the complete Android -> JSON -> TUI -> SQLite path. Measured-max semantics remain a later independent analytics contract. ## TUI v0.2 checkpoint Current state: ```text FIRST_USABLE_TUI=PASS TUI_V0_2_POLISH=IMPLEMENTED TUI_SESSION_DETAILS=IMPLEMENTED TUI_DURATION_HUMAN_INPUT=IMPLEMENTED TUI_BODY_METRIC_GRAPHS=IMPLEMENTED GATE_2=IN_PROGRESS TRAINLOG_FORMAT_V1=FROZEN ``` Completed before this checkpoint: - C17/Meson persistence core; - SQLite schema v1 foundation; - UUIDv4 generation; - Unicode catalog normalization; - direct workout recording; - direct body observation recording; - colored ncursesw dashboard; - keyboard navigation; - body-weight graph; - flat-series graph rendering; - navigable workout history; - full read-only session detail. Next implementation slice: 1. shared duration parser accepting seconds and minute-oriented syntax; 2. shared human duration formatter; 3. generic body-metric history query; 4. F4 metric selector; 5. graphs for weight and every body measurement; 6. left/right asymmetry presentation. No incompatible change to frozen Trainlog JSON v1 is required. ## Next TUI visualization slice Canonical state after the current checkpoint: ```text FIRST_USABLE_TUI=PASS TUI_V0_2_POLISH=IMPLEMENTED TUI_SESSION_DETAILS=IMPLEMENTED TUI_DURATION_HUMAN_INPUT=IMPLEMENTED TUI_BODY_METRIC_GRAPHS=IMPLEMENTED TUI_GLOBAL_BODY_OVERLAY=IMPLEMENTED DASHBOARD_GRAPH_ONLY=IMPLEMENTED GATE_2=IN_PROGRESS TRAINLOG_FORMAT_V1=FROZEN ``` Next deliverables: 1. normalized global body graph in `F4`; 2. color + symbol identity for every overlaid metric; 3. global percent-change summary; 4. richer home weight graph; 5. previous-measurement delta on dashboard; 6. dashboard min/max weight; 7. latest waist summary when available; 8. compact asymmetry warning when relevant. No database schema migration is expected. No Trainlog JSON v1 change is expected. ```text DASHBOARD_12_MONTHS=IMPLEMENTED ``` ```text TUI_EXERCISE_PERFORMANCE=IMPLEMENTED TUI_SESSION_EDIT=IMPLEMENTED TUI_BODY_OBSERVATION_EDIT=IMPLEMENTED ANDROID_USB_DETECTION=NEXT MEASURED_MAX_TRACKING=LATER ``` ## Session type / measured max foundation ```text DATABASE_SCHEMA_V2=IMPLEMENTED SESSION_TYPE_PERSISTENCE=IMPLEMENTED SESSION_TYPE_TUI=IMPLEMENTED TUI_SESSION_EDIT=IMPLEMENTED TUI_BODY_OBSERVATION_EDIT=IMPLEMENTED TUI_PRIMARY_NAVIGATION=IMPLEMENTED TUI_SECONDARY_VIEW_POLISH=IMPLEMENTED ANDROID_USB_DETECTION=NEXT MEASURED_MAX_TRACKING=LATER TRAINLOG_FORMAT_V1=FROZEN ``` SQLite schema v2 adds `sessions.session_type` with `training` and `max_test`. Existing v1 rows migrate to `training`. No existing session is retroactively classified as a max test. ## Editable session data ```text SESSION_EDIT_PERSISTENCE=IMPLEMENTED SESSION_EDIT_TUI=IMPLEMENTED BODY_OBSERVATION_EDIT=IMPLEMENTED USB_PHONE_DETECTION=NEXT ``` Recorded session exercise/set correction uses atomic replacement of child rows while preserving the parent session row and linked body observations. The TUI exposes correction both while reviewing an in-progress draft and after persistence. Escape cancels without committing partial edits. ## Body observation record workflow ```text BODY_OBSERVATION_HISTORY_UI=IMPLEMENTED BODY_OBSERVATION_EDIT=IMPLEMENTED BODY_OBSERVATION_SCROLLBAR=IMPLEMENTED USB_PHONE_DETECTION=NEXT ``` `F4 Corps` now uses newest-first observation records with detail/edit views instead of making individual metrics the primary navigation model. ## Editability/navigation checkpoint Completed: - local schema v2 migration with `training` / `max_test`; - session-type selection and persistence; - persisted session editing with stable parent identity; - body-observation history, detail, and editing; - Escape-safe prompt cancellation; - framed ASCII-banner primary and secondary views; - top `Accueil / Séance / Historique / Exercices / Corps` navigation; - Tab focus with yellow border-only focus indication; - direct exercise creation while building a session; - 12-month dashboard axis kept inside its frame. Next implementation cursor: ```text ANDROID_USB_DETECTION=NEXT ANDROID_MINIMAL_RECORDER=AFTER JSON_V1_USB_IMPORT_EXPORT=AFTER ``` ## Direct MTP transport checkpoint ```text USB_MTP_DETECTION=PASS MTP_STORAGE_ACCESS=PASS MTP_ROOT_FOLDER_ACCESS=PASS MTP_WRITE=PASS MTP_LIST_FOLDER=PASS MTP_READ=PASS MTP_ROUNDTRIP=PASS MTP_TRANSPORT_FOUNDATION=PASS JSON_V1_MTP_TRANSFER=NEXT ANDROID_MINIMAL_RECORDER=AFTER TRAINLOG_FORMAT_V1=FROZEN ``` The Linux side now detects one physical MTP phone without counting USB interface children, opens the exact device with libmtp, accesses internal storage, and performs a verified write/list/read roundtrip without mounting the phone. Next implementation slice: ```text 1. freeze the MTP exchange directory/file convention 2. roundtrip a real examples/session-v1.json 3. validate/import the downloaded JSON 4. begin the minimal Android recorder ``` ## Sync TUI integration ```text MTP_TRANSPORT_FOUNDATION=PASS TUI_SYNC_PAGE=IMPLEMENTED TUI_SYNC_DEVICE_STATUS=IMPLEMENTED TUI_SYNC_REMOTE_JSON_CANDIDATES=IMPLEMENTED TUI_SYNC_LOCAL_CATALOG_COUNT=IMPLEMENTED JSON_V1_MTP_CLASSIFICATION_IMPORT=NEXT CATALOG_SNAPSHOT_SYNC=AFTER ANDROID_MINIMAL_RECORDER=AFTER TRAINLOG_FORMAT_V1=FROZEN ``` Sync direction: ```text Android -> PC sessions new exercises embedded in sessions -> automatic reconciliation body data embedded in sessions -> automatic import PC -> Android canonical exercise catalog snapshot ``` Standalone body-observation exchange, if required by the Android recorder, gets its own explicit versioned contract rather than changing session JSON v1. ## Sync UI checkpoint complete ```text MTP_TRANSPORT_FOUNDATION=PASS TUI_SYNC_PAGE=IMPLEMENTED TUI_SYNC_DEVICE_STATUS=IMPLEMENTED TUI_SYNC_FOCUS_NAVIGATION=IMPLEMENTED TUI_SYNC_REMOTE_JSON_CANDIDATES=IMPLEMENTED TUI_SYNC_LOCAL_CATALOG_COUNT=IMPLEMENTED ANDROID_APP_SCAFFOLD=NEXT ANDROID_FAKE_DATA_FLOW=AFTER JSON_V1_ANDROID_EXPORT=AFTER CATALOG_SNAPSHOT_SYNC=AFTER TRAINLOG_FORMAT_V1=FROZEN ``` Development now moves to the Android client. Fictitious sessions, exercises, and body observations are used during Android development and synchronization testing. The development database will be purged before normal production use begins. ## Exercise data-model checkpoint ```text EXERCISE_DATA_MODEL_V1=FROZEN_FOR_IMPLEMENTATION DATABASE_SCHEMA_V3=NEXT PROFILE_AWARE_C_MODEL=AFTER_SCHEMA PROFILE_AWARE_TUI=AFTER ANDROID_PROFILE_AWARE_UI=AFTER SESSION_EXCHANGE_V2=DESIGN_LATER TRAINLOG_FORMAT_V1=FROZEN ``` The model separates: ```text recording organization: SETS | CONTINUOUS primary metric: REPS | DURATION supplemental fields: SPEED_KMH | DISTANCE_KM ``` Existing schema-v2 data migrates conservatively to `SETS`. ## Current implementation cursor ```text MTP_TRANSPORT_FOUNDATION=PASS TUI_SYNC_PAGE=PASS EXERCISE_DATA_MODEL_V1=PASS DATABASE_SCHEMA_V4=PASS PROFILED_CATALOG_API=PASS PROFILE_AWARE_EXERCISE_CREATION=PASS CONTINUOUS_ACTIVITY_PERSISTENCE=PASS CONTINUOUS_ACTIVITY_DETAIL_DISPLAY=PASS CONTINUOUS_DURATION_MINUTES_UI=PASS ANDROID_APP=NEXT TRAINLOG_FORMAT_V1=FROZEN PROFILE_AWARE_SESSION_EXCHANGE=DESIGN_LATER ``` Next Android slice: ```text 1. Android project scaffold 2. shared Trainlog visual identity 3. launcher icon = themed T 4. home/navigation 5. session recording 6. inline exercise creation 7. standalone exercise creation 8. standalone body measurement recording 9. fictitious local records 10. only then connect exchange/sync ``` Do not revert continuous activities to performed sets. Do not modify JSON v1 to accommodate continuous metrics. ## Android local checkpoint ```text ANDROID_PROJECT=PASS ANDROID_THEME=PASS ANDROID_EXERCISE_CATALOG=PASS ANDROID_SESSION_RECORDING=PASS ANDROID_SESSION_HISTORY=PASS ANDROID_BODY_RECORDING=PASS ANDROID_MTP_SYNC=NEXT ``` The next implementation cursor is synchronization between the Android client and the desktop TUI over the existing direct-MTP transport architecture. Constraints remain: ```text no GVFS/FUSE dependency no SQLite-file synchronization TRAINLOG_FORMAT_V1 remains frozen profile-aware data must not be forced into v1 ``` ## MTP mobile export v1 Android now prepares a versioned full mobile snapshot at: ```text Download/Trainlog/trainlog-mobile-export-v1.json ``` The file contains: ```text exercise profiles sessions body observations ``` It is explicitly separate from frozen `TRAINLOG_FORMAT_V1`. Desktop direct-MTP validation is available through: ```text ./build/tui/trainlog-mtp-mobile-export-probe ``` The probe traverses: ```text internal storage → Download → Trainlog → trainlog-mobile-export-v1.json ``` and downloads it directly through libmtp without a mount. Next after hardware PASS: ```text DESKTOP_MOBILE_EXPORT_IMPORT=NEXT PC_TO_ANDROID_CATALOG=AFTER ``` ## Mobile import cursor ```text MOBILE_EXPORT_MTP=PASS DESKTOP_MOBILE_IMPORT_V1=IMPLEMENTED TUI_SYNC_ACTION=NEXT PC_TO_ANDROID_CATALOG=AFTER ``` The CLI importer is the reference import engine for the next TUI Sync action. ## Sync implementation cursor ```text MOBILE_EXPORT_MTP=PASS DESKTOP_MOBILE_IMPORT_V1=PASS TUI_ANDROID_TO_PC_SYNC_ACTION=IMPLEMENTED TUI_ANDROID_TO_PC_SYNC_HARDWARE_VALIDATION=NEXT PC_TO_ANDROID_CATALOG=AFTER ``` ## Bidirectional sync cursor ```text ANDROID_TO_PC_MTP=PASS DESKTOP_MOBILE_IMPORT=PASS PC_TO_ANDROID_CATALOG=IMPLEMENTED SYNC_HISTORY_UI=IMPLEMENTED BIDIRECTIONAL_HARDWARE_VALIDATION=NEXT ``` ## Sync agent cursor ```text ANDROID_AUTO_OUTBOX=IMPLEMENTED ANDROID_SYNC_REQUEST=IMPLEMENTED TRAINLOG_SYNCD=NEXT ANDROID_SYNC_RECEIPT=AFTER TUI_SYNC_LOG_SHOW=AFTER_AGENT_FOUNDATION ``` ## Synchronization cursor ```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_CHANGE=PASS SYNC_HISTORY_GIT_LIKE=NEXT COMMON_SYNC_ENGINE=NEXT TRAINLOG_SYNCD=NEXT ANDROID_REQUEST_RECEIPT=AFTER AUTO_OUTBOX=AFTER_AGENT_FOUNDATION ``` Do not regress to: ```text SQLite file synchronization filesystem mounts exercise-name heuristics manual fake sets for continuous activities overloading frozen Trainlog JSON v1 ``` ## Variable repetition sets Trainlog preserves each performed set independently. Accepted repetition input: ```text 5x10 4,5,6,7,8,9,10,9,8,7,6,5,4 4..10..4 ``` `4..10..4` expands to: ```text 4,5,6,7,8,9,10,9,8,7,6,5,4 ``` Desktop schema v5 permits targetless `SETS` rows for actual-only mobile observations. Synchronization therefore does not invent a uniform target when performed sets are heterogeneous. `performed_sets` remains the source of truth for actual per-set values. Existing planned desktop sessions may still carry explicit target sets/reps or target durations. `trainlog-mobile-export` v1 keeps ordered heterogeneous `sets[]`. Frozen `TRAINLOG_FORMAT_V1` is unchanged. ## Variable sets and session exercise removal checkpoint Validated functionality in this checkpoint: ```text VARIABLE_REPETITION_SETS=PASS REPETITION_SHORTHAND_5x10=PASS REPETITION_EXPLICIT_LIST=PASS REPETITION_PYRAMID=PASS DESKTOP_SCHEMA_V5=PASS V4_TO_V5_MIGRATION_REGRESSION=PASS MOBILE_HETEROGENEOUS_SET_IMPORT=PASS MOBILE_IMPORT_IDEMPOTENCE=PASS NO_FAKE_UNIFORM_TARGET=PASS ANDROID_SESSION_DRAFT_EXERCISE_REMOVE=PASS DESKTOP_SESSION_EXERCISE_REMOVE=PASS ``` Accepted repetition examples: ```text 5x10 4,5,6,7,8,9,10,9,8,7,6,5,4 4..10..4 ``` A heterogeneous mobile session is persisted as ordered `performed_sets`. The desktop does not invent `target_sets`, `target_reps` or `target_duration_seconds` for actual-only mobile observations. On Android, an exercise already added to the current session can be removed before saving the session. On the desktop TUI, session editing already supports: ```text d supprimer ``` for removing the selected exercise from a current or persisted session draft. The database replacement remains transactional. `TRAINLOG_FORMAT_V1` remains frozen and unchanged. ## Shared bidirectional synchronization v1 Validated architecture: ```text Android local write -> automatic mobile snapshot Android "Synchroniser maintenant" -> trainlog-sync-request-v1.json trainlog-syncd -> shared C synchronization engine -> Android → PC mobile import -> PC → Android catalog publish -> trainlog-sync-receipt-v1.json Android -> receipt matched by request_id -> PC catalog applied locally -> final result displayed ``` The ncurses TUI and `trainlog-syncd` call the same `trainlog_sync_run()` implementation. Direct libmtp remains mandatory. No filesystem mount and no SQLite-file synchronization are introduced. ### Concurrency The shared engine owns: ```text $XDG_DATA_HOME/trainlog/sync.lock ``` A TUI-triggered transaction waits for the lock. Daemon request polling is non-blocking and retries later. ### Sync history Every actual synchronization transaction creates: ```text $XDG_DATA_HOME/trainlog/sync_runs/sy_*.json $XDG_DATA_HOME/trainlog/sync_runs/sy_*.txt ``` and appends a compact entry to: ```text $XDG_DATA_HOME/trainlog/sync_history.log ``` The TUI behaves like: ```text git log ↑/↓ select synchronization git show Enter opens structured detail ``` Legacy three-field history entries remain readable but have no structured detail file. ### Android request and receipt Request: ```text format = trainlog-sync-request version = 1 ``` Receipt: ```text format = trainlog-sync-receipt version = 1 ``` The receipt carries the originating `request_id`, a generated `sync_id`, status, summary and synchronization counts. Android ignores a receipt for a different request ID. ### User service Install/refresh the user service with: ```text bash tools/install_syncd_user.sh ``` No root privilege is required. ### Status ```text COMMON_SYNC_ENGINE=PASS TUI_SYNC_LOG_SHOW=PASS TRAINLOG_SYNCD=PASS ANDROID_TRIGGERED_SYNC=PASS ANDROID_SYNC_RECEIPT=PASS BIDIRECTIONAL_SYNC_V1=PASS ``` Frozen `TRAINLOG_FORMAT_V1` remains unchanged.