Skip to content

feat(alerts): alert silences that hold back notifications - #2290

Merged
Makisuo merged 14 commits into
mainfrom
feat/alert-silences
Nov 6, 2026
Merged

Makisuo merged 14 commits into
mainfrom
feat/alert-silences

fix(alerts): withhold the phone all-clear only for an incident opened…

4c5022f
Select commit
Loading
Failed to load commit list.
Maple Review Bot / Maple / review completed Nov 6, 2026 in 5m 12s

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 = silenced and the phone push is skipped
  • An open incident gets exactly one catch-up page after every silence ends
  • New AlertSilencesService plus /v2/alerts/silences list, get, create, update, end
  • alert_silences table, silence_id/silenced_at columns, silenced delivery status

Before merge

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: claimableDeliveryWhere claims only queued and expired processing (AlertsService.ts:2165)
  • Held-back and catch-up rows cannot collide, because makeAlertDeliveryKey includes scheduledAt (AlertsService.ts:1154)
  • A service-scoped silence does not mute an all-service rule: covers requires 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.ts
  • packages/domain/src/http/v2/openapi.test.ts
  • packages/web-data/src/alerts/incident-timeline.test.ts
  • packages/web-data/src/alerts/overview-derive.test.ts
  • packages/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

See this annotation in the file changed.

@maple-review-bot 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.