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_blocktool 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— therecord_failurerequest-path entry-point with 5-second dedupe coalescing.purge— backgroundtokio::time::intervaltask 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.