halkyon/apps/meditate/AGENTS.md
Tizian 「ティツィアーン」 86c9f4522a
feat(meditate): the dream journal, a fourth tab, Today's morning card
ADR 0038, D14. A dream (`Dream`) holds the night it belongs to (the
morning's date, today's by default), an optional title, its text, the
marks lucid, nightmare and recurring, a clarity of 1 to 5, the mood on
waking and free labels, and when it was written; a row each in `dream`,
sealed for its row with the person's key as the sittings
(`DreamRepository` on `SealedRows`, `SyncDreamGateway`), followed by
`JournalCubit` for every screen.

- The Journal, between Explore and Profile: dreams by month, the
  newest first, searched over title, text and labels as Explore
  searches, filtered by marks, `+` above the bar; empty, it says that
  entries are sealed on the device before they sync.
- A dream's screen over the tabs: a new one with its text first and
  focused, the rest folded below; a kept one to read, with Edit and
  Delete (asked first); leaving one written but not saved asks first.
- Today, from 4 to 12 o'clock while last night has no dream, a gentle
  card leading to a new one; Settings' Journal card switches it off
  (`dreamPrompt` in the sealed settings, schema 1 kept).
- Profile's Practice: this month's dreams and the lucid ones, once the
  journal holds one.
- English and German texts; What's new 0.1.0 names the journal; the
  account's deletion says it takes the dreams too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 09:31:58 +01:00

6.8 KiB

Halkyon Meditate

What holds for every app and package of this repository is in the root AGENTS.md; it applies here in full, and Learn's patterns (apps/learn/AGENTS.md) wherever Meditate needs the same thing. What Meditate is and why: ADR 0038 (proposed 3 Nov 2026, built by an agent without the developer's interjection; its decisions await their reading).

  • Packages: halkyon_meditate_server, halkyon_meditate_client (generated, never edited), halkyon_meditate_flutter; app ID app.halkyon.meditate on every platform; no web build. Licences as Learn's (server SSPL, client and app AGPL; LICENSE.md). Copied from Todo's tree on 3 Nov 2026 and stripped to the shell, so its frame, keys and sealed rows are Todo's and Train's.
  • The server includes halkyon_base (ADR 0014) and configures it in server.dart (meditateBase(), lib/src/meditate_base.dart): its basics say it serves meditate (never change it once apps use it), sign-in mails name Halkyon Meditate, the source is this repository, minimumAppBuild 1, a person's space deleted with them (deletePersonData: [deleteMeditateData], lib/src/meditate_data.dart). Accounts (ADR 0016): Meditate holds its own register when it runs alone, and is a member of the instance's register where the operator sets ACCOUNT_REGISTER_URL (AccountsMode.fromEnvironment()) with the instance's shared INSTANCE_SECRET; INSTANCE_MEMBER_MEDITATE names it to the register. schedulePruning (lib/src/pruning.dart) keeps the module's pruning scheduled once each. Synced tables (ADR 0038, D7; database: sync, every row sealed): content_key, meditate_settings (one row a person, under an ID keyed by the account), sitting and dream (D14), each indexing spaceId. Migrations: initial (Serverpod's, the auth modules', the module's and the sync engine's tables), sittings (the three tables) and dreams (dream). Tests: test/integration/meditate_server_test.dart (the module's endpoints through the server's own dispatch, halkyon_base.<endpoint>, and the sync engine's connector refusing calls without a session), test/integration/sitting/person_deletion_test.dart.
  • The app is halkyon_app's InstanceGate and SessionGate with Meditate's frame (meditateFrame) and ServerpodInstanceChecker(app: 'meditate'); MeditateDependencies per instance, MeditatePerson per person (their sync, SettingsRepository, SittingRepository, DreamRepository, and the device's BellPlayer and ScreenAwake); SimpleLogin's way back on app.halkyon.meditate:// and the loopback port 43826. The person's app (MeditatePersonApp) washes in fern (ScreenColor, D3) and holds PracticeCubit (settings and sittings, read again on every change) and JournalCubit (the dreams) for every screen. go_router (buildMeditateRouter, MeditatePaths): halkyon_ui's NavigationFrame (MeditateShell) with Today, Explore, Journal and Profile (D2, D14; a bar below medium windows, a rail with the icon from medium on), a practice's page in Explore's branch (PracticeScreen), and over the tabs the sitting (SittingScreen, handed its SittingPlan as extra), a dream (DreamScreen, /dream new, /dream/:id kept) and Settings. Today (TodayTab, D9): greeting by the hour, from 4 to 12 o'clock a card to write down last night's dream while it has none (dreamPromptDue, D14), Sit now (5, 10, 20 and the timer's own length), the practice of the day (practiceOfDay), the week and the days in a row (WeekCard), favourites, recently sat. Explore (ExploreTab, D10): search folded without case or accents (matchesSearch), four goal tiles, kind chips, practice cards. The Journal (JournalTab, D14; lib/src/feature/journal/): dreams by month, the newest first (dreamsByMonth), searched over title, text and labels and filtered by marks (findDreams), + to write one; a dream's night is the date of the morning after it (defaultNight: today's date at any hour); its screen writes with the text first and focused, the rest folded, and reads a kept one with Edit and Delete. Profile (D2, D8): halkyon_app's HalkyonProfile with a Practice tab (minutes, sittings, days, the week, the journal's dreams this month and lucid ones once it holds one, every sitting with Delete). The practices are practices in lib/src/feature/practice/model/practice.dart (IDs never change once released), their words in the ARBs by ID (practiceTexts), their cues spread evenly over the length (Practice.cueAt). A sitting (SittingCubit, D6): its time from clock.now(), the ticks only showing it; the bell at the start (where on), between every interval and at the end; the screen kept on while it runs (where on); kept whole at its end, and ended early kept from a minute on where the person says so. The figures (practiceFigures, D8) on the device: a sitting's day by the wall clock it was sat by (Sitting.offsetMinutes), the streak up to today or yesterday, the week from the language's first day. Settings (D11): Sync, the operator's card, Sitting (bell, starting bell, bells between, screen on, the timer's length), Practice (the streak shown), Journal (Today's morning card, dreamPrompt), the account, What's new, About, the version. Texts in British English (the template) and German (du), en_US and en_GB empty. Widget tests start the whole app with halkyon_app's fakes (test/app/meditate_app_test.dart), the bells and screen recorded (test/support/recording_device.dart); a sitting on screen ticks every second, so tests pump seconds (sit) rather than pumpAndSettle; a test needing the hour (Today's morning card) starts the app inside withClock (startAt).
  • The bells (D5) are assets/bells/*.wav, synthesised by apps/meditate/tool/bells.py (Python's standard library, run from apps/meditate; the same bytes every run) and played with audioplayers (AudioplayersBellPlayer). Only while the app runs: bells with the screen off are for later.
  • The icon is Lucide's sprout from cobalt through pacific into fern (ADR 0031, D5, D7), drawn by the repository's tool/app_icons.py (python3 -I tool/app_icons.py meditate from the root): assets/icon/halkyon_meditate_icon.png, Lucide's licence beside it, every platform's launcher icon.
  • serverpod start runs in apps/meditate/halkyon_meditate_server on ports of its own (8280 to 8282, PostgreSQL 8290), so it can run beside Learn's (8080…) and Train's (8180…); its tests need throwaway passwords in its git-ignored config/passwords.yaml (database, serviceSecret, serverSideSessionKeyHashPepper, emailSignInPepper) or from the environment, as CI gives them.
  • Not yet: releases (apps/meditate/release.dart, its key, F-Droid listing, snap), the deploy beside learn.halkyon.eu (compose.meditate.yaml, config/production.yaml, the Dockerfile), and what ADR 0038 lists under Later.