8b49ea896a
Drift-audit Group 2 (Phase-8 amputation — live wiring, no consumer): - Recurring tasks never recurred: spawn_recurring_tasks() had no caller. Register it as a 15-min interval job in the event scheduler (which app.py already starts/stops). Also add a deleted_at IS NULL guard to the spawn query in the same change, so a trashed recurring parent can never resurrect children once the sweep is live. - Event reminders were stamped reminder_sent_at but never delivered. _fire_reminders now creates an 'event_reminder' in-app notification before stamping, so a delivery failure stays retryable. Frontend NotificationsPanel renders the new type (⏰ + message); message logic pulled into a notifMessage() helper. - Remove the dead _fire_push_notif no-op stub (push left in Phase 8) and its three create_task call sites — no more throwaway tasks per share. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>