Files
calendula/docs
Jean-Luc Makiola b91c13030b feat(reminders): schedule and fire reminders in-house (#75)
Delivery no longer waits to be told. The scan reads `Instances` and `Reminders`,
plans every reminder, posts what has come due and arms one exact alarm for the
next — replacing both halves of what the provider used to do for us.

Reacting to `EVENT_REMINDER` could not be made reliable, only more hopeful. An
app that reacts cannot distinguish "nothing was due" from "the broadcast never
came", and AOSP's own unbundled calendar carries three workarounds for OEM
providers that retarget it or write the alert row late. Etar's fallback was not
the answer either: it replaces the alarm but still reads `CalendarAlerts` for
what to show, and it disables itself the moment one real broadcast arrives —
useless against a broadcast that arrives with no row behind it.

Keeping the old receiver alongside was rejected for the same reason it looks
attractive: on a healthy device both paths fire, and there is no honest way to
suppress one without the latch we just ruled out. So the provider path goes —
`EventReminderReceiver`, `ReminderAlertStore`, `ReminderRecovery` and the
`CalendarAlerts` writes with it. Reminder delivery no longer needs WRITE_CALENDAR.

One alarm exists at a time, re-planned on every firing, so an edit needs no alarm
bookkeeping to stay in sync. Every trigger runs the same idempotent scan: the
alarm, boot and package-replace (both wipe pending alarms), clock and timezone
changes, a provider change while the app is up, launch, and a daily worker for a
device that drops the alarm with nothing to announce it. RECEIVE_BOOT_COMPLETED
is new and load-bearing — without it reminders stop dead after a restart.

`ReminderAlert.alertId` becomes `key`, derived from the reminder rather than a
row id that no longer exists, and the notification tag and PendingIntent request
codes ride on it, so a re-posted reminder still replaces itself.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 19:25:51 +02:00
..

Documentation map

Where to look for what:

Document What it is
ARCHITECTURE.md Orientation tour: principles, layers, navigation, recurring-write / conflict / reminder pipelines, testing
../CHANGELOG.md Release history (Keep a Changelog, SemVer)
../.planning/ROADMAP.md Living roadmap: shipped milestones, current scope, idea backlog
../.planning/PROJECT.md What the project is, stack, naming, infrastructure
../.planning/REQUIREMENTS.md Requirement checklist per milestone
../.planning/STATE.md Snapshot of where development currently stands
superpowers/specs/ The original design spec (2026-06-08) — historical record, not updated
superpowers/plans/ Per-milestone implementation plans with task checklists — historical record of how each slice was built, including provider lessons learned
../fastlane/metadata/android/ Store metadata (single source of truth): descriptions, title, icon, screenshots (DE + EN). Harvested directly by the official F-Droid repo; transformed into the self-hosted repo layout at release time by ../scripts/fastlane_to_fdroid_localized.sh
../fdroid-metadata/ App-level F-Droid control file (*.yml: Categories, License, links) for the self-hosted repo's fdroid update
fdroid-official/ Draft recipe + notes for publishing to the official F-Droid repo (reproducible build + developer-signed binary)

Conventions: plans and specs under superpowers/ are point-in-time artifacts of the agentic workflow that built each milestone — they get status updates but are never rewritten. The .planning/ files are living documents and should stay current.