We shipped reminders six weeks ago. Users could create them from the UI, specify when they wanted to be notified, choose email or Slack. The feature worked perfectly — until you actually wanted to receive a notification.
Emails never sent. The bug was simple to describe and catastrophic to experience: every reminder created from the UI was hardcoded to SMS, and since our Twilio A2P registration is still pending, SMS doesn't work. So users selected "email" and got nothing.
Three Bugs, One Symptom
The investigation revealed three separate failures, each in a different layer:
1. Service layer default: When notification_channels wasn't provided, the service hardcoded {"sms": true} instead of deriving it from reminder_type. The UI sent reminder_type: "email" but the service ignored it entirely.
2. Handler passthrough bug: Even when notification_channels was provided in skill parameters, the handler overwrote it with {reminder_type: True}. So even if layer 1 worked, layer 2 would break it.
3. Token freshness: OAuth credentials for Google Calendar were read at module import time, not call time. When GoogleTokenManager rotated the tokens, the notifications module still held the stale copy.
The fix changed one line in the service (notification_channels = {reminder_type: True}), one line in the handler (respect user-provided channels), and one line in the notifications module (read tokens at call time).
Multi-Channel Selection
While we were in the code, we shipped two related improvements:
First, the UI now supports multiple notification channels. Instead of a dropdown forcing you to choose email or Slack, you get checkboxes. Select both if you want redundancy.
Second, we exposed notification_channels in the skill manifest so the LLM can set channels from chat. "Remind me at 3pm via email and Slack" now works.
We also changed the system-wide default from SMS to Slack. SMS requires carrier approval we don't have yet; Slack is operational today.
The TDD Audit Rounds
We ran two audit rounds after the initial fix. Round 1 added 398 lines of tests covering the three bugs and the new multi-channel UI logic. Round 2 found three more issues:
Channel key validation: The service now rejects unknown keys like "telegram" or "whatsapp". Before, they'd be silently stored and then ignored at dispatch time — a terrible UX.
Empty channels fallback: If all channels are false (e.g., {"sms": false}), the handler now falls back to {reminder_type: true} instead of forwarding a dict with zero enabled channels.
Dispatcher warning logs: When the dispatcher falls back to Slack because notification_channels is empty, it now logs a warning. This surfaces data integrity issues instead of masking them.
Total test coverage: 532 new lines across three files (test_reminder_service.py, test_reminder_notifications_audit.py, test_reminder_router_audit.py). The audit found bugs the manual QA missed.
What's Next
Reminders now work as designed. You select email, you get email. You select both email and Slack, you get both.
The next milestone is the M1 foundation verification plan: 37 test cases across 6 phases, covering nudge logic, list-reminder promotion, cross-list queries, and the full reminder dispatch pipeline. We're validating that the entire Unified Lists + Reminders stack is production-ready.
SMS will remain disabled until Twilio approves our A2P registration. When that happens, it's a one-line config change.
The honest takeaway: one symptom, three bugs, two audit rounds. This is why we audit.