docs(meditate): ADR 0038, what Halkyon Meditate does and how it looks

Proposed by the agent on 3 Nov 2026, the developer having asked to go on
without their interjection: three tabs (Today, Explore, Profile) on
Todo's and Train's frame, fern washes, Halkyon's own practices as text
cues with a silent timer, bells synthesised for the app, a sitting over
the tabs, sealed and synced settings and sittings, the figures worked
out on the device, development ports of its own. Every decision is the
agent's recommendation, to be confirmed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-11-03 09:25:30 +01:00
commit 504aca3e68
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -0,0 +1,171 @@
# ADR 0038: Halkyon Meditate, what it does and how it looks
- **Status:** proposed 3 Nov 2026 by the agent, the developer having asked to "continue as
far as possible without my interjection"; every decision below is the agent's
recommendation unless it says the developer's, and is put to the developer with the
first version. Built the same day: steps 1 to 4.
- **Builds on:** ADR 0033 (Todo's shell, which Meditate copies as Train copied Todo's,
ADR 0030, D12); ADR 0030, D7, D14, D15 and D61 (sealed rows, the person's key, What's
new); ADR 0031, D5 and D7 (Meditate's icon: Lucide's `sprout`, cobalt through pacific
into fern); ADR 0014 and 0016 (the module and the register).
## Context
On 3 Nov 2026 the developer asked for Halkyon Meditate, following "the basic approach as
the other applications": the bar and the rail, the theme, the settings and account
screens; and to look at eleven screenshots of 7Mind's Android app (1 Nov 2026,
`~/Downloads/7mind-screenshots/`). On 2 Nov they chose fern as Meditate's backdrop
(ADR 0031, D5). What the screenshots show:
- **Three tabs**: *For you*, *Explore*, *Profile*.
- **For you**: a greeting with the person's name; a band of newly added content, each a
card with its kind (Meditation, Sound, Sleep Story, Movement, Morning Stories, a
course of several tracks), its length and its speaker; *Your favorites* and *Recently
played*, each a row of the same cards with *More*.
- **Explore**: a search field, four goals as large tiles (reduce stress, sleep better,
learn to meditate, take care of yourself), categories as a row of icons (autogenic
training, breathing, dream journeys, knowledge, meditation, morning stories, movement,
progressive muscle relaxation, routines, sleep stories, sounds), and topics as a list
of cards with a line each (sleep, strength, personal development, relaxation, feelings
and relationships, daily life, silence and sounds).
- **Profile**: minutes, sessions and days as large figures; the week's days as dots with
the days in a row; Favorites, Downloads and *Your path*; then Account, Subscription,
Notifications, Language, Appearance, the streak's visibility, help, legal information.
- No player is shown; 7Mind's content is recorded speech by its speakers, behind a
subscription.
Halkyon has no recorded meditations, and none may be taken from 7Mind (its content, texts,
names and pictures are its own). Everything below is Halkyon's own.
## Decisions (3 Nov 2026, the agent's, to be confirmed)
- **D1 Meditate's frame is Todo's and Train's** (ADR 0033, D1; ADR 0030, D60): go_router
with a `StatefulShellRoute`, each tab keeping its own stack, halkyon_ui's
`NavigationFrame` (the bar floating above the foot below medium windows, a rail with
Meditate's icon on top from medium on), halkyon_app's first start, sign-in and
account, What's new after an update, About, English and German. Copied from Todo's
and renamed, as Train's was (ADR 0030, D12), Todo's own features left out.
- **D2 Three tabs: Today, Explore, Profile**, as 7Mind has three, Today opening.
*Today* (not *For you*: nothing is chosen for the person by anyone) starts a sitting at
once, shows the week and the streak, the favourite practices and the ones done last.
*Explore* holds every practice, by kind and by what it is for, with search. *Profile* is
every Halkyon app's (`HalkyonProfile`, decided 2 Nov 2026): the person's own tab first,
then Meditate's *Practice* tab with the figures, the week and the sittings. The cog to
Settings stands atop Today and Profile, as Train's does atop Home and Profile. Over four
tabs with the timer as one: the timer is one tap from Today and from Explore, and a
sitting is a screen over the tabs (D6), as Train's recording is.
- **D3 The washes are fern** (the developer's, 2 Nov: ADR 0031, D5): every tab and the
sitting's screen wash down from the top in `PaletteColor.fern`; buttons and links stay
cobalt, as in every app.
- **D4 What is practised: Halkyon's own practices, guided by text and bells, and the
silent timer.** Without recorded speech, a *practice* is a sitting of a set or chosen
length with **cues**: short lines of guidance shown on the sitting's screen one after
the other, spread evenly over the length chosen (so a practice scales to each of its
lengths), and later read out by the device's text-to-speech (see *Later*). The
practices are written for Meditate in English and German (`practice.dart` holds what
each is, the ARBs its words), so their licence is the app's. Version 0.1.0 has six:
counting breaths, a three-minute pause, a body scan, open awareness, kind wishes
(loving-kindness) and settling for sleep, under the kinds Breathing, Body, Awareness,
Kindness, Sleep and Pause, and the goals *Calm down*, *Sleep*, *Focus* and *Begin*;
breath at the nostrils, walking, the 4-7-8 breath and box breathing come next, the
breathing ones with a paced ring. A practice's ID never changes once released. The
**silent timer** is a sitting of any length from 1 to 120 minutes with a bell at the
start, at the end and, if chosen, every 1 to 30 minutes between. Recorded guidance (the
instance's or the person's own files) is a later step: it needs the instance to hold and
serve files, their licences checked, and their downloads kept on the device.
- **D5 The bells are made for Meditate**: three sounds (a singing bowl, a small bell, a
wooden block) synthesised by `apps/meditate/tool/bells.py` (Python's standard library
alone: decaying partials with the bowl's beating) into `assets/bells/*.wav`, so they
carry the app's licence and no one else's; played with `audioplayers` (already in the
workspace's lock, Learn's). Over recorded bells from freesound.org and the like, whose
licences differ file by file.
- **D6 A sitting is a screen over the tabs**: a ring filling as the time passes, what
is left inside it counted down by the seconds begun (5:00 until a second has passed),
the practice's cue
of the moment, Pause and Resume, and End (also the close button and back). Time is counted from the clock, not from a
ticker, so a sitting paused by the system or the app going to the background is right
as it comes back. The screen stays on while it runs where the person's setting says so
(on by default, `wakelock_plus`, as the lock already holds it). Ended early, it asks
whether to keep what was sat (Keep, Discard, or Sit on); a sitting under a minute is
never kept and ends without asking. At the end, the bell, and *Done* with what was
sat. Bells while the app is in the background are a later step (a scheduled
notification's sound), as is reading the cues out.
- **D7 What is kept: sealed rows, synced, as Todo's and Train's** (ADR 0033, D5;
ADR 0030, D7, D15): the person's key (`content_key`), one **settings** row
(`meditate_settings`: the bell, the interval and starting bells, the screen kept on, the
favourite practices, whether the streak shows) and a row a **sitting** (`sitting`: the
practice or the timer, when it started as a wall-clock time with the device's offset,
how long it was planned and sat). Everything is in the sealed payload; the instance
sees that rows exist, their sizes and when they changed. Called *sitting*, not
*session*, as Serverpod's `Session` is every endpoint's. A person's data goes with them
(`deleteMeditateData`, the sync spaces' cascade, as Todo's).
- **D8 The figures are worked out on the device from the sittings**: minutes, sittings,
days with a sitting, the days in a row (up to today, or up to yesterday while today has
none yet, so a streak doesn't break before the day is over), and the week's days as
dots from the week's first day in the person's language. The streak can be hidden
(Settings, as 7Mind's *Streak visibility*), as some find streaks a pressure.
- **D9 Today** greets by the time of day (no name: the account has a handle, not
necessarily a first name), offers *Sit now* with 5, 10 and 20 minutes and the timer's
own length, the practice of the day (one after the other by the date, the same on
every device), the week with the streak, the favourites and the last three sittings.
- **D10 Explore** is a search field (titles, summaries and kinds, every word typed,
without case or accents), the goals as four tiles (chosen again to let go), the kinds
as chips, and every practice as a card,
narrowed by what is chosen. A practice's page says what it is, its length (a choice of
lengths where it scales), how it goes (its cues in order), its favourite heart and
*Start*.
- **D11 Settings** as Todo's (ADR 0033, D4): Sync's card first, the operator's card, then
Meditate's own *Sitting* card (the bell, the starting bell, the interval bell's
default, the screen kept on, the timer's default length) and *Practice* card (the
streak shown), the account, What's new and About, the version below.
- **D12 No reminders and no notifications in the first version**: a daily reminder at a
time the person sets is the first thing to add after it (Todo's notifications show the
way).
- **D13 Development ports of its own**: API 8280, Insights 8281, web 8282, PostgreSQL
8290 (Learn's and Todo's are 8080 to 8090, Train's 8180 to 8190), so Meditate's server
runs beside the others as a member of Learn's register.
## Steps
1. **The shell** (3 Nov): Todo's copied and renamed, its features left out, the three tabs
(D1, D2, D3), Settings (D11), the icon (ADR 0031, D7).
2. **The data** (3 Nov): the server's models and its migrations, the device's key,
settings and sittings (D7).
3. **The practices and the sitting** (3 Nov): the catalogue (D4), the bells (D5), the
sitting's screen (D6), Explore (D10).
4. **Today and Profile** (3 Nov): D8, D9.
5. **Released and deployed** (later, as Todo's step 5): `apps/meditate/release.dart`, a key
of its own, the F-Droid listing, a snap, `deploy/compose.meditate.yaml` at
meditate.halkyon.eu (its `.env` values the developer has set already, 2 Nov), its
database and role, `redeploy.sh` and `backup.sh` taking it in.
## Migrations
- `initial`: Serverpod's tables, its auth modules', halkyon_base's and the OIDC module's
as they stand, as Train's first migration (ADR 0030); only `CREATE TABLE`, `CREATE
INDEX` and foreign keys, nothing to transform.
- `initial` also holds the sync engine's tables (`offline_sync_*`), as the module is
in `config/generator.yaml` from the start.
- `sittings`: Meditate's three tables, `content_key`, `meditate_settings` and `sitting`
(`CREATE TABLE`, each an `id` UUID made by `gen_random_uuid_v7()`, which the migration
defines again with `CREATE OR REPLACE`, a nullable `spaceId`, and the envelope's
columns), an index on each `spaceId` (`CREATE INDEX … USING btree`) so deleting a
person doesn't read the tables, and each `spaceId`'s foreign key to
`offline_sync_spaces` deleting with the space (`ON DELETE CASCADE`); then the version
rows of every module (`INSERT … ON CONFLICT DO UPDATE` into `serverpod_migrations`).
Purely additive: no row exists to transform.
## Later
- Recorded guidance (D4), the cues read out by text-to-speech, bells in the background
(D6), a daily reminder (D12), sittings written to Health Connect as mindfulness
sessions (Android 14's `MindfulnessSessionRecord`), courses of several practices in
order (7Mind's courses), ambient sounds.
## Consequences
- The practices are text, short and translatable, so a new one is a change to
`practice.dart` and its words in the two ARBs; a practice once used keeps its ID, as
sittings name it.
- Without recorded voices, Meditate is a timer with guidance more than a library;
recorded guidance (D4) is what would make it more.