Add profile-aware continuous exercise tracking

This commit is contained in:
fy59 2026-09-06 13:16:17 +02:00
parent db349c609d
commit aa82e0f7fa
9 changed files with 646 additions and 0 deletions

View file

@ -153,3 +153,20 @@ Next:
- exposed PC -> Android canonical exercise-catalog direction; - exposed PC -> Android canonical exercise-catalog direction;
- kept frozen Trainlog session JSON v1 unchanged. - kept frozen Trainlog session JSON v1 unchanged.
<!-- TRAINLOG_SYNC_FINAL_CHANGELOG _END --> <!-- TRAINLOG_SYNC_FINAL_CHANGELOG _END -->
<!-- TRAINLOG_PROFILE_AWARE_CHANGELOG -->
### Profile-aware and continuous exercise tracking
- added set-based versus continuous recording organization;
- added speed and distance supplemental-field metadata;
- added profiled catalog creation API;
- added profile-aware exercise creation in the TUI;
- added inline profiled exercise creation while recording a session;
- migrated SQLite persistence to schema v4;
- added one-to-one continuous activity persistence;
- kept continuous activities out of `performed_sets`;
- added profile-aware session detail loading;
- added continuous duration/speed/distance history display;
- made continuous TUI duration entry explicitly minute-based;
- preserved frozen session JSON v1 unchanged.
<!-- TRAINLOG_PROFILE_AWARE_CHANGELOG _END -->

View file

@ -284,3 +284,66 @@ and no set count.
Creating an exercise directly inside session entry must configure this metadata Creating an exercise directly inside session entry must configure this metadata
before adding it to the catalog/session. before adding it to the catalog/session.
<!-- TRAINLOG_ANDROID_PROFILE_AWARE_ENTRY _END --> <!-- TRAINLOG_ANDROID_PROFILE_AWARE_ENTRY _END -->
<!-- TRAINLOG_ANDROID_PROFILE_CURSOR -->
## Android implementation cursor
Android is the next implementation area.
The application must share Trainlog's visual language with the TUI.
Theme direction:
```text
dark background
cyan/teal Trainlog accent
yellow active/focus role
green success
red error
```
Launcher icon:
```text
T
```
Only the letter `T`, using Trainlog theme colors.
Required recording sections:
```text
Séance
Exercice
Mensurations
```
Session recording must allow creating a new exercise inline without leaving the
session flow.
Android forms are driven by the same profile metadata as desktop:
```text
recording_mode
tracking_mode
data_fields
```
Examples:
```text
SETS + REPS
sets / reps / load / rest
SETS + DURATION
sets / duration / optional load / rest
CONTINUOUS + DURATION + SPEED_KMH
duration / speed
```
The app must never infer an input form from an exercise display name.
Initial development uses fictitious records. Test/development data is removed
before normal production use starts.
<!-- TRAINLOG_ANDROID_PROFILE_CURSOR _END -->

View file

@ -170,3 +170,30 @@ from USB/MTP backend details.
Current transport foundation supports folder creation, file upload, folder Current transport foundation supports folder creation, file upload, folder
listing, file download, and verified byte-for-byte roundtrip. listing, file download, and verified byte-for-byte roundtrip.
<!-- TRAINLOG_DIRECT_MTP_ARCHITECTURE _END --> <!-- TRAINLOG_DIRECT_MTP_ARCHITECTURE _END -->
<!-- TRAINLOG_PROFILE_AWARE_ARCHITECTURE -->
## 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.
<!-- TRAINLOG_PROFILE_AWARE_ARCHITECTURE _END -->

View file

@ -304,3 +304,57 @@ No name-based migration is allowed.
Continuous actual activity data is stored separately from `performed_sets`; Continuous actual activity data is stored separately from `performed_sets`;
Trainlog will not manufacture a fake one-set workout. Trainlog will not manufacture a fake one-set workout.
<!-- TRAINLOG_EXERCISE_DATA_MODEL_V1 _END --> <!-- TRAINLOG_EXERCISE_DATA_MODEL_V1 _END -->
<!-- TRAINLOG_SCHEMA_V4_CONTINUOUS -->
## Schema v4 — continuous exercise persistence
Current database schema:
```text
TRAINLOG_DATABASE_SCHEMA_VERSION = 4
PRAGMA user_version = 4
```
Relevant profile metadata is stored in both:
```text
exercises
session_exercises
```
`session_exercises` snapshots:
```text
recording_mode
data_fields
```
Continuous actual activity data is stored one-to-one in:
```text
continuous_activity
```
Columns:
```text
session_exercise_row_id
duration_seconds
speed_kmh nullable
distance_km nullable
```
For a valid continuous activity:
```text
target_sets NULL
target_reps NULL
target_duration_seconds NULL
load_mode none
rest_seconds 0
target_weight_kg NULL
performed_sets none
```
Schema migration never infers profile information from exercise names.
<!-- TRAINLOG_SCHEMA_V4_CONTINUOUS _END -->

View file

@ -193,3 +193,51 @@ data_fields
7. Android uses the same model 7. Android uses the same model
8. design session exchange v2 8. design session exchange v2
``` ```
<!-- TRAINLOG_PROFILE_AWARE_IMPLEMENTED -->
## Implemented checkpoint
The profile-aware exercise model is now implemented in the C model, SQLite
persistence and TUI.
Current canonical rules:
```text
recording_mode = SETS | CONTINUOUS
tracking_mode = REPS | DURATION
known data_fields:
SPEED_KMH
DISTANCE_KM
```
Valid model-v1 combinations:
```text
SETS + REPS
SETS + DURATION
CONTINUOUS + DURATION
```
Continuous exercise actual data is persisted as one `continuous_activity`
record rather than a performed-set list.
A continuous activity never manufactures a one-set representation.
The TUI asks continuous duration in **minutes**, converts to seconds, and stores
seconds internally.
Example:
```text
Marche
CONTINUOUS + DURATION + SPEED_KMH
TUI entry:
Durée (minutes)
Vitesse km/h
```
Historical session rows snapshot recording metadata and are not reinterpreted
when catalog metadata later changes.
<!-- TRAINLOG_PROFILE_AWARE_IMPLEMENTED _END -->

View file

@ -0,0 +1,298 @@
# Profile-aware continuous exercise checkpoint
## Status
```text
EXERCISE_DATA_MODEL_V1=IMPLEMENTED
DATABASE_SCHEMA_V4=IMPLEMENTED
PROFILED_CATALOG_API=IMPLEMENTED
PROFILE_AWARE_EXERCISE_CREATION=IMPLEMENTED
CONTINUOUS_ACTIVITY_PERSISTENCE=IMPLEMENTED
CONTINUOUS_ACTIVITY_DETAIL_DISPLAY=IMPLEMENTED
CONTINUOUS_DURATION_MINUTES_UI=IMPLEMENTED
TRAINLOG_FORMAT_V1=FROZEN
ANDROID_APP=NEXT
```
The implementation now distinguishes set-based exercises from continuous
activities.
This checkpoint must be treated as the current architecture by future agents.
## Core model
Exercise catalog metadata:
```text
recording_mode = SETS | CONTINUOUS
tracking_mode = REPS | DURATION
data_fields = bounded supplemental-field bit mask
```
Known supplemental fields:
```text
SPEED_KMH
DISTANCE_KM
```
Initial valid combinations:
```text
SETS + REPS
SETS + DURATION
CONTINUOUS + DURATION
```
`CONTINUOUS + REPS` is invalid.
Behavior is driven by catalog metadata, never by exercise-name heuristics.
## Examples
```text
Presse à cuisses
SETS + REPS
Gainage
SETS + DURATION
Marche
CONTINUOUS + DURATION
SPEED_KMH
Vélo
CONTINUOUS + DURATION
SPEED_KMH | DISTANCE_KM
```
## Historical stability
Catalog metadata controls future entry.
Each `session_exercises` row stores a snapshot of:
```text
recording_mode
data_fields
```
Historical sessions must use their snapshot rather than current catalog
metadata.
Existing rows migrated from older schemas remain `SETS` unless explicitly
changed for future entries.
No migration guesses behavior from names such as `Marche`.
## SQLite schema v4
Current schema version:
```text
PRAGMA user_version = 4
```
Relevant tables:
```text
exercises
sessions
session_exercises
performed_sets
continuous_activity
body_observations
```
`continuous_activity` is one-to-one with a continuous session exercise and
contains:
```text
duration_seconds
speed_kmh nullable
distance_km nullable
```
A continuous activity has:
```text
target_sets = NULL
target_reps = NULL
target_duration_seconds = NULL
load_mode = none
rest_seconds = 0
performed_sets rows = 0
```
Trainlog must not create a fake one-set representation for a continuous
activity.
## TUI behavior
Set-based exercise:
```text
Charge
Séries prévues
Répétitions or duration target
Repos
Séries réellement faites
```
Continuous exercise:
```text
Durée (minutes)
optional configured supplemental fields
```
For `Marche + SPEED_KMH`:
```text
Durée (minutes)
Vitesse km/h
```
A bare duration value in this continuous form is minutes.
Example:
```text
15 -> 15 minutes -> 900 seconds persisted
```
SQLite still stores durations in seconds.
## Session detail behavior
Set-based history uses the existing sets presentation.
Continuous history displays its actual continuous values.
Example:
```text
Exercice 1/1 — Marche
Mode : continu
Durée : 15 min
Vitesse : 7.0 km/h
Réalisé : activité continue
```
It must never display:
```text
0 séries
0 série × 0 s
aucune série réalisée
```
for a valid continuous activity.
## Catalog creation
The lower-level catalog supports explicit profile creation.
Current API:
```text
trainlog_catalog_create_exercise_profiled(...)
```
Legacy exercise creation remains available and means:
```text
recording_mode = SETS
data_fields = 0
```
TUI creation from both:
```text
Exercices page
session inline creation
```
supports selecting continuous mode and supplemental fields.
## Frozen exchange boundary
Trainlog session JSON v1 remains frozen.
Continuous metrics are not forced into v1.
Trainlog must never:
- hide speed/distance in notes;
- synthesize a fake set to make continuous data fit v1;
- silently drop continuous metrics.
A future versioned exchange contract will carry profile-aware session data.
## Android implementation cursor
The next implementation area is the Android client.
Android must use the same model:
```text
recording_mode
tracking_mode
data_fields
```
Required top-level recording sections:
```text
Séance
Exercice
Mensurations
```
Inside session recording, the user must be able to create a new exercise
without leaving the session flow.
Android visual identity must match the TUI:
```text
dark background
Trainlog cyan/teal accent
yellow active/focused border role
green success
red error
```
The Android launcher icon is intentionally minimal:
```text
T
```
using the Trainlog theme colors.
Initial Android development uses fictitious data. The development database is
purged before normal use begins.
## Validation checkpoint
Normal build/test checkpoint after continuous activity work:
```text
15 tests expected
git diff --check clean
```
The important functional manual checks are:
```text
create continuous Marche + speed
record duration in minutes
persist duration as seconds
persist speed in continuous_activity
no performed_sets rows for continuous activity
reopen session detail
display duration + speed as continuous activity
```

View file

@ -309,3 +309,43 @@ supplemental fields: SPEED_KMH | DISTANCE_KM
Existing schema-v2 data migrates conservatively to `SETS`. Existing schema-v2 data migrates conservatively to `SETS`.
<!-- TRAINLOG_EXERCISE_DATA_MODEL_ROADMAP _END --> <!-- TRAINLOG_EXERCISE_DATA_MODEL_ROADMAP _END -->
<!-- TRAINLOG_PROFILE_AWARE_ROADMAP_FINAL -->
## 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.
<!-- TRAINLOG_PROFILE_AWARE_ROADMAP_FINAL _END -->

View file

@ -190,3 +190,35 @@ The hardware probe is intentionally separate from the normal automated test
suite because CI is not expected to have a connected unlocked Android MTP suite because CI is not expected to have a connected unlocked Android MTP
device. device.
<!-- TRAINLOG_MTP_VALIDATION _END --> <!-- TRAINLOG_MTP_VALIDATION _END -->
<!-- TRAINLOG_CONTINUOUS_TEST_CHECKPOINT -->
## Profile-aware / continuous validation
Expected normal test suite after this checkpoint:
```text
15 tests
```
Coverage added around:
```text
exercise profile schema
profiled catalog creation
schema migration
continuous session persistence
continuous session detail loading
```
Manual TUI validation includes:
```text
Marche configured CONTINUOUS + DURATION + SPEED_KMH
entry asks duration minutes + speed
no sets/rest/load prompts
continuous_activity row persisted
performed_sets count remains zero
history reopens as continuous
duration and speed render correctly
```
<!-- TRAINLOG_CONTINUOUS_TEST_CHECKPOINT _END -->

View file

@ -696,3 +696,70 @@ Continuous exercises do not display a set count.
`Marche` will use the continuous form only after its catalog metadata is `Marche` will use the continuous form only after its catalog metadata is
explicitly changed; behavior is never inferred from its name. explicitly changed; behavior is never inferred from its name.
<!-- TRAINLOG_PROFILE_AWARE_ENTRY _END --> <!-- TRAINLOG_PROFILE_AWARE_ENTRY _END -->
<!-- TRAINLOG_CONTINUOUS_TUI_IMPLEMENTED -->
## Continuous exercise TUI — implemented
Exercise creation supports explicit organization:
```text
1 séries
2 continu
```
Continuous creation forces duration tracking in model v1 and can enable:
```text
speed
distance
```
The same creation path is available:
```text
from Exercices page
inline while recording a session
```
Session entry is profile-aware.
Set-based exercises retain:
```text
charge
sets
reps/duration
rest
performed sets
```
Continuous exercises display only their relevant fields.
For `Marche + SPEED_KMH`:
```text
Durée (minutes)
Vitesse km/h
```
Bare continuous duration input is interpreted as minutes.
Example:
```text
15 -> 15 min -> 900 seconds in SQLite
```
Session detail is also profile-aware.
Continuous detail example:
```text
Mode : continu
Durée : 15 min
Vitesse : 7.0 km/h
Réalisé : activité continue
```
Do not render set-oriented labels for a valid continuous activity.
<!-- TRAINLOG_CONTINUOUS_TUI_IMPLEMENTED _END -->