# Proposal: Snooze Reschedule Reliability Fix ## Intent Snoozing an alarm silently fails: the alarm never re-fires, no countdown notification appears, and the "Next alarm" panel keeps showing the already-fired alarm. Confirmed structural defect (exploration `sdd/snooze-reschedule-fix/explore`): `posponerAlarma()` and `posponerProximaDesdePreaviso()` await `android.programar()` with NO try/catch, so a native scheduling failure (most likely exact-alarm permission revoked — these methods never re-request it) throws past `notifyListeners()`, leaving in-memory state mutated but UI stale and no real alarm armed. `guardarAlarma()` already handles this correctly; the snooze paths regressed. Snooze is core alarm functionality — silent failure is critical. ## Scope ### In Scope - Wrap `android.programar()` in try/catch in `posponerAlarma()` and `posponerProximaDesdePreaviso()`, always reaching `notifyListeners()` (mirror `guardarAlarma()`). - Add `_solicitarPermisosNecesariosParaAlarma()` pre-check before scheduling in both snooze methods. - Surface failure to the user in `_posponer()` (SnackBar/feedback) instead of silent `debugPrint`. - Regression tests (Strict TDD) for both snooze paths: success reschedules + notifies; failure still notifies + reports error. ### Out of Scope - **Notification visual improvements** (custom small icon, color theming, action-button icons, BigTextStyle, fallback artwork, channel groups) — split into a separate change `notification-visual-polish` (see Approach for rationale). - Changing the Kotlin `scheduleMainAlarm()` inexact-fallback policy (intentional behavior; out of this fix). - iOS snooze path (this defect is Android-specific). ## Capabilities ### New Capabilities - `alarm-snooze-reschedule`: snoozing an alarm MUST reliably re-arm the native alarm, refresh the UI, and report any scheduling failure to the user — covering both ringing-screen snooze and pre-notice snooze. ### Modified Capabilities - None. (`alarm-pre-notice-countdown` behavior is unchanged; only its scheduling reliability is hardened, which the new capability covers.) ## Approach Mirror the proven `guardarAlarma()` pattern in both snooze methods: permission pre-check → try/schedule/catch-into-`_error` → `notifyListeners()` in a `finally` so the widget tree always rebuilds. Propagate a failure signal to `_posponer()` for user-visible feedback (the ringing screen dismisses by design, so feedback must survive dismissal). Dart-only change — fully testable under Strict TDD with a mocked `ServicioAlarmasAndroid`. **Split decision (one of the two questions this proposal answers):** Bug fix and notification visuals are SEPARATE changes. Justification: (1) **Risk isolation** — a critical, fast, Dart-only, fully-testable fix must not be blocked or complicated by cosmetic Kotlin work that has no test infra. (2) **File overlap is shallow** — the bug fix barely touches Kotlin (only optionally a signal in `scheduleSpec`); visuals are Kotlin-heavy. Coupling them would force the critical fix through a larger, riskier review. (3) **Delivery** — this fix is small (well under the 400-line budget), ships as a single PR with no `size:exception`; visuals ship later as their own change. ## Affected Areas | Area | Impact | Description | |------|--------|-------------| | `lib/estado/estado_alarmas.dart` | Modified | try/catch + permission pre-check + guaranteed `notifyListeners()` in both snooze methods | | `lib/pantallas/pantalla_alarma_sonando.dart` | Modified | `_posponer()` surfaces snooze failure to user | | `test/` (Dart) | New | Regression tests for both snooze paths (success + failure) | | `android/.../AlarmScheduler.kt` | Modified (optional) | Optionally distinguish "main fire failed" so caller can react — only if needed by tests | ## Risks | Risk | Likelihood | Mitigation | |------|------------|------------| | Root cause is permission-dependent; unconfirmed without device logcat | Med | Fix targets the confirmed structural defect (missing try/catch + notify), valid regardless of which native call failed | | Different unfound bug if permission was actually granted | Low | Request `adb logcat` (tag `PluriWave`) for a snooze attempt before/alongside implementation | | Re-requesting permission at snooze time disrupts the ringing UX | Low | Pre-check mirrors `guardarAlarma()`; if already granted it's a no-op | ## Rollback Plan Single-PR, Dart-focused change. Revert by reverting the PR commit(s) — `estado_alarmas.dart` and `pantalla_alarma_sonando.dart` return to current behavior; new tests removed with the revert. No data migration, no schema change, no persisted-state format change, so rollback is clean with zero residual state. ## Dependencies - Optional but recommended: device `adb logcat` (tag `PluriWave`) confirming `scheduleMainAlarm`/`setAlarmClock` failure, to upgrade the hypothesis to a confirmed reproduction. ## Success Criteria - [ ] Snoozing from the ringing screen re-arms the native alarm; it rings again at the snooze time. - [ ] The per-minute countdown notification appears after snoozing. - [ ] The "Next alarm" panel updates immediately to the snoozed time (UI rebuilds via `notifyListeners()`). - [ ] On scheduling failure, the user sees explicit feedback (no silent failure) and the UI still rebuilds. - [ ] Pre-notice snooze (`posponerProximaDesdePreaviso`) has identical reliability. - [ ] Dart regression tests cover success and failure for both paths and pass under `flutter test`.