A task's date may recur: every n days, weeks (on which weekdays), months (on its day, or its nth or last weekday), years, from its date or from when it is done, until a day or for n times. Done, it moves on to the next day after both its date and today, its deadline as far, its time kept, its done sub-tasks open again and when it was done kept in its payload; undo writes all of it back. The date sheet offers the rules that fit the task's day and a rule of the person's own; rows show a recurring date with its own icon, the task sheet the rule in words. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
11 KiB
11 KiB
ADR 0033: Halkyon Todo, what it does and how it looks
- Status: decided 1 Nov 2026, by the developer (D1–D10 below, the agent's recommendations taken as a whole). Built the same day: steps 1 to 3, Today and Upcoming as lists, and recurrence (step 4 without reminders and the week strip).
- Numbering: 0025–0029 are held (ADR 0030's note), 0032 is Jellyfin's; this is the next free number.
- Builds on: ADR 0030, D7, D12, D14, D15 and D60 (Train's sealed rows, its shell, its frame and settings, which Todo takes); ADR 0017 (the account's keys); ADR 0031 (Todo's icon); Train's D61 (What's new).
Context
On 1 Nov 2026 the developer asked for work on Todo to start from 68 screenshots of
Todoist's Android app (31 Oct 2026, ~/Downloads/todoist-screenshots-mobile/), "following
the same foundational reasoning of Halkyon Learn and Halkyon Train": when the bar and when
the rail, a wash of strawberry, settings grouped as theirs. What the screenshots show:
- Five tabs (Inbox, Today, Search, Upcoming, Browse), a floating add button above the bar, the bar's items and the button's side set by the person; Browse holds the projects, Reporting, templates and the cog to Settings.
- A task has a title, a description, a date (optional time and duration, "06:30–07:00") and, apart from it, a deadline (date only, its own icon, neutral colour; a task may have a deadline and no date), a priority (P1–P4), labels, reminders (relative to the time, automatic 30 minutes before a timed task; by location in the paid plans), a recurrence (completing it moves it on; its sub-tasks reset), sub-tasks (counted "0/5" on its row), comments with attachments. Rows colour the date by meaning (overdue red, tomorrow orange, this week purple).
- Projects have sections (name, description, count), seen as a list, as a board (a column a section, paged) or as a calendar (a week strip opening into the month, an agenda below). Today puts Overdue first, with Reschedule for all of them; Upcoming is an agenda from today on, or a 3-day grid of hours where a long press picks 30 minutes. Every view has its own Display sheet: layout, completed tasks, grouping, sorting, filters by date, deadline, priority and label.
- The task sheet shows a row for each field set and a chip for each one not, the sub-tasks, and a comment field at its foot; quick add is a card above the keyboard with chips (date, deadline, priority, reminders, project), dates recognised in what is typed.
- Productivity: a daily and weekly goal, streaks, karma, days off and a vacation mode; Reporting, the completed tasks by day.
- Settings: Account, General (home view, smart dates, reset sub-tasks, the week's first day, how "next week" and "weekend" are read, swipe actions), Theme, Navigation bar, Quick Add, Productivity, Reminders, Notifications (a morning overview and an evening review), About, What's new.
Decisions (1 Nov 2026, the developer's)
- D1 Todo's frame is Train's (ADR 0030, D60): go_router with a
StatefulShellRoute, every tab keeping its own stack; halkyon_ui'sNavigationFrame, a bar floating above the foot below medium windows and a rail with Todo's icon on top from medium on, a tick on every choice; Todo's account sheet goes. Over a frame of Todo's own, which would make the three apps behave differently on the same window. - D2 Four tabs: Today, Upcoming, Inbox, Browse (recommended, over Todoist's five and over
a bar the person arranges). Today opens. Browse holds the projects, the labels and,
later, saved filters, with the cog to Settings atop it as Train's Home and Profile carry
theirs. Search is an icon in every tab's app bar, not a tab, as neither Learn nor
Train gives search a tab. Adding a task is a floating button above the bar
(Train's
TabScreenplaces it clear of the bar), on the rail's screen from medium windows on. From expanded windows on, a task opens in a pane beside its list, as Train's Maps opens a route's map beside the list (Material's list–detail layout), and in a bottom sheet below. Arranging the bar may come later; Search may become a tab if it proves to be used as one. - D3 Every screen washes down from the top in strawberry (the developer's):
TopGradientunder the app'sScreenColor,PaletteColor.strawberry, as Learn's in cobalt and Train's in carrot; buttons and links stay cobalt. - D4 Settings as Learn's and Train's (the developer's): a screen over the tabs,
grouped in
SectionCards. Sync's card first (as Learn's), the operator's card for whoever operates the instance, then Todo's own groups (Tasks: the week's first day from the language unless set, resetting sub-tasks; Reminders: the automatic reminder before a timed task, snoozing), the account near the foot (halkyon_app'sAccountTiles,SimpleLoginSection,AccountSecuritySection), What's new and About Halkyon Todo last, the version small below them. Groups come as their settings are built; none is shown empty. - D5 Todo's rows are sealed, always, as Train's (ADR 0030, D7, D15), synced with
serverpod_offline_sync: every payload ADR 0017's sealed content under the person's data key (content_key, halkyon_app'sPayloadEnvelope), nothing readable in a column, not a title nor a date. Recommended over readable rows (ADR 0002's default), which would let the server send reminders by email and a morning overview, and would make shared projects simpler: what a person plans to do is theirs, the server needs to read none of it to sync it, and Todo's rows can't be unsealed later without the person's devices. So reminders are scheduled on the device, as local notifications on each device that holds the account's keys; there is no email reminder and no overview the server writes. A project shared later is sealed under a key its members share, which is that step's to decide. - D6 Four tables:
project,section,task,label(recommended, as Train keeps a workout and its series apart, D14): one row each, changed when edited, the last writer winning, every payload JSON with aschemaVersion, UUID v7 ids. Inbox is a project the app makes once per person, under a row ID keyed by the account (Train's D57,keyedRowId), not marked in its payload (as built, 1 Nov): every device makes the same row, so two making it offline write one, and the instance can tell neither which row is the Inbox nor whose it is; a payload's mark would let a moved row claim to be it. A sub-task is a task naming its parent. A task's payload: title, description, its project and section, its parent, its place among its siblings, a date (a day, or a day and a time with an optional duration, the time as the person's wall clock with its time zone), a deadline (a day), a priority (1–4), its labels' ids, a recurrence (D7), its reminders, when it was completed. Completing a recurring task moves its date on and writes the occurrence done into its payload's history, so Reporting can count it. Comments and attachments are a later step (attachments need sealed files, which no Halkyon app has yet). - D7 A recurrence is a structured rule, set through a picker (recommended, over
parsing what is typed): every n days, weeks (on which weekdays), months (on which day,
or the nth weekday), years, from the date or from completion, until a day or for n
times. Dates and tokens typed into quick add ("tomorrow 6am", "every Monday",
#project,p1), in English and German, are a later step, built on the same rule. As built (1 Nov): the rule is part of the task's date (RepeatinTaskDate, its payload'sdate.repeat), so a task without a date doesn't recur and clearing the date ends it; schema 1 holds it, as no task was written before. Done, a task counted from its date moves to the rule's first day after both its date and today (an overdue one isn't moved onto a day already past), counted from completion it moves the rule's units on from today, on no particular weekday; its deadline moves as far, its time is kept, its done sub-tasks open again, and when it was done is added to itscompletions. Undo writes back every task that changed. The picker offers the rules that fit its day (every day, every weekday, its weekday, its day or nth weekday of the month, its day of the year) and a rule of the person's own; weeks start on Monday for every n weeks, until the week's first day is a setting (D4). - D8 The first version (recommended): Inbox and projects with sections, shown as a list; sub-tasks; Today (Overdue first, rescheduling them together, then today's); Upcoming as an agenda with the week strip opening into the month; the task sheet (rows for the fields set, chips for the others, the sub-tasks); quick add; labels; completed tasks shown or hidden per view; reminders on the device. Later, each put to the developer when its turn comes: the board and calendar layouts, Upcoming's 3-day grid, each view's Display sheet (grouping, sorting, filters), saved filters, Reporting and goals (karma not before the developer asks for it), location reminders, comments and attachments, typed dates, sharing.
- D9 Colours that mean something come from halkyon_ui's palette (recommended): a
project's colour is picked from
PaletteColor, with its localised name, as Learn's colours are; priorities and dates take palette colours chosen not to blur against the strawberry wash, so overdue and P1 are cerise, not strawberry, P2 carrot, P3 cobalt, P4 none; tomorrow and this week as Todoist's meanings go, in palette colours. Each is checked against the wash in light and dark before it is kept. - D10 The icon stays as ADR 0031 drew it (cobalt through pacific into fern), though the wash is strawberry: Learn's icon ends in strawberry and washes in cobalt, so an app's wash and its icon already differ, and Todo's and Meditate's icons are kept related (ADR 0031, D1). The agent made no recommendation; this is the default the decision of 1 Nov left in place, to be asked again if the screens look wrong beside it.
Steps
- The frame: go_router and
NavigationFramewith the four tabs (empty screens that say what comes), the strawberry wash, the cog, Settings as D4 with what exists today (the account, What's new, About, the version); the account sheet removed. - The first synced model:
content_keyandproject(Inbox made once), its gateway and repository on Learn's and Train's pattern, the server's access rules each with a test asserting denial; walked through with the developer as the first of Todo's. - Tasks:
section,task,label; Inbox and a project's list; the task sheet; quick add. - Today and Upcoming, recurrence (D7), reminders on the device.
- Todo's first release (0.1.0), its What's new drafted by
tool/release.dart.
Consequences
- No email reminder, morning overview or evening review: the server can't read what is due. A device without the account's keys neither reads nor writes Todo's rows, as in Train.
- Todo's server holds no logic over tasks; what Today, Upcoming and Reporting show is worked out on the device, over every task the person has. That is fine for thousands of tasks; a person with far more would need the device to keep an index of its own.
- Sharing a project is harder than with readable rows (a key per shared project, its members' keys wrapping it); D5 accepts that for now.