Conversation
Greptile SummaryThis PR adds the central notifier delivery runtime: a durable
Confidence Score: 5/5Safe to merge; the routing, backoff, and shutdown logic are all correct and the previously-identified bugs have been fixed. The core routing loop is idempotent (ON CONFLICT DO NOTHING), attempt-aware backoff is correctly implemented using the fetched delivery row, and all daemon error paths call notifier.Stop(). No data-loss or correctness bugs were found in the changed code. backend/internal/storage/sqlite/notification_delivery_store.go — the lease-expiry re-queue path uses next_attempt_at = now rather than a backoff-derived timestamp, which is asymmetric with the explicit-error path. Important Files Changed
Sequence DiagramsequenceDiagram
participant D as Daemon
participant M as Manager (tick)
participant S as SQLite Store
participant E as Electron (AO-app)
D->>M: Start(ctx) → startDispatcher goroutine
loop every 1s
M->>S: ReleaseExpiredDeliveryLeases(now)
S-->>M: n released
M->>S: ListUnroutedNotifications(limit)
S-->>M: []Notification
loop per notification
M->>M: ResolveRoutes(settings, priority)
M->>S: EnqueueDelivery(DeliveryRow) [ON CONFLICT DO NOTHING]
M->>S: MarkNotificationRouted(id, now)
end
end
E->>M: ClaimDesktopDeliveries(owner, limit)
M->>S: "ClaimDueDeliveries(sink=ao-app, owner, now, limit, leaseTTL)"
S-->>E: "[]DeliveryRow (status=leased)"
alt delivery success
E->>M: MarkDeliverySent(id, owner, externalID)
M->>S: "UPDATE status=sent, attempts+1"
else transient error
E->>M: MarkDeliveryError(id, owner, code, msg)
M->>S: GetDelivery(id) → row.Attempts
M->>M: NextAttemptAt(now, row.Attempts+1)
M->>S: "MarkDeliveryRetry → status=retry_wait or failed"
else permanent error
E->>M: MarkDeliveryError(id, owner, code, msg)
M->>S: "MarkDeliveryFailed → status=failed"
end
note over M,S: Lease expiry path (no explicit error)
M->>S: "ReleaseExpiredDeliveryLeases → attempts+1, next_attempt_at=now"
Reviews (6): Last reviewed commit: "fix: preserve notification surface disab..." | Re-trigger Greptile |
whoisasx
left a comment
There was a problem hiding this comment.
Reviewed locally with focused storage/runtime/config passes. Backend checks are green (go test ./..., gofmt -l ., go vet ./..., go test -race ./...), but I found a few correctness issues that should be fixed before merge.
There was a problem hiding this comment.
Pull request overview
This PR introduces the backend “notifier runtime” layer that routes durable notifications into durable notification_deliveries (desktop/AO-app handoff), including routing/settings defaults, retry/lease mechanics, and CDC events, and wires it into daemon startup/shutdown.
Changes:
- Add notification runtime package (settings, routing, retry policy, dispatcher loop, manager API) and integrate it into daemon lifecycle.
- Add
notification_deliveries(+ attempt audit table) schema, CDC triggers/events, and arouted_atmarker for notifications; regenerate sqlc code. - Implement SQLite storage for delivery enqueue/claim/lease/retry/sent state + unit/integration tests.
Reviewed changes
Copilot reviewed 25 out of 29 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| backend/notifier_wiring.go | Starts notifier manager/dispatcher and exposes a stop hook for shutdown sequencing. |
| backend/main.go | Wires notifier runtime into daemon boot and shutdown ordering. |
| backend/internal/storage/sqlite/queries/notifications.sql | Extends notification queries to include routed_at in returned columns. |
| backend/internal/storage/sqlite/queries/notification_deliveries.sql | Adds sqlc queries for unrouted notifications, routing marker, and delivery insert/get APIs. |
| backend/internal/storage/sqlite/notification_store.go | Maps new routed_at column into the domain notification row. |
| backend/internal/storage/sqlite/notification_delivery_store.go | Implements durable delivery enqueue/claim/lease/release/update operations on SQLite. |
| backend/internal/storage/sqlite/notification_delivery_store_test.go | Unit tests for enqueue idempotency, leasing, expiry, terminal states, and CDC behavior. |
| backend/internal/storage/sqlite/migrations/0003_notification_deliveries.sql | Adds routed_at, delivery tables, indexes, and CDC triggers. |
| backend/internal/storage/sqlite/gen/querier.go | Extends sqlc querier interface with delivery and routing methods. |
| backend/internal/storage/sqlite/gen/notifications.sql.go | Regenerated sqlc code including routed_at scans/returns. |
| backend/internal/storage/sqlite/gen/notification_deliveries.sql.go | New sqlc-generated code for delivery + routing queries. |
| backend/internal/storage/sqlite/gen/models.go | Adds sqlc models for NotificationDelivery (+ attempt model). |
| backend/internal/notification/store.go | Defines notifier runtime durable store interface (routing + deliveries). |
| backend/internal/notification/settings.go | Adds settings provider + normalization/defaulting behavior. |
| backend/internal/notification/settings_test.go | Tests defaulting and clone/immutability behavior for settings provider. |
| backend/internal/notification/routing.go | Implements priority→route resolution for dashboard/desktop + unknown route skipping. |
| backend/internal/notification/routing_test.go | Tests routing decisions for defaults, disables, and unknown routes. |
| backend/internal/notification/retry.go | Implements retry policy (backoff + jitter) and error classification. |
| backend/internal/notification/retry_test.go | Tests backoff caps, jitter bounds, and retry classification logic. |
| backend/internal/notification/manager.go | Adds manager orchestration for routing + AO-app delivery claiming and completion APIs. |
| backend/internal/notification/enqueuer.go | Renames enqueuer store interface to avoid collision with runtime store. |
| backend/internal/notification/dispatcher.go | Adds background dispatcher loop that ticks Manager.RunOnce. |
| backend/internal/notification/dispatcher_test.go | Tests dispatcher loop behavior and routing/error handling interactions. |
| backend/internal/notification/delivery.go | Defines delivery row/status model, ID generation, and normalization helpers. |
| backend/internal/integration/notification_runtime_test.go | Integration test verifying desktop deliveries are created only for eligible priorities. |
| backend/internal/domain/notification.go | Adds RoutedAt to the domain notification model. |
| backend/internal/config/config.go | Adds notification config types and safe default notification configuration. |
| backend/internal/config/config_test.go | Verifies notification defaults are populated by config load. |
| backend/internal/cdc/event.go | Adds CDC event types for delivery created/updated. |
Files not reviewed (4)
- backend/internal/storage/sqlite/gen/models.go: Language not supported
- backend/internal/storage/sqlite/gen/notification_deliveries.sql.go: Language not supported
- backend/internal/storage/sqlite/gen/notifications.sql.go: Language not supported
- backend/internal/storage/sqlite/gen/querier.go: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 29 changed files in this pull request and generated 1 comment.
Files not reviewed (4)
- backend/internal/storage/sqlite/gen/models.go: Language not supported
- backend/internal/storage/sqlite/gen/notification_deliveries.sql.go: Language not supported
- backend/internal/storage/sqlite/gen/notifications.sql.go: Language not supported
- backend/internal/storage/sqlite/gen/querier.go: Language not supported
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 29 changed files in this pull request and generated 1 comment.
Files not reviewed (4)
- backend/internal/storage/sqlite/gen/models.go: Language not supported
- backend/internal/storage/sqlite/gen/notification_deliveries.sql.go: Language not supported
- backend/internal/storage/sqlite/gen/notifications.sql.go: Language not supported
- backend/internal/storage/sqlite/gen/querier.go: Language not supported
| EventPRCheckRecorded EventType = "pr_check_recorded" | ||
| EventNotificationCreated EventType = "notification_created" | ||
| EventNotificationUpdated EventType = "notification_updated" | ||
| EventSessionCreated EventType = "session_created" |
There was a problem hiding this comment.
these sound like notification events. Can we decouple them from CDC?
For notifications we should simply create new entries in the notifications table.
| // NotificationConfig contains the global notification settings used by the | ||
| // central notifier runtime. It intentionally starts global (not per-project) so | ||
| // the routing model can grow without changing lifecycle reactions. | ||
| type NotificationConfig struct { |
There was a problem hiding this comment.
all our configs need to be stored in sqlite now.
Can we add a new settings table in sqlite instead and have a single boolean called enable_notifications
| Dashboard DashboardNotificationConfig | ||
| Desktop DesktopNotificationConfig | ||
| Routing NotificationRoutingConfig | ||
| Retry NotificationRetryConfig |
There was a problem hiding this comment.
let's not add any retries before notifications are completely operational
|
|
||
| type DesktopNotificationConfig struct { | ||
| Enabled bool | ||
| Priorities []ports.Priority |
There was a problem hiding this comment.
let's remove priorities altogether for now
| Priorities map[ports.Priority][]string | ||
| } | ||
|
|
||
| type NotificationRetryConfig struct { |
There was a problem hiding this comment.
remove all logic related to retries
| type NotificationRoutingConfig struct { | ||
| // Priorities maps notification priority to built-in route names. The | ||
| // notifier currently implements dashboard and desktop only. | ||
| Priorities map[ports.Priority][]string |
There was a problem hiding this comment.
why don't we have a priority level instead of the config having a list of priorities
Is there any case when a user will not want high priority notifs but want low priority notifs
Summary
Tests
Closes #55