docs(auth): the short reading of the fixes (ADR 0017)

ADR 0017 gets "The short reading": its findings, the developer's four
decisions of 21 Oct, and what changed. "The rules as they stand" says a
reset takes effect 14 days after it was asked at the latest, that a member
keeps the session a reset signed in and deletes nobody while the register
is out of reach, and no longer claims every secret is kept out of every
capture: Android holds them back, Windows from 10 2004, macOS from
screenshots but not ScreenCaptureKit on macOS 15 and later, iOS covers
the app switcher's picture alone, Linux nothing. The module's and
halkyon_app's READMEs and Learn's AGENTS.md follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-21 18:17:08 +02:00
commit a3b70175f6
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -25,7 +25,7 @@ A spaced-repetition learning app: studysets of flashcards, reviewed on an FSRS s
- **App-wide vs. per person.** `Dependencies` holds what outlives a person (the client, auth). Everything of the signed-in person (their local database, its sync, their repositories) is `PersonDependencies`, opened by halkyon_app's `PersonScope` inside `SessionGate`'s keyed subtree (`Dependencies.openPerson`) and disposed with it; widgets reach it through `PersonDependencies.of(context)`.
- **The device's own settings** (appearance, language, citation style, the PDF sidebar's share of the room as last dragged; `core/device_settings/`) belong to the device, not to a person: kept in `<app support>/device_settings.json`, read in `Dependencies.create()` so the first frame already has them, and provided by `DeviceSettingsScope` around halkyon_app's `SessionGate` (`LearnSessions`, `lib/src/app/`), whose plain app takes its look from it (theme mode and language, `DeviceSettings.look`), as `PersonApp` does; halkyon_app's `InstanceGate` reads the same two before any instance opens (`readLook`). What should follow the person to their other devices goes into their synced `PersonSettings` instead.
- **Every person has an app of their own** (`PersonApp`, `lib/src/app/`), which `SessionGate` builds inside the keyed subtree: a `MaterialApp.router` with its own `GoRouter`, and `PersonScope` around the router's navigator (`MaterialApp.builder`). So every screen and dialog is inside the person's scope and reads their repositories from `PersonScope.of(context)` wherever it is built, and a change of person disposes the router with every screen on it and starts again at home. Until someone is signed in, `SessionGate` shows a plain `MaterialApp`: the spinner, or the failure with a way to try again. **The gates are halkyon_app's** (ADR 0014, shared by Halkyon's apps): `InstanceGate`, `SessionGate`, `PersonScope`, `AppScope`; Learn gives them `learnFrame` (`lib/src/app/learn_frame.dart`: its name and word, kingfisher, theme, languages, launch screen, and the two sentences only it can say, the first start's subtitle and what a switch leaves behind, in its own words), its dependencies, `ServerpodInstanceChecker(app: 'learn')` and its `CountryNames` (`main.dart`).
- **Platform plugins sit behind small interfaces in `Dependencies`**, with fakes in tests: picking files (`FilePicker`, `file_selector`), saving files the app made, such as exports (`FileSaver`: `file_selector`'s dialog on the desktop, `file_picker`'s on Android and iOS, where `file_selector` has none), playing sounds (`SoundPlayer`, `audioplayers`), playing YouTube videos (`YoutubeVideos`, `youtube_player_iframe` where a web view exists, else the browser), reading PDFs (`PdfEngine`, pdfrx on PDFium, which `flutter test` can't load: widget tests use `FakePdfEngine`, whose PDF bytes say their pages and chapters), cutting Japanese on the device (`JapaneseAnalyser`: MeCab, which needs the IPADIC the app downloads, on an isolate of its own), reading Anki packages on another isolate (`AnkiPackageReader`; a widget test's fake clock can't wait for one), zipping an export with its files on another isolate (`ExportArchiver`, for the same reason), and the Pomodoro timer's side of the system (`PomodoroPlatform`, `FakePomodoroPlatform` in tests).
- **Platform plugins sit behind small interfaces in `Dependencies`**, with fakes in tests: picking files (`FilePicker`, `file_selector`), saving files the app made, such as exports (`FileSaver`: `file_selector`'s dialog on the desktop, `file_picker`'s on Android and iOS, where `file_selector` has none; halkyon_app's `writePrivately` writes them the own user's alone, on macOS into the chosen file itself, which the sandbox's `files.user-selected.read-write` entitlement allows: keep it, or nothing saves there), playing sounds (`SoundPlayer`, `audioplayers`), playing YouTube videos (`YoutubeVideos`, `youtube_player_iframe` where a web view exists, else the browser), reading PDFs (`PdfEngine`, pdfrx on PDFium, which `flutter test` can't load: widget tests use `FakePdfEngine`, whose PDF bytes say their pages and chapters), cutting Japanese on the device (`JapaneseAnalyser`: MeCab, which needs the IPADIC the app downloads, on an isolate of its own), reading Anki packages on another isolate (`AnkiPackageReader`; a widget test's fake clock can't wait for one), zipping an export with its files on another isolate (`ExportArchiver`, for the same reason), and the Pomodoro timer's side of the system (`PomodoroPlatform`, `FakePomodoroPlatform` in tests).
- **Material's `Card` widget clashes with the domain's `Card`**: files that need both import `material_ui` with `hide Card`.
- **State: `flutter_bloc`, uniformly.** Blocs fold repository streams into immutable states with value equality, are provided as close to their consumer as possible, and log through one global `BlocObserver`. Why: the data layer is stream-shaped, event → state machines test without widgets, and one pattern everywhere is itself the point.
- **Value equality comes from `equatable`**, mixed in (`with Equatable`), for models and states alike; `leancode_lint`'s `missing_equatable_props` catches a field left out of `props`.