docs(openspec): add SDD artifact trail for recent alarm and EQ changes
Persist the exploration, proposal, spec, design, tasks, and verify/archive reports produced during the multi-device EQ, alarm-countdown, and notification-visual-polish SDD cycles.
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
# 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`.
|
||||
Reference in New Issue
Block a user