# Architecture ## 1. Purpose Trainlog separates capture from analysis. The Android application is optimized for fast data entry during training. The TUI is optimized for durable storage, inspection, statistics, and visualization. ## 2. Components ### Android client Responsibilities: - start a session; - record the session start timestamp; - select an existing exercise; - create a new exercise; - record workout targets; - record actual performed sets; - record rest duration; - record body data; - record the session end timestamp; - export one valid Trainlog JSON document. Non-responsibilities: - long-term analytics; - canonical history; - complex graphing; - cloud synchronization. ### Exchange format The exchange format is the compatibility boundary between Android and the TUI. It is: - JSON; - UTF-8; - versioned; - self-contained enough to import newly created exercises; - designed for idempotent import. ### TUI Responsibilities: - import Trainlog JSON; - reject malformed or incompatible input cleanly; - deduplicate sessions; - maintain the canonical exercise catalog; - create workouts directly from the terminal; - maintain SQLite history; - calculate progress metrics; - render graphs and summaries; - export data when needed. ### SQLite store SQLite is the canonical local history. The database must use: - foreign keys; - uniqueness constraints; - schema versioning; - explicit migration rules. ## 3. Data flow ```text Android | | export v Trainlog JSON | | import + validation v TUI application | | persistence v SQLite ``` ## 4. Identity rules Exercises have: - a stable machine identifier: `exercise_id`; - a mutable display name: `name`. The display name is not the identity. Sessions have: - a globally unique `session_id`. A second import of the same `session_id` must not duplicate the session. ## 5. Separation rules for the TUI The C17 TUI will be split into layers: ```text ncursesw rendering | v TUI state / navigation | v application services | +---- exchange-format parser | +---- analytics | v SQLite persistence ``` The rendering layer must not own business rules. The persistence layer must not depend on ncurses. ## 6. Error philosophy Trainlog must prefer explicit failure over silent corruption. Examples: - malformed JSON: reject import with a precise error; - unsupported format version: reject import; - duplicate session: report already imported, do not duplicate; - unknown exercise: import it when valid catalog data is present; - incomplete active session: preserve it explicitly rather than silently inventing an end time. ## 7. Direct Android USB/MTP transport The Linux side does not require the Android device to be mounted as a normal filesystem. Transport layering is: ```text Android USB file-transfer mode | v libudev physical-device discovery | | bus number + device number v libmtp exact raw-device open | v Android internal MTP storage ``` This avoids GVFS/FUSE mount state and manual mount/unmount lifecycle management. `libudev` owns physical-device discovery. `libmtp` owns storage and object operations. The JSON exchange layer remains above both and stays independent from USB/MTP backend details. Current transport foundation supports folder creation, file upload, folder listing, file download, and verified byte-for-byte roundtrip. ## Profile-aware activity architecture Trainlog has two distinct actual-work persistence paths: ```text SET-based exercise session_exercises | +--> performed_sets [0..N] CONTINUOUS exercise session_exercises | +--> continuous_activity [exactly 1] ``` The two paths must remain semantically distinct. Catalog metadata determines future entry forms. Session-exercise snapshot metadata determines historical rendering/editing. The Android client must consume the same catalog profile metadata rather than maintaining an independent exercise-type system.