219 lines
3.7 KiB
Markdown
219 lines
3.7 KiB
Markdown
# Trainlog Exchange Format v1
|
|
|
|
## 1. Status
|
|
|
|
This document defines the initial Trainlog v1 exchange contract.
|
|
|
|
Until explicitly marked `FROZEN`, incompatible changes are allowed during early development.
|
|
|
|
Once frozen, incompatible changes require a new format version.
|
|
|
|
## 2. Encoding
|
|
|
|
A Trainlog exchange document is:
|
|
|
|
- JSON;
|
|
- UTF-8;
|
|
- one top-level JSON object.
|
|
|
|
## 3. Required top-level fields
|
|
|
|
```json
|
|
{
|
|
"format": "trainlog",
|
|
"version": 1,
|
|
"exercises": [],
|
|
"session": {}
|
|
}
|
|
```
|
|
|
|
### `format`
|
|
|
|
Must equal:
|
|
|
|
```text
|
|
trainlog
|
|
```
|
|
|
|
### `version`
|
|
|
|
Integer schema version.
|
|
|
|
For this document:
|
|
|
|
```text
|
|
1
|
|
```
|
|
|
|
## 4. Exercise catalog entries
|
|
|
|
Each exercise entry contains:
|
|
|
|
```json
|
|
{
|
|
"exercise_id": "leg_press",
|
|
"name": "Presse à cuisses"
|
|
}
|
|
```
|
|
|
|
### `exercise_id`
|
|
|
|
Stable identifier.
|
|
|
|
Rules:
|
|
|
|
- non-empty;
|
|
- unique within the document;
|
|
- treated as identity;
|
|
- must not change merely because the display name changes.
|
|
|
|
### `name`
|
|
|
|
Human-readable display name.
|
|
|
|
The TUI may update the local display name later without changing `exercise_id`.
|
|
|
|
## 5. Session
|
|
|
|
Required fields:
|
|
|
|
- `session_id`;
|
|
- `started_at`;
|
|
- `ended_at`;
|
|
- `exercises`.
|
|
|
|
Optional body fields may include:
|
|
|
|
- `body_weight_kg`;
|
|
- `measurements`.
|
|
|
|
## 6. Session identifier
|
|
|
|
Example:
|
|
|
|
```text
|
|
20260905-183412-a84c
|
|
```
|
|
|
|
The exact generation algorithm is implementation-defined in v1.
|
|
|
|
The invariant is uniqueness.
|
|
|
|
The TUI must enforce uniqueness at import.
|
|
|
|
## 7. Timestamps
|
|
|
|
Timestamps use ISO 8601 with an explicit UTC offset.
|
|
|
|
Example:
|
|
|
|
```text
|
|
2026-09-05T18:34:12+02:00
|
|
```
|
|
|
|
The timezone offset is part of the serialized value.
|
|
|
|
The TUI must not silently reinterpret a timestamp as local time without using the encoded offset.
|
|
|
|
## 8. Workout exercise entry
|
|
|
|
Example:
|
|
|
|
```json
|
|
{
|
|
"exercise_id": "leg_press",
|
|
"rest_seconds": 60,
|
|
"target": {
|
|
"sets": 4,
|
|
"reps": 5,
|
|
"weight_kg": 80
|
|
},
|
|
"sets": [
|
|
{ "reps": 5, "weight_kg": 80 },
|
|
{ "reps": 5, "weight_kg": 80 },
|
|
{ "reps": 5, "weight_kg": 80 },
|
|
{ "reps": 3, "weight_kg": 80 }
|
|
]
|
|
}
|
|
```
|
|
|
|
The `target` object describes the intended work.
|
|
|
|
The `sets` array describes what was actually performed.
|
|
|
|
These two concepts must remain distinct.
|
|
|
|
## 9. Timed exercises
|
|
|
|
Timed exercises such as planks use `duration_seconds`.
|
|
|
|
Example:
|
|
|
|
```json
|
|
{
|
|
"exercise_id": "plank",
|
|
"rest_seconds": 60,
|
|
"target": {
|
|
"sets": 3,
|
|
"duration_seconds": 45
|
|
},
|
|
"sets": [
|
|
{ "duration_seconds": 45 },
|
|
{ "duration_seconds": 45 },
|
|
{ "duration_seconds": 38 }
|
|
]
|
|
}
|
|
```
|
|
|
|
A set may represent repetitions or duration.
|
|
|
|
The schema forbids an empty set object.
|
|
|
|
## 10. Body measurements
|
|
|
|
The initial v1 measurement object supports named measurements in centimeters.
|
|
|
|
Example:
|
|
|
|
```json
|
|
{
|
|
"measurements": {
|
|
"waist_cm": 91.0,
|
|
"chest_cm": 104.0,
|
|
"left_arm_cm": 35.0,
|
|
"right_arm_cm": 35.0,
|
|
"left_thigh_cm": 58.0,
|
|
"right_thigh_cm": 57.0
|
|
}
|
|
}
|
|
```
|
|
|
|
The initial schema intentionally uses explicit field names rather than arbitrary free-form keys.
|
|
|
|
Additional measurements may be added before v1 is frozen.
|
|
|
|
## 11. Idempotent import
|
|
|
|
The TUI must treat `session_id` as a uniqueness key.
|
|
|
|
If a session has already been imported:
|
|
|
|
- do not create another session;
|
|
- do not duplicate its sets;
|
|
- report that the session already exists.
|
|
|
|
## 12. New exercises
|
|
|
|
When Android exports a session containing an exercise unknown to the TUI:
|
|
|
|
1. the exercise must exist in the top-level `exercises` array;
|
|
2. its `exercise_id` must be valid;
|
|
3. its `name` must be non-empty;
|
|
4. the TUI imports the catalog entry;
|
|
5. the session may then reference that exercise.
|
|
|
|
## 13. Unknown fields
|
|
|
|
Before v1 is frozen, implementations may reject unknown fields during development to catch mistakes early.
|
|
|
|
The final forward-compatibility policy will be frozen explicitly before release.
|