Commit graph halkyon/docs/adr/0030-halkyon-train.md
Author SHA1 Message Date
2acc9fc6b6
docs(train): setting up and keeping the Loop (ADR 0030, D34)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-30 15:52:55 +01:00
5ace60c50b
docs(train): the body's figures in a sealed, synced profile (ADR 0030, D33)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-30 15:52:54 +01:00
e62cf7695f
docs(train): the Loop's own days first (ADR 0030, D32)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-30 15:52:54 +01:00
8fb2dbf35e
feat(train): other apps' workouts read from Health Connect (ADR 0030, D31)
Read from other apps, off until turned on, asks for reading with the
history; the apps that wrote sessions are listed, each off. One turned
on brings its sessions, earlier ones at once and later ones through the
changes token as the app opens and comes to the front: each a workout
of Train's own store, sealed and synced, its origin in its payload, its
heart rate, pauses and a row's power as series, named by its app in the
list and on its page. A double of a workout Train has brings at most its
heart rate; one deleted in Train stays out; a track comes only when
asked for on the page, Health Connect asking for consent. What came from
Health Connect is never written back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-30 15:52:53 +01:00
09f61c3b0a
feat(train): workouts written to Health Connect (ADR 0030, D31)
A Health Connect row in the account sheet opens its screen: Write my
workouts, off until turned on, Health Connect asking as it is. Then
every workout this phone holds is written there (its session with its
pauses and route, heart rate, speed and the rower's watts as samples,
distance and elevation gained), and written again or deleted as it
changes, here or through a sync, and as the app opens. Client IDs come
from the workout's ID, a version counts up with each change; what was
written is kept in the person's health_connect.json, never synced. A
permission taken back in Health Connect turns writing off and says so;
Health Connect's link to Train's rationale opens a screen of its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-30 15:52:53 +01:00
d1dc833aa6
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>
2026-10-30 15:52:52 +01:00
2fae7e4f3b
docs(train): Health Connect through Kotlin of our own (ADR 0030, D30)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-30 15:52:52 +01:00
0dbe81b336
docs(train): Health Connect before the Loop (ADR 0030, D29)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-30 15:52:52 +01:00
f3d08e4e69
feat(train): rowing on the WP 5000 over FTMS (ADR 0030, D28)
Rowing indoors is offered again, with the rower chosen on the Record
sheet: a sheet looks for Fitness Machine Service rowers, and the one
chosen is remembered in the person's folder. The recorder service
connects to it as to the strap (one NotifyLink for both), parses
FTMS's Rower Data in Kotlin (tested on the JVM on frames written from
the specification and D8's), counts its totals from its first frame,
pauses 5 s after the last stroke, and gets the connectedDevice type. A
row's screen shows its metres, split per 500 m, stroke rate and watts;
its page its average split, stroke rate and power, the highest power,
its strokes, splits per 500 m and charts of the split, stroke rate and
power. Not yet tried on the WP 5000.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-30 15:52:51 +01:00
de5fd43e06
feat(train): the heart-rate strap, recorded by the service itself (ADR 0030, D25-D27)
The Record sheet gains a Heart rate row: a sheet looks for straps
advertising the Heart Rate Service, the nearest first, once nearby
devices are allowed, and the one chosen is remembered in the person's
folder; it records with every workout unless switched off for one.
The recorder service connects to it with Android's own Bluetooth API,
no package (D26), parses Heart Rate Measurement in Kotlin (tested on
the JVM), writes each rate and the strap's state to the recording
file, and asks for a lost strap again for as long as it records. The
recording screen shows the heart rate, and "Strap lost" while it is
gone; a workout's page shows the heart's average and highest rate,
each split's average and its chart.

iOS's recording waits for a Mac (D25), after the rower.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-30 15:52:51 +01:00
405577050d
feat(train): the workouts listed, and each one's page with its map (ADR 0030, D22-D24)
Home lists the person's workouts newest first under their months, each
with its activity's icon, its name (else the time of day's and the
activity's), when it started, its distance, moving time and pace or
speed; those this device can't open come last, locked. A workout's page
shows its track on the instance's map source (flutter_map with
vector_map_tiles, pinned at 9.0.0-beta.13), in pieces between its
pauses, start and end marked; its totals with the elevation gained;
its splits per kilometre on foot, per 5 km cycling; pace or speed and
elevation over the distance, drawn by the app's own painter; and what
the map's source sees. Its name and notes are edited there; deleting
asks first and can be undone for a moment from home (halkyon_app's
DelayedDeletions, TrainPerson.deletions).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-30 15:52:50 +01:00
dd778cb2cd
feat(train): the instance names its map source (ADR 0030, D11, D22)
Train's first endpoint of its own, map.source, names the map workouts
are drawn on, for people signed in only, as a provider's key may be in
its style's address: OpenFreeMap's Liberty style where the operator sets
nothing, else MAP_STYLE_URL (HTTPS only) with MAP_SOURCE_NAME,
MAP_ATTRIBUTION and MAP_ABOUT_URL. A model without a table: no
migration. D22 is recorded in ADR 0030.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-30 15:52:49 +01:00
7df1cb1740
feat(train): recording a workout on Android (ADR 0030, D13, D16, D19, D21)
RecorderService, a foreground service of the types location and health
started from the open app, takes fixes from LocationManager's GPS and
steps from the step detector, pauses by itself (AutoPause, tested on the
JVM) and appends each sample to the person's recording file, flushed
every five seconds. MainActivity asks for location, notifications and,
for auto-pause on foot, physical activity, never background location
(D19), and bridges the service to the app.

The app follows the file's lines with RecordingBloc and shows the moving
time, the distance, the pace or speed and GPS's state; the notification
shows the time and the distance with Pause or Resume. Stop is held down;
a stopped recording is saved as a workout and its series, or discarded
after asking, and one cut off or left unsaved is offered as the app
opens. Home has the Record button where the device records.

Three choices of the implementation's are recorded under D21 for review:
rowing waits for 5f, auto-pause on foot without steps goes by GPS speed
(below 2 km/h, on above 3 km/h), and Stop's hint shows above the button.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-30 15:52:49 +01:00
aab25bf932
feat(train): a recording's file read and saved as a workout (ADR 0030, D18, D20)
The recorder's file is text, one sample a line (D18): positions with
their accuracy, heart rates, steps, pauses and the end. Reading it drops
a last line cut short and skips any line it can't read; a file without
its end is a recording cut off, which ends at its last sample.

A recording becomes its workout and series: every fix kept with its
accuracy (D20), the distance through those within 20 m and never across
a pause, the moving time without the pauses (D16), each kind of sample
in chunks of ten minutes (D14). D18 to D20 are recorded in ADR 0030.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-30 15:52:49 +01:00
ff91f62303
docs(adr): ADR 0030's D13–D17, how Train records
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-30 15:52:46 +01:00
2fd39352fc
docs(adr): ADR 0030 (Halkyon Train) and ADR 0031 (the apps' icons)
ADR 0030 records what Train does and where its data comes from: running,
cycling and rowing, in the order recording, intervals, routes, music; the
Polar Loop taken over through Polar's BLE SDK; music from Jellyfin;
devices on phones through flutter_reactive_ble; Health Connect both ways
through the health package; every row sealed; the WP 5000 an FTMS rower,
as scanned; no WaterRower USB driver for now; the Loop's firmware from
Polar directly or through the instance; flutter_map, the instance's map
source, BRouter as a sidecar, offline use and working with CoMaps; and
Train's shell after Todo's.

ADR 0031 gives the apps' icons one family: Lucide glyphs fading from
cobalt through a colour between into each app's own, Learn's graduation
cap, Todo's list, Train's bike, Meditate's circle-dot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-30 15:52:44 +01:00