trainlog/docs/exchange_format.md
2026-09-05 19:05:07 +02:00

3.7 KiB

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

{
  "format": "trainlog",
  "version": 1,
  "exercises": [],
  "session": {}
}

format

Must equal:

trainlog

version

Integer schema version.

For this document:

1

4. Exercise catalog entries

Each exercise entry contains:

{
  "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:

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:

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:

{
  "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:

{
  "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:

{
  "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.