# Proposal: Pre-notice Live Countdown ## Intent The 30-minute alarm pre-notice posts a single static notification at T-30min and never updates — it shows "30 min" frozen until the alarm fires. Users expect the same live, decrementing behavior the snooze countdown already ships (29, 28, ... 1 min). This change makes the pre-notice a TRUE per-minute live countdown, reusing the proven snooze-chain pattern already in production in this exact codebase. ## Scope ### In Scope - Re-arm the pre-notice as a repeating per-minute chain (first post at T-30min unchanged; ticks at T-29 ... T-1). - New `AlarmScheduler.armNextPreNoticeCountdownTick(id, remaining)` mirroring `armNextSnoozeCountdownTick`. - Self-healing ticks: each recomputes remaining minutes from wall clock (`ceilMinutes()`), not a decrementing counter. - Self-stop at `remaining <= 1` (final minute handled by the real fire alarm, same as snooze). - Extend cancellation to tear down the new tick chain at all 5 sites (see Risks). ### Out of Scope - Refactoring snooze + pre-notice into one shared countdown engine (Approach 1 — rejected; risks shipped snooze code). - Any l10n / ARB / Dart / `MainActivity` work (`preNoticeCountdown` key already wired in all 13 locales). - iOS pre-notice behavior. ## Capabilities > No `openspec/specs/` exists yet. These are NEW capabilities. ### New Capabilities - `alarm-pre-notice-countdown`: per-minute live countdown for the 30-min pre-notice notification, including arm/tick/cancel lifecycle and self-healing minute computation. ### Modified Capabilities - None. ## Approach Reuse `ACTION_PRE_NOTICE` for both first-post and tick (no new action constant). Keep `schedulePreNotice()` arming the first exact alarm at T-30min. After the receiver posts the pre-notice notification (skip/postpone actions stay in `PluriWaveAlarmReceiver`), it calls `AlarmScheduler.armNextPreNoticeCountdownTick(id, remaining)` to re-arm at the next minute boundary — keeping notification-building in the receiver and AlarmManager primitives in the scheduler, consistent with current separation of concerns. Switch pre-notice to `ceilMinutes()` for consistency with snooze. Use requestCode slot 9. ## Affected Areas | Area | Impact | Description | |------|--------|-------------| | `android/.../AlarmScheduler.kt` | Modified | Add `armNextPreNoticeCountdownTick` + `cancelPreNoticeCountdown`; extend `cancelAlarm()` and `scheduleSpec` teardown; use `ceilMinutes()`. | | `android/.../PluriWaveAlarmReceiver.kt` | Modified | `ACTION_PRE_NOTICE` re-arms next tick after posting; `ACTION_SKIP_NEXT`/`ACTION_POSTPONE_NEXT` now cancel the tick chain. | ## Risks | Risk | Likelihood | Mitigation | |------|------------|------------| | Doze 9-min quota delays ticks over the longer 30-min window | Med | Each tick computes from wall clock → countdown "jumps" not breaks; parallel `setAlarmClock()` exits Doze near fire. | | Missing a cancellation site leaks a repeating chain | Med | Explicit 5-site checklist: `cancelAlarm`, `scheduleSpec` no-trigger branch, snooze-transition branch, `ACTION_SKIP_NEXT`, `ACTION_POSTPONE_NEXT`. | | Notification ID overlap with snooze countdown | Low | Invariant already holds (`scheduleSpec` branches on `snoozeUntilMillis != null`); preserve it. | | OEM aggressive battery killers | Low | Pre-existing, already accepted for snooze; not unique to this change. | ## Rollback Plan Revert the two Kotlin files (`AlarmScheduler.kt`, `PluriWaveAlarmReceiver.kt`) to prior commit. No schema, l10n, Dart, or config changes accompany this change, so rollback is a clean single-commit revert with zero migration. Pre-notice falls back to the existing single-shot behavior. ## Dependencies - None. Prior `alarm-live-countdown` change already shipped the l10n template, `setNotificationStrings` plumbing, and the snooze-chain reference implementation. ## Success Criteria - [ ] Pre-notice notification updates each minute from 29 down to 1 with the device idle/screen-off. - [ ] Chain self-stops at the final minute; the real fire alarm takes over. - [ ] Skip-next and postpone-next from the pre-notice cancel the tick chain (no orphaned repeating alarm). - [ ] Snooze transition cancels the pre-notice tick chain; no double-notification. - [ ] Snooze countdown behavior is unchanged (no regression).