docs(train): Health Connect, written and read (ADR 0030, D31)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-28 16:33:43 +01:00
commit d1dc833aa6
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -1,6 +1,6 @@
# ADR 0030: Halkyon Train, what it does and where its data comes from
- **Status:** decided 27–28 Oct 2026, by the developer (D1–D30 below). The shell (D12)
- **Status:** decided 27–28 Oct 2026, by the developer (D1–D31 below). The shell (D12)
is built; recording (D13–D28) is built on Android, iOS waiting for a Mac; Health
Connect (D29) comes next.
- **Numbering:** 0025 is held by the federation draft (stashed), 0026–0029 by the .NET
@ -484,6 +484,39 @@ Strava. What was found, 27 Oct:
again replaces, deleted deletes) and each record's origin. Its logic is tested on the
JVM, the Dart side on a fake platform; Apple Health needs Swift of its own later,
as iOS waits for a Mac anyway (D25).
- **D31 Health Connect, from turning it on to the workouts** (the developer's; 28 Oct):
- *Turning it on*: a *Health Connect* row in the account sheet (Train has no settings
yet) opens a screen with two switches, both off: *Write my workouts* and *Read from
other apps*. They are this phone's, as Health Connect is, kept per person in their
folder (`health_connect.json`, with what was written and the changes token), not
synced. Each permission is asked as its switch is turned on. Built into Android from
14; below, the row says it needs the Health Connect app. `connect-client` 1.1.0, the
stable one. The rationale Health Connect asks for is a short screen of Train's,
linked from the system's permission dialog.
- *Writing*: every workout of the person this phone holds, synced and earlier ones too;
Train's workout ID is the record's client ID and a counter its version, so an edit
replaces and a deletion deletes; imported ones are never written back. As a workout
is saved, edited or deleted, and as the app opens (what a sync brought). The session
(type, name, notes, its pauses as pause segments), its route with altitude and
accuracy, heart rate, speed and the rower's watts as samples, distance and elevation
gained as totals. No calories (no weight, no energy model yet); stroke rate has no
type there.
- *Reading*: the screen lists each app that wrote workouts there (Gadgetbridge,
Christopeit Connect), each with its own switch, off, new ones appearing as they
write; Train's permissions are per type, the filtering by app its own. As the app
opens and comes to the front, through the changes token; nothing in the background.
Turning it on asks for the history too, so older than 30 days comes in; refused, the
last 30 days only. Each session becomes a workout of Train's own store (D7: sealed,
synced), named by its app, with the samples Health Connect holds for it (heart rate,
distance, power). Another app's route asks consent per session, so none comes by
itself: an imported workout's page offers *Bring in the track*. An imported one's
name and notes can be edited, and it can be deleted, remembered on this phone so it
doesn't come back. Doubles: a session mostly overlapping one of Train's own of the
same kind isn't added; where Train's has no heart rate, the other's is added to it,
otherwise it is left out. Weight and resting heart rate wait for what uses them
(calories, zones), with the synced model they need.
- No migration: an imported workout is a workout, its origin one more field of the
sealed, versioned payload.
## Consequences