Commit graph halkyon/apps/todo/halkyon_todo_server/test/integration
Author SHA1 Message Date
a29ad45ef1
build: the organisation on code.halkyon.app is halkyon-days
All checks were successful
CI / check (push) Successful in 46m54s
ADR 0045, D12, step 1: project-halkyon was renamed halkyon-days, its
repositories keeping their names. Every URL naming it moves along: the
servers' source-code links, which the AGPL requires, and their tests;
the forks in the pubspecs and the lock (appflowy-editor, pure-zstd,
serverpod-offline-sync); the snaps, the F-Droid listings and the
release configurations; the docs. The curator's credit for the
alphabets names the repository by its name since ADR 0015, halkyon,
rather than halkyon_learn. Forgejo redirects the old addresses until
the name is taken again.

ADRs 0015 and 0039 keep the name they were written with, and the local
folders theirs.

halkyon_editor's gen-l10n caches under build/, committed before its
.gitignore named the folder, are no longer tracked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-12 16:30:46 +01:00
3ca4b4308d
feat(todo,app): files attached to tasks and comments
Halkyon's ADR 0043, D1, D3, D5 and D8: any file up to 100 MB, attached to
a task's description or to a comment, sealed as everything of Todo's.

Server: the attachment table, synced and sealed as the others, with the
sealed file's hash readable for the store; todoFiles hands halkyon_base
its rows, readable by their space's members, and takes sealed files
alone; the files no attachment names go daily. Its tests hold downloads
to the row's space, refuse a readable file and prune what no live row
names. Migration attachments: CREATE TABLE "attachment" with its indexes
and the foreign key to its space, cascading as every synced table's.

App: AttachmentRepository seals each file for its row, keeps it sealed
and opened in the person's folder, uploads it after each sync and fetches
it as it shows, both held back by Without files on mobile data but for a
file tapped. Under the description and a comment's text, a row of them:
pictures as thumbnails opening full-size, other files as chips opening in
the system's app, else saved; the paperclip beside the description and in the comment's
composer adds them, the composer holding them until it is sent. A task deleted takes its files
and its comments' along, a comment its own.

halkyon_app opens a file in the system's app (FileOpener): on Android
through its plugin and a FileProvider of its own class, sharing the
cache's halkyon_opened/ folder alone; on the desktop as a file: URI.

Whats-New: Attach files to a task's description or a comment with the paperclip: pictures show as thumbnails, other files open in their app.
Whats-New-De: Hänge mit der Büroklammer Dateien an die Beschreibung einer Aufgabe oder an einen Kommentar: Bilder erscheinen als Vorschau, andere Dateien öffnen sich in ihrer App.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-11 01:19:41 +01:00
a91aa98817
feat(todo): Todo's server answers a place's forecast (ADR 0033, D40)
The weather of D40 comes through the instance, as Train's workouts'
(ADR 0036, D1): Todo's server gets a `weather` endpoint, `offered` and
`forecast`, answering a place's hours for the next 16 days from
Open-Meteo for people signed in alone, the place rounded to about a
kilometre, nothing kept or logged, 30 requests a person a minute.
Refusals are `WeatherRefused` (off, invalid, tooOften, unreachable).
`WEATHER=off` and `OPEN_METEO_API_KEY` switch it as they switch Train's,
and production's compose file passes them to Todo's server too.

D40 records the developer's decisions of 10 Nov 2026 on the weather and
the agent's details (what dry, sunny, a temperature range and calm mean
over a task's hours).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-10 15:27:58 +01:00
016b74ff74
feat(todo): comments on a task (ADR 0033, D36)
Plain text with links, written, edited and deleted in the task's sheet,
counted on its row. They are sealed in `task_comment`, the task they belong
to with them, and go with their task.

Whats-New: Comments: write on a task, edit or delete what you wrote, and open its links.
Whats-New-De: Kommentare: Schreibe zu einer Aufgabe, bearbeite oder lösche es und öffne Links darin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-07 21:49:44 +01:00
b1f2078357
feat(todo): filters in Todoist's language, and a view of each label (ADR 0033, D35)
A filter is a query read on the device, as the server sees no task, kept
sealed in `saved_filter`. Filters & labels is a screen of its own under
Browse; a label opens its tasks, and favourite filters and labels join
Browse's Favourites. D36 records what of Todoist's Pro stays out.

Whats-New: Filters: keep views like "today & p1", and open a label to see its tasks, under Browse in Filters & labels.
Whats-New-De: Filter: Speichere Ansichten wie „heute & p1“ und öffne ein Label, um seine Aufgaben zu sehen, in der Übersicht unter Filter & Labels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-07 21:49:42 +01:00
ce07fab193
feat(todo): a pin in the map's middle, and what is around it (ADR 0033, D32)
The location picker fills the screen with its map, the search floating
over it. A pin stays in the middle, lifting while the map moves, as
Molly's; where it lands the instance names the address and lists the
places around, as Threema's. That is `nearby` on the search endpoint,
for those signed in, the point neither kept nor logged, counted among
the searches: two lookups, as a plain one finds house numbers alone.

Fixes the map's credit, which overflowed a phone and was pale on white.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-07 21:49:41 +01:00
c9d3eb4dd1
feat(todo): the server finds places through the instance's Photon (ADR 0033, D32)
Todo's locations search through the person's own instance, as Train's
planner does, not a public service: `SearchEndpoint` on `PlaceSearch`, for
those signed in, 60 searches a minute, nothing kept or logged. Its `Place`
carries the address's parts and the object in OpenStreetMap, and a shop's
car park or charger is left out, which came first for `Lindau EDEKA`.

`compose.todo-search.yaml` puts Todo's server on compose.search.yaml's
Photon. A file of its own: without it Todo starts as it did, and offers no
search.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-07 21:49:35 +01:00
2939437c74
feat(todo): a project's Calendar, Upcoming's Display and its 3-day grid (ADR 0033, D17)
A project's Display gains the Calendar layout: Upcoming's week strip
and agenda, now shared as DayAgenda, over the project's tasks it keeps,
with No date beside the month and Future occurrences, each repeating
task also on the days it will come back (calendarOf on Repeat.next),
muted, neither ticked off nor dragged.

Upcoming gets a Display of its own, the same sheet over another target
(DisplayTarget): Agenda or 3-day, done tasks, future occurrences,
sorting, the filters by deadline, priority and label, and in the grid
Colouring by priority, label or project. It is kept in the person's
settings, one sealed row under an ID keyed by the account in the new
synced table todo_settings (migration settings: the table, its index
on spaceId and its foreign key to the space, cascading on delete).

The 3-day grid shows today and the two days after, stepped or swiped
three at a time: tasks without a time all day, timed ones as blocks as
long as they last, overlapping ones side by side (layOutDay). A long
press on a half hour adds a task there for half an hour; Overdue opens
the overdue tasks with Reschedule.

The board's separator builder names its BuildContext, as the root's
analyzer plugin asks (never_discard_build_context).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 20:01:00 +01:00
128bcb127b
feat(todo): sections, tasks and labels on the server (ADR 0033, D6)
Three more synced tables, each a sealed payload alone, as project's:
section, task and label; deleted with the person's space.

The migration only adds: the three tables (id uuid defaulting to
gen_random_uuid_v7(), spaceId, keyId, payload, nonce), each indexed on
spaceId and cascading with its space (ON DELETE CASCADE to
offline_sync_spaces), and the modules' version rows. Nothing is dropped
or changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 01:06:24 +01:00
3876e30681
feat(todo): the sync engine and Todo's first models (ADR 0033, D5, D6)
Todo's server syncs with serverpod_offline_sync as Learn's and Train's
do, and holds two synced tables, every payload sealed on the devices:
content_key (the person's data key, sealed to the account) and project
(the Inbox or one of the person's own, changed when edited). A person's
space goes with them, with everything in it.

The migration only adds: the two tables, each indexed on spaceId and
cascading with its space (ON DELETE CASCADE to offline_sync_spaces), the
sync engine's own tables (crdt_*, offline_sync_*) with their foreign
keys, the gen_random_uuid_v7() function their ids default to, and the
modules' version rows. Nothing is dropped or changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-01 01:00:53 +01:00
87671208df
feat(base)!: no anonymous accounts on the server (ADR 0020, D25)
All checks were successful
CI / check (push) Successful in 30m13s
CI / deploy (push) Has been skipped
The server's half of D25: every account has a way in, and the anonymous
accounts left from before go with the daily pruning.

- The module signs nobody in anonymously: `anonymousIdp` and
  `gitHubUpgrade` are gone, and so is linking GitHub at the register.
- A password is set only by someone whose address is theirs (a
  `firstPassword` code) or who signs in with GitHub alone (a fresh
  sign-in and a `signUp` code); an anonymous account is refused
  `wrongCredentials` before any code is used.
- SimpleLogin refuses to link onto a caller without `identified`
  (`wrongCaller`), the member's endpoint too, so nothing turns an
  anonymous account into one with a way in.
- `pruneAnonymousPeople` deletes every anonymous account left, however
  new, never one identified. Learn and Todo schedule it as
  `prune-accounts` and cancel the old `prune-abandoned-anonymous-people`
  entry once.
- Learn: someone without `identified` keeps no files;
  `FILE_LIMIT_ANONYMOUS_MB` is gone. `minimumAppBuild` is 23, as builds
  up to 22 start with an anonymous account.
- Learn's sync tests make their person and session by SQL in the
  development database, as Serverpod stores sessions.
- Docs, store listings and ADRs 0010, 0014, 0016, 0017 and 0019 amended.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 16:14:42 +02:00
65c5cd740d
feat(accounts): one account across an instance's services (ADR 0016)
halkyon_base runs as the instance's register or as one of its members.
The register keeps the ways in, handles and profiles; members ask it
over HTTP with the instance's shared secret (instanceSecret), and open
sessions of their own for its accounts, under the same account ID.

- AccountsMode from the environment: INSTANCE_MEMBERS on the register,
  ACCOUNT_REGISTER_URL on a member. Learn always holds the register
  (Explore joins the profiles); Todo is a member where it is set.
- AccountRegister: LocalRegister, RemoteRegister, an in-memory one in
  tests. Calls between services on the web server, refused without the
  secret (constant-time compare); a server needing it doesn't start
  without it.
- Deleting an account deletes it everywhere: notices per member kept in
  the deletion's transaction (account_deletion_notice), told at once and
  hourly until confirmed; members prune daily whom the register no longer
  knows, never while it knows none of them or can't be reached.
- Deploy: INSTANCE_SECRET and INSTANCE_MEMBERS in compose and .env.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-22 18:11:07 +02:00
28dd3ea65a
feat(todo): the server on halkyon_base
Todo's server includes halkyon_base (ADR 0014), configured for Todo:
its basics say it serves `todo`, sign-in mails name Halkyon Todo, the
source is this repository, minimumAppBuild 1. Authentication through
the module (anonymous, GitHub, email codes, server-side sessions) in
place of the scaffold's email-and-password provider; the module's
pruning scheduled once however often the server starts. The scaffold's
greeting, its Flutter web hosting, cloud storage and Docker files go;
session logs are never persisted. Todo was never deployed, so its
migrations start again with one initial migration. The root's lints
apply, with the four constructor rules off as on Learn's server.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-22 18:11:06 +02:00
314a901fa1
chore: Halkyon Todo's scaffold joins the repository in apps/todo
Its server, client and app as `serverpod create` made them, with the
switch to server-side sessions that was waiting uncommitted, as files:
its one commit holds the scaffold alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-22 18:11:03 +02:00