docs: say what actually shipped, and what 1.0.0 actually is

The branch's documents describe a world where the vendored provider reached
users. It never did, and several claims follow from that mistake.

`ROADMAP.md`:
- Phase 5's "**Breaking:** the authority and both custom permissions no longer
  exist — anyone who pointed DAVx5 at that authority loses it, and the release
  notes have to say so" is wrong in the way that *removes* work: they were
  added and deleted inside this unreleased cycle, so nobody could have pointed
  anything at them. The release notes must not warn about losing something that
  never shipped. The per-locale release-notes item went with it.
- "Run the instrumented suite on a device … none has ever executed" was stale:
  52 tests, 0 failures, Pixel 10 / API 36, 13 Aug. What is genuinely open is a
  re-run against the tip, since the 4 Sep commits reworked the store and added
  instrumented cases that have never run. Both now say so, with the ARM64 aapt
  exit-code trap noted where someone will hit it.
- The device-verification item described upgrading from a v0.3.2 APK with
  seeded data, which cannot be the real path. Replaced with the four cases that
  matter, including the one that only exists on a device that side-loaded a dev
  build of this branch.
- M6's Glance item claimed "deps present in build.gradle.kts" — not any more.
  Translations and the language picker shipped in 0.4.0 and are marked done.

`OWN-STORE.md` gets a correction banner over "Migrating existing users" saying
the premise is wrong, and a section for the copy that replaces it.
`STORAGE-AND-SYNC.md`'s banner said the vendored-provider decision was "made,
shipped, and then costed properly" — built, not shipped.

`PRIVACY.md` had the opposite problem: it describes CalDAV sync, Nextcloud
Login Flow v2, RFC 6764 discovery and a Keystore-held password, none of which
exist in 1.0.0 — the app holds no `INTERNET` permission at all. The permissions
section listed six it does not declare. Since it is a legal document users are
sent to from Settings → About, section 4 is now marked as describing a planned
feature, section 9 lists exactly what the manifest declares (and says what is
*not* there), and the backup and crash-report sections no longer assume network
access or sync bookkeeping. Kept forward-looking rather than cut, so it does
not have to change underneath anyone when sync lands. **Worth a read before
merging** — it is the one change here with legal weight.

`fastlane/.../full_description.txt` still opened with "It works directly on an
existing tasks provider (OpenTasks / tasks.org) … no own account, no own sync"
as the app's premise. That is the F-Droid listing for a release whose headline
is that it needs nothing installed. Rewritten, with the feature list and the
no-internet-permission point that is now literally true.

`README.md` and `ExportWriter`'s "ships in eleven locales" (it is three) follow.
This commit is contained in:
2026-09-21 13:38:23 +02:00
parent b49a8af83d
commit 3150781376
7 changed files with 176 additions and 41 deletions
+43
View File
@@ -332,6 +332,17 @@ gain.
## Migrating existing users
> ⚠️ **Corrected, 2026-09-21.** The premise below is wrong, and it was wrong when
> it was written: **no release ever bundled the provider.** It was added and
> deleted inside this one unreleased cycle, so `databases/tasks.db` exists on no
> published install and `OneShotImport` finds nothing to do for every real user.
> Anyone on 0.3.x or 0.4.0 keeps their tasks in OpenTasks or tasks.org, which
> `ProviderResolver.autoMode()` correctly keeps them on. The path onto the new
> store for those users is `ExternalImport` — see
> [Copying from an external provider](#copying-from-an-external-provider) below.
> Everything in this section still applies to a device that ran a dev build of
> this branch, which is why the import, its fixture and its tests stay.
Anyone on v0.3.x has their tasks inside the bundled provider's SQLite file at
`/data/data/de.jeanlucmakiola.agendula/databases/tasks.db` (dmfs schema
version 23). Removing the Gradle module does **not** remove that file — an app
@@ -384,6 +395,38 @@ still exist:
3. Only after a release with no import defects reported does a subsequent
version delete `tasks.db.imported`.
A failure is no longer invisible, either. `runIfNeeded` used to hand back an
`ImportResult.Failed` that `StartupGate` discarded — an upgrading user got an
empty app, no message, and their tasks in a file only a developer could name. The
outcome is now recorded in DataStore and logged, and Settings → Storage offers the
retry, which is what makes `reimportFromArchive()` reachable at all.
### Copying from an external provider
The migration that actually matters for 1.0.0, since the one above serves nobody.
`ExternalImport` (`data/tasks/transfer/`) reads the external provider through the
existing `exportTasks` seam and writes lists, tasks and alarms into Room in one
transaction with verified counts — the same shape as `OneShotImport`, for the
same reason. Task `uid`s are preserved, so the rows can be attached to a CalDAV
collection once sync lands rather than duplicating server-side.
- **User-initiated, once.** Settings → Storage → *Copy tasks from …*, behind a
confirm that names the real task count. Offered only while a provider is
installed and permitted and no copy has succeeded; a second run would duplicate
everything, because what it writes is indistinguishable from hand-typed tasks
the moment it finishes.
- **A copy, not a sync.** The source is untouched, whatever syncs it keeps
syncing it, and the two sets drift from that moment. Switching stores now asks
first for the same reason: neither store hands its rows to the other.
- **One-directional.** Writing a *list* into a third-party provider means
impersonating its sync adapter, and the external store is already the one that
can sync.
- **What does not come across**, because the read seam is shaped for iCalendar
output: per-occurrence `RECURRENCE-ID` overrides (a series arrives as master +
rule), `EXDATE`, `CLASS`, `DURATION`, the per-task timezone, and a task's exact
`PRIORITY` digit (`Priority` buckets 1–4 as HIGH). Nothing the app itself
displays is lost.
This is the reason phase 5 (deleting `:provider`) ships *after* phase 4 rather
than with it — and the reason the deletion is its own release.