Notification

When a block or stack fails to load, parse, or validate, the daemon records the diagnostic as a notification. Notifications drive three user-facing surfaces:

  • the dashboard notification bell (Header.tsx + SSE),
  • the tray badge (Stackie › View Dashboard (🔔 N)),
  • the MCP validate_block tool result.

Architecture

This module follows the dependency-inversion pattern called out in the SOLID/DRY audit (Finding #4). Callers depend on the NotificationStore trait, never on the concrete SqliteNotificationStore. Tests use the in-memory MemoryNotificationStore (also implementing the trait) so they never need a SQLite file on disk.

    daemon failure path        ─┐
    CLI failure path           ─┼─►  service::record_failure
    MCP validate_block         ─┘            │

                                   &dyn NotificationStore
                                      /          \
                         SqliteNotificationStore   MemoryNotificationStore
                         (production, daemon)       (tests)

Modules

  • sqlite_store — production SQLite-backed implementation.
  • service — the record_failure request-path entry-point with 5-second dedupe coalescing.
  • purge — background tokio::time::interval task that deletes rows older than the configured retention in chunks of 500 to avoid long write transactions.

Wire shape

Notifications flow over SSE and /api/notifications using DiagnosticDto(crate::diagnostic::DiagnosticDto) plus the Notification envelope (id, created_at, dismissed_at). T010 owns the SSE/REST wiring; this module owns persistence + dedupe + retention.