fix(schedulers): wire recurring-task spawn + deliver event reminders
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 35s
CI & Build / Python tests (push) Successful in 47s
CI & Build / Build & push image (push) Successful in 1m10s

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>
This commit is contained in:
2026-06-02 18:47:54 -04:00
parent e70fe545cc
commit 8b49ea896a
4 changed files with 71 additions and 25 deletions
@@ -266,12 +266,6 @@ async def create_in_app_notification(user_id: int, notif_type: str, payload: dic
return n
async def _fire_push_notif(user_id: int, title: str, body: str, url: str) -> None:
# Push delivery was removed alongside the chat subsystem (Phase 8).
# In-app notifications still flow through the bell-icon feed.
return None
async def _fire_share_email(user_id: int, subject: str, body_text: str) -> None:
try:
if not await is_smtp_configured():
@@ -325,7 +319,6 @@ async def notify_project_shared(
"invited_by": inviter.username,
"url": url,
})
asyncio.create_task(_fire_push_notif(uid, "Project shared with you", msg, url))
asyncio.create_task(_fire_share_email(uid, f"[Fabled] {inviter.username} shared a project with you", msg))
@@ -361,7 +354,6 @@ async def notify_note_shared(
"invited_by": inviter.username,
"url": url,
})
asyncio.create_task(_fire_push_notif(uid, "Note shared with you", msg, url))
asyncio.create_task(_fire_share_email(uid, f"[Fabled] {inviter.username} shared a note with you", msg))
@@ -385,7 +377,6 @@ async def notify_group_added(
"invited_by": inviter.username,
"url": url,
})
asyncio.create_task(_fire_push_notif(target_user_id, "Added to a group", msg, url))
asyncio.create_task(_fire_share_email(target_user_id, "[Fabled] You've been added to a group", msg))