feat: live SSE updates for the SYST3M reminders section

Add RemindersChanged SSE event so the pending-reminders list in the
SYST3M tab updates live without polling.

Backend emission sites (every path that mutates the reminders table):
- agent_server: store_remind (remind MCP call)
- dashboard.rs: post_cancel_reminder, post_retry_reminder
- questions.rs: cancel_loose_end Reminder kind
- reminder_scheduler: after each delivery batch (any_delivered)

Coordinator gets emit_reminders_snapshot() mirroring the existing
emit_schedules_snapshot() pattern: lists PendingReminder rows from the
broker and emits DashboardEvent::RemindersChanged.

Frontend: applyRemindersChanged(ev) calls renderReminders(ev.reminders)
and is registered as reminders_changed in MUTATION_HANDLERS.

Docs: dashboard.md reminders_changed entry; CLAUDE.md file map updated.
This commit is contained in:
iris 2026-06-05 10:13:16 +02:00 committed by mara
commit a1e46e2b3d
9 changed files with 55 additions and 1 deletions

View file

@ -63,6 +63,7 @@ fn tick(coord: &Arc<Coordinator>) {
// Single-transaction batch: one DB lock acquisition for N reminders
// instead of N sequential lock/unlock cycles.
let results = coord.broker.deliver_reminders_batch(&items);
let any_delivered = results.iter().any(|r| r.is_ok());
for ((id, agent, _body), result) in items.iter().zip(results.iter()) {
if let Err(e) = result {
let reason = format!("{e:#}");
@ -82,6 +83,11 @@ fn tick(coord: &Arc<Coordinator>) {
}
}
}
// Emit after the batch so the dashboard's pending-reminders list
// updates when deliveries land (removes delivered rows).
if any_delivered {
coord.emit_reminders_snapshot();
}
}
/// Build the inbox body for a due reminder. When `file_path` is None