docs: decide to build our own store and delete the vendored provider

The vendored dmfs provider was kept on the grounds that it hands us the
sync bookkeeping for free. The phase-1 sync audit measured that
bookkeeping and found most of it broken, absent, or unusable: _DIRTY not
set on delete, no home for a per-collection sync token, read-only
collections inexpressible, ACCOUNT_TYPE write-once so enabling sync is a
full migration, and cleanUpLists able to delete a user's lists after a
backup restore. Sixteen findings are provider-imposed rather than
platform- or protocol-imposed.

Costing the alternative showed the swap is far smaller than assumed.
TasksDataSource is already a 14-method, domain-shaped interface;
exactly one file above the data layer references TasksContract. The
work is a second implementation behind an interface built for it, not a
rewrite. Against ~5 weeks to build, owning the store removes 2.5-4
weeks from the sync plan, and 8,200 of the vendored 14,555 lines are
things we would never write - 23 migrations from a 2013 schema, 798
lines of full-text search the app has zero call sites for, and 1,581
lines of a type-safe layer over ContentValues that Room deletes.

External mode (OpenTasks, tasks.org) is unaffected and keeps every
file that describes somebody else's schema.

STORAGE-DECISION.md is the reasoning; OWN-STORE.md is the architecture
and the six-phase plan. :provider stays in-tree until phase 5 so
recurrence parity can be tested against it before it goes.

Also corrected here: the provider's JVM test count (51 -> 56, measured
from the test-results XML) and a fourth site of the debunked "switching
sync on is never a migration" claim, in StorageMode.kt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-13 14:46:07 +02:00
parent 98ed339346
commit 13cb27b2ab
10 changed files with 1984 additions and 39 deletions

View File

@@ -142,7 +142,7 @@ squatting `org.dmfs.tasks`, which is a dead end).
prune account types we authenticate ourselves — the deletion is unsafe without
that rework. Modernized to minSdk 29 / targetSdk 36 / Java 17.
[`provider/PROVENANCE.md`](../provider/PROVENANCE.md) records every deviation,
each also marked `AGENDULA CHANGE` at the site. Upstream's 51 JVM tests pass.
each also marked `AGENDULA CHANGE` at the site. Upstream's 56 JVM tests pass.
- ✅ Storage modes + the permission-gate bypass — `ProviderStatus.NEEDS_PERMISSION`
can no longer fire in Local mode, and an upgrading Posture A user stays on the
provider that holds their data (`ProviderResolver.autoMode`).
@@ -152,9 +152,12 @@ squatting `org.dmfs.tasks`, which is a dead end).
-**Frontend surfaces for the above** — a storage-mode picker in Settings and
an export screen. The backend is done and unused until these exist.
- ⬜ File the DAVx5 issue (step 4) — non-blocking, cheap, serves F-Droid users.
- ⬜ Sync adapter (step 5) — the 1.x arc. Design discussion still open: protocol
coverage, account model, conflict resolution, and `ical4android`'s licence
against our MIT.
- ⬜ Sync adapter (step 5) — the 1.x arc. **Designed in [`SYNC.md`](SYNC.md)**,
not started: mapper → auth → engine → hardening, ~89 weeks. The account model
is settled (`AccountManager`) and `ical4android` is closed out (superseded by
`synctools`, GPLv3, so we write the mapper in-house); what's still open is
dav4jvm's JitPack-only distribution, conflict policy, and whether External mode
survives the milestone.
- ⬜ Verify on a device: the local path with no account, and the vendored
provider's timezone-change behaviour (change 3 in `PROVENANCE.md`).
@@ -168,7 +171,9 @@ These carry over from [`PLAN.md`](PLAN.md) §9; resolved ones are struck through
2. ~~**tasks.org provider authority**~~ verified on device:
`org.tasks.opentasks` + `org.tasks.permission.*`.
3. **jtx Board** — support its richer contract later, or stay OpenTasks-only?
(Not in the candidate list today.)
(Not in the candidate list today.) Note this is now downstream of
[`SYNC.md`](SYNC.md) open question 3: if External mode is retired once we sync
ourselves, the question disappears with it.
4. ~~**Posture B authority choice**~~ resolved: **our own**
`de.jeanlucmakiola.agendula.tasks`. Squatting `org.dmfs.tasks` is a dead end,
not merely a trade-off — two apps cannot declare the same authority or
@@ -181,8 +186,9 @@ These carry over from [`PLAN.md`](PLAN.md) §9; resolved ones are struck through
6. **Resolver ordering / mode-selection UX**`autoMode()` picks a sane default
today (see [`ARCHITECTURE.md`](ARCHITECTURE.md) §4.1); the Settings override it
assumes is not built yet.
7. **Sync protocol coverage**, account model, conflict resolution — the next
design discussion.
7. ~~**Sync protocol coverage**, account model, conflict resolution — the next
design discussion.~~ Taken up in [`SYNC.md`](SYNC.md); the remaining opens
live on that document's list.
---