Reminder actions apply to today, not to the day they asked about #69

Closed
opened 2026-08-20 21:24:13 -05:00 by null · 0 comments
Owner

What is true now. NotificationActionHandler.handle (app/src/main/kotlin/dev/privacyllc/period/notifications/NotificationActionHandler.kt:34-35) writes against LocalDate.now(clock). The PendingIntent carries only the action string (core/notifications/.../PeriodNotifier.kt:166-172) — no date. A reminder that sat in the shade over a weekend and is tapped on Monday records Monday.

Related: MainActivity.kt:79 reads the intent extra in onCreate and nothing ever calls removeExtra, so a rotation or theme change re-parks the same action and the gate delivers it again. After process-death restore the original Intent is redelivered with its extras intact, so removeExtra alone is not enough — the parking must also be gated on savedInstanceState == null.

What it costs. A start date silently off by days, which is the single input the whole prediction engine is built on. confirmPeriodStart is idempotent per date and recordNotYet ignores duplicates, so the re-parking is mostly harmless today — but it re-runs the write path on every recreation and will stop being harmless the moment a non-idempotent action exists.

What to do. Add an epoch-day extra alongside the action; parse both into a small ReminderActionRequest(action, date?); apply the write to that date. Add a pure staleness rule — a request with no date, a future date, one older than the previous day, or one already answered by a newer confirmed start, writes nothing and simply opens the app. AppLockController.holdNotificationAction takes the request rather than a bare String, so the parked path carries the date too.

Traps. Legacy notifications posted before the upgrade have no date extra — parse as stale, write nothing. The parked action is dropped if the session never unlocks (AppLockViewModel.kt:69), which stays true and is fine; do not persist it.

Verify: ReminderActionRulesTest (table-driven: today applies, yesterday applies, older is stale, future is stale, no date is stale, already-answered is stale) and a stale request writes nothing in NotificationActionHandlerTest.

**What is true now.** `NotificationActionHandler.handle` (`app/src/main/kotlin/dev/privacyllc/period/notifications/NotificationActionHandler.kt:34-35`) writes against `LocalDate.now(clock)`. The PendingIntent carries only the action string (`core/notifications/.../PeriodNotifier.kt:166-172`) — no date. A reminder that sat in the shade over a weekend and is tapped on Monday records Monday. Related: `MainActivity.kt:79` reads the intent extra in `onCreate` and nothing ever calls `removeExtra`, so a rotation or theme change re-parks the same action and the gate delivers it again. After process-death restore the original Intent is redelivered with its extras intact, so `removeExtra` alone is not enough — the parking must also be gated on `savedInstanceState == null`. **What it costs.** A start date silently off by days, which is the single input the whole prediction engine is built on. `confirmPeriodStart` is idempotent per date and `recordNotYet` ignores duplicates, so the re-parking is mostly harmless today — but it re-runs the write path on every recreation and will stop being harmless the moment a non-idempotent action exists. **What to do.** Add an epoch-day extra alongside the action; parse both into a small `ReminderActionRequest(action, date?)`; apply the write to that date. Add a pure staleness rule — a request with no date, a future date, one older than the previous day, or one already answered by a newer confirmed start, writes nothing and simply opens the app. `AppLockController.holdNotificationAction` takes the request rather than a bare String, so the parked path carries the date too. **Traps.** Legacy notifications posted before the upgrade have no date extra — parse as stale, write nothing. The parked action is dropped if the session never unlocks (`AppLockViewModel.kt:69`), which stays true and is fine; do not persist it. Verify: `ReminderActionRulesTest` (table-driven: today applies, yesterday applies, older is stale, future is stale, no date is stale, already-answered is stale) and `a stale request writes nothing` in `NotificationActionHandlerTest`.
null added this to the Batch 12 — Reminders that tell the truth milestone 2026-08-20 21:24:13 -05:00
null added the
P1
label 2026-08-20 21:24:13 -05:00
null closed this issue 2026-08-21 00:20:32 -05:00
Sign in to join this conversation.
No Label
P0
P1
P2
release-blocker
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: null/Privacy-Period-Tracker#69
No description provided.