Repository navigation
feat(alerts): escalation chains that page step by step until someone acknowledges - #2304
Merged
Merged
Maple Review Bot / Maple / review
succeeded
Nov 6, 2026 in 2m 42s
Confidence 10/10 · No issues found
🟢 Confidence 10/10 · safe to merge. Chain advance is a compare-and-set pinned by twelve PGlite cases and eleven planner table tests; nothing new since c51738f needed filing.
no findings · tests covered · risk high · 3/3 new units observable
Adds escalation chains: snapshotted runs advanced by the alert lane, sent through the existing delivery outbox, with v2 CRUD, rule-tier wiring and MCP tools. No findings; merge after #2287. It does not touch the open McpQueryError or VcsWebhookEnqueueFailure issues.
planEscalationAdvancesteps a chain by position;applyEscalationPlancommits each step with a compare-and-set- Runs snapshot chain and trigger payload at incident open or tier escalation
- An ack pauses a run, an unack resumes it, a resolve ends it
- v2
/v2/alerts/escalation_chainsCRUD plusescalation_chain_idon rule tiers
Before merge
- Migration · Check it is safe on production data and with the code still deployed ·
packages/db/drizzle/20261106231750_escalation_chains/migration.sql - Manual · Merge after #2287 (ack), before #2306, #2305 and #2307
Production impact
Open errors in the changed files (3)
| Issue | Service | Occurrences | File |
|---|---|---|---|
| @maple/mcp/errors/McpQueryError (4 issues) | maple-chat | 30 | apps/ai/src/mcp/tools/registry.ts |
| @maple/api/routes/VcsWebhookEnqueueFailure (2 issues) | maple-api | 9 | apps/api/src/runtime/http-graph.ts |
| @maple/mcp/errors/McpQueryError (4 issues) | maple-ai | 12 | apps/ai/src/mcp/tools/registry.ts |
After this merges, Maple checks whether they stop.
Production traffic of the changed files (last 7 days)
| File | Calls/day | Busiest operations |
|---|---|---|
packages/backend/src/services/alerts/AlertsService.ts |
364 | AlertsService.prepareIncidentDeliveries 201/day, 0.0% errAlertsService.advanceEscalations 163/day, 0.0% err |
packages/backend/src/services/alerts/EscalationChainsService.ts |
15 | EscalationChainsService.list 15/day, 0.0% err |
packages/domain/src/http/v2/escalation-chains.ts |
15 | GET /v2/alerts/escalation_chains 15/day, 0.0% err |
Telemetry this change adds and removes (15)
- ➖ span name
AlertsService.prepareIncidentDeliveries(~201/day today) ·packages/backend/src/services/alerts/AlertsService.ts:1420 - ➕ span name
McpTool.createEscalationChain·apps/ai/src/mcp/tools/create-escalation-chain.ts:41 - ➕ span name
McpTool.deleteEscalationChain·apps/ai/src/mcp/tools/delete-escalation-chain.ts:23 - ➕ span name
McpTool.listEscalationChains·apps/ai/src/mcp/tools/list-escalation-chains.ts:21 - ➕ span name
McpTool.updateEscalationChain·apps/ai/src/mcp/tools/update-escalation-chain.ts:39 - ➕ span name
AlertsService.prepareIncidentNotification·packages/backend/src/services/alerts/AlertsService.ts:1424 - ➕ span name
AlertsService.prepareEscalationStart·packages/backend/src/services/alerts/AlertsService.ts:1609 - ➕ span name
AlertsService.staleEscalationSteps·packages/backend/src/services/alerts/AlertsService.ts:2400 - ➕ span name
AlertsService.activeSilenceForEscalation·packages/backend/src/services/alerts/AlertsService.ts:6018 - ➕ span name
AlertsService.advanceEscalationRun·packages/backend/src/services/alerts/AlertsService.ts:6056 - ➕ attribute
maple.escalation.silenced·packages/backend/src/services/alerts/AlertsService.ts:6072 - ➕ span name
EscalationChainsService.get·packages/backend/src/services/alerts/EscalationChainsService.ts:216 - ➕ span name
EscalationChainsService.create·packages/backend/src/services/alerts/EscalationChainsService.ts:226 - ➕ span name
EscalationChainsService.update·packages/backend/src/services/alerts/EscalationChainsService.ts:277 - ➕ span name
EscalationChainsService.delete·packages/backend/src/services/alerts/EscalationChainsService.ts:324
What was checked
- Step 1 re-uses the tier's own delivery key, so the drain dedupes it (
makeAlertDeliveryKey,alerting-core/src/index.ts:327) - A silenced step defers 60s and is realigned to the catch-up timestamp, so the held page is not sent twice
alert_escalation_runs_live_idxon (status, next_step_at) serves the due-run scan (migration.sql:40)
Observability coverage: 3 of 3 changes observable
| Change | Kind | Observable | Evidence |
|---|---|---|---|
v2 escalation-chain routes (/v2/alerts/escalation-chains) |
inbound HTTP | yes | maple-api reports GET /v2/alerts/escalation_chains in route traffic; EscalationChainsService.list/create/update/delete are Effect.fn spans |
packages/backend/src/services/alerts/AlertsService.ts escalation tick |
background cron consumer | yes | AlertScheduler.escalationTick wrapped by AlertScheduler.escalationTick span + Effect.annotateCurrentSpan |
packages/backend/src/services/alerts/EscalationChainsService.ts |
service method | yes | EscalationChainsService.* are Effect.fn spans |
62aeecf · Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.
Loading