Skip to content

feat(alerts): build escalation chains in the dashboard and see them on incidents - #2305

Merged
Makisuo merged 112 commits into
mainfrom
feat/alert-escalation-chains-ui
Nov 7, 2026
Merged

Makisuo merged 112 commits into
mainfrom
feat/alert-escalation-chains-ui

fix(web): a refused chain delete's message is rebuilt from the chains…

f92b112
Select commit
Loading
Failed to load commit list.
Maple Review Bot / Maple / review succeeded Nov 7, 2026 in 3m 22s

Confidence 8/10 · No issues found

🟢 Confidence 8/10 · likely safe to merge. Held at 8 because 2 reviewed files were not read.
no findings · tests covered · risk medium · 2/3 new units observable −1 · 2 files not read · 1 fix not yet re-checked against the whole diff

Adds escalation chains to the dashboard: a Routing-tab section with create/edit/delete, a per-tier picker in the rule editor, an escalation block on the incident rail, and a nullable escalation on v2 alert incidents filled from one batched run query per page. Safe to merge.

  • listIncidentEscalations reads a page's latest runs in one query and v2 incidents expose escalation
  • Routing tab gains the chains section, the chain dialog and a 409 delete naming its blockers
  • Rule editor tiers get an escalation-chain picker beside their destinations
  • Incident rail shows escalating, paused or finished, re-read once a step is due

Fixed since the last review

  • ✅ F24 · A refused chain delete can name the wrong rules and miss the one blocking it

Production impact

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/alerts/AlertReadModelsService.ts 182 AlertReadModelsService.listRuleChecks 182/day, 0.0% err
Telemetry this change adds and removes (1)
  • ➕ span name AlertReadModelsService.listIncidentEscalations · packages/backend/src/services/alerts/AlertReadModelsService.ts:295
What was checked
  • A refused delete re-reads the chains and rebuilds the message from the fresh rule_ids (hub/update.ts:867-874, test hub-chains.scene.test.ts:167-179)
  • listIncidentEscalations filters orgId and is served by alert_escalation_runs_org_incident_idx (packages/db/src/tables/escalation-chains.ts:73)
  • The chain picker's values are decoded internal ids, so a pick reaches the rule payload as EscalationChainId (view-notifications.ts:92, escalation-test-fixtures.ts:54); dialog limits match the AP…
Observability coverage: 2 of 3 changes observable
Change Kind Observable Evidence
AlertReadModelsService.listIncidentEscalations (Postgres read) database query yes Effect.fn("AlertReadModelsService.listIncidentEscalations") plus annotateCurrentSpan({ orgId, "maple.alert.incident_count" }) at AlertReadModelsService.ts:295-298
v2 alert-incident list/retrieve/ack attaching escalation HTTP handler yes Existing HttpApiBuilder server spans; acknowledge and unacknowledge keep Effect.withSpan (alert-incidents.http.ts:203,210)
Dashboard chains section, chain dialog, tier picker, incident rail browser UI no apps/web has no client OTel bootstrap (@opentelemetry appears only in docs snippets); their reads show up as API server spans
Files not reviewed (2)

The review ended before it read these diffs, so nothing above vouches for them.

  • apps/ios/Packages/MapleAPI/Sources/MapleAPI/openapi.json
  • apps/web/src/lab/escalation-lab.ts

f92b112 · Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.