fix: Serverpod v4.0.4 and serverpod_offline_sync v0.0.10

serverpod_offline_sync v0.0.10 collects outbound changes in one
consistent snapshot, so a sync no longer fails on writes made while it
runs (upstream #84, #163), keeps checkpoints per author, so a device
clock moving backwards no longer stalls sync or resends data, and keeps
unique conflicts and foreign key defaults alike on every device. Its
tables and its messages are unchanged: no migration, and apps on v0.0.8
keep syncing. It needs Serverpod >=4.0.4 <4.1.0 (v4.0.4 is fixes only),
so the root overrides that held the workspace on one Serverpod go: each
package pins v4.0.4 itself. The generated code is identical under the
v4.0.4 CLI, which CI now uses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-09 11:09:52 +02:00
commit 72c15300ed
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
8 changed files with 54 additions and 67 deletions

View file

@ -21,7 +21,7 @@ jobs:
FLUTTER_VERSION: 3.47.5 FLUTTER_VERSION: 3.47.5
# The Serverpod CLI, for the generated-code check: the server's Serverpod version # The Serverpod CLI, for the generated-code check: the server's Serverpod version
# (the root pubspec's overrides); move it together with them. # (the root pubspec's overrides); move it together with them.
SERVERPOD_VERSION: 4.0.3 SERVERPOD_VERSION: 4.0.4
steps: steps:
# Before anything else runs, the job proves it can neither see nor reach production # Before anything else runs, the job proves it can neither see nor reach production
# (the runner's sandbox, deploy/runner/). If it can, nothing else should run. # (the runner's sandbox, deploy/runner/). If it can, nothing else should run.

View file

@ -154,7 +154,7 @@ A spaced-repetition learning app: studysets of flashcards, reviewed on an FSRS s
- **The client's local migration** (in `halkyon_learn_client/lib/migrations/`, written by the same `create-migration`) creates the device's SQLite schema from the whole definition, so it also contains the server's own tables. Only the synced tables and the bookkeeping ever hold data on a device. - **The client's local migration** (in `halkyon_learn_client/lib/migrations/`, written by the same `create-migration`) creates the device's SQLite schema from the whole definition, so it also contains the server's own tables. Only the synced tables and the bookkeeping ever hold data on a device.
- **Older apps** can't be made to update once published. Before the first sync of each launch the app asks whether the server still works with its build (`minimumAppBuild` in `instance.info()`, from `lib/src/instance/app_compatibility.dart` in the server; the build is the number after `+` in the app's version), and the sync engine refuses an app whose synced tables differ from the server's. Both end in `AppServerMismatchException` (`core/sync/`), and the app says so: a banner on Home and the Sync card in Settings. Raise `minimumAppBuild` in the commit that breaks older apps (an endpoint they call changes or goes), to the build of the first app that works again; a change to the synced tables needs no raise. Every release raises the build number. - **Older apps** can't be made to update once published. Before the first sync of each launch the app asks whether the server still works with its build (`minimumAppBuild` in `instance.info()`, from `lib/src/instance/app_compatibility.dart` in the server; the build is the number after `+` in the app's version), and the sync engine refuses an app whose synced tables differ from the server's. Both end in `AppServerMismatchException` (`core/sync/`), and the app says so: a banner on Home and the Sync card in Settings. Raise `minimumAppBuild` in the commit that breaks older apps (an endpoint they call changes or goes), to the build of the first app that works again; a change to the synced tables needs no raise. Every release raises the build number.
- **Where sync stands** is `SyncService.status` (syncing, the last sync that went through, the last failure; since the app started), which `SyncStatusBloc` (`core/sync/`, provided by `PersonApp`) hands to the trouble dot on the Settings cogs (on Home and Profile; no tab carries it), the Sync card in Settings and the banner on Home. Only the last outcome counts: one sync that goes through clears a failure. The Library's Refresh and pulling it down sync now (`syncAndWait`, which a pull's spinner waits on). - **Where sync stands** is `SyncService.status` (syncing, the last sync that went through, the last failure; since the app started), which `SyncStatusBloc` (`core/sync/`, provided by `PersonApp`) hands to the trouble dot on the Settings cogs (on Home and Profile; no tab carries it), the Sync card in Settings and the banner on Home. Only the last outcome counts: one sync that goes through clears a failure. The Library's Refresh and pulling it down sync now (`syncAndWait`, which a pull's spinner waits on).
- **Serverpod's version moves together with `serverpod_offline_sync`**, which pins Serverpod exactly: the root `pubspec.yaml` overrides the four core packages to the project's version. Upgrade Serverpod only together with those overrides and the CLI version CI generates code with (`SERVERPOD_VERSION` in `.forgejo/workflows/ci.yaml`), after reading the sync package's changelog. - **Serverpod's version moves together with `serverpod_offline_sync`**, which allows only a narrow range of Serverpod (v0.0.10: `>=4.0.4 <4.1.0`; v0.0.8 pinned v4.0.0 exactly, which the root `pubspec.yaml` overrode until 2026-10-09). Every package pins Serverpod's packages to one version: upgrade them all together with the sync packages and the CLI version CI generates code with (`SERVERPOD_VERSION` in `.forgejo/workflows/ci.yaml`), after reading the sync package's changelog. Never override a version the sync package doesn't allow without checking what changed in `serverpod_database`, which it builds on.
- **The source-code link**: both licences require offering the source of what an instance runs to the people who use it. `instance.info().sourceCodeUrl` names it (the operator's `SOURCE_URL`, else this project's repository) and the app's About dialog shows it next to the licences and every package's notice. Keep it working when changing the dialog or the endpoint. - **The source-code link**: both licences require offering the source of what an instance runs to the people who use it. `instance.info().sourceCodeUrl` names it (the operator's `SOURCE_URL`, else this project's repository) and the app's About dialog shows it next to the licences and every package's notice. Keep it working when changing the dialog or the endpoint.
- **Deleting a person** goes through `deletePerson` (`lib/src/auth/account.dart`), in one transaction and in ADR 0001 rule 6's order: their sessions end first (a device with a valid session would re-upload everything), then their space goes with all its content through the foreign keys, then the person with their identities. `AccountEndpoint.delete` deletes the caller; the app also calls it when an anonymous account is given up (signed out of, or switched away from to GitHub), since nobody could ever sign into it again. - **Deleting a person** goes through `deletePerson` (`lib/src/auth/account.dart`), in one transaction and in ADR 0001 rule 6's order: their sessions end first (a device with a valid session would re-upload everything), then their space goes with all its content through the foreign keys, then the person with their identities. `AccountEndpoint.delete` deletes the caller; the app also calls it when an anonymous account is given up (signed out of, or switched away from to GitHub), since nobody could ever sign into it again.
- **Pruning**: `PruningFutureCall` deletes anonymous people unused for 90 days, daily, scheduled when the server starts. Scheduling a future call always adds an entry, so the server cancels the previous one by its identifier first. - **Pruning**: `PruningFutureCall` deletes anonymous people unused for 90 days, daily, scheduled when the server starts. Scheduling a future call always adds an entry, so the server cancels the previous one by its identifier first.

View file

@ -37,7 +37,7 @@ Targets: Linux, Windows, macOS, Android and iOS. There is no web build.
[Android](https://docs.flutter.dev/platform-integration/android/setup), [Android](https://docs.flutter.dev/platform-integration/android/setup),
[macOS](https://docs.flutter.dev/platform-integration/macos/setup), [macOS](https://docs.flutter.dev/platform-integration/macos/setup),
[iOS](https://docs.flutter.dev/platform-integration/ios/setup). [iOS](https://docs.flutter.dev/platform-integration/ios/setup).
- **The Serverpod CLI v4.0.3**: `dart install serverpod_cli@4.0.3`. Pass `--no-analytics` - **The Serverpod CLI v4.0.4**: `dart install serverpod_cli@4.0.4`. Pass `--no-analytics`
to every call; otherwise it reports the commands run to Serverpod. to every call; otherwise it reports the commands run to Serverpod.
- **Linux**, on top of Flutter's packages, the libraries of three plugins: WebKitGTK - **Linux**, on top of Flutter's packages, the libraries of three plugins: WebKitGTK
(pulled in by the sign-in package), libsecret (keeps the session) and GStreamer (pulled in by the sign-in package), libsecret (keeps the session) and GStreamer

View file

@ -7,6 +7,6 @@ environment:
resolution: workspace resolution: workspace
dependencies: dependencies:
serverpod_auth_idp_client: 4.0.3 serverpod_auth_idp_client: 4.0.4
serverpod_client: 4.0.3 serverpod_client: 4.0.4
serverpod_offline_sync_client: 0.0.8 serverpod_offline_sync_client: 0.0.10

View file

@ -86,9 +86,9 @@ dependencies:
git: git:
url: https://code.halkyon.app/project-halkyon/pure-zstd.git url: https://code.halkyon.app/project-halkyon/pure-zstd.git
ref: 596b63d5741d479073fbfc78377cc7dbdd342da3 ref: 596b63d5741d479073fbfc78377cc7dbdd342da3
serverpod_auth_idp_flutter: 4.0.3 serverpod_auth_idp_flutter: 4.0.4
serverpod_flutter: 4.0.3 serverpod_flutter: 4.0.4
serverpod_offline_sync_client: 0.0.8 serverpod_offline_sync_client: 0.0.10
# Anki collections are SQLite databases (the sync engine already bundles it). # Anki collections are SQLite databases (the sync engine already bundles it).
sqlite3: ^3.6.0 sqlite3: ^3.6.0
url_launcher: ^6.3.2 url_launcher: ^6.3.2
@ -112,7 +112,7 @@ dev_dependencies:
sdk: flutter sdk: flutter
# The sync tests (test/sync/) sign in like the app and read the server's database. # The sync tests (test/sync/) sign in like the app and read the server's database.
postgres: ^3.5.16 postgres: ^3.5.16
serverpod_auth_core_client: 4.0.3 serverpod_auth_core_client: 4.0.4
yaml: ^3.1.4 yaml: ^3.1.4
dependency_overrides: dependency_overrides:

View file

@ -11,11 +11,11 @@ resolution: workspace
dependencies: dependencies:
clock: ^1.1.3 clock: ^1.1.3
crypto: ^3.0.7 crypto: ^3.0.7
serverpod: 4.0.3 serverpod: 4.0.4
serverpod_auth_idp_server: 4.0.3 serverpod_auth_idp_server: 4.0.4
serverpod_offline_sync_server: 0.0.8 serverpod_offline_sync_server: 0.0.10
dev_dependencies: dev_dependencies:
serverpod_test: 4.0.3 serverpod_test: 4.0.4
test: ^1.25.5 test: ^1.25.5
serverpod: serverpod:

View file

@ -1391,13 +1391,13 @@ packages:
source: hosted source: hosted
version: "3.0.1" version: "3.0.1"
serverpod: serverpod:
dependency: "direct overridden" dependency: transitive
description: description:
name: serverpod name: serverpod
sha256: f98517ca2d7d2c834d9891f7ed13a46022c3260f10515b89090c15882ea5ed8f sha256: "59e5b3fc10fbe8942bbbc8363c35490e8a6e47752b8cb84b4bf5e93e6128399e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.3" version: "4.0.4"
serverpod_argon2: serverpod_argon2:
dependency: transitive dependency: transitive
description: description:
@ -1410,90 +1410,90 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: serverpod_auth_core_client name: serverpod_auth_core_client
sha256: cb21124db2bdcecbdb6405ee0f8f8c802787bae1ae08faced7b3ba9b3aa74a9d sha256: "2150795e2b2817749e9834add6c1610d4c2f29d5709f0216892267afe3ad9b4f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.3" version: "4.0.4"
serverpod_auth_core_flutter: serverpod_auth_core_flutter:
dependency: transitive dependency: transitive
description: description:
name: serverpod_auth_core_flutter name: serverpod_auth_core_flutter
sha256: df14e6448db79781fb57bc1a521ef12ef788e4a096a1e3d322a1da038ec636b5 sha256: "0f75a43f85e79d2e48baadb99bb70799da3784188af5df51979fca6bb523cab7"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.3" version: "4.0.4"
serverpod_auth_core_server: serverpod_auth_core_server:
dependency: transitive dependency: transitive
description: description:
name: serverpod_auth_core_server name: serverpod_auth_core_server
sha256: f06d5001f5349bc1b9310e29465320e540bd5f872aaf7efa1dfc3e1c6a5b7e60 sha256: "7c19294ce313c5e34ad9c99ca8a5667f8287c13c63699d5003814a2d030ec797"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.3" version: "4.0.4"
serverpod_auth_idp_client: serverpod_auth_idp_client:
dependency: transitive dependency: transitive
description: description:
name: serverpod_auth_idp_client name: serverpod_auth_idp_client
sha256: "8d122d71d4a79be3441f0e5ded7dd566cb3cd14b164a23ff56f82f2a640e51a3" sha256: "045d6d49b15bf163a23dc18393c406f605e92c0c897f2244042f052435b1e176"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.3" version: "4.0.4"
serverpod_auth_idp_flutter: serverpod_auth_idp_flutter:
dependency: transitive dependency: transitive
description: description:
name: serverpod_auth_idp_flutter name: serverpod_auth_idp_flutter
sha256: "5845051159001ceddccb5052053538f6e468ca525fffe80b79b1e5c693de42a5" sha256: e3c2e8dc5dc5b421beac1910f702a227b1f774969bf67e37f39c62e455f1bc3b
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.3" version: "4.0.4"
serverpod_auth_idp_server: serverpod_auth_idp_server:
dependency: transitive dependency: transitive
description: description:
name: serverpod_auth_idp_server name: serverpod_auth_idp_server
sha256: e03fc76028fb848f2600b8721635ac35e2fff8c2a6dbd5e076e15a692b4a1263 sha256: "48b5b0a9985fa16ed250744ab47d51e4dc1b99922359a2f5e402919079baac07"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.3" version: "4.0.4"
serverpod_client: serverpod_client:
dependency: "direct overridden" dependency: transitive
description: description:
name: serverpod_client name: serverpod_client
sha256: "7e8df97e386635ddc8a838af890100a98814170aa118d6245918ad0e2cf160bb" sha256: "9c86584382bfddab72cdd4fc992ee52d0fecc6dc83da2d831d7577a28ded8a60"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.3" version: "4.0.4"
serverpod_database: serverpod_database:
dependency: "direct overridden" dependency: transitive
description: description:
name: serverpod_database name: serverpod_database
sha256: "761463ecb24201fa5743f9938e4bc20b0c29a35c0a03d1ee36085f6f57a76eb5" sha256: "78603002f4cff60babea959765ce7b573c316450983b0481da6decc87ad0c702"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.3" version: "4.0.4"
serverpod_embedded_postgres: serverpod_embedded_postgres:
dependency: transitive dependency: transitive
description: description:
name: serverpod_embedded_postgres name: serverpod_embedded_postgres
sha256: f98b685137b74b767097131cc93da4881ff9c2184e5eb85c63b923122a26fdd1 sha256: "6e23e86da825d0a18c409668e276d9c63565030662d79039a0ae03dc7b87155d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.3" version: "4.0.4"
serverpod_flutter: serverpod_flutter:
dependency: transitive dependency: transitive
description: description:
name: serverpod_flutter name: serverpod_flutter
sha256: f8d975c5bd6bf6835bd5ecbd6badff7691b06b42c9ccff2bfe604b037e4634a1 sha256: "5acd244bb9daa51955195434d97c3cc489dbf928d98e710f5a1c0c262cc56e31"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.3" version: "4.0.4"
serverpod_lints: serverpod_lints:
dependency: transitive dependency: transitive
description: description:
name: serverpod_lints name: serverpod_lints
sha256: c1d6a338e98249c9f6954466cb5762d81013917234c60f030a4ff8d4b99dd9a3 sha256: "5f6b8a2976b3b5aad0066b039fbe91148a4c023d1bd618f16c5f58eb1686aefc"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.3" version: "4.0.4"
serverpod_logging: serverpod_logging:
dependency: transitive dependency: transitive
description: description:
@ -1506,50 +1506,50 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: serverpod_offline_sync name: serverpod_offline_sync
sha256: abf99fdc78dfe02e21960d4618acb3cbb71976262731b6f17c2c29f2782450c4 sha256: "48d455bd8b361974b1072481872492658cf0ca22f1b43e58a0b390f738152783"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.0.8" version: "0.0.10"
serverpod_offline_sync_client: serverpod_offline_sync_client:
dependency: transitive dependency: transitive
description: description:
name: serverpod_offline_sync_client name: serverpod_offline_sync_client
sha256: e517b3161dc1a07f5bed4801aa971349a550774ae6f0b85472d0d273c88a3198 sha256: "7e426f78cebb0a72294a0cfa695d68020de1d5969d4041a5d5c732201175a1bd"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.0.8" version: "0.0.10"
serverpod_offline_sync_server: serverpod_offline_sync_server:
dependency: transitive dependency: transitive
description: description:
name: serverpod_offline_sync_server name: serverpod_offline_sync_server
sha256: fcb313b21a72b04602793de0719bca7aeaf929f97b90f5bfe77a82a91e5eaf36 sha256: b44cc3fbff12fae2a4c09af04214aaad1e0a0e6380c3d804e7b2db1f9218b401
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.0.8" version: "0.0.10"
serverpod_serialization: serverpod_serialization:
dependency: "direct overridden" dependency: transitive
description: description:
name: serverpod_serialization name: serverpod_serialization
sha256: "35f02a34ef6843d5d5077fe3829ece5c7226dc53a4ebe9c1a2da563a3ccce1d1" sha256: "5f6e417c97afd7232931c5db25b078498c732f5d484262da9b1aad8d3b2bf2ca"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.3" version: "4.0.4"
serverpod_shared: serverpod_shared:
dependency: transitive dependency: transitive
description: description:
name: serverpod_shared name: serverpod_shared
sha256: "99e1e2ee568d9e00cedf8ff0b9d22a330a0c4f999e9fe156ec3d8519a30417fe" sha256: f53f81b55bf4483cf352970da3c3fe63c6eeba9370f315d0d0720ae72c352717
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.3" version: "4.0.4"
serverpod_test: serverpod_test:
dependency: transitive dependency: transitive
description: description:
name: serverpod_test name: serverpod_test
sha256: "9e4416c99971b4736e76696efd48232dca735c7d7f9fc1329d0b0f171640fa5b" sha256: "8d6ac3e70edcec17a8e9f37ceadd30b2a91fdc07fa7c8b1591cba8ab7ce518cd"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.3" version: "4.0.4"
shelf: shelf:
dependency: transitive dependency: transitive
description: description:

View file

@ -15,16 +15,3 @@ workspace:
dev_dependencies: dev_dependencies:
# Referenced by analysis_options.yaml in this directory. # Referenced by analysis_options.yaml in this directory.
leancode_lint: ^27.0.0 leancode_lint: ^27.0.0
# serverpod_offline_sync v0.0.8 pins Serverpod v4.0.0 exactly. The sync spike showed the
# pin is conservative (serverpod_database v4.0.0 and v4.0.2 have identical lib/, and sync
# works on v4.0.2), so these four keep the whole workspace on the Serverpod we run. Every
# Serverpod upgrade re-checks them until the package widens its constraints (ADR 0001).
# v4.0.3 checked: serverpod_database only gained backwards-compatible changes (custom SQL
# defaults tolerated, an optional ignoreDefault on column comparison, a laxer integrity
# check for unmanaged tables), none of which the sync package calls into.
dependency_overrides:
serverpod: 4.0.3
serverpod_client: 4.0.3
serverpod_database: 4.0.3
serverpod_serialization: 4.0.3