Repository navigation
feat(alerts): alert silences that hold back notifications - #2290
Confidence 6/10 · No issues found
🟡 Confidence 6/10 · needs attention. Held at 6 because 5 reviewed files were not read.
1 note · tests covered · risk high · 3/3 new units observable · 5 files not read · 2 fixes not yet re-checked against the whole diff
Adds alert silences: an org-scoped table, matcher, service and /v2/alerts/silences API that write held-back deliveries as silenced while rules keep evaluating, then send one catch-up page. Safe to merge; the open VcsWebhookEnqueueFailure error in http-graph.ts is untouched by this diff.
- Silenced deliveries are written
status = silencedand the phone push is skipped - An open incident gets exactly one catch-up page after every silence ends
- New
AlertSilencesServiceplus/v2/alerts/silenceslist, get, create, update, end alert_silencestable,silence_id/silenced_atcolumns,silenceddelivery status
Before merge
- Migration · Check it is safe on production data and with the code still deployed ·
packages/db/drizzle/20261106164718_alert_silences/migration.sql
Findings
🔵 Note · F5 · end audits a silence it never changed
correctness · apps/api/src/routes/v2/alert-silences.http.ts:185-188
AlertSilencesService.end returns the row untouched when the silence has already ended (AlertSilencesService.ts:309), but the route records alert_silence.ended regardless. A repeated or stale POST /:id/end therefore adds an audit entry naming a user who ended nothing.
Record the audit entry only when the returned window actually ends at now, or have `end` report whether it wrote.
Fixed since the last review
- ✅
F3 · Resolve during a silence drops the phone all-clear for phone-only alerts - ✅
F4 · Resolve during a silence counts a silenced notification when none was held
Production impact
Open errors in the changed files (1)
| Issue | Service | Occurrences | File |
|---|---|---|---|
| @maple/api/routes/VcsWebhookEnqueueFailure | maple-api | 6 | apps/api/src/runtime/http-graph.ts |
After this merges, Maple checks whether they stop.
Telemetry this change adds and removes (11)
- ➕ metric
alerting.notifications_silenced_total·packages/backend/src/observability/AlertingMetrics.ts:62 - ➕ metric
alerting.silence_catch_ups_total·packages/backend/src/observability/AlertingMetrics.ts:67 - ➕ span name
AlertSilencesService.findRow·packages/backend/src/services/alerts/AlertSilencesService.ts:114 - ➕ span name
AlertSilencesService.requireRuleIds·packages/backend/src/services/alerts/AlertSilencesService.ts:136 - ➕ span name
AlertSilencesService.list·packages/backend/src/services/alerts/AlertSilencesService.ts:168 - ➕ span name
AlertSilencesService.get·packages/backend/src/services/alerts/AlertSilencesService.ts:192 - ➕ span name
AlertSilencesService.create·packages/backend/src/services/alerts/AlertSilencesService.ts:201 - ➕ span name
AlertSilencesService.update·packages/backend/src/services/alerts/AlertSilencesService.ts:248 - ➕ span name
AlertSilencesService.end·packages/backend/src/services/alerts/AlertSilencesService.ts:300 - ➕ span name
AlertsService.findActiveSilence·packages/backend/src/services/alerts/AlertsService.ts:1498 - ➕ attribute
maple.alert_silence.active_count·packages/backend/src/services/alerts/AlertsService.ts:1515
What was checked
- Silenced rows are never drained:
claimableDeliveryWhereclaims onlyqueuedand expiredprocessing(AlertsService.ts:2165) - Held-back and catch-up rows cannot collide, because
makeAlertDeliveryKeyincludesscheduledAt(AlertsService.ts:1154) - A service-scoped silence does not mute an all-service rule:
coversrequires a non-empty scope (alert-silences.ts:178)
Observability coverage: 3 of 3 changes observable
| Change | Kind | Observable | Evidence |
|---|---|---|---|
| GET/POST/PATCH /v2/alerts/silences and POST /:id/end | inbound HTTP | yes | Effect HttpApi group, auto-instrumented like the other v2 groups in http-graph.ts:187 |
Per-check silence lookup over selectActiveSilences |
database read | yes | AlertsService.findActiveSilence is an Effect.fn span recording maple.alert_silence.active_count (AlertsService.ts:1513) |
| Silence catch-up page | background work | yes | alerting.silence_catch_ups_total counter and a structured log at AlertsService.ts:3540 |
Files not reviewed (5)
The review ended before it read these diffs, so nothing above vouches for them.
apps/ai/src/mcp/tools/__tests__/alert-tools.test.tspackages/domain/src/http/v2/openapi.test.tspackages/web-data/src/alerts/incident-timeline.test.tspackages/web-data/src/alerts/overview-derive.test.tspackages/web-data/src/alerts/preview-insights.test.ts
4c5022f · Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.
Annotations
Check notice on line 188 in apps/api/src/routes/v2/alert-silences.http.ts
maple-review-bot / Maple / review
correctness: `end` audits a silence it never changed
`AlertSilencesService.end` returns the row untouched when the silence has already ended (`AlertSilencesService.ts:309`), but the route records `alert_silence.ended` regardless. A repeated or stale `POST /:id/end` therefore adds an audit entry naming a user who ended nothing.